pax_global_header00006660000000000000000000000064143461650430014520gustar00rootroot0000000000000052 comment=e19bc0f31557dbea1a08d56028326f39ca1fdd5b linphone-desktop-5.0.2/000077500000000000000000000000001434616504300150075ustar00rootroot00000000000000linphone-desktop-5.0.2/.github/000077500000000000000000000000001434616504300163475ustar00rootroot00000000000000linphone-desktop-5.0.2/.github/ISSUE_TEMPLATE/000077500000000000000000000000001434616504300205325ustar00rootroot00000000000000linphone-desktop-5.0.2/.github/ISSUE_TEMPLATE/bug_report.yml000066400000000000000000000134321434616504300234300ustar00rootroot00000000000000--- name: Bug report description: File a bug/issue title: "[Bug]: " labels: ["bug"] body: - type: markdown attributes: value: '# Reminder' - type: markdown attributes: value: | The responses are provided by the **community** and, on a **best effort** basis, by some Belledonne Communications SARL engineers working on Linphone and its related projects. The community means any people all around the world simply willing to participate to the discussions. Belledonne Communications SARL **disclaims any WARRANTY** that the content posted on github issues or mailing lists is technically correct. Responses from Belledonne Communications SARL engineers shall be considered as individual contributions and shall not be seen as Belledonne Communications's official point of view or commitment. The Github issue tracker must be seen as a place for **collaboration**. Issues submitted should be of general interest, in the goal of improving the software. Consider that a **well documented** issue (with precise reproduction procedure, logs, stack trace if relevant, possibly a corrective patch) has a higher chance to receive interest and feedback from community members and Belledonne Communications' engineers. __Issues poorly documented, with no facts, or asking for debugging assistance for a custom app using Linphone's libraries, or for a modified version of Linphone are unlikely to receive any kind of response.__ People using Linphone or its related projects within the scope of their company job are invited to contact [Belledonne Communications](https://linphone.org/contact#content-bottom3) in order to obtain commercial support. - type: markdown attributes: value: | # Well ordered issues are treated issues **If the issue is about the SDK (build, issue, etc...) open the ticket in the [Linphone-SDK](https://github.com/BelledonneCommunications/linphone-sdk) repository.** - type: markdown attributes: value: | # Useful links [Linphone.org](https://linphone.org) [Linphone commercial contact](https://linphone.org/contact#content-bottom3) Linphone Vulnerability/Security contact: vulnerabilities@linphone.org [Contributor agreement (to sign and to return to sales@belledonne-communications.com for a pull request)](https://linphone.org/sites/default/files/bc-contributor-agreement_0.pdf) - type: textarea attributes: label: | Context description: | - For which purpose do you use the project ? - With which software/hardware it is integrated ? - Did you use sip.linphone.org or a different SIP service (in this case specify which one and which version) ? placeholder: | I use the linphone-sdk in the Linphone-desktop Linux version with sip.linphone.org for my company. I want to do a simple call between an Android phone and a Linux client. validations: required: true - type: textarea attributes: label: General information description: | Complete it multiple time if there are multiple devices involved. Please note that the issue has more chances to be read if you report a bug seen in the latest version of the app. Ex : - Device: [e.g. ASUS Zenbook Pro UX501] - OS: [e.g. Manjaro 21.1.6 Pahvo KDE, Windows 10 - 2004, MacOs 10.5] - Version of the App [e.g. 4.3.2] - Version of the SDK [e.g 5.0.49] value: | - Device: - OS: - Version of the App: - Version of the SDK: validations: required: true - type: textarea attributes: label: Expected behaviour description: "A clear and concise description of what you expected to happen." value: | I wanted to do a simple call with the Linux client calling the Android phone. However, the desktop app crashed. validations: required: true - type: textarea attributes: label: To Reproduce description: "Steps to reproduce the behavior:" placeholder: | 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error validations: required: true - type: textarea attributes: label: 'Additional context' placeholder: Add any other context about the problem here. - type: markdown attributes: value: | # Logs ## SDK logs Launch the application with --verbose parameter to get full logs and send it with your request. Enable debug logs in advanced section of the settings, restart the app, reproduce the issue and then go to the advanced section of settings page, click on "Send logs" and copy/paste the link here (or send them in attachments). - type: input attributes: label: 'SDK logs URL' - type: markdown attributes: value: | ## SDK crash logs In case of a crash of the app related to the SDK, please also provide the backtrace of the crash in attachments using adb logcat (Android) or the device console (iOS). For desktop versions, you can get the backtrace from a core dump. - type: markdown attributes: value: | # Screenshots Please add screenshots in attachments to help us to understand your problem. - type: markdown attributes: value: | # Pcap file If this is a network issue, join a pcap file of your attempt in attachments (done with Wireshark or TCPDump, for example) - type: markdown attributes: value: | # Contributing You can follow this [part](https://gitlab.linphone.org/BC/public/linphone-desktop#contributing) to contribute. linphone-desktop-5.0.2/.gitignore000066400000000000000000000013501434616504300167760ustar00rootroot00000000000000# Temporary files -------------------------------------------------------------- *~ .* \#*\# .#.* # Project configuration -------------------------------------------------------- *.pro.user Project.sln.lnk WORK OUTPUT Makefile CMakeLists.txt.user build* build-*-Debug build-*-Default prepare.conf.user build/* # Tags ------------------------------------------------------------------------- GPATH GRTAGS GTAGS .ctags .clang_complete # QMLC/JSC --------------------------------------------------------------------- *.qmlc *.jsc # RPM -------------------------------------------------------------------------- linphone-qt/ rpm-*/ # OTHER ------------------------------------------------------------------------ vgcore.* linphone.spec linphone-desktop-5.0.2/.gitlab-ci-files/000077500000000000000000000000001434616504300200205ustar00rootroot00000000000000linphone-desktop-5.0.2/.gitlab-ci-files/job-linux-desktop-archlinux-latest.yml000066400000000000000000000065661434616504300274230ustar00rootroot00000000000000.install-qt-webview: &install-qt-webview before_script: - sudo pacman -Sy qt5-webview --noprogressbar --noconfirm job-archlinux-latest-ninja-clang: tags: [ "docker-archlinux-latest" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION except: refs: - schedules variables: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: clang CXX: clang++ extends: .job-linux-desktop <<: *install-qt-webview ################################################# # Nightly ################################################# job-archlinux-latest-makefile-gcc: tags: [ "docker-archlinux-latest" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ extends: .job-linux-desktop <<: *install-qt-webview job-archlinux-latest-makefile-clang: tags: [ "docker-archlinux-latest" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: clang CXX: clang++ extends: .job-linux-desktop <<: *install-qt-webview job-archlinux-latest-ninja-gcc: tags: [ "docker-archlinux-latest" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ extends: .job-linux-desktop <<: *install-qt-webview job-archlinux-latest-ninja-gcc-novideo: tags: [ "docker-archlinux-latest" ] only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO extends: job-archlinux-latest-ninja-gcc <<: *install-qt-webview job-archlinux-latest-ninja-clang-novideo: tags: [ "docker-archlinux-latest" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-archlinux:$ARCHLINUX_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF CMAKE_GENERATOR: Ninja CC: clang CXX: clang++ extends: .job-linux-desktop <<: *install-qt-webview ################################################# # Package - Nightly ################################################# #job-archlinux-latest-makefile-clang-package: # stage: package # tags: [ "docker-archlinux-latest" ] # only: # variables: # - $NIGHTLY_MASTER # - $DEPLOY_LINUX # variables: # CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON # extends: job-archlinux-latest-makefile-clang # artifacts: # paths: # - build/OUTPUT/Packages/Linphone*.AppImage # expire_in: 1 week # ################################################# # Deploy - Nightly ################################################# #job-archlinux-latest-makefile-clang-deploy: # stage: deploy # tags: [ "docker-archlinux-latest" ] # dependencies: # - job-archlinux-latest-makefile-clang-package # only: # variables: # - $NIGHTLY_MASTER # - $DEPLOY_LINUX # script: # - scp build/OUTPUT/Packages/Linphone*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/ linphone-desktop-5.0.2/.gitlab-ci-files/job-linux-desktop-centos7.yml000066400000000000000000000104071434616504300255030ustar00rootroot00000000000000 #job-centos7-makefile-gcc: # tags: [ "docker" ] # image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos:7 # only: # variables: # - $NIGHTLY_MASTER # variables: # CMAKE_GENERATOR: Unix Makefiles # CC: gcc # CXX: g++ # extends: .job-linux-desktop #job-centos7-makefile-clang: # tags: [ "docker-centos7" ] # image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION # only: # variables: # - $NIGHTLY_MASTER # variables: # CMAKE_GENERATOR: Unix Makefiles # CC: clang # CXX: clang++ # extends: .job-linux-desktop ################################################# # Ninja ################################################# job-centos7-ninja-gcc: tags: [ "docker" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION except: refs: - schedules variables: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ extends: .job-linux-desktop before_script: - source /opt/rh/devtoolset-8/enable #job-centos7-ninja-clang: # tags: [ "docker-centos7" ] # only: # variables: # - $NIGHTLY_MASTER # image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos:7 # variables: # CMAKE_GENERATOR: Ninja # CC: clang # CXX: clang++ # CMAKE_OPTIONS: -DENABLE_LIME=ON # extends: .job-linux-desktop ################################################# # Package - Nightly ################################################# job-centos7-ninja-gcc-package: stage: package tags: [ "docker-test-centos7-liblinphone-nuc" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION dependencies: [] only: variables: - $DEPLOY_LINUX_CENTOS7 variables: CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON CMAKE_GENERATOR: Ninja CC: gcc CXX: g++ APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS extends: .job-linux-desktop before_script: - source /opt/rh/devtoolset-8/enable script: - echo "$GPG_SIGNING_PUB" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key - gpg --import file.key - echo "$GPG_SIGNING_KEY" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key - gpg --import file.key - cmake --version - export CC=$CC - export CXX=$CXX - mkdir -p build/OUTPUT - echo $CI_BUILD_TYPE - echo $CMAKE_GENERATOR - echo $DEFAULT_LINUX_CMAKE_OPTIONS - echo $CMAKE_SANITIZER_OPTIONS - cd build - cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS - cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS artifacts: paths: - build/OUTPUT/Packages/*.AppImage when: always expire_in: 1 week ################################################# # Deploy - Nightly ################################################# job-centos7-ninja-gcc-deploy: stage: deploy tags: [ "deploy" ] dependencies: - job-centos7-ninja-gcc-package only: variables: - $DEPLOY_LINUX_CENTOS7 script: - rsync -rlv --ignore-existing build/OUTPUT/Packages/*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/ ################################################# # Debug ################################################# job-centos7-debug-nuc: stage: deploy tags: [ "docker-test-centos7-liblinphone-nuc" ] #image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos:7 image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION dependencies: [] only: variables: - $DEBUG_CENTOS7_NUC script: - sleep 10m job-centos7-debug: stage: deploy tags: [ "docker" ] #image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos:7 image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-centos7-fuse-qt-wget:$CENTOS_7_QT_IMAGE_VERSION dependencies: [] only: variables: - $DEBUG_CENTOS7 script: - sleep 10m linphone-desktop-5.0.2/.gitlab-ci-files/job-linux-desktop-debian10.yml000066400000000000000000000067011434616504300255060ustar00rootroot00000000000000 job-debian10-ninja-gcc: tags: [ "docker-debian10" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION except: refs: - schedules variables: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ extends: .job-linux-desktop ################################################# # Nightly ################################################# job-debian10-makefile-gcc: tags: [ "docker-debian10" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS extends: .job-linux-desktop job-debian10-ninja-gcc-novideo: tags: [ "docker-debian10" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF CMAKE_GENERATOR: Ninja CC: gcc CXX: g++ extends: .job-linux-desktop job-debian10-ninja-gcc-smallsdk: tags: [ "docker-debian10" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_ADVANCED_IM=NO -DENABLE_DB_STORAGE=NO -DENABLE_PQCRYPTO=OFF CMAKE_GENERATOR: Ninja CC: gcc CXX: g++ extends: .job-linux-desktop job-debian10-ninja-clang: tags: [ "docker-debian10" ] only: variables: - $NIGHTLY_MASTER image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION variables: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: clang CXX: clang++ extends: .job-linux-desktop job-debian10-ninja-clang-novideo: only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO extends: job-debian10-ninja-clang job-debian10-makefile-clang: tags: [ "docker-debian10" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian10:$DEBIAN_10_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER - $DEPLOY_LINUX variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: clang CXX: clang++ ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS extends: .job-linux-desktop ################################################# # Package - Nightly ################################################# job-debian10-makefile-clang-package: stage: package tags: [ "docker-debian10" ] dependencies: [] only: variables: - $NIGHTLY_MASTER - $DEPLOY_LINUX variables: CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON extends: job-debian10-makefile-clang artifacts: paths: - build/OUTPUT/Packages/Linphone*.AppImage expire_in: 1 week ################################################# # Deploy - Nightly ################################################# job-debian10-makefile-clang-deploy: stage: deploy tags: [ "docker-debian10" ] dependencies: - job-debian10-makefile-clang-package only: variables: - $NIGHTLY_MASTER - $DEPLOY_LINUX script: - scp build/OUTPUT/Packages/Linphone*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/ linphone-desktop-5.0.2/.gitlab-ci-files/job-linux-desktop-debian8.yml000066400000000000000000000035271434616504300254400ustar00rootroot00000000000000################################################# # Makefile ################################################# job-debian8-makefile-gcc: tags: [ "docker-debian8" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian:8 except: refs: - schedules variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS LBC_NODEBUG_OPTIONS: -- -j$MAKEFILE_JOBS extends: .job-linux-desktop job-debian8-makefile-clang: tags: [ "docker-debian8" ] image: gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian:8 only: variables: - $NIGHTLY_MASTER variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: clang CXX: clang++ ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS LBC_NODEBUG_OPTIONS: -- -j$MAKEFILE_JOBS extends: .job-linux-desktop ################################################# # Package - Nightly ################################################# #job-debian8-makefile-clang-package: # stage: package # tags: [ "docker-debian8" ] # only: # variables: # - $NIGHTLY_MASTER # - $DEPLOY_LINUX # variables: # CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON # extends: job-debian8-makefile-clang # artifacts: # paths: # - build/OUTPUT/Packages/Linphone*.AppImage # expire_in: 1 week ################################################# # Deploy - Nightly ################################################# #job-debian8-makefile-clang-deploy: # stage: deploy # tags: [ "docker-debian8" ] # dependencies: # - job-debian8-makefile-clang-package # only: # variables: # - $NIGHTLY_MASTER # - $DEPLOY_LINUX # script: # - scp build/OUTPUT/Packages/Linphone*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/ linphone-desktop-5.0.2/.gitlab-ci-files/job-linux-desktop-debian9.yml000066400000000000000000000145461434616504300254440ustar00rootroot00000000000000 job-debian9-ninja-gcc: tags: [ "docker-debian9" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION except: refs: - schedules variables: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ extends: .job-linux-desktop job-debian9-ninja-gcc-smallsdk: except: refs: - schedules variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_ADVANCED_IM=NO -DENABLE_DB_STORAGE=NO -DENABLE_PQCRYPTO=OFF extends: job-debian9-ninja-gcc ################################################# # Nightly ################################################# job-debian9-makefile-gcc: tags: [ "docker-debian9" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS extends: .job-linux-desktop job-debian9-ninja-gcc-novideo: only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF extends: job-debian9-ninja-gcc job-debian9-ninja-clang: tags: [ "docker-debian9" ] only: variables: - $NIGHTLY_MASTER image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION variables: CMAKE_OPTIONS: -DENABLE_DOC=ON -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON CMAKE_GENERATOR: Ninja CC: clang CXX: clang++ extends: .job-linux-desktop job-debian9-ninja-clang-novideo: only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF extends: job-debian9-ninja-clang job-debian9-makefile-clang: tags: [ "docker-debian9" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER - $DEPLOY_PLUGINS variables: CMAKE_OPTIONS: -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON CMAKE_GENERATOR: Unix Makefiles CC: clang CXX: clang++ ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS script: - echo "$GPG_SIGNING_PUB" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key - gpg --import file.key - rm -f file.key - echo "$GPG_SIGNING_KEY" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key - base64 -w 0 file.key | base64 -d | gpg --import --no-tty --batch --yes - rm -f file.key - cmake --version - export CC=$CC - export CXX=$CXX - mkdir -p build/OUTPUT - echo $CI_BUILD_TYPE - echo $CMAKE_GENERATOR - echo $DEFAULT_LINUX_CMAKE_OPTIONS - echo $CMAKE_SANITIZER_OPTIONS - cd build - cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS - cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS extends: .job-linux-desktop ################################################# # Package - Nightly ################################################# job-debian9-makefile-clang-package: stage: package tags: [ "docker-test-liblinphone" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION dependencies: [] only: variables: - $NIGHTLY_MASTER - $PACKAGE_LINUX - $DEPLOY_LINUX variables: CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DLINPHONE_BUILDER_SIGNING_IDENTITY=$GPG_SIGNING_KEYID -DENABLE_G729=ON -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$LINUX_PLATFORM/$APP_FOLDER -DENABLE_PQCRYPTO=ON CMAKE_GENERATOR: Unix Makefiles CC: clang CXX: clang++ APPIMAGETOOL_SIGN_PASSPHRASE: $GPG_SIGNING_PASS extends: .job-linux-desktop script: - echo "$GPG_SIGNING_PUB" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key - gpg --import file.key - rm -f file.key - echo "$GPG_SIGNING_KEY" > file.key && sed -i 's/\r /\n/g' file.key && chmod 600 file.key - base64 -w 0 file.key | base64 -d | gpg --import --no-tty --batch --yes - rm -f file.key - cmake --version - export CC=$CC - export CXX=$CXX - mkdir -p build/OUTPUT - echo $CI_BUILD_TYPE - echo $CMAKE_GENERATOR - echo $DEFAULT_LINUX_CMAKE_OPTIONS - echo $CMAKE_SANITIZER_OPTIONS - cd build - cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS - cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS artifacts: paths: - build/OUTPUT/* expire_in: 1 week ################################################# # Deploy - Nightly ################################################# job-debian9-makefile-clang-deploy: stage: deploy tags: [ "deploy" ] needs: - job-debian9-makefile-clang-package only: variables: - $NIGHTLY_MASTER - $DEPLOY_LINUX script: - rsync -rlv --ignore-existing build/OUTPUT/Packages/*.AppImage $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM/$APP_FOLDER - rsync -rlv build/OUTPUT/RELEASE $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM - rsync -rlv build/OUTPUT/RELEASE $MAIN_DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM job-debian9-makefile-clang-plugins-deploy: stage: deploy tags: [ "deploy" ] needs: - job-debian9-makefile-clang only: variables: - $DEPLOY_PLUGINS script: - rsync -rlv --ignore-existing build/OUTPUT/plugins/app/*.so $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$LINUX_PLATFORM/$APP_FOLDER/plugins/ ################################################# # Debug ################################################# job-debian9-debug: stage: deploy tags: [ "docker-test-debian9-liblinphone-nuc" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-debian9-qt-fuse-wget-gpg2:$DEBIAN_9_QT_IMAGE_VERSION dependencies: [] only: variables: - $DEBUG_DEBIAN9 script: - sleep 10m linphone-desktop-5.0.2/.gitlab-ci-files/job-linux-desktop-ubuntu-rolling.yml000066400000000000000000000061411434616504300271070ustar00rootroot00000000000000 job-ubuntu-rolling-ninja-clang: tags: [ "docker-ubuntu-rolling" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-rolling-qt-fuse-wget-gpg2:$UBUNTU_ROLLING_IMAGE_VERSION except: refs: - schedules variables: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: clang CXX: clang++ extends: .job-linux-desktop ################################################# # Nightly ################################################# job-ubuntu-rolling-makefile-gcc: tags: [ "docker-ubuntu-rolling" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-rolling-qt-fuse-wget-gpg2:$UBUNTU_ROLLING_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS extends: .job-linux-desktop job-ubuntu-rolling-makefile-clang: tags: [ "docker-ubuntu-rolling" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-rolling-qt-fuse-wget-gpg2:$UBUNTU_ROLLING_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: clang CXX: clang++ ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS extends: .job-linux-desktop job-ubuntu-rolling-ninja-gcc: tags: [ "docker-ubuntu-rolling" ] image: gitlab.linphone.org:4567/bc/public/linphone-desktop/bc-dev-ubuntu-rolling-qt-fuse-wget-gpg2:$UBUNTU_ROLLING_IMAGE_VERSION only: variables: - $NIGHTLY_MASTER variables: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON CC: gcc CXX: g++ extends: .job-linux-desktop job-ubuntu-rolling-ninja-gcc-novideo: only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF extends: job-ubuntu-rolling-ninja-gcc job-ubuntu-rolling-ninja-clang-novideo: only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=OFF extends: job-ubuntu-rolling-ninja-clang ################################################# # Package - Nightly ################################################# #job-ubuntu-rolling-makefile-clang-package: # stage: package # tags: [ "docker-ubuntu-rolling" ] # only: # variables: # - $NIGHTLY_MASTER # - $DEPLOY_LINUX # variables: # CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON # extends: job-ubuntu-rolling-makefile-clang # artifacts: # paths: # - build/OUTPUT/Packages/Linphone*.AppImage # expire_in: 1 week ################################################# # Deploy - Nightly ################################################# #job-ubuntu-rolling-makefile-clang-deploy: # stage: deploy # tags: [ "docker-ubuntu-rolling" ] # dependencies: # - job-ubuntu-rolling-makefile-clang-package # only: # variables: # - $NIGHTLY_MASTER # - $DEPLOY_LINUX # script: # - scp build/OUTPUT/Packages/Linphone*.AppImage $DEPLOY_SERVER:$APPIMAGE_UPLOAD_DIRECTORY/ linphone-desktop-5.0.2/.gitlab-ci-files/job-linux-desktop.yml000066400000000000000000000015001434616504300241150ustar00rootroot00000000000000################################################# # BUILD ################################################# .build_all_linux_script: &build_all_linux_script | cmake --version export CC=$CC export CXX=$CXX mkdir -p build/OUTPUT echo $CI_BUILD_TYPE echo $CMAKE_GENERATOR echo $DEFAULT_LINUX_CMAKE_OPTIONS echo $CMAKE_SANITIZER_OPTIONS cd build cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_VERBOSE_MAKEFILE=ON -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_LINUX_CMAKE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $CMAKE_SANITIZER_OPTIONS cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS .job-linux-desktop: stage: build extends: .linux-prepare script: - *build_all_linux_script artifacts: paths: - build/OUTPUT expire_in: 1 week linphone-desktop-5.0.2/.gitlab-ci-files/job-linux-prepare.yml000066400000000000000000000017621434616504300241140ustar00rootroot00000000000000.linux-prepare: cache: key: $CI_JOB_NAME paths: - ccache/ extends: .job-prepare before_script: ## ## If a TUNNEL_USER_KEY is defined then start ssh-agent and add the key ## - if ! [ -z ${TUNNEL_USER_KEY+x} ]; then eval $(ssh-agent -s); fi - if ! [ -z ${TUNNEL_USER_KEY+x} ]; then echo "$TUNNEL_USER_KEY" | tr -d '\r' | ssh-add - > /dev/null; fi - if ! [ -z ${TUNNEL_USER_KEY+x} ]; then mkdir -p ~/.ssh && chmod 700 ~/.ssh; fi - if ! [ -z ${TUNNEL_USER_KEY+x} ]; then echo -e "Host gitlab.linphone.org\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config; fi ## ## Then configure ccache ## - mkdir -p ccache - echo "max_size = $CCACHE_SIZE" > ccache/ccache.conf - echo $CCACHE_SIZE - echo ${PWD}/ccache - export CCACHE_BASEDIR=${PWD} - export CCACHE_DIR=${PWD}/ccache - ccache -s after_script: - if ! [ -z ${TUNNEL_USER_KEY+x} ]; then rm -rf ~/.ssh || true; fi - export CCACHE_DIR=${PWD}/ccache - ccache -s linphone-desktop-5.0.2/.gitlab-ci-files/job-macosx-desktop.yml000066400000000000000000000115611434616504300242600ustar00rootroot00000000000000#Build template to use in other job scripts without having to copy same code #format = .className: &referenceName | scripts #Use = scripts: -*referenceName #Example : see .job-macosx-desktop for the default script and job-macosx-makefile-package for override .build_all_script: &build_all_script | ccache -s export Qt5_DIR=~/Qt/5.15.2/clang_64/lib/cmake/Qt5 export PATH=~/Qt/5.15.2/clang_64/bin:$PATH if [ -d "build" ]; then rm -rf build; fi; mkdir -p build/OUTPUT cd build #SDK Building echo $CI_BUILD_TYPE echo $CMAKE_GENERATOR echo $DEFAULT_MACOS_CMAKE_OPTIONS echo $CMAKE_OPTIONS echo $ADDITIONAL_BUILD_OPTIONS cmake .. -G "$CMAKE_GENERATOR" -DLINPHONESDK_PLATFORM=Desktop -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_MACOS_CMAKE_OPTIONS $XCODE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS -DLINPHONE_BUILDER_SIGNING_IDENTITY="$MACOS_SIGNING_IDENTITY" -DLINPHONESDK_MACOS_ARCHS="$LINPHONESDK_MACOS_ARCHS" -DLINPHONESDK_OPENSSL_ROOT_DIR_X86_64="$LINPHONESDK_OPENSSL_ROOT_DIR_X86_64" -DLINPHONESDK_OPENSSL_ROOT_DIR_ARM64="$LINPHONESDK_OPENSSL_ROOT_DIR_ARM64" cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS -- $ADDITIONAL_BUILD_OPTIONS ccache -s .job-macosx-desktop: stage: build tags: [ "macmini-m1-xcode13" ] script: - *build_all_script variables: LINPHONESDK_MACOS_ARCHS: "x86_64" LINPHONESDK_OPENSSL_ROOT_DIR_X86_64: "/usr/local/opt/openssl@1.1" LINPHONESDK_OPENSSL_ROOT_DIR_ARM64: "/opt/homebrew/opt/openssl@1.1" artifacts: paths: - build/OUTPUT when: always expire_in: 1 week ################################################# # On each push ################################################# job-macosx-ninja: except: refs: - schedules variables: CMAKE_GENERATOR: Ninja CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON extends: .job-macosx-desktop ################################################# # Nightly ################################################# job-macosx-makefile: only: variables: - $NIGHTLY_MASTER - $DEPLOY_PLUGINS variables: CMAKE_GENERATOR: Unix Makefiles CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS extends: .job-macosx-desktop job-macosx-ninja-novideo: only: variables: - $NIGHTLY_MASTER variables: CMAKE_OPTIONS: -DENABLE_VIDEO=NO -DENABLE_PQCRYPTO=ON CMAKE_GENERATOR: Ninja extends: .job-macosx-desktop #job-macosx-xcode: # extends: .job-macosx-desktop # variables: # XCODE_OPTIONS: -DLINPHONESDK_MACOS_BASE_URL=$MACOS_SNAPSHOTS_URL # CMAKE_GENERATOR: Xcode # ADDITIONAL_BUILD_OPTIONS: -IDEBuildOperationMaxNumberOfConcurrentCompileTasks=$MAX_NUMBER_TASK # only: # variables: # - $NIGHTLY_MASTER # - $DEPLOY_RUN_MACOSX # ################################################# # Package - Nightly ################################################# # WAIT for QT6 for arm64 job-macosx-makefile-package: stage: package tags: [ "macmini-m1-xcode13" ] dependencies: [] only: variables: - $NIGHTLY_MASTER - $PACKAGE_MACOSX - $DEPLOY_MACOSX variables: CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DENABLE_G729=ON -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$MACOSX_PLATFORM/$APP_FOLDER LINPHONESDK_MACOS_ARCHS: "x86_64" extends: job-macosx-makefile script: - *build_all_script artifacts: when: always paths: - build/OUTPUT/* when: always expire_in: 1 week job-macosx-codesigning: stage: signing tags: [ "macmini-m1-xcode13" ] needs: - job-macosx-makefile-package only: variables: - $NIGHTLY_MASTER - $PACKAGE_MACOSX - $DEPLOY_MACOSX script: - cd build - codesign --options runtime,library --verbose -s "$MACOS_SIGNING_IDENTITY" OUTPUT/Packages/*.dmg - ./../tools/app_notarization.sh artifacts: when: always paths: - build/OUTPUT/* when: always expire_in: 1 week ################################################# # Deploy - Nightly ################################################# job-macosx-makefile-deploy: stage: deploy tags: [ "macmini-m1-xcode13" ] needs: - job-macosx-codesigning only: variables: - $NIGHTLY_MASTER - $DEPLOY_MACOSX script: - rsync -rlv --ignore-existing build/OUTPUT/Packages/Linphone*.dmg $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM/$APP_FOLDER - rsync -rlv build/OUTPUT/RELEASE $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM - rsync -rlv build/OUTPUT/RELEASE $MAIN_DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM job-macosx-makefile-plugins-deploy: stage: deploy tags: [ "macmini-m1-xcode13" ] needs: - job-macosx-makefile only: variables: - $DEPLOY_PLUGINS script: - rsync -rlv --ignore-existing build/OUTPUT/plugins/app/*.dylib $DEPLOY_SERVER:$UPLOAD_ROOT_PATH/$MACOSX_PLATFORM/$APP_FOLDER/plugins linphone-desktop-5.0.2/.gitlab-ci-files/job-windows-desktop.yml000066400000000000000000000213271434616504300244610ustar00rootroot00000000000000################################################# # BUILD ################################################# .windows-vs2019: extends: .job-prepare stage: build tags: [ "windows-powershell" ] rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $DOCKER_UPDATE == null && $SKIP_WINDOWS == null - if: $CI_PIPELINE_SOURCE == "schedule" && $DOCKER_UPDATE == null && $SKIP_WINDOWS == null variables: CMAKE_OPTIONS: -DLINPHONE_WINDOWS_SIGN_TOOL=$WINDOWS_SIGN_TOOL -DLINPHONE_WINDOWS_SIGN_TIMESTAMP_URL=$WINDOWS_SIGN_TIMESTAMP_URL -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON LINPHONESDK_PLATFORM: Desktop OUTPUT_ZIP_FOLDER: win64 MINGW_TYPE: mingw64 CMAKE_GENERATOR: "Visual Studio 16 2019" CMAKE_ARCHITECTURE : -A x64 script: - Set-Variable -Name "PATH_TEMP" -Value ($(Get-ChildItem -Path Env:\PATH).value) - echo $env:Path #Remove MinGW of MSYS from PATH and add MINGW_TYPE for MSYS2 # We double the "\" to escape paths as -replace uses regular expressions - $PATH_TEMP = $PATH_TEMP -replace "C:\\MinGW\\bin;" -replace "C:\\Strawberry\\c\\bin;" -replace "C:\\Program Files\\NASM" - echo $PATH_TEMP - $env:Path = ($PATH_TEMP + ";C:\msys64;C:\msys64\usr\bin;C:\msys64\" + $MINGW_TYPE + "\bin;" + $env:SIGNTOOL_ROOT + "\x64") - If ($MINGW_TYPE -eq "mingw64") {Import-BatchEnvironment "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"} Else {Import-BatchEnvironment "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\VsDevCmd.bat"} - If ($MINGW_TYPE -eq "mingw64") {$env:Path = ($env:Path + ";C:\Qt\5.15.2\msvc2019_64\bin")} Else {$env:Path = ($env:Path + ";C:\Qt\5.14.2\msvc2019\bin")} - echo $env:Path - If ( Test-Path -Path "build-desktop" ) {Remove-Item -recurse -force -path "build-desktop" } - mkdir build-desktop - cd build-desktop #we launch the msvc-cl wrapper located in python scripts folder #this wrapper relays only needed calls to the real compiler #cache stats display - C:\PROGRA~1\Python37\Scripts\cl -s - Write-Output $CMAKE_ARCHITECTURE - Write-Output $SCHEDULE_CMAKE_OPTIONS - Write-Output $MAKEFILE_JOBS - Write-Output $CMAKE_C_COMPILER - Write-Output $CMAKE_CXX_COMPILER - Write-Output $CMAKE_RC_COMPILER - Write-Output $DEFAULT_CMAKE_OPTIONS - Write-Output $CMAKE_OPTIONS - Write-Output $CMAKE_ARCHITECTURE - Write-Output $SCHEDULE_CMAKE_OPTIONS - Write-Output $CMAKE_GENERATOR - Write-Output $LINPHONESDK_PLATFORM - Write-Output $MINGW_TYPE - Write-Output $CLI_OUTPUT_INFO_NINJA - Write-Output $Write-Output - Write-Output $NINJA_BUILD_PARALLEL_LEVEL - Write-Output $PARALLEL_OPTIONS - Write-Output $NINJA_OPTIMIZATION - Write-Output $NINJA_EXPLICIT_COMPILER_SET - Write-Output $DISPLAY_NINJA_LOG - Write-Output $SEARCH_NINJA_ERROR - Write-Output $DISPLAY_SEARCH_STATUS - Write-Output $SET_EXIT_CODE_BASED_ON_SEARCH_STATUS - Write-Output $SHOW_SEARCH_STATUS_SCRIPT - Write-Output $LAUNCH_SEARCH_STATUS_SCRIPT #We are forced to use Invoke-Expression to explain to powershell that we don't want it to touch to spaces in arguments #If we don't use it, '-A Win32' will be interpreted as "-A ' Win32'" thus making the build fail - echo $LastExitCode - Invoke-Expression "& cmake .. -G '$CMAKE_GENERATOR' -DLINPHONESDK_PLATFORM=$LINPHONESDK_PLATFORM -DENABLE_CSHARP_WRAPPER=YES -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $NINJA_BUILD_PARALLEL_LEVEL $NINJA_EXPLICIT_COMPILER_SET $DEFAULT_CMAKE_OPTIONS $DEFAULT_WINDOWS_CMAKE_OPTIONS $CMAKE_OPTIONS $CMAKE_ARCHITECTURE $SCHEDULE_CMAKE_OPTIONS" - echo $LastExitCode - 'if (-not ($LastExitCode -eq 0)) {throw "Error: Configure failed"}' - $CLI_OUTPUT_INFO_NINJA #Warning : Ninja doesn't return an error code on Linker error. #Store outputs in a file log #Only in powershell 7 (Gitlab 14+) # - cmake --build . --target install --config RelWithDebInfo --parallel $MAKEFILE_JOBS | Select-String -NotMatch -Raw -Pattern "inclusion du fichier" - Write-Output $PARALLEL_OPTIONS - Write-Output $NINJA_OPTIMIZATION # /!\ By design, we must keep $NINJA_OPTIMIZATION as the last option of the line, because it contains an output redirect - echo $LastExitCode - Invoke-Expression "cmake --build . --target $BUILD_TARGET --config $CI_BUILD_TYPE $PARALLEL_OPTIONS $NINJA_OPTIMIZATION" - if ($EXITS_ON_ERROR_MSVC) { Invoke-Expression "$EXITS_ON_ERROR_MSVC" } else { Write-Output "EXITS_ON_ERROR_MSVC is null" } - if ($DISPLAY_NINJA_LOG) { Invoke-Expression "$DISPLAY_NINJA_LOG" } else { Write-Output "DISPLAY_NINJA_LOG is null" } - if ($SEARCH_NINJA_ERROR) { Invoke-Expression "$SEARCH_NINJA_ERROR" } else { Write-Output "SEARCH_NINJA_ERROR is null" } - if ($DISPLAY_SEARCH_STATUS) { Invoke-Expression "$DISPLAY_SEARCH_STATUS" } else { Write-Output "DISPLAY_SEARCH_STATUS is null" } - if ($SET_EXIT_CODE_BASED_ON_SEARCH_STATUS) { Invoke-Expression "$SET_EXIT_CODE_BASED_ON_SEARCH_STATUS" } else { Write-Output "SET_EXIT_CODE_BASED_ON_SEARCH_STATUS is null" } - if ($SHOW_SEARCH_STATUS_SCRIPT) { Invoke-Expression "$SHOW_SEARCH_STATUS_SCRIPT" } else { Write-Output "SHOW_SEARCH_STATUS_SCRIPT is null" } - if ($LAUNCH_SEARCH_STATUS_SCRIPT) { Invoke-Expression "$LAUNCH_SEARCH_STATUS_SCRIPT" -ErrorAction stop } else { Write-Output "LAUNCH_SEARCH_STATUS_SCRIPT is null" } - C:\PROGRA~1\Python37\Scripts\cl -s - cd linphone-sdk - mkdir $OUTPUT_ZIP_FOLDER - Copy-Item -Path "*.zip" -Destination "$CI_PROJECT_DIR/build-desktop/linphone-sdk/$OUTPUT_ZIP_FOLDER" -Recurse ################ artifacts: paths: - build-desktop\ninja_buildlog.txt - build-desktop\ninja_buildlog.txt - build-desktop\invertSearch.ps1 - build-desktop\OUTPUT\* when: always expire_in: 1 week .windows-vs2019-msvc: extends: .windows-vs2019 variables: CMAKE_OPTIONS: -DENABLE_UNIT_TESTS=ON -DLINPHONE_WINDOWS_SIGN_TOOL=$WINDOWS_SIGN_TOOL -DLINPHONE_WINDOWS_SIGN_TIMESTAMP_URL=$WINDOWS_SIGN_TIMESTAMP_URL -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON LINPHONESDK_PLATFORM: Desktop CMAKE_GENERATOR: "Visual Studio 16 2019" BUILD_TARGET: install PARALLEL_OPTIONS: "-- /maxcpucount /nodeReuse:true /p:TrackFileAccess=false" EXITS_ON_ERROR_MSVC: 'if (-not ($$LastExitCode -eq 0)) {throw "Error: Build failed"}' .windows-vs2019-scheduled: extends: .windows-vs2019-msvc rules: - if: $NIGHTLY_MASTER - if: $NIGHTLY_RELEASE - if: $ENABLE_WINDOWS_TESTS before_script: #cache disabled on scheduled builds since we dot not need the fastest build - Set-Variable -Name "CLCACHE_DISABLE" -Value 1 ###################################################### # JOBS ###################################################### vs2019-msvc-win64-windows: extends: .windows-vs2019-msvc variables: CMAKE_C_COMPILER : cl.exe CMAKE_CXX_COMPILER : cl.exe CMAKE_RC_COMPILER : rc.exe ###################################################### # NIGHTLY ###################################################### ## ON SCHEDULE ## vs2019-win64-scheduled-windows: extends: .windows-vs2019-scheduled rules: - if: $NIGHTLY_MASTER - if: $NIGHTLY_RELEASE - if: $DEPLOY_RUN_WINDOWS ################################################# # PACKAGE ################################################# #Remove . when packaging process is ready to use vs2019-win64-package: stage: package extends: .windows-vs2019-msvc dependencies: [] rules: - if: $NIGHTLY_MASTER - if: $NIGHTLY_RELEASE - if: $PACKAGE_WINDOWS - if: $DEPLOY_WINDOWS variables: CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=YES -DLINPHONE_WINDOWS_SIGN_TOOL=$WINDOWS_SIGN_TOOL -DLINPHONE_WINDOWS_SIGN_TIMESTAMP_URL=$WINDOWS_SIGN_TIMESTAMP_URL -DENABLE_G729=ON -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$WINDOWS_PLATFORM/$APP_FOLDER -DENABLE_PQCRYPTO=ON ################################################# # DEPLOY ################################################# vs2019-win64-upload: stage: deploy tags: [ "windows"] rules: - if: $NIGHTLY_MASTER - if: $DEPLOY_WINDOWS needs: - vs2019-win64-package script: - scp "build-desktop/OUTPUT/Packages/*.exe" "%DEPLOY_SERVER%:%UPLOAD_ROOT_PATH%/%WINDOWS_PLATFORM%/%APP_FOLDER% - scp "build-desktop/OUTPUT/RELEASE" "%DEPLOY_SERVER%:%UPLOAD_ROOT_PATH%/%WINDOWS_PLATFORM%/ - scp "build-desktop/OUTPUT/RELEASE" "%MAIN_DEPLOY_SERVER%:%UPLOAD_ROOT_PATH%/%WINDOWS_PLATFORM%/ vs2019-win64-plugins-upload: stage: deploy tags: [ "windows"] rules: - if: $DEPLOY_PLUGINS needs: - vs2019-win64-scheduled-windows script: - scp "build-desktop/OUTPUT/plugins/app/*.dll" "%DEPLOY_SERVER%:%WINDOWS_UPLOAD_DIRECTORY%/plugins" linphone-desktop-5.0.2/.gitlab-ci.yml000066400000000000000000000042141434616504300174440ustar00rootroot00000000000000################################################# # Base configuration ################################################# variables: GIT_SUBMODULE_STRATEGY: recursive MAKEFILE_JOBS: 5 CCACHE_SIZE: 2G #this option is used to speedup submodule building times, when we don't need to trace debug (like SDK where it is already tested in its project) LBC_NODEBUG_OPTIONS : --parallel $MAKEFILE_JOBS DEFAULT_LINUX_CMAKE_OPTIONS: -DCMAKE_BUILD_PARALLEL_LEVEL=$MAKEFILE_JOBS DEFAULT_MACOS_CMAKE_OPTIONS: -DCMAKE_BUILD_PARALLEL_LEVEL=$MAKEFILE_JOBS DEFAULT_WINDOWS_CMAKE_OPTIONS: -DCMAKE_BUILD_PARALLEL_LEVEL=$MAKEFILE_JOBS # DEFAULT_LINUX_CMAKE_OPTIONS: -DENABLE_NON_FREE_CODECS=YES -DENABLE_OPENH264=YES # DEFAULT_MACOS_CMAKE_OPTIONS: -DCMAKE_OSX_DEPLOYMENT_TARGET=10.11 -DENABLE_UPDATE_CHECK=YES # DEFAULT_WINDOWS_CMAKE_OPTIONS: -DENABLE_NON_FREE_CODECS=YES -DENABLE_OPENH264=YES -DENABLE_UPDATE_CHECK=YES #activated by default, if there is a problem, see the default #build options in CMakeBuilder #CMAKE_OPTIONS: -DENABLE_LIME_X3DH=YES # Docker image version ARCHLINUX_IMAGE_VERSION: latestupdated CENTOS_7_QT_IMAGE_VERSION: 20211012_add_qtwebview DEBIAN_9_QT_IMAGE_VERSION: 20211027_update_qt_5.12.12 DEBIAN_10_IMAGE_VERSION: 20210217_python3 UBUNTU_ROLLING_IMAGE_VERSION: 20211012_add_qtwebview workflow: rules: - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push" when: never #Launch merge request pipeline is there is a merge request open - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE !~ /^Draft:.*/ #Launch pipeline if there is a schedule event - if: $CI_PIPELINE_SOURCE == "schedule" ################################################# # Platforms to test ################################################# .job-prepare: variables: ALL_JOB_VARIABLE: "" include: - '.gitlab-ci-files/job-linux-prepare.yml' - '.gitlab-ci-files/job-linux-desktop.yml' - '.gitlab-ci-files/job-linux-desktop-debian9.yml' - '.gitlab-ci-files/job-windows-desktop.yml' - '.gitlab-ci-files/job-macosx-desktop.yml' stages: - build - package - signing - deploy linphone-desktop-5.0.2/.gitmodules000066400000000000000000000004401434616504300171620ustar00rootroot00000000000000[submodule "linphone-sdk"] path = linphone-sdk url = https://gitlab.linphone.org/BC/public/linphone-sdk.git [submodule "plugins/contacts/contacts-api"] path = plugins/contacts/contacts-api url = https://gitlab.linphone.org/BC/public/linphone-desktop-plugins/contacts/contacts-api.git linphone-desktop-5.0.2/.tx/000077500000000000000000000000001434616504300155205ustar00rootroot00000000000000linphone-desktop-5.0.2/.tx/config000066400000000000000000000003021434616504300167030ustar00rootroot00000000000000[main] host = https://www.transifex.com [linphone-desktop.ts] file_filter = linphone-app/assets/languages/.ts source_file = linphone-app/assets/languages/en.ts source_lang = en type = QT linphone-desktop-5.0.2/CHANGELOG.md000066400000000000000000000413351434616504300166260ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 5.0.2 - 2022-12-13 ### Fixed - Default Language didn't match with the system language (Qt bug). ## 5.0.1 - 2022-12-09 ### Fixed - RF3987 to allow IRI parsing in chats. - Image display in chats from an URL. - Display a notification of all kind of messages. ## 5.0.0 - 2022-12-07 ### Added - Video conference and iCalendars. - Make a meeting directly from a group chat. - New call layouts. - Display a waiting room before going into a conference. - Log viewer. - Read contacts from all friends lists. - Option to set the display name in "using an account" tab of assistant. - Long pressed buttons. - Date and Time pickers. - Phone dialpad on main window. - Animated file in chats/notifications. - Round progress bar for transferring a file and allow to cancel it. - Hide all accounts if their custom parameter 'hidden' is set to 1. - Right-click on a timeline will show a slide menu to do actions on the timeline. - Post quantum ZRTP. - Windows stack trace dumps into logs on crash. - Mark as Read synchronized between devices. - Merge messages into one notification to avoid spam. - Design overhaul on calls. - Audio devices can be changed while being in call. - Use a cryptographic checksum when downloading openH264 from CISCO (Update to 2.2.0) ### Fixed - Crash on exit. - Crash when using no account. - Many Windows crashs (camera, incall) - Memory stability. - Clean 3 chat behaviors : Leave chat room (in group info section of conversation menu), erase history (in conversation's menu), delete chat room (in slide menu, or if chat room is empty and left) - On Mac, close windows instead of minimizing them. - Running application detection on Install/Uninstall. - SVG Icons in better quality. - Event timestamps. - Optimizations and more minor fixes. ## 4.4.10 - 2022-09-20 ### Fixes - Lime exceptions because of unknown boundaries. - AppimageTool update for code signing. ## 4.4.9 - 2022-08-29 ### Fixes - Update SDK to fix a crash on startup due to a test on a removed participant device. - Use default values for new accounts in settings panel. ### Added - Add 'sip' scheme in authentication popup. ## 4.4.8 - 2022-07-05 ### Fixes - Display name are based on friends (coming from local or LDAP server) and caller address only. - Running application detection for uninstalling. ## 4.4.7 - 2022-07-01 ### Fixes - When receiving a SIP URL, copy it in Smart search bar instead of openning conversation. - Update SDK to prepare video conference and improve DTLS handshakes. ## 4.4.6 - 2022-06-14 ### Fixed - Url version check and selection synchronisation. - Show display name of the caller if it exists instead of call logs. ## 4.4.4 - 2022-06-01 ### Fixed - Revert ordering messages from receiving time. - Some crashes on Wasapi. - Update SDK to 5.1.41 ## 4.4.3 - 2022-05-30 ### Fixed - Crash on searchs with special characters - Update SDK to 5.1.38 ## 4.4.2 - 2022-05-25 ### Added - Based on LinphoneSDK 5.1.36 - Add Sanitizer build. - Version types selection for version checker. ### Fixed - Order messages from receiving time. - Fix H264 download URL on Linux. - Hide Admin status in One-to-one chats. ## 4.4.1 - 2022-04-06 ### Fixed - Fix codec downloading on Windows and popup progress bar. ## 4.4.0 - 2022-04-04 ### Added - Features: * Messages features : Reply, forward (to contact, to a SIP address or to a timeline), Vocal record and play, multi contents, preview. - Add a feedback on fetching remote provisioning when it failed. - Option to enable message notifications. - CPIM on basic chat rooms. - Device name can be changed from settings. - New event on new messages in chat and a shortcut to go to the end of chat if last message is not shown. - Shortcut in Reply to message's origin. - Allow redirected downloads (bzip2/OpenH264) - Auto-download message files, editable in settings (10Mb as default) - 64bits application on Windows - Based on Linphone SDK 5.1 ### Fixed - Simplify filtering timelines with 2 modes (minimal or exhaustive) and on 3 kind of search : security level, simple/group chats, ephemerals. - Sort timelines by taken account of unread events in chat rooms. - Fix systemTrayIcon that could be cloned on each restart. - Fix thumbnails display in notification. - Fix errors on Action-Buttons on restart. - Enable G729 on public builds. - Take account of return key on Numpad. - Huge messages are better shown and with less flickering. - High CPU consumption on idle state. - Hide deleted/terminated chat rooms. - Adapt UserAgent with device name. - Video freeze on network change. - Support OpenGL 4.1 and GLSL 4.10. - Fix some glitches on Apple M1. - Audio errors in settings when using different audio format between input and output. - Set default log size to 50MB - Reduce ICE candidates on Windows. - Show logs in console on Windows. - Crash on the smart search bar. ## 4.3.2 ### Fixed - ALSA volumes can be view/changed while being in call. - Remove constraints on actions (call/chat) that were based on friends capabilities. - Unblock secure group chat activation. - Unselect current contact if history call view is displayed. - Show chat actions in history view. - Group chat creation : If no groupchat capabilities has been found in recent contacts, ignore test on capability and display them. ## 4.3.1 - 2021-11-04 ### Added - Features: * New version behavior : Manual check for new version, option to activate the automatic check and a way to set the URL. * A banner is shown when copying text. * Options to enable standard and secure chats. * Add tunnel support if build. * Overhaul of color managment and use monochrome images. * Change Contact Edit and SIP Addresses selections to start a standard chat or a secure one. * Call history button in the timeline panel. * Timeout of incoming call notification is now based on `inc_timeout` * More actions in contact edit panel (call/video call). * Allow to make a readonly variable in configuration (only for enabling chats yet). ### Fixed - Better quality of icons. - Crash on start and on exit. - Allow to use a secure chat room to be used when calling (set by context : encrypted call/secure chat enabled). - History buttons that should not appear if chat room mode is not activated. - Keep the fullscreen mode when receiving a notification. - Clicking on the fullscreen action on the call window will go to the fullscreen if exists. - Fix scrolling speed and add a cache in lists. - Fix Mac crash by adding an option to deactivate mipmap. - Add more translations. - Mac: Enable automatic graphics switching indicating whether an OpenGL app may utilize the integrated GPU. - Version checking that could request an update to older version. - A crash on authentication with empty configs. - Main search with UTF8 - When requested, remove all history of a chat room and not only desplayed entries. - Fix missing qml variables. - Add more debug logs. - Use macqtdeploy when building in order to use binary without having enabling packaging. ## 4.3.0 - 2021-10-20 ### Added - Features: * Chat groups with administrator mode, participants management and devices display. * Secure chat rooms for 1-1 and group chat using LIME end-to-end encryption. * Ephemerals Chat rooms (per-participant mode). * Attended transfer. * LDAP integration: settings allow remote LDAP servers to be configured. Contacts can then be searched in the smart search bar, and during incoming call the display name of the caller is automatically retrieved from the LDAP server. * Address book connectors : custom plugins can now be imported from settings in order to be used to synchronize contacts. - Enhance user experience : * Show subject in notifications for group chats. * Attended transfer. * Chat area is no more fixed but adapts to content. * Click on notification bubble in top left account lead to the call history view. * Double-Click on avatar in conversation to fill the smart search bar with the participant address. * Allow to hide or show the timeline panel. * Allow to hide or show empty chat rooms in settings. * Messages font can now be changed in settings. * Sort contact list using System Locale. * In fullscreen mode, the preview size can be changed by using mouse wheel. * Echo calibration in settings view. * Autostart for AppImage. * Add more tooltips. * Add a forgotten password link in assistant. - Search and filtering features: * Search in timeline from subject/addresses. * Search in messages. * Filter timelines by the kind of chat rooms (1-1, group chats) and modes (secure and ephemerals). - Chat room management: * Updatable subject by clicking on it. * Upgrade security level by authenticating participants. * Add more events in chat rooms like chat rooms status, participants updates, security level updates, ephemerals activations. - In Chat, allow custom menu to appear by removing the repeating key when holding it. On Mac, there is an accent menu for this feature. - Add URI handler configuration : `linphone-config` to fetch a configuration file. - Fetch a configuration file from a CLI command/URI Handlers : * sip:user@domain?method=call&fetch-config=base64(scheme://url) * linphone-config://url * linphone-config:fetch-config=base64(scheme://url) * linphone --fetch-config=scheme://url * linphone " fetch-config=scheme://url" - Options to audio codec can be used and stored. - Devices can be selected in linphone configuration file from a regex rule. - Opus can now use `packetlosspercentage` and `useinbandfec` configuration. - A silence file have been added : `silence.mkv` and can be used to switch off some musics (hold_music). - Use of new mediastreamer2 MSQOgl filter as video display backend (based on QQuickFramebufferObject). - MSYS2 support for Windows. ### Fixed - Cursor shape of mouse is changed when hovering on buttons. - When clicking on a chat notification, it will close it. - Persistent call bubble notifications. - Fix on Missed calls and messages count bubbles. - Unmatched room when using malformed username. - Contact names handle special characters. - UTF8 characters on Windows. - Mark as Read only if in foreground. - Show avatar and username once for a same kind of message. - Load optimizations. - Refactoring data modelisation and colors management. - On Mac : Camera freeze and black screen when using third-party. - Prevent opening call Window if the option to stay in background has been activated. - Crash while searching contacts. - Stop receiving messages when proxy has been deleted. - Transfer menu of calls : Dynamic size for texts. - XCode build wasn't fully supported. - Sort languages in the UI settings. ## 4.2.5 - 2020-12-18 ### Added -iLBC support ### Fixed - VP8 freeze - Audio quality distortion - OSX deployment target propagated to linphone SDK ## 4.2.4 - 2020-11-21 ### Added - Play DTMF when receiving it and show the Dialpad on outgoing call to allow sending DTMF - Transport protocol deactivation has been replaced by not listening ports - Show all call logs when clicking on the `previously` bar in the left panel - A call log can be used to callback or add the contact in friends list ### Fixed - Displaying names in UTF8 - Keep unsend typed message in memory when changing of chat room - Log files have Qt logs - Missing `sqlite3` backend - Use the more generic `linphone` folder and not `Linphone` for installation - Simplify build process to use install keyword - Links errors like liblinphone++.so.10 ## 4.2.3 - 2020-10-09 ### Added - Add support to tel and callto protocols - Allow Pulseaudio to switch devices automatically. For example, it will mute all applications that have music when receive a call from Linphone. ### Fixed - Contact name can contain special characters - Avoid to reduce window if it is currently maximized when clicking on contacts - Cleaner use of Windows registries ## 4.2.2 - 2020-07-30 ### Fixed - Crash on Opus ## 4.2.1 - 2020-07-03 ### Fixed - Crash on authentifications - Multiple Popups are no longer ignored and are open in a StackView. ## 4.2.0 - 2020-06-26 ### Added - Added a `CLI` function in order to support `URI handlers` from browsers. Help is available with `linphone --cli-help`. (See also: https://wiki.linphone.org/xwiki/wiki/public/view/Linphone/URI%20Handlers%20%28Desktop%20only%29/). - Improved general audio/video quality thanks to better rate control algorithms in liblinphone and mediastreamer2. - More efficient echo cancellation. - `OpenH264` codec can be downloaded and used in the application from Cisco website. - `G729` codec can be used in the application. - Improved High DPI Displays support for 4K screens. - On multiscreens, when choosing full screen mode during a call, the call screen open in the current screen. The old behaviour kept the call screen in the primary screen. - Detect audio/video hardware changes while using settings. - Updatable audio/video devices while in call. - Added an option to automatically show Dialpad. - Dialpad supports A, B, C and D keys. - Dialpad supports keyboard when hovering on it. - DTMF sound played when sent. - Added an option to keep windows in background when a call is received. - Added an option to allow Linphone to be launched automatically with the system (autostart). - Added an option to play sound notification when an incoming chat message is received. - Added Call tools in Fullscreen mode (medias settings, security, mutable speaker). - Audio settings display the microphone being used and allow you to adjust capture and playback gains. - Conference participants are mutable by clicking on them. - Added the possibility to record calls automatically. - Moved logs folder without restart. - Added caller and callee information into file names of recordings. - Enhanced interface for switching between multiple SIP accounts: the timeline now shows activity for the currently selected SIP account only. - Timeline uses current proxy config info and show data only on selected profile. - Tooltips can be shown in multiple lines. - Display the name of the caller in incoming notifications. - Notifications are shown in all available screens. - Display unread message count in system tray (Linphone icon). - Display unread chat message count and missed calls in `Manage Accounts` dialog and in `Main Window`. - Added a media parameter dialog in the `Call View` to select devices and set volume. - Display a spinner when a message is being sent. - Disabled screensaver on fullscreen video call. - New logo, icons and installer assets. - New Linux deployment (Appimage). - Supports chinese, danish, french, english, german, hungarian, italian, japanese, lithuanian, portuguese, russian, spanish, swedish, turkish, ukrainian from community contributions. - Use Native BZip2 instead of Embedded Minizip to extract `OpenH264` codec. - App Nap avoiding for MacOs. - Simplified building process. ### Changed - Upgraded to use QT 5.12. - Depends on linphone-sdk project (numerous direct submodules removed). - License changed from GPLv2 to GPLv3. ### Fixed - Removed `:` separator from file names of recordings because it is not allowed on Windows. - Avoided mark `as read` on selected chat rooms if window is not active. - Search box in main page will not reset text when clicking on it. - More stable account authentifications. - Message status behaviour : Resuming status when changing logs, cursor shapes updates, bind the resend message action to error icon. - Apple permissions that could lead to muted microphone. - Incoming call notification window (sometimes not showing). ### Removed - `Prepare.py` configuration. - Remove useless splashscreen. - `Minizip` dependencies. - `Flatpak` support. ## 4.1.0 - 2017-07-19 ### Added - Add tooltips on `recording` and `screenshot` buttons in `Calls Window`. - Show notifications on `recording` and `screenshot`. - Show `XXX is typing...` in `Timeline` and `Chat View`. - Handle correctly `SIGINT`. - Handle clicks on SIP URI in chat messages. - Show video framerate in `Calls Stats`. - Add a `Logs` menu entry in `Settings Window`, it provides send, remove, activate buttons... - Supports EXIF orientation for file transfer images preview. - Echo canceller supports 48kHz. - Better GUI when a proxy config is modified in `Settings Window`. ### Fixed - Handle correctly ringer device changes in `Settings Window`. - In `Video Settings`, display FPS field only in `custom preset` mode. - Use now the directory containing user documents files for saved video/audio/screenshots. - Update `Chat View` correctly if it is used in many windows. - Update correctly selected language when app is restarted. - Avoid a deadlock on Mac OS when a call ends in fullscreen mode. - Application can be started from one binary only. - Single instance is now supported with flatpak. (It uses D-Bus.) linphone-desktop-5.0.2/CMakeLists.txt000066400000000000000000000316761434616504300175640ustar00rootroot00000000000000################################################################################ # # Copyright (c) 2010-2020 Belledonne Communications SARL. # # This file is part of linphone-desktop # (see https://www.linphone.org). # # 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 . # ################################################################################ cmake_minimum_required(VERSION 3.1) get_cmake_property(vars CACHE_VARIABLES) foreach(var ${vars}) get_property(currentHelpString CACHE "${var}" PROPERTY HELPSTRING) if("${currentHelpString}" MATCHES "No help, variable specified on the command line." OR "${currentHelpString}" STREQUAL "") #message("${var} = [${${var}}] -- ${currentHelpString}") # uncomment to see the variables being processed list(APPEND USER_ARGS "-D${var}=${${var}}") if( "${var}" STREQUAL "CMAKE_PREFIX_PATH") set(PREFIX_PATH ";${${var}}") endif() elseif("${var}" STREQUAL "CMAKE_GENERATOR_PLATFORM" AND NOT("${${var}}" STREQUAL "")) message(STATUS "User-Setting Platform to ${${var}}") endif() endforeach() if(ENABLE_BUILD_VERBOSE) message("User Args : ${USER_ARGS}") endif() if( APPLE ) if( NOT CMAKE_OSX_DEPLOYMENT_TARGET) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum OS X deployment version") endif() endif() project(linphoneqt) include(GNUInstallDirs) include(CheckCXXCompilerFlag) set(CMAKE_CXX_STANDARD 11) # Prepare gobal CMAKE configuration specific to the current project set(SDK_BUILD_DIR "${CMAKE_BINARY_DIR}/WORK") # SDK build in WORK. Keep all in it. set(LINPHONE_OUTPUT_DIR "${CMAKE_BINARY_DIR}/linphone-sdk/desktop") set(APPLICATION_OUTPUT_DIR "${CMAKE_BINARY_DIR}/OUTPUT") set(CMAKE_PREFIX_PATH "${LINPHONE_OUTPUT_DIR};${APPLICATION_OUTPUT_DIR};${APPLICATION_OUTPUT_DIR}/include${PREFIX_PATH}") if(WIN32) set( CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${APPLICATION_OUTPUT_DIR}/${CMAKE_INSTALL_BINDIR}") elseif(APPLE) set( CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${APPLICATION_NAME}.app/Contents/Frameworks") else() set( CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${APPLICATION_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}") endif() string(REPLACE ";" "|" PREFIX_PATH "${CMAKE_PREFIX_PATH}") #set(PREFIX_PATH "${LINPHONE_OUTPUT_DIR}|${APPLICATION_OUTPUT_DIR}${PREFIX_PATH}") # Avoid cmake warning if CMP0071 is not set. if (POLICY CMP0071) cmake_policy(SET CMP0071 NEW) endif () #set_property(GLOBAL PROPERTY USE_FOLDERS ON) #------------------------------------------------------------------------------ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified") set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo" FORCE) # Set the available build type values for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo") endif() # ------------------------------------------------------------------------------ #------------------------------------------------------------------------------- # SET OPTIONS #------------------------------------------------------------------------------- option(ENABLE_APP_LICENSE "Enable the license in packages." YES) option(ENABLE_APP_PACKAGING "Enable packaging" NO) option(ENABLE_APP_PACKAGE_ROOTCA "Embed the rootca file into the package" YES) option(ENABLE_APP_WEBVIEW "Enable webviews." NO) #Webview is not fully supported because of deployments. Used for subscription. option(ENABLE_BUILD_APP_PLUGINS "Enable the build of plugins" YES) option(ENABLE_BUILD_EXAMPLES "Enable the build of examples" NO) option(ENABLE_BUILD_VERBOSE "Enable the build generation to be more verbose" NO) option(ENABLE_DAEMON "Enable the linphone daemon interface." NO) option(ENABLE_FFMPEG "Build mediastreamer2 with ffmpeg video support." ON) option(ENABLE_SANITIZER "Enable sanitizer." NO) option(ENABLE_STRICT "Build with strict compilator flags e.g. -Wall -Werror" NO) option(ENABLE_TESTS "Build with testing binaries of SDK" NO ) option(ENABLE_TESTS_COMPONENTS "Build libbctoolbox-tester" NO ) option(ENABLE_TOOLS "Enable tools of SDK" NO) option(ENABLE_UNIT_TESTS "Enable unit test of SDK." NO ) option(ENABLE_UPDATE_CHECK "Enable update check." YES) option(ENABLE_OPENH264 "Enable the use of OpenH264 codec" YES) option(ENABLE_NON_FREE_CODECS "Enable the use of non free codecs" YES) option(ENABLE_VIDEO "Enable Video support." YES) option(ENABLE_LDAP "Enable LDAP support." YES) option(ENABLE_CONSOLE_UI "Turn on or off compilation of console interface." NO) option(LINPHONE_SDK_MAKE_RELEASE_FILE_URL "Make a RELEASE file that work along check_version and use this URL" "") if(WIN32 OR APPLE) else() option(ENABLE_V4L "Ability to capture and display video using libv4l2 or libv4l." YES) endif() option(ENABLE_RELATIVE_PREFIX "Set Internal packages relative to the binary" YES) set(APP_OPTIONS "-DENABLE_UPDATE_CHECK=${ENABLE_UPDATE_CHECK}") list(APPEND APP_OPTIONS "-DENABLE_APP_LICENSE=${ENABLE_APP_LICENSE}") list(APPEND APP_OPTIONS "-DENABLE_APP_PACKAGING=${ENABLE_APP_PACKAGING}") list(APPEND APP_OPTIONS "-DENABLE_APP_PACKAGE_ROOTCA=${ENABLE_APP_PACKAGE_ROOTCA}") list(APPEND APP_OPTIONS "-DENABLE_APP_WEBVIEW=${ENABLE_APP_WEBVIEW}") list(APPEND APP_OPTIONS "-DENABLE_BUILD_EXAMPLES=${ENABLE_BUILD_EXAMPLES}") list(APPEND APP_OPTIONS "-DENABLE_BUILD_VERBOSE=${ENABLE_BUILD_VERBOSE}") list(APPEND APP_OPTIONS "-DENABLE_CONSOLE_UI=${ENABLE_CONSOLE_UI}") list(APPEND APP_OPTIONS "-DENABLE_DAEMON=${ENABLE_DAEMON}") list(APPEND APP_OPTIONS "-DENABLE_FFMPEG=${ENABLE_FFMPEG}") list(APPEND APP_OPTIONS "-DENABLE_LDAP=${ENABLE_LDAP}") list(APPEND APP_OPTIONS "-DENABLE_NON_FREE_CODECS=${ENABLE_NON_FREE_CODECS}") list(APPEND APP_OPTIONS "-DENABLE_OPENH264=${ENABLE_OPENH264}") list(APPEND APP_OPTIONS "-DENABLE_SANITIZER=${ENABLE_SANITIZER}") list(APPEND APP_OPTIONS "-DENABLE_STRICT=${ENABLE_STRICT}") list(APPEND APP_OPTIONS "-DENABLE_TESTS=${ENABLE_TESTS}") list(APPEND APP_OPTIONS "-DENABLE_TESTS_COMPONENTS=${ENABLE_TESTS_COMPONENTS}") list(APPEND APP_OPTIONS "-DENABLE_TOOLS=${ENABLE_TOOLS}") list(APPEND APP_OPTIONS "-DENABLE_UNIT_TESTS=${ENABLE_UNIT_TESTS}") list(APPEND APP_OPTIONS "-DENABLE_VIDEO=${ENABLE_VIDEO}") if(LINPHONE_SDK_MAKE_RELEASE_FILE_URL) list(APPEND APP_OPTIONS "-DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=${LINPHONE_SDK_MAKE_RELEASE_FILE_URL}") endif() if(LINPHONESDK_MACOS_ARCHS) list(APPEND APP_OPTIONS "-DLINPHONESDK_MACOS_ARCHS=${LINPHONESDK_MACOS_ARCHS}") endif() if(ENABLE_V4L) list(APPEND APP_OPTIONS "-DENABLE_V4L=${ENABLE_V4L}") endif() list(APPEND APP_OPTIONS "-DENABLE_RELATIVE_PREFIX=${ENABLE_RELATIVE_PREFIX}") list(APPEND APP_OPTIONS "-DLINPHONE_OUTPUT_DIR=${LINPHONE_OUTPUT_DIR}") list(APPEND APP_OPTIONS "-DENABLE_QT_GL=${ENABLE_VIDEO}")#Activate on video include(ExternalProject) set(PROJECT_BUILD_COMMAND "") if(CMAKE_BUILD_PARALLEL_LEVEL) list(APPEND APP_OPTIONS "-DCMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL}") if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0") #CMAKE_BUILD_PARALLEL_LEVEL will not always work for External projects list(APPEND PROJECT_BUILD_COMMAND "--parallel" "${CMAKE_BUILD_PARALLEL_LEVEL}") endif() endif() if(CMAKE_VERBOSE_MAKEFILE) list(APPEND APP_OPTIONS "-DCMAKE_VERBOSE_MAKEFILE=${CMAKE_VERBOSE_MAKEFILE}") if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0") list(APPEND PROJECT_BUILD_COMMAND "--verbose") endif() endif() if(UNIX AND NOT APPLE) set(CMAKE_INSTALL_RPATH "$ORIGIN:$ORIGIN/lib64:$ORIGIN/../lib64:$ORIGIN/lib:$ORIGIN/../lib:${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}") list(APPEND APP_OPTIONS "-DCMAKE_INSTALL_RPATH=${CMAKE_INSTALL_RPATH}") elseif(APPLE) list(APPEND APP_OPTIONS "-DENABLE_FAT_BINARY=ON") #Disable XCFrameworks as it is not supported. endif() if(CMAKE_OSX_DEPLOYMENT_TARGET) list(APPEND APP_OPTIONS "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}") endif() list(APPEND APP_OPTIONS "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}") if(ENABLE_BUILD_APP_PLUGINS) file(GLOB children "plugins/*") set(dirlist "") foreach(child ${children}) if(IS_DIRECTORY ${curdir}/${child} AND (ENABLE_BUILD_EXAMPLES OR NOT ${child} MATCHES "example")) list(APPEND dirlist ${child}) endif() endforeach() list(LENGTH dirlist count) if(NOT count) set(ENABLE_BUILD_APP_PLUGINS OFF) message(STATUS "No plugins found for the application to build") endif() endif() if(NOT LINPHONE_QT_ONLY) ExternalProject_Add(sdk PREFIX "${CMAKE_BINARY_DIR}/sdk" SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-sdk" INSTALL_DIR "${LINPHONE_OUTPUT_DIR}" STAMP_DIR "${SDK_BUILD_DIR}/stamp" BINARY_DIR "${SDK_BUILD_DIR}" STEP_TARGETS build BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ ${PROJECT_BUILD_COMMAND} INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time." LIST_SEPARATOR | # Use the alternate list separator CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${PREFIX_PATH} BUILD_ALWAYS NO #${DO_BUILD} ) ExternalProject_Add_Step(sdk force_build COMMENT "Forcing build for 'desktop'" DEPENDEES configure DEPENDERS build ALWAYS 1 ) endif() include(FindPkgConfig) set(APP_DEPENDS sdk) find_package(Qt5 5.10 COMPONENTS Core REQUIRED) if ( NOT Qt5_FOUND ) message(FATAL_ERROR "Minimum supported Qt5 version is 5.10!") endif() find_package(LinphoneCxx CONFIG QUIET) find_package(Linphone CONFIG QUIET) find_package(bctoolbox CONFIG QUIET) find_package(belcard CONFIG QUIET) find_package(Mediastreamer2 CONFIG QUIET) find_package(ortp CONFIG QUIET) if(NOT (LinphoneCxx_FOUND) OR NOT (Linphone_FOUND) OR NOT (bctoolbox_FOUND) OR NOT (belcard_FOUND) OR NOT (Mediastreamer2_FOUND) OR NOT (ortp_FOUND) OR FORCE_APP_EXTERNAL_PROJECTS) message("Projects are set as External projects. You can start building them by using for example : cmake --build . --target install") ExternalProject_Add(linphone-qt PREFIX "${CMAKE_BINARY_DIR}/linphone-app" SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-app" INSTALL_DIR "${APPLICATION_OUTPUT_DIR}" BINARY_DIR "${CMAKE_BINARY_DIR}/linphone-app" DEPENDS ${APP_DEPENDS} BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ ${PROJECT_BUILD_COMMAND} INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time." LIST_SEPARATOR | # Use the alternate list separator CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${PREFIX_PATH} # ${APP_OPTIONS} BUILD_ALWAYS ON ) if( ENABLE_BUILD_APP_PLUGINS) ExternalProject_Add(app-plugins PREFIX "${CMAKE_BINARY_DIR}/plugins-app" SOURCE_DIR "${CMAKE_SOURCE_DIR}/plugins" INSTALL_DIR "${APPLICATION_OUTPUT_DIR}" BINARY_DIR "${CMAKE_BINARY_DIR}/plugins-app" DEPENDS linphone-qt BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ ${PROJECT_BUILD_COMMAND} INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time." LIST_SEPARATOR | # Use the alternate list separator CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${PREFIX_PATH} ) endif() install(CODE "message(STATUS Running install)") set(AUTO_REGENERATION auto_regeneration) if( ENABLE_BUILD_APP_PLUGINS) add_custom_target(${AUTO_REGENERATION} ALL COMMAND ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS app-plugins) else() add_custom_target(${AUTO_REGENERATION} ALL COMMAND ${CMAKE_COMMAND} ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS linphone-qt) endif() else() message("Adding Linphone Desktop in an IDE-friendly state") set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}") add_subdirectory(${CMAKE_SOURCE_DIR}/linphone-app) if(NOT LINPHONE_QT_ONLY) add_dependencies(app-library ${APP_DEPENDS}) endif() if( ENABLE_BUILD_APP_PLUGINS) add_subdirectory(${CMAKE_SOURCE_DIR}/plugins "plugins-app") endif() endif() ExternalProject_Add(linphone-qt-only PREFIX "${CMAKE_BINARY_DIR}/linphone-app" SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-app" INSTALL_DIR "${APPLICATION_OUTPUT_DIR}" BINARY_DIR "${CMAKE_BINARY_DIR}/linphone-app" BUILD_COMMAND ${CMAKE_COMMAND} --build --config $ ${PROJECT_BUILD_COMMAND} # INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time." LIST_SEPARATOR | # Use the alternate list separator CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${PREFIX_PATH} EXCLUDE_FROM_ALL ON #BUILD_ALWAYS ON ) linphone-desktop-5.0.2/LICENSE.txt000066400000000000000000001045161434616504300166410ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . linphone-desktop-5.0.2/README.md000066400000000000000000000341541434616504300162750ustar00rootroot00000000000000[![pipeline status](https://gitlab.linphone.org/BC/public/linphone-desktop/badges/master/pipeline.svg)](https://gitlab.linphone.org/BC/public/linphone-desktop/commits/master) [![weblate status](https://weblate.linphone.org/widgets/linphone-desktop/-/svg-badge.svg)](https://weblate.linphone.org/engage/linphone-desktop/?utm_source=widget) # Linphone Desktop Linphone is an open source softphone for voice and video over IP calling and instant messaging. It is fully SIP-based, for all calling, presence and IM features. General description is available from [Linphone web site](https://www.linphone.org/technical-corner/linphone) ### License Copyright © Belledonne Communications Linphone is dual licensed, and is available either : - under a [GNU/GPLv3 license](https://www.gnu.org/licenses/gpl-3.0.en.html), for free (open source). Please make sure that you understand and agree with the terms of this license before using it (see LICENSE file for details). - under a proprietary license, for a fee, to be used in closed source applications. Contact [Belledonne Communications](https://www.linphone.org/contact) for any question about costs and services. ### Documentation - [Supported features and RFCs](https://www.linphone.org/technical-corner/linphone/features) - [Linphone public wiki](https://wiki.linphone.org/xwiki/wiki/public/view/Linphone/) ## Getting started Here are the general instructions to build Linphone for desktop. The specific instructions for each build platform is described just below. You will need the tools : - `cmake` >= 3.15 : download it in https://cmake.org/download/ - `python` : https://www.python.org/downloads/release/python-381/ - `pip` : it is already embedded inside Python, so there should be nothing to do about it - `yasm` : https://yasm.tortall.net/Download.html - `nasm` : https://www.nasm.us/pub/nasm/releasebuilds/ - `doxygen` (required for the Cxx Wrapper) - `Perl` - `pystache` : use 'pip install pystache --user' - `six` : use 'pip install six --user' - `git` For Desktop : you will need [Qt5](https://www.qt.io/download-thank-you) (_5.12 or newer_). `C++11` support is required! ### Set your environment 1. It's necessary to install the `pip` command and to execute: pip install pystache six 2. You have to set the environment variable `Qt5_DIR` to point to the path containing the cmake folders of Qt5, and the `PATH` to the Qt5 `bin`. Example: Qt5_DIR="~/Qt/5.12.5/gcc_64/lib/cmake/Qt5" PATH="~/Qt/5.12.5/gcc_64/bin/:$PATH" Note: If you have the third party tool `qtchooser` installed : eval "$(qtchooser -print-env)" export Qt5_DIR=${QTLIBDIR}/cmake/Qt5 export PATH=${QTTOOLDIR}:$PATH 3. For specific requirements, see platform instructions sections below. ### Summary of Building steps `git clone https://gitlab.linphone.org/BC/public/linphone-desktop.git --recursive` `cd linphone-desktop` `mkdir build` `cd build` `cmake .. -DCMAKE_BUILD_PARALLEL_LEVEL=10 -DCMAKE_BUILD_TYPE=RelWithDebInfo` `cmake --build . --target install --parallel 10 --config RelWithDebInfo` `./OUTPUT/bin/linphone --verbose` or `./OUTPUT/Linphone.app/Contents/MacOS/linphone --verbose` ### Get sources git clone https://gitlab.linphone.org/BC/public/linphone-desktop.git --recursive ### Building : General Steps The build is done by building the SDK and the application. Their targets are `sdk` and `linphone-qt`. 1. Create your build folder at the root of the project : `mkdir build` Go to this new folder and begin the build process : `cd build` 2. Prepare your options : `cmake ..`. By default, it will try compile all needed dependencies. You can remove some by adding `-DENABLE_=NO` to the command. You can use `cmake-gui ..` if you want to have a better access to them. You can add `-DCMAKE_BUILD_PARALLEL_LEVEL=` to do `` parallel builds for speeding up the process. Also, you can add `-DENABLE_BUILD_VERBOSE=ON` to get more feedback while generating the project. Note : For Makefile or Ninja, you have to add `-DCMAKE_BUILD_TYPE=` if you wish to build in a specific configuration (for example `RelWithDebInfo`). 3. Build and install the whole project : `cmake --build . --target --parallel ` (replace `` with the target name and `` by the number of parallel builds). Note : For XCode or Visual Studio, you have to add `--config ` if you wish to build in a specific configuration (for example `RelWithDebInfo`). When all are over, the files will be in the OUTPUT folder in the build directory. When rebuilding, you have to use `cmake --build . --target install` (or `cmake --install .`) to put the application in the correct configuration. Binaries inside other folders (like `linphone-app` and `linphone-sdk`) are not supposed to work. 4. When doing some modifications in the SDK, you can rebuild only the SDK with the target `sdk` and the same for the application with `linphone-qt-only` 5. In order to get packages, you can use `cmake .. -DENABLE_APP_PACKAGING=YES`. The files will be in `OUTPUT/packages` folder. ### Update your project 1. Update your project with : git fetch git pull --rebase 2. Update sub-modules from your current branch git submodule update --init --recursive Then simply re-build using cmake. #### General Troubleshooting * The latest version of Doxygen may not work with the SDK. If you some build issues and have a specific version of Doxygen that is not in your PATH, you can use `-DLINPHONESDK_DOXYGEN_PROGRAM`. Eg on Mac : `-DLINPHONESDK_DOXYGEN_PROGRAM=/Applications/Doxygen.app/Contents/Resources/doxygen` * If the build of the SDK crash with something like "cmd.exe failed" and no more info, it can be a dependency that is not available. You have to check if all are in your PATH. Usually, if it is about VPX or Decaf, this could come from your Perl installation. * If the application doesn't start and create an empty file with a random name, it could be come from a bad configuration between your application and others sub-modules. Check your configurations and force them with `-DCMAKE_BUILD_TYPE=` or `--config `. * On Mac, the application can crash at the start from QOpenGLContext. A workaround is to deactivate the mipmap mode on images by adding into your configuration file (linphonerc): `mipmap_enabled=0` in `[ui]` section. ## Specific instructions for the Mac Os X platform To install the required dependencies on Mac OS X, you can use [Homebrew](https://brew.sh/). Before you install packages with Brew, you may have to change directories permissions (if you can't change permissions with sudo on a MacOS >= High Sierra, get a look at [this StackOverflow answer](https://stackoverflow.com/questions/16432071/how-to-fix-homebrew-permissions#46844441)). 1. Install XCode from the Apple store. Run it at least once to allow it to install its tools. You may need to run : xcode-select --install 2. Install Homebrew by following the instructions here https://brew.sh/ 3. Install dependencies: brew install cmake pkg-config git doxygen nasm yasm 4. First ensure you have [pip](https://pypi.org/project/pip/) 5. Then, you can install a pip package with the following command: python -m pip install [package] For instance, enter the following command: python -m pip install pystache six graphviz 6. Download [Qt](https://www.qt.io/download), install a Qt5 version and set Qt5_DIR and PATH variables. 7. Qt5 is not available for arm64. If you are building on a arm64 system, you have to select the x86_64 processor on the generation stage of cmake : -DCMAKE_APPLE_SILICON_PROCESSOR=x86_64 8. Build as usual (General Steps). 9. If you get an error about modules that are not found for Python, it may be because cmake try to use another version from your PATH. It can be the case if you installed Python from brew. Install Python modules by using absolute path. For example: /opt/homebrew/python3 -m pip install pystache six graphviz ## Specific instructions for the Windows platform 64-bit version is not fully supported at this moment by Linphone Desktop and wasn't tested. If a build for 64bits is needed, replace all `mingw32` by `mingw64`, `i686` by `x86_64`, `-A Win32` by `-A x64` or simply remove it. 1. Install main tools: - `MinGW/MSYS2` : [download](https://www.msys2.org/) - Follow instructions on their "Getting Started" page. - Install toolchains and prepare python: - `pacman -Sy --needed base-devel mingw-w64-i686-toolchain` - `pacman -S python3-pip` in `MSYS2 MSYS` console - `python3 -m pip install pystache six` in `cmd` - In this order, add `C:\msys64\`, `C:\msys64\usr\bin` and `C:\msys64\mingw32\bin` in your PATH (the last one is needed by cmake to know where gcc is) to the PATH environement variable from windows advanced settings. When building the SDK, it will install automatically from MSYS2 : `perl`, `yasm`, `gawk`, `bzip2`, `nasm, `sed`, `patch`, `pkg-config`, `gettext`, `glib2` and `intltool` (if needed) - `git` : use MSYS2 : `pacman -S git` or [download](https://git-scm.com/download/win) - Visual Studio must also be properly configured with addons. Under "Tools"->"Obtain tools and features", make sure that the following components are installed: - Tasks: Select Windows Universal Platform development, Desktop C++ Development, .NET Development - Under "Installation details". Go to "Desktop C++ Development" and add "SDK Windows 8.1 and SDK UCRT" - Individual component: Windows 8.1 SDK 2. Ensure that you have downloaded the `Qt msvc2015 version` or `Qt msvc2017 version` (32-bit). 3. Or open a Command line with Visual Studio `Developer Command Prompt for VS 2017` and call qtenv2.bat that is in your qt binaries eg: `C:\Qt\\msvc2017\bin\qtenv2.bat` 4. Build as usual with adding `-A Win32` to `cmake ..` (General Steps) : - `cmake .. -DCMAKE_BUILD_PARALLEL_LEVEL=10 -DCMAKE_BUILD_TYPE=RelWithDebInfo -A Win32` The default build is very long. It is prefered to use the Ninja generator `-G "Ninja"` - `cmake --build . --target ALL_BUILD --parallel 10 --config RelWithDebInfo` 5. The project folder will be in the build directory and binaries should be in the OUTPUT folder. ## Installing dependencies There are [docker files](docker-files) configurations where dependencies can be retrieved. Also, more configurations are available in the docker-files folder of linphone-sdk submodule. ## Options | Options | Description | Default value | | :--- | :---: | ---: | | ENABLE_APP_LICENSE | Enable the license in packages. | YES | | ENABLE_APP_PACKAGING | Enable packaging. Package will be deployed in `OUTPUT/packages` | NO | | ENABLE_APP_WEBVIEW | Enable webview for accounts. The Webview engine must be deployed, it takes a large size. | NO | | ENABLE_APP_PACKAGE_ROOTCA | Embed the rootca file (concatenation of all root certificates published by mozilla) into the package | YES | | ENABLE_BUILD_APP_PLUGINS | Enable the build of plugins | YES | | ENABLE_BUILD_EXAMPLES | Enable the build of examples | NO | | ENABLE_BUILD_VERBOSE | Enable the build generation to be more verbose | NO | | ENABLE_DAEMON | Enable the linphone daemon interface. | NO | | ENABLE_PQCRYPTO | Enable post quantum ZRTP. | NO | | ENABLE_STRICT | Build with strict compilator flags e.g. -Wall -Werror | NO | | ENABLE_TESTS | Build with testing binaries of SDK | NO | | ENABLE_TESTS_COMPONENTS | Build libbctoolbox-tester | NO | | ENABLE_TOOLS | Enable tools of SDK | NO | | ENABLE_UNIT_TESTS | Enable unit test of SDK. | NO | | ENABLE_UPDATE_CHECK | Enable update check. | YES | | LINPHONE_SDK_MAKE_RELEASE_FILE_URL | Make a RELEASE file that work along check_version and use this URL | "" | ## Contributing ### Code In order to submit a patch for inclusion in Linphone's source code: 1. First make sure that your patch applies to the latest Git sources before submitting : patches made to old versions can't and won't be merged. 2. Fill out and send the contributor agreement for your patch to be included in the Git tree by following links [there](https://www.linphone.org/contact). The goal of this agreement is to grant us the peaceful exercise of our rights to the Linphone source code, without losing your rights over your contribution. 3. Then go to the [github repository](https://github.com/BelledonneCommunications/linphone-desktop/pulls) and make a Pull Requests based on your code. Please note that we don't offer free support and these contributions will be addressed on our free-time. Translation status ### Languages
Linphone is getting a full internationalization support.

We no longer use transifex for the translation process, instead we have deployed our own instance of [Weblate](https://weblate.linphone.org).


If you want you can contribute at: https://weblate.linphone.org/projects/linphone-desktop/



### Feedback or bug reporting Launch the application with `--verbose` parameter to get full logs and send it with your request. You can use the "Send logs" button in settings to upload log files and share it by email or with a post in the corresponding Github project : - [Desktop Application](https://github.com/BelledonneCommunications/linphone-desktop/issues) - [Linphone SDK](https://github.com/BelledonneCommunications/linphone-sdk/issues) On some OS (like Fedora 22 and later), they disable Qt debug output by default. To get full output, you need to create `~/.config/QtProject/qtlogging.ini` and add : [Rules] *.debug=true qt.*.debug=false linphone-desktop-5.0.2/docker-files/000077500000000000000000000000001434616504300173565ustar00rootroot00000000000000linphone-desktop-5.0.2/docker-files/bc-dev-centos7-fuse-qt-wget000066400000000000000000000027531434616504300243560ustar00rootroot00000000000000FROM gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos7:20210217_python3 MAINTAINER Peio Rigaux # QT Dependenciesi (pigz is used to parallelize compression) RUN sudo yum install -y libxkbcommon* libxcb freeglut-devel pigz icc-profiles-openicc fontconfig fontconfig-devel libfontconfig1-dev freetype-devel RUN sudo yum install -y devtoolset-8 RUN sudo yum install -y devtoolset-8-gcc* RUN scl enable devtoolset-8 bash RUN sudo yum install -y gperf flex SHELL ["/bin/bash", "--login", "-c"] RUN scl enable devtoolset-8 bash RUN gcc --version # Configure AppImages dependencies RUN sudo yum install -y fuse fuse-libs wget # Build qt5.12.12 RUN git clone -b feature/webview_subscription --single-branch https://gitlab.linphone.org/BC/public/linphone-desktop.git && \ ./linphone-desktop/tools/build_qt_rpm && \ sudo rpm -i ./linphone-desktop/rpm-linphone-qt-5.12.12/rpmbuild/RPMS/x86_64/*.rpm && \ sudo mv ./linphone-desktop/rpm-linphone-qt-5.12.12/rpmbuild/RPMS/x86_64/*.rpm / && \ sudo rm -rf ./linphone-desktop RUN echo 'source /opt/rh/devtoolset-8/enable' >> /home/bc/.bashrc RUN echo 'source /opt/rh/devtoolset-8/enable' >> /home/bc/.shrc; exit 0 # This tells /bin/sh to load '~/.shrc' on starting ENV ENV=~/.shrc ENV Qt5_DIR=/opt/com.belledonne-communications/linphone/lib/cmake ENV PATH=$PATH:/opt/com.belledonne-communications/linphone/bin USER bc WORKDIR /home/bc ENV PS1='\[\e[34m\]\u@bc-dev-centos7>\[\e[0m\] ' CMD bash linphone-desktop-5.0.2/docker-files/bc-dev-debian9-qt-fuse-wget-gpg2000066400000000000000000000037571434616504300251510ustar00rootroot00000000000000FROM gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-debian9:20210217_python3 MAINTAINER Peio Rigaux #Tools for QT buid script RUN sudo apt-get update && sudo apt-get install -y pigz #QT Dependencies #atspi and dbus may be used to enable qt accessibility for screan reader #xkbcommon is needed for special keyboard features #flite1-dev, libspeechd-dev speech-dispatcher are needed for text to speech #libfontconfig1-dev is needed to load correct fonts (support of ideograms, etc..) #needed for xcb : libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libx11-xcb-dev libxcb-glx0-dev libxkbcommon-x11-dev RUN sudo apt-get update && sudo apt-get install -y libxkbcommon* flite1-dev libspeechd-dev speech-dispatcher libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libx11-xcb* libxcb* qdbus-qt5 libqt5dbus5 libdbus-1-dev libdbus-glib-1-dev libatspi2.0-0 libatspi2.0-dev RUN sudo apt-get update && sudo apt-get install -y libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev #needed fot qt to find dbus ENV PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig # Build qt5.12.12 RUN git clone -b master --single-branch https://gitlab.linphone.org/BC/public/linphone-desktop.git && \ ./linphone-desktop/tools/build_qt_rpm && \ find "./linphone-desktop/rpm-linphone-qt-5.12/rpmbuild/RPMS/x86_64/" -iname "*.rpm" -exec sudo fakeroot alien -d {} + && \ find "." -maxdepth 1 -iname "*.deb" -exec sudo dpkg -i --force-overwrite {} + && \ rm -rf ./linphone-desktop ENV Qt5_DIR=/opt/com.belledonne-communications/linphone/lib/cmake ENV PATH=/opt/com.belledonne-communications/linphone/bin:$PATH #Appimage deps RUN sudo apt-get install -y wget fuse libfuse2 gnupg2 USER bc WORKDIR /home/bc ENV PS1='\[\e[34m\]\u@bc-dev-centos7>\[\e[0m\] ' CMD bash linphone-desktop-5.0.2/docker-files/bc-dev-ubuntu-rolling-qt-fuse-wget-gpg2000066400000000000000000000031071434616504300266110ustar00rootroot00000000000000FROM gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-ubuntu-rolling:20210217_python3 MAINTAINER Peio Rigaux #Tools for QT buid script RUN sudo apt-get update && sudo apt-get install -y pigz RUN sudo ln -s /usr/bin/python3 /usr/bin/python #QT Dependencies #atspi and dbus may be used to enable qt accessibility for screan reader #xkbcommon is needed for special keyboard features #flite1-dev, libspeechd-dev speech-dispatcher are needed for text to speech #libfontconfig1-dev is needed to load correct fonts (support of ideograms, etc..) RUN sudo apt-get update && sudo apt-get install -y libxkbcommon* libxcb-xfixes0-dev flite1-dev libspeechd-dev speech-dispatcher libfontconfig1-dev qdbus-qt5 libqt5dbus5 libdbus-1-dev libdbus-glib-1-dev libatspi2.0-0 libatspi2.0-dev #needed fot qt to find dbus ENV PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig # Build qt5.12.5 RUN git clone -b master --single-branch https://gitlab.linphone.org/BC/public/linphone-desktop.git && \ ./linphone-desktop/tools/build_qt_rpm && \ find "./linphone-desktop/rpm-linphone-qt-5.12.5/rpmbuild/RPMS/x86_64/" -iname "*.rpm" -exec sudo fakeroot alien -d {} + && \ find "." -maxdepth 1 -iname "*.deb" -exec sudo dpkg -i --force-overwrite {} + && \ rm -rf ./linphone-desktop ENV Qt5_DIR=/opt/com.belledonne-communications/linphone/lib/cmake ENV PATH=/opt/com.belledonne-communications/linphone/bin:$PATH #Appimage deps RUN sudo apt-get install -y wget fuse libfuse2 gnupg2 USER bc WORKDIR /home/bc ENV PS1='\[\e[34m\]\u@bc-dev-centos7>\[\e[0m\] ' CMD bash linphone-desktop-5.0.2/linphone-app/000077500000000000000000000000001434616504300174015ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/CMakeLists.txt000066400000000000000000000736051434616504300221540ustar00rootroot00000000000000################################################################################ # # Copyright (c) 2017-2021 Belledonne Communications SARL. # # This file is part of linphone-desktop # (see https://www.linphone.org). # # 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 . # ################################################################################ cmake_minimum_required(VERSION 3.1) find_package(bctoolbox CONFIG) set(FULL_VERSION ) bc_compute_full_version(FULL_VERSION) set(version_major ) set(version_minor ) set(version_patch ) set(identifiers ) set(metadata ) bc_parse_full_version("${FULL_VERSION}" version_major version_minor version_patch identifiers metadata) project(linphoneqt VERSION "${version_major}.${version_minor}.${version_patch}") if(ENABLE_BUILD_VERBOSE) #message("CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}") message("Options : ${ENABLE_UPDATE_CHECK}, ${ENABLE_UNIT_TESTS}, ${ENABLE_TESTS}, ${ENABLE_TESTS_COMPONENTS}, ${ENABLE_TOOLS}, ${ENABLE_STRICT}, ${ENABLE_FFMPEG}, ${ENABLE_OPUS}") endif() include(GNUInstallDirs) include(CheckCXXCompilerFlag) set(TARGET_NAME linphone-qt) set(LINPHONE_QML_DIR "WORK/qml_files/ui") set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS true) set(CMAKE_CXX_STANDARD 14) if(UNIX AND NOT APPLE) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") list(APPEND CMAKE_MODULE_PATH "${LINPHONE_OUTPUT_DIR}/cmake") set(APP_LIBRARY app-library) set(APP_PLUGIN app-plugin) include(application_info.cmake) string(TIMESTAMP CURRENT_YEAR "%Y") if( "${CURRENT_YEAR}" STREQUAL "${APPLICATION_START_LICENCE}") set(COPYRIGHT_RANGE_DATE "${APPLICATION_START_LICENCE}") else() set(COPYRIGHT_RANGE_DATE "${APPLICATION_START_LICENCE}-${CURRENT_YEAR}") endif() set(APPLICATION_SEMVER ${FULL_VERSION}) if(WIN32) set(EXECUTABLE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_DIR} ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_DIR} ) set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_DIR} ) foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )# Apply to all configurations string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${EXECUTABLE_OUTPUT_DIR} ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${EXECUTABLE_OUTPUT_DIR} ) set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${EXECUTABLE_OUTPUT_DIR} ) endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) endif() find_package(LinphoneCxx CONFIG) find_package(Linphone CONFIG) find_package(belcard CONFIG) find_package(Mediastreamer2 CONFIG) find_package(ortp CONFIG) if(ENABLE_BUILD_VERBOSE) message("INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} FRAMEWORK_PATH=${CMAKE_FRAMEWORK_PATH}, PREFIX_PATH=${CMAKE_PREFIX_PATH}") message("LINPHONE : ${LINPHONE_INCLUDE_DIRS} => ${LINPHONE_LIBRARIES}") message("LINPHONECXX : ${LINPHONECXX_INCLUDE_DIRS} => ${LINPHONECXX_LIBRARIES}") endif() # Build configuration ############################# set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG -DQT_NO_DEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG" ) ############################# #Sanitizer if(ENABLE_SANITIZER) if(MSVC) set(sanitize_flags "/fsanitize=address /Oy-") else() set(sanitize_flags "-fsanitize=address,undefined -fno-omit-frame-pointer -fno-optimize-sibling-calls") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${sanitize_flags}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${sanitize_flags}") if(NOT MSVC) set(sanitize_linker_flags "-fsanitize=address,undefined") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${sanitize_linker_flags}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${sanitize_linker_flags}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${sanitize_linker_flags}") endif() endif() ############################# if( WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WINSOCKAPI_")#remove error from windows headers order add_definitions(-DNOMINMAX) endif() set(CMAKE_INCLUDE_CURRENT_DIR ON)#useful for config.h set(QT5_PACKAGES Core Gui Quick Widgets QuickControls2 Svg LinguistTools Concurrent Network Test Qml) if(ENABLE_APP_WEBVIEW) list(APPEND QT5_PACKAGES WebView WebEngine WebEngineCore) add_definitions(-DENABLE_WEBVIEW) endif() if (UNIX AND NOT APPLE) list(APPEND QT5_PACKAGES DBus) endif () set(QT5_PACKAGES_OPTIONAL TextToSpeech QmlModels) set(CMAKE_AUTOMOC ON) find_package(Qt5 COMPONENTS ${QT5_PACKAGES} REQUIRED) find_package(Qt5 COMPONENTS ${QT5_PACKAGES_OPTIONAL} QUIET) bc_git_version(${TARGET_NAME} ${PROJECT_VERSION}) #------------------------------------------------- set(ASSETS_DIR "assets") set(SOURCES src/app/App.cpp src/app/AppController.cpp src/app/cli/Cli.cpp src/app/logger/Logger.cpp src/app/paths/Paths.cpp src/app/providers/AvatarProvider.cpp src/app/providers/ImageProvider.cpp src/app/providers/ExternalImageProvider.cpp src/app/providers/QRCodeProvider.cpp src/app/providers/ThumbnailProvider.cpp src/app/proxyModel/ProxyListModel.cpp src/app/proxyModel/SortFilterProxyModel.cpp #src/app/proxyModel/ProxyMapModel.cpp #src/app/proxyModel/ProxyModel.cpp src/app/translator/DefaultTranslator.cpp src/components/assistant/AssistantModel.cpp src/components/authentication/AuthenticationNotifier.cpp src/components/call/CallListener.cpp src/components/call/CallModel.cpp src/components/calls/CallsListModel.cpp src/components/calls/CallsListProxyModel.cpp src/components/camera/Camera.cpp src/components/camera/CameraDummy.cpp src/components/chat/ChatModel.cpp src/components/chat-events/ChatCallModel.cpp src/components/chat-events/ChatEvent.cpp src/components/chat-events/ChatMessageListener.cpp src/components/chat-events/ChatMessageModel.cpp src/components/chat-events/ChatNoticeModel.cpp src/components/chat-room/ChatRoomInitializer.cpp src/components/chat-room/ChatRoomListener.cpp src/components/chat-room/ChatRoomModel.cpp src/components/chat-room/ChatRoomProxyModel.cpp src/components/codecs/AbstractCodecsModel.cpp src/components/codecs/AudioCodecsModel.cpp src/components/codecs/VideoCodecsModel.cpp src/components/conference/ConferenceAddModel.cpp src/components/conference/ConferenceHelperModel.cpp src/components/conference/ConferenceListener.cpp src/components/conference/ConferenceModel.cpp src/components/conference/ConferenceProxyModel.cpp src/components/conferenceInfo/ConferenceInfoModel.cpp src/components/conferenceInfo/ConferenceInfoListModel.cpp src/components/conferenceInfo/ConferenceInfoProxyModel.cpp src/components/conferenceScheduler/ConferenceScheduler.cpp src/components/conferenceScheduler/ConferenceSchedulerListener.cpp src/components/contact/ContactModel.cpp src/components/contact/VcardModel.cpp src/components/contacts/ContactsImporterModel.cpp src/components/contacts/ContactsImporterPluginsManager.cpp src/components/contacts/ContactsImporterListModel.cpp src/components/contacts/ContactsImporterListProxyModel.cpp src/components/contacts/ContactsListModel.cpp src/components/contacts/ContactsListProxyModel.cpp src/components/content/ContentModel.cpp src/components/content/ContentListModel.cpp src/components/content/ContentProxyModel.cpp src/components/core/CoreHandlers.cpp src/components/core/CoreManager.cpp src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp src/components/file/FileDownloader.cpp src/components/file/FileExtractor.cpp src/components/friend/FriendListListener.cpp src/components/history/HistoryModel.cpp src/components/history/HistoryProxyModel.cpp src/components/ldap/LdapModel.cpp src/components/ldap/LdapListModel.cpp src/components/ldap/LdapProxyModel.cpp src/components/notifier/Notifier.cpp src/components/other/clipboard/Clipboard.cpp src/components/other/colors/ColorModel.cpp src/components/other/colors/ColorListModel.cpp src/components/other/colors/ColorProxyModel.cpp src/components/other/colors/ImageColorsProxyModel.cpp src/components/other/images/ImageModel.cpp src/components/other/images/ImageListModel.cpp src/components/other/images/ImageProxyModel.cpp src/components/other/text-to-speech/TextToSpeech.cpp src/components/other/timeZone/TimeZoneModel.cpp src/components/other/timeZone/TimeZoneListModel.cpp src/components/other/timeZone/TimeZoneProxyModel.cpp src/components/other/units/Units.cpp src/components/participant/ParticipantModel.cpp src/components/participant/ParticipantListModel.cpp src/components/participant/ParticipantProxyModel.cpp src/components/participant/ParticipantDeviceListener.cpp src/components/participant/ParticipantDeviceModel.cpp src/components/participant/ParticipantDeviceListModel.cpp src/components/participant/ParticipantDeviceProxyModel.cpp src/components/participant-imdn/ParticipantImdnStateModel.cpp src/components/participant-imdn/ParticipantImdnStateListModel.cpp src/components/participant-imdn/ParticipantImdnStateProxyModel.cpp src/components/presence/OwnPresenceModel.cpp src/components/presence/Presence.cpp src/components/recorder/RecorderManager.cpp src/components/recorder/RecorderModel.cpp src/components/search/SearchListener.cpp src/components/search/SearchResultModel.cpp src/components/search/SearchSipAddressesModel.cpp src/components/search/SearchSipAddressesProxyModel.cpp src/components/settings/AccountSettingsModel.cpp src/components/settings/SettingsModel.cpp src/components/sip-addresses/SipAddressesModel.cpp src/components/sip-addresses/SipAddressesProxyModel.cpp src/components/sip-addresses/SipAddressesSorter.cpp src/components/sip-addresses/SipAddressObserver.cpp src/components/sound-player/SoundPlayer.cpp src/components/telephone-numbers/TelephoneNumbersModel.cpp src/components/timeline/TimelineModel.cpp src/components/timeline/TimelineListModel.cpp src/components/timeline/TimelineProxyModel.cpp src/components/tunnel/TunnelModel.cpp src/components/tunnel/TunnelConfigModel.cpp src/components/tunnel/TunnelConfigListModel.cpp src/components/tunnel/TunnelConfigProxyModel.cpp src/components/url-handlers/UrlHandlers.cpp src/utils/Constants.cpp src/utils/LinphoneEnums.cpp src/utils/MediastreamerUtils.cpp src/utils/QExifImageHeader.cpp src/utils/UriTools.cpp src/utils/Utils.cpp src/utils/plugins/PluginsManager.cpp ) set(PLUGIN_SOURCES src/utils/plugins/PluginDataAPI.cpp src/utils/plugins/PluginNetworkHelper.cpp src/utils/plugins/LinphonePlugin.cpp ) set(HEADERS src/app/App.hpp src/app/AppController.hpp src/app/cli/Cli.hpp src/app/logger/Logger.hpp src/app/paths/Paths.hpp src/app/providers/AvatarProvider.hpp src/app/providers/ImageProvider.hpp src/app/providers/ExternalImageProvider.hpp src/app/providers/QRCodeProvider.hpp src/app/providers/ThumbnailProvider.hpp src/app/proxyModel/ProxyAbstractListModel.hpp src/app/proxyModel/ProxyAbstractMapModel.hpp src/app/proxyModel/ProxyAbstractObject.hpp src/app/proxyModel/ProxyListModel.hpp src/app/proxyModel/SortFilterAbstractProxyModel.hpp src/app/proxyModel/SortFilterProxyModel.hpp #src/app/proxyModel/ProxyMapModel.hpp #src/app/proxyModel/ProxyModel.hpp src/app/single-application/SingleApplication.hpp src/app/translator/DefaultTranslator.hpp src/components/assistant/AssistantModel.hpp src/components/authentication/AuthenticationNotifier.hpp src/components/call/CallListener.hpp src/components/call/CallModel.hpp src/components/calls/CallsListModel.hpp src/components/calls/CallsListProxyModel.hpp src/components/camera/Camera.hpp src/components/camera/CameraDummy.hpp src/components/chat/ChatModel.hpp src/components/chat-events/ChatCallModel.hpp src/components/chat-events/ChatEvent.hpp src/components/chat-events/ChatMessageListener.hpp src/components/chat-events/ChatMessageModel.hpp src/components/chat-events/ChatNoticeModel.hpp src/components/chat-room/ChatRoomInitializer.hpp src/components/chat-room/ChatRoomListener.hpp src/components/chat-room/ChatRoomModel.hpp src/components/chat-room/ChatRoomProxyModel.hpp src/components/codecs/AbstractCodecsModel.hpp src/components/codecs/AudioCodecsModel.hpp src/components/codecs/VideoCodecsModel.hpp src/components/Components.hpp src/components/conference/ConferenceAddModel.hpp src/components/conference/ConferenceHelperModel.hpp src/components/conference/ConferenceListener.hpp src/components/conference/ConferenceModel.hpp src/components/conference/ConferenceProxyModel.hpp src/components/conferenceInfo/ConferenceInfoModel.hpp src/components/conferenceInfo/ConferenceInfoListModel.hpp src/components/conferenceInfo/ConferenceInfoProxyModel.hpp src/components/conferenceScheduler/ConferenceScheduler.hpp src/components/conferenceScheduler/ConferenceSchedulerListener.hpp src/components/contact/ContactModel.hpp src/components/contact/VcardModel.hpp src/components/contacts/ContactsImporterModel.hpp src/components/contacts/ContactsImporterPluginsManager.hpp src/components/contacts/ContactsImporterListModel.hpp src/components/contacts/ContactsImporterListProxyModel.hpp src/components/contacts/ContactsListModel.hpp src/components/contacts/ContactsListProxyModel.hpp src/components/content/ContentModel.hpp src/components/content/ContentListModel.hpp src/components/content/ContentProxyModel.hpp src/components/core/CoreHandlers.hpp src/components/core/CoreManager.hpp src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp src/components/file/FileDownloader.hpp src/components/file/FileExtractor.hpp src/components/friend/FriendListListener.hpp src/components/history/HistoryModel.hpp src/components/history/HistoryProxyModel.hpp src/components/ldap/LdapModel.hpp src/components/ldap/LdapListModel.hpp src/components/ldap/LdapProxyModel.hpp src/components/notifier/Notifier.hpp src/components/other/clipboard/Clipboard.hpp src/components/other/colors/ColorModel.hpp src/components/other/colors/ColorListModel.hpp src/components/other/colors/ColorProxyModel.hpp src/components/other/colors/ImageColorsProxyModel.hpp src/components/other/images/ImageModel.hpp src/components/other/images/ImageListModel.hpp src/components/other/images/ImageProxyModel.hpp src/components/other/desktop-tools/DesktopTools.hpp src/components/other/text-to-speech/TextToSpeech.hpp src/components/other/timeZone/TimeZoneModel.hpp src/components/other/timeZone/TimeZoneListModel.hpp src/components/other/timeZone/TimeZoneProxyModel.hpp src/components/other/units/Units.hpp src/components/participant/ParticipantModel.hpp src/components/participant/ParticipantListModel.hpp src/components/participant/ParticipantProxyModel.hpp src/components/participant/ParticipantDeviceListener.hpp src/components/participant/ParticipantDeviceModel.hpp src/components/participant/ParticipantDeviceListModel.hpp src/components/participant/ParticipantDeviceProxyModel.hpp src/components/participant-imdn/ParticipantImdnStateModel.hpp src/components/participant-imdn/ParticipantImdnStateListModel.cpp src/components/participant-imdn/ParticipantImdnStateProxyModel.cpp src/components/presence/OwnPresenceModel.hpp src/components/presence/Presence.hpp src/components/recorder/RecorderManager.hpp src/components/recorder/RecorderModel.hpp src/components/search/SearchListener.hpp src/components/search/SearchResultModel.hpp src/components/search/SearchSipAddressesModel.hpp src/components/search/SearchSipAddressesProxyModel.hpp src/components/settings/AccountSettingsModel.hpp src/components/settings/SettingsModel.hpp src/components/sip-addresses/SipAddressesModel.hpp src/components/sip-addresses/SipAddressesProxyModel.hpp src/components/sip-addresses/SipAddressesSorter.hpp src/components/sip-addresses/SipAddressObserver.hpp src/components/sound-player/SoundPlayer.hpp src/components/telephone-numbers/TelephoneNumbersModel.hpp src/components/timeline/TimelineModel.hpp src/components/timeline/TimelineListModel.hpp src/components/timeline/TimelineProxyModel.hpp src/components/tunnel/TunnelModel.hpp src/components/tunnel/TunnelConfigModel.hpp src/components/tunnel/TunnelConfigListModel.hpp src/components/tunnel/TunnelConfigProxyModel.hpp src/components/url-handlers/UrlHandlers.hpp src/utils/Constants.hpp src/utils/LinphoneEnums.hpp src/utils/MediastreamerUtils.hpp src/utils/QExifImageHeader.hpp src/utils/UriTools.hpp src/utils/Utils.hpp src/utils/plugins/PluginsManager.hpp ) set(PLUGIN_HEADERS include/LinphoneApp/PluginDataAPI.hpp include/LinphoneApp/PluginNetworkHelper.hpp include/LinphoneApp/LinphonePlugin.hpp) list(APPEND SOURCES include/LinphoneApp/PluginExample.json) set(MAIN_FILE src/app/main.cpp) if (APPLE) list(APPEND SOURCES src/app/single-application/SingleApplication.cpp src/components/core/event-count-notifier/EventCountNotifierMacOs.m src/components/other/desktop-tools/DesktopToolsMacOs.cpp src/components/other/desktop-tools/DesktopToolsMacOsNative.mm src/components/other/desktop-tools/screen-saver/ScreenSaverMacOs.m src/components/other/desktop-tools/state-process/StateProcessMacOs.mm ) list(APPEND HEADERS src/app/single-application/SingleApplicationPrivate.hpp src/components/core/event-count-notifier/EventCountNotifierMacOs.hpp src/components/other/desktop-tools/DesktopToolsMacOs.hpp ) elseif (WIN32) list(APPEND SOURCES src/app/single-application/SingleApplication.cpp src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp src/components/other/desktop-tools/DesktopToolsWindows.cpp ) list(APPEND HEADERS src/app/single-application/SingleApplicationPrivate.hpp src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.hpp src/components/other/desktop-tools/DesktopToolsWindows.hpp ) else () list(APPEND SOURCES src/app/single-application/SingleApplicationDBus.cpp src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp src/components/other/desktop-tools/DesktopToolsLinux.cpp src/components/other/desktop-tools/screen-saver/ScreenSaverDBus.cpp src/components/other/desktop-tools/screen-saver/ScreenSaverXdg.cpp ) list(APPEND HEADERS src/app/single-application/SingleApplicationDBusPrivate.hpp src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.hpp src/components/other/desktop-tools/DesktopToolsLinux.hpp src/components/other/desktop-tools/screen-saver/ScreenSaverDBus.hpp src/components/other/desktop-tools/screen-saver/ScreenSaverXdg.hpp ) endif () set(QRC_RESOURCES resources.qrc) if(ENABLE_APP_WEBVIEW) set(QRC_WEBVIEW_RESOURCES webview_resources.qrc) endif() #qt5_add_big_resources(QRC_BIG_RESOURCES resources.qrc) #Use this macro if resources size is too high set(LANGUAGES_DIRECTORY "${ASSETS_DIR}/languages") set(I18N_FILENAME i18n.qrc) set(LANGUAGES da de en es fr_FR hu it ja lt pt_BR ru sv tr uk zh_CN) # ------------------------------------------------------------------------------ function (PREPEND list prefix) set(new_list "") foreach (elem ${${list}}) list(APPEND new_list "${prefix}${elem}") endforeach () set(${list} ${new_list} PARENT_SCOPE) endfunction () # Force absolute paths. PREPEND(SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/") PREPEND(HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/") PREPEND(PLUGIN_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/") PREPEND(PLUGIN_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/") # ------------------------------------------------------------------------------ # Compute QML files list. # ------------------------------------------------------------------------------ set(QML_SOURCES) file(STRINGS ${QRC_RESOURCES} QRC_RESOURCES_CONTENT) if(ENABLE_APP_WEBVIEW) file(STRINGS ${QRC_WEBVIEW_RESOURCES} QRC_WEBVIEW_RESOURCES_CONTENT) list(APPEND QRC_RESOURCES_CONTENT ${QRC_WEBVIEW_RESOURCES_CONTENT}) endif() foreach (line ${QRC_RESOURCES_CONTENT}) set(result) string(REGEX REPLACE "^[ \t]*<[ \t]*file[ \t]*>[ \t]*(.+\\.[a-z]+)[ \t]*<[ \t]*/[ \t]*file[ \t]*>[ \t]*$" "\\1" result "${line}" ) string(REGEX MATCH "\\.[a-z]+$" is_ui ${result}) if (NOT ${is_ui} STREQUAL "") list(APPEND QML_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/${result}") endif () endforeach () # ------------------------------------------------------------------------------ # Init git hooks. # ------------------------------------------------------------------------------ if (NOT WIN32) add_custom_target( check_qml DEPENDS ${QML_SOURCES} COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/../tools/check_qml_syntax" ) endif() execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/../tools/private/pre-commit" "${CMAKE_CURRENT_SOURCE_DIR}/../.git/hooks/pre-commit" ) set(_QML_IMPORT_PATHS "") list(APPEND _QML_IMPORT_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui/modules") list(APPEND _QML_IMPORT_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui/dev-modules") list(APPEND _QML_IMPORT_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui/scripts") list(APPEND _QML_IMPORT_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui/views") set(QML_IMPORT_PATH "${_QML_IMPORT_PATHS}" CACHE STRING "Path used to locate CMake modules by Qt Creator" FORCE) set(QML2_IMPORT_PATH "${_QML_IMPORT_PATHS}" CACHE STRING "Path used to locate CMake modules by Qt Creator" FORCE) set(QML_SOURCES_PATHS "${CMAKE_CURRENT_SOURCE_DIR}/ui/") set(QML_MODULES_PATHS ${QML_SOURCES_PATHS}) if(ENABLE_BUILD_VERBOSE)#useful to copy these Paths to QML previewers message("QML_IMPORT_PATH=${QML_IMPORT_PATH}" ) message("Qt5 Paths : Qt5_DIR=$ENV{Qt5_DIR}, PATH=$ENV{PATH}") endif() if(APPLE) if(MS2_PLUGINS_LOCATION) set(MSPLUGINS_DIR ${MS2_PLUGINS_LOCATION}) else() set(MSPLUGINS_DIR "Frameworks/mediastreamer2.framework/Versions/A/Libraries") endif() else() set(MSPLUGINS_DIR "plugins/mediastreamer") endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/config.h") # ------------------------------------------------------------------------------ # Build. # ------------------------------------------------------------------------------ include_directories(src/) include_directories("${LINPHONE_OUTPUT_DIR}/include/OpenGL") if (CMAKE_INSTALL_RPATH) #Retrieve lib path from a know QT executable get_target_property(LUPDATE_PATH Qt5::lupdate LOCATION) get_filename_component(LUPDATE_PATH "${LUPDATE_PATH}" DIRECTORY) get_filename_component(QT_PATH "${LUPDATE_PATH}/lib" ABSOLUTE) list(APPEND CMAKE_INSTALL_RPATH "${QT_PATH}") endif () # Add languages support. add_subdirectory("${LANGUAGES_DIRECTORY}" "assets/languages") list(APPEND SOURCES "${CMAKE_CURRENT_BINARY_DIR}/${LANGUAGES_DIRECTORY}/${I18N_FILENAME}") get_directory_property(TS_FILES DIRECTORY "${LANGUAGES_DIRECTORY}" DEFINITION TS_FILES) list(APPEND SOURCES ${TS_FILES}) # set application details if(WIN32) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_builder/linphone_package/windows/appDetailsWindows.rc.in" "${CMAKE_CURRENT_BINARY_DIR}/appDetailsWindows.rc") set(RC_FILE ${CMAKE_CURRENT_BINARY_DIR}/appDetailsWindows.rc) endif() add_library(${APP_PLUGIN} SHARED ${PLUGIN_SOURCES} ${PLUGIN_HEADERS}) add_library(${APP_LIBRARY} OBJECT ${SOURCES} ${HEADERS} ${QML_SOURCES} ${QRC_RESOURCES} ${QRC_WEBVIEW_RESOURCES} ${PLUGIN_HEADERS})#Need to add Headers to resolve moc Qt symbols if (WIN32) add_executable(${TARGET_NAME} WIN32 $ ${MAIN_FILE} ${QRC_BIG_RESOURCES} ${RC_FILE}) install(TARGETS linphone-qt RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) install(FILES "$" DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() elseif (APPLE) add_executable(${TARGET_NAME} $ ${MAIN_FILE} ${QRC_BIG_RESOURCES}) install(TARGETS linphone-qt RUNTIME DESTINATION "${APPLICATION_NAME}.app/Contents/MacOS") # using ${TARGET_NAME} lead to cmake error on target name. Use directly linphone-qt. else () add_executable(${TARGET_NAME} $ ${MAIN_FILE} ${QRC_BIG_RESOURCES}) install(TARGETS linphone-qt RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") endif () set_property(TARGET ${APP_LIBRARY} PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt set_property(TARGET ${APP_PLUGIN} PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt #Turn on automatic resources compilation by cmake #Instead of excplicitely calling qt5_add_resources set_property(TARGET ${APP_LIBRARY} PROPERTY AUTORCC ON) set_property(TARGET ${APP_PLUGIN} PROPERTY AUTORCC ON) set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME "${EXECUTABLE_NAME}") if(MSVC) set_target_properties(${TARGET_NAME} PROPERTIES PDB_NAME "${EXECUTABLE_NAME}_app") endif() target_compile_definitions(${APP_PLUGIN} PUBLIC "-DENABLE_APP_EXPORT_PLUGIN") set_target_properties(${APP_PLUGIN} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) set(INCLUDED_DIRECTORIES "${LINPHONECXX_INCLUDE_DIRS}" "${MEDIASTREAMER2_INCLUDE_DIRS}") list(APPEND INCLUDED_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/include") set(LIBRARIES_LIST ${BCTOOLBOX_CORE_LIBRARIES} ${BELCARD_LIBRARIES} ${LINPHONE_LIBRARIES} ${LINPHONECXX_LIBRARIES} ${MEDIASTREAMER2_LIBRARIES} ${ORTP_LIBRARIES} ${OPUS_LIBRARIES}) if(WIN32) set(LIBRARIES) foreach(LIBRARY ${LIBRARIES_LIST})# Search for lib full path find_library(FIND_LIBRARY_ITEM_${LIBRARY} NAMES ${LIBRARY} lib${LIBRARY} REQUIRED)#find_library need a specific variable name each time if(FIND_LIBRARY_ITEM_${LIBRARY}) list(APPEND LIBRARIES ${FIND_LIBRARY_ITEM_${LIBRARY}}) else() message(SEND_ERROR "${LIBRARY} not found!") endif() endforeach() else() set(LIBRARIES ${LIBRARIES_LIST}) endif() if(ENABLE_BUILD_VERBOSE) message("LIBRARIES : ${LIBRARIES}") endif() foreach (package ${QT5_PACKAGES}) list(APPEND INCLUDED_DIRECTORIES "${Qt5${package}_INCLUDE_DIRS}") # `qt5_create_translation` is provided from `LinguistTools` package. # But the `Qt5::LinguistTools` lib does not exist. Remove it. if (NOT (${package} STREQUAL LinguistTools)) # list(APPEND LIBRARIES ${Qt5${package}_LIBRARIES}) target_link_libraries(${APP_LIBRARY} Qt5::${package}) target_link_libraries(${APP_PLUGIN} Qt5::${package}) target_link_libraries(${TARGET_NAME} Qt5::${package}) endif () endforeach () foreach (package ${QT5_PACKAGES_OPTIONAL}) if ("${Qt5${package}_FOUND}") message("Optional package ${package} found.") list(APPEND INCLUDED_DIRECTORIES "${Qt5${package}_INCLUDE_DIRS}") list(APPEND LIBRARIES ${Qt5${package}_LIBRARIES}) string(TOUPPER "${package}" INCLUDE_NAME) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${INCLUDE_NAME}_ENABLED") else () message("Optional package ${package} not found.") endif () endforeach () if (APPLE) list(APPEND LIBRARIES "-framework Cocoa -framework IOKit -framework AVFoundation") # -framework linphone") #This doesn't work yet endif () target_include_directories(${APP_LIBRARY} SYSTEM PUBLIC ${INCLUDED_DIRECTORIES}) target_include_directories(${APP_PLUGIN} SYSTEM PUBLIC ${INCLUDED_DIRECTORIES}) target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${INCLUDED_DIRECTORIES}) target_link_libraries(${APP_LIBRARY} ${LIBRARIES}) target_link_libraries(${APP_PLUGIN} ${LIBRARIES}) target_link_libraries(${APP_LIBRARY} ${APP_PLUGIN}) target_link_libraries(${TARGET_NAME} ${LIBRARIES}) target_link_libraries(${TARGET_NAME} ${APP_PLUGIN}) if(WIN32) find_library(LDAP_LIBRARIES NAMES ldap libldap HINTS "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}") find_library(LBER_LIBRARIES NAMES lber liblber HINTS "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}") target_link_libraries(${TARGET_NAME} wsock32 ws2_32 ${LDAP_LIBRARIES} ${LBER_LIBRARIES}) endif() add_dependencies(${APP_LIBRARY} update_translations ${TARGET_NAME}-git-version ${APP_PLUGIN}) add_dependencies(${TARGET_NAME} ${APP_LIBRARY} ${APP_PLUGIN}) # ------------------------------------------------------------------------------ # CPack settings & RPM. # ------------------------------------------------------------------------------ set(TOOLS_DIR "${CMAKE_BINARY_DIR}/programs") set(LINPHONE_BUILDER_SIGNING_IDENTITY ${LINPHONE_BUILDER_SIGNING_IDENTITY}) add_custom_command(TARGET ${TARGET_NAME} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/include/" "${CMAKE_INSTALL_PREFIX}/include/") add_custom_command(TARGET ${APP_PLUGIN} POST_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/") add_custom_command(TARGET ${APP_PLUGIN} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/") add_custom_command(TARGET ${APP_PLUGIN} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/") #add_custom_command(TARGET ${TARGET_NAME} PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/include/LinphoneApp/*" "${CMAKE_INSTALL_PREFIX}/include/LinphoneApp/") #configure_file("${CMAKE_CURRENT_SOURCE_DIR}/include/*" "${CMAKE_INSTALL_PREFIX}/include/LinphoneApp/" COPYONLY) install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include" DESTINATION ".") add_subdirectory(build) add_subdirectory(cmake_builder/linphone_package) deployqt_hack(${TARGET_NAME} ${LINPHONE_QML_DIR}) # ------------------------------------------------------------------------------ # To start better integration into IDE. # ------------------------------------------------------------------------------ source_group( "Json" REGULAR_EXPRESSION ".+\.json$" ) source_group( "Qml" REGULAR_EXPRESSION ".+\.qml$" ) source_group( "Js" REGULAR_EXPRESSION ".+\.js$" ) source_group( "Svg" REGULAR_EXPRESSION ".+\.svg$" ) source_group( "Lang" REGULAR_EXPRESSION ".+\.ts$" ) source_group( "Rc" REGULAR_EXPRESSION ".+\.rc$" ) linphone-desktop-5.0.2/linphone-app/application_info.cmake000066400000000000000000000005571434616504300237300ustar00rootroot00000000000000set(APPLICATION_DESCRIPTION "A libre SIP client") set(APPLICATION_ID "com.belledonnecommunications.linphone") set(APPLICATION_NAME Linphone) set(APPLICATION_URL "https://www.linphone.org") set(APPLICATION_VENDOR "Belledonne Communications") set(APPLICATION_LICENCE "GNU General Public License V3") set(APPLICATION_START_LICENCE "2010") set(EXECUTABLE_NAME linphone) linphone-desktop-5.0.2/linphone-app/assets/000077500000000000000000000000001434616504300207035ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/app-icon.rc000066400000000000000000000000671434616504300227420ustar00rootroot00000000000000A ICON MOVEABLE PURE LOADONCALL DISCARDABLE "icon.ico" linphone-desktop-5.0.2/linphone-app/assets/assistant/000077500000000000000000000000001434616504300227145ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/assistant/create-app-sip-account.rc000066400000000000000000000056321434616504300275140ustar00rootroot00000000000000
10000000
1 0 1 sip:voip-metrics@sip.linphone.org;transport=tls 1 180 600 sip:?@sip.linphone.org <sip:sip.linphone.org;transport=tls> 1 nat_policy_default_values sip.linphone.org message-expires=604800 sip:conference-factory@sip.linphone.org sip:videoconference-factory@sip.linphone.org 1 1 https://lime.linphone.org/lime-server/lime-server.php
stun.linphone.org stun,ice
https://www.linphone.org:444/lft.php
sips:rls@sip.linphone.org
sip.linphone.org SHA-256 -1 1 -1 64 1 ^[a-z0-9+_.\-]*$ https://subscribe.linphone.org:444/wizard.php
linphone-desktop-5.0.2/linphone-app/assets/assistant/use-app-sip-account.rc000066400000000000000000000056131434616504300270440ustar00rootroot00000000000000
10000000
1 0 1 sip:voip-metrics@sip.linphone.org;transport=tls 1 180 600 sip:?@sip.linphone.org <sip:sip.linphone.org;transport=tls> 1 nat_policy_default_values sip.linphone.org message-expires=604800 sip:conference-factory@sip.linphone.org sip:videoconference-factory@sip.linphone.org 1 1 https://lime.linphone.org/lime-server/lime-server.php
stun.linphone.org stun,ice
https://www.linphone.org:444/lft.php
sips:rls@sip.linphone.org
sip.linphone.org SHA-256 -1 1 -1 64 1 ^[a-z0-9+_.\-]*$ https://subscribe.linphone.org:444/wizard.php
linphone-desktop-5.0.2/linphone-app/assets/assistant/use-other-sip-account.rc000066400000000000000000000044201434616504300274000ustar00rootroot00000000000000
10000000
0 0 0 0 0 3600 1
MD5 -1 0 -1 128 1 ^[a-zA-Z0-9+_.\-]*$
linphone-desktop-5.0.2/linphone-app/assets/fonts/000077500000000000000000000000001434616504300220345ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/000077500000000000000000000000001434616504300250515ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/LICENSE_OFL.txt000066400000000000000000000103151434616504300273740ustar00rootroot00000000000000This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. linphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/NotoSans-Bold.ttf000066400000000000000000015707741434616504300302370ustar00rootroot00000000000000 GDEF,GPOSXyGSUBD7jOS/2m ? @ A B F G G H H K S U U V V W \ ` ` c c f s t t u    $,44BBJS ;w-# ((    # % ) + , - . / 0 1 7  =  &E ( )\ , -^ / 1` 3 9c < <j I Ik N Nl U \m ` `u f mv o s~ u w y    = m q v z ~ ST  %&VV   , 2 5 8 9 : < > A J N T V  1f : ; > H K M O b f ,         ! ' ( ) X k m o p q r u v w y z | } ~   4nDFLTcyrlgreklatnkernmarkmkmk. oƖ2҄F׀ݾ V   |ff h 4Ff rt*0f 4 Bffff ^ Xf h h h ^ h h r r rF"` | h F  F4 " h" `" X  $ r   *0`F"\\br6vzv""FFFFFF""""````````""""4FF``fffffffffffp.^ ^^^l.....<FdZ $%&'()*./2345679:;<=>FYZ\^cm} #%'56789:<>BDFUZ[\^_`aceghklnopqrstuwx}    ?@DGISTVWXY`dfpuxy{}~   !#)+-/13579;=?EGIKMOPQRST`>?@ABCDEFGHIJKLM !"#         79:<#%'579OQS`. "$@`B D         m        -ZM(-279:<#%'579OQS`-d79:<#%'579OQS` ;-nPP-2"PF m]elrlmnopq]  l m n o p q        m]lmnopqru ?^achopqsw @r]^aehl lmnopqm]x|}>?@ABCDElmnopqz{|}~    x}>?z{ x} >?z{3x} >?@ABCDEz{|}~  V`9 VW^df"paprtp;ops a VWZ& 2 `df  * 2 `df  Qegodf[a*  `df  + 2 Q`defgo  [a[an_aZ Vdf  +  V`dfn-2-<;,;2,;2  0  "" " prtru cscopstp                                      <<<<              )ZZ                9>BFUUY^`aehllnnpqssuxz{}}     @@ CE GJNNPTVZ\\^^apu|/7:FHTXZ\^`dem{}$)ACCET`a>MZ[j{ $6>RUV`b $glmx      ! !<;<$$&&+''((..C//*2233U44774889:';;C<<===DDIIPKKPQUU:WW0YZ [[D\\ mmN}}M+U ++++QCD***Q**      :::##4$$0%%4&&0''4((0))++--//113355'66 7788 99::=<<=>>=BBCCDDFFUU YY ZZ[[\\]] ^^ ``Waa eeff ggVhh llnnTppWqqssVuuvv wwxxzz{{}}OOX &%1&32%2 S1%&& B$$R$R%' ' '  ;!<  !  <NM  @@CCDDEEGG)HH"II(JJ NNPP QQ8RRKSS$TT1VV3WWJXX3YYJZZI\\I^^2aabb ccddee8ffgg8hijj kkll mmnn oo8pp1uuvvwwKxx$yySzz{{>||H7/27/>1%GFGF7/EE3B3B  2    %>H7//6.6.6.6..          !!##))++--//11335577)88"99)::";;)<<"==)>>"??)@@"AACCEE(FF GG(HH II(JJ KK(LL MM(NN OOPP QQRR SSTT ``4aa0>?@EFM Z[jklq ry z{   T !"#$$P#L@?@?99 -  , 5 A A A : : ?,;,$$ &&**224477288 9:"<<===@@?DDEEFHIIJJ*KKNOPQRRSSTTUUVV9WW(XXY\]]5``?mmL}}K Q    ****    9  9##2$$(%%2&&(''2((()) **++ ,,-- ..// 0011 2233 4455"66778899::=;;5<<===5>>=??5BB CCDDQEEFFGGII9]]^^3aa3eePhh3llPqq#ww#xx yy+zz{{)||}} M+)M I> I  01!N0 ! /  . -/ < <.""";:,  :  ,,LK@@#CCDD EEGGHHII JJNN OOPPQQ7RR'UUVV1WWYY]]^^!`` bbcc ddHee7ffHgg7hhii jjkk llmm nnoo7ppqq vvww'xx<zz{{'~~!/'<  NGFGF - -EDED -111 CC!/   !/0.0.0. -'B B A@A@                       ""$$))**++,,--..//00112233445566778899::;;<<==>>??@@AA BBCC DDEE FFGG HHII JJKK LLMM NNOOPPQQRRSSTT``2aa(#>? @E NO+PS&Z[\ajk)lqz{ |6 &+)      3 >#OJ88 %  $ 4g hne$=D]4KIjUUW  ==BEGJLinquU`'V(\\gcdhgijkpmttst $@DHH\q BRXY]`ef  g ikn<pFGLOQRUUXY\\^_dgiit{}   :  >  B * /C 1 1I > KJ N OX V VZ \ _[ c c_ t x` z ~e j l n o q r s t u v w z {             ^L|nj>_bF8^DKzR\^2_bhz_DJ^P~F<^^^^^^pnnnnjjjj_b_b_b_b_bKzF<v_z______V^^|\6nnnn2zjjjj8>DJ_b PbbbzV\zb_b__b__b_hK8nt8zp^^^^~KzKz<<<^<_*h@I^HXzn_bj6_bF^Kz~BhjKz_tLD__tL\__ptD:D_DnXLjjrH>^@@LXnI_PllH_bF^>~686B@@>RN_t~~_DG`_xDrF l00Hj2z_ ~~~KzhhD@@nl~R *J""Kz(\.4B:_@n@nFLRX^djFVpKv|H_H_lpvKzKzf6l6 jI $*0j^^<nI_P_tvl~l~_b___>>>>6l<BBHNTZ`F@@\flrx~>_P_t^^^^^^^^^nnnnnnj_b__b__b__b__b_||||KzKz6bR@@@@H_P h&j>_,2L8>DVJPV|\bhnKzt^zvvF_tV^j_b_^^<"V_00vz^^nnjj_b__b_88^_b__b__b__b_Kz:` >^^p$H6n"(V8KzV_tV.4z2:@FKLL~_RX^^^djnnpv|_P:vv_bVIn J 0@@r$J*D0FVI6p@@@@J~XKh<fBBHNTZ`6flllK?2_rrxF~P <$Z2 ?&,28(>DJPV\\bhntzzD_PR\^D "(.4:@FLRX^djpv|\nn^ j_$*0*06<BHhNTzZ`flr_b__b__b__b_FDFD8x~x~^^6P~FFKz<<^~^^DX_t_t_t_t_t_t  &,28LLLLLLLL>DJPV\bHh_,nt______z0DDDDDDDD8\_t_tLL__DDVVVVVVVVFLLLLLLLL( "((.4::VVVV^^@^FLLLLL_bRX^djjDDDDppDDKzKzIv|hbFrnBj DDDDDDDDDDDD$*06<BH_NTZ`flrx~Kh^>_b_bFD_P;^n__tX_  &,28___b>DJJPVV\bhznzt_zJllKzKzKzKzKz_____________ "444(.4:::::::::::@FLRXXX^jjjdjpvKK|F_b_b $*06<BHNDG`NTZ`flrx^~^_P^?&==J: = &,28>DJPV\#h.B\#6###I#f## "##x ##*G u00cY.Q:@K<gH0h<3o- 0<Dz0M<3 <@<0<0k<0<|G 0l<K 0t<l0H<0<l0<nC_0 ojY.../.(....T.T.x.>.|.|.8.6%6wJ5"4D.WS<.O q R@P}2#0(0.I*| 6*DINc7261*bHF:r5 . 9*cAd? e P6rv? cSMS.M.SRMTr#XX.-.XQ-QX-X8-8/8(8/1(-/{.K.{{K{(K(11\.6.\M6Q4..4WW48/..I.HIH8I8f..fRT ..".."]M"8,x8>(E....N2..2x.@;/-uqWixbrO~A<EO_H5J.00K<\06<"0<$}..zP. A t0F<0m<.v dVr<x@7 ("f1.[=-/GI,.Yd))8U#UB=PP44##Z#ZNZO Ou##<#>>e2nx0//0u~0E<P<Z<&LmE`<L<].hG\#K;(=cAd>kfLC">Lf L'+/27GKORW #$%&(XZdelpi)*+,-./0123456`a 2Te} $(,4>Cm89bcdefghiz{|}~ !BceGQRde . b@ .j?@?@ @ @ @ @ @@@@@b.j????@ @@ @@ @???@ @ @@@ @@j@ @.@ @@ @@ @@ @@ @@ @@ @?bb.?@ @@ @@ @bb.@ @@ @j@ @@ @@ @@ @?@  &,28>DJPV.\b.b.b.b.b.jjjjj????@ @@ @@ @@ @????j?hntz@@@@@@@@?j @ @ "?(.??4:@FLLRRXXXXX^^^^^^^^^dddd@ jpv|eeHc- reeeeeeyeueveeeeheeeeeeepemeeeeeeD"e ee!ee[=16#B##O##IDcDDeeGH Hne$=D]4KIjUUW  ==@@BEGJLinquU`*V+\\jcdkgimkppttvw $@DHH \q "EU[\`chi  j lnq<sFGLOQRUUXY\\^_dgiit{}   =  A  E * /F 1 1L > KM N O[ V V] \ _^ c cb t xc z ~h m o q r t u v w x y z } ~             :R j <#^ "$$B .#| 4;4;4% : (%t #$N=jI: $T%% % ;X $ : vI ,,,B$<H >>>|JJJjN\\\T$`Z222`fl$NDDDPPPL @444r* F> 2 x8~ X^X^X^=jj# I>D((#^$TlfV "%P$B ;X .% .% .% .%& 4 & 4 bh\4nt% ntZ`Z` Z`:::: ( jp>n &,28>\4 DJPV\:%b #^;4 "$B#| 4h;4#v$:tNntz$` I @ x #;Xx;X< I>I| "$#v:BR%# ;"#v#|#^;4#v :%t \B% $Z;F#I;F$* ##;X $NIv#"##(.40:@F %L%RR4X#^djpjpv|>NP78: ;4;X ( >D<4$``%2;4;X< $bh`f$Tlrlr "x~#^$ "NIZ|$*;4;X* &,28>DJPV\bhn#tIB=jz4#;" : ,28 I "(.4>D:@ "%;4;XFLRXRX^djp\4vv|$ ( I#:: *B r jB ;X;" . "$ 4 ;4*06< $BdH:NTT: ZZ||`f Ll$rx~5,2JP\4$$(;4;X( %&,28>DJPfV\4bhntz#^  Ix:  .:   # ( $%&$% I $$I II $Z v "II ((%#O.4%:@@@ $*;XFRLRRRXX ^^ v^d jIpI v|| $Z$Z %;X#F #46$6$#4 : $N  78;4;XB #@$*#;X$N%;40 6<B B;4$ HNTZZ :` vflrx~#;X :  > # :3*$$ &,22482> pDJPV\bhnt6$z$$ O# #=j: : p#4#4#4 "(.6$44:@FL#R#R#X^d=jj=jj=jj=jj=jpv| I I#^$T#^$T#^$T "%$B $B .% .% .%#|  4 4 4  $*06<BH% NT% rx Z`flrx~:::::: (  <  $T @@FFFFLL::&,,28>>DDDDJJPV\b||h4Dn2ptz4#| 442j"((..44::@@FFFFLLRX^djpv||$` : $$*06<BHNTTZ`flrrx~ %$T$B : & ,#I28>DDDDJJJJPPPPVVVV\\\\bbbbhhnnttzz 4 (2;X#6f#:$B$B$B .;4;4;4;4%$ 4 $B 4 5;"  :  " ( . 4 : v v @ F L RI v v v X ^ ^;X$ d v v p v j v p p v v | % $$ =j% # I I% % ! !!! !!!!$!*!0J!!6!"D"J"P"V"\"b"t"h"n"t""z"""""""""""""""""""""""""""6"## ####"#(#.#4#:#@#F#L#R#X#^#d#j#p##v#|;4;4#;4######################$$$ $$$$$$*$0$6$<;"$B$H$N$T:$Z$`$f$l$r$x$~$~I$$$$$$$$$$$$$$$8$$$$$$%%%%%% %&%,%2*Yd..E"6.Yyr-m(-::h""zmIzIE'mY.gFI8@:T<F5J"*"r"<"<6<FPLXM?"c""3"")"" #a8hQK:"mK"8"<c&"g/H""Sh 43"M"o\"3"k"" |G"l"t"l"Y. Y.-( DmhhLLmLLlH"nN"" "{o"j"Y.>Y.,Y .gY.LY.nY.y-(>-(-(,- (g-(y>6>6, 6g6yT>zI>||>||"8>8y`L"646"w(!m*zIWzIrYY.Wmm6(#"m Y..-(6RRzIzI5";Y66W"(8=,"I|""""O" "#"K""=D egggg7)""5"?~""4"1""""""N""H""$"6 "":""Ilk2 ;gggFgggggggk?gD"ggggigvgg?ggg gcgggggggUUyrr-(-(-(* m:q^mI6q6^66EETRRm^"czIzIzIqzI^E"N N 8''hEE.'**::>9-+yu*v*\W**66h<<<\x*r>*O*:6<@@@\B"""""""TTT\"""""""\ """""h"@@@@YYm_S"TT" `mK<<< 5<<`88m""":J."$""@l@l<l<<<}"""P.AtF"mvL Eoy:!-z"67"" ",ccg""""["T""="/":""S,."I"`"))')"))))F)i))F=="=6"'"FSFS'S"SOSBF;FB"'"=FFF"&FFFFZZ"ZFZO3OOO.OQFOO%F FFOF.F"F<F"F?F>>'>>>">>F>i>FF"F"F"cn/x u~E""P"Z"&"m""nE"""`"C""L"gg]""h"GYp^";"@""="ehghchF->g~84ky[ZRF>") *.2$=D]4:CINTY_du  &(?HIUUW[^_bgijllnnpqssvw~   BELLNN$&()*+%,'6UADeOTi`aoppqrsv  ((..2255<<FFTTVVY[eekk}}  (*03599;<>> BG IJMSVV\\cciilptt#$%&)*,<ZFMTYbir  !@CHHbceg"%*+  , .02 411:99;FG<MO>QRAUUCXYD\\FdgGt{KSYfno      - . N N z z                     ( V h |h V p \ 4 p  > 22  z       hhhhh 4    zzzzz          (     2 VVVVhzhzhz V V V p p 4 4 p p p   p (h h | p 4  4 z z    (h | p z  22 4 "   4     hz                      2hzhzhzhzhzhzhz 4 4 4 p(   hz   hzhz      hzhz V V ( p hzhzhzhz 4  V p  V 4 :. 42: >@ > > >hF L R HX^ T d 2 h jpv|   B    $ * 0 6 < B H N T Z ` f l r d x ~ >       > ( ( ( ( (  2 VVVV hzhzhzhz | | V V V V p p p p \ \ 4 p p p         & , 2 8 > D J J P V \ b h n t zzzzzzz zz        " (  . . 4 4 : @ F L R X ^ d  j p v Vhh | hh \ 8  > >  V     2              $ * * 0 0 0 0 0 0 0 0 0 0 6 < < < < < B BPPPPPPPPPP H H N N T T T T T Z ` ` ` ` ` l l l l l l l l l f l r r r r x x x x x x ~ ~ ~ ~ > h > >      & , 2 > 8 > D J P2 V \ b h"b\Eg"^*2&ngg)g>Y=H#KY"_KXkhhga{sg^ggg| eg|kgtge|kt5;gyg=g *g^.""D"-(MO^[PK T|zRG\4hskd/`ichg{""|"e"%"""D"j""I*";"\"z>F"FWFFFRFpF F#F#FFFFFFFFhFF4F1F FF("7"3"H<<"nhhh^)-4vQ"x &8J\n|B  "Q Yi 31  $ D  a 6"(.4#<#<$*06<E>E0>q )9  f-T 0 = o r<$N6<N`    ee 6"(.4::#1:<1#C<C  &4fT 0 3 = e o r c &8J\bb||X M "Q Yi  '$ D  "t" 6"""(.4#<#<$*06<>0>D 9" tP if-T 0 = o r c BT`lx,2@T`r* q<B|"l RNd J _"  " GC"  N^ ):4#&# { GFeF   W"""(.4=mU$*06<`,`D L"  L((    # % ) + , - . / 0 1 7  8 3 9: < <A I IB N NC  D((    # % ) + , - . / 0 1 7  =  &E ( )Z , -\ / 1^ < <a N Nb U \c ` `k f ml o st u wy y | Ep"(.:4::::::@@RpFLRpX^djpv|(o9h$"XX((.4:@FLRX^djjpv| *$r*`r06r<BHNTrTZ`flrrrx~#4*40*o.=&98(1Mb&O*%!o,ll/ll(  (  lFl& Vl ll  llll l l  2p =   m q v z ~  &,28_  V    < ST  %&VV   , 2 5 8 9 : < > A J N T V  1f : ; > H K M O b f ~ $ %& & ) , - . 0 2 5 6 ; ? E G  L  )V , -n / 1p : ;s > Hu K M O R U \ ` ` f m o w y .L4:@FLRpX^djpv|HHH~~HHHHHH~~ 0$6**06<BHNTfZ`flr~~x~"""""+"!""%"""-"'"("2" ""X1""C"""r""K"""""""F6FF1F0F'F,F$F"""""@"""`frl~rx~2 &,b828>DJPVz\bhntzd  "(.4:@@FLRX^djpv|>A#(+\8XCEJbM\U1t 66EUTC, pFFFFF FFFFE2fy34,J2(F VFF  F F FuF F  l (         ! ' ( ) X k m o p q r u v w y z | } ~  $*u"(RX^jdvjpvv|p|nFFF{FFpFsE5FFF]gFhFZuFFFjF *DFLTcyrlFgreklatn  SRB 2  "CAT JMAH tMOL NAV ROM       aaltc2sccaseccmpccmpccmpccmpdnomfraclnumlocllocllocllocl loclloclnumronum"ordn(pnum.rtlm4smcp:subs@supsFtnumLzeroR!#      $"%&NbnnT  D D f  6 J $   $ 2 p p P P.B   2 fT ((   & ) , 0 3 7 N N9     ! " # K M$ k k' p r( v w+ | |- ~ ~. / 0&*,+"#<3g456789:;LM(5jN^0:DNj|",6@JT    LG   I 3     LH   J 4 :BJRZbjrz                            :BJRZbjrz + - / . , 6 9 8 7 A C E D B ; = ? > < 1 3 5 4 2 * @ : 0:BJRZbjrz c e g f d o q s r p z | } { u w y x v i k m l j b n t h:BJRZbjrz G I K J H S U W V T ] _ a ` ^ X Z [ Y M O Q P N F R \ L:BJRZbjrz           % ' ) ( &  ! # "        $ ' )  I I I I$(,28DHLRX&( n&0:DNX        $.8BLV`jt~            $.8BLV`jt~            } "#34HI#$ `a  4R<R^(^jjyOy/yy2 - . / 0 1 2 3 4 5 6   KNOPQSVW$ ={tu*+>,-?   Q   "B*    ! $,$D2Rl|l|$2DR .    .       .        :     J          > ? @ A B C D E F G H I "$%&'()*+,-./0123456789:;<=>@^`cq  !#%')+-/13579:<>BDFH` <     J          > ? @ A B C D E F G H I ">@DEFGHIJKLMNOPQRSTUVWXYZ[\]^`cq   "$&(*,.02468;=?CEGIa B      kXKX^2B @)GOOG --%    ~01ac67Y] %&AEbw~  OP\_'/=?EMWY[]} d q  !_!!,m,-.Ds}!.Ze/ 12bd78Z^ &'BFcz PQ]`(>@ HPY[]_ f t !!!,`,n-.@t~".0[ KMHS {JL@By]><:8654320/.,+)(.Jf2ee`\`eaa^2;?.>D<RfjHhTHI#$)*&'JKLMNOPQR  k r w ~ p q v | v { w | x } y?@AO O P Q R Sstuvwxyz  ?  @  A  B  < : ; t l m n o x s u f y z g h } i j {    DE./$OPQRST-~'()* +,   -   [  O ./0123 4 < f 5 > = 7 P : ; _ ` H z h 8 9 Z j ? u S K v N J M B |6 789:;<= *+>,-?  EFGHIJK U U   W   ! XY * + ,Z[ $ % & ' ( )"#$% " klmnopq%20!1 l k W p T m d c E X q e V o G ^ L g Q C F Dr R ] y U n w x Y r \ t I i s } b a  @ A ~ {  L  M  N  O  P  Q R ! S " T # U $ V % W & X ' Y ( Z ) [ * \ + ] , ^ - _ . ` / a 0 b r T c d e s 1 c 2 d 3 e 4 f 5 g 6 h 7 i 8 j 9 k : l ; m < n = o > p t u _ `=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] ^_`abc defghijklmnopqr~ s  t u  v wxyz {|  } ~  3 4 5 6 7 8 9 a b^*@'eU]M+3!%!!^563d9@]K_L$# +7#34632#"&w2$#22#$2|.%%.,''A$@!]L +#!#`V`pG@D  fe  KL +3##7##7#537#5373373337#~&k&_%i$t{&k&a&i&u``qeeqffqq+"(.E@B.)$#  !JI~g]L"" +5&&'55.546753&&'5654&'Af*)s5M](u]Co[.(Q#6b=jkC?dB!!:b!9F1KYKI)r/H;Ib ds * .f #+KPX@, h g _  K _LKPX@0 h g K _ K _L@4 h g K _ KK _LYY@+%$ )'$+%+##      +2#"&546#"32542#"&546"3254TWRYSUPStu{../TWRYSUPY../ujjwwjju 6\z{{zujjwwjjufz{{z(!-7}@(7JKPX@#_K_K_L@!_K]K_LY@#"53"-#- !! +26673#'#"&5467&&5466"6654&32676:Z4R= :-8,i>zF='5^=-*-(J!@0 8$F1E^#"K&887#p[L[#-L+3J(s#../+1A@]L +#`(b5 @]L +4673#&&(GLzDGFDyLGz[^wt\Xb+ @]L +#6654&'3+GLyCGGDzLGyX\tw^[$#@  GL +7''7'7'PmoLCsl3{ ;: z3+oT &@#Ue]M +3##5#5353Skkkkt@U]M +7#667/kt 5~7;4$I@U]M +75!zz9 @_L$" +74632#"&92$#22#$2F.%%.,''@KL +# 6$ @_K_L$$%# +#"&54663232654&#"1n[x0n[z*98,,89*esX­sX®z{z{z||; @ JKL +!#467'73!RI|U  B[&-@* J_K]L(&( +!!57>54&#"'>32! 6B/()N+RF[@Fe7/Y?\7i8K=#+*&#a/3W7;b`:V&+?@<&% Jg_K_L%%!%$* +#"'532654&&##5326654&#"'6632YAUZ=dtZ.d,QAKC67BE/73LF*qNn*JX TF>c9'83*t+&+$h(Y+ -@*JeKL   +%##5!533#54667#+V9V  i?yB9 &1D@A  Jg]K_L  +2#"&'532654#"'!#66,Af;8c%%h.CG<< '2`Gt7:l  l#,i@  JK PX@g_K_L@g_K_LY@ &$, ,$&$5 +4>32&&#"36632#"&&2654&#"#-Q}Y8-Ya(K<^npIvF,801"11/=ykS/y8eB#0vltCU=@4<-!@)%@"J]KL +3!5!o K_#'56@33"J_K_L,*'' +2#"&5467&&5466"6654&32654&&''>g?I7&E+?qJxP90C@i;%14#"427688 0 /9&M9AR5H/;X0fYI[U@8M&n'"%.-'"'Y'20()!: ,>@;  Jg_K_L &$, ,$&$5 +#"&'532667##"&54632%"326654&&-Q}Y8,Ya(ED[npIvF,801!20=ykS/y8eB#0vltCU=@4<-!@)9, @_K_L$$$" +4632#"&4632#"&92$#22#$22$#22#$2.%%.,''.%%.,'', "@a_L  $" +4632#"&#66792$#22#$2/k.%%.,'' 5~7;4+cq0+%%5%NcFu+/@,eU]M +5!5!+jjkk+cq0+7%%5+N؉uF):@7 J~_K_L(&" %) +754676654&#"'66324632#"&*3-'/**R+51rDhs4' 2$#22#$2$1E% / "k"dM)<4.%%.,''2O?M@ G /0JKPX@&gc_K _ L@$  ggc_LY@KI%'%%&(%%$ +#"&'##"&546632326654&&#"3267#"&&54>3232677&&#"O-D.%8 C/Ya:jH/e DvJiJ:}50vB|]W`^Xt[uAV70I;l)B1@. JfKL +!'!#3'.'44433)46;5 Zk"5@2Je]K]L"!"$!,  +32#!32654&##32654&#Zގ95#;"yXB34yhAE2@Kwh?lB|39h:V+@(J_K_L$%&! +#'#"&&54663232654&#"VZƒ tIIutIV_aTT``Vfx(\oo[[opppZ;@8Je]KL  +2####32654&*%=#ҨQ.1KAFjl1I3|213+.(.@+J_K_L%-$" +%#"'532654&&'.54632&&#"~qY3m68/%>(:5"p8d811N)+.DC7M*_q+%+!&! !1F1`kv( &, 8L/@]KL +!##5!#mL~~U!@K`L#$ +#"&533265AcHGJC2JwEwKYGNS!@ JKL +#3>76W ;AA; &'@$!JKL&& +#.'#3>73>7Ƕa  `[  hh  [6w ,4..3- zFFEApAFEE @ JKL +!##33ާpZp@JKL +3#38&L+ )@&J]K]L +!!5!5!!+V_b}bFb2@a]L +#3#32mmhgf@KL +# 6b@a]L +3#53#mm7g%'dD@Jt +D73#Fu0:b dD@U]M +D!5!aD(^B &dD@ Jt  +D#.'5)+c35.73 ',( *-&u@JKPX@g_K_L@#g_KK_LY@$"  +2#'##"&5467754&#"'663265.nuh#NDI`zz_-((L&1+l:H8( 0B-_bJ,(TYWR+(e0'"94NL"rJKPX@"_K]K_L@&_K]KK_LY@""$' +36632#"&'##"32654&J;\rt^.67<"/+<IIOUUPPQ-,7@4  J_K_L  +"&546632&&#"3267,zDyO8S,#=t=7/H""K d~<sRN-+"KPX@ J@ JYKPX@K_K_L@!K_KK_LY@"" +"&546323&&553#'#'26754&#"[st^;LrJ=21B188 ."= G"/wIIOUUPPQ-$,C@@  Je_K_L  +2!3267#"&&5466"3&&/qG?4W.)X?R~HAtN,83,wH?Hs={__@j8;1B:@7JI_K]KL%& +###57546632&&#"3|RR/W;+H&)NH((FM  m &"-+,)KPX@  J@  JYKPX@"_K_K_L@&K_K_K_LY@ $") )  +2373#"&'53255467##"&546"326554&e9 ~:c/dqsN1ampik976,PFuzw*| %+&yAQXLNF-@*JK_KL"' +36632#4#"#R2XlXC3)I*&_i?v]WH -@*_KKL       +2#"&546#!00!"//l*) )*" 7@4J_KK`L   $" +4632#"&"&'532653H/"!00!"/&6 *&U**) u"1G2R1Nl*@' JKKL +366773#'# 欝@=*3N@KL +3#3㕕N,"gIKPX@_ K]L@K_ K]LY@  "" +2#4#"#4#"#336632366]_R;2R>/rW/, @_K_L$%%" +#"&&5463232654&#">{LwD|MvD5<;55<;5B[B~ZQSSQQQQNL, KPX@  J@  JYKPX@_K_KL@!K_K_KLY@    +2#"&'##3366"32654~\rv\;FyJ :0/>31,+*&G!0wIIOUUP-+, KPX@ J@ JYKPX@_K_KL@!K_K_KLY@  $& +547##"&5463237326754&#"J<\rt]01A54))"/."F[IIOUUPN,`KPXJ@ JYKPX@_KL@K_KLY@  +2&&#"#3366   &F+qT,<4"\*<-,(.@+J_K_L%,%" +%#"&'532654&&'.54632&&#"ut9R),f'+&254A vb3\1-(H%B10/D%SY{+=.LLk$ )>@@=J]K`L  +%267#"&&5#57733#4.G*1M-GR+_$m o  NG?2stpKC"LJKPX@K`L@KK`LY@ #$ +#'##"&533265Cr[3Xj*.D2"F*&_id;;]W9" !@JKL  +333673Мi i"<6:7 N"*'@$"JKL** +!'.'##33>733>773*,?  C@  ACMAAMD"EA/2(>@AG=" @ JKL +373#'#jkss 黻9"'@$JK`L%# +336673#"&'532677g e vN$ /6 "..3UUv9(" )@&J]K]L +!!5#5!3QXXrabb,@)Jgc_L +"&&554�bV\$@==@$\V'.rr.'<0/(u(/0<n*[[*I(K'PX@ KL@ ]LY +3#kk((b{2@/Jgc_L +56655475&554&'523"('.rr.'V\$@==@$\n*[[*n<0/(u(/0<+ <dD@1JHGWg_O$$$" +D&&#"56323267#"& %3<2K;/%4=2K;- "q5  "q5 9L" @a_L$" +#"&546323#2$"33"$2w.%%.,''$F@JKPX@ pg]KLK2PX@!~g]KL@&~gW_OYY@#% +&&#"3267#5&&546675j/G,#=t<8.D'?#Wal3]=M sRN|\`^u= Q((!H@EJe_K]L !! +2&&#"3#!!56655#535466V6a'-#C /0_)/WW:ap%/^kF57 y39Gk_JY)7|G+:@7 J HGc_L$(.( +467'766327'#"''7&732654&#"\ ?H>23->I? =G>18)=G>e6&'77'&6a1>H> ?F?35,=G= 8& %J_K_L*(#!%( +467&&54632&&#"#"&'532654&&'.76654&';'"mY1W)(!E&($17FU"tc5S"&^&7(++2F%m7:.D+<8%?M]"N;1;4$HVe!+;8/ "/#m %dD@W_O$$$" +D4632#"&74632#"&())(())(" "! !" "! 1$=edD@Z.:/;Jgg g W _O&%8620-+%=&=$$  +D".54>32'26654.#"7"&546632&#"3267Pc67dNLe96cPRP-Pm?VNMgfe0\CA:2+;A9B92 6cPLe96cPPc6@NY?nT/NYYNJ~gCg;CUIMR E oT#@JKPX@gc_LK&PX@"ggW_O@)~ggW_OYY@! +2#'#"&5467754#"'663265GEB,J5AZQ0>7 NZ0,%,H=6<58:11B)(.?  0+7'77'(kkkk:: ::+y%@"U]M +#5!5kk$I1$2;idD@^-J ~g  ge W _ O%%;953%2%210/.(&$$  +D".54>32'26654.#"'32#'#532654&##Pc67dNLe96cPRP-Pm?VNM6RL0t[_>2''#,1 6cPLe96cPPc6@NY?nT/NYYNTF@/7 ¨' # Z dD@U]M +D!5!b'9dD@.gW_O  +D"&&546632'2654&#"4N--N44N,,N4 ,, ,,+L11L,+M11L+](#$))$#(+r 1@.ee]L   +3##5#53535!SkkkkkkWV0@-  JgU]M$' +!576654&#"'66323Wm-!'1<P5AO3:3Rk+0+J#?;-J5.SU'M@J%$JggW_O"  '' +2#"'53254&##532654&#"'66=P',2/V[G> A#F"/7-3)1JU=4"1 8#;D"]4 N!E(^B &dD@Jt  +D#5>7B.53c+) (,' 37NF"X JKPX@K_KL@KK_KLY@"$ +#'##"'##33265Fq;+=!YC2"I)*, +/v]W7:)@&J~]L&" +####"&&54663!:OQO)>\37dA' .l[_n.9  3+2dD@'JW`P%" +D#"&'532654&'73BU#)$-&^ 1z8>R K --L &@# JU]M  +#54667'7t&5LT *'>soh >KPX@c_L@gW_OY"$$"+#"&546323254#"hZMH]ZMG^ #BB# !U]]UU\\U11ba0(.?  0+'7'7'7'7?kkkk :: ::5$cdD@X !JU e f]  M +D3354667'735#533#'3547ut&5]~s==a 6 *'>sTJKRJP*02 F)adD@V Jh eU] M))('! +D3354667'73576654&#"'66323ut&5]m-!'1< O5@P2;36 *'>sTRk+0+J#?;-J5.b,D'+6>dDKPX@! ;/ J@! ;/ JYKPX@7g g  U g   f ]  M@>~g g  U g   f ]  MY@+,,((87,6,6543210.-(+(+*) '' +D"'53254&##532654&#"'66323!5#533#'3547G> A#F"/7-3)1J2=P(+10VMut_s==a "]4 N!E=4"1 8#;D6JKRJP*02 @!" 3+&$Cr3+&$v3+&$J^3+&$Qf3+&$j)3+p&$O++3+}pK.PX@'ee ]K]L@-  pee]K]LY@ +!!5##!!!!!%3#}VI@=>|| :Z&&zZ&(CF3+Z&(v3+Z&(J23+Z&(j3+e&,C3+ &,vA3+&,J3+j&,j3+ ?@<e ]K]L   +2###53#3#3254&:p[θCCPtt@kPr#|+||vpZ&1Q3+:&2C3+:&2v 3+:&2J3+:&2Q3+:&2j^3+?? 0+''7'7KIIJ?IJJK:!*<@9%$ JHG_K_L'-*# +#"''7&&5466327&#"4'326It_E,O,10Iu1T#)N+0/ '5`Vj,aTfo\ B5B1[o[=3@0X-LqpY8 U&8C3+U&8v3+U&8J3+U&8jJ3+p&<v3+ZG '@$geKL$"!# +###33232654&##G2udKW{1FF>B=x=jBp{4;43N5hKPX@ J@ JYKPX@_K_L@_KK_LY@ 31-,)'$. +#"'532654&&'&&54>54&#"#46632b**2804sn]7Q"'+''=0))@/5CGyMKwE^%6( !%E;SVv#:(, "&02,G]-%G*&DCG*&Dv*&DJ3*&DQ;*&Dj*E&DO*j--4?@+&%  JKPX@%  g _ K _L@*  W e _ K _LY@#/.=;7521.4/4*(#! -- +2!327#"&'#"&&5467754&#"'663266"3&&3265Ei;G?`Z)XACn#;M8/O0vv]+&'I%0+j9n9 T82<17D5%.>,:nPH?H+s23#-%M;WR)" c@ j8;1B0'"94-,&Fz-$&HCA-$&Hv-$&HJ--$&Hj&*C?Y&*vn&*J@&*ji-> ,6@3J  Hg_L"!(&!,",&+ +7#"&&5466327&&''7&&'"32654&"@n1SIKzLwDLPP.FNF&QQV->&RCO->&Rv->&RJ;->&RQC->&Rj+lU A@>geW_O      +"&546325!"&54632(())(())#')!!)'#kk#')!!)'#->;&<@9"! JHG_K_L&+*" +#"&''7&&5463277&#"4'326>{ 8!K!#'|A8J!$ ;5;5 131%iE(5)$fA0! QQ'SKC&XCbKC&XvKC&XJNKC&Xj9&\vNL$5@2JK_K_KL%#'" +#"&'##3366324#"326Lr\;JL:\rf:0/>31'-7"/IIOUU9&\jm&$L3+*&DLa&$Mt3+*&DMI&$P*-&DP.:Z&&v3+-&Fv:Z&&J~3+-&FJ$:Z&&N3+-&FN:Z&&K3+-&FK%Z&'Kx3+-&G'-w+KPX@  J@  JYKPX@'eK _K _ L@)e  gKK _ LY@ '%+ + +"&5463230&&55#53533##'#'26754&#"[st^;LLLrJ=21B188 ."%5aOOaG"/wBCHMMIIIZm&(L`3+-$&HL[Z&(MH3+-$&HMCZ&(N3+-$&HNZ&(P-$,(/O@L%&Je_K_K_L*)-,)/*/"&&%" +3267#"&5467#"&&546632!3267"3&& $8B)R~HAtMqG?4W.<,m,83oW>2">={__@wH?Hs5>8;1BZ&(K33+-$&HK.:&*J3+-+&JJ?:&*M3+-+&JMU:&*N3+-+&JN:#&* -+&J)=Z&+J3+F&KJְ3+;@8  e  eK L +3#5353!533##!!5!ZZZZZ a____a4XFjKPX@&e  K_K^L@$eg  K^LY@"" +3#36632#4#"##535㚚 Q3YjXC3LLOa )I*&_i!v]WHaO&,Q3+c&*Qim&,L3+?&*L&,M3+\&*M e&,Pq-&LP e&,NL3+ .v&,-H&LM1.{&-J3+n&,JZ#&. ]N#l&N 7Nl"&@# JKL +#'#336677_䪙F"","<)J&/v"3+?Y&Ovְ3+Z#&/ 5E#&O Z/&/' Ұ3+N&O'SZ&/N> ٰ3+N&ON ˰3+ ,@) JK^L  +35'737!Z"7YF9"`6X+`M}> &@# JKL  +35'737N#7Z"9[`7`7Z&1v3+NF&QvZ#&1 N#F,&Q JZ&1K3+NF&QKO&QqZ.!8@5 JdKL!! +"&'53267#0#330.553 0(756~v+2*AFH*?C6meNF, u@ JKPX@!_K^K_L@%K_K^K_LY@    +"&'532654#"#3366322#XD2r [2Xj#Nu"1ok]W"F*&_ia2R1:m&2L3+->&RLi:&2M3+->&RMQ:&2R3+-[&RR:%E@ #"JKPX@#e _ K _LKPX@.e _ K ]K _LKPX@8e _ K ]K ]K _LKPX@5e _ K]K ]K _L@3e _ K]K]K _LYYYY@ %%  +2!!!!!!#"&&5466"3267&&{?V>nEEnUPOU>>||}\oo[~pp  -, '3KPX@  J@   JYKPX@$e  _ K _LKPX@/e _ K _ K _L@9 e _ K _ K _K_LYY@#)("!/-(3)3%$!'"'   +2!3267#"&'#"&&546326"3&&"32654&tJ@7Z/*[A>j%#b:NwD}7b"Fx.<5P;55<;55,wH@Gs%'&&B[&&Lj8;1BQQQSSQQQZ&5v3+N&Uv|Z#&5 gH#,&U Z&5KX3+,&UK.&6v3+-&Vvw.&6J!3+&&VJ.&6z-,&Vz.&6K"3+'&VK#/&7 ##&W /&7K(3+:&W'//@,e]KL +3#535#5!#3#yyyy#|~~| I@FJ e]K`L %# +7535#57733#3#3267#"&&55 >GR+_$.G*1M-a^?2stp^aH o  NGHU&8Q3+KC&XQVUm&8L3+KC&XL|U&8M3+KC&XMdU&8O3+KCE&XOU&8R3+Kn&XRU&5@2JK`K_L#&%" +3267#"&5467#"&5332653 $8BHGJC01/*dW@7;wKYGNS2@k#-GKC"&XPL&:J3+ N&ZJp&<J=3+9&\J"p&<j3++&=v3+&]vr+&=N3+&]N}+&=K-3+&]KN+@( J_KL  +"#46632&&!#/W:0B#(&JFM  m ` %R@O J!Ie_K_L %% +2&&#"3##"&'53265#575466->#!oo$N@3$RR,Q m &"p<2R1u"1H((EN  '3S@P.J ~ egf L )(#!''    +56673&54632#'!254&#"3'.'0 8@C64H 45/J2 X5 $$ +6>>5$~.99 +/&* ">I@< ; ) JKPX@: egg g _K _L@> egg g _KK _LY@,$# GEA?9742.,('#>$>""    +#56672#"&546"32654&2#'##"&5467754&#"'663265 8@c0 4HH46CC6nuh#NDI`zz_-((L&1+l:H8( 0B $$ 5>57>>66>E_bJ,(TYWR+(e0'"94}&vx3+*j&vP:&v3+->&v.#&6 -#,&V (^)dD@ Jt +D#&&'#5>7S-0c?<c0-74 ++ 37(^)dD@ Jt +D.'536673-0c<?c0-^73 ++ 47(^s'dD@U]M +D!5sgg(^.dD@#W_O#" +D#"&'332667jSVdS/0"I\ZK (f (dD@W_O   +D2#"&546y!00!"//*) )*(]E 9dD@.gW_O      +D"&54632'2654&#"6CC64HG5]>66>>57>E(,dD@!JHW_O%" +D3267#"&5467 $8B@.A"&oW>2,L 6(]4dD@)Wg_O"""" +D663232673#"&&#"(K422IL340]NIMJ(^ =dD@2JU]M    +D#5>7##5>7&21O$" /&21O#"  (,' 46 (,' 46^ -dD@"JU]M +D5>73.^?? '\/cmF !OdD@DJWe_O !!    +D#56672#"&546!2#"&5460Cr$$##($$##F !N' V V "! !" "! !" &$S Ѱ3+9+yxx3+h&(sS Ѱ3+&+sS Ѱ3+ ',S Ѱ3+&2=S Ѱ3+,'<S Ѱ3+F&uOS Ѱ3+F&T$Zk%Z;K2PX@]8K9L@]8LY@ +!#ԗ}vGJK2PX@8K^9L@b8LY@+!5.'{  {VWvw(''(Z(+=Z+:/@,e_@K_9L$%&#+#"&&54663232654&#"#5IttIIutIV_aTT``V4fo\\oo[[oppp-|| e,Z.:JK2PX@ 8K9L@ 8LY@ +#.'#6=@@<TZU0Z1( eK2PX@ e]8K]9L@ea]8LY@   +5!5!5!<esQN||||}}:2Z>K2PX@]8K9L@]8LY@ +3!#!Z56LZG3&/V@   JK2PX@]8K]9L@a]8LY@ $1+357'5!#"&'633&6EEwt|}/7p<-/%jK2PX@! gg 8K9L@! gg ]9LY@%%!  +#5.5466756654&t=Iz[\{H=ubMS\\SMXKvF-^O2nn2P]-FvKXR=CS-SC=R;57NK2PX@g8K9L@g]8LY@ +##5"&&55333266557;wz9&M<6N+Xw<=vV:?{>:!VJK2PX@_@K]9L@a_@LY@ !!+"!53&&5466323!56654&Uc8JȵFUSiiSUHK9cWidV(+dZJJ[e+'Udhj&,j3+p&<j3+-q&}S&&SNF&S2N&SFPF&T-q+".KPX@ J@ JYKPX@"~_CK _9LK'PX@&~;K_CK _9L@-~~;K_CK _9LYY@$#+)#.$.  "" +"&54632366733267#"&'#'26754&#"`vyh:J { ,2> L>01A54 ))) zC"u$."0wKRPTUQNq-N@KJg_BK_9K=L)'&$ --  +2#"'#466"32654&##532654&SGp@KDUama>EvF2D>=K4#518+U@HU  Z[itQe0t73=6>0v9.//8""@ J;K=L+#466733>778 ЛP   P")YU"T])HD;G -> -3@0J_BK_9L)'  +2&&#"#"&&54667&&54632654&WDh5;-T+( CDZTyLvD3W6.FyR;(:46<6i4".xN~4dHE]8S7IM'E4/?C?4G&,)E@B !  Je_CK_9L)(%,%$!+#"3267#"&54675&&546632&&#"3RMB%s =2 =>NF"w,"2JK2PX@ ;K9L@ ;LY+336653#j91%]T"DKŎlT-G4+@(Je]:L!%!j+74675&&54670##5!#"33#"#6654&'&&-M=38DI"0* %6]:8OQRUK"=*YE*,9dsB[ >25?mg8)*,g<<++ @-,X#&Q  f->,R "KPX@J@JYKPX@];K_9LK2PX@];K9K_9L@ ~];K_9LYY@ +%267#"&5###57!#{ <EQrW]sk k LTP@2r;=,!;@8J~_CK_9K=L!!#"+#"&'#0#4632%"32654&=zh&H |IsB659800)=C~IHSLWWL-G, +@(J_CL  +2&&#"#6654&'&&5466>[S+%C>7"?)YE*,9dsD{,'q`_-/ B/-]$)U  p_x:-y#!@];K_9L%%&#+%#"&&54663!#32654&'#"F>xXPxCJ_$46>=6 LEDn@?{Zd|9m&d"FVPA2FP"$@!;K`9L +"&&5332654&'3Abm,38:; EtI)G?Vh@tBAsF--&,@) J_CK=L&&*+54632#5.546"6654&&l&*M7nUm~RMN{G>DQ'-F4fBMH `c{]~D>tZV2'/dK+H+V)$K"PX@"!J@"!JYK"PX@_CK`=L@;K_CK`=LY@ $$+2733267#"&''#'&&#"566u/:&$_$ +BK3A% ,)>1l!tHG&w F0@-J:K;K9K=L+6654&'3#5.53PK&NRSI>PsJ^BFg|:8ydUG5*"+4@1 J~;K`9L++#%%&+#"&'##"&&5467332655332654&'1bI@II@Ib1-2-!!-2- "HPQ|F8//8F|QPHBJI[@::@[GJB&jqFP&j ->&RSFP&S5*&SZ&(j3+ KPX@ J@ JYKPX@ e]&K_.L@$e]&K'K_.LY@  +"&'53266554&####5!#32)4,). ֪pxe } #>+#L~~e\?ekZ&`v3+:F@C  Je_-K_.L +"!!3267#"&&546632&&Qk5c[0a93b>wJSt@l46,QV[U|W_[nl^{.6 e,j&,j3+.-"+KPX@ JKPX@ J@ JYYKPX@ g]&K_.LKPX@*g]&K]'K_.L@(g]&K]'K_.LYY@+)%# "" +"'53267>7!32###%32654&##P'!  8_z;ǎ  +K.>GMC# |=T g8b?juL.loc&>X.,79$ZKPX@g&K^ 'LK'PX@"We&K^ 'L@#ge&K^ 'LYY@%! +333332###%32654&##Z8_z;.=HMC#8b?ju4|,79$-@*e]&K'L#!+#32#54&####54ּeq"'~e\+#L~Z&v3+ &&63+Z0 #@ &K^'L+!##5#3!3ђҗL$ZQ 1@.g]&K]'L !+3!!32#'32654&##Z93EOVJ'}8b?ju|,79$Zk%Z`03@0Q]&K]'L +3#5!#3>7#!_A7&B4 '6 NJ҄~FGZ(%@" J&K'L+333###sWZZjj.]+?@<&% Jg_-K_.L%%!$&)+#"&'532654&##5326654&#"'6632GfObiQ}.NP"_U|iC7##ZJ>;+6WA<-Z&&3+Z @ J&K'L+!##33jZQ@ JKPX@]&K_'L@]&K'K_.LY$(+!###"'53267>7!  +K<'!  L.loc&>X. |=T gZU0Z+:2ZmZG3:Z&/7 -@* J&K`.L%$+#"&'5326733667DgT>92,  |Eb4 1#7:,-/r;Z0 MK'PX@R&K^'L@a&K^'LY@ +%#5!3!3xL5~)@&Jh&K'L##+!##"&5332673~?i5dq09*T4aZ'44@Z @&K^'L+!!33333~ޘޗLLZ0FSK'PX@R&K^'L@a&K^'LY@ +%#5!33333FޘޗxLL 1@.g]&K]'L !+3#5!32#'32654&##J93EOVJ'L~8b?ju|,79$Z0 6@3g&K^'L    ! +3332#!3%32654&##Z1c>|(CMOH!8b?ju6|,79$ZQ +@(g&K^'L !+3332#'32654&##Z93EOVJ'8b?ju|,79$#]F@CJe_-K_.L +"'6632#"&'53267!5!&&0^-05vCJw=c39a0\d5^Vzn[[[|S]Z!KPX@e_&K_.LKPX@#e&K_-K_.L@'e&K_-K'K_.LYY@ $%"#+#"&&'##33663232654&#"EldIlD!NSVLLTUNfo\O`膝[oppp?3@0Je]&K'L'+#.54633##"33J:&K;@>AG: /O:ci6<*1/7*-D-A+4@1(JHg_.L%#+++4676673>32#"&2654&#"-4n:"QP2B$ +?*bsDxOy/;+5 7&5G  !HD'yv]}>5@RAM( 3^;NC""/@,Je](K]'L!$!$!*+#!!24##3264&##3262769E1jV@e;OiX/131be*7,> A1/J*"?A2l N"@](K'L+##"pN"<"3@0Q](K]'L +3#5!#3>7#39P.$2p *&"K17Rp[G-$,Hi",@) J(K'L+###33VܟЋПɚ‹"  &,*J@G('Jg_/K_.L%#**+2#"'532654##532654&#"'66;g?<1#8!9x\J"c8=T:7JQ4;'](,/o,@21: 1)-M/"{#Ag!hN"#@ J(K'L+3#54667#"24)B9"N &&LNk" @ J(K'L+3##3ߕ""7"Q@  JKPX@](K_'L@](K'K_.LY##+!###"'532>7!7 +M?4! Yw $^N"'@$ J(K'L+#467####3pӈ" (L `!K-"rNH" '@$e(K'L +353#5##ЕЕ""->,RN>"!@](K'L+###>ƕ"N"NL,S-,F"@](K'L+###5!Np9"\-.@+ J(K]*L+#5&&546756654&ݎALJCCHJ ss ru ZACYFYCAY="[N<" #@ R(K^'L+#5!33332ڕP"NK<B"/@, Jh(K'L#"+326753#5#"&55B+J%#Z9Uf"GWYN{" %@"(K^'L +!3333{ӕ""NNN<"-@*R(K^'L +3#5!3333{P "K"NN" 6@3e](K]'L   $!+32#!#5#32654Ci~ypfh+8"PQP^pƁ&<N" 6@3g(K^'L    ! +3332#!3%32654##NH}xp?E,7eC"PQP^"g&<NC" #@ e(K^'L!"$ +32#!34##326i~ypefh+8OPQP^"<$,F@CJe_/K_.L +"&'53267#53&#"'66324Q#$P,3=b#A*\4HrC t:Ccy f2xkN@,_KPX@e_(K_.L@'e(K_/K'K_.LY@ $%""+#"&'##33663232654&#"@vebdgIsA07600760sx"jrB~ZQSSQQQQ" +@(Je](K'L!#&+3#7&&54633#5#'335#"*A|aRb6+Sf(&MCRW˭"''-$&HjF,@ JKPX@/ e _ (K ^'K_ *L@- e  g ^'K_ *LY@&$"! ,, +"&'532654&#"##53533#366322#.*C3LL Q3Yj#Nu"1Q56]WHaOOa )I*&_i2R1N&v-,F@C  Je_/K_.L +"&546632&&#"3#3267?~D{R[S+%Cd b0N(#K m}4$jyc}r-,VHL@&*jiMU"!KPX@ J@ JYKPX@! g](K_'LKPX@+ g](K_'K_'L@) g](K]'K_.LYY@!!##$! +32####"'532>7#32654"Eyum~t +M?4! ?A)4"PQP^Yw $^Ɓ&<NU"fK"PX@ g (K^'L@# We (K^'LY@$! +32##5##335#32654"Eyum~ݪ?A)4"PQP^"Ɓ&<FNk&v9 &\&N<R" #@ (K^'L+!#333## ڕ"NZ1a%@"]&K'L+!#!51TaʗNFKPX@&K](K'L@](K'LY@ +##!5N"&:C3+ N&ZC&:vb3+ N&Zv*&:j3+ N&Zj|p&<CQ3+9&\C6(E@U]M +75!(pp(E@U]M +75!(pp(E*dD@eU]M +D!5!!5!aaZDD @]L +'6673/k 5~7;4 @]L +#667 0k 5~7;4t 3+ @]L +#&&'7k/4;7~5  $@!]L    +#'667##'667/y/;4 5~7;4 5~7 $@!]L    +#667##667 0k[ 0k 5~7;4 5~7;4t  3+< @ JKL +'#5'37Ȣ,v7)@& JKL +7'#75'75'37'+vvphvvh0H @W_O%" +4632#"&0Q;&@&R:;QmTGD8SHH9&': #/7?KPX@2   h g_K  _ LKPX@6   h gK_K  _ L@:   h gK_KK  _ LYY@;9810%$ =;8?9?530717+)$/%/##      +2#"&546#"32542#"&546!2#"&546"3254!"3254TWRYSUPStu{../TWRYSUPTWRYSUP../M../ujjwwjju 6\z{{zujjwwjjuujjwwjjufz{{zz{{z.7@L +3#`.$@!]L +3!3 Á(.H0+7'(kk::(.H0+'7'k :9&@A@KL +#Atu63zmGQ)&f 3+7@4ee]K L +35#53!!!!3#ZIIW||WW(('Z@WJ  e e_ K]L#"!  '' +2&&#"3#3#!!5665#535#535466X9\'-&@"#) )_0(WWWW8bp%/7V8W>y=$W8V8JY)Z` ,@* !JKPX@/ eg ] K  K`LKPX@3 eg ] K  KK`L@6   ~ eg ] KK`LYY@% ,,)(%#      +2####32654&3#3267#"&55#5775~k) CF>#%<#FSGR+wh?lB|3944=tf! e H^52s 40`@]J+I e f_ K_L-,%$#"  00 +2&&#"3#3#3267#"&'#53&5447#53>2S'0": ;N &CE.uC:9A P|qEAV Ww }vWVPr=*4k@h  J ~~g g  gW_O31/-)'#!  +"&546632&&#"3267##"&546323254#"Ma-N27)&!!#32tuYLG[YLE]#??#qVW@N# L 5++2 P Y6U]]UU\\U11ba0)o@ ' JK PX@!pgW`P@"~gW`PY@$"+%2673#"&555667546324&#"669&eRdWf--`\O]oj=/*]11_icb> ` ]QUIaz1r(2 $%WB!+/`@]JgU   g  U ]  M,,#",/,/.-'%"+#+!!+3330.53##0%"&54632'254#"5!B sH]ZMF_[KBB# y8%32!32675&&#"5Us;.K\.IvDlN-IV"#;TTL42GN~HHhD C|U%<6%>%>&"C*6C@  =%JKPX@#h ]K _ L@'h ]KK _ LY@"877C8C20 ** +3354667'73"&5467&&5466326654&#"2654&''utn&5]RV.$)F*?X)!5\K6 *'>sTG8)01%$177%/0)8J   U'+DP]0KPX@! W?2 J@! W?2 JYKPX@5  h  g_K_K _ LKPX@9  h  g_K_K K _L@=  h  gK_K_K K _LYY@+RQ-,((Q]R]LJ:8,D-D(+(+*) '' +"'53254&##532654&#"'66323"&5467&&5466326654&#"2654&''G> A#F"/7-3)1J2=P',2/VJutRV.$)F*?X)!5\K"]4 N!E=4"1 8#;D6 G8)01%$177%/0)8J   /^!:FS@   M5( JKPX@5  h  g]K_K _ L@9  h  g]K_K K _LY@*HG#"GSHSB@0.":#: !! +33"&'532654#"'73#6632"&5467&&5466326654&#"2654&''ut#CB&.R " 5 AZ]RV.$*E*?X)!5\K6 ^#>ZBEBGNG8)11%$177%00)8J   E #/<@ 6JKPX@+ ~h]K _  L@/ ~h]K K _ LY@$10 0<1<+) # #   +33#5!"&5467&&5466326654&#"2654&''utFRV.$)F*?X)!5\K6RZIG8)01%$177%/0)8J   3^  cK PX@W_OKPX@_&L@W_OYY@ "#+#"&&'33267 0fVXc*/4+7 4O,*O67$'4XS @J]L  +#5667S(W !M( V #T ^K @J]L  +#5667K( V !M(rO 6@3JUe]M   +##5#533#547r=ts= >JJKP*02 ])]LB@?JegW_O#%$2 +#6632#"&'532654#"'7@ AZ]Y#CB&.R " 5LZBEBGN ^#>cL*@'JU]M +#5!KFRZIeV$19@61JgW_O,*$$  +2#"&5467&&5466"6654&32654&'?X)!5\KRV.$)F)V77%/0)8JG8)01%$1M   )E +:JV^bfosw}!K PX@B / .JK PX@B / .J@B / .JYYK PX@5  p)%!&&%p  4321e  U W  g7  geU6"g$"#!%"!e/-+(&''&U/-+(&&'^<0;.:,9*8 '&'NK PX@5  p)%!&&%p  4321e  U  g7  ge6"g$"#!%"!e/-+(&''&U/-+(&&'^<0;.:,9*8 '&'NKPX@5  p)%!&&%p  4321e  U W  g7  geU6"g$"#!%"!e/-+(&''&U/-+(&&'^<0;.:,9*8 '&'N@5   ~)%!&!%&~  4321e  U W  g7  geU6"g$"#!%"!e/-+(&''&U/-+(&&'^<0;.:,9*8 '&'NYYY@~~xxgg-, ~~x}x}|{zywvutsrqpgognjhfedcba`_^\YWUSOMJH=;7631,:-:*($"   =+53#5!53!53!5353##53#53#"&54632"'532655332##'32654&#"32654###53#5332654&##53#53533!5353!53!5335355^j5566G>BB>>BB>2 =6T575.e "" "" ' +T66j55B$266j55`6^x_56^666666^6^BQQBCPP22-! )!"',-33--336K򄄄_55_555555)d+C@@JGU_O*($"%-+ 54676654&#"663232654&#"56!++\P*X"(!>!& ())(66{">1CJW"7'##%o&,K 6@]@ZJ~~ g _ BK_9L87>=7@8@0.*(!66 +23##"&&54654&#"'6632326544'.5466"&&aYq? G?FuWW\# %K,+3*2GH}B,[J#$TG FHRpDz_6/J)3 Y.+A#,pyAe9/M.p4"QXtK'PX@  J@  JYK PX@~8K9LK'PX@~@K9LK2PX@~@K8K9L@~@K8LYYY%%+6676632&&#"#37<9/& 6:Dz,& p]u;".D@A J~] ;K_9L*(%$!%% +##"&'##"&5467#57!32655332654&~om@II@nn UYZ-2-!!-2-"p-a/y8//8y/a->2p.]-IG@:CC:@GG,`ZU&0vU3+N&Pvs&$F*-&DF}+ 9dD@.gW_O      +D"&54632'2654&#"6CC64HH4>66>>57>E:W$/@,JK_K_L$(&# +#"&&546632665332654&#"IttIIuV)C?V_aTT``Vfo\\oo[4/G0 ReNhppp-t$/@, J_K_L$*%" +#"&&54632665332654&#">{LwD|7b"(! #F= 5<;55<;5B["! K4 1R<D'QSSQQQQU.@+ JKK`L#) +6653#"&533265$" +YQAcHGJC\ I4 6Z= JwEw?OEKJK&tcJKPX@]K`L@!]KK`LY@#" +#'##"&5332653665 +[Rr [2Xj*.D2%$t 7Z> qF*&_id;;]W9 K4vST>)@&JW_O%& +#'6654&#"56632OU % 0DDL#HR6Z&(CF3+Z&C3+-$&HCAN&C$(@%# J&K'L$$+3.5336677&&'33653#&&'+M;"'4 < #7#/1[a)GSB^tѮ=&\&5b:dͽKm@DN"%(@%!J(K'L%%+#&&'#.5336677&&'33667N&L<9g!@4 +": %)/"`c0h23HY/:r+j2RETφt9@6eg&K^ 'L! +3#53533#32#'32654&##}}}93EOVJ'p^^pD8b?ju|,79$s@@=  e](K^'L$! +3#32#!#535#32654i~yp~~fh+8bpcPQP^pbd&<Z&KPX@# $ J@# $ JYKPX@" e_&K _ .L@* e&K_-K'K _ .LY@! && +"&&'##33>32&&#"!!3267mMeiYd9q11,X*Pe +.R;/^82a O`WI{UO~7W2N,$KPX@! " J@! " JYKPX@" e_(K _ .L@* e(K_/K'K _ .LY@ $$ +"&'##33>32&&#"3#3267Tq ll EkB4\*A#23=3,P$#Q sx"T`(f 95oC9t 0@- Jf&K'L +#####3'.f12e]344[ *0- ,,,0)" ,@)](K]'L +#'##5###3'.J&'JZ""V0*)'+0Z dJKPX@ f &K'L@  U  f &K'LY@ +#######333'.f12euk]3444[ *0- ,,,0)N"aKPX@] (K ]'L@" U] (K ]'LY@ +#'##5##7##337#3'.P Q_Y[ s ""V0**/3@0 J]&K'L+#'&&'##7667'5#ѻ@TJ<)$&),]W"4F<& !'=F4enZ #F@C# Je] &K'L"!  +#'&&'##7667##3!'5#޻ARJ<)$%*0RZ^IZc10(8L%%' /!o=_\*h^^U|iCHX  YG^w  p [A0H)/83/v*%+ e&F 43#UKPX@MRJG  F 'J(G@" M RJG  F 'J(GYKPX@-  ~  gha_.L@4  ~  ~  gha_.LY@ONDB><;964/*%#"UU +2&&#"#"326632&&#"#"&546632654##532654&#"'667&&'53> (6I8. 64yf*+$,$-%$& .!X<]P*dVB?:7JQ4;'](,=#1`4-:JB51: 1)-M/   p RD3J)!Ag!h 6 23#57tF:7@4e_-K_.L&#+#"&&546632%"!&&267!IttIIutIPX g WPTX Yfo\\oo[[ZPPZbXXb->, 7@4e_/K_.L%"+#"&&54632%"3&27#>{LwD|MvD___B[B~HnnqqR@  JK'PX@_&K'L@&K_-K'LY@+"#36677>32&&  S*<1 / R0, D,@&&D(>X. vZ&2@/J~(K'L +2&&#"#336677> * ќd  <$7&s" =;3@&d }3+Z&e W:':E@B%:32J_-K(K_.K`*L%#&&&#+#"&&546632326654&&#"%336673#"&'532677@ss@@ss@H>@GG??Hg e vN$ /6 fo\\oo[[oKl::lKKl::lq..3UUv9(-,&R\W: -6@3(#JgW_O'% +"'.54676632'66326654&'#"'9c> #$ { "" FB@F $1FAB=8 `d  •8y]\x%x\]y-zL..@+ ,&JgW_O**)%+#"'&&546766324&'#"&'663266zzk#: e~yn""g$'&#%(  )&v7vvw;ST:=UV:-(^@O3 N4 C@ \ J ~gg_+K  _-K  _.L*)ZXSQLJFDBA><861/)^*^((#!#"%+54>3233#".#"56654.54632"&&54632&&#"32675332654&#"'6632#"&'h)0&IR36K4(&#%**EjA'V52256654.546"&546632&&#"3267'332654#"'6632#"&'%JR36K4(Y)0=&))E*'_z[S+%C>7A8*9sD{,'qRSOQ  m}43/v 0+'''7'77'7>Y$b#X?X"a#v$P<:*7*75310.,))'%#"  ""(+D6632#&&#"6632#&&#"!6632#&&#"6632#&&#"!6632#&&#"6632#&&#"!6632#&&#"6632#&&#"2205'% 2205'% 2205'% |2205'% 2205'% K2205'% 2205'% 2205'% Y+45*,35*,35*,35*,35*,35*,35*,35*D#,5>GWdD@L75,+('# <;21JeU]M???G?GDC+D#'667'&&''766&&'%&&'5&&'%'66#667 D !40'"$P0"U)^$&X&&X&)^$2(!5j0"U'"$P' !/ )^$&X&k$P0"U'!50( D !/ !/ D"U'"$P0'!4A&X&)^$Z0%M@JJ g   a&K'L%%$#"!  +"&&'3326737#467##3333Yb*.4+7/f\JW+N68#'44O,-W1r).l+N< % JK PX@$ g   a(K'LKPX@&   a _&K(K'L@$ g   a(K'LYY@%%$#"!  +"&&'3326737#54667#333eYb*.4+7/fCG^*O63('44O,?;">=KQ>@;e g &K^'L%! +3#32###535#32654&CC'3EOVJ|L8b?ju|Jr,79$C>@; e e^'L$! +3#32#!#535#32654㸸i~ypLLfh+8maPQP^*am&<ZG5@2Jg]&K'L'2!'+'###323227'7654##G+1+J8)9A< %K1xE9e!=7P w56F,hNL,,|@ ('&JKPX@_(K_.K*L@!(K_/K_.K*LY@$!,, +"&'##336632'"3227'76654z;FyJ:\r&!.M5H:0/>>R4 +0G!0Mq$<:DIIOUM<@- -@*e]&K'L +!3###53ėCC||#|+" -@*e](K'L +#3##5#535CC"yjssZ"z@ JK2PX@#g]&K'K_*L@ gc]&K'LY@ ""+"#!!6632#"&'532654&C. @"6o_:KyE6E#>#HKt|!L^iH iQc^N 7" G@DJg](K'K_*L 4%&"+#632#"&'532654&#"#$%HyJBmB >:2DGS "yv?|]az;  GRCR"08@5 J&K^'L +3#5###33𥛒RjjsWZZ<"5@2 Ja(K'L +3#5###33VɇFЋПɚ‹"  .]&^&,&^Z01@. J&K^'L+3#5##3ZjZN<".@+ Ja(K'L+5##333CߕѤؑz" Z-@* Je&K'L+%#5'#375373#'oA==A]URkNk"3@0 Je(K'L+73#'#5'#375PdrF''Lb0"13@0 Je&K'L +53533#3##0VV |BB|Zj k=@: Je]'K(K'L +3#3###535㚚ѤߕLLOa HaO +@( J]&K'L + ###5!6jL~Z" +@( J](K'L +###5!ߋ9"p Z050@-ea&K'L +5#!#3!33嗗4N<"0@-ea(K'L +5#5##33533IЕЕ"KZB -@*e]&K'L +33!!##!Z6~4N" -@*e](K'L +3335!##5#NC"pNZ3'w@ JK2PX@$g]&K'K_*L@!gc]&K'LY@'''%'2 +6632#"&'5326654.#"###{&G3j[7KxF6F">#+B&*BK!'!L^iH 0T6AM' LN V"!E@BJg](K'K_*L!!$%&2 +6632#"&'532654&#####/  EuFBnA> :1EA@"?|]az;  GRCRN":3?O@L=  Jc_-K_/K_.L%&4%%#%&+3267#"'#"&&54632&&#"3227&&546324&#"66$0  !4TGBiK G&)VKhP*gW2Y6*KC`?v0 Zk u txsuFqh,bU5==3;VW-n,3>`@]9")#J gc_/K_.L544>5>-+'%  33 +2&&#"327&&546323267#"&'#"&&5466"6654&,;!%:/=. RP+I-0  '$F6%Ms>6q,, oYRTH>2QW#L"@J(K*L+#53366773jԤV  W"PPp1@. Jf&K'L+35#535333#휜휜~&L~>"/@,Jf(K*L+3##5#53336677>ԎԤV  W"pp"PP0/@, Ja&K'L+5##333ASާpZ<r"/@, Ja(K'L+5#'#3733Xssjkq~Ļ 01@.R]&K^'L +5!#5!#!33˳L~~2L< "1@.R](K^'L +5!#5!#333|ƕppK508@5Jha&K'L##+3#5##"&533267~?i5dq09*T4aZ'44@<<"8@5Jha(K'L"#+3#5#5#"&55332675B#Z9UfB+J%"KWYG5~;@8Jge&K'L+##5"&&5353667~?6AFrD.7A:!6 QJ'34 @<B"<@9 Jhe(K'L2+#5#5#"&5533536675B+=  Uf&%=*" vjWY&!}v Z)@&Jg&K'L##+36632#54&#"#Z?i5dq09*T4aZ44NFKd&-@ JK,PX@&h _ -K(K_.L@)~h _ -K_.LY@('+*'-(-$" && +2!32667#"&&'#"&546733366"!4&zDc`7o\,kkW PP r MajP_s#Zf%+O`D;3%~UPNW,")@  JK PX@(ph _ /K_.L@)~h _ /K_.LY@$#'&#)$) "" +2!3267#"&&'"&54733366"3&&tJA6Z/)ZAO|KKUdi.=5,wH?Hs5mT2;.# noj8;1B0d(/@ JK,PX@#ha _ -K(L@&~ha _ -LY@*)-,)/*/&$ (( +2!32667#5&&'#"&546733366"!4&zDc`7o\%hN} PP r MajP_s#Zf%%~D;3%~UPNW<,#*@ JK PX@,p h _/K_'L@-~ h _/K_'LY@%$('$*%*"# +&&'"&5473336632!3267#"3&&fTjKUdhtJA6Z/>WA.=5ue2;.# nowH?Hs8;1B e,&&3+i &&Z&z@"JK2PX@&~|&K'K`*L@#~|d&K'LY@&&&%'!+32#"&'532654.#"#36677 0k^:KyE6E#>#AR,GO$/$Dx\iH ^\AM' 2N e"=@:Jg(K'K_*L$%#+%#"&'532654&#"#3732cBmB)2+1FPM, ϳAk?(`~> ~ FNNS"8s02KPXJ@ JYKPX@Q]&K_'LKPX@a]&K_'L@!a]&K'K_.LYY@ $(+%3#7###"'53267>7!W\  +K<'!  ÂL.loc&>X. |=T g<"K'PX@  J@  JYKPX@Q](K_'LK'PX@ Q](K'K_.L@!a](K'K_.LYY@ ##+%3#7###"'532>7!7GC +M?4! mYw $^Zd@ JK2PX@e&K'K_*L@ec&K'LY@%$+#"&'532655!#3!@wR1K"A-@C嗗bVK aQN H";@8 Je(K'K_*L%$+#"&'532675##335H?mF&9:0;Е"Vr8 AL"Z0F*@'ea&K'L+%3#7#!#3!3W\嗗4N<"0@-ea(K'L +333533#7#5#NЕGC"K50~2@/Jha&K'L##+!##35#"&5332673~~y?i5dq09*T4RaZ'44@<<B"8@5Jha(K'L"#+##35#"&55332675Bp#Z9UfB+J%"1lWYGZ0.@+ Ja&K'L+!##3333#7#4>7#ΩΣW\0>D<"R?D: N<"0@- Ja(K'L+%#7#467####33GCpӈm (L `!K-"rK e,&$&=3+* &D&&$j)3+*&Dj}*j-D&(&3+-$ &H& P C@@Je_-K_.L   +"5>32#"&&55!&&267!>pVNjGuRNnzD cMMaPW&#\oqY_t#ZfVONW+",)P&jm3++"&)j&j3+i&j.]&j3+&&j(H@E  J~|]&K`.L%$%+#"'5326654&##57!5yzAh~^2r/>G[p<am^>c9'1,4j}"A@>  J~](K_*L&$)+#"'5326654&&##57!5Xh.@f{]0o.=D"UL:"a Eh?Em?'$:"$<#jrZm&L3+N&LZ&jx3+N&j8:&2j^3+->&Rj:b->,c:&bj`3+->&cj#]&j3+$&j m&L3+9&\LP &j"3+9&\j &R3+B&\Rf5~&j(3+<B&jZ0 (@%a]&K'L +!3#5#Ԓ}5N<" (@%a](K'L +#3#5#䆆"p"Z0&j3+N&jz&{_s"@ JK2PX@-  ~ e](K'K` *L@*  ~ e d](K'LY@  +"&'532655#5#535!#3#3)  CC|Hh!@syjs_VPI@F J~&K'K`*L +"&'532655##3333#Jާ#Ku"1%pZ2R1c"s@ JK2PX@ ~(K'K`*L@~d(K'LY@ +"&'532655#'#3733(  SssjkqoHh!@ VP/@, Jf&K'L+3333####7ʭ‹ |.="/@, Jf(K'L+3'3733##'#7#2ijkjfsseL֮pܻ-$ 2@/g&K^'L      +!"&5466333'35#"T>e<3'JVOuj?b86|$97,-+G-I'>@;J~g&K`.L%#%$## +326553#"&'#"&546633#"3265&*,&j3_U=ss;z_8#CM2('2 27g]%$uj?g<r,<4--P#/K'PX@ J@ JYK'PX@&~ _/K`.L@0~ _/K`.K_.LY@%$*($/%/##$%## +326553#"&'#"&5463230&&55"326754&'+*%{i>H8?woZ8Ia20c:/-%"27zg] #."%3UPIIOU <-K.PX@+*J@+*JYK.PX@e_-K`.L@&~e_-K`.LY@(&! --+2326553#"&54&##5326654&#"'66nYAVY ##+'~iqxSgSSAI473JL*vYQJX TF+27g]pj,=j.&+ e(,,K'PX@*)J@*)JYK'PX@e_/K`.L@&~e_/K`.LY@'%! ,,+2326553#"&54&##532654&#"'669c=7/7='+*%{ggL>KHDK27%X&,.k,@21:  7/ 27zg]HH4/g!h 0%F@C#"Jea_-K'L  %%+23#5#54&&##5326654&#"'66sYAVY&XJYYFP 8<6QL,zYQJX TFN/j.&+ e(&<,$F@C"!Jea_/K'L $$+23#5#54##532654&#"'66;g?8. 6PMJO4;'](,/o,@21: 1)2ğJg!h)<@9J~]&K`.L))$(##+326553#"&&5##"'53267>7{&++'~iEi;  +K<'!  #!27g]'TF.loc&>X. |=T g\"!6@3J~](K`.L###"+%326553#"&&55##"'532>7!('+*%zhDi:z +M?4! $!27zg]'UFYw $^ZK PX@e&K`.LKPX@#~e&K`.L@'~e&K'K`.LYY@## +326553#"&&55!#3!$))${gCf;%"27g]&UE~Nm"KPX@e(K`.LK'PX@ e(K'K`.L@'~e(K'K`.LYY@#" +353326553#"&&'5##ƕN)$ygCf:ƕ"D27zg]'TF*":"3@0Je_-K_.L&%%%+!#"&546632&&#"3266'#U!K|[UqPv04!^ICX*+\J?G7!3r3#  +K<'!  Ï#Ku"1%L.loc&>X. |=T g2R1"" KPX@JK'PX@J@JYYKPX@"](K_'K`*LK'PX@&](K'K_.K`*LK2PX@-~](K'K_.K`*L@*~d](K'K_.LYYY@ "" +"&'532655###"'532>7!3$)   +M?4! {Hh!@Yw $^KVP.&$ *.-&D f&$K3+*>&DKW&$V3+*p,&DVT&$W3+,&DWU &$X3+*Fg&DXV&$Y3+*y&DYT.&$' J^3+*.&D&J3 [&$Z3+*L&DZX&$[3+*L&D[W&$\3+*n&D\X&$]3+*y&D]X.&$'Mt 3+*.&D&MI VZ.&( g-.$,&H `Z&(KT3+-$>&HK_Z&(Q:3+-$&HQ5Zl&(VP3+-s,&HVW&(WR3+$,&HW[Z@ &(XP3+-Gg&HXWZ&(YP3+-$y&HYWZ.&(' gJ23+-.$&H&J- ` e&,K3+9>&*K .e&, H.&L :.&2 -.>,&R n:&2K3+->>&RK_:&2V3+-v,&RVZ:&2W3+>,&RW[: &2X3+-Jg&RXZ:&2Y3+->y&RY\:.&2' J3+-.>&R' nJ;:W&Gv 3+-&Hv:W&GC3+-&HCO:W&GK3+->&HKe:W&GQ3+-&HQC:.W&G -.t&H pU.&8 K.C"&X vU&8K3+KC>&XKmU&Iv3+K&&JvU&IC3+K&&JCbU&IK3+K&>&JKpU&IQ3+K&&JQVU.&I K.&t&J w.p&< p9"&\ p&<K^3+9>&\KCp&<QE3+9&\Q*-bw&Bu"^, /@,JeL +6673##&&'#56673 v<J+O44O99- 3_** J""J^, =@: JeL    +&&'5356673#&&'=v+99O453 - _ J""J **"^g%n@ "JK PX@og_L@g_LY@%%!   +2#'6654&#"56#&&'#5667u.7 >#  C9O44O9g#'!9 >s"J ** J"$^y&x#JK#PX@  gh L@) ~W g`PY@&&"!"""" +#"&&#"#66323267#&&'#5667r352*=52 2)4?E9:EAy1B1B"@ $$ B"$^L @ JK PX@pdLK,PX@~dL@"X`PYY@    +#5667#"&'332679@ [SVUI8+)9L : 5II\ZK*),'$^L @ JK PX@pdLK,PX@~dL@"X`PYY@    +#&&'5#"&'33267 @9%[SVUI8+)9L5 : II\ZK*),'$^n"@  JK PX@pgcLK,PX@ ~gcL@*~|gW_OYY@""   +2#'6654#"566#"&'33267*14[SVUI8+)9n!!6 3kI\ZK*),'$^y#?@< ghc L##!"""" +#"&&#"#66323267#"&'33267s442+=62 2*6UVS[K9)+8y1B1BBOQ@"$#@  J`*L%%+4&'3#"&'5326d&"W%3B8$ o6 ;*2>W}2@/J]'K_*L +"&'532655#53b3# #Ku"1%}2R1/&7z&WzL) JKPX@,e _K]K  _ L@0e _K]KK  _ LY@!%#))  +"&'###53533#36632'2654&#"z5AVJC8 gu8dBjz|08=%b71KM :@7 JK_K`L    +"&5336632'2654&#"O|N;[sBsL367/;47 ;"/_~>xLWSQKGSH#C7@4J_K_L  +"&'532654&#"'66329X.3S+^``X+L$1.q=kII sp{\lo]:o)L@I&  Jg_K_L%#)) +2&&#"&&#"3267#"&&546632546|' 1(Q'W\U^+X3/\9oDNl-,Zo r(qqr}[nl^ PL-K(N@K %&J_K_K_L#! (( +"&54663254632&&#"&&#"3267,zDyO  VE& ,#=t=7/H""K d~<)\P rLnRN$ [K PX@p]K]L@~]K]LY@ 6! +3#"#&&5466332#'3254&## v#RDp[θ3AhcQN!.M/Pr}vp-$ 9@6g]K]L    +!"&5466335!5!'35#"H?g73'JVOri?d:}6|%97+-+%KPX@ J@ JYKPX@"]K_K_L@&]K_KK_LY@!%% +"&5463230&&55!5!#'#'26754&#"[st^;LrJ=21B188 ."%3IpG"/wIIIQPKPQ-%>,k" "3+; /@,e]K]L  +35!5#535!5!;}||6P+Z,J@G%Jg_K_L  ,, +2&&#"33#"32667#"&54675&&5466KW8B*`8@B&_T@;Je]K_L  +"&'53265!!3#>, _ r||\T:o/[@X+  Jge_K_L*(#!// +2&&#"&&#"32675#5!#"&5466325466' 3!T.Ba5&RB ,8zLVx0-*Fo r!p24gTi.11/ hzgv]W B PbkqB>BL&U+@( JK`L  +"&&5332673Q/$'B #QG#& w j7@4 JfKL +3#!575#535'5eW\\WW\\WV(|(VV(|(VZpKPX@ J@ JYKPX@_KL@K_KLY@  +2&&#"##3667766C& &x@ Q-W wr-..l=PNl<@9J_KKL +2&&#"366773#'#46*  "欝@a r>*3S\P @ '@$eKL  +3#5333#ZPPQQ4pTpb+KPX@ &  J@ &  JYKPX@~_KL@~_KKLY@ ++$(%' +3''7&&#"566327327#"&''&&'# dS#  2GU]S' 7=?/  eR {41S>%s =2 =>U"hJKPX@]K` L@]KK` LY@ "" +"&533265332653#'##"&'# \oW?5WB2r Z5>QV ak#xVP#x_X6L++0*+/!:@7 JKK`L !! +"&'53265330.53##07& 6^ r*?CR6*AFp\TNF,:b: ,X@ JKPX@_K_L@_KK_LY@ $'"$&# +#"&&5466326632#4#"32654&#"GqqGGqOx(#dF?`6g+3 QZ\OO[[Qfp[[qo[/+'31hRy)d8ppp-V,)3@0 J_K_KL$&#$%" +#"&&546326632#4&#"32654&#"*vJrAw:]#!X4Xj*.8 16511651@~^&()%_i/;;#@%QSSQQQQ"bK PX@!pg]KL@"~g]KLY@" %6! +3#"#&&5466332##32654## v#RD4yhA2@KxEN!.M/wh?lBz39hNL#/T@Q J_K_K_KL%$,*$/%/ ## +2&&#"36632#"&'##46"32654* 1L:\rr\;Ia:0/>31 r7+"/''A\PIIOUUPZ;@8JgeKL! +##33232654&##Q9%=#]1KAFI.ddjl1I3213+*&7@4$#J_K_L! && +23267#"&54676654&#"'66h{4R-=N446h4,o@zy\RIG/,)P2-4qe`AR32)%)~l_Re#.* $v&,&7@4$#J_K_L! && +23267#"&5467>54&#"'669]6KN53X'Z-OknqSF03&!%G((1g, B3EI 2q'SOGP k&/o$m@  JK2PX@g_K_L@gg_LY@!$$  +23267#"&&5#"&546"3354&\O$.B'3R/JDH6`Sv o  NGME6.GR$U@R!J~]KK`L $$ +"&'532655#"&&5#57733#3267)   1M-GR+_$.Hh!@ NG?2stp VPVNK PX@p]KL@~]KLY@ &! +3#"#&&54663!#C v#RDL!.M/~#P@M J_K]K_L ## +2&&#"3#3267#"&&5#575461 (9$.G*1M-GGg r8,p o  NG?+5\P/5@2J]K_L  +"&5#5!#3267R] /R^~~wr !5@2J]K_L !! +%2654&'5!##"&&5467#5!Uc8J8FUSiiSUH:K9cthdV(+dZJJ[e+'UdgU]@ JKPX@_K`L@K_K`LY@  +"&5332654&#"56632wHGJC* @$MQA wKYGNS#u QNJwEppKPX@  J@  JYKPX@~KL@~KKLY@  +23#'.#"566N:BVn +?8$L+) u  b,&KPX@J@JYKPX@~K`L@~KK`LY@ && +2&#"#"&'532677336677>/ &sY( " +9 ٢V  6 &:, s%>sS\v2-B!>" 8"+7@4Je]K]L +37!5!3#!!57#>y]s_a}b|}b"=@: Je]K]L +3#3!57#537#5ZLUQiOM"azatrXair('3E@B J~]K_L  +"&5467'5!!#"3267RsgE arj#:%6K~0 ")M@J&'J~|]K_L$" )) +"&54676654&##57#5!3267[uYhF Gf7ED 3+.F I@Z6]:(E aroaB$*4* [/"A@>Je]K_L  +"&'532654&###5!!328m%FHG[JIYKp?   1;85~~6dGq&"A@>Je]K_L  +"&'532654&##5#5!#325l""l/CNQD_TCj= {""ppa!H5(?2stp#);3/K-NL,lKPX@ J@ JYKPX@_KL@K_KLY@  +2#3366"6654&~>\4IyJ :1_r2,?qLN^G!0wKJ jP8K\@KL +33\k\&2bK"PX@"e  K]KL@ ee  KLY@ +3#3##5#535#5357kaVaaVa9Z&''=K3+Z&'']K-C&G']yKsZ.&&/-5Z&/M5N&OM1Z.&1--Z&1M-N{&QM&$K_3+*&DK4&,K3+o&*K:&2K3+->&RK<U&8K3+KC&XKOU&8z3+KCW&XIU&8z3+KCq&XIU&8z3+KCr&XIU&8z3+KCq&XI&$Y3+*W&D.&$Y*W&D.}m&L-3+*j&L:'X@U  %Je e_K_ L$#"!  '' +"&546632&&#"32675#535#5!3#Vx9n-2!T.Ba5\c,}}AA8z pZy,&RP:m&2'LP3+->&R&LiP(&K'3+&K$Z&'=Z&']-C&G]y:&*v3+-+&JvZbKPX@ eKK`L@$eKKK`LY@## +333332653#"&55#Z+11,w}}v @:">54&[tSs6H?GCi<3Շ`(\/~oa`}DCGZ&1C3+NF&QCb&$ 3+"&D _&$ Y3+*&D .!&( ^3+$&H YZ&( -3+-$&H (e&, 3+7&* &, 3+Y&* :&2 3+*>&R g:&2 3+->&R 6F&5 3+&U /Z&5 R3+>&U U&8 3+=C&X zU&8 z3+KC&X I&LJ'%@"J! G_L +5>54&''>54&#"'66323vT!-((Y/RwA>77b,B9Ju4-1Ep촀854&#"'6632D%NY%2%(I",)b5@g;*&,5Dzw!TG%4  j&,#l&N<.JQ>GG>>G-H&<@9JhK_L $"& &  +"&&547&&553326553'254#"9MyF;-&44'1;AIDzOuvu ;sT8fKccGJIGddKeiLTtW&R6:&23+->W&R6:&2N3+->&RN:&2->W&R6pm&<Lk3+9&\LP&":@7  JgK_L""$# +'667&&536632#"&'72654#"lF   *?EQO*E /"%$.8 7 B>;G a(N,)3KPX@1'JG@1'JYKPX@ g_K_L@(gK_KK_LY@+*0.*3+3$%$* +'667&&554&#"#3366326632#"&'72654#"F  &)>-rU1Sd)?EPP*D /"%$.8 ~;;]W"F*&_i B>;G a( *L@I (Jg]K_L"!'%!*"*$# +'667&&55#57733#6632#"&'72654#"|F  GR+_ *?EQO*E /"%$.8 ?2stp B>;G a(-".:P@M !JK_K  ` L0/$#64/:0:*(#.$. "" +"&&546323&&55336632#"''2654&#"!2654&#"&Jp?s[;JJ;\rBrJ}:~_/"5 5"/_~>\\xKZSOQSWLLWSQOSZK-,".:P@M! J  _ K_KL0/$#64/:0:*(#.$. "" +2#"&'##5467##"&5466326"32654&!"32654&Iq?r\;JJ;[sBsI}:<367/;030531:/76,>~_/"5 5"/_~>\\xLWSQOSZKKZSOQSWLE@B J fKK L +!'##7##37337&&''4UaU4a0MX* t"B@? J]KK_L  +"&&'&&##5#5!327@L+-3:7:,##6*E)*.XXra H&8* t-@* J_KL  +2#>54&#"'66Ji8[[JN8+:051j7^54&#"'66Jh7Z[JN8+.P51j,7^01,."F r`P%5"/yUPIIOU A@>Je ]KL  +2####53#32654&*%=#ҨQPP.1KAFjl1I3{=|213+,yKPX@ JH@  JYKPX@e_KL@ eK_KLY@$4 +##5#535336632&&#"OlNNqT7   1U ?aa\*<,6p5@2J fKL +##'#53'33737#phg330044gkaa____f9"&9@6J fK_L#"%# +33733##"&'532677#5336677#65<2W vN$ /6 r]6 J"aUUv9(a.11.K2,D\" "3+-+,~KPX@ J@ JYKPX@_K_L@!K_KK_LY@  +"&54632373#'#'26554&#"[st^;L ~rJ=41B188 ."FG"/uJJOUSRTONL,!y" "3+NL!-@JKPX@*~_K _K_L@.~_K _KK_LY@#")'"-#-!! +2&&#"36632#"&'##46"32654&* J;\rt^.67 r7"/+<Q\PKQTVUPPQ,7@4  J_K_L  +2#"&'532654&#"'66yz7I##F/7CE:9* Y,RQRPs-,&1U@R * JGg_K_L('-+'1(1&& +2&&#"6632#"''667&&5466"32654&E;N,": B>O0KU8[6d;  J F~j';C"-,sPU  !K@9E'$!1%Z:d;#,-%2T@Q"#JK_K_K`L'&.,&2'2  %% +"&55467##"&5463230&&553326726754&#"<\JJ;[st^;L0=21B188`P%2"/."%3 r]IIOUUPPQ-"/KPX@ J@ JYKPX@"_K_K_L@&_K_KK_LY@$#+)#/$/"" +"&5463230&&554632&&#"#'#'26754&#"[st^;LXU, rJ=21B188 ."$2[S rG"/wIIOUUPPQ+",GHO@+",>@;Je_K_L  +2#"&55!&&#"566#326 R~HAtMq`G?5V.(Y25+:,={__@wH?Hs1B8&,-O,+?@<Jg_K_L'%$"++  +2#"&&5466"32654&##532654&Un};24HAvNQKNP;Q$>&.C=GG=>,KL18 367O*<~bZ~BnWUCI#!g#1"=@:JeK`L  +"&'53265#53533#"6 *OONN&Uu"1 aa2R1--8c@`&J_K _K_K_L/.31.8/8%# -- +2&&#"#"&'53255447##"&546323&&5546"326554&C, :c/dqsN1ampbe9Xik976 ruzw*| !+&P8[SBPXL-+,J-,F@CJe_K_L  +2&&#"32675#53#"&5466U1Z2,B))E*?Hh+rAA,s"H8LX\cPJ9"#2@/ JK_L## +"&54673366773'254&'EH"ɜa  `"JD"P@1b<A<A7d)=RY6?"!?9,.9=@:( J_K_L64" .. +2&&#"#"&5467'&&#"5663236776632654+ "N JEEHK& )*?$  #:,n"f$F*8RP;,D"d! n'$2#& 0#)*&K*C"K" "3+NF";@8J_K_KL""")%# +34632&&#"36632#4#"NaN* R2XlXC3Q\P r"?*&_i5v]WNF.S@PJ_K_KK_L+*'%!.. +2&&#"3632#"&'532654#"#46* 2nXj#N@2#XD2a r $=P_ia2R1u"1ok]WQ\P1 ?@< e_KKL      +2#"&546##5#5353!00!"//NNN*) )*OaaN"U" &@# JKL  +!57'5UVVVV"H**HH*>*H<@9JggKL"#2" +3&#"#6632233273#"' $ IH4  " IF3 [4JL95JM>@; Jg gKL$! +3#"&5463233#354#"#F?<5bb+#$=/2= yMq3 NO+@(  JK`L  +"&533267\J0`P8 rN!S@PJ~K]KK_L !! +"'5326654&&##57!#3!{]0o./-*'')*&_idvTOv]WN,/@  J% IKPX@$_ K ]K_L@(K_ K ]K_LY@-,*($#"! // +2#"&'532654#"#4#"#336632366]_#N@3#R;2R>/rW/,c-,,%KPX@ J@ JYKPX@#e _ K _L@8e _ K ]K ]K _LY@!%%  +25!#3#3!5#"&&546"32654&5:z=KvC};55<;55, p`ppr B[xQQQSSQQQ.,+C@@ J~_K_L&$! ++  +2#"&'##"&5466"32655332654&&wSme@II@emWsRf/*+-!!-+*/f,Q_q8//8q_Qx6\7=D@:``:@C<7]7-x"U" "3+x`KPXJ@ JYKPX@K_L@KK_LY@  +"&'7326653#'#G   &F+qT <4\*<"">@; JK_K`L "" +"&55467##"&'73266533267ZHN7   &F+2`P*3&7<4 rN,`KPXJ@ JYKPX@_KL@K_KLY@  +2&&#"#3366   &F+qT,<4\*<N,}KPX@J@JYKPX@_K`L@K_K`LY@ +2&&#"3267#"&53366   &F+0\JqT,<4 r`Pb\*<Ky.+@(J_KL  +2&&#"#46$6) _.y-#gfaC.+@(  J_KL  +2#4&#"'66[_ )6.afO#-yT]" 3@0Je]KL ! +3!2#'#32654&##TawA2MV+3')d"RS?F/'"'T]"GM"@ "3+-,5B@?() J_K_K_L%,%$%" +%#"&'3267#"&5532654&&'.54632&&#"tp!   "/KB,f'+&254A vb3\1-(H%B10/D%SYH&h WN+=.LLk$ )>O7@4J_K_L +2&&#"#"&'5326546,  K@/X ry2R1r [SO$L@IJe_K_L  $$ +"&'53265#5354632&&#"3#8/NNXU, NN Kr 5p[S rp2R1O,7@4  J_K_L  +"&&54&#"566323267@K  .VW..P2r R\E rO&I@FJg_K _L" &&  +"&546334632&&#"3#'2655#"5IGEIXU, DERgG.6EQ[S rMH[R ,W" "3+@@=J]K`L  +267#"&&5#57733#4.G*1M-GR+_$y o  NG?2stp"l JKPX@  eK `L@#  eKK `LY@$ +553533533##'##"&553267#KΕNNr[3Xj*.:4aaF*&_i1 ;;C?o" 5@2 J]K_L    +"&&5467#5!32654&'5!#DW}C='xjN@@N45u(9C} zJHHJ?Y npbJJn<KU, pKPX@ J@ JYKPX@_K_L@K_K_LY@    +2#"&&53326554&#"566K  $JRTXXra h ?" ;@8 JGg]K]L"#$" +'67#5#5!36632##73254#" O <+VA6Bj[M TM "WXXrZRMC7UB)'%"")5@- '# J$GKPX@)~]K_K_L@'~g]K_LY@+*1/*5+5)) +"&&546326654&&##57!5!&&''267&&#"^L@g.(YJ2Ri3" D "ZZD&F(!4G:@H4+ &B(cra Gi<9M ?  m "'-@* J_KL  +2#>54&#"'66Ji8\[JO8+:051j7^d-50:+8OJ[\;n "k5*A?t*wV.)?%,LmC YO6> 514G#n!g!#J@-z.]@Z+  Je_K_K_L)'" .. +2&&#"&&#"32675#53#"&5466325465% ,B))E*?Hh+rAAc#V rVi"H8LX\cPJ,\PNH"' &K@Hg _KK ` L " &&    +"&54632"&5463333#'2655#""//"!00IGEIDERgf )**) G.6E"MH[R*"Nl" "3+N"@K^L +333N"Pr-$0P@MJ_K_K_KL&%-+%0&0$$%)$' +5467##"&546323&&554632&&#"26754&#"J<\rt]01A541"/."5"[S r[IIOUUP ?@<Je_KL    +23##5#535>54&#"'66Ji8\[llZZJO8+:051j7^j15/9+8OJZZll[\8j"k 5*A@aaC*xU<:9820*6+6$"$$ +'67!'##"&546323&&553!36632##%26754&#"#3254#"O J;[st^;L<+VA6Bj[M =21B188/BM "WG"/."7ZRMC7UB)IIOUUPPQK'% #6KPX@ 0J@ 0JYKPX@_K ^LKPX@)_K_K ^L@'_K]K ^LYY@&$43$6&6 #" +!"&&55#577336632&&#"#'32654&&'.5467# 3M+GR+_*3\1-)G%B10.E%|w*'253B }!PD?2stk$ )=0RWp+:-A,5g@d%&0J~_K ]K `K_L.-21-5.5%#%%# +#"&'532655#"&&5#5773354632&&#"267#' K@/G*1M-GR+_XU, .$;2R1r L  NG?2st-[S r# 4w7CKPX@ ( A 5J@ ( A 5 JYKPX@)  g_K ` L@?  g_K]K` K  _ LY@#98?=8C9C31,*$" 77 +"&&5#57733#3267&&546632&&#"6632#"&'%2654&#" 4M*GR+_%!C F}U;N,": B>M.KW7\6Df%1p~"->0 "OD?2stp#@"d;sPU K@9E('('mw7KPX@"#  J I@"#  J IYKPX@._K _  K ]K_ L@9_K _ K ] K ]K_ LY@1/-,+*'%  77 +"&'532654#"####57546632&&#"3366323#XD2RR/W;+H&) [2Xj#Nu"1ok]WNH((FM  m &"F*&_ia2R1N)KPX@J@JYKPX@K_K_L@ K_KK_LY@ )) +"&'#332654&&'&&54632&&#">d.ek5/.1GCn^2Y--%B!;-,ALx 7 KDJNk& GAYXN 6@3 JK]K^L  +33!3%#N[areK N@K J~ | KL    +#'#3737#'#3737fk<=jd]>?f:@^fk<=jd^=?f:@PPN0@-~e]KL +!#5##!#5##NvvvvC,"KPX@ J@ JYKPX@_K_KL@ K_K_KLY@ "" +232653#5467##"&554#"566@VJ*.D2M:Xj+ .,R\;;]W1)(_i<r ,.KPX@,+J@,+JYKPX@!_K_K`L@%K_K_K`LY@)'#! .. +2326533267#"&55467##"&554#"566@VJ*.D20ZHM:Xj+ .,R\;;]W r`P*3)(_i<r 3zGK)&f 3+3zG3)&f 3+GM)&f 3+3mGU)&f 3+gGF)&f 3+8gGH)&f 3+7gGN)&f 3+&gGZ)&f 3+rgG\)&f 3+    ?' *dD@gW_O +D2#52654&#4HH4'>57>E?' *dD@gW_O +D"&5463"34HH4?>75>E)8dD@-  JW_O%% +D5654&#"'6632Nf)!A(RM9>f7 NJ8.D31G9@ 3+! 3+ 'dD@Jt +D73#' o(p;II / /3+(T Z(^Bv(^BC(* Z ְ3+(MsL 3+(4B (4B 9,dD@!JU]M +D'37YYY苋9dDt +D'3Y( y y3+( y y3+~  3+~  3+  3+M dD@U]M +D#53>(; rgG0)&f 3+3GO)&f 3+*mGV)&f 3+tgG[)&f 3+5Gb)&f 3+NT,dD@!U]M +D!#5!nBPNT0dD@%Ue]M +D!#5353BBPNT0dD@%Ue]M +D!#533BFB(PNT0dD@%Ue]M +D!5#533BBPNT&dD@U^N +D!!533TBBnNT&dD@U^N +D33!NBBNT*dD@Ue]M +D33##NBBB(9K ۰3+(S*5 53+  (v'dD@Jt +D'373OXYNކ(v'dD@Jt +D73#'(1OXY܄(#0+'57#k1kE>?(#0+57'5(E>?Ek1(BC 3+(GR@ 3+(R 3+(1Q ԰3+9̰3+(&dD@U]M +D3##(q55(-dD@"U]M +D#5#55q5( ,dD@!U^N +D333(5q 5( &dD@U^N +D3#5353Φq55(0IdDK PX@nU^N@U^NY +D!53!53B8M9Хcc(0FdDK PX@nU^N@U^NY +D!53!B8Хc(K 1dD@&JH GU]M +D'57!!#O1;DK* @J]8L+6673#& F#W7V !U $ 5JKPX@ ]=L@U]MY+#56673& F#WIV !U ,$-,&FNc c3+,&$Nb b3+,,A)4O@L"2Jgg_BK _9L+* 0.*4+4&$) )%&&$ +6632#"&&546632#"&7"32654254&#"Y:'S^'@S Ͱ3+t&@j3+-"6@3' JK2PX@$~_;K9K`=L@&~|_;K`=LY@1/)(!66+"&'532655&&5467#>54#"'663233267)  8G%D -4N,- '( Hh!F Y]/%ew?vf'YM#$%j|?D1VP:T JK2PX@_@K]9L@a_@LY@ +2#5&&5466"32654&vGIu\ZY\]XXLWtvWL~\PP\\PP\->,4@1 J_CK]=L +2#5&&546"32654&7MvDeYUiz;55<;55,B~ZrrxQQQSSQQQ:ZV@JK2PX@_@K]9L@a_@LY@ +2&&#"33#5&&54665k11(P(W\\b2vM{WUQZ}YJ-2,&4@1Jc_CL&&+"&'532654&'&&546632&&#" =6,0#5buI~P:^+,'H"=/YExp jekEq-, K>RVZ K PX@ pe]8K9LK2PX@!~e]8K9L@ ~e]8LYY@ +3!!3#5#Z~x|uN" VK PX@pe];K=L@ ~e];K=LY@ +#!!3#5#xsps+F@C()Je_@K_9L&$+++"&54677!76654&#"'6632!3267@O A 1GM J . <92 f f B:*!J i-,=@:) *J_BK_=L'%,,+"&54675654&#"'6632%3267KPdn  2HF P*x! .CB,?^! f E:/=RiQ@  JK2PX@_@K9L@_@LY@ +!>55'7&&''7&&#"'6632<, -35N?#jOpI&(_g3TKj.vLo #q'cZ?@  H=L+6654&''7&''7&&''%,(nF1*%Ne)U61JbH",KPX@ +  !J@ +  !JYKPX@_;K`9LK2PX@_;K9K`9L@~_;K`9LYY@,,%&%%+3>54#"'663233267#"&&5467)%D -4N,- '( $2R1%ew?vf'YM#$%j|?D1i%WL/.>,&4>@;, J_CK_9K=L('0.'4(4&&+2#"&'##6654&&'.5466"32654&@JrBzh,K./6R- %$6R7B{S&9K711,C~Z,)-   $OeJx.ocLWWL-,FM:b-,$,ZGNL:Z&ZUPJK2PX@~8K9L@~]8LY@ +3333#4>7###ZľV6R@D=MX\"N" X JK2PX@~;K9K=L@~];K=LY@ +373##'Ńˋhd=,(O@L& J~e _CK _9K =L#!((%# +5#534632#"&'#03#2654&#";;;|IsBzh&H k80086599ahC~Z"5a9^LWWLHS#Ch:Z&&y#C&hyM:V4-+,T: N"Z &",@)JfKL  +33#'#73'.' őƀ))G(  "ރ (*.("8@5ee ]K]L +!!5##!#3#3%35#:x#0"]|]+k--8?@&   JKPX@#  e_K _L@( U  e_K _LY@><:975%%$"#%#" +3267#"'#"&&55!&&#"56632>324&#"766#326kvv],%'I%0+j9n9 U:Ei;aG?`Z)Y@Cn#:L9/O0%.>7D50/1<WS)" c@ :oOH?H+s23#-%M:#85-02A8 l"&K'PX@! e]K ]LK.PX@&U e]K ]L@'e e]K ]LYY@%#$!$!% +3##!5#535!24##3264&##3262UE1jVEE@e;OiX/131be*7-Z,/J*Z?A2l ;,7@4  J_K_L  +"3267#"&&546632&&ACICI#D(&G-Vo6=uT*T&'?eWYa`FTSH\V%"@]K]L!$!" +##324&##32%zRM@3"[W"%" ?@<e ]K]L     +2##5#535#3#3254&44?[[3S"]]][WV" )@&e]K]L +!!!#3#3B̾"]z]&,*J@G#Jg_K_L ** +"&'732654&##53254&#"5632Mo/,(]';4QJ7:T=8c"J\x9!8#1cF7!@ !3+s8C@ C3++," "3+9")9@6JgK`L%#))#) +#"&5467&&55332655"32654&!)10ryxr/1*v,22+]:54:;44"A1HL5XjiX3OF2BA/66/A4//55//4- >,$@!_L  +2#4&#"#467MwC5<;5,A^URRU-> !@`L"" +#"&&'33267>zLvD5;:5 A}YOPPOV" 2@/g]KL       +2####32654& lc)_Q3v6(2:-"ZO0S2"](,)'"8@5Je]KL' +37.54633#5#35#"-odvJ8;.21 $;-KP0!%%*";@8JgK^L  +!"&54667'3353'35#"do-Jv;8212PL-<$ ]*&&!"@]KL +!##5!##v__KA"!@K_L#$ +#"&&533265A.o_bm+4701"IuEEuI)G??G.&R Q&Y@ Y3+ @ ǰ3+)rUP)@ 3+"FY8@ ",'@$%JKL,, +3334>733>773#'.5#: >;  <'    )"EA/2(>@AG޿ 8B@..@C8"]!"H@E  J~|]K`L$$$ +#"'532654&##57#5_`t{bK'Z%I6FY/"ITHH``/%#(P^1*%;@8"#J_K_L  %% +"&546756654&#"'66323267`_JGA1%# ?'#)X2R`IEuR+R("X QB4DH YKB8BF 62_*'+@(JK_L($%%" +%3267#"&&'#"'53267&&546632O#0 ' 24#1M-/ .$/>.T99S.B."Y/+>-Y!.2k?0M--M0?mV"@]KL +##v"^<" "!@ JKL +33#.' wh  i"L .01- V"@]KL +!###!vv<"V"5"+@(gKL +##5"&&553332655.l^k^l.oCFkCF"AY.-YAB*&*A "Q@  JKPX@]K_L@]KK_LY$& +!###"'53267>7! v @G    c.vn&FN ].?kMG$)&f 3+DG)&f 3+:G%)&f 3+ %lKPX@# e]K]L@!g e]LY@%$ $! +535323###5732654&##32654&# .\`:,[Oc9+!'*4@,##/G0<%G':BHp :G')&f 3+:FG()&f 3+&2Gp)&f 3+&G*)&f 3+:G+)&f 3+G,)&f 3+G-)&f 3+:G.)&f 3+:YG/)&f 3+:*G0)&f 3+:G1)&f 3+:>JKPX@KL@]LY@ +333#5667#:X}X;AT=&G2)&f 3+"G)&f 3+:{G3)&f 3+:G5)&f 3+ kG7)&f 3+7G8)&f 3+uG:)&f 3+XmGD)&f 3+1mmG )&f 3+imG!)&f 3+9mG)&f 3+3~GE)&f 3+iGG)&f 3+dmGH)&f 3+cmG))&f 3+QmG*)&f 3+PmG)&f 3+imGJ)&f 3+.gG)&f 3+3GN)&f 3+3NmGP)&f 3+3zmG )&f 3+umGR)&f 3+1mG$)&f 3+umG)&f 3+uG)&f 3+3~mGS)&f 3+GW)&f 3+1xgGX)&f 3+*YG )&f 3+1LgG<)&f 3+rgGY)&f 3+ EkG)&f 3+3G~)&f 3+qgG)&f 3+uG)&f 3+mG)&f 3+kG)&f 3+/hFL)&f 3+3FU)&f 3+1xFX)&f 3+rFY)&f 3+3kF~)&f 3+qF)&f 3+&tF)&f 3+F)&f 3+F)&f 3+K,'.KPX@  J@  JYKPX@"   f]K_LKPX@&   fK_K_L@0   fK_K_K_LYY@(((.(.&###%" +%!3267#"'#"&&53326536632'&&#"G?4W.)X?EVHbk*0705@"Dh:34*:>Is\&6EuI)G??G.;oN1B8;L-9@ # & JKPX@4  g_K _ K]K _L@8  g_K _ K]KK _LY@8620+)!""" +#"&'##&#"#6632533273#"&'366324&#"326Lt^.6+<u6JL"R6JL 7"/7w`PGOUU--:KPX@'  * J@'  * JYKPX@1hK_K _K  _ L@5hK_K _K K  _ LY@#/.64.:/:)(%$"! -- +"&5463230&&55&#"#6632533273#"'#'#'26754&#"[st^;L $ IG3  %ID4 rJ=21B188 ."%366JL#R6JLG"/wIIIQPKPQ/c@`# JI g  g_K]K  L//-*('"%&" +35&#"#66325#57546632&&#"3#3273#"&'f %IE4 RR/W;+H&) $ IF3 6JLfH((FM  m &"p6JL,.7AK PX@ >/ -*' !J 5I@ >/ -*' !J 5IYK PX@-  ~  g  _K] LKPX@4  ~  ~  g  _K] L@8  ~  ~  gK  _K] LYY@888A8A<:31.."$ +35#4675336632366326673#5&&'#5&&'54#"54#"NI?0rW/5<;57"d( F`Fp5iM_t71irV@;7II7:A8KPX@1+ 2 7 J@1+ 2 7 JYKPX@.~   K_K ^ L@=~  K_K]K K ` LY@8864/-*)"' +7&&5#57733#537336632#4#"#3267#"'#]GR+_ #kR2XlXC3 .G*%:Kv?,?2stpT,,"9 *&_i?v]W o IU"7@4 JfKL +#!575#535'5!NOVVMMVAVGat*HH*tai*HH*i"7@4  JeK`L%# +#3267#"&&55#5353Eb$.C'3Q0NNGa; o  NG;ay,!'KPX@   J@   JYKPX@*   e _ K _KL@.   eK _ K _KLY@'#"%$"'#' !!  +23##"&'###5353366"3&27#~Sn // rU;FNNyJ 30SU 1,tqavz+*&aG!0w77nw:=" A@> eK _ L    +".'5#53533533#'2655#HH`9NN̕NN0mW/24 %@Q+aa:g@x8118" 'R@OJ  e]K  _ L"!%$!'"'    +"&&55#53667#5!!&'5!#3#'267!DW}C->,xGHu, <-;LL >>>NL&3KPX@J@JYKPX@, _K] K_K_LK2PX@0 _K] KK_K_L@-c _K] KK_LYY@('/-'3(3&&$%(' +36632#"&'532655#"&'##"32654&J;\r HF(   .67<"/Hl$zVPh!7+<IIOUUPPQ-]%2KPX@ "J@ "JYKPX@(K_K _ K_LK2PX@,K_KK _ K_L@)cK_KK _ LYY@'&.,&2'2! %% +"&546323&&5533#"&'532655#'#'26754&#"[st^;L2HF(  (J=21B188 ."= uVPh!@G"/wIIOUUPPQ(@JIK2PX@.~_K]KK_ L@+~ c_K]KLY@%$#"!  (( +"&'532655##57546632&&#"3#3(  KRR/W;+H&)2Hh!@H((FM  m &"pVP-1,0;1KPX@ 'JKPX@ ' J@ ' JYYKPX@6g  _ K ]K _K_LKPX@4g  _ K]K _K_L@6egK  _ K _K_LYY@!21641;2;,*"  00 +2373!#"&'532655##"&'53255467##"&546"326554&e9 ~HF(  :c/dqsN1ampik976,PFYVPh!=uzw*| %+&yAQXLNn"u@ JK2PX@$~KKK`L@!~dKKLY@""%# +3667733#"&'532655#'# ٕSHF)  2@=*ȭVPh!@37`@  JK2PX@~KK_L@~cKLY@ %# +3#"&'532655#2HF(  KuVPh!@N,2@J IKPX@-  ~_ K ]K_ LK2PX@1  ~K_ K ]K_ L@.  ~ cK_ K ]LYY@/.+)'&$" 22 +"&'532655#4#"#4#"#336632366323/(  KR;2R>/rW/31,Gl#|VPh!7+*&G!0wIIOUUP7,#KPX@ J@ JYKPX@$~_KK_LK2PX@(~K_KK_L@%~cK_KLYY@ ## +2&&#"3#"&'532655#3366   &F+2HF(  KqT,<4VPh!@"\*<-,9y@)("JK2PX@ _K_K_L@c_K_LY@-+&$99 +2&&#"#"&'532655#"&'532654&&'.5463\1-(H%B10/D%)HF)  "9R),f'+&254A v,k$ )>0E+rVPh!;{+=.LL.@%$JKPX@"_K]K_LK2PX@*e_K_K_L@%eW_K_LYY@)'"  .. +2&&#"!#"&'532655##"&'5326546, HF(   K@/X r/VPh!UP2R1r [S9"l@JK2PX@~KK`L@~dKLY@  +"&'532655#3366733u(  Мi  iqHh!@"<;7KVP5"m@  JK2PX@ ~KK_L@~cKLY@%# +3#"&'532655#'#375q:HF)  ssjk"VPh!@ "m@  JK2PX@ ]K]K_L@c]K]LY@%# +3#"&'532655!5#5HF)  "aVPh!@XXr*-*5KPX@()J@()JYKPX@+  g_K _K` LK2PX@6  ~  g_KK _K` L@3  ~  g d_KK _LYY@,+1/+5,5&$!  ** +"&55#'##"&5467754&#"'6632332672655P>K#NDI`zz_-((L&1+l:nu2  $0B:H8(JRJJ,(TYWR+(e_b h I94-0'"-,!.KPX@J@JYKPX@$ _ K_K`LK2PX@,K _ KK_K`L@)dK _ KK_LYY@#")'".#. !! +237333267#"&55#'##"&546"326554&;L ~2  $1>K(J;[st1883=41,."FK h JRJG"/ySRTOJJOU-1>c@` !./J_K_K _K_L32:82>3>,*%# 11 +"&55467##"&5463230&&554632&&#"326726754&#"<\JJ;[st^;LXU, 0=21B188`P%2"/."$2[S rw r]IIOUUPPQ-g,#*@!JK2PX@)e _K_K_L@&ec _K_LY@%$('$*%* #" +"&&546632!32673267#"&55"3&&ER~HAtMqG?4W.  $1>K!(,83 ={__@wH?H h JRD8;1B&g,6@ -45JK2PX@(e_K_K_L@%ec_K_LY@20+)%#" 66 +"&55#"&54675&&546632&&#"33#"32673267>K-~I<53@lB9r*.$M2mAFKRMBK"c8=T:7JQ4;'](,/oM;g?<1#8!9x\+  $JR#Ag!h@21: 1)-M/: h +,$+@"#JK2PX@*e_K _K` L@'e d_K _LY@&%)(%+&+  $$ +"&5#"&55!&&#"5663233267267#>Kfq`G?5V.(Y?MyJz  $G+:2JR"qqwH?Hs6mS h F8;1BHw @ JK2PX@)~_KKK`L@&~d_KKLY@      +2#"&54633267#"&55#!00!"//l2  $1>KK*) )*K h JRJ",&w@$# JK2PX@ _K_K_L@c_K_LY@! && +2#"&'3267#"&5532654&#"'66yz    $1>K#F/7CE:9* Y,8 h JRRQRPsO%n@JK2PX@~_K`L@~d_LY@ %% +2&&#"3267#"&55326546, ;J  $1>KX r_Cd 9 h JR [SK"#KPX@  J@  JYKPX@K`K`LK2PX@"KK`K`L@dKK`LYY@###$$# +33267#"&55#'##"&533265C2  $1>K([3Xj*.D2"K h JRJF*&_id;;]W&"(@&'JK2PX@(~]K_K_L@%~c]K_LY@$" (( +"&5532654&##57#5!#"&'3267>K"c8=TGOTĥgO9x\+  $JR#!SyrayUA/P/: h 3~mG")&f 3+:mGF)&f 3+PmG%)&f 3+uG)&f 3+PmG&)&f 3+ GI)&f 3+gG,)&f 3+imG.)&f 3+1xgG2)&f 3+G5)&f 3+3gG6)&f 3+ gG7)&f 3+ gGg)&f 3+Gj)&f 3+3G:)&f 3+$Gq)&f 3+3(gGl)&f 3+3NmG>)&f 3+1LgG=)&f 3+zmG?)&f 3+3mG@)&f 3+3gGA)&f 3+umGB)&f 3+GE)&f 3+*mGO)&f 3+GP)&f 3+G)&f 3+gGV)&f 3+gGW)&f 3+1wgG)&f 3+1mGX)&f 3+rgGY)&f 3+*gG])&f 3+igG])&f 3+vgG^)&f 3+`gG_)&f 3+tG)&f 3+Zk&%N3+NL&EN Z.k&% N.L&E ZRk&%L 3+NTL&ELw 3+:Z&&'zv3+-&F'zvZ&'N3+-+&GNZ.&' -.+&G eZQ&'L 3+-Q+&GLm 3+Z'7@4J]K]K_L!,!&%" +#"&'532654&'7##324&##32AV#)$-'p[1DhcQAz8>R KPr& -vp/-+*7KPX@ ) J@ ) JYKPX@'K_K _K_L@+K_KK _K_LY@,+31+7,7$"** +"&546323&&553#'##"&'532654&'7726754&#"[st^;Lr1BU#)$-.=21B188 ."= G= -%8>R AwIIOUUPPQZ8&' X-8+&G -Z+&(/3+-$q&H&Z+&(+3+-$q&H%Z8&( /-8$,&H (!1&(--$,&HQ5 а3+Z&('zMH3+-$&H&MCzZ&)N3+&IN3+:m&*L3+-+&JLmZ&+N3+LF&KN$ְ3+Z.&+ N.F&K Z&+jO3+F&Kjkְ3+>&+zP2F&KzDZ(&+ N(F&K I1&, 1e&LQ ԰3+ &,3+Vq&*Z&.v3+El&Nvְ3+Z.&. N.l&N nZM&. \NQl&NL 3+Z.&/ lG.&O .m&/' lL3+.?&O' Lְ3+ZW&/Le 3+W;&OL 3+Z8&/ 4/n&OJ Ѱ3+ZU&0N`3+N&PN~Z.U&0  N.,&P &Z&1N 3+NF&QNZ.&1 N.F,&Q ZH&1L 3+NHF,&QL 3+Z8&1 N8F,&Q I:&23+->q&R6:&23+->^&R6:+&23+->q&R+:+&23+->q&R4ZG&3v3+NL&SvZG&3N3+NL&SNZ&5N3+N&UNZ.&5 J.,&U Z.m&5'L 3+J.&U&L1 ZR&5L 3+ T,&UL 3+.&6N3+-&VN..&6 D-.,&V 1.&63+-&V.&63+-^&V..&6'N D3+-.&V'N 1/&7N3+c&WNMkk3+./&7 Z.&W )]/&7LQ 3+M&W 8/&7 ",&WJ ΰ3+U&8jJ3+KC&XjU&8Q3+KC&XQVU8&8 xK(C"&XJN ʰ3+U&8z3+KCq&XIU&8z3+KC^&XI&9QS3+9&YQ&.&9 {.9"&Y T&:Nm3+ N&ZN5.&:  .N"&Z &;N3+=&[N&;j3+=&[jp&<N3+9&\N+&=J,3+&]J.+&= j."&] 0M+&= "M"&] NMF&K Ih&Wjxx3+ NE&ZO 9E&\O}*'&DN&@N3+-q&}-q&}&-q&}#-q&}$-q&}-q&} --q\&}-q\&} &$ù 3+&$& 3+X'$ ̰3+Q'${ ̰3+8'$e ̰3+=' {$ ̰3+#*'{$q ΰ3++*&$y { ΰ3+&&&&&&&&&&&&& '^&(i칻 3+^&(i& 3+'({ ̰3+'( { ̰3+'(] ̰3+'( q ̰3+NF&NF&&NF&7NF&8NF&-NF& ANF\&/NF\& ' &+f칻 3+&+d& 3+'+{ ̰3+'+{ ̰3+'+{ ̰3+'+ q ̰3+*'+q ΰ3+*'+ q ΰ3+N&N&& & &&z& \&|\& t',칻 3+',& 3+',>{ ̰3+',4{ ̰3+',Z{ ̰3+',D { ̰3+*', q ΰ3+*', q ΰ3+->&R->&R&->&R->&R->&R->&R #)&2G칻 3+8&2V& 3+'2{ ̰3+'2{ ̰3+ '2h ̰3+'2 { ̰3+FP&FP&&FP&FP& FP&FP& )FP\&FP\& &&< 3+'{<@ ̰3+' {<_ ̰3+*& <N ΰ3+5*&)5*&&(5*&5*&5*&5*& 5*\&5*\& A&uJ칻 3+Q&uZ& 3+'u{ ̰3+'u{ ̰3+'u^ ̰3+'u { ̰3+*'uq ΰ3+*'u ΰ3+-q&}-q&}%3&&&&%-NF&"NF&%G%&oN&%->&R->&R%)FP& FP&%/5*&5*&%-q&}'-q&}'&-q&}&#-q&}&$-q&}&-q&}& --q\&}&-q\&}& 4&$ '칻 3+#&&&$ 3+'{'$> ̰3+'{'$B ̰3+']'$& ̰3+' {'$1 ̰3+*'{'$ ΰ3+*' {&$x ΰ3+NF&'|NF&'&|NF&&7|NF&&8|NF&&-|NF&& A|NF\&&/|NF\&& '|&+g&칻d 3+&+b&&_ 3+'+'{ ̰3+'+'{ ̰3+'+"'{ ̰3+'+' q ̰3+p*'+'q ΰ3+m*'+' q ΰ3+5*&')5*&'&(5*&'5*&'5*&'5*&' 5*\&'5*\&' &uV&칻h 3+&u`'r& 3+'u '{ ̰3+'u '{ ̰3+j'u'[ ̰3+~'u' { ̰3+I*'u'q ΰ3+I*'u' q ΰ3+-q&}M[-q&}Ls-q&}&-q+&}-q&}&%3-q&}QM-q&}&QM&$Mt3+m&$L3+&$I 3+&$%' 3+4&$GS2dD@'JW`P'+D5665&&54632G "" !$?S*,"3D`dD@ JK PX@pU_O@~U_OY@ %#+D3267#"&55K  (/742R48PGSe]Q=pW`!-QdD@Fg h W _  O#")'"-#-!!"""" +D663232673#"&&#""&546323"&54632p62 2*=442+""$$##$$1B1BNF&&"|NF,&|NF&&%G|NF&QaNF&&Qa|U&(`1 ̰3+S&(^% ̰3+ &+i; ̰3+&+^% ̰3+Z&+TdDKPX@ JKPX@ J@ JYYKPX@g_OKPX@Ug_O@Ug]MYY@#+D5665&546327.'534 #C'!  T*0(+A65 %U TdDKPX@ JKPX@ J@ JYYKPX@g_OKPX@Ug_O@Ug]MYY@#+D5665&546327566734 #CN  "'T*0(+A U% 66vF\$ϱdDK PX@1p ng h X `   PKPX@2~ ng h X `   P@3~|g h X `   PYY@$$ """" +D663232673#"&&#"565&&54632v62 2*=442+8751B1B'#%8&M&L &z &y&Q`&&,M3+hm&,L3+',1 ̰3+',% ̰3+TdDKPX@ JKPX@ J@ JYYKPX@g_OKPX@Ug_O@Ug]MYY@ $+D&&54632.'53?C# 4n'!  TA+(0 65 %U TdDKPX@ JKPX@ J@ JYYKPX@g_OKPX@Ug_O@Ug]MYY@ $+D&&5463256673?C# 4  !'TA+(0 U% 56vF\$dDK PX@1 p  |g h  W ` P@2~  |g h  W ` PY@$#"!"""" +D663232673#"&&#"&&54632v62 2*=442+@571B1B8%#FP&MWFP&LoFP &FP &;=&;=&&FP&QIFP`&p&<MS3+pm&<Lj3+'<1 ̰3++'<% ˰3+&3c& 3+[c "`dD@  JK"PX@UW_O@We_OY@ $$$&+D#.'53%4632#"&%4632#"&o@&" x #$$#"#$$#j66 %U." "! !" "! [c "wdD@ JK"PX@UW_O@We_OY@ ""    +D56673"&54632!"&54632 x "&##$$##$$j U% 66 !" "! !" "! ^a 'dD@ JU]M+D#.'53aE'#  ^66 %U5*&'5*"&5*&'%5*&Q5*&'Q=&2[; ̰3+&2,% ̰3+^&ug1 ̰3+7&u@% ̰3+&u^ -dD@"JU]M +D56673  "'^ U% 66MS,dD@! JW`P$+D&&54632E?$! "" SD3",{t@U]M +3*{m!@  Ht +'7'77'>RRSSRR>>RQSSQR>d{ 4@1JHU]M  +3'7'7#@ll@y=kk=H+{ 4@1JHU]M  +#'73yAmmA=kk=!($@U]M +5!($pp\'_~_ $@!]L    +#&&'7##&&'7k/[k/4;7~5 4;7~5 {Z$@!U]M +3#迅'H,{Z$@!U]M +#53'!{t&@#U]M +#53#__b{t 0@-eU]M  +#53#3#__b'&{t 0@-eU]M  +#535#53#__b&'. /@,]L   +3!333聈恈}Zq9 #/K#PX@+ g_K _K_ LK)PX@( g c_K _LK2PX@& g g c_L@,g g gW_ OYYY@#%$ +)$/%/##    +"&54632"&54632"&54632"&54632(--('//'(--('//'(--('//'(--('//C'')%%)''''(&&('''')%%)''''(&&(''{t5@2eU]M +#535#53#3#______b&''&{t 2@/eU]M  +#53#35#__bb{u%@"JU]M +'3``}{u@Ht +#7#`uu`b{u @ Ht +'77'`uu`>>>sYkkY777{t 3@0eU]M  +#535#533#_____b&'&gV 1@.gW_O      +"&54632'254#"RVPXTWRX//.wjjuujjwe{zz{fU'J@G JggW_O#!''  +2&#"36632#"&54>"32654&$ 6; .%;DWJM_4[ !U\ 6"H@AUdb/XG)!/# bU'J@G  JggW_O#!''  +2#"&'532667##"&546"32654&L`4[E % 6; .%:EWP  Uea/XG)\ 5#GAAUW"  !/XFD)&f 3+dFH)&f 3+uFR)&f 3+tF[)&f 3+cF))&f 3+%P@MJe g_ K]L  +2&&#"5!#3#3!5"465N,1g8($y s OFpIpVr$F$-4@/+& !JKPX@!  a]K`L@h  a]LY@%%%-%-*($$ +7&'#7&&546677373&&'667'&&##'5%!IMJ@|ZI%"I 0OB$KS T R"oD14:O Zr(jXY LHQc x|{ OkEOcG-G$',K"PX@"! J@"! JYK"PX@W_K_L@g_K_LY@&$,, +2&&#"336632&#"667#"&&5466_3f/1%L$ST24\2( #.5-]6i?Gvq[v8I$,z/>^ {Zmm`N&KPX@ %"J@ %"JYK PX@! n_ KLKPX@ _ KL@$ K_ KLYY@$#! && +2#4#"#5#54#"#336632736]_R;2KdR>/rW/e/Md8!,_i?vTO3x5v]W"F*&7y  2#'+]@Z( Jf   eK  L+*'&%$#"!  +35#535#53533533#3##'#3'#3'#3'#3'#D::::N[o9999PX B..CFJHJJHJ`HHH[Kt >@/ . JKPX@, e ] K_ K ]L@0 e ] K_ KK _LY@#31,*>>   +2####32654&2&&#"#"&'532654&&'&&546uo:-t,!#4-0+M)$ :5 (&%7c`0E"#Q# (*>:cdmIX|213+"k&(=0SY{ F?LQ "&*.14h@e"J  f   eK L4310.-,+*)('&%$#!  +3'#53'#53'33733733#3##'#3'37#3'#37#7#7#)VJ >2)q&\+*]%q)3? KW)-\,P - D@ &. E B JHJJHJ\HHHHHddd<8@5JIfK L +3#533333##H@@ ƚw*/a::a/*(6@3J]KL +35'75'75#5!#77U)~T*~[*Z*z:=VY:=V~~a?=[Y>=Z/<@:*JKPX@7  g_KK` K _ K L@4  g_KK^ K _K LY@10750<1<//(&!%&! +33326654&&#"'6632#6632#"&'#2654&#"tZOm95_?+[$-&y=rRft;Rf[[R%SF"3  "2 "*  $"WNS8U/pN]ceX[F-X:E-@6."&15 +&2C@@!  J_KK_L.,&& +"'532654&''#7&&5466326654&#"r%! kR4hP=592V5UZ12=`O- s .c@eb^@EW)`X6`V&ZLVXl#W%%#M:4 2#,2i@feg ]K ] KL%$ 1/.-'&$,%,"!# #  +23#3#####535#535#3&#364544#326er5,-;v99995')9UME  EG\cE;Ex**o;  y+:%N@K %"JIe_KK]L!  +5&&546753&&'3#667]W8k,2 Q,/e<@G?H< PH *$yGZ~w $D@A  fe KL$#"! +3#3##'##7#537#533.#3[u]DNGHMC\t[S D 7rJHJJHJ 3563H 13^@[0 1 Je   e_K _ L.,('&%"!  33 +"&547#53667#5!654&#"'66323#3!3267'zy1W2N/,)P2-4q?h{3]< 446h4,o l_J$J' $ve`J!J %)~:Z#4@1#  JK]L +&&'667#5.546675+T'1; E&#C%Wd>C^BE@G&{ %IG]idb &}adyAD@AJ~ eU]M"" +353267#53&&##5!#3#<5H 82OÝ# qo _I:J/*J&3JJ&3JJSh pp3+1$/7X@U ~gge W _ O%%7520%/%/.,(&$$  +".54>32'26654.#"'32##532654##Pc67dNLe96cPRP-Pm?VNM/RLV>RF#+OE 6cPLe96cPPc6@NY?nT/NYYNTEDCL#'F :@+ * JK PX@9~  ~ e  g  W _ OK PX@2~ e  g  W _  O@9~  ~ e  g  W _ OYY@ /-(&::   +#'##3#3'&&'2&&#"#"&'532654&'.546hcoazm6uu):#4 !.2LG<3" 0 FrJJX6S  R J  ++38 O   ) .8" .@+eU]M +3535#5353ԕppC9@  * )3JKPX@*  hg ]K_  L@.  hg ]K K_ LY@".,'%!99 +3354667'73"'53254&##532654&#"'6632ut}&5]G> A#F"/7-3)1J2=P',2/V6 *'>sT"]4 N!E=4"1 8#;D iC.KPX@#    3 = J4I@#    3 = J4IYKPX@2  h  e  g_K_ LKPX@6  h  e  gK_K_ L@:  h  e  gK_K K_LYY@$861/+)(&#!CC$' +576654&#"'663233"'53254&##532654&#"'6632m-!'1<P5AO3:3utxG> A#F"/7-3)1J2=P',2/VRk+0+J#?;-J5.b6"]4 N!E=4"1 8#;D,$<@9e eK ^  L +35#535#53533#3#!ZZZZZ"VJTTJVa}15@2e eK  L +35#535#5333#3#NNNNNNNNNTLTTLTB@?Jgg K^L"" +3273#"&'!!&#"#6632 $ IF3 [ %IE4 5JL}G5JL! GkKPX@%g]K]K L@#eg]K LY@%! +3#53532##32654##3#ZPP4yhA2@KxErra}lCqCf6@rAaZ#N@KJe]KK_L##  +2##3267#"&5#32654&*%=#ҨQ /R].1KAFjl1I3r R^ |213+*0$*0K"PX@ " .-'J@ " .-'JYK"PX@#VbK_KL@$faK_KLY@ 5 +#7&&54677'6632373#'#4'76657M,ASpq&J%1+l:-M/CGh8)q%8!&3URTRoe\KJ"'r%SW6..#} '0!P@M Ja ]K ]K_L!!1 +#7&&5#57733733#3267#17M.+5GR+_D0M0 H .G*,PL?2stp o Z05N<6@3JaK_KL"' +366323#5#4#"#R2XlQ`XC3)I*&_i?v]WZ0/@, JaKL +%#5##3773$@<-.TN<l3@0JaKKL +%#5#'#3366773l&@ ٕm3=*0+ 5@2J]K]L  +5!5!5!!V_b}b<" 5@2J]K]L  +5!5#5!3DXXra: KPX@  J@  JYKPX@_K_LKPX@K_K_L@!K_KK_LYY@    +2373#'##"&&5466"32554&LH`}\IN{GH|wFVXGJ2%L6N%3SxyS|wztenZ&e3xKPX@ ,!J@ ,!JYKPX@~KL@~KKLY@'& 33 +2&&#"#.'#3>73>766$  va  `[  hh  @Fx *- w ,4..3- zFFEApAFEE@K v,5mKPX@ .!J H@ .!JYKPX@]KL@K_KLY@55$- +333>733>776632&#"#'.'#>  C@  EF$  ]*,"EA/2(>@7=yFQn,8FO@ @PF 9,'p@$ JKPX@g_KL@gK_KLY@ ' ' +23673#'#5267&&546"654?B.+ iЙb6  P>&,D<4J28977S 0?DY/0,Z #@ eKL +33!!Z~N"#@ eKL +3##Е"o"--"0@- JH_KL  +"&546754632'6654&&#">2l&*M7nUm~X)DQ'" U=F4fAMI `c{]HvcK+H+'/JB0+7'%'%4$$$$5555(}+,@)W_O +##"&54632+]'#$$#K(&h^ #@ J GW_O$"+#"&54632'7#$$#B"/B(^s^ "@gU]M$"+#"&54632!5! #$$#fK"](S2FK PX@nU^N@U^NY@ +5353(5S5qFH@t+##5#LF:z@t+'3533L:?He$@!W_O$#+#34632#"&Z)))) X&%%# ?Ro &@#~W_O$"+#"&546323#))))sZ,%%# ?zV V3+( 0+5''5'(f5a ::6K12 "F ]][^q [JK"PX@]L@U^NY@    +566735!`6:=K F" 21]]JWr(h JK PX@ _L@ _LY@$"(( +&&'536673"&546323"&54632C?E9:EA""$$##$$"@ $$ B"[WWXK PX@e_L@e_LY@  +!52#"&54632#"&546B$$""$$##W]]S^W;@8e d_L  +5!663232673#"&&#"K301(=220*]]1B1B[WWJK PX@e_L@e_LY@  +!52#"&546$$##W]][0@-eW_O  +!52#"&546$$##]]M #/\@YJ  h  _ 8L%$ +)$/%/##   +56673"&'332673"&546323"&54632 l9VUI8+)9K[##$$""$$Y 5 :ZK*),'I\M #/@ JK PX@*  nh _  8L@)  h _  8LY@&%$ +)$/%/##   +#&&'5#"&'332672#"&54632#"&546 @9%[SVUI8+)9$$##$$""5 : II\ZK*),'*Mu %JK PX@$n f_  8L@# f_  8LY@" !%%  +#5667!52#"&54632#"&546F8@ B$$##$$"" : 5]]|*Mu %O@LJ f  _8L !%%  +&&'535!"&546323"&546329l K##$$""$$^: 5 y]]$D'KPX@ JKPX@ J@ JYYKPX@ W g c8LKPX@!g g c8L@(~g e c8LYY@%$" ''$ +&&5463256673"&'332673?C" 4  "&OUPM ++,,KSA+(0 U% 56H;;H$Dz'KPX@ JKPX@ J@ JYYKPX@ W g c8LKPX@!g g c8L@(~g e c8LYY@%$" ''$ +&&54632.'53"&'332673?C# 4n'!  UPM ++,,KSA+(0 65 %U H;;H$D'KPX@ JKPX@ J@ JYYKPX@!W  g c8LKPX@"g  g c8L@) ~g e c8LYY@%$" ''# +5665&54632756673"&'332673M4 #CN  "'NUPM ++,,KS*0(+A U% 66H;;HDr'KPX@ JKPX@ J@ JYYKPX@!W  g c8LKPX@"g  g c8L@) ~g e c8LYY@%$" ''# +5665&546327.'53"&'332673-4 #C'!  UPM ++,,KS*0(+A65 %U H;;H"KPX@J@JYKPX@]&K]'L@]&K'K_.LY@ ""+"'53267>7!3##'#P'!  H񭦦u  +K |=T gp.loc&>X.l"KPX@ J@ JYKPX@](K]'L@](K'K_.LY@ +"'532>7!73#'#'#U4! Ljkssfn +M w $^黻YZc:@7 Jg]&K'L#!+33273###32654##Z| k dEA2@KxE`V8z39hNX,(|@ JKPX@](K _'K*L@'(K_/K'K _.K*LY@#!((#$ +33663273#'#"&'#02654#"NyJ;HgfmkJ;Fm31f:0/G!0YW\^+& _UPIIOU>J@GJe e ]&K] 'L' +#.5463!!3#!!#35#":&j^GK;@>: /O:ci||}*1/7^,!(1KPX@J@JYKPX@*e   e _(K_ .LKPX@8e   e _/K ](K'K_ .L@6e   e_/K ](K'K_ .LYY@!*)-+)1*1'%#" !! +"&'##7&&546336632!32673&&#"35#"lR{*A|aK%Af:G?4W.)X34,8Sf(&6 ilMCRW(:nPH?HsY1B8['"'Z*@'  J H&K'L+!##37'773'_HS8sIgGjdCVLxCk^Nk2)@&  JH(K'L+773'##37D6 mRDJ/ߕw@9)V@M9")8KPX@-! J@-! JYKPX@)g]&K_'K_ *LK2PX@-g]&K'K_.K_ *L@*g c]&K'K_.LYY@1.,+%#88 +"&'532654.#"###"'53267>7!6632 6F">#HK-GQ$!  +K<'!  "A6o_:Kx iQAM' L.loc&>X. |=T g!L^iH I",KPX@"J@"JYKPX@)g](K_'K_ *L@-g](K'K_.K_ *LY@&#!  ,, +"&'532654&#####"'532>7!6632X >:2DCGt +M?4!   EtGBm  GRCRYw $^?|]az;ZO){@JK2PX@'eg &K'K_*L@$egc &K'LY@)(&%'1 +6632#"&'532654.#"#!#3!3"A6o_:KxF6F">#HK-GQ$! !L^iH iQAM' 4N o"%T@Q Je  g(K'K_ *L %% +"&'532654&###5##33536632~ >:2DBGЕЕ  EtGBm  GRCR"?|]az;Z0! *@'a]&K'L +5#!#!35LN<" *@'a](K'L +%#5###!ĆƕmN"KZ058@5 Jga&K'L##+5#54&#"#36632309*T4?i5dq44aZN<6@3Ja_/K]'L'"+5#4#"#3366323FXC3R2Xl?v]W)B*&_iU` S JKPX@o]&L@U]MY@ +#'##'##'5(KK'+T2222T+$>@; J~KL  +"&'575575377>532aaaa?G$Q 2!W!>!W!z6W6>6V6 ?cBEf<Z: *@'K^L   +!3!!3" }6Z.U 6@3JdKL %# +#"&'53267467####33UpZ++' Ω6mev+2P9q30jz"U&KPX@ J@ JYKPX@~_K_L@#~_KK_LY@"! && +2#"&'532654&##57&&#"#466{zoRa6Z('W%G>EP+q>/JCIl[mbYd{:/.4it"NSLYy>$ ʰ3+:&L@JHK#PX@_K_L@g_LY&%%' +326654&#"56632#".5466=yR_*_K1?E3 ' 3#Di;BiAu[4Du93`oGit-L,EEg 9mNQR+U|Qgm  5@2 J~K`L  +"&5466733>773'3267IN.W>䠘c@Xq97O FB(M: ,- -* Eb4:?2FJ#E@Bgg]K_ L##  +#532654##532#532654##532|2;]DQX[*[W .;T/9UN$R5.%M`NE-I,k5"*C`PA'C)1%@"c]L  +"&&54633#"33Sf<ziXGBZI=e>iw~>7?IEZ.>@;JecKL  +"&'532665!#3!3,#*嗗9f~75d\q3Q&7) 3+'C I@F J~_K_L   +"&5467'572&&##"3267Rg\D_M)61\/e7-, %2_9 t)W  ~"5iz| wmlFB8 ( &Y. =>&$$`*@ JKPX@! f_K_LKPX@% f_KK_L@) fK_KK_LYY@)'#!&# +7#36632#"&/.'%32654&#"42'mtIItv$#43ZV_aTT``V͏GP[oo\`T46;5 =ppp"dJKPX@fK_L@fKK_LY@  +"&''##3326533'.'m4 6/JCA4 \L !1NS2JwE346;5 1@.JfKL +>73#'!#3'.'M4443! :@A; 6\46;5  "7@4JfKL"!  +1333##3'.'>77#T_U_p p+6+0046;5 ,75GD  +H@E&JIfKK_L!!!+!+%# +!#"&'532677'!#3>73'.'HoP ?9,- 4443Gl= )+T :@A; _46;5 #C&hy83+4@1 JeKL +53533#773##DBB<@ |BB|Tr-. Z'@$  JKL +%7'#37737#'`H@@;g e ]KL%! +75332##3##5#32654D4yhAXXE2@KJ|wh?lB8|JJ39h$pK PX@#pg ]KL@$~g ]KLY@!$$  +2###5#"#&&546633#326544yhA v#RDE2@Kwh?lB!.M/P|39hq"*KPX@  JKPX@  J@  JYYKPX@ g _KLKPX@( g_K _KL@& g_K ]KLYY@$#'%#*$*!  "" +%"&554#"566323332###5#32654]wx+ ,UK3:4yhAE2@Kv*<r S[,D;Pwh?lBP39h:H&8@5JG_K_L$%&" +7'#"&&5466327#'32654&#"lX+ tIIutIVZ$dNsV_aTT``Va#4\oo[[ox(4W(W .ppp:)'3A@>JGg_K_L$%&% +76654H#''7'#"&&54663232654&#"VZ-# &-> ::au$n@ tIIutIV_aTT``Vfx(!9!,D'?#@R%lK9FN\oo[[oppp(b)@&Je]KL$!  +32##532654&##(В%=#ҨQ1KAFI.jl1I3{213+9$;@8Je_K]L'&( +!#5!57>54&#"'>323533#lb*4;"R;O7[g =+L֓JJS+8.!a'RP,HG*JuXKPX@ J@ JYKPX@KL@KKKLY +3>73#'#73=Hw**EKQ7 ;AA; 6|38@5-"JIKK`L%" +%#"&'532677.'#3>73>73$)y99,- a  `[  hh  [K )+w ,4..3- zFFEApAFEEGI@Fe   g eKL  +2####53533#2654&##H{2udKXXoo1FF>B=n`7c>P1c66c;262+G=@:  g eeKL! +73##5#53332#'32654&##ooXXW{2udK1FF>B=3c>>c)Pp]9c=p18/- $@!  JKL +!57'5!6673WWWEW9Ĭ+r(VV((VV(B@7h3C+.*?@<%$ Jg_K_L%%!$%) +#"&'532654&##5326654&#"'6632U@PV}xBe&"^&F@\N2-?F10)H1,nKbn#GX  YH^w723/w)%+ e$*dp*>@;Jg_K_L! '% *!*%$$$ +3546632###"&'5326655#%2654&#"6jNmmq;9fC,#*i,2(" /5lRlaR\q3~84Sw)+ (309-Q">@;  Jg_K_L "" +"3267#"&54632#526654&01$&>5\mO~KJ]j-:YB;4< l vlrBigv=fahQK2PX@]K_L@g_LY@  +#"&&5467#52654&#"rHIttI*/aTT``VV\no\\oEz2}pppp[1{KPXJ@ JYKPX@!e_K_L@e_K]LY@  +2&&#"3##336608&E+q^<4Y|H\*<92*.@+$ J]K_L**!% +#"&&5467&&5467#5!#"2654&'z_YzEpBaN($~Ɩ'!.4,(5#<3xMiy1_GNa1*xx "420<!6%3692k ʰ3+"/5 ʰ3+Z1 !g@JKPX@!_K_K_L@_K_K]LY@ $%%" +%3267#"&54&#"#3366320\J2A?Jr%j?sy r`P.AH5V/F*&u[1gKPXJ@ JYKPX@_K_L@_K]LY@  +2&&#"#336608&E+q^<4s\*<7R1@.J]K_L%%! +5!#"3267#"&&54677h^z<_T+X3/\9eL[BL~~HzKajP]i'Z @fKL +!#!3!3N4Z0='@$ JaKL +!###33&&'33# 6j1b2RZ!E@B  Je_K_L!!%"%# +53>32&&#"3#3267#"&&'@ Sb5k11(Q'KY UP+X3/\9cK #|\N{aV|T[J\'D@A'&% Je_K_L%'#& +32675#5!#"&5576632&&#"%&RB ,8zL7A9n-2!T.Bb Fl=w~Y |y=6?Y'@$  JKL +5737737##W<ͬ@Y ^T!Y-.),@) JKL +73737##7'3&&'WVVWTWsS\ b Y 3  (O( @@=  Je]KL+% +!#'##575327'7&&##267cWW(SJA2B5.1@A Y Y ?`$+6$.6@3 .-   J_K_L$-%& +7&&54632&&#"7#"'532654&&'&&'Yp8d811N)+.00w'+~qY3m68/%>(  :&`kv( ("YL9_q+%+!&! \K PX@!pe]KL@"~e]KLY@ &! +!#!##"#&&546633!3 v#RD4N!.M/.]:#3KPX@ J@ JYKPX@ g_K_L@$gK_K_LY@%$-+$3%3 ## +"&'532655467##"&&546632373266554&#"YFx9zNc`G]}?HV/@18H"QL4G$ C3EN,)/ZakR'Li5X4te8c?>b7Wv JKPX@& gK_K ^L@$g gK ^LY@$! +%!#"&54632533#54#"3WG#F?<5gg+#}}6=/2=M3 >. ʰ3+/7C ʰ3+%S4@1JGgdL##$) +&'#"&5463233267&#" )IF/CXL5 !!("1>! @==< % $KPX@"!J@"!JYKPX@_K`L@K_K`LY@ $$ +233267#"&''#'&&#"5666?)7w% $NWBY% #-%J71<({ WV8+{ Zk$;@8Je]K]KL$#"$!,  +32###32654&##32654&#Zގ95#;"ytXB3Z&1 :|" /@,e]K]L  +3535#535#5!:̾B^]z]N"*",->-,)0KPX@  J@  JYK PX@( pe _K`LKPX@) ~e _K`L@3 ~e _K_K`LYY@+*.-*0+0))$"&$& +%#"&'#"&&546632!326732654&'%"3&&@3"-%K9R~HAtMqG?4X.#,83"%A*={__@wH?H!  8;1B&,&,9[@X76  "!Jgg_K_L42.,+)&$  99 +273267#"&'#"'532654##532654&#"'66Gxz !!9#1D- 5$#8!9x\J"c8=T:7JQ4;'](,/o,-0%3.& X7>$ 1)-M/"{#Ag!h+",)-E,$,P@M !  )(Jg_K_L&%%,&, $$ +"&5%&&#"5663273267#"&''265'siD96Z/)[@es !!:"2D8t]5>+ wc84sZ["4/% X7> TJjCM;'N"@KL +3#3㕕"-"&*P"+@(JK`L  +"&'532653"6 *&Uu"1G2R1^*@' eKL +3##53533533###JJJJHaOOOOaH*@'  J_KL%% +7546632&&#"7# W/W:0B#(#f*-Y*FM  m &x1WF1@.  Je_KL"%$ +3546632&&#"3###H/W:0B#(#HCFM  m &e"*R3 )0@-Jg_K_L$&&%%" +#"&&546324&#"663264&#"326RP|GP{G=<=< ;+*8 HC\BdLZVL2%%2-$"FhFM)&f 3+x tKPX@J G@ JYKPX@_K_L@_KK_LY%&$4 +!#'##"&'7326654&#"'6632xqT7   &F+)&H+;W/\*<<48& m  MF1"=@:Je(K`*L +"&'53265#53533#"6 *NNNN&Uu"1 aa2R1'"8@5g(K`*L  +"&5463333#'2655#"5IGEIDERgG.6E"MH[RgG,)&f 3+",3F*)&f 3+Nw"`@  JK2PX@~(K'K`*L@~d(K'LY@ $#+33267#"&55#2  $1>KK"K h JRJ"g ,@)Ue]M +##5#53532a33a::gG4)&f 3+1e"&*Q ԰3+K."&* F$T"C@@ggg_ L""  +#53254&##532#532654##532Q(AHFP!I>&?28=JDG5B`L<"=&6 ,`@>4E+d'%@"c]L  +%"&&54633#"33+Wq8wsO?C>L=4Y9\mw7.8:ANF$G@DJK_K^K_L $$ +"&'532654&#"#3366322#.*C3 Q3Yj#Nu"1o56]W)I*&_ia2R1z2f@c- J~]K `K_L/.,*$" 22 +%267#"&&5#5773!#"'5326654&&##57!4.G*1M-GR+_qXh.@f{]0o.=D"UL:D$m o  NG?2sta Eh?Em?'$:"$<#j'%-#>@; J~_K_L%%# +7'572.##"3267#"&&546V|5#"-_\.R(&&< !#44?GIX!m!& &@2JS["F*b-&3>I@$2#(   JKPX@%   g_ K  _L@)   g_ KK  _LY@!GEA?<:640.+)! && +26632#'##"&'#"&5467754&#"'6667754&#"3265%3265.a9(]2mvh#ND1MPCSdzz_-((L&1+l)5_-(7:H8( 0BQ:H8( 0B-%_bJ,(&'!,TYWR+(e%+( 0'"94-0'"94*-#/:KPX@! J@!  JYKPX@"g_ K _L@7g_ K_ K_K _LY@8620.,(& ## +2632#"&'##"&5467754&#"'6632654&#"3265.u8CiMvD{En#(^CSdzz_-((L&1+l!5<;55<;5:H8( 0B-54B~Z6372TYWR+(eQSSQQQQf0'"94*u-+6KPX@)(JKPX@)(J@)( JYYKPX@"g_ K _LKPX@&gK_ K _L@4gK_ K_KK _LYY@42.,&$! ++ +232653#'##"&'#"&5467754&#"'663265.nv*.D2r[3:W.D2Sdzz_-((L&1+l:H8( 0B-_b;;]WF*&(,&TYWR+(e0'"94*v-%0KPX@JKPX@J@JYYKPX@ g_K_LKPX@$gK_K_L@(gK_KK_LYY@.,(&%%%#$$ +!'##"&5467754&#"'66326673'3265#NDI`zz_-((L&1+l:nu :H8( 0BJ,(TYWR+(e_b:9=0'"94*v-)4KPX@ *JKPX@ *J@ *JYYKPX@ e_K_LKPX@$eK_K_L@(eK_KK_LYY@20%$%)$ +!'##"&5467754&#"'6632373'36677#'3265#NDI`zW-((L&1+l:nunNS J:C=( 0BJ,(TY[Z $e^]sN'99 60"94* v-2=KPX@,+J@,+JYKPX@(g_K_K_L@,gK_K_K_LY@ $#%#%#$$ +!'##"&5467754&#"'66326673#"&'532673265#NDI`zz_-((L&1+l:nu -lJ$ /7 #:H8( 0BJ,(TYWR+(e_b:9=TIv:'0'"94,l8@5 JeKKL +#366773#'##5353} 欝@LLa=*3HaOONl+@(  JKKL +%7'#33667737#' I3@ 9P9Rv5F\9H3=*M>H@K6l"9@6"! JeKKL +%7'##53533#3667737#' I3@LL 9P9Rv5F\9H3HaOOa=*M>H@K6Nx"@JKL +7#㕕11 !@eKL +3##53533#<<<<HaOOa,E@B e _K _L"" +55366323##"&'%"3&&267#UogVU oh 0550245hnuunhqzzq5665:99:-,&2@ JKPX@" g_K _L@, g_K_K _LY@('.,'2(2" &&  +"&&546326632#"&'254&#"2654&#"4LwD|7W!8,JL?1 @v2;55<;55 B[ $XE%G.ItBT? %SQQQQQQS-,&RRyL,(@  JKPX@' e _K _KL@+ eK _K _KLY@%#(($$ +53336632#"&'#3##5"32654LyJ;\rv\;F:0/>31abG!0+*&,aOOVIIOUUP,(4KPX@%-" J@%-" JYK PX@,pg _ K_KLKPX@-~g _ K_KL@1~gK _ K_KLYY@*)1/)4*4$#!(( +2#"&'#&#"#&&5466323366"32654\r?oI"7 v#RDyJ ;0939,XE ,!.K-G!0wII J[n,,9KPX@  *J@  *JYKPX@_K_KL@!K_K_KLY@ $EE$%% +%&&554#"566323336632#""##5&&2632654&#"eV+ ,UK9CyJ;\rMe  / UJ24:1'Sb<r R\]@TG!0\H cNGNI-w,(KPX@  J@  JYKPX@' e _K _KL@+ eK _K _KLY@%#(($& +53547##"&546323733##526754&#"J<\rt]01A54a5))"/."FaOO IIOUUP-,'3@&JIGKPX@*_K_K _KLKPX@'_K_K _KL@'_K_K _KLYY@)(0.(3)3''$) +5'767##"&5463237376654426754&#"3J<\rt]01A54,"/."Fn 3(,CU@2H%[IIOUUP")@&Je]KL$!  +32#'#532654&##b{A*{RS+6&(f"WRCMd'"'&u-#:@7Ja_K]L'&' +#5!576654&#"'>323533#61=4R9J5Uc ;*FJK`1F#a&WG'@@)Dt91@. J]K]L +3'#3336732EQ7PHs iCz'<6:7 N";8@54&JKK`L;:.-! %# +%#"&'532677'.'##33>733>773Oa5& )< 2    ,?  C@ A dl*v0/ 4>;+,=@6"EA/2( *3.AGL ,D@A JeK_K _KL+)#'" +#"&'###53533#366324#"326Lr\;J<<L:\rf:0/>31'-8aOOa7"/IIOUUL ,K@H J eK_K _KL+)$" $' +##5#53336632#"&'#4#"326}<<L:\rr\;Jf:0/>31?aPPa77"/'-,SIIOUUNK"@JKL +#366773㕕  ;7 ,+?@<%$Jg_K_L$-%$!" +%4&##532654&#"'6632#"'53266?QP?=HS7:"D-(`E@;Jg_K_L &$) )$$%# +#"&'53265#53546632##72654&#"?&UF6 *1hQhr}w;;-4%%$.;2R1u"1x4@;  Jg_K_L !! +"3267#"&54632#526654&085/. " 81\m~GYe+;QD:Fv v}orEz{c-c2,KPX@  JK.PX@  J@   JYYKPX@-  ~K _K ^ LK.PX@9  ~K _K ^K ` L@5  ~K _K^K  _ LYY@!! (& ,!, +"&546323&&5533733##7#'#'26754&#"[st^;L27`7u8a8{J=21B188 ."= zrG"/wIIOUUPPQNc ,@)~K^L +33##7#33K`7u8a81 rzNc,," IKPX@7~ ] K _ K^ L@;~ K ] K _ K^ LY@#*)'%!  ,, +23733##7#4#"#4#"#336632366]_27`7u8a8R;2R>/rW/"\-4Tc" L@IJ  ~  e ]K^ L ! +3!23733##7#'#32654&##TawA2Q37`7u8a8]MV+3')d"RS?Ftr/'"'D&0@JKPX@-  ~ _ K_ K`L@1  ~ _ K_ KK`LY@!('-+'0(0"!  && +2####3267#"&&5#577335466254&#"\]q`$.G*1M-GR+_-Z8E#]FXPN o  NG?2st4V5:%$&. ,8@5'J_K]L"!!,",   +2##532654&''7&&5466"6654&BR&*383mXñ@6i%=+P;!/K*8X/[85V4r,).l~!S6+K-e-*->#)@&Hg_L##% +#"&&54667&&'72654&#"-5zLwD=kFDg0iLN,xKPXJ@ JYKPX@_K]KL@ K_K]KLY@  +2&&#"3##3366   &F+qT,<4r\*<.e6" "3+N"@KL +#3㕕N, a@ JKPX@_K_KL@K_K_KLY@ %%$# +%54&#"#3366323267#"&*.D2r[3Xj0\I;;]WF*&_i r`N,`KPXJ@ JYKPX@_KL@K_KLY@  +2&&#"#3366   &F+qT,<4\*<"5@2J]K_L  +"&5467#5!#"3267,u<4PYtH7&F""K xtJgrr^^ADV&C@@ J ggdL$"&&%#$! +7#"&5463233#3267#"&"3354#F?<5bb0\J+#=/2= yM r` 3N<,U JKPX@a_KL@aK_KLY@ $# +!#4&#"#3366323#_*.D2r[3XjP?;;]W"F*&_i,E@B Je_K_L%"%" +5536632&&#"3#3267#"&'Tk8S,#=^;0/H""K9r awmsla=;t}u,%+1KPX@ 0/)(%  J@ 0/)(%  JYKPX@"_K_K_L@&K_K_K_LY@-,'&,1-1&+'+($'# +766323737#"&'53255467##"&'%"7&&265*o_e9 ~JJ:c/dqsN1Si.+[ 5,97 ~PF Y uzw*| %+&oe{2,AQtl+@( JKKL +733667737#'#5KЮ@K <)Y3 ,\@  JKPX@_KL@K_KLY@ $ +73366327#5#5%"7&Kr[3XjHHΕKV;4 F*&_i Y  EAj,oKPX@J H@ JYKPX@_KL@K_KLY$4 +7336632&&#"7#5 WqT7   #@-ĕW \*<2*Y ,,6@3 ,+!   J_K_L%.%% +7&54632&&#"7#"&'532654&&'M#vb3\1-(H%B10Cut9R),f'+&.0! &BLLk$ Y / SY{Nq~:, RN"L" "3+->#+@(JHg_L%%$) +32654&#"56632#"&5461K^4<8RnvwTs<9",@) JK`L +"&5467336673'3267DJh^ţg edi-3GA@?e ..3QY7B+*-&DP-$,&HP. &O NF,&Q W-&LPKC"&XP3&II&I'IL&I'IOm&ILf&ION'KPX@ $%J@ $%JYKPX@"_K]K_L@&_K]KK_LY@"  '' +"&&5#575&&#"#466323#3267e1M-HH7=5DtFX $.G NG?+N75!Na-1p o -Q%KPX@  8 A76B+,JKPX@  8 A76 B+,J@  8 A76 B+,JYYKPX@% _K _K_ LKPX@/ _K _K_K_ L@8 _K _K]K_ K_ LYY@FD<:540.)'$#"!QQ +"&'532654&&'.546323&5466323#3267#"&&5#575&&#"&&#"9R),f'+&154B vb 3eKXy $.G*1M-GG*75#-(H%B10/D%u {,=.LL*G,1p o  NG?+U & k$ )>0SYRI1@. JfLKML +13#'#7'.'ͶϚ''#$I}}u2442uR& AR'&MD$$3+R"&J.$$3+R& >R& @R&L\$$3+RI&P<Ri&O$$3+R&& F A3+3+R&Q6$$3+FpK.PX@' e  e]LK]ML@-p e  e]LK]MLY@ +%##!#3#3!#373a||Fqmrs& AF F"D@AJg]LK]ML!""  +2##2654&##2654&## }v6(0wh1%,/:G3()5DFEM5C7/OZF!v' (/O7@4  J_PK_QL  +"3267#"&&546632&&LBHDJ&J(L[_y:B\+]+,"CcWY_vKYXMo/& A,/"&KM$$3+/O&z/"&JL$$3+/& ?F>F@]LK]ML!#!" +##324##326>ŏ:.UQ,FZ >F 7@4e]LK]ML ! +35#53532#'32654##3#F::ŏ%.UQ:hhqꑉrZ\zqF>"&K9$$3+ >FFF )@&e]LK]ML +!#3#3!F_FpppqF& A:'&M$$3+%"&K$$3+$"&J$$3+F& >F&N$$3+'& @F&L*$$3+FF&PqFF #@ e]LKML +!#3##F]Fpq/(O;@8  Je_PK_QL$%$" +%#"&54632&&#"32675#53(.oA3a%.G&STHK' nniQUblp/('&Mn$$3+/("&JX$$3+/#(O& V/(& ?F=F '@$eLKML  +#5##335=אFF lG;@8   e   eLKML +##5###535335335#l8ɔ77ɕ8Xe::::PPF="&JC$$3+>F &@# JLKML  +357'5!JJ"IIPlNNP& As'&M$$3+"&J$$3+U& >~>&N9$$3+>& @xP2F&[ V&L$$3+>F&P%z&Q$$3+PF(@%JcLL  +"&'532653' &&3Zp-;Qb,Pe"&J$$3+F8F @ JLKML +366773#'#F  ȡ3F$"F#8F& 5FF@LK^ML +3!F+qFF& AwFF&'~N N3+F#F& FF&N 3+G ,@) JLK^ML  +%!5'7537%:_b=wwc9>b_FF&@#JLKML +#4667####33ۄF F=H>HFQFlF#@ JLKML +###33&&'lF5=FZZ(Fl& A?Fl"&Kc$$3+F#lF& \FNlF3@0JdLKML%# +#"&'53267##33&&55lqZ*!)&FXZj "5=FX)Fl&Qj$$3+/oP@_PK_QL$%&# +#"&&54663232654&#"o<ec=>cd/o& @/{.&R003+/o&L003+/or!)<@9%$ JHG_PK_QL&-*# +#"''7&&5466327&#"4'326o<eK6$I#,)>c&@%H#+)U $JB!K?$ZK7/6(wKZJ 8/6'wK 6 cVA* b/o& A6/o%&Qc003+0P%@ #"JKPX@#e _ PK _MLK"PX@.e _ PK ]LK _MLK'PX@+e _ PK]LK _ML@3e _ PK]LK]MK _QLYYY@ %%  +2!#3#3!#"&&5466"3267&&H:j:^|==|`B>>A44PqnrsK[YKtcUUb M FF -@*g]LKML    +2###32654## so+fV2#-8U3F`W3[8F(+IHF '@$ggLKML$"!# +###33232654&##+eW8>ro%07,0053Z7qFWd(,$$/uuP+@(J_PK_QL$%&! +#'#"&&54663232654&#"oCIkc=>cdDJKe$~"*B4RWk"J&& A&"&K$$3+&O&z&"&J$$3+&#O& >RP%K"PX@%  J@%  JYK"PX@~_PK_QL@"~_PKMK_QLY@ #$$$& +#"'532654&##57&&#"#4632>D0dN:21+.-:P &24vivJ MD0R2 y # #]`>@epzWPF!@]LKML +###5ٝFr,rF/@,e]LKML +#3##5#535#5ٝ^^__Frggr"&K$$3+F&z#F& C9F!@LK`QL#$ +#"&53326598pUz74:2FBe:xhnD9C9& @ Cd"&R$$3+C9&Lr$$3+C9F'2@/JcLK`QL#&%" +3267#"&5467#"&5332653 $8B," z74:2%%'(oW>2$@xhnD973>73G  HG  HG H 6::6 F:;GABG;93C& AC"&J$$3+C& >tC& @nDF @ JLKML +%#373#Ǻxuʤ,F#@ JLKML +#537ǏǚtsFh&  A"& J$$3+&  >&  @F /@,J]LK]ML  +35!5!!X}qVq&  A"& K$$3+& N$$3+/GL)&f 3+9: @W_O%" +4632#"&9K6";#L46K`E87.B::2,21@ 13+2G)%@-G3t@ZU0 ʰ3+ e @ J]L +!!57'5!eWWEWWV((VV([(1*@', JKL11 +13>73>73#.'#.'[  hh  [a  `Y  [zFFEApAFEE6w ,4..3- @BFFz5@2Je&K'K`*L%#+3!3#!#"&'5326T_S. =65]T s0$O@L Je Q ] &K ]'L$#+!33##!3#5!#3>7#! _A7&B4 '6  Zj8NJ҄~FG/ &H@E#  J  ~ R]&K^'L%$"! +3667&&53667!3#5!6675#35mGK8 pZA)F(ikhWN= ZF&DRW19z&;/|KPX@ J@ JIYKPX@Q]&K_'L@"a]&K'K_.LY@$(+%#5###"'53267>7!}  +K<'!  }L.loc&>X. |=T g'@#K.PX@JG@JGYK.PX@a]&K]'L@#pb]&K]'LY@ U"%!#+5663232654&&#!5!5!76#"&#"J :6@.5'9:Vofq.gi4`Hp  b}b+M1C\ '@+K.PX@  J G@  J GYK.PX@' e a]&K ]'L@-p e  b]&K ]'LY@*% %!# +5663232654&&#!57#537!5!3#76#"&#"J :6@.5'9:ezmsofq.gi4`Hp  b|}b|+M1C\ (G6'@ '@$ J&K`.L%%+7'5!3267#"&&wWEW$,B'3R/(VV(_ o  NU6@3 Je]&K'L# +53533##54&#"#54675nlCJGHok|II|L_SNGYgK:".@+ J~&K`.L&"+%#"&546733332654&'#55⥯cgc`V_aT05:5hhQrqK4::4'4  6:5S@P& % 3J~_-K_ .L1/*(#! 55 +"&&54632&&#"32675332654&#"'6632#"&'ljA'V52З3EOVJ'& m  MFj8b?ju|,79$ <@9g]&K] 'L ! +3#5!32#!3%32654&##J1c>|(CMOH!L~8b?ju6|,79$Z] =@:e  g&K ^ 'L %! +333533#32###%32654&##Z1c>Ä(CMOH!LLL}I8b?ju|,79$:!KPX@e_-K_'LKPX@#e&K_-K_'L@'e&K_-K'K_.LYY@ $%##+4663233###"&&%4&#"326:DlIdlENUTLLVSNfo[64`O\opppZ:4@1Jf&K 'L +33!3####3'.'Zofeq%3**,,0) *0- 7@4Jf&K]'L +!3'.!'&]  {5[63L .4. CC.4- E@B  J ~e]&K'L  +%##5##7667'5!#7#"!&&#T$JRA{@TJ|a><J;4W\DD\W3.01Z: ?@<J f&K] 'L  +33!3!#3'.'!'Zo&q8:];%3*,,0) *0-  ~`@ JK'PX@]&K_'L@ ]&K'K_.LY@ %'+!#'&&'####"&'532673͙: '9. / .P:6e@W- v0,0G @05@2Q] &K]'L +!3#5!#3>7#!_A7&B4 '6 }1NJ҄~FGU@ JKPX@]&K_'L@]&K'K_.LY@ $(+!###"'53267>7!!  +K<'!  L.loc&>X. |=T g}ZX,@) J]&K'L+!##33!!#4667#Ω0P\$"}R"XO:&2" "3+:'3I@F  g_-K _.L)(/-(3)3#!''  +"&&546632'2654&#"7"&546323"&54632tIIutIItaTT``VV(())(()) \oo[[oo\pppp !##! !##! :' .  .07@4JQ]&K]'L +3!3#5!#3>7#!_A7$A4 '6 NH~gFG.+7@4J_-K_*L+++"&'532654&&'.546632&&#"8`,+b0A?$?(<2@nDJb)1)C)3:!=)3N-$D4)=71A[?Hl<vA3*<4%Q^;}.(7@2 1 JKPX@# g ] &K]'L@+ g&K _ -K'K_.LY@64/-(&$&$ +333####"&'532654&##5326654&#"'6632Gї.Q}.NP"_U|iC`l'7(R0&/*T4!aZ'44 '53@,5<@&JK PX@/Wgc ] &K  ]'L@0 ggc ] &K ]'LY@!<;763210,+*)('$" 55+%2#"&'532654##532654&#"!#3>7!366#!a'C)("$/S[*E>#&6b%#/3!%:7&B4Z> '6 )%"&+? S )A  0NJ҄ FG ;@8Je]&K'K_*L%&!"+%4&####5!#32#"&'53265Y). ֪px#N@4#+#L~~e\2R1u"1/+@(J]&K_.L%$+7#5!#3267#"&&κ+#,Cf9V~~57~3qZ%D@AJe&K^'K]*L %$ +"&5463!#"3326654&#!3!3;55-&&"='00^(((aU7+,3C %!%2LN,/R3:/=@:Jc]&K'L +"&54667#5!##"32678B)C'g..,  $@5+?% ~~$/W37@4 Jg]&K'L##+5!#32673##"&5509.^:Er:epL~~~44@6aZZ!7@4Jg_-K'L#&%#+46632&&#"6632#54&#"#Z9fC,#+?i5dq09*T4\q3~75aZ44Z*)J@G"Je &K^'K] *L!  )( +"&5463!!"3!26654&#!33333#;55- &l!>'00ޘޗ''(`V7+,3C %!%2LLL,/S3::#;@8!Jf-K.L &#+#"&&546632&&'#3553#66IttIIutI ?7~6> ?9;?fo\\oo[[(CU TH] ^@"A@>Je(K'K`*L +"&'532653353#5#1,Ε K s ^2S1<""#O@L Je Q ] (K ]'L#"+3533##5#3#5!#3>7#39̕ѤߕP.$2p *&" z17Rp[G<"!(>@;$J R](K^'L#" +3667&&553667!3#5!6675#35`  F@yYO!;` H7!~ +M?4! kYw $^I-$(4V@S!,Jgg gW_O*)0.)4*4%#(((%%" +%#"&546632#"&'6632"3254&"32654&$jmP`PT.N0&C% V%[h#<5G >A:(,%)Jbɴ{`K7-; GT&" X*$ 29 /5!$" 8@5eU]M  +3#!#3667#39P.6: p *&"s{KopOA-,"@gW_O&&&#+#"&&546632326654&&#"1Z>9Z30[>:Y3   j|66|jj{55{j@GG@@FF-[,:@7  JgW_O +"&546632&&#"3267rQbIq/,3`!_WXPCc2+_ d~<s RSRN%@"U]M+#!5vd5ZpN{" *@'U]M +3!####N-"NNs 9@6eeU]M   $!+32#!#5#32654i~ypfh+8WPQP^p&<sC@@ e eU]M$! +3#32#!#535#32654i~yp~~fh+8pcPQP^p&<+_-@*JW_O$#'+3#"&&5467332654&#"E~SQ=|__|=OQ=CD<;DF;}THrBBrHS|@II@?IK'"$<@9JGa](K]'LU"%1#+5663232654&&##5#5!32#"&#"0&$%-$+622-\k.\f>a/nernvvnrdn/*6@3%J`.L * * +"&5467.536653'2654&'Xa8--8 #!+0.M,8/)R>%" \H2`80KDK0+?>);]G}96a;+O1e $1"6 ':,4S@P% $ 2J~_/K_ .L0.)'"  44 +"&546632&&#"3267'332654#"'6632#"&'9z, +<9~WXo/,,-ffff=F44F<zz !N" gKPX@ f (K]'L@#U f (K]'LY@  +373!7###3'.!'][Õ= r e,.""V0*").vv]"6@3J~](K'K*L%&+!#'&&'####"&'532673]  >= * <6DNs~<b"G@<"5@2Q] (K]'L +#3#5!#3>73#P.$2!o *"p17RKE["U@  JKPX@](K_'L@](K'K_.LY@ ##+!###"'532>7!#7 +M?4! mYw $^pN"-@* J](K'L+##467####3ϋpӈ"pN (L `!K-"r->, %>@;g_/K_.L!%%  +"&&54632'2654&#"7"&546324LwD|MvDyF>>GF>?G**** B[B~ZxSQQQQQQS[ )**) -, %1-@*g_/K_.L$$$$$%%"+#"&&5463232654&#"4632#"&74632#"&XNXN(OYXOOYYN%''%%''%B[B~ZQSSQQQQM" "! !" "! -,' ` `<6@3IQ](K]'L +333#5!#3>7#3P.$2p *&K17Rp[G-,'.@+J_/K_*L%*%"+#"&'532654&'&&54632&&#"sk9\)$a&.00FDQqg3\1)K ()11-D' lzr1+*I,+hS_n b2 )- @Q&,5KPX@3 2  J@3 2  JYKPX@$  g ] (K]'L@,  g(K _ /K'K_.LY@0.*('%"  55 +23533##5##"'532654##532654&#"'66;g?Ѥߕ 9x\J"c8=T:7JQ4;'](,/o,@2- %-M/"{#Ag!h<""6@3 Jh(K'L"""$#" +326753#5#"&'#"&5533267&55.<'C"!U61J(d?Q`<'E""G +WYG1"4:KPX@%J@%JYKPX@/Wgc ] (K  ]'LKPX@0 ggc ] (K ]'L@5 ggUc ] (K ]'LYY@!:965210/+*)('&$" 44+%2#"&'532654##532654&#"!#3>7!366#3$?&% "+MU'@:!$2\"!+0#/1 .$2I8p *&}%##(; M '< %17RK 5[G"$I@FJg](K'K_*L$$%%&# +#6632#"&'5326554&#"##5#Z9Ve#N@3#&!'H&"pWY2R1u"1+"p"+@( J](K_.L%#+7#5!#3267#"&&«* 7FU&pp2!u1RN""D@AJe(K^'K]*L "! +"&54633#"33254&#!333;55-&-3ڕ=(aU7+,3C [%,"N!-P/R3:"=@:Jc](K'L +"&5467#5!##"32678BP7Y..,  $@5=LrppN$/W"1@. Jg](K'L#"+#326753#5#"&55#5!B7]0+nGUfXG WY[pNF#W@JK2PX@g_(K'L@gg'LY@ ")%#+46632&&#"36632#4#"#N/W;+H&)R2XlXC3JFM  m &?*&_i+v]WN"&J@G Je (K^'K] *L &% +"&5463!!"3!254&#!33333#n;55-X&-3~8(aU7+,3C [%,"NN,M/S3-,T->,  B@?Jf/K.L   +2#"&&54633&&'567#7MvD{LwDS**Y))**ROV,B~ZB[x:549 vU6<zzi->-@H_.L(&/+#"&&54667.546632654&&&#C17P,EyMLvD1L+4$WH5#:46<2}E]@Ni65gLAW6 #4&9H* ';(8@A9&5&.,/ .j/ .j/ .rj/ .j/ .j/ .rj .j<3+3+3+ 3+ 3+3+3+k' ^ gG)&f 3+3xgG)&f 3+Z:K2PX@e8K9L@e]8LY+##33˗4 JK2PX@~]8K9L@~]8LY@ +!###!##uuLx ZZf@  JIK2PX@8K9K_=L@]8K_=LY@ +"&'53267#366773JU}#l8Vk@ Y" z 9&=.*rDm?.-N"BK2PX@e;K9L@e];LY@ +3##㨨"o"/ JK2PX@~]:K9L@~]:LY@ +!###!#5#mD~~D N"K`,=@:Jgg_.L(&%#,, +2#"&5466"32654&##532654&IGo?JNSg~}BsF.9=:8L>*!;97+T@HT  __gnngLQe0r<7>@<8>;r9//-=zKG@DG.J" HW_OCB;:KK+".5467>7>7327#"&&''2>54&&547"326678r_9=;+?& &*B2.\NbH me@ HO)! JA@8HK PX@6n~||W`P@5~||W`PY@VTEC=;'% ee +"&'7326'.#.#"'>3276654.54>732667#"&&'>32)4#8=/7F $!"+*(&2&%8 :E" |E^f(=7; %(%KVZ*@;2! @5 '3F. ")& TRCz,04 IX@U:9#"J!I~~|W_O-+II#&#&+'>3232667#".#""&&''76632326654&&54>73*3<<6)" HN *66$<)-) 1h  %24 $30 "" :6$. 1=9&77-=+| "."3S/-]Q &*#' 3267>54.#"'>54.54667>32>3232673267'6654&&#"06677"&&554&#"  40  !49-&'%  %! ,=&)I4 8L1#& " 5Z2,!% D0%<=="6 .* 7%  !>fNKg@(((+(' %$)/( ' +F(E2%& -& tz:+C<LP 71tE"+,3-P V@5$# J76 H  n  ~  |||||hW_ ONLA?><20.,&%! VV +"&'4&#"'>3232>54&#"'7>54&#"#"&&5732632>324  ,70C)VF"2 /72 &: 00 $!K &/&0!10.0# 0>$8^vq :1  &@KKEK A,:!!  l  *2'A3%/3oT#:.Y@V  - J~e_K_L,*$" .. +"&54632&&#"33#"'5326654&&##57dqjQ)<( +4G߰vBh`1t/?G$[Q9u`dbb ^ 0)36P o_Dm?'#72j%.-X@U  ,! Jgee_L+)%# -- +"&546632&&#"332#"'532654&##57[l0S5'8&.:3ɩx?euX.j,XAQ]P)WY=S, Z,)-$TaY8_:!5&$-bC@PTQN#[ nm J  ~  |  |gg g  WW_ OwufdLJA?8731'% +".546332667>7>7&&#"32>53#"&54>7663267#"&&54667032676654&&'#"&54632>54&'3< 1"$  +$>YK**8*4"6#=q[59'$#K@)9O04:':98[(C/'7R' %&,HR' -1 % 7  !1# 1<(![ .2(7&  QX-SY2%-$ $5F*)(.Rm?0%PF+44+I:( !. B(/H4$9)'QE*$ ,<"%095$P8   :V23NcuA(@h>:~@{W >(=\ sJ  ~  gggg W _ O}|wuecQOEC:8/- +".5463232667'0>776670#"&54>54&#"'0>3232>7667>32032>73#"&5467667>54&#"9A1"$  1+8J=%?P1C) O?2:4 AL%0:< 1CKH -" %. %\db+"%4nW/- +0,!#/43!:+$mvcD (,$ (1-;B(|E"3k]9 @MICG$P>OO@LD-J~ggW_O43&%$# >>+".54632#"32667.54667663>54'77>75? 1"$#22*E=-:;oA73A00 !@) !A5`|CPa=:=pX3& !21(7'   '5]; 5C8ld)32&#">32326670#"&'67>54&#"267&&#"";%=%&T)  BR+4[u@---):cI( 27=DLT-+&/UqCG(!<!HA%6D(&E +WBB<?,%<20x,9$J(4$3 %# H+  *;>5`K+  .Nb5,8 <}qZ4*$gl\3CY&D-!11!B5We0Dq"a 5@2eU]M  +3!##'3#3#aGxCCCC6k5``^~@{z ~{xM = J  ~  gg  g g  WW_ Ovtkiba][QOCA;:42,* +"&&54>54&'"#"&54632>54'#".546332667667>7&&#"32>53#"&54>766326732>7p $!   .0"01<(![U9A1"$  -$>PB) W>4"6#=q[;@-$"L@) 9O04:-A?8[(C98om !# $3> #EF=  .C-^ NcuA(@h>&5. (7&  C}XCL%-$ ,AN*)(.Rm?0%PF+44+QE1 !. O(@h+"JD() 5=,2)% ʰ3+p2F >@;  JgW_O +"&&546632&#"3267'~pIOo0]0PU+K>W)X*/VS) Zpl] 8" ; zSO|a ?@< eeU] M  +3333##'3#3#axCCCC:6[5``a &@# Jt +333#%5aL5M:"65bca\ ?@<JeeU]M %!+3!2##'3#3#6654'axBl=cxCCx$/Sm[>_65`1@54&#"&&546632254&'&"=Y0EqC ;d@0<" 0%"$#-'"1!./K+5#9#A3 3(M?&$<$%?(%# (EX{ % 4\;=hE 4*,S5.29))< 073,36aC   6NZ/556H(%'6Z4$(B(#--O:!  P8@10d vV |YL JhI  ~  ~g g  g g  W _  O:9rpRPJICA9:(& 88+".54>3232670#".#"32>54&'7".5463326677'0>7>77667>320>7"!':\S3@.."%#  /&DCF(6mY6 2,#3Jk.;!1"$  -',F= 8R,E07QB %&  G N `5%$ !RSF+ %!.Oe6&% #7A< ')+#(7&  6^;A1 ":e_/ $Ze5$ 8"'!** UL{@@=HG*Jr[6HW`PfdMKDB{{+"&5463232>7>7>766766732667#"&54>7>7#"&&54667>7>H3(!). )%7_VW/?|i# #9?>,="8f062 "$8> @@"(&,' /40#TyG=Y#3GF00$!SYP(QWg J:*;#( "=e~@Vj 5HNG2 8NUPUfd'8( DdV5-=&4^lFC2Zki'6whA!*3JK PX@( p g]K]LK'PX@) ~ g]K]L@. ~W e]K]LYY@+++3+2.,*($"!!!,!& +7&&54663332#!#"732654&##32654&#"NAޏ:4$:"z XB3=@PcD67H "(F,Pe=T #D8an=!)))$5,(1D@AJe]K_L  +"&'53265#53!!3#>, kk_ rW|4||\T+5KPX@J@JYKPX@!g_K _L@)gK_KK _LY@1/.,'%55 +"&&54675&&546632373#'#'266554&&#"33#"=T{CcgW]9lKMm'}5NIQ %VK17Pg VkR 6`>H[PC3U3,+M6N(w/eQOAR%#0/t3260++?@<Jg_K_L'%$"++ +"&&54675&&546632'2654&#"33#"Rh=cgW]9|f|JKghWjO@hR}5N 6\;M] 'NCM8t47)7lyx6N( #+K@H% JegWe_O   +"&54>323#773#2>54&&#"@Q"Bc@4D B'RCB/Qbo2 ;&KUyL.; ""(/5@2*(JgW_O  +"&54>3232676677&"#"6654'nq*SxNH`'.-^-,YI4% +{$)/L9A m]@}g><>Bd ?4 -'lo)H'< V M@Jg geU] M     +"&54632'2654#"3'3#"-2/L; qrnC\CP&%*76632326673'>54#"&1 ,!!7C%! :fO NR#Q\-;L+2-% ),$5 (&AN! ::2(+L/*U9<@4 2KO#;S@P 'J~|gW_O%$64.,$;%;##+".5467'7>32>73'267&&546326654&#"%0 ,PLR'+.4!/0*@*<)"  +M7+ ),$5 _$B),TZ%.&'1' 1o+%^jS.'{Q3FUg@d!$ LJ~~g g W _OHG54GUHU><4F5F)(#"33 +"&&54>77#"&&5467'667663273>73026676654#"26677 !*5S_W"$3$&  A #;#^:)'.v'B. 3T6N2GXx!B: "01-#"51O/BF=&{",!DA9+ G )87@ &D"(;!(@:5 ?I1GH17T,"J (+EPN; -N2(3<@ - ,)O@L  (! J_K_K_L%# )) +"&546632&&#"32677#"&'532655,zDyO8S,#=t=7/H" #M>4$ d~<sRN#1S3u%7N{&@JK2PX@)~K_KK_L@&~cK_KLY@#" && +"&'532657#4#"#3366323(  OXC3R2Xl5Hh!@?v]W)I*&_iVP/VA(5@(5JKPX@'c_K]K_L@+c_K]KK_LY@ %)%'&1 +3632#"&'##3267#"&&546732654&&#"C]}Ht^F)@ (ABsF/>.6 G;.Pe7+<#fGP m ;j*OUTM.K, N@K J I e_K]KL %& +##5#535#57546632&&#"3#zOORR/W;+H&)Uaa^H((FM  m &"p]&+,4KPX@J@JYKPX@!g_K _L@)gK_KK _LY@ 0.-+'%4 4 +"&&546675&&54>32373#'#'26554&#"33#"6Z6;2/9$7>3?& ~rPG78F(2,7 607 "F59) B/'7!%FG2sKF,KMg!!"&H,g:+"%|KPX@ "J@ "JYKPX@gK`L@gKK`LY@!  %% +"&&54675&&55333#"32653#'#4P..:3527)/*$G>rP 'G/.H>;|p02g!%KF(G2+)/5KPX@    J@    JYKPX@/  ~  f _K _LK'PX@3  ~  fK _K _L@:  ~ ~  fK _K _LYY@+10+*430515-,*/+/'&$" )) +"&'#536632366733#3267#"&'#"3&&267#Zs+- v]:J { WW,2> LV3472 }xYru))) zCY>"u$."0p79=A~*j-.9@g@d+J  g  e_K _ L;:0/>=:@;@53/909(&#"  .. +"&&5467754&#"'66326632&&#"!#"&&''2655267#/O0vv]+&'I%0+j9n9&Y:?X).W4?G`n#KB;M .>7D5%438 %M;WR)" c@ sH?Hw-$#-m94-0'"B1;8:&@#JGt* +'7.55467733267'7654''$E- ` #,' dF %E< r $ yJm L,!'H@EJ  ee _K_L#"%$"'#'%"3% +734546632!!!3267#"&'#"3&&*AtMvA'4W.)X?c7.J/ _@  O$!sY[I-,-4@'(  JKPX@* g  g  _K_LK.PX@0g  g  g  _K_L@0g g  g  _K_LYY@/.21.4/4,*$!%""" +746326632!3267#"&&'&&#"327#"&"3&&WK0eqG?5V.(Y?NyJ+ DM+:2DQmmwH?Hs6oUcU=8;1B3@0J_K]KL%$ +###53546632&&#"3|cc/W;+H&)Np(FM  m &"-+,+8DKPX@(<JG@(<JGYKPX@(gg _K _L@,ggK _K _LY@:9-,@>9D:D31,8-8%+%* +%&&'#"&&54632655447##"&546632373"326554&267&&#"+7J W43U4UK1PN1am2_Ae9 ~:657974^"1;'-&lO!' 8/5@&$ ! (#{JxFPGpO7EUFCCM3E@B *'J~~ghKL"%*"&" +#"&&5466325332654&'&&54632#"'#&#"v (+@    'D: $  @20:%  <1@I{'" 3c@`& ' 2J g g  gK_K L331/-,+)%##2#2" +3&#"#663225&#"#66322533273#"'3273#"' $ IH4  $ IH4  " IF3  " IF3 4JLO4JL5JMO5JM @ JKL +!#5&&546753'54&'66'5DD55CC5^>=__=>^2211N,1:@/+( , JIKPX@1   ~] K_K  ] L@1   ~K_K]K  _ LY@!32652:3:%#!  11 +"&546754#"#4#"#33663236632&&''255";PT@R;2R>/rW/JvTO?v]W"F*&')*&_irJYTZ2 N,#,@!JKPX@!~_K_L@)~K_KK_LY@%$('$,%, ## +"&546754&#"#336632&&''255";PSA*.D2r[3Xj*Oe" <=A>J;;]W"F*&_irJYTZ2 N,$/@ +"$JKPX@(g_K^K_L@,gK_K^K_LY@ #*"#$$ +&'#"&546324#"#336632%3267&#"9F/DWL5 !XD2r [2Xj )!"@==< Ok]W"F*&_ix'!2?k% ,)7@JHKPX@ L@ tY! +#.'566554&'7326654&'&&K &2N)1K, -,+& , #B(+OR. %) :%.J$,"+6F@J0/&%$ HGKPX@ L@ tY42 +7#&&''7&'566554&'77&&'&&4&'3266K <%H&2N)/#,%% -_W t)* \n#B(+OR. C9%)$, ::>;"*?@< &%" JHG_K_L-*%% +77&#"566327#"&''7&&'%4'326 /H##H5#9J!${ 8!K!"l;5  (5)$fA 1317#e'S-,G"@ "3+-G(08?FKPX@$ 6, 7+ '%$"JH#GKPX@$ 6, 7+ '%$"JH#G@$ 6, 7+ '%$"JH#GYYKPX@$  e_K  _ LKPX@/  e_K _ K _ L@9  e_K_K _ K _ LYY@#:921=<9?:?1828/-! (( +"&55!&&#"5663266327#"''7&'7&#"2654'267#)tlJ@7Z/*[A>j%#b:91M&,}6/NF ;5q;5 .<5 wH@Gs%'&&-0-$nH&-)L5#SQQ1"8;1B-, '.5$KPX@  JKPX@   J@   JYYKPX@'   e _K _ LKPX@8  e  e _K_ K _ L@B  e  e_K _K_ K _ LYY@+0/)("!32/505,+(.).%$!'"'    +"&55!&&#"566326632#"&'"3&&267#267#)tlJ@7Z/*[A>j%#b:NwD}7b"F1450255.<5 wH@Gs%'&&B[&&L97799::98;1B-,%1H@E  #J_K _L'&-+&1'1"  %% +"&&546632&#"327&54632#"'%2654&#"Z@W =<21 ,*)'JH(G_K _L877>8>53&$ .. +"&&546632&#"327&546327#"''7&'7&#"2654'FaK'"'NI" @J HKL +#33667I+;q7% >="\2 N, uKPX@  J@ JYKPX@_KL@K_KLY@    +2&&#"&&#"#33663266O  6>(%6%qT7  R,:2"\*<b/7,"+KPX@ J@ JYKPX@~_K`L@#~K_K`LY@*($#"" +2&&#"&&'#"&54673366"325   &F+)OO;PT@qT" ,<4$JYT<=A>-\*(%6%)OO;PT@qT7  P-" ,:2$JYT<=A>-\*<b15v2,?@ JHK PX@_KL@gLY2! +3267#"#"&'b^/\.  1_/,  ,&m@ J HK PX@g_K_L@gg_LY@%#  +"&55"#"&'532676632'4&#"326ep  1_/b^/\.(4L)X" "% {p p  ,J+Jd!#$!.@+J_K_L%%%# +%#"&'532654632&&#" K@/XU, 2R1r [S rKC"ZJKPX@K_K`L@K]K`LY@ #$ +#'##"&5332655Cr[3Xj*.D2aF*&_id;;]W@"y JKPX@)  eK_K  `L@'  eK]K  `LY@$ +3533533##'##"&55#267#HΕKKr[3XjH565DcF*&_i#t;9tK- .$ IKPX@$  e] K`  LKPX@(  eK_ K`  L@,  eK_ KK` LYY@! ,+)'#"!  . .  +2#"&546"&5332655332653#'##"&'#!00!"//]_R;2R>/rW/1l,I,Jd!#$!V)1KPX@, %$JK"PX@, $%J@, $%JYYKPX@%_K_K`LK"PX@,~_K_K`L@0~K_K_K`LYY@10%$%' +5332677'&&#"56632733267#"&''#J J% ,/:&'\$ +BK*E&=4) %w >2u"tFI(9"#o@  JKPX@hK_L@hKK_LY@"   +"&''#3736632'4&#"326AN9sjk2"-B$K ;-^ '&@&AW=" #@ JKKL +373#'#Ⱥjki b"#C@@JhK_KL" %$ +3736632#"&''4&#"326m5jk2"-B$KIAO1 '&@&AW;-Qb{~="d@  JKPX@pKK^L@~KK^LY@# +53327373#'#J Ϻjki+?5)Y$:"KC"!<@9J~K`K`L#($" +!#"&'53255467##"&53326553C:c/dqsQ2Xj*.D2u{w*| %+&_id;;]W@-+,!3zhFK)&f 3+3hFN)&f 3+3hFO)&f 3+3NFP)&f 3+3zFQ)&f 3+3~FS)&f 3+*FV)&f 3+-FW)&f 3+tmGc)&f 3+G)&f 3+`mG)&f 3+3zG?)&f 3+.G )&f 3+G8)&f 3+ mG )&f 3+]l 0+56654&&546jHNII134LI023M5( @_-K_.L$$%#+#"&54663232654&#"(1m\x0m\z*98,,89*esX­sX®z{z{z|| n !@ J&K'L +3467'73!RI|U  B[6!3@0  J_-K]'L(&(+357>54&#"'>32!#6B/()N+RE[@Fe7/Y?\7i8K=#+*&#a/3W7;b`:V&+?@<&% Jg_-K_.L%%!%$*+#"'532654&&##5326654&#"'6632YAUZ=dtZ.d,QAKC67BE/73LF*qNn*JX TF>c9'83*t+&+$h(Y+ 7@4Je&K'L   +!5!533#5467#B9VVi?u "/"'2D@A  Jg]&K_.L +2#"&'532654#"'!#66-Af;8c%%h.CG<< (2`Gt7:l  l5-,>@;  Jg_-K_.L &$, ,$&$5+4>32&&#"36632#"&&2654&#"5-Q}Y8-Ya(K<^npIvF,801"11/=ykS/y8eB#0vltCU=@4<-!@)  %@"J]&K'L+3!5!_ K_0%'55@20J_-K_.L)((5)5#! +"&5467&&5466326654&#"2654&&''+xP90C@i<=h?I7&E+?qJ"41&%14!88 / .:7 fYI[U@8M&&M9AR5H/;X0-'"''"%.0()!:)'2-%,>@;  Jg_-K_.L &$, ,$&$5+#"&'532667##"&54632%"326654&&%-Q}Y8,Ya(ED\mpIvF,801"11=ykS/y8eB#0vltCU=@4<-!@)/4- -@*_/K_.L     +"&54632'2654&#"0}~~8247743 xQSVMNURRm- 1 JK,PX@ (K'L@ ]'LY+!#467'73m VH!B  @_--@*  J_/K]'L&%'+!!57>54#"'6632!2=W+P&I5zRdsKMRpr!-%C"e.-YPE[/2Z 8(i@#"  JK)PX@gc_/L@ ggW_OY@ %$!$%(+#"&'532654&##532654&#"'6632RK:^6-b+MITX67MT:,)L)?3xGkyC]dp333/s22''e%$ZX4- z@ JK#PX@]'K](LK,PX@e](L@Ue]MYY@  +%##5!533#5467#4Y=Y !oaVr%L/3Z"A@>  Jgc](L +2#"&'532654&#"'!!66+h};e%,Y3AM@DC: -kgo~8:09 j+ $>@; Jg_-K_.L$$$$#"+462&#"3632#"&2654&#&+-3+9gl5fakr|.51/1// v puXxks1>@7:.%?'"d"%@"J](L+!5!p@~Z#'36@31"J_-K_.L,*''+2#"&5467&&5466"6654&32654&'>g?I7&E+?qJxP90C@i;%14#"4276886?/9&M9AR5H/;X0fYI[U@8M&n'"%.-'"'Y'20(#3$:#\-';@8 Jgc_/L!''$%%"+%#"&'53267##"&54632%"326654&&1-mxM;_jr{/51031ϻxgs.*wkq/@=5<.$?&$ (@%J_-K_.L'&%#+#"&5466327&#"4'3261n[x0n[z<9* *%8,esX­sX®K|z33z#- &- "!- Z 8 X.- 3Z" +  "d" & \- g=` `3+-{` `3+Wt` `3+Su` `3+r*` `3+)]+` `3+f>` `3+c,` `3+e-` `3+b?` `3+g=~ ~3+-{~ ~3+Wt~ ~3+Su~ ~3+r*~ ~3+)]+~ ~3+f>~ ~3+c,~ ~3+e-~ ~3+b?~ ~3+-& mq& ' -v& 'm & ' R_& 'r & ' I-g& 'm )y& ' -& 'm  -r& 'm -g& 'm'  tg4= ް3+-~*{ ް3+~W4t ް3+vS3u ް3+~r-* ް3+)v]*+ ް3+vf3> ް3+~c*, ް3+te4- ް3+vb3? ް3+kS' ' G QGG3+G3+5,8" %@"a]L    +%"&&54663!'3#Ad77dA'QQ,.n__n. b2W,Z" $@!a]L ! +7!2#'3#W'Bc77cBQQ,.n__n.b2Fb2 &@#ea]L +#3#3#32mmmmhggb ,@)ea]L  +53#53#53mmmmggg|Q!@JL +3#'1OchA"@J]L +3#'0NOcgBM @GW_O$" +'632&&#"@,b_-&HJBPEEM&  ><A @ JKL +#5'3&,v @JKL +5'37'#Xv,%0 #/;K#PX@. g e     g]L@3U g e     g]MY@.10%$ 750;1;+)$/%/##    +!5!3!!"&54632!"&54632"&54632!"&54632rMpN$22$#22$22$#22$22$#22$22$#22!NpMp*',.%%.,'',.%%.,'',.%%.,'',.%%.,'EOKPX@gK_KL@ggKLY@ $$$# +3#4632#"&4632#"&SUU:(''(f(''(6''&&((''3*  .@+  Jc_L$+$" +4632#"&5%%54632#"&####n########NN2####%1=L@I~~c_K _L32972=3=&$  11 +"&&5463232654&'&&54632#"&'&&#"7"&546322@  "' I@0=  % 9#  $.85?"   07;(49 0+%&'&&77Ew33!"#|)N)S!33wE")N)|# - 5@KOSWe@ JH[ZGK PX@lpn  ~  ne    feee]K]LKPX@mpn  ~  |e    feee]K]LKPX@n~n  ~  |e    feee]K]LK#PX@l~n  ~  |ee    feee]L@m~|  ~  |ee    feee]LYYYY@ITTPPLLdbTWTWVUPSPSRQLOLONMJIEC?=984321-+&%$"* +47#"&535!5!4632!!#"&547!5!&73654&#"32654'#5!5!535654&&5432w:0!W$$E$$7 *  + ¶(w:0!R% '#//////E$$ / %% /  q  //////R% '#9&"90 #/;K#PX@!g_K _ LK2PX@g  c_L@$gg W _  OYY@:8$$$$$$$$" +4632#"&4632#"&%4632#"&%4632#"&4632#"&_-('//'(--('//'(-L-('//'(--('//'(--('//'(-)%%)''')%%)'''')%%)'''')%%)'''(&&('''90 #/;K#PX@!g_K_ LK2PX@g c_L@%ggW_ OYY@:8$$$$$$$$" +4632#"&%4632#"&4632#"&4632#"&%4632#"&9-('//'(-L-('//'(--('//'(--('//'(-L-('//'(-'''')%%)'''')%%(&&('''(&&(''''(&&('''5@2 Je]L +%#''5'7#53'75373i=hUi;hi1 0$ ¼ J# IKPX@=nYXL?>1 0$ ¼ J# IkHKPX@1 0$ ¼ Jk# IKPX@1 0$ ¼ Jk# IK"PX@1 0$ ¼ Jk# IK.PX@1 0$ ¼ Jk# I@?n YX L ?>1 0 $¼ Jk# IYYYYYYK PX@W ~  ~  ||||| g KKLK PX@W ~  ~  ||||| g KKLKPX@W ~  ~  ||||| g KKLKPX@[ ~  ~  ||||| g KKLKPX@_ ~  ~  ||||| gK KKLKPX@e   ~  |  ~  ||||| gK KKLKPX@k   ~  |  |  ~  |||||gK KKLK"PX@k    ~  |  |  ~  |||||g KKLK)PX@w   ~  |  |  |~  |  |||||g KKLK.PX@y   ~  |  | |  |~  |  |||||g KL@   ~  |  | |  |~  |  ||||||g KLYYYYYYYYYY@"~|zyrp(+++-. +7"&'667.546327.546327.76327.546327&&546327&&546327&&54632>32663226632266322663226632>326632"' #                &  %/#) %' +F -, !11'% %+!60 )$, ">:  5/!)%    )%  / &*  " 8 1 +  #   %    #   $      f@U]M +!5Z@@'""&"3+9(3+#'@$]K_L$# +##5!#4632#"&GKoGG#@]L +##5!#GoGG<O)@&a]L +!35<{O{42  #/;@ JK'PX@#  g_K_ L@   g c_LY@#10%$750;1;+)$/%/##  +"&54632'7"&54632!"&54632"&54632)""))##IJKI.. ..3 -- ..)""))##<. -- .JKIJ")($$()"")($$()". .. .@GU]M +%!5!#@@ILGx@G"@9,G@x|GK)PX@]K_L@g]LY$# +#!#4632#"&GVGx|@]L +#!#GGw}2@/HW_O  +'7'"&546326' m32,4I @`9(grpb#+5I @`9)gqob/ 3H& 3H&9  #wK#PX@g_K_LK2PX@gc_L@ ggW_OYY@ $$$$$" +4632#"&4632#"&4632#"&_-('//'(--('//'(-&-('//'(-)%%)''')%%)'''(&&('''+ } #>@;JGgWg_O$$$$$" +4632#"&&&#"56323267#"&())(3%3<2K;/%4=2K;3)!!)'## "q5  "q5 +D#>@;JHggW_O$$$$$" +&&#"56323267#"&4632#"& %3<2K;/%4=2K;b())(- "q5  "q5 )!!)'##+ &aOm m3++"@]KL +!#!5!kk:*(@% ( Jc_L%*%' +467&54632&&#"#"&&732654&':2"IiY0Z&'"G!M/4OW'Q?=^6r54 0E,<.FBKW5%J6(F,(F>#4,"9 #NK'PX@g_K_L@gc_LY@ $$$$$" +4632#"&4632#"&4632#"&92$#22#$2-('//'(-2$#22#$2.%%.,''(&&('''.%%.,''=t 3+5  3+9 3+9'  3+9'9 @_K_L$$$" +4632#"&4632#"&92$#22#$22$#22#$2.%%.,''.%%.,''90 #eK#PX@_K_LK2PX@c_L@gW_OYY@ $$$$$" +4632#"&%4632#"&4632#"&9-('//'(-L-('//'(--('//'(-'''')%%)'''')%%''''(&&M @ H_L$" +3267#"'@&JH&-_bPEEP7m/0+776uo  0+''7'77nAB/AD*DA/?BOCA/BF*F@/AC9 #/;GK"PX@? g g _K_K _K _ LK#PX@< g g  c _K_K _LK)PX@: g g  g  c _K_LK2PX@8 gg g  g  c _L@> g gg g  g W _ OYYYY@3=<10%$ CA54.54>54.54>54.54>7{$$)?C8$$$$$$$$$$$)?C7$$$$$$$$$ + +8!A."'..'$* + +8!A-#'.*$$*-"7BHf@c21" FE;  '& J  h g _K]L:8$#?<8B:@+)#7$7 +3253&'67##&&546672675#"&&5&675"#327&&5=O('O<70"!5-0O&)O~E{P>Bi)/`&Hl>QE\e}!E"&-09      WwC   'YKNhn\iwn\<=tG@C AdD@6 JHeU]M+D'#7'373#73'7#'#3DEEDCFFC-\..\--]..]uxyuuyx&NPQNNQPz*/0+7'&&'6'6&')N)"Ew33!"#*#"!33wE")N)9 y%6c Y Y3+6c Y Y3+Pb0$@!U^N +33PVHb@U^N +3#53VLHP0@]L +#3#VH@]L +#53lHL(b:'  b0&  "@eU]M1613 +3!!".54>3!!")EX/4hT32Ug5/XE)b,/0B<54.#!5!2#/XE))EX/5gU23Th40/,,/0B<554675&&554&&'523G%=!XQ$1..1$RW!;'!%f/<gp/,.2mg;.j&1xU>K!PX@a]LL@eU]MY +3#3#1OOU^^xU>K!PX@a]LL@eU]MY +#53#53gOO^#^%G@]LK_QL$# +3#4632#"&%{/ !..! /G(!!(&!!%G'  %z "@gU]M$" +#"&54632#30! .. "/{))&""~"!0+'57!mmhn6 " 0+77'7mmhX7  U -K!PX@ ]LL@U]MY +74673#&&9>v8447u>9eIMa_KFU -K!PX@ ]LL@U]MY +%#6654&'38?u8348v?8cFK_aMJO'2@/  J~_PK_QL$#%( +74676654&#"'6632#4632#"&s!,+#G'4-c;Zb-3{ .#..#.&<"dOB1F! (!!(&!!q &5@2J~gW`P$*$" +#"&54632327#"&5467665535." .. ".,,#=P4,d;Zc-3$s(!!(&""&8#)eOB0F!i &@#W_O ""+76632#&&#"zO\uDS=6TUIIU!*&%c ]!!KPX@J@ JYKPX@'_ K]K` L@$]K ]  K`LY@!!%# +3#577337733#3267#"&&5#^GR+_z+_$.G*1M-q?2ststp o  NGN373@0J~]KL +35466753#'.'#53Nakk,1[UBnK#PX@%~]KK^L@'~|]K^LY@#! +3!2#4&##332653#!U _o1HGKGH3s^BvP YGWGY>PvBFOWbm@j()  ]USJ EC9 J    g_K ]LQPa_\YPWQWNLFFBA@<;:-+&$$$& +5&&5463236632366326654&'.54632&&#"#5"#"'#5&''4&#"7"5432754#"t-3$%8 .A +-/SK:5"p8d811N)+.DC7M*XSI ("H1,x  ,1H(N{9&,%'.3-(1;" !2F1`kv( &, 8L8LhMDFM `   ** ,9O29)/ %@"e]KL +!!##5!#Y~~~ *4DU@RDCBA*$%Jg_K _K_L><##$%($"%% +7'7&55#"&&54632!2#"&'''3267#"'4&#"3332654#!77.^ %.G(NF@P mpB4&NPU\O@(@%E ##C:!yAEi3F7CO(G-CK^Kf\FLCTB6. ") <$TQ[D;7H/)A@>  Je_K]L&"" +73&&54675363253&'#5&#"#533!7B[vkCC(*(*CCK?}^^~'isofgp  2sKzH~  ʰ3+#YX @ J Ht+&&'#467&&'7>737RkcLI)NF+bc,%(mM=0MI`pQ.CH%^i8+c`%aC:d)  ʰ3+ E+@(eU]M!#+35!54&##5323 *DO]l.MXSOV6u]X {KPX@_K`LKPX@_KK`L@ K_KK`LYY@   $" +4632#"&%#4632#"&2$"33"$2u/62$#22#$2.%%.,''s6|.%%.,''E&@#U]M+!!5!#"MXX#D| ` `3+#cD/@,eU]M +5!5!#!!DDuDDaP@ HGt+5>554&''5#'2C'T3 ;f'%i,)4$}!]) J@s+ʮ00#DB ` `3+#D@]L+5!#!DD ^ #/;CKSKPX@8   h g_K _ LKPX@<   h gK_K _ L@@   h gK_KK _ LYY@KMLED=<10%$ QOLSMSIGDKEKA?P* ʰ3+"/5 ʰ3+p<p ʰ3+)4@ ݰ3+'@@1 =2 > J~~  ~ gg g g  W _ O)(;964/-(@)@%# +2#'#"&5467754#"'66#3265"&54632&&#"3267GEB,J5AZQ0>7 Ntu0,%,PWaO%6(M(%/0H=6<58:11B6ʷ)T\^U H h31P  'M@A B4. - J~~  ~ gg g g  W _ OFD?=20,*%# +2#'#"&5467754#"'66#3265#"'532654&&'&&54632&&#"GEB,J5AZQ0>7 Ntu0,%,JLD2F !4.K@ ?:-/4H=6<58:11B6ʷ)b38R   0+/0 H   *-H0x@u    J ~   ~  ~g g  W _ O00-+)(%# +"&54632&&#"3267##'##"&55332655PWaO%6(M(%/0tu(K O5MZ&K PX@!     JK PX@!     J@!     JYYK PX@-   e U e ] MK PX@1   e U e ] M@-   e U e ] MYY@ &% +!'##3!#'#373#!#3#%&&'3 GCDEL i3pZ||J&#K'm&r)zHL)6@3J_K_L%#))' +7'6632'676654&'"&5463251rDhs4'  #&#,$22$#22\k"dM)<4 /%',.%%.,'}%2o@l J   ~ |   e  g  W _ O'&-+&2'2! %%+"&'#####53533533#36632'2654&#"G[p<ԣam^>c9'1,4j}}a,v`r@5;6 ~}vWI Z J  ~  ~   ~  ~  ggg  h  gW_Ous|wsuqpjhQPB@:7/-*)'% ``+"&&5467&&546677.#"327#"&5466323266326322&&54632#>54'6654.#"227'"&#"767&&'72654&'"#"&',20H*A! "$    #(     " 2C+G2  $ !"e= 2 -$(% +b    yt$![040 )& $:!Y5)) )  )  /GU'BP(4$**%2 7X!:$)A:%)> 2_ " 4+ V92U+4ZJ@G J~gW]M! +33273#'#7'#32654&##ZВL9##jl*'mJ^1KAFI.jl@f49??q213+aG@D~ ggU] M +!##3532!6654&#_ZZ5|l$c][en2UU`i8c?Pc?FAAZon$'e@b"! &JH~gU] M%%%'%' +27'7#'#7##30237&4'667*(# %=#Ҩ^E"s.1-n$/4EIl+1I3q|^) ,{{#]g$9K PX@40(JK PX@40(J@40(JYYK PX@gW]  MK PX@-~ ~gW_ O@gW]  MYY@%%%9%932,+*)'&$$ +"&'532654&'&&54632&&#"733#5467###!><)(8/9?;-6!/4%J}^^a[@e5`g = /'&9 5(.#41`+*"GUcqR@OhZLC73($   J    g W _Opnb`TRGE?=%&&&!$$$ +#"&'#"&'#"&'##5327.5432327.5432327.5463233>54&#">54&#">54&#"7U !V67U !W66V !S4B/$IE%0.FD- #%0.GE+$EI&07#  %% M  !%%Z  $$ C"SW'ps:3"SW':3"SW'sp:3  KHHK LNNL KHHK LNNL KHHK LNNjT@Q ee U ]  M +#5!#33#3#3333ve fņ||3?j*66`6Y4g6`6no)@&JHt+'7>73#'#3m BEcfDC]7:A; 6>;!,O]C@@TH9JH~ggW_O\Z,))$'-+6676632#"&5463232>54.#"#"&&5467&&54>4&&'326*2 (Y)R'?S)$FhES\*&*@(3B(.'.j40Z%50C61O.$!)=@"$7&6'OaA46\#F*^E3.& XN(@%JU]'L+!#533BnB-PN )@&JU]'L +!'#5353BĪBPN )@&JU]'L +!5#533BBKPN )@&JU]'L +!#5353Bb BP1$@!JH]'L+373?C P@JH]'L+373#=BB P%@"J^'L+3733?sBnP  @ J]'L+3753#>BB?P @ J]'L+3773#5>BBzP @ J]'L+3753#>BB0P@"1%@"JH]'L+3'753,CĻ1P&#@ J]'L+!'773-BU/P"$@!JH]'L+!'73,nBƸ2P( LJKPX@](K]'L@e]'LY@ +!#'73530Bɳ/P& %@"JH]'L +!5'73(kBsK4P&"@J]'L+!73.:B^.2P1%@"JH]'L+3573hBiSP '@$JH]'L +!753>B},WP -@*JHe]'L +!5#733;B7P '@$JH]'L +!773ԠBBPz  '@$JH]'L +!5'3>BrwP @ JH]'L+3'3#'X>ԠBBPN.@+J]'K]'L+3533#NBBBnP-N /@,J]'K]'L +35353#NBBBfPvN /@,J]'K]'L +35373#5NBBBPN /@,J]'K]'L +35353#NBBBqP1@J]'L+3'53#NנBBhPSi@J]'L+3'3#N<ܠBB xP %@"J](K]'L+3'353##N;զBBwP @ J]'L+3'3#5P>BBPa- @ J]'L+3'3#'N<٣BB$P1"1@J]'L+3'73#5N,CC1P"@J]'L+3'73#N,éBBn2P&#@ J]'L+!''73-ȠB/UP& @J]'L+3'753#N(ȠBBk4KsP( *@'Je]'L +!5#'7330B/P&"@J]'L+!'3.hB2.^P1@J]'L+3'3#N8CC#i$P #@J]'L+3'3#]:kBB!P&@J]'L+3'3#N4ҢBB&i/P  @J]'L+3'753#P:˭BBy"]Py @ J]'L+3'3#5'N4ҢBB%iP' #@ Je]'L+3'33##_8BB#e(PF%1!@J]'L+3'73.CS3P'$@!Je'L+!#'73/n0P.&@#JH]'L+!'73'ɗB7q2*P% '@$JH]'L +!''753-ɠB2P% '@$JH]'L +!5'73)ɠBrx3sP% '@$JH]'L +!'73*ɠB93P1!@J]'L+373>CvGP@J]'L+373#=BBuCP%@"J^'L+3733>mBvnP @ J]'L+3753#=BBu9?P @ J]'L+3773#5>BBvzP @ J]'L+3753#>BBv!0P@N+@(J](K]'L+!#5373}æBTnBPN+@(J](K]'L+!#533vBBPN ,@)J](K]'L +!5#533BOBPN ,@)J](K]'L +!'#533B-B#P1"@J]'L+3573:CP#@ J]'L+!739B|#P!@J]'L+!'73:mBxcSP $@!J]'L +!7539B"@XP *@'Je]'L +!5#733:BP $@!J]'L +!773:B2P%1"@J]'L+3'73.C+1&P'#@ J]'L+!'73/B/0$P%!@J]'L+!'73-jB21 P% $@!J]'L +!'7753/B͠/P%"@J]'L+!573.;Bi1P" *@'Je]'L +!#'733Ȥ0BF-(P"1!@J]'L+3'3K|  $JR~ h v. 'dD@W_O   +D"&54632"//"!00 )**) Y=j а3+~Oa 3+ozcPu*2 ְ3+N&IdDK PX@oU]M@U]MY +D!#5##dBB0nnCO;dD@0JW_O!!"" +D#"'#"&53326533265>0661<6708=;9##::B "B "-9K ۰3+,8J ڰ3+@(M ʰ3+@( ʰ3+61Q ԰3+[ML3 3+b dD@U]M +D!5!,ԞD135fQ q q3+0I 3+rI&dD@U]M +D%5!ggQ0+'%+4X0dD@t +D#`A2= *dD@gW_O +D2#52654&#<6CC6<550661<6708;9##::B "B ";f 0+77''7f*<;+<<+;<*;+<<+;<*;;*<@@b*dD@gW_O +D463"#52654&&?E:D;#/2$-#03&,02 ڰ3++^EC^v&PIdDK PX@oU]M@U]MY +D!#5!#BBnn13y* ְ3+]FdDK PX@oU]M@U]MY +D3#5#]Bx>\\>>]%dD@Ht +D5#7# ;\\;x__xe '/7?GKOW_gow$dDK PX@/-"&,-p6 e754 U  9 8  g;:g=<gA@g?>e!C B#g%#E$D"-#"g+)'H*G(F&,'&g20,..,U20,,.^K3J1I.,.NKPX@/-"&"-&~6 e754 U  9 8  g;:g=<gA@g?>e!C B#g%#E$D"-#"g+)'H*G(F&,'&g20,..,U20,,.^K3J1I.,.NKPX@6 e 754 e  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g/-+H*,+*g20,..,U20,,.^K3J1I.,.NKPX@54  ~6 e 7 g  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g/-+H*,+*g20,..,U20,,.^K3J1I.,.N@54  ~/-+&+-&~6 e 7 g  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g+H*,+*g20,..,U20,,.^K3J1I.,.NYYYY@ɤyxqpiha`YXQPLLHHA@9810)(!  }{xyuspwqwmkhoioec`gag][X_Y_USPWQWLOLONMHKHKJIEC@GAG=;8?9?530717-+(/)/%# '!'     L +D53#!5#53%53"5432"5432#"5432"5432!"5432"5432!"543253!53%"5432!"5432"5432!"5432"5432!"5432"5432#"5432"54325353!533353f_gLO;NQ6_5>y|b;g566fz.6ff6f66p.F3VF.p6gg666NP1dD@& JU]M +D#5667&&'5I6886 ., D 3  2IG1  3+O^U BdD@7~gW_O "" +D6632#&&#""&54632[SVUI8+):g$$""I\ZK*),'R!_ 0+77''7_*31/12*31/1K*21/13*21/1P0dD@% JU]M +D.'5>73E6886 ., D 3  2P1dD@& JU]M +D#5667&&'5I6886 .,( D 3  2H%}dDKPX@ ! J@ ! JYKPX@U]M@~U]MY@%$ +D#5667&&'5>73#&&'#6886 ., D 3   3( D 3  2@BB@ B@IG1  3+dfN<j#dD@  Gt +D37''7'7#F >F3883F>&F"B 0*@@*0 B"@+JdD@? JgW_O*($"  +D2632#"'#"&54632654&#"4&#"326S891<<1871<<  5  ''7228((8227iT_\dDKPX@noU^N@U^NY@  +D#7#73_"3{"3xCxCu 1dD@&W_O    +D"&'332673(d&pe,d2ea,712h^uK ; ;3+g, dD@U]M +D!5!,Xg=y,  3+u_4dD@)Wg_O#""" +D#"&&#"#6632327dESK$3 IdDAphi:MMKNK5ut 2dD@'W_O    +D2#&&#"#66(d&pe+d2tea,712h^.dD@#JHGU]M +D!55!w]DffD"H0+'77Br*r*=OOA=OOdL*dD@JGU]M +D#'73x*AOMNP0dD@% JU]M +D.'5>73E6886 .,N D 3  2H%~dDKPX@ ! J@ ! JYKPX@U]M@~U]MY@%$ +D#5667&&'5#.'5366736886 .,{ D 3   3( D 3  2@BB@ B@h 'LHMa H3+ 3+^K@U]M+!!jg^KL^L m6 !dD@  JKPX@"UWg_O@#Wge_OY@ !!    +D"&5463256673"&54632##$$0""$$ !" "! 6 V !N' !" "! 4m6 !dD@ JKPX@#UWg_O@$Wge_OY@ !!    +D2#"&546#&&'52#"&546$$##HC04$$""6 "! !"  V 'N! - "! !" ]^)dD@ Jt +D#.'#5>7YR^Z$c1po/KLc%\^S((% + $)'"H0+'77'I*r*rHOO=AOO=dT*dD@JHU]M +D#'73*xTMOApdD@JHKPX@ pWg_O@!~Wg_OY@ #+#" +D#363232654&'7#".#"#V2"2(&!'6>='>5-$(W` : @+5D.lT*dD@JHU]M +D37#x*AOM.2dD@'We_O"" +D5!326673#"&'>5#)S[N2Mg2" $$Si-(lL*dD@JGU]M +D3'#*xMOAz@,dD@!JGW_O%" +D4&#"56632'66 $8B@.A"&W>2,L 6,2'8dD@-J~W_O$. +D"546654&&546654#"'6632,W !3     ?P6dD@+Wg_O  +D"#".55332>333RJ%0)Y(4K65-_]^'J5J]<E #/UdD@Jg W  g _O%$ +)$/%/##    +D"&54632%"&54632!"&54632%2654&#"6CC64HG(())(())]>66>>57>- !##! !##! MU",[dD@P+Jg  W  _O$#*(#,$,""  +D"'#"&546326632%27&&#"2654&#"tF/:7HF9:9#8E 8%"$$# ME$G<7K%$H;#<%M9!""!89DuT2dD@' JU]M +D667#&'55",I##I,T 52>>25 Xm #/dD@$gW_O$$$$$" +D4632#"&4632#"&74632#"&E())(c())(())([##! " "! !" "!   0+''7'77''7'77#AB/AD*DA/?BEAB/AD*DA/?BOCA/BF*F@/AC*CA/BF*F@/AC CCdD@8 gW_ OCC@>:8$$#$$$$$ +D>3232>3232>32#.#"#".#"#".#" !8+ + *$$* + +8!A-#'..'#-C?)$$$$$$$$)?C7$$$$$$$$$$7K &dD@ JU_O +D"&553KMIaJ;pd "V ,dD@! JU_O +D"&553"&553MIaMIaJ;pd "IJ;pd "a(dD@t +D73#NN(a( dD@Jt% +D73#"&5467NS )(!(! ! -cC %dD@U]M +D4673#&&%#6654'3E#EE#E :1@:8 8:@1:cJC &3/dD@$U]M +D4673#&&%#6654'34673#&&%#6654'3E#EE#EE#EE#E :1@:8 8:@1: :1@:8 8:@1:- 3+hJG}xl l3+hGE|l l3+ G~ul l3+h{JG)l l3+luGIl l3+llG l l3+x(JgRlG l3+3+ JGS|l l3+ \GPl l3+l({FgXvlG l3+3+NlFGZIl l3+hpGl l3+hGl l3+h{Gvl l3+ip  3+a4p'U(dD@W_O"C +D#.#"#6632UU1JU*EqHSbX KZ H] /adD@V+J*IHGggWg_O$$$$$$$" +D&&#"56323267#"&&&#"56323267#"& "("2( $(#1' "(!3( $(#1'] L$ L#v L$ K$hG~l l3+lFG|l l3+lkFGl l3+{FGul l3+FlFGFl l3+hvJGl l3+lFGl l3+yhxFGyl l3+olFGLl l3+l|FGul l3+hJGl l3+lyFGwl l3+ JG|l l3+hoJGl l3+l|FGl l3+lFGl l3+FGol l3+lzFGvl l3+SlFG0l l3+OFG,l l3+hJGcl l3+"hFCdD@8JeW_O +D"&5463!##5#"3267k77.53c+* (,' 37|Q 3@0JU]M    +5>7335>73#" &21s$! &21| 46 (,' 46 (,'by!@ Jt +#&&'#5>7+-c8:c-+64 *+ 46by!@ Jt +#.'53667-++-c8: 4664 *+rt&@#W_O#" +#"&'332667jSVdS/0"I\ZK bJ "@gW_O$$$" +#"&546324&#"326H46CC64HM7>>66>>6e{,@)Wg_O"""" +#"&&#"#66323267L322IK440MJNI@U]M +!!Kg$@!JHW_O%" +3267#"&5467l $8B@.A"&oW>2,L 6Zq6]Q*T' Ұ3+"mFT}+0dDKPX@noU^NKPX@nU^NKPX@noU^N@U^NYYY@ !#"+D#"&5433632#50^#9.#6]6dD@+Wg_O +D2#&&#"##532660)Y(4K63RJ5-_S8&dD@JW_O"+D4632.*%#&)D*#,  * 4S7dD@Jt++D56654.54632*E('"&)!4 *  ,?6dD@+Wg_O +D233#".#"#54>9%JR36K4(Y)0_-50#dDKPX@oU]MKPX@U]MKPX@oU]M@U]MYYY@ $4!+D#"&5463!2#"'00^.##.hqJGl l3+lFGhl l3+lUGl l3+ FGl l3+jlFGjl l3+nlFGKl l3+lFGl l3+LlFGQCl l3+BQdDKPX@nW`P@W`PY@ "#+D#"&533267$TGiR^*0)1#4<7# xk'edDJKPX@pg]M@~g]MY@ "#+D#7#"&55332774Y "()Z 'D+/) 0|?dD@4 J~U]M+D#5&&5467576654&,[a^^W[c]a)0.+W*.0^IH__HI_6'(55('5yhJGUVl l3+[0 FKPX@oU]M@U]MY@ #"+#"&543!<5^#9I[^ FKPX@nU^N@U^NY@ !+53632#0^I.#1D/;ZE/;E/;/;/;l/;Z/;;qFE3+E3+3+װ3+3+3+3+q3+L6>@>|3+|3+ p #'+/dD@ee  e     eeU]M  /.-,+*)('&%$ # #"!  +D3##5#53535!!5!5!!5!5!!5!3#3#3#!~~G~~GnC[CC[CC[C"HHHHHH H}}HHHHHHHHHHHHHnCChvJG/l l3+lkFGll l3+lFG]l l3+lFGAhl l3+lFGMl l3+hpKGDl l3+OhKG<l l3+FhKGG3l l3+VhKGICl l3+ oJGl l3+hoJGFl l3+hvGG|l l3+h|JGHl l3+hGl l3+^Y&MM vJGJ|l l3+l|GKvl l3+l.GLl l3+hGll l3+lGN~l l3+l+GOl l3+lkG@l l3+PlJGP-l l3+l|JGQvl l3+hJGRl l3+]GU 3+lgJGUl l3+lFGZl l3+hiJGVl l3+h]uGWl l3+h{FGXvl l3+lFGYl l3+lFG[l l3+ljFG]l l3+G9)&f 3+#S >JKPX@ ]L@U]MY@  +#5667S(W= !M( V Zj ʰ3+.?=@:+*J~_K_L>(:5"p8dv( &, 8L8Um   -s (G.&,%+!&! !1F1`k->@;J]KK_L%( +35!5!3267#"&&'.#V.<' ?"( 8?U91*b}b /:;'u)E)(-^,!5@2J_K]L !! +"#53&&5466323#56654&FBK*96A?uQPu?@89,LPMBd ha!xMEg99gFMv"ahgBMO " @ JKL +!#57'53CCCCBbBBNE" 6@3gK^L    ! +!332#!3%32654##sH}xp%E+8eC"PQP^"g&<N:,"KPX@  JKPX@  J@  JYYKPX@# e _ K_LKPX@(U e _ K_L@0U eK _ KK_LYY@ ""  +2!3267#"&'##3366"3&&EqG?4W.)X?q bef,83,wH?Hstw"nnj8;1B53/F)H )3+K,(wKPX@  J@  JYKPX@]K`L@K_K`LY@'%!##%" +632#"'#"&53326532654&#"C4ALwD{H%bEgo*.D25<;55<;5" B~ZV++_id;;]WQSSQQQQR E@B  JIe]&K]*L +5!!5!#!'!6=K9UN77?w:>Z/"+4@    JK PX@/n o   g _K ]LK PX@. o   g _K ]L@-    g _K ]LYY@,,,4,3/-+)%#""!! +5#3533253#5"##32654&##32654&#ggC#C@A,).NEC /&1'/114)+7bbeehsPG=T #D8Lembb)))$5,(1("@aL +%"&&546323!6ev33* ! 76v7`;:A("#p(]0+'7'7c;R;R>Z>Z(2+@(eU]M +"&54633#"33DHIC~~W*-~bE@AC1S+)1( 2<@9eeU]M +"&54633#"335!DHIC~~W*-~bE@AC1S+)1~11(2   3+()2   3+(2(@%eU]M!#!+53254&##532#(~W*-~~DHIC1S+)1E@AC()29@6eeU]M!#!+53254&##532#5!(~W*-~~DHIC~1S+)1E@AC~11(2 ZEE3+( 2 3+NT.@+W_O +!3"&54632BP?NT1@.W_O +!3"&54632BPNT1@.W_O +!3"&54632BP NT1@.W_O +!3'"&54632BPNT-@*W_O +!3#"&54632BPNT.@+W_O +33"&54632NBP@NT1@.W_O +33"&54632NBPNT1@.W_O +33"&54632NBP NT1@.W_O +337"&54632NBPNT-@*W_O +333"&54632NBPNT 3+NT 3+NT*@'U]M+333#NBBNT 3+NT$@!U]M+333NBBɒ>_<zC{ --   X^9A<+( AS(S!<+B9<$<;<&<&<<1<#<<#< 9<+<+<+2Z}:Z0Z%Z:Z KZ5ZZ-Z:tZ:Z'.CUpCKFK<j(\*yN-y-O-y-N1H1lN1NNNk-yNy-N-K9X B9'(<+9<F<(<7<'4_@1g(<+B@1'<+{{j(N79{-g(>jM,}:0Z0Z0Z0Z -Z:::::<?:UUUUptZN\*\*\*\*\*\**-O-O-O-O-11?11k-Nk-k-k-k-k-<+k-KKKK9yN9\*\*\*}:-}:-}:-}:-Zy--0ZO-0ZO-0ZO-0ZO-0ZO-:y-:y-:y-:y-Z111 1-  bHK1ZlNlN5J1?5Z1E5Z1N5ZjN51-ZN-ZN-ZN-ZN:k-:k-:k-:-ZNZHZ,'.-'.&'.-'.'CCCUKUKUKUKUKUKX p9pCCCN<`\**:k-'.-(((((E((((AAc 9p)Y,aZ1Z0ZCZ: ZZ-Z;(:ZtZQ&Cp\-l5p-+&NNF-N8k-+&-Nj-NlNgNS-k- j;--F-PEF_5Fk-F_50Z%1Z:'. KZ%Z Z~ZZ1Z0Z.7Z7ZZZZ:ZtZ}:C \- Z56ZZZZ~Z#.Z\*n-uNNO-i+&NNkNLNNk-NyN-)9.-BN<NNRNfN$mNQO-N --1H11xxNkN9NEZNX X X p9(((    <7x0W9/. .p(p(:9@3<<(|Z< Bk2xxx/xA3{{){{M<d))1 wZN\*M+9:-uUKv0Z7ZO-NuZ NZN$o 1ZpNx+l5EF:k-]]':-D:-@::u: -a3Z"N~ftZyN/ZZN.+&ZNZkNlIZNBZNeZyN :-}:-C&p>p>$5<5<ZN iZNFZNZZN5< ZN \*\**0DO-1PO+1PO+i.+&OE7ZN7ZN:k-:k-:k-#$ 9 9 95<1ZNZRN/B~-y--- f & ZN :-++&2\*\*\*\\*\*\*\*\*\*\*\*0ZO-0ZO-0ZO-0ZO-0O0ZO-0ZO-0ZO- 19 1H:k-:k-:k-:k:k-:k-:k-9:-9:-9:-9:-9:-UKUKuUKuUKuUKuUKuUKp9p9p9-""$$$$$$Cy~ZyNUzK}#}:-^~-y-k-0;1P+%:NUZlNJ g U-N:k:-yNZ'*&Q&jCUpg COO'E'E0:B+&$yN#\'\29ZZa-ZfZbNxZ^ZN\*1:k-UKUKUKUKUK\*\**:y-:y-Zl:k-:k-OEZZa-:y-ZZ-ZN\"\*0!O0ZO-11:k*:k-FZ>U=UKu&ZZc-5u-C\*0ZO-:k-:k-:k-:k-p9&N--}:-5C- 0ZO-K1:y- p9\Ky-yNyN&-y-y-O+O++&u-1y-y-W-99KNN1Ni1NNKKNNNk-h->..-NNK]T]T-1111 K9X 99?EE-:uNu&W-N1lNy--+-E-0uNN^N33'3''7,r  == (j(j(((j(j($9$9(( (r3Cx7NNNNNNN(( ((K(K(j((((9(((((((vA-"n,w.-:k-}:-)ZN'-Rk.-1: -$tZyN}:Z6Nj}#}:}#:y-X 1 +u ;aVa"V+&1Gy2VVV;#-+G9k-k- V**Ks&;s) !1V gV V5ak: :l:l&&::o:e:::&"::x 7u1T3iZ.3~33N31~1rO 3q/'31r3q&Kyy-yyN36CiyyNy-E-lN17NNyN7-9B\*y-y-O-+&+&+1H1K+&3Nfi 1 3 3$73~3~133C11r==vzZyNZyNZyN}:-Zy-Zy-Zy-Zy-Zy-0ZO-0ZO-0ZO-0!O-0ZO-%Z:y-ZLZNZ>2ZN1 1ZlEZlNZlN5Z1G515Z15Z1ZNZN-ZN-ZN-ZN-ZN:k-:k-:k-:k-tZyNtZyNZNZJZJZ '.-'.-'.-'.-'.-CCCCUKUKUKUKUK99X X BBp9CCCNX 9\*N--------XQ8=#++&+&+&+&+&+&A<0.NNNNNNNNca NN  k-k-k-k-k-k-cr" FFFFFFFF_5_5_5_5_5_5_5_5\l--+&+&NN%Nk-k-FF_5_5--------I8NNNNNNNN_5_5_5_5_5_5_5_5 ^^-------IGAG_eDpNNNNNf[ZAAQv  AAQvFFFFj;j;FFpp+A[A[A_5_5_5_5_5wHyRAM+<('\ ,.9{{{x<Z$<$N< K <<b< :< }:<A@1 x 515t Z\*IZNZrNC:]l 9 GZN-RS(((Z(FF???(((02;?^Cwu--------FFFFFFFFFFFFZ-ZU""""^K[SS[[J[S[[**$$$qcZ]NyZkN[lZN5ZNIZNAU<\ZZU: F1Zxy';j}#ZZ5:%:t::b(9tt .-"[k9k95""Z"[u7ZQZ}-'w.:yCsZ@:0Z U5Z-Z:1N1k--+&&O+E-1N1-1Xk*11131N11KHF+NE'%V)***v*v*v*llNlN1--y y--&9X yyKN i-I-NNdNN,T].k-N61NNNNylN:LNX-8\*O-1.N1-K77N-RRRRRRRRRRR776F//////kFk kFk FF:%$FF'FFFf/f/f/f/f/Fv F[[[[[[[y[ [[7F7FFFFFF#FFFFFFF///////////E0FH/2F2F2D2F&&&&&&w>|C|C|C|C|C|C|C|C|C|C|C3HHHHHE/s9.2%2t-Z CC'( U:@:Z.::Z$:Z  lZ::%:'.(.b5%CZCZ6Z%::"L--z-N+&NK:RNm-No N]Nk----&<`)N)HNN-k-k-Y.A3Z7ZZKNCNK=/ QJ [W%F"A  ah%2x2 a aa =av&$(a+Z[8%++IC  W Wo 1--D-Nn/y&u&y:*:O(y-NNN,,k:-----KeT^NN cKKNPPBB~Ky-333~333C|3. A]5 /!<&E<2Z5 U0Z-b/;5O?3F+7"<#F#<$AA&A!AAA3A+A"A&A{{-{{{{){{{{{{-{{{{){{{{--}|-)--{-{{-{{{{){{{{_5WKFKaX<U%E<3%9i9i909i99B5dXw;;%'!i99##<R2I9xxw;;%'<ei9(C9<+<+<+X+:9=59!:99i9ax79%0-=#Cz9%IPIIPIX(X%BBB ((>33.`3=_`1%%%>>""AcXXXXXXXXj3X UC, u7X# g#g#g#g#}g#g#5"lP5"p)%0-'YZ'1HO,ZaXZ#D,eNNNN "&"(&&#' NNNN""&&(&#'%'.%%%NNNN%'%%%""#""""'%'%%%??NNNN%GGGGG3%%%%%%%%"%%-****+NNNNXrYi-.C[0yO@KN"vYNC-,@@6[150Q0NCC0+&1yHHSQOHd@uuuu"dH 4]"dllz?]X V--xlN]{FyoSO"uhYzgru3e_1JfL_b_b_rAe_6"+6?jnLBxy[[LOFVPZ'.C-9 hNeN`Kl<Z((Z(Z(Z(Z(Z(Z(Z(Z(NNNNNNNNNNNNNNNTTTTx0x p0h, x h @lx`HP\44hhhH Hh !"###$%%,%&''()*0*+T+,X,-0-. .P./l/l/01t282344p56677,8@8|9 9t9:; ;<<<<===>?AXAxAAABB,BPCC CDChCCCCDD@DDEE<E`EEEFFFGG<G`GHHIII0IHI`JJJJKK,KDK\KtKLLLdL|LLLLMxN4NLNdN|NNOPOhOOOOOPP4PLPpPPPPQQ$Q<QLR`RRRRRSS,TT,TDThTTTTTUU(ULUpUVVVWW0WTWlWWWWWXX,XDX\XXYY(Y@YdY|YYZ$Z|ZZZZ[ [$[<[\\\]],]P]h_$```aaa@aXa|aaaabb$b<bTblbbccccd d$dHd`ddddeeeeeff4fXfpffffgPhi jjjkkk4kLkl$lhlm$mnno$op8p\pxpppq q4qXqpqqqr|rrrs@sPs`sssttuuuuuvvw\xx<x`xxxxxxz(z{p|,|}|~L~HX|d|hD\t< $4L\ \$4DTdt TXPp<H`@`phx4pT(@Ph$<Lt,<Tlt(Ld4x l,L(x DTdtd\ p<<<<<<<<<<<<<<0̴X΀Dϴ,h҄Ӡ԰<ռ֐T؄@۰t݄ݔ4l 84LX 8 hL 8pX(H4,H,t`pp  h    p  l  ,Ph$H`x8Pt $H`8P0H(\ !d""##$H$X%|'''4'X'p''''( ($(H(`(((() )$)H)`))))***<*T*x****++,+D+h++++,, ,8,P,h,,,,---@-X-|---...@.X.|....///0/T/l////00 0D0\0t000001141L12t3X4L5467078D8889:;;h?,?<?@|AxAABBCTD8DEF$FGhHHXIpJ@JJKKLMXN4NOpPP,QQRXSST$TUVW WWX,XYZHZ[t\$\]]4]]^ ^,^L^d^|^^^^___<_T_x____``,`D`h`````aa(aLadb,cpccccd d$dPdpdddddee0efffggg@gXg|ggggh h0hHhlhhhhhi i8i\itiijLjk k0kmndoo(o8o\otooooppp@pXpppppqlrslt`uXvvwwxxypz z{{|X|h}~~Lt4|,L,LdhL4TTdPD L<@`X4hx,DPhx4(D `h@4 ,Pt(Lp h<,@<\”¨8\èPĤ,xŘŸXƀƨ ,Hnj `0Ɍɜ@P`ʄʨʸˤ$`Ϭ\Ѩp(Ӡՠհ P$<Tdt؄ؔ|ڸۼLܨ0݄Hެl@<H(d@TT8\$HlDh@dPt(Lp$Hl Dh@d<`(Hhd0T|D    dLl($8D l!"#$|%X&p'p'''(($(H(l(((() )D)h))))**@*d****++<+`++++,,8,\,,,,,--4-X-----..4.X/0T0l0000011,1D1h11112 282\2222233(3@3X3p33334404H4`44445 505T5l555556 686P6h6666777@7X7|77778 808H8l888899$9H9l99999::,:X:x::::;;(;@;d;;;;;<<<>>@>X>p>>>>>?? ?8?\?t?????@@@@@d@@@AA$AHA`AxAAAAAB BHBpBBBBCC C8CPChCCCCDD@DhDDDDEEE0EHE`ExEEEFF8F`FFFFFGG(G@GdGGGHH(H@HXHpHHHHHI I4I\IIIIIIJJ(J@JdJJJKK(KPKtKKKKKLLL4LLLdL|LLLLMM M<MXMtMMMNN4NdNNNO O@O`OOOOPP PLPxPPQQ8QhQQQQRR8RXRxRRRS SPSSSTT(T@T\TtTTTTU U0UTUlUVpVVWhWWWWWXX@XdXXYZ[\\(\@\X\p\\\\] ^$_(`(`@`X`p`````a a0aXaabtcXcccddd<d`dddde@eef8fgg8gTghhHhhiHiHiik8kllDllm4mndo$oDodoooplqrstvw$wx$yzd{X|$|}~t (h\lTlXh`H d4X$4d@lp<8 8Ph(@Xp0H`x`pP8 |Px\  tP<\ x,8\´TXx8Ƅ|ǠP̐<0ΨϬ\(@ҬԤՀ֔@Tڈ@ܔ\ ,LHhppp(@Xp 0@L,\|Th\h0<$  H 0 T ` $H, | ,Dt8XHp $ !h"#4#$%t%%%&H&&' '$'<'T'l''''')++++++,,0,H,l,,-t-.8...//4/L/0(0H0X00011$1<1`1x111222223P334H4`4444455(5@5`556@6X666677p78P8h88989X99::(:@:X:|:;h;;= =|=>x>? ?,?D??@@4@T@lA`ABB$B<BTBBBCC(C@CdCD,DPDpDElEEEEF,FxFFFFG<GTGtGGHH,HDH\H|HIJJKLM|NNOOP@Q,QRXRSTU UVdW(W@WXxXY ZZZ[\\]$^t__`8`aLabbclcdexfhg\ghdhi<ijjk<klllmXnnoppprlrsttu u$uv@vwlx,xDxylz{\}}~$~XL\DX|xL\8H$  xX0\P@<,,t<8 \Dx@4P,<$|<$tHP\ ͤθ( lLּ| DްL\|ߜ߼<\4Xp<h0,@P`p @` @` @`x8Xx @`8H0,|(T$$4`$0Hd,0H`$t ( $d "T#($l$%%d%t%%%&4&l&&&'P'((`((((()))P)x))*D*+8++,,d,,--|.$./,/@/@/@/@/@/@/@/@/@/@/@/@/@0401X2$335 556d6678 8,8899D;D;d;;<<<<=>l?@AACTClC|D4E,EH`IIJJL0MN\NP$PPQ8QQR(RpRSSLSST(TxTU<UUV(VtVW$WxWXXdXY YdYYZ(ZtZ[[l[\\t\\]@]]^^\^^_0__``L``a alaabHbbcDccdddddeDeefDffg(g|gh$hlhiiXiij<jjk0kkl$lhllmDmmn npnoopoppdpqqLqqr8rrsspsttpttuDuuv vhvwwTwwx<xxy$yxyzzhz{{8|p||||||}}},}@}T}h}}~<~` ,L(l<(XxH(HhX|X$8HL\(Ppd8L|<4`4P Xt880$t(Lp8p 4@d<`8\ 0Tx,Ptt8X(`d<dL ,@T4x 0Txl H¸\Ü@dňŬ<`Ƅƨ,PtǘǼ(LpȔȸ$Hlɐɴ<4l̼<ΌδϤ@Ѡ8Ҙ8XӸ4TtHմ քX,ؐذ<|  m ^ ^ p 0x   T Dn * ( B >F <  4VCopyright 2015 Google Inc. All Rights Reserved.Noto SansBold2.000;GOOG;NotoSans-BoldNoto Sans BoldVersion 2.000;GOOG;noto-source:20170915:90ef993387c0; ttfautohint (v1.7)NotoSans-BoldNoto is a trademark of Google Inc.Monotype Imaging Inc.Monotype Design TeamDesigned by Monotype design team.http://www.google.com/get/noto/http://www.monotype.com/studioThis Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.http://scripts.sil.org/OFL2   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a  bcdefghjikmlnoqprsutvwxzy{}|~    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  NULLCRuni00A0uni00AD overscoreuni00B2uni00B3uni00B5uni00B9AmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflexCdotcdotDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGdotgdotuni0122uni0123 Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflexuni0136uni0137 kgreenlandicLacutelacuteuni013Buni013CLcaronlcaronLdotldotNacutenacuteuni0145uni0146Ncaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracuteuni0156uni0157RcaronrcaronSacutesacute Scircumflex scircumflexuni021Auni021BTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongs Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0218uni0219tonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9 IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygrave afii00208 underscoredbl quotereversedminutesecond exclamdbl nsuperior afii08941pesetaEuro afii61248 afii61289 afii61352uni2126 estimated oneeighth threeeighths fiveeighths seveneighths cyrillicbrevecaroncommaaccent commaaccentcommaaccentrotateuni2074uni2075uni2077uni2078uni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni02F3OhornohornUhornuhornhookuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCuni0162uni0163uni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019Funi01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5uni01E6uni01E7uni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9uni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217uni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236uni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0242uni0243uni0244uni0245uni0246uni0247uni0248uni0249uni024Auni024Buni024Cuni024Duni024Euni024Funi0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Buni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268 iotaLatinuni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276 omegacloseduni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295 glottalturneduni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2dzliguni02A4 dzligcurltsliguni02A7 tcligcurluni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BAuni02BBuni02BDuni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DFuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF hookabovecombuni0374uni0375uni037Auni037Buni037Cuni037Duni037Euni03D0uni03D3uni03D4phi1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni051Auni051Buni051Cuni051Duni1D00uni1D01uni1D02uni1D03uni1D04uni1D05uni1D06uni1D07uni1D08uni1D09uni1D0Auni1D0Buni1D0Cuni1D0Duni1D0Euni1D0Funi1D10uni1D11uni1D12uni1D13uni1D14uni1D15uni1D16uni1D17uni1D18uni1D19uni1D1Auni1D1Buni1D1Cuni1D1Duni1D1Euni1D1Funi1D20uni1D21uni1D22uni1D23uni1D24uni1D25uni1D26uni1D27uni1D28uni1D29uni1D2Auni1D2Buni1D2Cuni1D2Duni1D2Euni1D2Funi1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Duni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D5Cuni1D5Duni1D5Euni1D5Funi1D60uni1D61uni1D62uni1D63uni1D64uni1D65uni1D66uni1D67uni1D68uni1D69uni1D6Auni1D6Buni1D6Cuni1D6Duni1D6Euni1D6Funi1D70uni1D71uni1D72uni1D73uni1D74uni1D75uni1D76uni1D77uni1D78uni1D79uni1D7Auni1D7Buni1D7Cuni1D7Duni1D7Euni1D7Funi1D80uni1D81uni1D82uni1D83uni1D84uni1D85uni1D86uni1D87uni1D88uni1D89uni1D8Auni1D8Buni1D8Cuni1D8Duni1D8Euni1D8Funi1D90uni1D91uni1D92uni1D93uni1D94uni1D95uni1D96uni1D97uni1D98uni1D99uni1D9Auni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB8uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7Funi1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni200Cuni200Duni200Euni200Funi2012uni2016uni201Funi202Auni202Buni202Cuni202Duni202Euni202Funi2034uni203Euni205Euni206Auni206Buni206Cuni206Duni206Euni206Funi2070uni2076uni2079uni2090uni2091uni2092uni2093uni2094uni20A0uni20A1uni20A2uni20A5uni20A6uni20A8uni20A9uni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20B9uni20F0uni2117uni214Duni214Euni2153uni2154uni2184uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C6Duni2C71uni2C72uni2C73uni2C74uni2C75uni2C76uni2C77uni2E17uniA717uniA718uniA719uniA71AuniA71BuniA71CuniA71DuniA71EuniA71FuniA720uniA721uniA788uniA789uniA78AuniA78BuniA78C dieresisacute dieresisgraveuniFE20uniFE21uniFE22uniFE23uni03B1030403130300uni03B1030403130301uni03B1030403140300uni03B1030403140301uni03B1030603130300uni03B1030603130301uni03B1030603140300uni03B1030603140301uni03B9030403130300uni03B9030403130301uni03B9030403140300uni03B9030403140301uni03B9030603130300uni03B9030603130301uni03B9030603140300uni03B9030603140301uni03C5030403130300uni03C5030403130301uni03C5030403140300uni03C5030403140301uni03C5030603130300uni03C5030603130301uni03C5030603140300uni03C5030603140301uni03B9030803040300uni03B9030803040301uni03B9030803060300uni03B9030803060301uni03C5030803040300uni03C5030803040301uni03C5030803060300uni03C5030803060301Eng.alt1Eng.alt2Eng.alt3 uni1FCD02C9 uni1FCE02C9 uni1FDD02C9 uni1FDE02C9dotacutecarondotmacrondieresis tildedieresis tildeacute macrongrave macronacute dieresiscarondieresismacron tildemacron dotmacron dotmacron.capuni030103060308uni030003060308uni030103040308uni030003040308 uni1FDE0306 uni1FDD0306 uni1FCE0306 uni1FCD0306uni0514uni0515uni0516uni0517uni0518uni0519uni051Euni051Funi0520uni0521uni0522uni0523uni0524uni0525uni0526uni0527cyrillic_otmarkuni20BAuni1EFAuni2C6Euni1E9ETurnedauni1EFCuni1EFEuniA722uniA724uniA726uniA728uniA72AuniA72CuniA72EuniA732uniA734uniA736uniA738uniA73AuniA73CuniA73EuniA740uniA742uniA744uniA746uniA748uniA74AuniA74CuniA74EuniA750uniA752uniA754uniA756uniA758uniA75AuniA75CuniA75EuniA760uniA764uniA766uniA768uniA76AuniA76CuniA76EuniA779uniA77BuniA77DuniA77EuniA780uniA782uniA784uniA786uniA78DuniA790uniA792uniA7A0uniA7A2uniA7A4uniA7A6uniA7A8uniA7AAuniA7ABuniA7ACuniA7ADuniA7B0uniA7B1uniA7B2uniA7B3uniA7B4uniA7B6Aogonek.loclNAVEogonek.loclNAVIogonek.loclNAVUogonek.loclNAVLcommaaccent.loclMAHNcommaaccent.loclMAHTurnedeafii10103dotlessafii10105dotless deltalatinuni2C78uni025Cuni025Duni01DDuni025Aiogonekdotlessuni0237uni1EFBuni1E9Cuni1E9Duni2C7A subscriptjuni2C79uni0249dotlessuni029Ddotlessuni02B2dotlessuni03F3dotlessuni1D62dotlessuni1D96dotlessuni1DA4dotlessuni1DA8dotlessuni1E2Ddotlessuni1ECBdotlessuniA723uniA725uniA727uniA729uniA72BuniA72DuniA72FuniA730uniA731uniA733uniA735uniA737uniA739uniA73BuniA73DuniA73FuniA741uniA743uniA745uniA747uniA749uniA74BuniA74DuniA74FuniA751uniA753uniA755uniA757uniA759uniA75BuniA75DuniA75FuniA761uniA765uniA767uniA769uniA76BuniA76DuniA76FuniA771uniA772uniA773uniA774uniA775uniA776uniA777uniA778uniA77AuniA77CuniA77FuniA781uniA783uniA785uniA787uniA78EuniA791uniA793uniA7A1uniA7A3uniA7A5uniA7A7uniA7A9uniA7B5uniA7B7uniA7FAuni1EFDuni1EFFaogonek.loclNAVeogonek.loclNAVlcommaaccent.loclMAHncommaaccent.loclMAHiogonek.loclNAVuogonek.loclNAVf_ff_f_if_f_lf_if_llongs_ts_ta.sc aacute.sc abreve.scacircumflex.sc adieresis.sc agrave.sc amacron.sc aogonek.scaring.sc aringacute.sc atilde.scae.sc aeacute.scb.scc.sc cacute.sc ccaron.sc ccedilla.scccircumflex.sccdot.scd.sceth.sc dcaron.sc dcroat.sce.sc eacute.sc ebreve.sc ecaron.scecircumflex.sc edieresis.sc edotaccent.sc egrave.sc emacron.sc eogonek.scf.scg.sc gbreve.scgcircumflex.scgcommaaccent.scgdot.sch.schbar.schcircumflex.sci.sc iacute.sc ibreve.scicircumflex.sc idieresis.sc idotaccent.sc igrave.scij.sc imacron.sc iogonek.sc itilde.scj.scjcircumflex.sck.sckcommaaccent.scl.sc lacute.sc lcaron.sclcommaaccent.scldot.sc lslash.scm.scn.sc nacute.sc ncaron.scncommaaccent.sceng.sc ntilde.sco.sc oacute.sc obreve.scocircumflex.sc odieresis.sc ograve.scohungarumlaut.sc omacron.sc oslash.scoslashacute.sc otilde.scoe.scp.scthorn.scq.scr.sc racute.sc rcaron.scrcommaaccent.scs.sc sacute.sc scaron.sc scedilla.scscircumflex.scscommaaccent.sc germandbls.sct.sctbar.sc tcaron.sc tcedilla.sctcommaaccent.scu.sc uacute.sc ubreve.scucircumflex.sc udieresis.sc ugrave.scuhungarumlaut.sc umacron.sc uogonek.scuring.sc utilde.scv.scw.sc wacute.scwcircumflex.sc wdieresis.sc wgrave.scx.scy.sc yacute.scycircumflex.sc ydieresis.sc ygrave.scz.sc zacute.sc zcaron.sc zdotaccent.scuni2071uniA78FuniA7F7uniA7FBuniA7FCuniA7FDuniA7FEuniA7FFuni0528uni052Auni052Cuni052EuniA640uniA642uniA644uniA646uniA648uniA64AuniA64CuniA64EuniA650uniA652uniA654uniA656uniA658uniA65AuniA65CuniA65EuniA660uniA662uniA664uniA666uniA668uniA66AuniA66CuniA680uniA682uniA684uniA686uniA688uniA68AuniA68CuniA68EuniA690uniA692uniA694uniA696uniA698uniA69Auni0529uni052Buni052Duni052Funi1C80uni1C81uni1C82uni1C83uni1C84uni1C85uni1C86uni1C87uni1C88uniA641uniA643uniA645uniA647uniA649uniA64BuniA64DuniA64FuniA651uniA653uniA655uniA657uniA659uniA65BuniA65DuniA65FuniA661uniA663uniA665uniA667uniA669uniA66BuniA66DuniA681uniA683uniA685uniA687uniA689uniA68BuniA68DuniA68FuniA691uniA693uniA695uniA697uniA699uniA69Bafii10066.loclSRBuniA66EuniA67FuniA69CuniA69Duni0370uni0372uni0376uni03CFuni037Funi0371uni0373uni0377 uni03D0.altCfrakturHfrakturIfrakturRfrakturZfrakturuniA762uniA763uni212Cuni210Buni2110uni2112PiDoubleStruckuni211BTurnedFuni212B CDoubleStruck HDoubleStruck NDoubleStruck PDoubleStruck QDoubleStruck RDoubleStruck ZDoubleStruckItalicDDoubleStruckGammaDoubleStruckuni2107uni212Auni2130uni2131uni2133uniA796uniA798uniA79AuniA79CuniA79EItalicdDoubleStruckItaliceDoubleStruckItaliciDoubleStruckItalicjDoubleStruckgammaDoubleStruckpiDoubleStruckuni210Euni210FscriptescriptoscriptguniA794uniA795uniA797uniA799uniA79BuniA79DuniA79FuniAB30uniAB31uniAB32uniAB33uniAB34uniAB35uniAB36uniAB37uniAB38uniAB39uniAB3AuniAB3BuniAB3CuniAB3DuniAB3EuniAB3FuniAB40uniAB41uniAB42uniAB43uniAB44uniAB45uniAB46uniAB47uniAB48uniAB49uniAB4AuniAB4BuniAB4CuniAB4DuniAB4EuniAB4FuniAB50uniAB51uniAB52uniAB53uniAB54uniAB55uniAB56uniAB57uniAB58uniAB59uniAB5AuniAB64uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209CuniA770uniA7F8uniA7F9uniAB5CuniAB5DuniAB5EuniAB5Funi2E2Fzero.lfone.lftwo.lfthree.lffour.lffive.lfsix.lfseven.lfeight.lfnine.lfzero.osfone.osftwo.osf three.osffour.osffive.osfsix.osf seven.osf eight.osfnine.osf zero.slash zero.tosfone.tosftwo.tosf three.tosf four.tosf five.tosfsix.tosf seven.tosf eight.tosf nine.tosf zero.dnomone.dnomtwo.dnom three.dnom four.dnom five.dnomsix.dnom seven.dnom eight.dnom nine.dnom zero.numrone.numrtwo.numr three.numr four.numr five.numrsix.numr seven.numr eight.numr nine.numruni215Funi2189uni2155uni2156uni2157uni2158uni2159uni215Auni2150uni2151uni2152uni2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni2042uni204Cuni204Duni2045uni2046caretuni2041uni2040uni2050uni2E36uni2E37uni205Cuni2E13uni2E16uni2E08downwardsancorauni2E0Euni2049uni2E2Duni2059uni2055uni2E10uni205Buni2058uni2027uni2043uni2E12uni2E18uni2054uni2E04uni2E1Cuni2E0Cuni2E02uni2E09uni2E20uni204Eonedotenleaderonedotovertwodotspunctuationuni2E19uni2E0Funi2047uni2048uni2E34uni2E33uni2E07uni2E06uni2E0Buni203Buni2E11reversedparagraphreversedquestionmarkuni204Funi2E01uni2E00uni2E05uni2E1Duni2E0Duni2E03uni2E0Auni2E21uni2E30squaredfourdotpunctuationuni2053uni2056uni2E1Euni2E1Funi2E1Buni204Auni2E39uni205Duni2E32uni2E38uni2E35uni2051twodotenleaderuni205Atwodotsoveronedotpunctuationuni203Funi2023uni2E3Cuni2E3Duni2E3Euni2E3Funi2E41uniA673 upwardsancorauni2E31uni208Duni208Ebrackhalfbottomleftbrackhalfbottomrightbrackhalftopleftbrackhalftoprightleftdoubleparenthesisrightdoubleparenthesisuni2E26uni2E27uni207Duni207Euni2E1Auni2010uni2011uni2E3Buni2E3Auni2E40uni2036uni2035uni2057uni2037uni2E42 braceleft.sc braceright.scbracketleft.scbracketright.sc exclam.sc exclamdbl.sc exclamdown.scguilsinglleft.scguilsinglright.sc parenleft.sc parenright.sc question.scquestiondown.scuniA92EuniA67Euni205Funi2028uni2029uni2061uni2064uni2063uni2062uni2066uni2067uni2068uni2069uni2060uni20B6uni20BCuni20BDuni20AAuni20B7uni20B8uni20BBuni20BEuni2127uni2135uni214Buni2136uni2052uni2138uni208Cuni207Cuni2137uni208Buni207Buni2031uni208Auni207AreversedSansSerifLsummationDoubleStruckturnedSansSerifGturnedSansSerifLturnedSansSerifYRotatedQuni2100uni2101uni2106uni2103uni2104uni213Buni2109uni2139uni203Duni2114uni2125uni214C prescriptionuni214Auni211Funi2108uni2120symbolforsamaritansourceuni2121uni2123 weierstrassuni02DEuni02E5_uni02E5_uni02E9uni02E5_uni02E5_uni02E6uni02E5_uni02E5_uni02E8uni02E5_uni02E5_uni02E7uni02E5_uni02E9uni02E5_uni02E9_uni02E5uni02E5_uni02E9_uni02E9uni02E5_uni02E9_uni02E6uni02E5_uni02E9_uni02E8uni02E5_uni02E9_uni02E7uni02E5_uni02E6uni02E5_uni02E6_uni02E5uni02E5_uni02E6_uni02E9uni02E5_uni02E6_uni02E6uni02E5_uni02E6_uni02E8uni02E5_uni02E6_uni02E7uni02E5_uni02E8uni02E5_uni02E8_uni02E5uni02E5_uni02E8_uni02E9uni02E5_uni02E8_uni02E6uni02E5_uni02E8_uni02E8uni02E5_uni02E8_uni02E7uni02E5_uni02E7uni02E5_uni02E7_uni02E5uni02E5_uni02E7_uni02E9uni02E5_uni02E7_uni02E6uni02E5_uni02E7_uni02E8uni02E5_uni02E7_uni02E7uni02E9_uni02E5uni02E9_uni02E5_uni02E5uni02E9_uni02E5_uni02E9uni02E9_uni02E5_uni02E6uni02E9_uni02E5_uni02E8uni02E9_uni02E5_uni02E7uni02E9_uni02E9_uni02E5uni02E9_uni02E9_uni02E6uni02E9_uni02E9_uni02E8uni02E9_uni02E9_uni02E7uni02E9_uni02E6uni02E9_uni02E6_uni02E5uni02E9_uni02E6_uni02E9uni02E9_uni02E6_uni02E6uni02E9_uni02E6_uni02E8uni02E9_uni02E6_uni02E7uni02E9_uni02E8uni02E9_uni02E8_uni02E5uni02E9_uni02E8_uni02E9uni02E9_uni02E8_uni02E6uni02E9_uni02E8_uni02E8uni02E9_uni02E8_uni02E7uni02E9_uni02E7uni02E9_uni02E7_uni02E5uni02E9_uni02E7_uni02E9uni02E9_uni02E7_uni02E6uni02E9_uni02E7_uni02E8uni02E9_uni02E7_uni02E7uni02E6_uni02E5uni02E6_uni02E5_uni02E5uni02E6_uni02E5_uni02E9uni02E6_uni02E5_uni02E6uni02E6_uni02E5_uni02E8uni02E6_uni02E5_uni02E7uni02E6_uni02E9uni02E6_uni02E9_uni02E5uni02E6_uni02E9_uni02E9uni02E6_uni02E9_uni02E6uni02E6_uni02E9_uni02E8uni02E6_uni02E9_uni02E7uni02E6_uni02E6_uni02E5uni02E6_uni02E6_uni02E9uni02E6_uni02E6_uni02E8uni02E6_uni02E6_uni02E7uni02E6_uni02E8uni02E6_uni02E8_uni02E5uni02E6_uni02E8_uni02E9uni02E6_uni02E8_uni02E6uni02E6_uni02E8_uni02E8uni02E6_uni02E8_uni02E7uni02E6_uni02E7uni02E6_uni02E7_uni02E5uni02E6_uni02E7_uni02E9uni02E6_uni02E7_uni02E6uni02E6_uni02E7_uni02E8uni02E6_uni02E7_uni02E7uni02E8_uni02E5uni02E8_uni02E5_uni02E5uni02E8_uni02E5_uni02E9uni02E8_uni02E5_uni02E6uni02E8_uni02E5_uni02E8uni02E8_uni02E5_uni02E7uni02E8_uni02E9uni02E8_uni02E9_uni02E5uni02E8_uni02E9_uni02E9uni02E8_uni02E9_uni02E6uni02E8_uni02E9_uni02E8uni02E8_uni02E9_uni02E7uni02E8_uni02E6uni02E8_uni02E6_uni02E5uni02E8_uni02E6_uni02E9uni02E8_uni02E6_uni02E6uni02E8_uni02E6_uni02E8uni02E8_uni02E6_uni02E7uni02E8_uni02E8_uni02E5uni02E8_uni02E8_uni02E9uni02E8_uni02E8_uni02E6uni02E8_uni02E8_uni02E7uni02E8_uni02E7uni02E8_uni02E7_uni02E5uni02E8_uni02E7_uni02E9uni02E8_uni02E7_uni02E6uni02E8_uni02E7_uni02E8uni02E8_uni02E7_uni02E7uni02E7_uni02E5uni02E7_uni02E5_uni02E5uni02E7_uni02E5_uni02E9uni02E7_uni02E5_uni02E6uni02E7_uni02E5_uni02E8uni02E7_uni02E5_uni02E7uni02E7_uni02E9uni02E7_uni02E9_uni02E5uni02E7_uni02E9_uni02E9uni02E7_uni02E9_uni02E6uni02E7_uni02E9_uni02E8uni02E7_uni02E9_uni02E7uni02E7_uni02E6uni02E7_uni02E6_uni02E5uni02E7_uni02E6_uni02E9uni02E7_uni02E6_uni02E6uni02E7_uni02E6_uni02E8uni02E7_uni02E6_uni02E7uni02E7_uni02E8uni02E7_uni02E8_uni02E5uni02E7_uni02E8_uni02E9uni02E7_uni02E8_uni02E6uni02E7_uni02E8_uni02E8uni02E7_uni02E8_uni02E7uni02E7_uni02E7_uni02E5uni02E7_uni02E7_uni02E9uni02E7_uni02E7_uni02E6uni02E7_uni02E7_uni02E8uniAB5B ampersand.scuni2129uni0308uni0307 gravecomb acutecombuni030Buni0302uni030Cuni0306uni030A tildecombuni0304 overlinecmbuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320hookpalatalizedbelowcombhookretroflexbelowcomb dotbelowcombuni0324uni0325uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334strokeshortoverlaycombstrokelongoverlaycombslashshortoverlaycombslashlongoverlaycombuni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni034Funi0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035Auni035Buni035Cuni035Duni035Euni035Funi0360uni0361uni0362acutegraveacute acutemacronuni1DFEuni1DFF brevemacroncombiningconjoiningmacroncombiningmacronlefthalfcombiningmacronrighthalf dottedacute dottedgravedoublecircumflexabovegraveacutegrave gravemacronuni1DD0uni1DC4 macronbreveuni1DC6 ogonekabove snakebelowsuspensionmarkuni1AB0uni1AB1uni1AB2uni1AB3uni1AB4uni1AB5uni1AB6uni1AB7uni1AB8uni1AB9uni1ABAuni1ABBuni1ABCuni1ABDuni1DE7uni1DE8uni1DE9uni1DEAuni1DEBuni1DECuni1DEDuni1DEEuni1DEFuni1DF0uni1DF1uni1DF2uni1DF3uni1DF4uni1DF5uni1DFBuni1DFCuni1DFDuni2DE0uni2DE1uni2DE2uni2DE3uni2DE4uni2DE5uni2DE6uni2DE7uni2DE8uni2DE9uni2DEAuni2DEBuni2DECuni2DEDuni2DEEuni2DEFuni2DF0uni2DF1uni2DF2uni2DF3uni2DF4uni2DF5uni2DF6uni2DF7uni2DF8uni2DF9uni2DFAuni2DFBuni2DFCuni2DFDuni2DFEuni2DFFuniFE00uniFE27uniFE28uniFE29uniFE2AuniFE2BuniFE2CuniFE2Duraboveusabove zigzagbelowuni1ABEdieresiscomb.scdotaccentcomb.sc gravecomb.sc acutecomb.schungarumlautcomb.sccircumflexcomb.sc caroncomb.sc brevecomb.sc ringcomb.sc tildecomb.sc macroncomb.sc ogonekcomb.sc overscore.scuni0342uni0343uni0344uni0345uni0483uni0484uni0485uni0486uni0487uniA66FuniA674uniA675uniA676uniA677uniA678uniA679uniA67AuniA67BuniA67CuniA67DuniA69EuniA69FuniFE2EuniFE2FuniA670uniA671uniA672uni1DDBuni1DDEuni1DDFuni1DE1uni1DE2uni0363uni1DD4uni1DD5uni1DD6uni1DD7uni0368uni0369uni0364uni1DD9flattenedopenaaboveuni1DDAuni036Auni0365uni1DD8uni1DDCuni1DDDuni1DE5uni036Buni1DE0uni0366uni1DCAuni036Cuni1DE3uni1DE4uni036Duni0367uni036Euni036Funi1DE6uni2C7D commaaccent2uni2C70uni2C7Euni2C7FuniAB65uniA7AEuniAB60uniAB61uniAB62uniAB63summationDoubleStruck.miruni20BFuni2E43uni2E44uniA700uniA701uniA702uniA703uniA704uniA705uniA706uniA707uniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716xx"-,-xx"-,-ssF-P-ssFF-FO-{{eeh-h-{{ee--xx"-,-ccHH*~h-4th-ccHHg-m-۰, UXEY KQKSZX4(Y`f UX%acc#b!!YC#DC`B-, `f-, d P&Z( CEcEEX!%YR[X!#!X PPX!@Y 8PX!8YY  CEcEad(PX! CEcE 0PX!0Y PX f a PX` PX! ` 6PX!6``YYY% CcRXK PX! CKPX!Kac CcbYYdaY+YY#PXeYY-, E %ad CPX#B#B!!Y`-,#!#! dbB #BEX CEc C `Ec*! C +0%&QX`PaRYX#Y!Y @SX+!@Y#PXeY-,C+C`B-,#B# #Babfc`*-, E Ccb PX@`Yfc`D`-, CEB*!C`B- ,C#DC`B- , E +#C%` E#a d PX!0PX @YY#PXeY%#aDD`- , E +#C%` E#a d$PX@Y#PXeY%#aDD`- , #B EX!#!Y*!- ,EdaD-,` CJPX #BYCJRX #BY-, bfc c#aC` ` #B#-,KTXdDY$ e#x-,KQXKSXdDY!Y$e#x-,CUXCaB+YC%B %B%B# %PXC`%B #a*!#a #a*!C`%B%a*!Y CGCG`b PX@`Yfc Ccb PX@`Yfc`#DC>C`B-,ETX#B E #B # `B `aBB`++"Y-,+-,+-,+-,+-,+-,+-,+-,+-,+-, +-),# bfc`KTX# .]!!Y-*,# bfc`KTX# .q!!Y-+,# bfc&`KTX# .r!!Y-, +ETX#B E #B # `B `aBB`++"Y-,+- ,+-!,+-",+-#,+-$,+-%,+-&,+-',+-(, +-,, <`--, `` C#`C%a`,*!-.,-+-*-/, G Ccb PX@`Yfc`#a8# UX G Ccb PX@`Yfc`#a8!Y-0,ETX EB/*EX0Y"Y-1, +ETX EB/*EX0Y"Y-2, 5`-3, EBEcb PX@`Yfc+ Ccb PX@`Yfc+D>#82*!-4, < G Ccb PX@`Yfc`Ca8-5,.<-6, < G Ccb PX@`Yfc`CaCc8-7,% . G#B%IG#G#a Xb!Y#B6*-8,#B%%G#G#a B C+e.# <8-9,#B%% .G#G#a #B B C+ `PX @QX  &YBB# C #G#G#a#F`Cb PX@`Yfc` + a C`d#CadPXCaC`Y%b PX@`Yfca# &#Fa8#CF%CG#G#a` Cb PX@`Yfc`# +#C`+%a%b PX@`Yfc&a %`d#%`dPX!#!Y# &#Fa8Y-:,#B & .G#G#a#<8-;,#B #B F#G+#a8-<,#B%%G#G#aTX. <#!%%G#G#a %%G#G#a%%I%acc# Xb!Ycb PX@`Yfc`#.# <8#!Y-=,#B C .G#G#a ` `fb PX@`Yfc# <8->,# .F%FCXPRYX +-o,:+?+-p,:+@+-q,:+>+-r,:+?+-s,:+@+-t,;+..+-u,;+>+-v,;+?+-w,;+@+-x,;+>+-y,;+?+-z,;+@+-{,<+..+-|,<+>+-},<+?+-~,<+@+-,<+>+-,<+?+-,<+@+-,=+..+-,=+>+-,=+?+-,=+@+-,=+>+-,=+?+-,=+@+-, EX!#!YB+e$PxEX0Y-KRXYcpB@ o_C/ *B@vfVJ6$ *B@{n^P@- *BA *@%@ @ @ *BA @@@@@@@@@ *D$QX@XdD(QXXDY'QX@cTXDYYYYY@xhXL8& *DdDD ttfautohint version = 1.7 adjust-subglyphs = 0 default-script = latn dw-cleartype-strong-stem-width = 0 fallback-scaling = 0 fallback-script = none fallback-stem-width = 0 gdi-cleartype-strong-stem-width = 1 gray-strong-stem-width = 0 hinting-limit = 200 hinting-range-max = 50 hinting-range-min = 8 hint-composites = 0 ignore-restrictions = 0 increase-x-height = 14 reference = reference-index = 0 symbol = 0 TTFA-info = 1 windows-compatibility = 1 x-height-snapping-exceptions = control-instructions = linphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/NotoSans-BoldItalic.ttf000066400000000000000000016277341434616504300313640ustar00rootroot00000000000000 GDEF,GPOSOԫ GSUB S zOS/2m=z `TTFAQ-cmap8 s ^cvt t4(Nfpgm9&|x mgasp glyfr\Lhead(6hhea z) $hmtx3ӌ})D2locam[2maxp& namelxpost8LprepsB,l$=D]wwIUUW##::==?BDGIfknrR]SXYaacdffhh#=AEEIIYn   9ILNOQRUVYY[\^^adffqxz          ( . 0 0 6 6 : H L L Q Q S S [ \ b b l l o t v { }                                        , / 0 2 4 5 5 = ? A B C D E I J J K K N V X _ c c f f i v w w x    ,,4<DN\\d $&/R -   # % ) + , - . / 0 1  7  )F + ,] / 0_ 2 4a 6 <d ? ?k L Ll Q Qm X _n c cv i ow r v~ x z |     @ t y } !CCvvST  "#SS   . 4 7 : ; < > @ C L P V  X  4h = > A K N P R e i /         ! " # $ * + , Y [ n r s t u x y z | }   4nDFLTcyrlgreklatnkernmarkmkmk. u&t.֪rt bXn $x~x&nnnnnnxxxxxxnnnxxxxxxnxF*d $F F8* * . $ :   nB x BB  B d*n44B"J0L0LBBB"d PP0LfB*B "0LBLRRRRXnnnnnnnnnnnnxxxxxxxx**FFFFFF****dddddddd****8FFddnx~~~~~~~~~~~< <* $%&'()*./2345679:;<=>FYZ\^cm} #%'56789:<>BDFUZ[\^_`bdfgkmnopqrsuv{}<=ADFGMPQSTUV\]ackmruvxz{|}   &(*,.02468:<BCDEFGHIJKLMNOPQ];<CDEFGHIJ}~          79:<#%'579LNP]!"$B A       m       -ZM(((-<7<#%'79LNP]-d79:<#%'579LNP] ; -2"PF mdkqmdkqq~<8Y[^bgnopru =abcdefopqrstuvq[\^dgk  =]v{~;<=>?@ABijklmnwxyz{| (v{;<=>?@ABwxyz{|0 v{};<=>?@ABwxyz{|2v{~ ;<=>?@ABwxyz{| S] SX9 MST[ack|~ "o~oqsoos  STW& dM]ack * dM]ackttMackX* M]ack + dMN]abcdkl XX\^X2W MSack| + MS]ack|;"6;"2,6;< "" "oqsq~brbnorso-K-<                                   <<     ZZ           BFTPY_((222222 22222222(<( PPF<( ((F2j<((d~~~2( $$&'./ 24 7>DEHINNRSUUWWY\^^#mm$}}%&,-.3:AEFKLVWXY\]^_`aefi jpqrs#)t++{--|//}11~335:<<>>BCEGUUY^``dgkkmmoprrtuxy}}==ABDGKKMQSWYY[[ _m ry{~!%124@EFIKMQRXYgioyz!!&>@@BQ]^CJWXgx} !  +-!27}~8:;FPVal |   p p     A;:;$$&&(''..F//'2233U4477288 9:$;;F<<==>>>ADDIIQNN9UU8WW.YZ [[9\\ ^^AmmO}}N( U ((((RF9'''R''  888##2$$.%%2&&.''2((.)) ++ -- // 11 33 55$66 7788 99::><<>>>>BBCCFFUUYYZZ[[ \\]] ^^``WddeeffVggkkmmTooWpp rrVttuu xxyy}}PPX #"/#10"0S/"##=<<  !!<!<"E$ $ $  :;;ON== AABBDD&EEFF%GGKKMMNN6OOMPP!QQ/SS1TTLUU1VVLWWKYYK[[0__``aabb6ccdd6efgghhiijjkkll6mm/rrss ttMuu!vvSwwxx@yyJ{{?||5}},~~05, @E?/" IHIH5,  ?GG   1=1=0"E@J5,,4+4+ 4+4++           &&((**,,..002244&5566&7788&99::&;;<<&==>> @@ BB%CCDD%EEFF%GGHH%IIJJ%KKLLMM NNOO PPQQ ]]2^^. CJWXghin ovwx}     T !!}}Q CBCB7- *  )  3 p p= D D D@ @ J,A,$$&&**224477288 9:$<<==F@@JDDFHIIYJJ+PQRRSSTTUUVVWW*XXYZ \\ ]]:``JmmW}}V]  ++++        ""##2$$*%%2&&*''2((*)) **++ ,,-- ..// 0011 2233 4455$66 7788 99::F;;:<<F==:>>F??:BBCCDD]EEFFGGIIUU8WW7XX6YY5ZZ3[[1\\4]]^^Gdd\ggGkk\pp%uu%vvwwxxyyzz{{}}XXTIT! !/0 # Z/"!#E    . D-C B B.$ $ $  AS@,S@,  ,WV^==%@@AABBDDEEFF GGKK LLMM?NN>OO(RRSS0TTVVZZ[[#\\]]"^^__`` aa?bb>cc?dd>eeff gghh iijj kk?ll>mmnn sstt(uuBwwxx({{||}}~~#(B!C  ZRQRQD!C"-"-POPO "-0E0E0E NN#   #/././."-(DDM M LKLK  !C            !!&&''(())**++,,--..//00112233445566778899::;;<<==>> ??@@ AABB CCDD EEFF GGHH IIJJ KKLLMM NNOO PPQQ ]]2^^*1%;BCD8EJKLMP=QR7SV<WXY^ _`6afghinop5qvw|}~3;1H4 G8 765   I%13 4}}~~Y[  U  ) '  &  9 p p q se fm$=D]ww45LIkUUW##::?BDGIfknrR]&S'XYfaahddiffjhhkl{!=AEEIIYn;KQRVX]^_`  b dg9iILNORRUVYY[\^^adffqxz   3  4  8  9 ( ,: . .? 6 6@ ; HA L LO Q QP S SQ [ \R o oT q sU v {X } ^ c e g h j k l m n p s w x             ]*"`],JFR@J]<H]lJx"*xFRx]>]]B]v]*]*]*]*]*]*P@````J]]]]]Rllll"x,******FxxxxD]]]]]]**]**2FRFRFRFR*`x`x`x`x,,,8"f>8DJFRPVFRFRH\JbhJn]]]]]]JTtzHHH]]*lllllB""]**PR(F]*"H`J@JFX]:]"<x<"^]]$:]:`HH\]t]*J"HL`]t]t:@]:]<x(: J*|x]DF]FR]j]t"^8Lvx|^866DH\BBB"zx@F`x ]*0LR0 <R((" (.4:J@H\FLRX^dj]t]tDBpv|R@F"",]**]**P`xJeJe]t]hL]]RR^8]t $*x]06F*<BHNP.TZ`flrx]**]**]**]**]**rx]**]**]**]**rx~.`x`x`x`x`x`x~.]]]]]]]]]]FFFFl""*]tJ]t^84J*J]tV^@R.n"<*::hhe: eH&ntz,28>DJ]**]]lllll]**]**PV,J\b\bhLntz,J]**]**`x`x]]]]llT]**]]]]]]]]"`]*H]@||>`"J*]t]t^8*e   h"(.4:@FLR|||X]^dj||pv|vv>^8FR^8]XFRR^8x <B^8^8e$^8*06<BJTHNT]**4Z ` flr``x~  z &,28>DJPV\bhNntz "x(.4:@FLRX^djp`x`xv|v|]]>,J@J ]]]]]]]]$$*0H6<HH6<]BHNTZ`fllrllJx~Bx]x]"B*I:I:(]]]]]] &,28>D::::::::JPV\bhh]]::ntz@@@@@@@@]*]*> ""(.4@@@:@FLRX^djpJ^8HHv|j]J @ ]*$JJJFR,*06>>8>>DJDJPPPPPPPPPV\JJJbJttttttthtntz]tJJJJeeeee    7"(.4L:@]FL]RX^djpvFR|]FE\]*]tJ]>]> >>$*06<BHNTZ`flrx>0~<J#=.###,#j##`#1####T,G90='q50< f0|<0<GB 0k<0<=0<0< 0< 0 <0<s0?<x0<X{v^0<6(...Y.......*...(Q.0WU..0xW!y2W*e 0?.XdVfHz` PwIjV'sw2~z.*b% V{2  &5+S;6 eL,}9t'Os$..\_ ..R^338:..)"&Z1..YR.\9_808F.f. ..WV 88.3._*0>>>>>>>>>>>>>^0>>>>>>>>>>*>*>>*>>>*>>>0>>>>>>>>>>>>>>>>*>^^>>>>>>>^^>>>>0>>>>>>>>*>>"(.4:@FLRX^^^^^00000>>>>>>>>>>>>*>*>*>*>0>>>>>>>djpv|>>>*f>>>   $$$$$>*>006<BHHNTZ`f>lreeH+2<{eeeee^eceesexeeeeeeeD<eeeee+e}cD'c#?)#&#v#%#2eh DxDPDeeFf H m$=D]ww45LIkUUW##::==?BDGIfknrR])S*XYiaakddlffmhhno~!=AEEIIYn>NTUY[`abc  e gj9lILNORRUVYY[\^^adffqxz   6  7  ;  < ( ,= . .B 6 6C ; HD L LR Q QS S ST [ \U o sW v {\ } b g i k l n o p q r t w { |              9 f Z9~#f `#$ l#Z r9 9$ <9~ f$\D:HH :$8H$$$ $ H: H !9l#  :"""8t$ f444:::>LLLD9J`\P(((Vz\nHTTT@@@*bhTTT ppp6:2n,.9Htz   Z:4T#f#<$ F `$@$ H l$ l$ l$ rH##RLTX^$ X^ D#xJP#<# rPV \b `hntznH&I9  "("($&\F. 4:@#FLRX^dj#&p9Hv|"(,. Z:*64T `$9: $*06LT<B<BHNTZ`f f#lrx~x~&  9~9l6: H6:#$$ H 99~^$##9#f$#H9l9~ H4:&&,F28x> DJPV\bhntz"(:@LTpH&,9: "(,.4T:@FLTRX^djpv| H #B@ 4: Z:9 f9~:# >, Z$@H$< HH : H$&#Hf#  $#$5J5J5J##$ *0 <6<<<BH TTNTZ9l#`flrx:HII<(~$&#$H# <HZ:H: & ,9~28>DJP#H$9:V\6: fb$ f$9H\:hn :H ##t:z Hp "(.4:@FFLRX^djp v |60xZZ rx 2V  H  $*06xH<BBHHN2V2VxxTZ`flrx~     tt Z: Z:$$$ `$ &,$ $ l$28 l$ l$>D#Z JP rH rH rHV\bhntntz6:$ $  DJ#JPPV L\bhnttz r$$z   $:$$$ " r(. 4:@FLRHX^djjjjppppvvvv||||#B##B# r 9  $$$$ l   $ * 09$9$ r 6$ r < B H N T9 Z ` f l r x## ~:9 H ###$ $& ## # # ##: $ HH 6:6: $ H H#H H ! H:$H$#!!!!&! !! !! !,!&!,!2!8!>!D!J!V!P!\!V!\!b!h!t!n!t!!!z!!!!!!!!I!!!!!!!!!!!!!!!!!!"" """"""""""(":"."4":"@"F"|"d"L"X"R"d"X"^"|"d"j"p"v"v"|#""#"""""""""""""""""""""""""""""##M# #M###$#*#0#6#<5#f#B#H#N#f#T#Z9#`#f9~9##l#r#x#~##:#5J####3:###H##5J####$#####$$$9$$$$ $$8$&$,$2$8$>$D$J$P$P$V$V$\H$h$b$h$n$t$z$$$$$$$$$$$$4$$$$$$$$$$% EumDD5H "mEKM(Fc FO"C3  Tg ="""T"Y] zaBLI g4?R"}","Kc[z"t""+ "l""m"z"""BL "7" }<FlmXmWZEZ"""H"J"v"">,-gLnsS >>,-gs> |">>"g>[b"m"O}n6tM"|u?xk? XWrqWAmmz{"V FTU-  WN"(%"d|x4"G"O"K"7-""": """" ,":""qF";"X" "Y9dn,k">ggTggg~9"Os}Vs"v,"""""""Z")""~""a"6"-""t"""""""."'">:L$"\SGk_e/8PgvgN9ggggggCSggk#g"y"g gg5g"ggggg4ggpgg*g'g.gggg g8wm+*^q*<m!2= #{qhRmHI$d|GJ\#{^XJ"jiXMkMhEiEBB 5*  *   \!05c* " \*N=N*~ \0"8""""" \""l"q"" \""""|""b"m7"" `G:m  `m ";"a5j"D"'"f""z z$z21<"""fi""*"`2p"),$,ph<sggu"d"+"*""_"%"H>""'#VFyF#IF#gF"eF"jF]'\"YZ\#P-F*F'"vOsF"'"#5FFF"UF #F FF#"FF'"#FCFvF~#~"S#S"$FFX")F'#"iFj@FF"#OF.F]"Z#FR#R"Pg{"o"p""]"N"n"""h"6ggM""f"i""lL8?"N"hhI-Vyg 7OXpY.d"") )$=D]4:CINTY_du  &)?HIUUW[^_acefhikkmmoprrtu|}    ?BIIKLww !"#(*+./01"2$3[>AkLQo]^ueewxy|   ++QQSSVXbbzz|}%',..026689;;?BDDFGJP SSXYff9JCJQV_fo=@EEII_`       !#"")..*JL+NO.RR0UV1YY3ad4qx8}@GT\]w    * + 6 6 S S q q } }                      \ h t p$ f V V  z 8 b n 6& 8 66 $ h 8 * 0   h $$$$VVVVV 8 8 8 8 8 8 n 6 6 6 6hhhhh  8 8 8 h n h n h n h n 6 6 6 6 6 t 8 t 8 t 8 t 8 p $$$$6$  $ffffVhVhVh * * * 0 0 0 0          0 \ p$  V z$  h 2h $$  \ pV h z 8 6h n  6 066    D  8 6 h n $$ 8 8 6Vh  8 8 8 8 8 8 8 8 8 8 8 8 6 6 6 6 6 6 6 6$$6VhVhVhVhVhVhVh     8$Vh      8 8 t 8  $VhVh t 8 8 8 6 6$$VhVh * *   p  8 6VhVhVhVh  h n 8 8tV$*06<B nH n6 h hV 8  NTZ`flrrx~ > > > > D      V  &  , 2 8 > D J P V \ b \ b \ b h n 6 6 6 6 6 & t 8 p  p  p  p  p $6$  $  $  $ffff  VhVhVhVh * * * * 0 0 0 0 0              z z  8   "  " ( ( L . . . 4 : @ : @ F F hhhhhh hh   "  " ( ( . 4 : @ : @ F F L R X ^ d j p$$ v | \   8     f V t   $ f 6 $ $ $  8 8    8 $ * 0 8 66 < B H N N N N N N N N N N N T Z Z Z Z Z Z ` ` f f f f f f f f f f l r r r r r x x x8888888888 ~ ~  V h t    & &  & , , 2 8 > D J P V \ b h n t z 6 "vox,_gNggggNYZVQ",^<wP@&gggpGggk2Lgyggug "9"b":"-"UBIn{DBFGZbukUY~%qOa>{.)\hh"y"&"")"""S"FAYF@F8FsFFFzFFYFWFFF.FF4FFGFFFF-F:FKFFF3]8}""N"x"hhhWmg0-G:`"?""TP $6HZZttV p  > $ ] B  Q"(.4=Z $*06<E0E"0q   c*Q - : l tB$T6BTf  ne eoe _"(.4((b/(/#2#  ^2cQ - 0 : b l t f $6HZZttV  q hZ |o at$   @"m" W"""(.4z-J$*06<XJXD ,"C" LJefc*Q - : l t f<NZfr~n 2>Pbn H|" v"  =o " " ?"   vW  FF $ o"""(.4^{0$*06</uA/uD  @@   # % ) + , - . / 0 1  7  8 6 <: ? ?A L LB Q QC D     " $ ( * + , - . / 0  6  )E + ,Z / 0\ 2 4^ ? ?a Q Qb X _c c ck i ol r vs x zx | { E:v"(.4v:@FLRX^vvdjpv|r? 5n &,28>DJPV\bhntz "(..4:@FLRXXdd^|ddjpv|4&& 00(.=#4O98((1Mb&O*%!`p ,Ilnl G4FlC RlPl0 JlNlW-lZlClGlHlKl  2p @    t y }  &,28_ {`  "CCvvST  "#SS   . 4 7 : ; < > @ C L P V  X  4h = > A K N P R e i #"# & ) , - . 0 2 5 6 ; ? E  G  ,W / 0o 2 4q = >t A Kv N P R U X _ c c i o r z | 6<BxHNTZ`lflrx~ ,,bb, bDbD &D,28   >DJ PV \b              hn                                tz                                         ""H""""J"`"P"^"\"V"j"Q"c"" N3"/"H"X"%"L"C""."8" qB"A""r">"-"@"Y""L"Yl2"/"3")"7"1"x","3"8"!^FFtF7F F9FBFEF3F'F4"")""'" "W0I"9C9";"J":"`flrx~ &,28>DJPV\bhntz "(.4:@FLRX^djpv|D>h~n\d jVXTxElcUENdJX[UN@H__b\Wdb\Xc9VUZ;ayll[NPS\6IRNVGV"QL`EaR`Te`CaC1paF`@\O~b##t#=#]#](^jL>S^GF@3HBsFF~FjKRFwFFvFu{FuFyF|F  j        $ * , Y Y [ [ n n r u x z | }  %$]tK"+X^dpjpv| $FF9FFF FF=FFFFuFFFF "DFLTcyrlFgreklatn  SRB 2  "CAT JMAH tMOL ~NAV ROM       aaltc2sccaseccmpccmpccmpccmpccmpdnomfracliga lnumlocllocllocl"locl(locl.locl4numr:onum@ordnFpnumLrtlmRsmcpXsubs^supsdtnumj "#     $!%L`~llN > > ` $ J J b * j *D   5 `N  & ) , 0 3 7 Q Q9     ! " # N P$ n n' s u( y z+  - . / 0$')( 9012345678LM%g|K^0:DNj|",6@JT    LD   F 3     LE   G 4 :BJRZbjrz                            :BJRZbjrz - / 1 0 . 8 ; : 9 C E G F D = ? A @ > 3 5 7 6 4 , B < 2:BJRZbjrz e g i h f q s u t r | ~  } w y { z x k m o n l d p v j:BJRZbjrz I K M L J U W Y X V _ a c b ` Z \ ] [ O Q S R P H T ^ N:BJRZbjrz           ' ) + * ( ! # % $ "       & $ &  L L L L$(,28DHLRX#% n&0:DNX        $.8BLV`jt~            $.8BLV`jt~            {  }~34HI#$ ]^ >(>JJyOy/y n o p q r s2 0 1 2 3 4 5 6 7 8 9   KNOPQSVW$ :{tu'(;)*<   Q   "B*    $ $,$D2Rl|l|$2DR .        .      .           :     M          A B C D E F G H I J K L "$%&'()*+,-./0123456789:;<=>@^`cq  !#%')+-/13579:<>BDFH] 8     M          A B C D E F G H I J K L ">@DEFGHIJKLMNOPQRSTUVWXYZ[\]^`cq   "$&(*,.02468;=?CEGI^ B    6 "(ILIOILOI IXKXJ^2B  @)GOOG!--%  J ~01ac67Y] AEbw~  OP\_'/=?EMWY[]} d q  !_!!,m,-.Ds}!.Ze/ 12bd78Z^ BFcz PQ]`(>@ HPY[]_ f t !!!,`,n-.@t~".0[  HJHP ~GtI=EvZ;9753210/-,+)(&%+Mc5ee`Y`baa^58<.>D<RfjHhTHI#$&'#$JKLMNOPQR  n u z s t y  y ~ z  { |<=>L R S T U Vpqrstuvw  <  =  >  ? ? = > w o p q r { v x i | } j k l m ~    AB+,!LMNOPQ*{|$%&' ()*   ^  R +,-./0 1 ? i 2 A @ : S = > b c K } k ; < ] m B x V N y Q M P E 3 456789: '(;)*<  BCDEFGH R R   T  x  UV - . /WX ' ( ) * + , !" % hijklmn"/-. o n Z s W p g f H [ t h Y r J a O j T F I Go U ` | X q z { \ u _ w L l v e d C D ~  I  J  K  L  M  N  O  P  Q R ! S " T # U $ V % W & X ' Y ( Z ) [ * \ + ] , ^ - _ t W f g h u . ` / a 0 b 1 c 2 d 3 e 4 f 5 g 6 h 7 i 8 j 9 k : l ; m v w b c:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ [\]^_` abcdefghijklmno{|}~p qr  s t u v w xy z  6 7 8 9 : ; < d e^*@'eU]M+3!%!!^563d <,@)]oK_xL  +73"&54632EQyk*"-7!(5#+$=%")6b$@!]oL +3!3'$J#JG@Df   eoK  p L +37#737#73733733#3##7#37#Q7t "{ :j9a9i9u "~ 9k9_8T`"`eqffqe,q#$*1w@&1 JK PX@!oWg]qL@ Wg]qLY@$$ +7&&'57.5466773&&'76654&'3V$%f:#+G*3232>54#""&54>32'2>54#"DF/O:CI0Ps  FG1N8CI0N3  RJ,eZ9NM+e[:6y'O$_S_"I0d#'_(9"" 1 6n#*) `YKd,>@D[.*D(Jd"#E(Gz20 3&0!. 4+-b @]oL +3b#J$b@oL +&&5473m%$~xGn?@P|NwLBb.@oL +>54&'3HGn?r%$~xNwLB@P|T60+''7'777'^,`e*}7 NPy-tU|5oT &@#Ue]M +3##5#5353]kkkkt@U]M +667322E#;4 5~7.I@U]M +77!zz @_xL   +"&54632X*"-7!(5 +$=%")6@oKpL +#3,u6 !-@*_wK_xL!!  +"&54>32'2>54&#"e`(PvM[h$KwG0&1%  |rbQny_Uz1Sek/626Wec%7:Y !@ JoKpL  +3667'%3X &_AzS  54&#"'6632!:D("$D.G2vMFY*7_<h3K;%&$&c+:2P,@g]2l+J@G$Jg_wK_xL ++ +"&'532654&##7326654&#"'66328a$)`-GY5IC$*Q4&*,G>0kOccZT6L> :;$0l1+( d(]FJg GD9gA .@+JfoKpL  +37!733#37>7#x_SS "r?u y<3 0 '!D@AJg]oK_xL !! +"&'5326654&#"'!#6632:a$X083eq+ 6X3? ';&6 &f)TAJ{I+30G@D Jg_wK_xL! (& 0!0  +"&546676632&&#"36632'26654&#"hi&G11zR;3dtJ5IW7##"&54>3226676654&#"!@;!6L2 F2URN("+lR(aY:o}G/+2y.  )4/E#*, , -@*_zK_xL      +"&54632"&54632*"-7!(5*"-7!(5+$=%")6m+$=%")6, *@'a_zL      +"&546326673*"-7!(52E#+$=%")6;4 5~75cq0+%%5%NcFu5/@,eU]M +5!5!5jjkk5cq0+7%%55N؉uFM'?@<  J~_wK_xL#!''%( +76676654&#"'6632"&54632 6<1,"#G,--m?Xc'B*"d*"-7!(5EZ)!, l$VL2G8"&+$=%")6-N@O@# E =>JKPX@( g c_oK _r L@&  g g c_oLY@BAIGAOBO;931+)! @@ +"&&54>32#"&'##"&54>3232>54&#"32672677&&#"rNL] ?Z9': :.8K#B_;6L4 -!syc\2nY=f4/q@'1  #4"[Y^rx=;oX4"&!'GP3aN. '@M'csZpEsE[I;l#7=&8,@) JfoKpL  +#3#'#3'&&7#<vH T  6)<5c!9@6Je]oK]pL!! +332#32654##32654&##f|RJ1:JOI5ASJcX7C,-QJUI^ E5Qg1+1C/74&)<7@4  J_wK_xL  +"&54>32&&#"3267B;Z|PD_/:"I- )@&e]oKpL  +3!#3#';||< >@;  Je_wK_xL    +"&54>32&&#"32677#7!D|5jgk]8$H+Ba@ DL% O1m [J0{8]p8PS~ '@$foKpL  +3333##;;AA64 "@ JoKpL  +#77'7![bJ<]bLV((VV(2(Vc.Q(@%JcoL  +"&'532673B*'0= {|;@I{j %@" JoKpL  +33773#KLʰގg=5f!"@oK^pL +333}}u'@$ JoKpL +3333#>7###  ϗG F6R"MK)IV)$@! JoKpL +333>73## H GCHR6->A<-@*_wK_xL  +"&54>32'26654&#"E-]b,[W:`9=<.O9 B {TQyWOSfBO5^yEGJg +@(e]oKpL &! +332##32654##Wi/RXL5P=CR_F4^>Up7zB:X3226654&#"W -]b0cL|:`9=<.O9 B{TQy\$SfBO5^yEGJH 3@0Je]oKpL ! +332##32654##k}ZCe::T&@G`$\hUk89P'7@4J_wK_xL'' +"&'532654&'.546632&#"6Y,V-1C0&8$ ҚRO!G"6Z"'@$JoKpL"" +333667336673#&&47#p"  $ĩz&\''Yp6=)_"6E+:.PB &@# JoKpL  +#373#9tKTuU ["@JoKpL +333:s?:!GJ %@"]oK]pL  +#7!7!!{#b}c}b"@a]oL +3#3iihgfglX@oKpL +33jm6b]"@a]oL +73#73Chi纞gg'dD@Jt +D73#.HmU05bE&dD@U]M +D7![DD^a -dD@" JU]M  +D.'53.( ^47 "M% ,A,%~KPX@ J@ JYKPX@_zK_xL@!rK_zKpK_xLY@ %%  +"&&54>32373#7#72667654&#"(F,$B]:4AqtnK4%$ /# 'WHCk@,$FG$-w0K+$.#-+FV+V'(KPX@ J@ JYKPX@qK_zK_xL@!qK_zKpK_xLY@" (( +"&'##336632'2>54#"*4Aq>2'G,$B]S/#@$2 % ,$F56!2'WHCk@w+FV+V4OR'/,,7@4  J_zK_xL  +"&&54>32&&#"32679\6&JlE1M"-3 /A",(%?"#N (YHIf:p El9//x,n(KPX@ J@ JYKPX@qK_zK_xL@!qK_zKpK_xLY@#!((  +"&&54>323466773#7#72>54&#"(F,$B]:18%p K "2$ /# 'WIBj@."%0G$-w5OQ'/+FV+V,,%>@;Jg_zK_xL#!  +"&54>32##326732654#"`x'MrK[\2/+F0+YmUK35) hkEd;TAVh ,4oF5#."<"K@HJ_qK]rK_tL "" +"'53267#?6632&&#"3#3"- dPY cP"9'"kki -O v (+G))cO m ' p2R1 A,&6KPX@J@JYKPX@"_zK_xK_tL@&rK_zK_xK_tLY@('0.'6(6"!&& +"&'532677>1##"&&54>323732>54&#"=P%&U/3@ D3'C)$B\85=qwKk/!$/#%/2%"1*WECk@+%FVc*]/JT%'/+FV+++)'@$qK_zKpL$) +33136632#654#" J1FG AC5"5' 060%.UH$4?#<9`:? -@*_qKrKpL       +"&546323+/*,+ttf!*,!'0"@ >@;J_qKrK_tL      +"&54632"'532673 */*,+3"- | -Of!*,!'0 v (+G2R1i)@& JqKrKpL +33373#'HݨY;$B#F@qKpL +33V,*V JKPX@_rKpL@rK_zKpLY@**$$%$ +333663236632#654#"#654#"tp  S98> V9DD AC0"5( /C0"5' 0"e1>;41>UH$4?#<8_;?#<9`:),LJKPX@_rKpL@rK_zKpLY@ $$ +3336632#654#"tp  S9FG AC5"5' 0"e1>UH$4?#<9`:,,-@*_zK_xL  +"&&54>32'26654&#"=_5$HmI_p"Gm>)9'!2  3^AHc8rfE~b9xBi;)7-HR%Z','l@ JKPX@_rK_xKtL@!rK_zK_xKtLY@!'''$ +336632#"&'#2>54#"!pL5'G,$B]:26 #/#@$2 %S#:'WIBj@."(B&]+FV+V4NR'/,A,*uKPXJJYKPX@_zK_xKtL@!rK_zK_xKtLY@$"**') +7>1##"&&54>323732>54&#"$  D3$C,$B]:1>q#1$ /#$20 "1'WHCk@*&F]5OQ'/+FV+++,iKPX@ J H@  JYKPX@_rKpL@rK_zKpLY@ %$ +3336632&&#"tp !T5  7W4"e4;WV ,%7@4J_zK_xL%% +"&'532654&'&&54632&&#"5M"%N)#6 237nb/X*0A & *1A} y @4UYiD8^`.f@ JK PX@n]rK_xL@]rK_xLY@  +"&547#?33#3267@N8J`@_8%C 7GH)stp  n 6K"^KPXJJYKPX@rK`xL@rKpK`xLY@  +"&546733266773#7#GD>C5!6( 0tp  R UH9'#<9`:e1>2:" !@JrKpL  +3336673uC  "CA"=G"%'@$JrKpL%% +333>733>773#'4667#Vqk r"(O.=2 "F)><<>;7D" &@# JrKpL  +#373#'1n8ww= ;"0@- JrK`tL  +"&'532677336673" .< P ,wu6*""PC)SO" %@"]rK]pL  +#7#7!3nXXrbrb+7@4Jgc_oL*) ++ +"&546776654#7267766333ZLf;E T_)%- E6)) D5Z  Dp)/KLn*66 7& W nI0K'PX@ qKtL@ ]qLY@  +3k(bh+6@3Jgc_oL+*%$#" +566776675&&54677654˜3"#1%2 E6))$#ZLf;E T_n*66 7& W nD5Z  Dp)/KL5 <dD@1JHGWg_O$$$" +D&&#"56323267#"&%3<2K;/%4=2K;- "q5  "q5 K " *@'a_rL       +"&546323!(5,*"-}vM|%")6+$=#P!e@ JK2PX@g]oKpL@gW_OY@ !!%' +7&&5466773&&#"3267;J:nP\6-3 /A",(%?"G, g[U[h MM p El9//x^^ =@:Je_wK]pL %# +#76677#7376632&&#"3#!,> ^^t`5]"7<%.  6&@x <:3ky^gq'-pk)1B8|G+@@= JHGc_rL! '% +!+.# +%'#"''7&5467'766327'2654&#">18)=G> ?H>23->I? ='77'&66|= H> ?F?35,=B6&'66'&6+v>@; Jf  eoK  p L +37#737#73333#3#{{{`^8c{{{lWCWm WCWlIKK'PX@]qK]tL@a]qLY@ +33kkkgrr 2>:@79,J_qK_xL" 22 +"&'532654&'&&5467&&546632&&#"6654&'6Q"W/0-#+9?6-6_=1Q&(D+'(*v:$;g)-)) m"!G11F22E$^ 4^8D17K'5 *+ ,(m  3dD@(W_O      +D"&54632#"&54632#'#$##'#$$m%(#+%(#+1$=edD@Z.:/;Jgg g W _O&%8620-+%=&=$$  +D".54>32'26654.#"7"&546632&#"3267Pc67dNLe96cPRP-Pm?VNMgfe0\CA:2+;A9B92 6cPLe96cPPc6@NY?nT/NYYNJ~gCg;CUIMR E Jm#K&PX@ J@ JYKPX@c_LK&PX@gW_O@+~~gW_OYY@## +"&&54>32373#7#72>54&#"0(A-*KMH0! &m:0!ME-,-J!24/F#6#,2  0+7'57'57c]Bvc]B, L2 L5y%@"U]M +#5!5kk.I1$2;idD@^-J ~g  ge W _ O%%;953%2%210/.(&$$  +D".54>32'26654.#"'32#'#532654&##Pc67dNLe96cPRP-Pm?VNM6RL0t[_>2''#,1 6cPLe96cPPc6@NY?nT/NYYNTF@/7 ¨' # WgZ&dD@U]M +D7!Wbb'9dD@.gW_O  +D"&&546632'2654&#"4N--N44N,,N4 ,, ,,+L11L,+M11L+](#$))$#(5r 1@.ee]pL   +3##5#53535!]kkkkkk8U2@/  JgU]M$' +776654&#"'66323802*21$Q5=G>,?Qk'1(M>/*:4!.bHT+M@J$JggW_O ++ +"&'532654&##732654&#"'6632%E>"#2>,(0.(#J,2=>,(#7X ^NI/33  -1>q^ %dD@Jt  +D56673q< )21^ U#(,(Q" HK/@,J~]qL&" +#"&&54663!##\)>\37cB'OQ .l[_n.@4 3+:dD@/ JW`P  +D"&'53254&'73( !2&/Z(IR(K# (%2D[K &@# JU]M  +7667'73) @2e[5(QyTSmPKPX@c_L@gW_OY@+"&546632'26654#"AC*S=F<)G- %!mJB:b;O< KC*P-B 4.B6,  0+%'7'7'7'7K]BxcN]Bxc,L2 L2 G'o'{~'wb~3+b3+GQ'm'{~t`~3+`3+/<'u''a3+a3+B#" 3+8&$C3+d&$v3+b&$Jf3+|&$Qt3+Q&$jD3+8&$O3+?@<ee ]oK] pL +#!#3#3!7#3#<!'q$k>'||})%<&&z#@&(Co3+@&(v3+@&(J>3+@&(j3+&,C3+&,vb3+&,J3+&,j3+ 7@4e]oK]pL ! +3#7332#'326654&##3#=ED@]-4Rj4I@8%tt#|+m}WROK|&1Q3+<&2C3+<&2v3+<&2J3+<&2Q3+<&2jw3+I? 0+''7'7KIIJ?IJJK1$.<@9 -J H_wK_xL&%%.&.%+( +'7&54>327#"'7&&#"26654&'|K;0-]bY<3J9,[bW="#'1Q9Uo7 A:X{?H@E"!J_qK_xK_tL86&$  ?? +"&'532676632#"'532654&'&&54676654&#"1-+ |}gw87 )0ro[6A"2-&&*8- &')(/| /Sv (/MjgXI?I  B2Ngu!":&2:&$27S/,A&DCb,A&Dv,A&DJ1,G&DQ?,A&Dj,AE&DOx,O,/9IXKPX@  ",)#JKPX@  ",)#J@  " ,)#JYYKPX@%h  _zK _ xLKPX@0h _zK _zK _ xLKPX@7hrK _zK _zKpK _ xL@B hrK _zK _zK  _ xKpK_ xLYYY@#;:CA:I;I7520+*'%  // +"&54>323736632##3267#"&'#7#32654#"267654&#"CT$C\72<\ M)OU.;%S!&Z05F\FUK36*+? "."$ eaHh<)'F,TAVh':o.G$-F5#."..'/+FV+/',,&Fz,&HCV,#&Hv,!&HJ%,&Hj/&'C&'v&'J&'j{#5#1>@; J Hg_xL%$,*$1%1 ## +"&&5466323&&''7&&'77'26654&#"?b7;pN2=q+b :!;t%^(!GnF$5#%)6& +[JMP%)O@H7  [$@J44yJQn?n:Z."0:X.'-N&QQF,&RCV,#&Rv,!&RJ%,;&RQ3,&Rj5lU A@>geW_O      +"&546325!"&54632'(())(())#')!!)'#kk#')!!)'#&<$0<@9 .J H_zK_xL&%%0&0&*( +'7&54>327#"&'77&&#"2>7645ZE:#$HmI@/"G) "GmJ8- $1M"025H4IHc8+634KE~b9 '@I! m)?I 6K&XCg6K&Xv6K&XJ66K&Xj;&\vv'+B@?JqK_zK_xKtL#!++&& +33632#"&'#2>54&#"!ԓ  ?KDV=\>2> #/"% 0"! LSbe3234677#73733##7#72>54&#"D[=]>2;KL{nG/  #&; % be7ve?* 4,aOOaG 1w,DL #+@a3++@m&(Lr3+,&HLY@&(ML3+, &HM3@&(N3+,&HN@&(P,,.8T@Q#$Jg_zK_xK_tL641/(&! .. +"&54>32##32673267#"&546732654#"`x'MrK[\2/+F00; &2:3(BUK35) hkEd;TAVh ,4o &B"V4+-?F5#."<_&(KC3+,F&HK*<&*J3+ A&JJ.<&*M3+ A&JM<<&*N3+ A&JN<#&* A&J&:&+J3+)&KJְ3+;@8  f  eoK pL +3#73733733###37#nVWWXnAA\ a____a4X#"f JKPX@!fqK_rK pL@fgqK pLY@""$& +3#73733#36632#654#"|ON I1FG ;=4!7' *HaOOa.K%.UH$4!#<9`:&,Q3+&'Qm&,L3+&'L&,M3+&'M&,PJ?&LP &,N_3+.&,-i&LM)c.&-J3+&)J#&. S#i&N :i"%@" JrKpL +33373#'tߨY;$"x?#&/vd3+&OvIְ3+#&/ &#F&O =&/% а3+&O%X&/N 3+&ON 3+ *@' JoK^pL  +37'737322lG4E4*c:Q&cR}d $@! JqKpL  +37'73702mS@4qCc;c=&1v(3+6&Qv#&1 z#),&Q =&1K3+Y&QK=&Qf. <@9 J IdoKpL   +"&'53267##33>73c.(6= G @v*1->ACK6me),#m@JKPX@_rKpK_tL@ rK_zKpK_tLY@ ## +"'53267654#"#336632 3"- K5"5' 0to U9FG M .P v (+d#<9`:"e1>UH$44S0<m&2L3+,&RLY<&2M3+, &RM3<&2R3+,&RRW<'@JKPX@#e _wK _ xLKPX@-e _wK ]oK _ xLKPX@8e _wK ]oK ]pK _ xLKPX@6e _wK]oK ]pK _ xL@3e _wK]oK]pK _ xLYYYY@!''  +"&54>32!#3#3!'267&&#"E-]b#.!'- '_*.O9 B {TQ||} 5^yEGJ,Y,'1@KPX@ %J@ %JYKPX@#g _zK _ xLKPX@-g_zK _zK _ xL@8g_zK _zK _ xK_ xLYY@32:82@3@/-*(#! '' +"&54>326632##3267#"&'32654#"26654&#"Yn"FjH4M#`@\[3.+F0+X;5[RUK36*'6!% .$ pdHb8(&$*TAVh ,4o#$!&F5#."3+ &]K7@4J_qK_tL  +"'532676632&&#"#3"- dP"9' ! .P v (+pcO m ' 2R15"I@FJe_wK_tL "" +"'53267#?6632&&#"3#F3"- [R\ cP"9' "kk` -O v (+I')cO m (p<2R1 (2V@S.J  ~gf pL *)$"((    +56673&54632#'#2654&#"3'&&'#w;<=e C64HLS6   X6%!  6>>5-I ;,Z "8HKPX@ / 5 J@ / 5 JYKPX@6 g h _zK _ xL@> g hrK _zK pK _xLY@.:9$# CA9H:H4321-+#8$8""    +56673"&54632'2654&#""&&54>32373#7#72667654&#"L<<>K6CC64HH4(F,$B]:4AqtnK4%$ /#X6%! >66>>57>Eh'WHCk@,$FG$-w0K+$.#-+FV+V&vi3+,O&v91&v3+7&v#&6 #,&V q^0dD@% JU]M +D5>73#&&'q653a-7^ 57#T .'^1dD@& JU]M +D&&'5366733a-7j64^#T .' 57o^&dD@U]M +D7!o.^gg^1dD@&W_O  +D"&&547332673-;EUK&1\ ]^'=! =#OVf" 'dD@W_O   +D"&54632 */*,+f!*,!'0]E 9dD@.gW_O      +D"&54632'2654&#"6CC64HG5]>66>>57>E8dD@-JW`P  +D"&546733267>2:;.a#: &4+1C;#Vo]4dD@)Wg_O"""" +D66323273#"&&#"oG3 .'TG4 -' ]OH4PG4q^9 9dD@.JU]M    +D5>73!5>733(( *43(( *43^ 46(,( 46(,(^ -dD@"JU]M +D5>73  >&^?? '\/m6F !MdD@BJWe_O !!    +D56673"&546323"&54632;! =""&!$#"&!$# V !N'9%(#+%(#+C&$ S 3+J> 3+$&(nS 3+$1&+pS 3+$\',S 3+$&2TS 3+ ('<S 3+$/'Se 3+(F&T8$c%>;K2PX@]5K6L@]5LY@ +3!#}|@(J=+<#>@;e_=K_6L  # #"!  +"&54>32'2>54&#"'73R/_`.\X7Q5A>:T5E XK[}H5]wBCT;bu:CS||,. :JK2PX@ 5K6L@ ]5LY@ +#3#&&7<mD $ 3])&SRu01' eK2PX@ e]5K]6L@ea]5LY@   +7!7!7!{XF@N||||}}<2>K2PX@]5K6L@]5LY@ +3!## }}6Lg3A P@ JK2PX@]5K]6L@a]5LY@ +#7%'7!!! #vr|}RZ7[<<"z@ JK2PX@%WW]5K ]6L@#UgW ]6LY@"! +7.5466773'6654&'Yg,[eUg0Z@Oa5U[6: mGg9dAXXBf<n-\U2DaR1B;WU#NK2PX@h5K6L@h5LY@###!! +!7#"&&5467733333266773##.Qf/.167PP)@- 26 -5W30  0({=:s&,j3+[&<j3+,^&{S! &S)&S&/w&S:FF&T,^,$4KPX@ !JK'PX@ !J@ !JYYKPX@_@K`6LK'PX@8K_@K`6L@)8K_@K`6K_6LYY@&%/-%4&4$$ +"&546632366733267#"&'#72676654&#"ATAtL58|1! *.> E(5  /" `fi`0$, vC t &+ 1wG7@'/*FS(]Y,L@I *Jg_?K_6K:L'%!,,,# +6632#"&'2654&##732654&#" xrdmZR;H6`K5D8<;7-4>$#5U6hg[PV` SC#UN2 ]O80*vA>!%2Ey:""@ J8K:L+6673366773 ,!D &y05>{>'P##S&XCY"/9@6*J_?K_6L$##/$/ ""+"&&5467&&546632&&#"'26654&'L^,g *:c<>_*:$E#+$ 4>DI"4+6654&'&&54667##7!&:+FIQ\2t}fO!0'"8!0+&E ^L\GmYR}dV++1$4'1N(),xJKPX@_8K6K:LK2PX@8K_@K6K:L@_@K]8K:LYY@ $$+654#"#336632v5"5' 0to U9FG t/#<9`:"e1>UH$417'9@6e_?K_6L #"' ' +"&&5467>3236654&#"267#FT% KsW]^ Iva,B,@ :c>)t=gSrk,f>dZ&@"&/sDpsM<)1/?"+@(J8K`6L +"&546733267AMKL*$B 7G "a # n i"$KPX@! "J@! "JYKPX@~_?K6LK2PX@~_?K6K6L@ ~|_?K6LYY@ $$+"&''&&'#'&&#"'6323267:9  x+   ,2TR 2 - CBbE>-. x \V:)r 2/" :JK2PX@ 8K6L@ ]8LY@ +336673vDAU">MÍx-G36@3 Jg]7L33'%$"+6654&'&&54675&&54667##7!#"33#"&8,JDbO.5*G-#9p %MB)q2"aZ+,"8!0+&E"[?V] 8',9# mg 3&PgE2'($3'1N(,,R."KPX@ J@ JYKPX@]8K_6LK2PX@]8K6K_6L@ ~]8K_6LYY@ +"&5467###77!#3267@D6\]u`Au5%: ?;  NH(p# j ',!<@9 J_@K_6K:L!!%$+>32#"&'2>54&#" o;iXdg=rQ*@ !0  +4RyAu_b_"&K)]3LP+0MO+,G,#)@&J_@L##%-+6654&'&&54>32&&#"&8+CL$HnK1L-6-B#0'"8!0+&E]Q:wd=p <\/0/$4'1N(," 0@-]8K_6L  +"&54663!#'26654&'#"`tJl%@`;)9 :J# qhcWmM2KRG,x?f: 2Bi8d."5@2J]8K_6L +"&547#77!#3267@N8]m8%C 7GH(p  n :<"$@!8K`6L +"&54673326653ia??E,E& #Dq gS%) LSKrC,,%7@4JH_@K6K:L#!+7&&546776632>54&#"3Xq^R`9A)!6aZ\bRh/H9H"" uemES9tF5;`br`g[_ Ll7&1'/g,"KPX@!J@!JYKPX@_@K_:L@8K_@K_:LY@""%$$$+'&&#"'6632733267#"&''84=> $ -?? % t EOY!tFL:1@.J7K8K^:L+7&&546733>530md??-*=Q**O]0 gM%) &+r ^TjC,"/:@7-J~8K`6L+)#" //+"&5467332677332>54&'3#"&'VTF2$:!&& 0! :vY;> L mWcO5y|:'-@:!5Xj5(D G!zp6--6/&j:<&j,&RS:<&S,&S@&(j3+R&KPX@ J@ JYKPX@ e]#K_+L@$e]#K$K_+LY@&& +"&'5326776654&####7!#322" ( &rF}v[] /Q | !."  L~~YH:AR&J&`v3+<"F@C   !Je_*K_+L ""+"&54>32&&#"!!3267B;Z|P>e/:"L*6P8CI(T)\ ?u]5z0Q0|JM&6,&,j3+c.Q-"+KPX@ JKPX@ J@ JYYKPX@ g]#K_+LKPX@*g]#K]$K_+L@(g]#K]$K_+LYY@+)%#"" +"&'532667667!32###%32654&##($ (&E,:!Ti2||('!CR:G;//5 |77D2X9|yLLy+U]#C1$(v3@0h#K] $L$! +333332###%32654##:;:*woCCt>D:^5iX}z<|B/OR-@*e]#K$L%!+3#7!#32#7654&##}n_` )1.jFL~~XN9 #&v3+&$!3+0 )@&#K]$L +7#333#-}}-L68$B 1@.g]#K]$L !+3!#32#'326654## !~q'14=^5|oYxx|2Sc%>`0 3@0Q]#K]$L  +367!3#7!3#`F?M@v}[G--cb?MNnNC@(,@) J#K$L+#333##=.GG䐚JJ}MNN_^J+P@MJ~g_*K_+L%$ +++"&'532654&##7326654&#"'6632Ia%.e0J]RJE>0U6...W+59yXB^2rYKL 5>1'v*(") f%*.K-XX V;ky$@!J#K$L+3333#667'H uG  &a%6P,X2&$|3+ %@" J#K$L +333#G䐥JN_pKPX@ J@ JYKPX@]#K_+L@]#K$K_+LY@+"&'532667667!##($ (&E,}('!CR |77D6LLy+U]#u0+<2lg3<&RZ70@- J#K`+L +"&'5326736673v83-1K  *I]  6" *' 5 Hc2<q;0 MK'PX@R#K^$L@a#K^$LY@ +7!3333-#}}~fFLZ+@(Jh#K$L%#+!#"&&547332673z:$Y-,Q375K&G#DE:   @@6 %@"#K^$L +333333}}}}LL60SK'PX@R#K^$L@a#K^$LY@ +7!333333-}}}}~fFLLR 1@.g]#K]$L !+3#7!32#'326654##|A:!~q'14=^5L~oYxx|2Sz 6@3h#K]$L    ! +3332#!3%326654##:~qi"4=^&oYxx6|2S +@(h#K]$L !+3332#'326654##:!~q'14=^5oYxx|2S E!F@CJe_*K_+L !!+"&'53267!7!6454#"'6632;^)2Y&Ui~+E-'/n:9X{ \Z| x@v\6'KPX@!f_#K _+LKPX@%f#K_*K _+L@)f#K_*K$K _+LYY@!''  +"&5467##33>32'26654&#"pzA;W[z})UQ3V366)F3: {  POyYNSfBO5^yEGJj8@5Je]#K$L'+#&&546633##37#"=-3@`헕:95)B=B=AI7>76632'26654&#"\n;Z<$mv1MJ-F0R7HI"Fl;#8 /'  ryIe~  'I< 4`T>yb:y@c2#('42;, ,,?@<Je_,K_+L! '% ,!, +"&54>3232654&#"2654&##py'LqKXmC3#42ib*/!+C0)-/7G3 sjD|a8LG=:  30.P1W""@*! ;1 ,+7@4()J_,K_+L&$+++"&&54>7>54#"'663232672V4!8C"0- ;">"$%c72S1 7H(/1'*R#)V D9-=( *hA6.=(o,9!.I@F &Jgg_+L#"*(".#. !!+"&&54663236454&#"'6632'2667&&#"I^,^L4%b[ t L_UPx>b7 +nY+0,,H,GKPX@'5 6J@'5 6  JYKPX@%   f_,K_ +L@-   f%K_,K $K_ +LY@!DCBA@>:831,*%#  GG+"&'7326654&#"566323733>32&&#"3267#"&5465##7#8!   .> $+QgQ11MHlH!   .> $+QgU//JGl uEl9//wkh Cl>uEl9//whh Bh< ,)J@G#Je_,K_+L ))+"&'532654##732654&#"'66328`![54JVV=>?!*!Q(#0j=8Y3I6)4 y)9g$k;1;> 5.Rd6K"X6r &X$ c" %@" J%K$L +3373#u6רꇥ}9" Q,zKPX@J@JYKPX@_,K_+L@_,K$K_+LY@ +"'532667>32#&&#",*"0@bJ2Zo\(1%!,? y/N.EfB! .ZC2XC&"'@$ J%K$L+3333#667###su<ύ 5"}&Fo)@ W" '@$f%K$L +33373#7#u-,t/0",,R),Q',S,,FV,P;"\,'5@2 J'I_%K'L+7&&54>773>54&'3gl"M}\,,ejGc/DF,<#*.;!*) |Y6p_>{[1jaD ^F*>D2C.?B-?D"[4<K"$nK'PX@ #J@ #JYK'PX@R%K`+L@b%K`+LY@$$$$'+7&&'##"&5467332667733267x/R5GF>C5!6( 0O  C /,6UH9'#<9`: PB"+@(Jh%K$L$'+!7667##"&546773326773; F,CJ:2= t- %UH9v#>ec>4x"*lKPX' J' JYKPX@%K`+L@%K$K`+LY@%# ** +"&547332667733266773#7##"&'#DD AC0"5' /C0"5' 0to U98= W UH$47#<8_;#<9`:e1>;41>4<x"5yK'PX@ 4 J@ 4 JYK'PX@ R%K`+L@ b%K`+LY@55$$$%' +7&&'##"&'##"&5473326677332667733267/R58= W9DD AC0"5' /C0"5' 0O  C 1,8;41>UH$47#<8_;#<9`: -j""?@< Jg]%K_+L"" +"&&5477#7!6632'2654&#"eGV& +>04!UX2rL.. )#+ *H."6pU?0[:g/ ! %4"$p JKPX@h%K _+L@!h%K$K _+LY@$$  +"&&54673663273%2654#"HS#B02CV1itt`#,=$" *H- 11NC3[: "],%:  3" 8@5 Jh%K_+L  +"&&54736632'2654&#"HU& B04!VW2rL..!(", *H."64U?0[:g/ ! %, F@CJe_,K_+L   +"'53267#736454&#"'6632J3<%1J1!-'$Q57Z7A { 2Cc 4)l,`O`^,$sKPX@!f_%K _+L@)f%K_,K$K _+LY@$$  +"&55##33>32'26654&#"_ie0u-^EhFaf!Dh<"5$- ob"@c9tdE~b9xBi;)7-HR%Z4"'KPX@ J@ JYKPX@ g]%K_+L@$g]%K$K_+LY@ #!' ' +"'53267667&&546633#7#"37#"@,  &!,@l@s, $>->$0" y*) 6/AR&)>" ,9)%!,&Hj#.@! JKPX@+n f _ %K$K_ 'LKPX@* f _ %K$K_ 'L@( f  g$K_ 'LYY@&$ .. +"'53267654#"##73733#36632 3"- E4!6( +{NN I1FG G .P v (+F#<9`:HaOOa.8%.UH$44S0 &v,,"F@C   Je_,K_+L ""+"&&54>32&&#"3#32679\6"FnK1M"-3 ,E-(%?"#N )YHDh>p 97c 1-x ,V?L&'j{@M*,.<K PX@$%J@$%JYK PX@!g_,K _+L@,g_,K_+K _+LY@0/64/<0<)'"  .. +"&&5477&&#"#"&'532667>326632'2654&#"%GV& , (0#.I?"-C5!6( 0to  Tw%{/ UH9'#<9`:e1>4]a%@"]#K$L+3!73# 9}ʗFKPX@#K]%K$L@]%K$LY@ +3373#t9Z"WZ&:C3+=G&ZCZ&:vh3+=G&ZvZ&:j3+=G&Zjf[&<Cm3+;&\C*E@U]M +77!ppE@U]M +77!ppES7dD@,eU]M +D7!7!MTZDDDD8&@]oL +'6673<F#n2 4~8;44"@]oL +667342F#;4 47t@U]M +667322F#;4 47m@J]oL +&&'73 3~9 @18$@!]oL    +'6673!'6673F#n2F#n2 4~8;4 4~8;44$@!]oL    +6673!66732F#2F#;4 47;4 47t*@'U]M    +6673!66733F#2F#;4 47;4 47] %@" JqKpL  +37737'u}EJvv,0@- JqKpL +377777737'7'yA+ A. vogvvgovD\ @W_O   +7"&54632;QQ;&@&RHSTGD8SH &'$8!1AO]KPX@2    h g_wK  _pLKPX@6    h goK_wK  _pL@:    h goK_wKpK  _xLYY@;QPCB32#"XVP]Q]JHBOCO;92A3A+)"1#1!!  +"&54>3232>54#""&54>32!"&54>32%2>54#"!2>54#"DF/O:CI0Ps  FG1N8CI0N1FG1N8CI0Nc    RJ,eZ9NM+e[:6y'T]!y:M 6 L=B,d5M&=0/N,2$D"> ~wW VmICVW;<~<,8v@s  J~ ~g g  g W _ O.- 42-8.8'%, , +"&546632&&#"32673"&546632'26654#":K*Q:-"%3!/sd;I*Q=?E(Q6 %!oCB ` ]QUIAZF }(211_iL5 $%  /3]@Z Jg  gU] M00"!030321*(!/"/  +336673#%"&54>32'2>54&#"7! ~ B~ An;I,F3:I-F* &k;76HR!͆D@&NB)JB%L@&P 00!110\\jC@@ J  U]M +33#5467###!#5!#E^^a[@e5`e fj`/ (*66'P&JK2PX@_=K]6L@a_=LY@''&)+#73.54>323!76654&#"3!2SzR^~@&>G!_THD9M.GA]>-c_M-EtILrP62bMR0P_0}D5;"D@A"JgeW_O  +"&&54>32!3267!5&&#"8Tt;.K\.JuDlN-IV"#:T#L41HN~HHhD C|U%<6%>%>&"GL'u'{*_3+_3+/e&u'*`3+`3+8`''(*_3+_3+M6'X'*^)~^3+~3+$@ JK2PX@5K^6L@b5LY@ +#73%3&&5"&!"e/-+)%''%U/-+)%%'^D0C.B,A*@ '%'NK PX@4 1 p(&"%%&p 5 32e  U76  g  g=<e;:98!g#!?$>"&!"e/-+)%''%U/-+)%%'^D0C.B,A*@ '%'NKPX@4 1 p(&"%%&p 5 32e  U W76  g  g=<e:U;98!g#!?$>"&!"e/-+)%''%U/-+)%%'^D0C.B,A*@ '%'N@4 1  ~(&"%"&%~ 5 32e  U W76  g  g=<e:U;98!g#!?$>"&!"e/-+)%''%U/-+)%%'^D0C.B,A*@ '%'NYYY@}}wwssoobb^^KJ;;-,!  }}~w|w|{zyxsvsvutororqpnlhfbebedc^a^a`_][XVQOJUKU;I;H><7631,:-:'% +!+   E+5#53%53!5353#75353!53"&54632%"'5326553%32#%2654&#"%32654##53!53%32654&##53!535353!533!53!53!53_^5`6B>>BB>>p =6T57,5." ""  +655B$"5`6_5`6^Q^6^6666^6^^66㄄QBCPPCBQ22- )3"',/3--33--3~򄄄5__5555555)d+A@>JHU_O! '% +!+%)+ 354676654&#"66322654&#"6g!++\P*X"(!>!& 8))((6{">1CJW"8&#%%#&)K4"97C]@ZJ~~ g_?K_ 6L888C8C?=3210*(#"  77 +"&&54676654#"566323267.546323#6654&#"!CS&  9$53  7XZq5y`IZ)IA.Ltx# 9 (A$% i4). w:W1^fApFpIl@  9=+##6Zo@ JK'PX@_5K6LK2PX@5K_=K6L@_=K]5LYY@ %&+336676632&&#":s? E"C+# 2H0954&'!VT,#`!w @`@;> L"&&  .' mWA3H(p2 ={f=6--6t@:CC!-M`3<~:'-u&0vP3+V&Pv[8&$C,A,&DC 9dD@.gW_O      +D"&54632'2654&#"G6CC64HG5>66>>57>E<n(:@7 JqK_wK_xL" ((  +"&54>326673'26654&#"E-]bLm &_D ,[W:`9=<.O9 B {TQ50E2 Sc'+WOSfBO5^yEGJ,t(:@7 J_zK_xL#!((  +"&&54>326673'26654&#"=_5$HmIk8)$-M<"Gm>)9'!2  3^AHc8GP. 6O;$E~b9xBi;)7-HR%ZE 1@.JqKoK`xL    +"&54673326736673#lr`_\C5!6( 0 /"@hLUo  T UH9'#<9`:7 L0 7^=qe1>S>.@+  JW_O%& +7>54#"56632!.;B6* SH P.,-4 #@&(Co3+&C3+,&HCV6K&XCgT&(@%% J#K$L&&+3.6733>77>733673#&&'"  Z Cm(-}~B^nƣ:9;#9:'`²Hh@CBM"$(@%# J%K$L$$+3.67336677467336673#&&'  #H /X$JlJz3HXs/8w$N2tYG}`d/s1>@9@6fg#K] $L! +3#73733#32#'326654##>mml!~q'14=^5p^^pDoYxx|2S#(JK PX@'ng]%K _ +L@&g]%K _ +LY@" ((  +"&&5477#73733#6632'2654&#"HU& *su4!VW2rL..!(", *H.$6pbbpnU?0[:g/ ! %*KPX@' ( J@' ( JYKPX@" f_#K _ +LKPX@& f#K_*K _ +L@* f#K_*K$K _ +LYY@%# ** +"&5467##33>32&&#"!!3267RaA;\]c>e/:#K*7P5E;+P6/_ }  NQz2K(|KL,+KPX@( ) J@( ) JYKPX@" f_%K _ +L@* f%K_,K$K _ +LY@&$ ++ +"&&5467##33>32&&#"3#32678]6c0u-]KkD1M"-3 *?-(%?""O )YH"@c9p 9,n //xa 0@-Jf#K$L +#3####3'&&'<\!>Bg 344,L#4." 0@-Jf%K$L +#3#'##7#3&&'=A\'/r[ !"3+DB`dJKPX@ f#K $L@  U  e#K $LY@ +3333######3'&&';\!>BAg 3444,L#4.":@7J]%K ^ $L +33373#'##7##7#3't-|\ *3|0H"Pp:@7 J~]#K$L!+#667'7!#'&&####"7#*{!TEmB >4 =@*_GD CF\R&26&/R"1@. J]%K$L+#7>7'7!#'&&'#77#2Z-5%`  .#| -2 N[-733O?$Lq#&@@= Jh ]#K $L&%##! +333'7!#'&&####"#7667#7#;fB '1 =@(ah A~CF+I=&26$/-K""B@? Jf]%K $L"! +333'7!#'&&'#7#767#7#t-V  .$| -2 NP~0["ҟ33O?# +LqJcSKPX@(93-&#"SJ2HG@2(93-&#"SJGYKPX@1~Wgg W ]MK.PX@2~ggg W ]M@7~  pgg gW^NYY@QO-$%%!,b +&&#"#"&54667>54&##7326654&#"'667&'536632&&#"32632%CAYT4r\EFRJE>0U6...W+5*V62Q#&I3 .'BIrYJM.Rk=G;:.?&!" Q:=Q2$ OKPX@"$5/)!;OJ.HG@".$5/)!;OJGYKPX@+~gfW]MK.PX@2~~gfW]M@7~~pgfW^NYY@MKJH$%$!)R +&&#"#"&54676654##732654&#"'667&&'536632&&#"32632c%*P.PQemOGVV@<>!*!Q(#>" Q#&I3 '+8I6)4KR/1#"E !! LBJT )6g&k 0 &):I:3;= 5.DK"  WUs:<#9@6e_*K_+L## +"&54>32!4654&#"267!E-]b,[5><>cG?gC {TQyWOBO^|bX GJ,,"9@6e_,K_+L"" +"&&54>32354&#"267#=_5$HmI_p"Gmv"')9((;' 3^AHc8rfE~b9P,8B?5 ,0ZO@ JK'PX@_#K$L@#K_*K$LY@ %,+3336677>32&&#"> p9J8%  ,b&Q !O>Z1y&6 2E&,@)J_%K$L#,+3336677>32&#"uC  C1>/% "=<3F$ t Z&a E3+2E&b <&2\,,&R\I<08@5% ,JgW_O0.$" +"5&&54>7663276654&'#"'6632Q=io&NzS ;hm%MyT!.FZ)' )0H(,' '=9wHyS 7sLyR u;S#VyEBT,ML,7@4'#JgW_O&$ +"&'&&54667632'66326654&'#"'P`7oU/- S_5mUD %-/))(6u[T^ %%y]Q^ nD"5(&AO&J@"a|@yP/O0 A _ J~   ~gg _ *K  `+L$#][TRMKEC@?<:42-+#a$a#!""+663233#".#"7654&'&&5432"&&54>32&&#"32677332>54&#"'6632#"&' F5'EF* ':/.!9  /!Nj5)PyO3M60!$=-:3')) ,3K22#0#"S">^3.YU4X"R>9?% $!MKN]~Gn 3Xt@KOA@ n?rL\P)"&+5o#^|@yN.M/ @ \ J~   ~gg _ ,K  `+L%$ZXRPKIDB?>;931,*$^%^#!""+663233#".#"7654&'&&54632"&746632&&#"326773326654&#"'6632#"&'_ G4(EF* ':0.0 9  ">RfE{O+<.!%4(#')&8$8*4S1AxP8W'O=9>8%  "%8e^yU o -FM43?j@+. j*XDsX0!.#T&M3+BM&N<6@3 J_*K_+K'L%%'+7&&54>32&&#"32672ux;Z|P>e/:"L*32&&#"32674C^&JlE1M"-3 /A".(0O `^If:p El92, 3/v 0+'7'77'77''?X"a#Y>Y$b#$P<:*7*75310.,))'%#"  ""(+D6632#&&#"6632#&&#"!6632#&&#"6632#&&#"!6632#&&#"6632#&&#"!6632#&&#"6632#&&#"2205'% 2205'% 2205'% |2205'% 2205'% K2205'% 2205'% 2205'% Y+45*,35*,35*,35*,35*,35*,35*,35*D#,5>GVdD@K ;:10,('$#"75JeU]M???G?GCB+D'6673'&&'7'7667&&'5&&'57&&'7%'6676673 !/ '!40"U'"$P&X&)^$)^$&X&50(y"$P0"U D !-&X&)^$"U'"$P0(!5 !/ D D !$P0"U' !40')^$&X&0+%JK PX@+n~    h#K$L@*~    h#K$LY@++*)('!   +"&&54673326737#667'#3333RW!,'-7 9gXvG  H u|,F&  $ (35O+,P,X2&a%4<k 3#JK PX@)n h `$K  ]% LK PX@+n _#K `$K  ]% LKPX@* _#K `$K  ]% LKPX@( h `$K  ]% L@, h$K `+K  ]% LYYYY@!33210/+)%$  +"&&54673326737#7##"&5467332667733jRW!,'-7 9gnjY R5GF>C5!6( 0]{^,F&  $ (35O+X,6UH9'#<9`:K9@6fg#K] $L! +3#73733#32#'326654##nIGyx!~q'14=^5|JJ|LoYxx|2S8({JK PX@%nfg _ +L@$fg _ +LY@" ((  +"&&547#73733#6632'2654&#"GV& DMN14!UX2rL.. )#+ *H."6Nk9(Cz9(=!6X',.z@"! JKPX@_%K_+K'L@!%K_,K_+K'LY@..(&+$+336632'#"&'#27'76654#"!pL5'G,6&-O/26 #)P#@$2 %S#:'WI;ue#I3M."(B&]C3:$`1V4NR'/> -@*e]#K$L +3#73!#3#>JI?%=#|+||" -@*e]%K$L +37#737!#3#,DD0i+syjsE$v@JK2PX@#g]#K$K_'L@ gc]#K$LY@ $$+"&'5326654&#"#!#6632*I@%6P+GB$ 9*,,WF*&Ny  =iCPT|AiKFk@ "!I@FJg]%K$K_'L !!+"&'5326654&#"#!#6632@56?*,'ti#[_@p  9[15>"yy}b7ue>05@2 Jb#K$L +7###3333-#JJ.GG`fF_^}MNN<,DKPX@5 C J@5 CJYKPX@)f   Q _ ,K _$L@1f   Q%K _ ,K$K _+LY@DDA?:831%%%#+7&&55##7##"&'7326654&#"566323733>32&&#"3267->LT//JGlF!   .> $+QgQ11MHlH!   .> &"#CĿ eYBh<uEl9//wkh Cl>uEl9//J&[ ,&[0.@+ Jb#K$L+7##333-WJGaF_N<c".@+ Jb%K$L+7##3733p/D}9u6רJxC "4@1 J]#K^$L+3377373#'#7'GF&F hѣ; F$!JW<8U,}"4@1 J]%K^$L+3377373#'#7',u6+ A Z쉡=A%9"16i~R03@0 Jf#K$L +3#73733#3#o??AA䐥J |BB|N_c` JKPX@nf%K$L@f%K$LY@ +3#73733#73#}OO>רꇥ}9HaOOa R  +@( J]#K$L +3#7!3#t|4G䐥JL~N_$" +@( J]%K$L +3#7!73#h]46רꇥ}9p 00@-fa#K$L +7###3333-AA;;~F4<k"0@-fa%K$L +7#7##33733.~/0u-,ZnC"XX -@*f]#K$L +333!###;;,|AA~4" -@*f]%K$L +3337!##7#u-,:\/0"pN&z@JK2PX@$g]#K$K_'L@!gc]#K$LY@ && +"&'5326654&#"###!6632v*F @%6P+K> 9}}D.,VF+'Pv  >iBTPLAiKIj> "#L@IJg]%K$K_'L ## +"&'5326654&#"###!6632@56?*,'[\u4#[_@p  9[15>N"}b7ue><2?\@Y/*0Jc_*K_,K_+L;9-+%# 22 +"'#"&54>32&&#"27&5466323267'>54&#"6O70;z-]`$:)'8T8+"32&&#"7&&5466323267'6654&#"#=6#bi#GpL<#!)9$" &M;>O/  %o" # vhA{b: k+DP$:79 8^7CF*O?fR&B(+<&&[-,,&F[R0Z *@'a]#K$L +7##7!#3-|cFL~~,<u,8m@&7JIKPX@ a_%K$L@  a%K_,K$LY@88(%$$* +7&&5477654#"#654#"#336632366323267/)0"5( /C0"5' 0to U98= W9DD   C 21"#<8_;?#<9`:"e1>;41>UH#4 [<2:""@J%K'L+7336673E3F  3"=<"%/@,Jf#K$L+37#737333#s?~!G~:"/@, Jf%K'L+7#733366733#E~~F  ֆp"=<"p0/@, Jb#K$L+7##3733-JTtKWF uU<D"/@, Jb%K$L+7#'#3733X.N=n8wACħ R0$1@.R]#K^$L +7!#7!#3332-#|a}|fHL~~2L-<"(|K'PX@ 'J@ 'JYK'PX@R]%K`+L@b]%K_+LY@(($$' +7&&'##"&54677#7!#32667733267/R5GF'zy+2#7( 0O  C /,6SC9pp#99`: Z04@1Jha#K$L%#+7##"&&5473326733-:$Y-,Q375K&G#D~FE:   @@P<V"4@1Jha%K$L$'+7#7667##"&5467733267733/ F,CJ:2= ZnCĔ. %UH9v#>ec>XZ7@4J~]#K]$L+!#7"&&5473736673z:,"G 1U475="G!+D E:   : @6PK" bJK PX@p]%K^$L@~]%K^$LY@ +!7667##7"&5467737366773D $CMP#C"- t,!zhUH9v#. pj`M>f+@(Jg#K$L%#+336632#76654#":$Y-,Q375K&G#DE:  @)K +7KPX@ ()J@ ()JYKPX@%h_*K%K_ +LK,PX@*Xh_*K%K_ +L@-~Xh_*K_ +LYY@53.,&$ ++ +"&&5467&&54673336632##3267326654&#"au5MX{ )ff4AK0W()b-9b;"!*L: K}L  >86%eSp LU~1,!-N |,)3K@H&'J~g_,K_+L1/,*$" )) +"&5465&&5473>32##326732654#"l`xBIh NuN[\2/+F0+YmUK35) hkA/1( !Cl@TAVh ,4oF5#."<0 -9KPX@ )*J@ )*JYKPX@* h _*K%K_$LK,PX@/ Xh _*K%K_$L@2  ~ Xh _*K_$LYY@750.--%$# +7.5467&&54673336632##3267326654&#";.=I"MX{ +ff4AK0W(BY*]-9b;"!*L:Nj=  >86%eSp LU~x1,!-N <|,*4H@E%&J~ga_,L20-+**%$# +7&&55&&54733>32##326732654#"-=HDFh NuN[\2/+F0"D)';UK35)cR=/1( !Cl@TAVh ,4o5#."<,&$3+ &$!g@JK2PX@]#K$K_'L@c]#K$LY@ !!+"&'5326654&#"#33 %I ?(6O,HM(5G0W8$Ly  J]%K$K_'L +"&'532654&#"#373;5@Q9:" &t6بEN"Gm  ^\4E" lY?tY40KPX@ J@ JYKPX@]#K_$LKPX@$~]#K_$L@(~]#K$K_+LYY@%'+7###"&'532667667!3}('!CR8$ (&E,}LLy+U]#|77D<u,#K'PX@ J@ JYKPX@_,K_$LK'PX@!_,K$K_+L@(~_,K$K_+LYY@##'#'#+7#&&#"#"'532667>323\jw\(1%!,?/*"0@bJ2ZX|.ZC2XC& y/N.EfB! ]j@ JK2PX@f#K$K_'L@fc#K$LY@ +"&'532677##333 *E =+AN4A;; T^R W"A@>Jf%K$K_'L +"&'532677##3373%=658+0u-,ty  FC"z{06@3~f#K$L +7###3333AA;;|4<v"6@3~f%K$L +7#7##33733]jw/0u-,]|"KZ07@4Jh#K]$L%#+37#"&&547332673#H!$Y-,Q375K&G#D,HE:   @@6P<B"7@4Jh%K]$L$'+37667##"&546773326773#Jw E,CJ:2= t)>2 %UH9v#>ec>06@3 J~#K$L+7#>7####3333MxG  F  |RA@2 ) 5A@<'#6@3 J~%K$L+7#667####3333jt<ύ 5s^&Hn(B #~J,&$$<3+,m &D$Q&$jD3+,A&Dj,O,z&($3+,a &H$!u)C@@Je_*K_+L! %# )!) +"&54>336654&#"56632'267#"nk&Zp4EF5X))b=ax7;Z{NJdKg\) eS2VB% HQ~JO>v\6~aN?1 ,%C@@Jg_,K_+L" %% +"&546336454&#"56632'2667#"\[3.+F0+X;`x'MrB6*UK TAVh ,4ohkEd;j"<(5#.!u&j@3+&j&j3+&jJ&j3+ &j7@@=J~]#K_+L +"&'532654&##77#7!8`&?AKZ6HKS]M   <;(3d}` \POl6 "@@=J~]%K_'L +"&'5326654&##77#7!8[%(\";L%=BBU_L)D)/=ir`gLWGm&L3+6K&XLj&j3+6K&Xj<&2jw3+,&Rj<_,,`<&_j\3+,&`j E&j3+&jm&L3+;&\L-&j)3+;&\j&R}3+d&\R+Z&jI3+PB&j0> (@%a]#K$L +7#!#3w-dF|*<" (@%a]%K$L +7#!#3b/th@nC"yz&j3+4&jj>&x\i"U@RJ  ~ e]%K$K_ 'L  +"&'532677#7#737!#3#3k/ * ,DD0i&]u"1'syjsL`UI@F J~#K$K_'L +"&'532677##3733/ ) LTtKM+^u"1' uU`UD"I@F J~%K$K_'L +"&'532677#'#3733J/ ) K=n8w?&^u"1' `U5@2Jf#K$L +##733733##9pzcKxzoT.| | D"5@2Jf%K$L +#7#73'3733##'1]aY8wada=p֣pܧI 2@/g#K]$L      +!"&5466333'37#" p~MW29$)%9N3eePj46|1<*),nG))H@EJ~g#K `+L %#) )  +"&'#"&5466333326773%2677#"/S"F6\`NX/:l"( )0s+ @S% !dRUw? 27g]}#*|@A%#,3'7N@K $J~_,K `+L)(1/(7)7"  '' +"&54>32346773326773#"&'#'2>54&#"[[=]>1>%u'$ !oZ?P%W /!!#/"% la:~mD." A  3?,!T(#0g=8Y3F60* $% !s MJ+g&k;1;?4(-2g]@0X*E@BJ~ea_*K$L**%%!% +7#76654##7326654&#"'66323~-'kp>0U6...W+59yXB^2rYDK Fв  Ru*(") f%*.K-XXI8 1W<2,*D@A $Jea_,K$L**%$!&+7#76654&##732654&#"'66323g/w..V=>?!*!Q(#0j=8Y3E:)1nCz #g$k;1;< 0(.B@?J~]#K_+L'& ..+"&'532667667!326773#"&5467#($ (&E,i") )0ta[oJ('!CR |77D 27g]RS'\Ly+U]#H,2K'PX@#J@#JYK'PX@ ~_,K_+L@+~_,K_+K_+LY@/.+)! 22+"&5477&&#"#"'532667>32326773fa0u-,G!"' !s [H " 27zg]<">@;  Je_*K_+L ""+"&54>32&&#"32667#7!U5if>g&8 F5=_B!AI2E) PO YJ{6\t=IU*H-~YeM,M, >@;  Je_,K_+L   +"&&546632&&#"3267#7!Cj32&&#"33#"3267Ip@6a>9=/M^.Oj.E V22==PD<4`>C<5e &m *R=@Q, I87M0(n,((-v10-& ,(KPX@J@JYKPX@)~]#K_$K_'L@-~]#K$K_+K_'LY@%$#" (( +"&'532677###"&'53267667!3/ * }":CQ4$  6F0y+]u"1'LDKT"|1CB`U,/KPX@* JKPX@* J@* JYYKPX@"_,K_$K_'LKPX@)~_,K_$K_'L@-~_,K$K_+K_'LYY@,+(& // +"&'532677#&&#"#"'532667>323/ ) \(1%!,?/*"0@bJ2ZT&^u"1'.ZC2XC& y/N.EfB! p`U.8&$ V,.A,&D E8&$H~3+,A>&DHN&$S3+,,&DSJP&$T3+,A-&DTJ&$U3+,g&DUJi&$V3+,As&DVJ.b&$' VJf3+,.A&D' EJ1b&$W3+,AL&DWJb&$X3+,AL&DXJb&$Y3+,An&DYJm&$Z3+,As&DZJ.a&$'Mt V3+,.A&D&M? E.@&( L,.,&H K@&(H\3+,>&HH?T&(QL3+,;&HQ3&(SW3+,,&HS>@&(TW3+,-&HT>&(UW3+,g&HU>A&(VW3+,(s&HV>.@&(' LJ>3+,.!&H' KJ%&,H3+k>&'H.&, .?&L <.&2 ,.,&R Q<&2H3+,>&RH=<0&2S3+,,&RS><&2T3+,-&RT><&2U3+,g&RU><&2V3+,(s&RV><.&2' J3+,.!&R' QJ%<n&Dv3+,&Ev<n&DC3+,&ECQ<n&DH3+,>&EH=<n&DQ3+,&EQ.<.n&D ,.t&E ^E.&8 y6.K"&X BE&8H3+6K>&XHPE&Fv3+41&GvE&FC3+41&GC_E&FH3+41>&GHPE&FQ3+41&GQ<E.&F 4.1s&G X[.&< 6;"&\ 3+[&<HI3+;>&\H[&<QJ3+;&\Qb&Bu^^~, >@; J~]qL    +566735>73#&&'K,y .2]310[*3 ; (( _ 24!N ,$^- +@( JU]qL  # +&&'535>73#&&'V4 q ;310[*3<5` 24!N ,$^^Mg$p@  !JK PX@pg]qL@~g]qLY@$$#& +76654&#"56325>73#&&'A) #(,Kc310[*39  ;':] 24!N ,$^^s+x(#JK#PX@ g hqL@)~ Wg` PY@++%$ $"$" +663232673#"&'&&#"5>73#&&' ;(@6)"n1/4[..;;  ?8  -/!D '^L jJK,PX@~dqL@"W`PY@     +56673"&5332673,j -3NFH$//7Ia : (( XM&-0#J[^L UJK,PX@dqL@W`PY@   # +&&'53"&53326734 g @ODG$//7Ia<6TQ&-0#J[^n@ JK PX@pgcqLK,PX@ ~gcqL@*~|gW_OYY@#$ +7654#"5632"&53326733"(!!MFG$//7Ia61"%xOV&-0#J[^s'?@<g g c qL%$" ''$"$" +663232673#"&'&&#""&5332673 9'?8&MPGH(.31 H^66 :3 JG "APy+@(J`'L +"&'532654&'3$ X$OX0>&=7+@(J_'L +"&'5326773/ ) (+^u"1`UNZ&7z.&Wz'.KPX@  J@  JYKPX@'fqK _rK _ xL@+fqK _rKpK _ xLY@! (& .!.  +"&'###73733#36632'2>54#"*4Aq|FFA/'G,$B]S/#@$2 % ,$FHaOOa<+&SCCk@w+FV+L1KO'/?&/{@ JK PX@&pe]oK]pL@'~e]oK]pLY@/-)'&$!5$! +3#"#&&546332#32654##32654&##}!)oiif|RJ1:JPI5ASJcX7C,-QN,# TjJUI^ E5Qg1+1C/74&)BU+KPX@ J@ JYKPX@"]qK_rK_xL@&]qK_rKpK_xLY@%#++  +"&'##!!36632'2>54&#"*4AqA/'G,$B]S/# $2 % ,$Fp56*&SCCk@w+FV+($1KO'/=J2@/foK_xL  +"&547332'26654&##"i|i:LYm1KK#<%4CO `S3Y8Tq9|5,(*bE="&:@7 JqK_zK_xL && +"&547336632'2>54#" bk g&:4FR*B\;!1 A 2!$ j^),C!0j^,e`N/w2MRX/JS$.*L& ʰ3+<o,K@H )*Jg_wK_xL'% ,, +"&54>3276632&&#"&&#"3267B;Z|P.L9!  %(\-3276632&&#"&&#"32679\6&JlE  O@" )A%/A",(%?"#N (YHIf:)\P r El9//x2!bJK PX@p]oK]pL@~]oK]pLY@!5$! +3#"#&&546332#'326654&##}!)oii܃]-4Rj4I@8N,# Tjm}WROKU9@6g]oK]pL  +!"&&5466337!7!'37#"Wk1SW1+)"@U44\;Qi3}6|84)+,n)KPX@ J@ JYKPX@"]qK_rK_xL@&]qK_rKpK_xLY@%#))  +"&&54>32346677!7!#7#72>54&#"(F,#B\:/< p K "2#"$:" 'WIDe<(%0?pG$-w5OQ#)Bk;V&--~H# #3+# /@,e]oK]pL  +#737#737#7!'!}||6!u|+J@G()Jg_wK_xL&$ ++ +"&54675&&54>32&&#"33#"3267}m>O2Ra.Vs/B#V5D@Lc9@qzMD1j1/n iV[a  F<:N.)$f/*&.v5=.+{>>@;Je]oK_tL  +"&'53267!#3#- "'I-F r||BM!<o/R@O Jge_wK_xL,+*)&$ // +"&54>3276632&&#"&&#"32677#7!D|5jg+O9"  %.T+Ba@ DL% O1m [JPC r$8]p8PS~U#1@. JoK`tL## +"&546736673'2654&'9BA1r2 " NE!C49vGM88 Q#0,X'YdY9%*,P!0eKPX@qK_zK_xL@%qKrK_zKpK_xLY@-,)' 00 +"&546776654&#"#31363232673Bdm#6& 3!<\?PD,0=@w UR(V !;[0>)SPIo DBM%~y2B+@(JoK`xL  +"&546733267@Nop"C 7G "   n 3@0JfoKpL +#?#737'7!3#[$WV$J<]$ZY$LV(|(VV(|(VT@  JKPX@_oKpL@oK_wKpLY@ %& +3377>32&&#"#KN^";@, $g=5gt*=" x!"i7@4J_qKrKpL%# +36632&&#"373#'~VM(  $ݨY;$SUW rB#L '@$fqKpL  +3#7333#ALLHHNNA4pTp)KPX@& 'J@& 'JYKPX@~_qKxL@~_qKpKxLY@#" )) +"&''&&'#''7&#"'63273267:9  x+\^  ,2>MLO2 - CBbE>Nx 31O:)r ;+lKPX(!J(!JYKPX@oK`xL@oKpK`xLY@&$  ++ +"&5473326673326673#5##"&'#G\je #)6" [eF*6! Xo"[35E#] VO#"#.L,#G3T06L++0*+/ :@7 JoKpK_tL    +"&'5326733>73##*" H TN rCHR6->ApbN),<_4&5k@  JKPX@_wK_xL@_wKpK_xLY@('/-'5(5 && +"&54>326632#654&#"'26654&#"=-]bGi +[D4S2ef,( = ,[W:`9=<.O9 B {TQ0*'3+P9 %'*+2WOSfBO5^yEGJ,I,(6@@= J_zK_xKtL*)1/)6*6((#')& +6654&#"#"&&54>32663226654&#"7v& ."GmJ=_5$HmIo7(O2-O0u?)9'!2 / & E~b93^AHc8K(##H9)^Bi;)7-HR%Z2"iJK PX@!pe]oKpL@"~e]oKpLY@" &5$! +3#"#&&546332##32654##}!)oiiWi/RXL5P=CR_FN,# Tj4^>Up7zB:X'%5P@M J_qK_zK_xKtL'&/-&5'5%%&(%# +6632&&#"3632#"&'#2>54&#"!VM( ?KDV=\>2> #/"% /"!CUW r7Sbe54&#"'66323267ln:^6.K,(%#C0*6`><[29dB+B&.+4S6,d iPG]9)"u-Q6FW9)"%~,'7@4$%J_zK_xL"  '' +"&5467>54#"'66323267ceV\45=#J %4\1\a,P665(!&M/+O UHBV$ iMA8E+pAn;!.C@@Jg_qK_tL#"+)".#. !! +"&&5467#"&546323267376654&#"&G,{7ETBGI*F@:2  B:5>CC7{ o J&.%@"JK PX@*n~]rKxK_tL@)~]rKxK_tLY@  %% +"&'532677"&547#?33#3267%# MG8J`@_83*Ih !?7#7!32>54&'7!#AZw:&>G!=Zc>@3J/'%*,?+X CrILsR75kCO/MW)Ob#qSHk>E']@ JKPX@_oK`xL@oK_wK`xLY@ '' +"&5467332676654#"56632'os`_\AA:'<2<<H} n['?%WKJ +u &>%LvDPkKPX@ J@ JYKPX@~wKpL@~wKoKpLY@ $% +3'.#"566323:9 08?+:)* u <<!G`,%d@ JKPX@_rK`tL@rK_zK`tLY@ %% +"&'5326773366776632&#"" .< PKP=('!,uu6*""ME ;D u))cSOJ3@0e]oK]pL +#77#737!7!3#!\a#b|}c|}"3@0e]rK]pL +#77#737#7!3#3tJVndK`Xairbyatr7^M@J J~|]oK`xL  +"&54667'7!!#"3267 K~M5:\6F:0X5/b qVOb.a}j0,-/}4"E@B J~]rK_tL  +"&&54667'7!!#"3267Pss]'4W>h%%xHG%dr` IB(B4$ ' n*=@:Je_qK]pL!%% +#77#736654&#"'66323#!ߤ+*("$D.G2vMbg("Dha(J(#%%c+:gP.VaLA@>Je]oK_xL  +"&'5326654&###7!#326e !g,/R2?Bp:O dpS 91,)~~mZ_v6"H@EJ~|]rK`xL  +"&'532654&##7#7!#325ib.FR@A[,R\j x#(ppaIGaj)f@ JK PX@n]rK_xL@]rK_xLY@)) +"&'53254&'&&54677#?33#1aU*m')\`@_00g v@5/ H)stp#  )4$4Q0',JJKPX@_rKtL@rK_zKtLY@ $ +336632>54&#"!pK6FR7fY/J32&&#"32677#737#7!3#D|5jgk]8$H+Ba@ OE( wy %>?1m [J0{8]p8]P)a0~a A,*:KPX@!  J@!  JYKPX@*   gf _zK_ tL@.   gfrK _zK_ tLY@!,+42+:,:('&%$# ** +"&'53267#737667##"&&54>323733#2>54&#"=P%&U//tM3'C)$B\85=qwGO"} /!$/#%oM%/"*$OB?{f<+%FMSB{+CL"#+'@N'''<&*K3+ O&JK3&.Ky3+i&NKְ3+<&2P,,&RP<m&2'LP3+,&R&LYPH&K,3+&K&'=}&'],*&G]R<&*v3+ A&JviKPX@ foKrK_xL@$foKrKpK_xLY@  +"&5477##33332673epA;;fG/2@??k XSV @?F-Sq9JJKPX@_oKtL@oK_wKtLY@ $ +336632>54&#"yU;5R.jԞ4Q?lQ-/'?G\&A1Y=pˡ.Kcp8+0cM&1C3+)&QCi8&$ `3+,A&D +Y&$ ^3+,A&D )@&( 83+,&H @&( 63+,&H &, 3+N&' &, 3+&' <&2 3+,&R <&2 3+,&R H&5 W3+&U P&5 U3+&U E&8 |3+6K&X 0E&8 z3+6K&X .Lo(#@ J  G_wL +7>54&'5>54&#"'6632-\yD Uf\G5/6k2-BJasK54'5>54&#"'6632GG}b7'H(@g>*!(L&.g59Z4?4%;gw#2F1/!  j$3&l"D4;M"A.CdI2%&+K3+)&KKְ3+SIKPX@_oKpKtL@oK_wKpKtLY@ $$ +654#"#336632W0U?K} *gJ6T0 %WAoDs5I/V:!*E,*0?Jb@_#HJGgqK_zK _xK `xLA@21FD@JAJ:81?2?$('* +'667&&'##"&&54>323467736632#"'%2>54&#"2654#"M ) *R4(F,$B]:18%s VA>=mc"" "2$ /#&0* / H  '43'WIBj@.">!  6=:2HB 5OQ'/+FV+V$!'$&!.`JKPX@goK`xL@gdoLY@#")'".#. !! +"&&547&54773326773'2654&#"C`>G9=JPF@^W]^]GI5W4J;gCF*TMM  )2J@LLEk9ieB[O19"I;2<)#2<@9JhqK_xL%$,*$2%2## +"&54667&&546773326773'26654&#"n0S2!#H3.D?^ET1<3+*7!7 tdHiA@. &cc#JIGdrKa3wWHx5W2-3$9@24Jj@ JK2PX@ ]oK]pK_tL@c]oK]pLY@  +"&'53277!7!7!!%- {#(Ih :@b}cZK"w8&$N3+,A&DN@&(z,,&Hz<&23+,4W&R<&23+,,W&R<&2N3+,&RN<&2m,1W&R[m&<Lp3+;&\L-&=@: $JGhqK_xL" &&%" +'>74673632#"&'72654&#" K )0qq/3F/O2:Q %( !0D:) <40>, &,j ,-8KPX@6+JG@6+JGYKPX@ g_rK_pL@(grK_zKpK_xLY@/.53.8/8$'$+ +'667&677654#"#3366326632#"&'72654&#"\@*5"5' 0tp  S9FG $4CeO)F "%6$&8=~#<9`:"e1>UH$4S67IF)l +|@)JGK PX@%ng]rK_xL@$g]rK_xLY@"!(&!+"+$# +'667&677#?33#6632#"&'72654&#"/@* 3J`@^,%5BdP(H !%6$&8=H)stp67IF)l,P'7FO@L %JqK_zK  _ xL98)(@>8F9F1/(7)7#! '' +"&54>32366773136632#"&''2>54&#"!2>54#"Zg$C^:*<  & :4FR@fG<[V)"2$0! X!1 A!1$ iaDh>+%$H4%!0j^=mC2.,4w0KV&-$3MP+/2MRX4RY%%,*P,|" "3+TE@BJ fqKoK pL +#373#'##7#37&&5#'<v\W; dv^w&T&m E.)(599<"+G@D %!JqK_wK_xL(&""%)) +7&&54>3273&&'3267#"'#"x277;Z|P#\!:(T)dA'"#3273&&'3267#"'(U%,&JlE>\G -  %?"#N3G;+<T@If:pxf Fg -@*foK]pL  +3#7333#3>A@?@qp##|+|}h6@3JU]oKpL +#7!733##708^4i3%^rXZ,p~MM~^[ ,9I@F 67J_zK_xK_tL42-,"  99 +"&&'&&'&&'532654&'&&54632&&#"32672<$  %N)#6 237nb/X*0A & *1Asa   )$C/'-y @4UYiD8Y_ %"r"=@:J]rKpK_tL  +"&'.##7#7!327GI " #n'"JN(XXrb 3,5/ qJ)@&  J_wKpL%' +3>54&#"'6632JE2a@.#,M04fCE[,wl6F6N7"(# k"0Q2i/!,)@&  J_zKpL%' +37>54&#"'6632?!2b@.#,M04dCE[-wo5N7"("k"0R1i.Vc)I@F Je e]oK] pL)('&%#! +37#7332#32654##32654&##3#$KL^f|RJ1:JOI5ASJcX7C,-Q noaJUI^ E5Qg1+1C/74&),a "A@> foK _ xL""  +"&54677#733333#'2677##lrRR7777RRKJ322733267#"&'322776654'7##d+0'MrK UL[..xp2+F0+Y:YZ  ;.@$"[BEd;F/Lc qo  K' % > c.i:@7JfcoL  +"&'532677#7333#B*'0= 6VV>?WV;{|;@|'|{j@ "P@MJf _qKrK_ tL  " "  +"&54632"'53267#73733# */*,+3"- 9LK//KK= -Of!*,!'0 v (+ aa2R1<):KPX@ &'J@ &'JYKPX@"_wK_xK_tL@&oK_wK_xK_tLY@+*42*:+:$" )) +"&54677667##"&&54>3237332672677654&#"HJO &XG=]52^QAG"( 6J^ 7;6Q6@CA  "!1"+5>uS\N6+V'r e{m#;fx9GE,A,'7KPX@$%J@$%JYKPX@"_zK_xK`tL@&rK_zK_xK`tLY@)(1/(7)7#! '' +"&5477>1##"&&54>323733272>54&#"GM D3$C,$B]:1>q~(2#1$ /#$D<!/!"1'WHCk@*&F# q]5OQ'/+FV+++H7@4 Je]oKpL! +3#7332##32654##:LLCk}ZCe::T&@G`${=\hUk89P,KPX@JH@JYKPX@f_rKpL@ frK_zKpLY@%$ +37#737336632&&#"3#/KL0p !T5  'G^h/ae4;.4a.4@1JfoK pL +3'#73'33733#7#:A_154#"*4Aq~VM( >2'G,$B]S/#@$2 % ,$FSUW r7!2'WHCk@w+FV+V4OR'/,7@4J_zK_xL  +"&'7326654&#"566321M"-3 .B",(%?""O38]6&Jl p El9//x(YHIf:,)4G@D3'JGg_zK_xL+*1/*4+4&(%+ +'667&&54>32&&#"6632#"'72654&#"1##"&&54>3234667733272>54&#"GM D1'C)$B]:18%(2#1$ /#$D<!/!#0*XEBj@."%0# q]5OQ'/+FV+++,%4KPX@ "J@ "JYKPX@"_qK_zK_xL@&_qK_zKpK_xLY@'&/-&4'4!  %% +"&&54>323066776632&&#"#7#72>54&#"(F,$B]:18VN(  }p K "2$ /# 'WIBj@."$2NZ rG$-w5OQ'/+FV+V,'C@@Jg_zK_xL'&"   +"&'53267#"&5466326454&#"3=R$(G-AJvw;d>F].%KpL'' #9; o;9_E;M':c?B|b:F 3>" &, ,,:,)?@< Jg_zK_xL$")) +"&546632'2654&##73254&#"lKe^qB<.2Du@*;7(m%:I"9 nnhWPC<= 5-=O&p#$g?Em<:06"=@:JfrK_tL  +"'53267#73733#,3"- 9LK//KK= -O v (+ aa2R1 4D_@\)*J_qK_zK _xK_tL65><5D6D.,'%44 +"&'53277661##"&&54>323066776632&&#"2>54&#"=P%&U/h D3'C)$BZ666WN( !Kk/"$/#%a 0"1*WECk@*&"2NZ rVc*]-JT''/+FV+++ A,J,),>@;  Je_zK_xL  +"&54>32&&#"32677#73 v~"M}[0a%.L'AJm f:+\ zq@w]7p <]2}\c?"$1@. JrK`tL$$  +"&546673366773'26654';>!3e+ QE "C4)RN#9 :<(R$R^Y+%,.M (J,/;C@@"6*# J_zK_xL100;1;'%! // +"&5467'&&#"56632366776632&&#"'2654&'=;53  %+5 ,=)"&d ME  C/8S%`!n(%0$!1#( o#p=KQY%$0 6K"'@$rK`xKtL$) +7>1##"&546733266773$  J0GD>C5!6( 020 %.UH9'#<9`:)';@8J_qK_zKpL''$)%# +36632&&#"36632#654#"~VM(    F1FG AC5"5' 0SUW r$.%.UH$4?#<9`:)2S@P%J_qK_zKpK_tL*( 22 +"'53267654#"#6632&&#"36632 3"- K5"5' 0~VM(    F1FG M .P v (+d#<9`:SUW r$.%.UH$44S0? ?@<f_qKrK pL      +"&546327#73733#+/*,+1KK./LK1f!*,!'0aa/?"" "@ JrKpL  +#77'7!(\CJ8[DJH*>*HH**H7@4JghqKpL"#"" +3&#"#663233273#"'JJ JP2 BL !JO1 ?[RD:5RE>@; Jg hqKpL$! +3#"&5463233#376654#"_>@9G6 9S__>]"$;):=  yMq#F+@(JqK_tL  +"&54673327GL(4@> (## q N@KJ~qK]rKpK_tL    +"&'5326654&##77##3!;7\%(\";L%=BB[.V^L)D)/=iP`gLWG4x"*lKPX' J' JYKPX@rK`xL@rKpK`xLY@%# ** +"&547332667733266773#7##"&'#DD AC0"5' /C0"5' 0to U98= W UH$47#<8_;#<9`:e1>;41>4x"/3@0 JrK`xKtL//$$%) +7>1##"&'##"&547332667733266773=$  K.-KW9DD AC0"5' /C0"5' 020 ',3<1>UH$47#<8_;#<9`:V,5z@(!JKPX@_rKpK_ tL@#rK_zKpK_ tLY@-+&$  55 +"'53267654#"#654#"#3366323663283"- K0"5( /C0"5' 0tp  S98> V9DD M .O v (+d#<8_;?#<9`:"e1>;41>UH$44S0),$m@ JKPX@_rKpK_tL@ rK_zKpK_tLY@ $$ +"&'53267336632#654#")"p  S9FG AC5"5' =\r_e1>UH$4?#<9`:aR),'m@$%JKPX@_rKpK_tL@ rK_zKpK_tLY@#! '' +"&5467654#"#336632327GLL5"5' 0tp  S9FG K(4@> &h#<9`:"e1>UH$4# q"$@! JrKpL +330>773#0t.t ."&97~"46,,`,9,&KPX@ JKPX@ J@ JYYKPX@#e _zK _ xLKPX@-e _zK ]rK _ xL@8e _zK ]rK ]pK _ xLYY@!&&  +"&54>327!#3#3!7'26654&#"_p$HkHA,{')9'!2 p`Hc8&p`ppr xAh;)7-HR%X",/C@@J~_zK`xL+)#!// +"&546632#"&'#'2677332>54&#"RYd`IApF==M#+( ) Y^Zt7 fWo`F|Q^C70/8t@:``# 3:E^HtCK,"iKPX@ J@JYKPX@rK_xL@rKpK_xLY@  +"&'7326773#7#  7W4tp !T WVe4;iKPX@ J@JYKPX@qK_xL@qKpK_xLY@  +"&'732673#7#  7Wbp !T WVe4;"$A@>!"JrK_xK`tL  $$ +"&5477>1##"&'7326773327FN K.  7W4~(1D<!/!',WV# q,iKPX@ J H@  JYKPX@_rKtL@rK_zKtLY@ %$ +336632&&#"!p !T5  7Wge4;WV,"KPX@  JH@  JYKPX@_rK`tL@rK_zK`tLY@ "" +"&5467336632&&#"327FM{p !T5  7W<(3@> *Ce4;WV# q .)@&J_zKpL%# +36632&&#" La\!6 EJgfa wQ#;.)@&  J_zKtL%% +654&#"56632#~4#ES~O w NH" 3@0Je]rKpL ! +332#'#326654&##tVgP>iV3+@:1 &%;"LGMP/# ~"3@0JfrK]pL  +33373#'32654##t,D&-EqBSP&AMM"=0DU&i(';,7I@F56J_zK_xK_tL31+( 77 +"&5467732654&'&&54632&&#"#"&'3267Y=D.%N)#6 237nb/X*0A & *1A}i%  ">9   @4UYiD8^`A h 7@4J_qK_tL  +"&'532676632&&#")"UN(  \rOZ ruaR#L@IJe_qK_tL  ## +"&'53267#7376632&&#"3#)"?KJ6UN(  4LL>\r(pOZ rpaR .#:@7 !J~zK`tL ## +"&5467654&#"56632327GLR  - >JT(4@> ( w LC!u # qQ&I@FJg_qK _tL" &&  +"&546336632&&#"3#'267#"/=C[QUN(  }IJ.KE#>,@FVOZ rM0I*R&+ l,W" "3+ f@ JK PX@n]rK_tL@]rK_tLY@  +"&547#?33#3267AMiJ`@_i$B7GH)stp  n f""KPX J JYKPX@  frK _ xL@#  frKpK _ xLY@   +"&5467#73733733##7#'3267#GDED****EE5p  R25'= UH8aae1>N=3)D){#N 6]%  Gp@"Y" "3+"Z" "3+L\" "3+2T""@JrKpL +37373+F+V"@@=J]rK]pK_tL  +"&54677!7#7!33267^=Dn'  ">9  KXXrb h "$1@.Gg]rK_pL$#$# +'667#7#7!3>32##7326654#"Cn2'?A*26wjD v+!*W" XXrb3G%?1SN)% " "'2@+ %!J"GKPX@)~]rK_xK_tL@'~g]rK_tLY@)(/-(2)2'' +"&5466326654&##77#7!&&''267&&#"vVl.L.@X" HB85Q.Q  O"SJ>:$<&JI2B!6%#3@ir`5S3K 6 m &3P )@&  J_qKpL%' +3>54&#"'6632PNV]%,&,Q14n;C\1{m@oFF"-" j"2T4_%W$+@(  J_qKpL%' +3&&546632&&#"A@AExN@Y)DD)/EANN0(jGPm7#j!?7.U-@* JqK`xL +"&'732654&'3@^)CE(/EANNAA@Cv "j A7.Uq)jFOn9 ,7@4  J_zK_tL  +"&&54>32&&#"32678^8)UX1M"-3 2L22(%7"#P.nag›[p MIH:x<&2 3+"!9@6Je]rK]pL!! +332#32654##32654&##rM_9=%,9a; K(1?KJV)2 !R"8A6H6(>P%J"%4*)"+,+?@<Jg_zK_xL'%$"++  +"&546675&&54632'2>54&#"33#"eu$>'(,{os@d0=! ;0#.-)?5+ RF-7 5)NWz}[Tn$=K&I= g""!,.P@MJe_qK_zK_xL+*)(%# .. +"&54663276632&&#"&&#"32677#73q|Do,IA" .G'-P245f:.` whcYQ@E rJo *_M5=\cW"R@ &K@Hh _qKrK _ tL " &&    +"&54632"&5463333#'267#" */*,+>B[QuuJJ /MD#f!*,!'0>,@F"M/J*R&+ '")@& JrKpKtL +667##73773NݨY;$mB,#"@rK^pL +333t\"Pr,*:L@I"#J_qK_zK_xKtL,+42+:,:**%*') +7>1##"&&54>323066776632&&#"2>54&#"$  D3$C,$B\917WN( !#1$ /#$20 "1'WHCk@,$"2NZ r]5OQ'/+FV+++ 7@4Je_qKpL%' +37#737>54&#"'66323#PWVV]%,&,Q14n;C\1{mihaFF"-" j"2T4_%CaL$9@6Je_qKpL%' +37#737&&546632&&#"3#ih@AExN@Y)DD)/EANVVaC(jGPm7#j!?7.Ua,-0KPX@ JK.PX@ J@ JYYKPX@ qK_zK _ xLK.PX@5qK_zK]rK ]pK _ xL@0qK_zK]rK]pK _ xLYY@ 0/(&- -  +"&&54>323466773!3!7#72>54&#"#(F,$B]:18%.u K "2$ /#! 'WIBj@."%0brG$-w5OQ'/+FV+VK,6E;KPX@%0 JK.PX@%0 J@% 0 JYYKPX@1  ~qK _zK  _pK_ tLK.PX@?  ~qK _zK ]rKpK  _xK_ tL@=  ~qK _zK]rKpK  _xK_ tLYY@!87@>7E8E.-,+#! 66 +"&'5326654&##77##7##"&&54>323466773!2>54&#"c7\%(\";L%=BB[p K4(F,$B]:18%.V^LD"2$ /#)D)/=iPG$-'WIBj@."%0`gLWG_5OQ'/+FV+V,/>AKKPX@ JGK.PX@ JG@ JGYYKPX@)  gqK  _zK _pLK.PX@?  gqK  _zK  ]rK _pK _xL@9  gqK _zK ]rK _pK _xLYY@10JHDBA@970>1>$#'$ +'667!7##"&&54>323466773!3>32##%2>54&#"#326654#"C K4(F,$B]:18%.w2&@A*17wjDW"2$ /#!+!*W" G$-'WIBj@."%0b3G%?1SN)5OQ'/+FV+V M%.#7KPX@ J@ JYK PX@ n_rK ]pLKPX@_rK ]pLKPX@)_rK_rK ]pL@'_zK]rK ]pLYYY@&$21$7&7 #" +3"&547#?336632&&#"#'32654&'&&5467#@N8J`@_$/X*0A & *1A}i#6 237q87@H)stiD8^Vp @4  .,7@"# JK PX@3p_qK ]rK _xK_ tL@4~_qK ]rK _xK_ tLY@.-21-7.7'%  ,, +"&'532677#"&547#?3376632&&#"267#*"A&@N8J`@_ VN(  \^6A8rK 7GH)st2OZ rwaR] 4 .@>IKPX@"#H ;J@"#H ; JYK PX@*n  g_rK _ xLKPX@)  g_rK _ xL@;  ~  g_zK]rK  _ xLYY@#@?FD?I@I971/'%  >> +"&547#?33#3267&54>32&&#"6632#"&'%2654&#"@N8J`@_8E)&JlE1M"-3 &<(H.@E&?J$4d&RI9%@ 7GH)stp If:p 1R0 F5/B)/"m#,RDKPX@. -J@. -JYKPX@*_qK _rK pK_ tL@5_qK _zK ]rK pK_ tLY@@?>=971/,*"  DD +"'53267#?6632&&#"336632#"'53267654#"##3"- dPY cP"9'"  T9GF M .O>3"- K5!6( 0\i -O v (+G))cO m ' e1>UH$44S0 v (+d#<9`:2R1)KPX@J@JYKPX@qK_zK_xL@ qK_zKpK_xLY@ )) +"&'#332654&'&&54632&&#"Lo10e6"7 237oa/X*0A & *2@} # @4UYiD8^` ,@)qK]rK]pL  +33!3%#.wbrcM ] I@F J ]oK] pL    +37373#'37373#'wT je m\hkT je m\hkzPP]?@<~e]oK pL  +7!#7#7!#7#s66u55u3 P./}KPX@ J@ JYKPX@~zK`xKtL@#~zKrK`xKtLY@//)%*) +7>1##"&54677654&#"566323266773$  J0FE  - >J5"5' 020 %.UH3jw LC!{#<9`: P.;KPX@ 9:J@ 9:JYKPX@$~zK`xK_tL@(~zKrK`xK`tLY@8610,*! ;; +"&5477667##"&54677654&#"566323266773327FP H0FE  - >J5"5' 0'%D<#5)*UH3jw LC!{#<9`: # qAGK5)&f 3+AG05)&f 3+GM5)&f 3+A_mGU5)&f 3+<gGC5)&f 3+<gGE5)&f 3+AgGK5)&f 3+]VgGZ5)&f 3+gG\5)&f 3+4"48&maM1+dD@ HW_O +D72654&'7a/3(BMCD9)/7{H/ $dD@ GW_O +D&&5463"+7S@H71>9Fgr6dD@+  JW_O%& +D76654&#"'6632y7<)?&FBDH f +  LE0:G2]8dD@-  JW_O%% +D7&54632&&#" W]P';%%%,(3"S@P M ! fl^Q 3+l^!Q 3+RY'dD@Jt +D73#'Ry#k;DNov/ /3+mT&dD@U]M +D73m%d%Tq^v^aC2KU ް3+M LY 3+4CT ְ3+4vT ְ3+ ,dD@!JU]M +D'37:v;苋o dDt +D'3:u u3+1u u3+~  3+)~  3+  3+M dD@U]M +D3#+ M<p;V 0+'7'77'!H.3/H"I/4.;*<;+<<+;<*;FgG-5)&f 3+A GO5)&f 3+<[mGV5)&f 3+gG[5)&f 3+nG_5)&f 3+NT,dD@!U]M +D!#5!nBPNT0dD@%Ue]M +D!#5353BBPNT0dD@%Ue]M +D!#533BFB(PNT0dD@%Ue]M +D!5#533BBPLR,dD@!U^N +D3533LBBnPNT,dD@!U^N +D733NBĦjBNT0dD@%Ue]M +D733#NBĦjB9qKU ۰3+nS)5 53+47'dD@Jt +D'373J`K:vRކ'dD@Jt +D73#'E0`K9w܄0+'7% k1kH@=0+77'7E H@=Bk1LC 3+L  3+,R 3+7\QT ڰ3+8>,Ұ3+%NdDK PX@oU]M@U]MY@  +D53#q5q%NdDK PX@oU]M@U]MY@  +D5#53qq5DNdDK PX@nU^N@U^NY@  +D7533D5qq5,NdDK PX@nU^N@U^NY@  +D75353,q55q0QdDK PX@nU^N@U^NY@  +D53!538M9Хcc0NdDK PX@nU^N@U^NY@  +D53!8ХcBK 1dD@&JH GU]M +D'57!!O1;DHf*- * 3+$ =JKPX@ ]:L@U]MY@ +56673H32#"&'66322654&#"26654&#" jpfaKJ6X1&@! Y3U`H/#)=.`#;"/"*B1 r=!dD16@R+4fVUGY# 'E-(1'"8;$R'=S 3+Z&=j3+,"6@3'J IK2PX@$~_8K6K_:L@&~|_8K_:LY@20)(!66+"&'53277&&5467#>54&#"'66323327&. (/5- /)D( ;6"'Hh :J:8)O"$`gc'! f@7/D ^mk.  ZKT\JK2PX@g_=K6L@g_=LY@(+37&&54>32'2654&#"]d(Va`FMcZiJMHV&I^Dz`7ExN[Xga?Q8\6BL,,2@/J_@K]:L(+7&&54>3226654&#"r48B$HmI_p1fO2)9'!2 cIHc8rfSd^Bi;)7-HR%ZPS@  JK2PX@_=K]6L@a_=LY@ %%(+37&&54>32&&#"33Wp5`N>a,: I)>\2XW.6sdL^3z9b;?C,2,(4@1Jc_@L((+"&'532654&'&&54>32&&#"98-2&BK#IpL8M04#.C$'0 K PX@ pe]5K6LK2PX@!~e]5K6L@ ~e]5LYY@ +3!#3#7#'3}m;|u" ]K PX@ pe]8K:L@!~e]8K:LY@ +!#3#7#!%0xlSpsy!(F@C%&Je_=K_6L#!((+"&54677!76654&#"'632!3267Y=K4n.7CC% |  0 724f# e>12 I h++@@=( )J~?K`:L&$+++"&5467756654&#"'6632%3267BF  8?Ap& ) 9G66?^ !  f >0&E=R*'iQ+P@ JK2PX@_=K6L@_=LY@ +!667'76454&''7&#"'6632.O <+H+&a@O@HQUNi & wOmsqc @  H:L+>7'7&&''7&'5[+N4 8}o`aM@KLNT?NPLa1Єn"/KPX@,-J@,-JYKPX@_8K`6LK2PX@_8K6K`6L@~_8K`6LYY@*(!  //+"&&5467#>54&#"'663233267,G*5- -)D( ;6" * >5)J"$`gc'! f@71B ^mk. i:,(7<@95J_@K_6K:L*)1/)7*7((%/+6654&'&&5467>32#"&'#2>54#"2KS Y|Pdq7###_ŗD v]  FU6E*dUMP^,"X JK2PX@~8K6K:L@~]8K:LY@ +373#667#'"KsG hUzM,,',(J@G'J e_@K _6K:L$"((%$ +73>32#"&'3##72>54&#"XDO;iXdg=rQ*@  &!0  +4avRyAu_b_""C$a99$3LP+0MO+Le<&&F 3+L&ew 3+P%J"%4*)"(,7@4  J_zK_xL  +"&54>32&&#"3267ac"FnK3H#,7".B+27?Juc<{f?[/JW)?> _ " '@$]rK]pL ! +332#'326654&##rmvDo!'MW$A:*"lmcR^Bj:D=" 7@4e]rK]pL ! +37#73732#'326654&##3#0II.mvDo!'MW$A:*zz]lmcR^Bj:D=]" /@,e]rK]pL  +3!#3#3r+"]z]^-*P@MJ~g_zK_xL$# ** +"'732654&##7326654&#"56632zWI=!3:0)B>$B+5/2U()WAkz-I+$3 =Xg !yUC+>$ 7*HX*")@&crKpL  +33"&54632ttp,+- */"!'0!*,W"+@(JrK_xL  +"&'532673F  $. MpQ\ ],0p\P" %@" JrKpL  +336773#'to8i|K.*"($Z" *@' JrK^pL  +37'7373&&Q7o)4'r K+J>^"'@$ JrKpL +3333#667###t˜tf7 g 6"e&Z!W_/"&@#~rKpL +3333#7>7#u. t5"62gC7* *-@*_zK_xL  +"&54>32'26654&#"af"EkIac!DjA+H+.-.I)1r_@}g=o]Cg=`@uO3=GwF88*7@4J_zK_xL +"&'732>54&#"566322H#+6"-B*27>J1ad"Fl[.KW(>> _ tc-c R75@ 53+. dF8 @  3+ v:3@ 33+D.p$ $3+ 7" ,<@9JgrK`xL"!'%!,",   +"&547&546773326773'254&#"mi5 n +.8< p 31Go57'B'8aK4 >  ?? %80=>4O+Nrg`v$+3*%-0 ,!@_zL$# +>32#654&#"0 FxV_p'#3  QLrf$#$)71M), $@!`xL  +"&&5467332673=_5I0? Dw 3^A#$!Z\CMK" +@(g]rKpL %! +332##32654##rbR>iC)(<2>G%"XFAU*!2.D" 8@5Je]rKpL & +#7&&54633#7#37#"#"&llqp,+'1`.8.Mb0U"%" ;@8JgrK^pL    +3"&5467'3373'37#"P^D2_yL,,nqw 05GOAR],->4"!@]rKpL +3#7!#j__^^<:f"$@!rK`xL  +"&&5467332673M]+>?Q/7?>>p 3T4$(S?G.MuA GX=@ ԰3+ @ ϰ3+{P@ Ѱ3+4" !@JrKpL  +3336673h4l t"/8M>"%'@$ JrKpL%% +333>733>73#&67#Oicyq  v" .31* 6*25, .>6" %@"]rK]pL  +#7#7!33jOodPc"@@=J~]rK_xL  +"'5326654&##77#7!U;O&A'.C8M `) (K^H [^ *&7@4#$J_zK_xL! && +"&54667>54#"'66323267VT-I+#>&A3$ )I.JU-Q51CH'?)"K Q>7G, "-YL=5C,(%7`**6@3'  JzK`xL$"** +"&'73267&&5466323267#"&&'*  4)$5_?/H(,F(&*!2*(:5 Z'*)].8_9&A'4YK",!Z -*(."@]rKpL +3!#t3_"];" !@JrKpL  +#3#&5=}2l ":.N?&"!@]rKpL +3!##tsp``"<"C"+@(hrKpL +37"&5467733326773##XZ&l(+-?h?:9 )m,uq!MC $&&0;dW*"pKPX@ J@ JYKPX@]rK_xL@]rKpK_xLY@ +"&'532667667!##  6!Yto` 1>]))4:qa @EG$5)&f 3+G5)&f 3+FG%5)&f 3+<$fKPX@" e]K] L@ g e] LY@$#! +7#737323##32654##32654'#F+56'CP240S3 /#*60@9#,.@H,3.H1>(!*FG'5)&f 3+FG(5)&f 3+3Gm5)&f 3+\G*5)&f 3+FG+5)&f 3+"RG,5)&f 3+G-5)&f 3+FG.5)&f 3+FZG/5)&f 3+FtG05)&f 3+F#G15)&f 3+F#=JKPX@KL@]LY@ +33#7667'FbX/mbV.5*BTB\G25)&f 3+N G5)&f 3+FG35)&f 3+FG55)&f 3+jG75)&f 3+bG85)&f 3+pG:5)&f 3+RmGD5)&f 3+TmG5)&f 3+RmG5)&f 3+AKmG5)&f 3+AGE5)&f 3+RGG5)&f 3+RmGH5)&f 3+EmG&5)&f 3+AmG'5)&f 3+0mG5)&f 3+=mGJ5)&f 3+(gG5)&f 3+AGN5)&f 3+A`mGP5)&f 3+AmG 5)&f 3+RmGR5)&f 3+.RmG!5)&f 3+TmG5)&f 3+RG5)&f 3+ mGS5)&f 3+SLGW5)&f 3+XgGX5)&f 3+G%eG5)&f 3+WvgG95)&f 3+VgGY5)&f 3+VkG5)&f 3+ G|5)&f 3+HgG}5)&f 3+GG~5)&f 3+R mG5)&f 3+mG5)&f 3+hFL̠)&f 3+FU̠)&f 3+JFX̠)&f 3+>FY̠)&f 3+SkF|̠)&f 3+>F}̠)&f 3+2F̠)&f 3+F̠)&f 3+y\F̠)&f 3+:j,0:KPX@ (.)J@ (.)JYKPX@"h ]rK` xLKPX@&hrK _zK` xL@1hrK _zK` xK_ xLYY@8631-+&$ 00 +"&&54673326736632##3267#"'32654#" K[*>?(%,5?;"SZ2/+F0+Y:q.SUK35) 3T4$(+(CC.TAVh ,4oI )F5#."<'/>KPX@#  J@#  JYKPX@1 hqK_wK _ rK  _ xL@5 hqK_wK _ rKpK  _ xLY@#10860>1>(& // +"&'##&#"#6632733273#"'36632'2>54#"*4Aq JQ1  !JO1 <2(H-$B]S/#C#1% ,$FsRD"O5RE=,$SECk@w+FV+L2LN'/,1?KPX@   . J@   . JYKPX@1hqK_wK _rK  _ xL@5hqK_wK _rK pK  _ xLY@#32;92?3?-,*(&%$" 11 +"&&54>32346677&#"#6632733273#"'#7#72>54&#"(F,$B]:18 JQ1  !JO1 {p K "2$ $;" 'WIDe<."(5*RD"O5REG$-w3MO&-Bj=43=G>G3<4<22%$ +37#6677336632366326673#7&&'#7&&'"654!"76545"Q\N'p  S98> V9DD QZL"#"O)(.)N"00 F%&<(L! $Kc e1>;41>UH$4$!J_  ,% UH$4( =O_  ;/  #<`',0?@  $/JKPX@/g h _rK  _xK  t L@3g hrK _zK  _xK  t LY@21971?2?00")'$"" +7&#"#6632336632#"&'#3273#"'2>54#"! JP2 ~pL5'G,$B]:26  !JO1 /#@$2 %_RDPS#:'WIBj@."(B&5RD2]+FV+V4NR'/,)KPX@JH@JYKPX@$g h_rK  p L@(g hrK_zK  p LY@))2#%$"" +37&#"#66327336632&&#"3273#"&'+ JQ1 4p !T5  5V !JO1 !RDe4;QQ5RD.'I@FJgg_zK pL''2$%$"" +37&#"#663276632&&#"3273#"&' + JQ1 a\!6 E !JO1 !RD9fa wQ^5RD,8_@\ !J~|||_zK_xL0/.-%#88 +"&'532654&'&&#"#667&&54632&&#"2735M"%N)#6 2  J 1!nb/X*0A & *% &J0} y  :D  UYi 57B ^`2@&/ 0 JK PX@/n g  g]rK _ xL@. g  g]rK _ xLY@-+%#!  22 +"&5477&#"#66327#?33#3273#"'3267@N JP2 J`@_ !JO1 %C 7GIRD\H)stp5RD  n "%K PX@ "J@ "JYK PX@(gg]rK ]  p L@6~~gg]rK ]  p LY@%%#2"$ +#77&"#"#66327#7!32673#"&'3"JS9 Ln J2>$! FXRD ]rb%=AUrE&6KPX@J@JYKPX@ g_zK_xL@$g_zKpK_xLY@('0.'6(6 && +"&'##>32&&#"136632'2>54&#",5=qwKkC=P%&U/3@ D3'C)$B\U/#% /!$ +%F/Vc*/2%"1*WECk@w+FV+++/JT%'/AgG5)&f 3+."&@@= Jg]rK_tL!&&  +"&&54667'77#7!'26654&#"Dg9>tQH 11@!EkC):*'0;)/Y=MwD71ird((hI3dQ1m0K+),/K,&/=KPX@7643J@7643JYK PX@(p   qK_rK pLKPX@)~   qK_rK pL@8~  qK_zK]rK pK x LYY@==:921$" +7&&547#?33#373632#654#"#767#"#U8J`@_8LsGF AC5!6( 04wC(?KX2&H)stp iUH$4?#<9`:|n A"3@0JfrKpL +#?#737'7!3#(\LLJ8[JJJH*tai*HH*iat*HH"=@:JfrK_xL  +"&54677#73733#3267AMKJ/.^^ *$B 7G "%aa. # n X, (1@  JKPX@*   f _rK _xKtL@.   frK _zK _xKtLY@ *)"!-,)1*1%$!("( #$ +'7373366323##"&'##%"3654267#J/pL5'G,2-B_:26 #d(6#9%aS#:'WIaAmB."(B&B, VF3'/"!A@> frK _ xL!!  +"&&5467#73733733#'2677#M]+IH....KJ@mE)6 3T4 aaDd6x-7S"$-C@@J  e]rK _xL,+)'$$# +'73667#7!3&&'73#3##"&&5473267!<<w %:{) G- RSNo<=33Oa'5pn8#!8np9%aLj76]< 39>;''6KPX@ J@ JYKPX@'qK_zK _xK_tLK2PX@+qK_zKpK _xK_tL@(cqK_zKpK _xLYY@)(0.(6)6 '' +"&'53277#"&'##3366322>54#"%.  4Aq>2'G,,( IF/#@$2 %h :8,$F56!2'WHJ5ZKS+FV+V4OR'/,n'6KPX@ J@ JYKPX@(qK_zK _pK_ tLK2PX@,qK_zKpK _xK_ tL@) cqK_zKpK _xLYY@)(1/(6)6$#"! '' +"&'53277#7##"&&54>32346677332>54&#"X&. ) K4(F,$B]:18%5%H"2$ /#h :@G$-'WIBj@."%0uZKS5OQ'/+FV+V2KPX@( 'J@( 'JYKPX@-_qK]rK ] pK _ tLK2PX@5  e_qK]rK _tK _ tL@0  eW_qK]rK _ tLYY@.-+)%#  22 +"'53267#?6632&&#"3#3#"&'53277#3"- dPY cP"9'"kkB(H@&. ~ -O v (+G))cO m ' pɼZKh :UP2R1 ,6F:KPX@ ,+J@ ,+JYKPX@1 _zK ^pK `xK_ tLKPX@. _zK^pK  _xK_ tLK2PX@:frK _zK  _xK_tK_ tL@5fWrK _zK  _xK_ tLYYY@!87@>7F8F21/-)'$#"!66 +"&'532677>1##"&&54>323733#"&'53277#2>54&#"=P%&U/3@ D3'C)$B\85=qZ(I@%. ~Kk/!$/#%/2%"1*WECk@+%FYZKh :U"Vc*]/JT%'/+FV+++iz@ JK2PX@$~qKrKpK`tL@!~dqKrKpLY@  +"&'53277#'#33733K%. Y;$HݨX>%Ih :@#BZKFd@ JK2PX@~qKpK_tL@~cqKpLY@  +"&'53277#330&. L5%Hh :@uZKV,8@*#JKPX@(  ~_ rKpK_ tLK2PX@,  ~rK_ zKpK_ tL@)  ~ crK_ zKpLYY@54/-(&"!  88 +"&'53277#654#"#654#"#336632366323%. LC0"5( /C0"5' 0tp  S98> V9DD *5%Ih :@?#<8_;?#<9`:"e1>;41>UH$4ʮZK),&@JKPX@%~_rKpK_tLK2PX@)~rK_zKpK_tL@&~crK_zKpLYY@#" && +"&'53277#654#"#3366323h&. LC5"5' 0tp  S9FG *5%Hh :@?#<9`:"e1>UH$4ʮZK',&5@!JKPX@#_rK _xK_tLK2PX@+rK_zK _xK_tKtL@)grK_zK _xKtLYY@('/-'5(5&&$$)$ +336632#"&'53277#"&'#2>54#"!pL5'G,,( I@%.  26 #/#@$2 %S#:'WII6ZKh :8."(B&]+FV+V4NR'/, KPX@ JH@ JYKPX@$~_rKpK_tLK2PX@(~rK_zKpK_tL@%~crK_zKpLYY@    +"&'53277#336632&&#"30&. Ltp !T5  7W5%Hh :@"e4;WVZK ,5x@"# JK2PX@ _zK_xK_tL@c_zK_xLY@'%  55 +"&'53277#"&'532654&'&&54632&&#"%.  5M"%N)#6 237nb/X*0A & *1AIh :8y @4UYiD8,DmZK+KPX@"!J@"!JYKPX@"_qK]pK_tLK2PX@*e_qK_tK_tL@%eW_qK_tLYY@('%# ++ +"&'532676632&&#"3#"&'53277#)"UN(  c(I@%. ~\rOZ r-ZKh :URaR2:"l@JK2PX@~rKpK_tL@~crKpLY@  +"&'53277#3366733&. C  i%Hh :@"CA"KZKD"s@ JK2PX@ ~rKpK_tL@~crKpLY@  +"&'53277#'#3733'%. =n8wGA%Ih :@ ZK"j@ JK2PX@ ]rK]pK_tL@c]rK]pLY@  +"&'53277!7#7!3%. n&Ih :@XXrbZK,H,)9KPX@ '(J@ '(JYKPX@$_zK _pK` tLK2PX@,rK_zKpK _xK` tL@) drK_zKpK _xLYY@+*42*9+9%# )) +"&&54677#7##"&&54>32373332672667654&#"!;%(K4(F,$B]:4Aq]6%'  $4%$ /#4)  EG$-'WHCk@,$FK "h S0K+$.#-+FV+V,H,x,6F_@\&'34J_qK_zK _xK_tL87@>7F8F20+)$" 66 +"&5477>1##"&&54>323066776632&&#"3272>54&#"GM D3$C,$B\917RO' 3 (2#1$ /#$D<!/!"1'WHCk@,$"2"@U r:# q]5OQ'/+FV+++,,-7@#+,JK2PX@(g_zK_xK_tL@%gc_zK_xLY@530.)'! -- +"&&54677#"&54>32##3267326732654#"!;%  `x'MrK[\2/+F0,'  $UK35)4)  =hkEd;TAVh ,4 "h "5#."< ,:@089JK2PX@(e_zK_xK_tL@%ec_zK_xLY@64.,)'&$! :: +"&&54677#"&54675&&546632&&#"33#"32673267!;%  axXH!0@h?!*!Q(#0j=8Y3I6)4. '  $4)  )9g$k;1;> 5.Rd> "h ~,1;@/0JK2PX@*g_zK _xK` tL@'g d_zK _xLY@32862;3;-+'&! 11 +"&&5467#"&546336454&#"56632332672667#"*!;%7MmF\[3.+F0+X;`x}R'  $6*UK4)  9Z4TAVh ,4ofb x "h F"<(5#.? #@ !"JK2PX@)~_qKrKpK`tL@&~d_qKrKpLY@  # #  +"&54632"&&54677#333267+/*,+I!;%Lt]5%'  $f!*,!'04)  E"K "h ,.w@$,-JK2PX@ _zK_xK_tL@c_zK_xLY@*(#! .. +"&&54677326654&#"56632#"'3267]!;%.H .B",(%?""O38]6&JlE '  $4)  El9//x(YHIf:9 "h *m@()JK2PX@~_qK`tL@~d_qLY@&$ ** +"&&5467732676632&&#"3267!;%("[UN(  YR> '  $4)   OZ r\[R8 "h 6Q"-KPX@ +,J@ +,JYKPX@rK`pK`tLK2PX@"rKpK`xK`tL@drKpK`xLYY@)'#"!  -- +"&&54677#7##"&54673326677333267!;%)  R9GD>C5!6( 0]5%'  $4)  Ee1>UH9'#<9`:K "h "-@#+,JK2PX@(~]rK_xK_tL@%~c]rK_xLY@)'! -- +"&&5467732654&##77#7!#"&'3267Y!;%.Z6?V:FQLB5~l1 '  $4)  )SyrayL69W2< "h AmG5)&f 3+RumGF5)&f 3+3umG"5)&f 3+LG5)&f 3+=ymG#5)&f 3+nGI5)&f 3+gG)5)&f 3+=mG+5)&f 3+XgG/5)&f 3+0G25)&f 3+TgG35)&f 3+1gG45)&f 3+1gGd5)&f 3+Gg5)&f 3+5 G75)&f 3+$ Gn5)&f 3+A:gGi5)&f 3+A`mG;5)&f 3+WvgG:5)&f 3+mG<5)&f 3+AmG=5)&f 3+AgG>5)&f 3+RmG?5)&f 3+RGB5)&f 3+[mGL5)&f 3+:GM5)&f 3+SLG5)&f 3+3gGS5)&f 3+JgGT5)&f 3+[gG5)&f 3+[mGU5)&f 3+agGV5)&f 3+&hgG]5)&f 3+&hgGZ5)&f 3+&gG[5)&f 3+gG\5)&f 3+UG5)&f 3+c&%N3+'&EN.c&% z.'&E K\c&%L 3+_'&EL 3+<&&'z#v3+,&F'zv&'N3+,n&GNl.&' q,.n&G GR&'L 3+,^n&GL 3+'?@< J]oK]pK_tL'% %,! +332#"&'53254&'7'326654&##Hd)IR( "2&-&4Rj4I@8ʋwt* (%2DR(K}WROK,n-<KPX@#( J@#( JYKPX@* ~qK_zKxK`tL@. ~qK_zKpKxK`tLY@/.75.323466773#7#2>54&#"( !2&%%B)$B]:18%p  (I"2$ /#R(A*UFBj@."%0G 4 (%2D]5OQ'/+FV+V3&'J հ3+3n&GJ հ3+@&(73+,h&H@&(73+,%h&H8@&( :,&HJ ܰ3+8@&(Q ۰3+ :,&HQ ݰ3+@&('MLz3+, &H'zM3>&)N3+&INְ3+<m&*L3+ A&JLb&+N3+)&KNFְ3+.&+ .)&K S&+jd3+)&Kjְ3+&+zD)&Kz8"&+M İ3+&)&KM Ȱ3+1&, n9?&LQ ܰ3+&,3+q&'&.v3+i&NvFְ3+.&. f.i&N MY&.L 3+Ri&NL 3+.&/ 9.F&O .m&/' 9L3+.&O' Lְ3+\&/L 3+_F&OL6 3+8&/ l8F&O u&0NM3+V&PNX.u&0 .V,&P &1N%3+)&QN.&1 .),&Q PW&1L$ 3+V),&QL 3+8&1 8),&Q 9<#&23+,J{&R<&23+,,q&R<&23+,h&R<&23+,%h&Rg&3v3+L&Svg&3N3+'&SNH&5N3+&UNm.H&5 Z.,&U .Hm&5' ZL3+.&U' L'_H&5L 3+V,&ULA 3+&6N3+ &VNb.&6  .,&V  &6'3+ G&VZ$&6'3+ |&V.&6'N 3+ .&V&Nb  RZ&7N3+.\&WNYdd3+R.Z&7 &..&W  \Z&7L 3+c&WL 3+8Z&7 8&W EG&8j ڰ3+-GK"&Xjb ڰ3+@1&8 a1K"&X 9E8&8 s8K"&X EE#&8{3+6[{&X/E&8{3+6K^&X/Z&9Q[3+2:&YQZ.&9 @2.:"&Y Z&:Ne3+=G&ZNZ.&: =.G"&Z &;N3+D&[N&;j)3+D&[j[&<N3+;&\NsJ&=J93+&]J.J&= )."&] YJ&=L 3+_"&]L 3+Y)&KL 3+.h&Wjxx3+=GE&ZO;E&\O@,AB&D=3+&@N3+,^&{,^ &{#,^&{ ,^&{ ,^&{,^&{,^\&{ ,^\&{1&$ Ű3+9&$# Ű3+5'$] ̰3+F'$b ̰3+5'$] ̰3+F'b$ ̰3+M*'o$ ΰ3+M*'$o ΰ3+ &  &# & &&&0&(T Ű3+C&(\# Ű3+5-'(] ̰3+F2'(b ̰3+5-'(] ̰3+F2'(b ̰3+)&) &#)&)&=&7&_\&S\&0&+T Ű3+C&+\# Ű3+5'+] ̰3+F'+b ̰3+5'+] ̰3+F'+b ̰3+P*'+ o ΰ3+P*'+ o ΰ3+/?& /? &#/l&y/n&{/&/&/\&y/\&m0/&,y Ű3+C6&#, Ű3+5',] ̰3+F', b ̰3+5',] ̰3+F',b ̰3+P*',8o ΰ3+P*',8o ΰ3+,&R, &R#,&R,&R,&&R, &R0&2J Ű3+C&2W# Ű3+5'2] ̰3+F'2b ̰3+5'2] ̰3+F'2b ̰3+:<&:< &#:<&:<& :<&:<&:W\&:K\&E &#< Ű3+F'b< ̰3+F'b<: ̰3+P*'o<J ΰ3+,&, &# ,&s,&u,&,&{,\&s,\&g0&P Ű3+C'&]# Ű3+5'] ̰3+F'b ̰3+5'] ̰3+F'b ̰3+P*'o ΰ3+P*'o ΰ3+,^&{,^&{" & &")& )&""/?&t/w&",&R,&R" :<&:<&",&n,&",^&{',^ &{'#,^&{& ,^&{& ,^&{&,^&{&,^\&{& ,^\&{&&$&Y Ű3+&$&#a Ű3+5%'$'] ̰3+F''$'b ̰3+5#'$'] ̰3+F''$'b ̰3+M(*'$'o ΰ3+M(*'$'o ΰ3+)&') &'#)&&)&&=&&7&&_\&&S\&&0;&+T& Ű3+CC&+\&# Ű3+5'+'] ̰3+F'+'b ̰3+5'+'] ̰3+F'+'b ̰3+P*'+ 'o ΰ3+P*'+ 'o ΰ3+,&', &'# ,&&s,&&u,&',&&{,\&&s,\&&g0Y&&P Ű3+Cf&]&#' Ű3+5''] ̰3+F''b ̰3+5''] ̰3+F''b ̰3+P*''o ΰ3+P*''o ΰ3+,^&{ME,^&{Lk,^&{&,^,&{,^&{&",^&{QE,^&{&QEa&$Mt3+Mm&$L3+>&$ ̰3+4&$" ̰3+&$`S*%dD@Jt(+D767&&54632 =! ")ES*!!&)7) QS*]3Q+W<` +QdD@Fg g W _  O"!'%!+"+  """" +D663232673#"&&#""&54323"&5432 A)2*? ?)1) E!! E""9>=:H!'H!')&& ),&)&&""R&QJR&&QJR&(o0 ̰3+4'"(e ̰3+R0&+o0 ̰3+4&'"+e ̰3+&+SdDKPXJK.PXJJYYKPX@W_OK.PX@WU_O@We_OYY@(+D767&&546327&&'73 8L!sS*"&3?&U! $U SdDKPXJKPXJJYYKPX@W_OKPX@e_O@We_OYY@(+D767&&54632756673 8LR @S*"&3? #P #VFO\$dDJK PX@+pg gW` P@,~g gW` PY@$$ """" +D663232673#"&&#"767&&54632 A) 2(? ?)1*4I9:=6'.8/&M/&L &t' &"&Q/`&w&,M3+m&,L3+RB',0 ̰3+4F'", ̰3+ThdDKPX@~g_O@&~Ug]MY@$+D&&54632&&'73K43. 'Z!sT4&,- &O! $O TdDKPX@ J@ JYKPX@~g_O@&~Ug]MY@$+D&&5463256673K43. ' @T4&,- #P #VFO\'ȱdDK PX@0 p  ng gX_OKPX@1 p  |g gX_O@2  ~  |g gX_OYY@'&"!"""" +D663232673#"&&#"&&54632 A) 2(? ?)1*//&"9:=60$' :<&MB:<&Lh:C &:[ &'&' &#:J&QB:B`&[&<MJ3+[m&<Lp3+R-'<0 ̰3+47'<" ̰3+C&3\# Ű3+c@ kdDK"PX@UW_O@We_OY@     +D&&'73"&54323"&5432k&p  E"" E""j#Q" $O H!'H!'cF wdD@ JK"PX@UW_O@We_OY@     +D56673"&5432!"&5432* {;# E"" E""j V !N'H!'H!'"^ ,dD@!JU]M +D&&'73]!u^&O! $O ,&&n,"&,&'",&Q,&'QR+'02p ̰3+4'"2M ̰3+R@&v0 ̰3+4&S" ̰3+ &,^ -dD@"JU]M +D56673,# F^ T "T T' UdDKPX@~mW_O@~W_OY$+D&&54632 64-$-T3)+1"{t@U]M +3*{m!@  Ht +'7'77'>RRSSRR>>RQSSQR>d{ 4@1JHU]M  +3'7'7#@ll@y=kk=H+{ 4@1JHU]M  +#'73yAmmA=kk=!$$@U]M +7!$$pp\'_~_h$@!]oL    +&&'73!&&'73m3~9 @13~9 @1{Z$@!U]M +3#迅'H,{Z$@!U]M +#53'!{t&@#U]M +#53#__b{t 0@-eU]M  +#53#3#__b'&{t 0@-eU]M  +#535#53#__b&'?'&WgZqE #/K#PX@+ g_wK _rK_ xLK)PX@( g c_wK _rLK2PX@& g g c_wL@,g g gW_ OYYY@#%$ +)$/%/##    +"&54632"&54632"&54632"&54632(--('//'(--('//'(--('//'(--('//C'')%%)''''(&&('''')%%)''''(&&(''{t5@2eU]M +#535#53#3#______b&''&{t 2@/eU]M  +#53#35#__bb{u%@"JU]M +'3``}{u@Ht +#7#`uu`b{u @ Ht +'77'`uu`>>>sYkkY777{t 3@0eU]M  +#535#533#_____b&'&]U1@.gW_O  +"&54>32'2>54#"@J/O32&&#"36632'2654&#"DQ9fO:  )-60!77+N4 " WU&^V7\ #5@22N-U. # [T(J@G JggW_O$"(( +"&'532667##"&54663272654&#"( ,1<" /!77+N6KJ@y"#[3A11O-RFXI+.CFD̠)&f 3+&FH̠)&f 3+,FR̠)&f 3+EF[̠)&f 3+F&̠)&f 3+8L!G@D  Jeg_wK ]  p L!!%& +37"&54>32&&#"7!#3#3*[^"DjH1F.0:Q$'fZ>v_9 r ha/+pIpVr s'29O@L40)&$J~a]qL(((2(2.+''#"! +7&&54>7733273&&'667#7&'7&&#""'V/14*WYJ " J)9/G1J$#  a.=:v mGDvP JJRh y  LL XlGCUc-*"h0KPX@  .-*#J@  .-*#JYKPX@W_wK_xL@g_wK_xLY@&$  00 +"&54>32&&#"336632&#"667z;XyLD_/:"I-9S6 Ab =& *7d ?u]5z<`q49I1X-4} .2\  V/KPX@  "J@  "JYK PX@"no_rK pLK PX@!o_rK pLKPX@ _rK pL@$rK_zK pLYYY@//%$"$ +333663273632#654#"#7#7654#"tp  S9,8vdMDD AC0"5( / ed 0"5' 0"e1>$!uUH$4?#<8_;+x5#<9`:L#'+]@Z( Jf   eoK  p L+*'&%$#"!  +37#737#73733733#3##'#3'#3'#37#37#477765M5s477774W4])@/$ 6 JHJJHJkHHHj  <@- . JKPX@+e]oK _zK]  pL@/e]oK _zK pK_ xLY@20+)<< ! +332##32654&##"&'532654&'&&54632&&#" ksK;@15:T8E'/f,I E%0++/eX+P&0:!$+7q_cQk;9(%y >4Mbi!?8^`"&*.14h@e"J  f   eoK p L4310.-,+*)('&%$#!  +37#737#73733733733#3##7#3737#37#37#7#7#J RG;0 mX^z WZp[1=HT[X^ +@=%*@! JHJJHJ\HHHHHddd3@0JfoK pL +3#7333733##@IIBD xc_I=5/a:3a!"'Z2@/ J]oKpL +37'77'77#7!#77T Y!*\!a!/v4;TX7;W~~]9;ZX<;\/<@:*JKPX@7  g_wKrK` pK _ pK tL@4  g_wKrK^ pK _xK tLY@10750<1<//(&!%&! +33326654&&#"'6632#6632#"&'#2654&#"tZOm95_?+[$-&y=rRft;Rf[[R%SF"3  "2 "*  $"WNS8U/pN]ceX[F-X:E-@6."&15 N'3C@@"  J_wKpK_tL/-'' +"'532654&''#7&&54>326654&#"! %" 0Id,O'@M&FP.J,m"/) sC7 I+d`Jz5F\6SE;f`1C>ol-h$=@9\!'-V@S e  g ]oK] rK p L-,*('&#"!! +3#737#737323#3###3&&##3665#3267#K66 66"bb</.9#q(u"3$$(<~cE;E[GEEMV( <!'L@IJewK]qK_xL'&#" +7&&54>773&&#3'6677#]g-XV W_U8$H+*O.fJ*_8K%$y! EPMtS{Q ($,{F`y<9Ms"J@GJ f   eoK  p L"!  +#7#737#7333#3##'#3&&53'#n4 R]JHJJHJ?&EHN5^@[2 3 Je   e_wK _ xL0.*)('$#"! 55 +"&547#73667#7!6654&#"'66323#3!3267ln 0I7A(%#C0*6a==Z2 /PC(.+4S6,d iP'!J$ J #u-Q6*#J!J$"%~<%-@*  JqK_wL +7&&54>773&&'667'fh+X^W)E":7e"D#T6,c/E,'PJrLU$'z"  G BZd/?ME;JK*PX@/p~ eU]M@0~~ eU]MY@"" +37327#73&&##7!#3#͈Ck)+F]Sdh0]IJ&hJ3JAY Z<p pp3+1$/7X@U ~gge W _ O%%7520%/%/.,(&$$  +".54>32'26654.#"'32##532654##Pc67dNLe96cPRP-Pm?VNM/RLV>RF#+OE 6cPLe96cPPc6@NY?nT/NYYNTEDCL#'F 9@* + JK PX@:   ~ ~e  gW_ OK PX@3   ~e  gW_  O@:   ~ ~e  gW_ OYY@$/-(&99   +333#'#73'&45"&'532654&'&&54632&&#"ss;i.Yl'M@96."0M>;0#%Q6rXJJS O   -'4: I   * <:" .@+eU]M +#737#7373)tppGW't'{~u`~3+`3+u'&tub3+b3+,!<@9f eoK ]  p L +37#737#73733#3#3/VVVU55VJTTJVa}X5@2f eqK  p L +37#737#7333#3#3JJJJ::LLLL3TLTTLT=@:JghoK^ pL"""" +3&#"#663233273#"'3E JP1 =G !JO1 !FRD!5RE}gkK,PX@%e]oK]rK pL@#ee]oK pLY@&! +3#73732##32654##3#\KL&Wi/RXL5P=CR_F gha4^>Up7zB:X7aH%I@F Je]oKpK_tL%#  +"&54732##326732654##GRk}ZCe:F 5F&@G`$@A\hUk r }89P0O",KPX@+)J@+)JYKPX@& ~qK_zKpL@. ~qKrK_zKpKxLY@$##,$,( +"##7&&54>32733#7#'#"2667654'fat&$B]:0!tan`tnK9/#?4% SBCk@G$- +FV0K+$.0!"R@O!J eqKrK_ xL  " "  +"'#7&547#?3373#32677gVy8J`@_;mVy %C+ 9H)stZ n UU0<D!0@-aqK_zKpL!!)$ +7#654#"#31366323*_C5"5' 0 J1FG *PA?#<9`:60%.UH$40.@+ JaoKpL +7##37733~-#g=5KLʰ\[H!"f<i2@/ JbqKrKpL +7#'#33733X*%Y;$HݨXOA#B0J +@(]oK]pL  +7!7!7!!-{#Gb}c<" +@(]rK]pL  +7#7#7!3*nBXXrb<(KPX@ JKPX@ J@ JYYKPX@_wK_xLKPX@oK_wK_xL@!oK_wKpK_xLYY@" ((  +"&&54>32373#'#'26776654&#">[31\S.=)"|(X Mb7>4O6: @tM\O,V6V,4o+&#ժ z(Z''V"p'O)*b"AJv'= `(OPB=l,1W@ +JKPX@]rKpL@rK_zKpLY@11%+ +333>73366776632&&#"#'4667#Vq 0 G?'  r"(O.=2 "F)%S-hFN s '&<>;7U, ,e@JKPX@g_zKpL@grK_zKpLY@(& ( +3'#7267&&546632366736654&#"60+D&4CD8   s  R*54&#"G]U^9A)!6aZ\bS79H"" hjEV8vE49`br`g\y Ll7&1'/JB0+7'%'%4$$$$5555}1@.W_O +3'"&54632@]$$##}Kh&^ *@'J GW_O  +"&54632'7$$##BBBB^^ 0@-gU]M      +"&546325!)$$""K]]xSFK PX@nU^N@U^NY@ +5353x5S5qH@Ht+5#7#H:z@t+'3533AL:UHEe1@.W_O +3"&54632/cN$(#%$ X &) $,ER5o 4@1~W_O      +"&546323$(#%%c[/ &) $,jX$we e3+S BK PX@o]qL@]qLY@  +7!##7#0# ]5BS5qqqAm BK PX@n^pL@^pLY@  +#7373373 ]4C5#5qqq.OJS а3+9 (& FPF3+3+/@,eU]M +7!7!?lllhjjkkI<@]oL +73IM}#`r@]oL +3`(]rXWq  dJK PX@_oL@_wLY@       +56673"&54323"&543229< E!! E"" H" 2/H!'H!'Wq `JK PX@e_oL@e_wLY@     +&&'53"&54323"&5432BB  E!! E""F# "A" H!'H!'Ke$@!W_O+667"\߃^?Kg^c21PK$@!W_O+&&'72AqkK+7cea_[ .@+JW_O %"+6632&&#")~B8[*5b:">_NKf E` 0@-JW_O   +"&'7326735\+3c<#=K(` f MK,^z&{a,^z&{a,^z&{a,^z&{_,^&{_,^&{_,^&{_,^&{_/z&/z&/z&/z&/&/&/&/&:<z&^:Fz&^:<z&^:Fz&\:<&\:K&\:<&\:A&\/&/&/&/&:<&\:<&\:F&\:F&\.%h@JIKPX@c_#K$L@c#K_*K$LY@%%+"&'53267654#"#336632U*(0<WW0U?K} *gJ6T0 ]{|;@%WAoDs5I/V:!*H{j. P+{IKPX@*~_#K_#K`+L@(~_*K]#K`+LY@"  +++"&5473326776654#"#336632Bu}mEN'W0T>O} *gJ6T0 ,L s["" WQD WAnE ts5I/V:!*Pv@oUzKPXJKPXJJYYK PX@gU]MKPX@g]5LKPX@gU]MKPX@"UgU]M@#egU]MYYYY@( +767&&546327&&'737! 8L!s>*"&3? &O! $O {]]mUzKPXJKPXJJYYK PX@gU]MKPX@g]5LKPX@gU]MKPX@"UgU]M@#egU]MYYYY@( +767&&546327566737! 8LR A>*"&3? #P #V{]]qUzK PX@Wg b7LKPX@"Wg7K ]5LKPX@Wg b7L@'~ge b7LYYY@$ +&&54632&&'737! 43. 'Z!s>4&,- &O! $O {]]pUzKPX@  J@  JYK PX@gb7LKPX@g7K]5LKPX@gb7L@egb7LYYY@$ +&&54632566737!43. ( A>4&,- #P #V{]]^f'N'v^3|'NK3+^ ^ ^K"PX@g]oL@gU]MY@     +"&546323"&546327! #&#$##'"$#>#%!'#%!']]]q -G@D  g c_wL --+)'%#"     +"&546323"&54632663232673#"&&#"#&#$##'"$# C--&K B.*%#%!'#%!'@>C;],{ D@AJ d_wL   +56673663232673#"&&#"!6:> C--&K B.*% @! .-@>C;Uh zJK PX@eU]MKPX@e]oL@eU]MYY@  +&&'537!AC#>I" "A" ]]Uh JK PX@U^NKPX@]oL@U^NYY@    +566737!DAD> I! 41]]W1r)h JK PX@ _oL@ _wLY@%#)) +&&'536673"&546323"&546323O/9Y65#&#$##'"$# H *& .0#%!'#%!'WWXK PX@e_oL@e_wLY@  +7!"&546323"&54632;>#&#$##'"$#]]#%!'#%!']W;@8e c_wL  +7!663232673#"&&#"C> C--&K B.*%]]@>C;WWJK PX@e_oL@e_wLY@  +7!"&54632>> $ !!]]#%!'-/@,eW_O  +7!"&54632$> $ !!]]#%!'wM $0JKPX@*n  h  _ 5L@)  h  _ 5LY@&&% ,*%0&0 $$   +56673"&5332673"&546323"&54632,j .2RBH$//8 Ia#'"$##&#$#Y : (( PU&-0#J[#%!'#%!'wM #/|JKPX@$n h  _5L@# h  _5LY@ %$ +)$/%/##  # +&&'53"&5332673"&546323"&54632-3 g@RBH$//8 Ia#'"$##&#$#Y<6SR&-0#J[#%!'#%!'wM &N@KJ f  _5L " &&    +566737!"&546323"&54632,m .2>#'"$##&#$#^ :(& y]]#%!'#%!'wM %kJK PX@nf _5L@f _5LY@ !%% # +&&'537!"&546323"&5463253 g>#'"$##&#$#^<5y]]#%!'#%!'xD)KPX@ J@ JYKPX@%W g c?K 5L@-~g e c?K 5LY@'&$" ))$ +&&5463256673"&533267343. ' @bPIH'.1/I^4&,- #P #VC@;HxD)KPX J JYKPX@ g c?K5L@% eg c?K5LY@'&$" ))$ +&&54632&&'73"&533267343. (Z!sPIH'.1/I^4&,- &O! $O C@;HxD&KPXJKPXJJYYKPX@ g c5LKPX@ Ug c5L@! eg c5LYY@$#!&&( +767&&54632756673"&5332673 8LR @lPIH'.1/I^*"&3? #P #VC@;HxD&KPXJKPXJJYYKPX@ g c5LKPX@ Ug c5L@! eg c5LYY@$#!&&( +767&&546327&&'73"&5332673 8L!sPIH'.1/I^*"&3? &O! $O C@;H "KPX@J@JYKPX@]#K]$L@]#K$K_+LY@""+"&'532667667!73##'#($ (&E,NKTIm('!CR |77D uLy+U]#p,'KPX@J@JYKPX@_,K]$L@!%K_,K$K_+LY@  ''+"'532667>3273#'#'&&#",*"0@bJ68ww=@ (1%!,? y/N.EfB!.ZC2XC&:@7 Jg]#K$L#!+33273###32654##Wi/g*yG*5PCR_$4^>F%#zB:XZ,-|@ JKPX@]%K _$K'L@'%K_,K$K _+K'LY@ '%- -$$ +33663273#'#"&'#2>54#"!pN<"C,wEAV426 #/#@$2 %S#:!K?8Y5."(B&]+FV+V4NR'/ZJ@GJe e ]#K] $L' +#&&54663!#3#3!#37#"=-3@`!'{:95)B=B=AIg   g _,K ]%K_ +K_ +LYYYY@#BAECAIBI?=:820+)%#  77+"&'5#"#"'53267667&&5466336632##326732654#"37#"1ep $>4,  &!,@l@ = RY3.+F0+XpUK6,I->$0" id)>" , y*) 6/AR&TAVh ,4oF3#0@S)%!-@*  J#K$L+337'773'#G8O*MEP6BJ`1FYs1ZL_"-@*  J%K$L+337'773'#u6z0M%9?O3(9"~K39;`3N* 9KPX@-! J@-! JYKPX@)g]#K_$K_ 'LK2PX@-g]#K$K_+K_ 'L@*g c]#K$K_+LYY@1/,+%# 99 +"&'5326654&#"###"&'532667667!6632|*I@%6P+GB$ 9}('!CR8$ (&E,F,,WF*&Ny  =iCPTLLy+U]#|77DAiKFk@ ,;KPX@01"!J@01"!JYKPX@)g_,K_$K_ 'L@-g_,K$K_+K_ 'LY@53.,%#  ;; +"&'5326654&#"#&&#"#"'532667>326632@56@*,'\(1%!,?/*"0@bJ2Z/#\^@q  9[15>.ZC2XC& y/N.EfB! }b7ue>*@ JK2PX@(f  g#K$K_ 'L@%f  g c#K$LY@"  ** +"&'5326654&#"###3336632*I@%7O+GB$ 9AA;;F,,WF*&Nx  =iCPT4AiKFk@ "'K PX@J@ JYK PX@'V h%K$K_ 'L@(f  g%K$K_ 'LY@! '' +"&'5326654&#"#7##33736632@56?*,'/0u-,4#[_@p  9[15>"}b7ue>0 *@'a]#K$L +7###!3-}} ~FL<e,\JKPX@a_%K$L@a%K_,K$LY@$$+7#654#"#3366323.~C5"5' 0tp  S9FG 'nC?#<9`:"e1>UH$404@1Jga#K$L#%+7#76654#"#366323-5K&G#D:$Y-,Q3F  @E: <e!0@-a_,K$L!!)$+7#654#"#31366323.~C5"5' 0 J1FG 'nC?#<9`:60%.UH$4cF JK PX@oU]MKPX@o]#LKPX@oU]M@U]MYYY@ +'7!#'##'#  8" @"" @"cT++T2222 !?@< J~oK`xL  +"&'??3776673'GBXYYY&0Oj .WCW2W2B1W2Wa +@(oK^pL   +33333|}6},u#;@8JcoKpL ## +"&'53267>7####333$ !'1 ? F  ϓi}8D*"MK)IV)Jzn&KPX@! J@! JYKPX@~_wK_xL@#~_wKpK_xLY@ && +"&'532654&##57&&#"#66324R" R(:IGJ 4+AH\`Pk; ?L 96)3_}$QPLČ;]4sYItv`$ ʰ3+B%Y@ J HK#PX@~rK_xL@_xLY@%% +"&&54667326654&#"76632OMzFL]wT{C@:4@( $ .OY+Pp =y[o 9.gjDS5U101x lfBx\6 2@/ J]oK_xL  +"&5466736673'3267}:3/Q1M{G(B'18F->@;JfcoKpL  +"&'53267##333S+ .'8A;;j|@= M}mR&7 3+g$P@M !"J~|_wK`xL $$ +"&546675'7732&&##"3267 Ck<9y.BN#q5:W1F:0X5/b qVF^1_by(%f"Bqk0,-/};@8JeoK_xL  +"&'5326777337667#,'' ffi~ " |.-rq}meP 9,^*X@UJfoK_  xK_  xL  * *&%  +"&'53267!733#37667#7>73,,' fMi~ "p 6" |:7rqt}meP 9,* => &Y.&$$.@ JKPX@!f_oK _xLKPX@%f_oKpK _xL@)foK_wKpK _xLYY@! (& .!.&# +#36632#"&'#31'.5#26654&#"<v /[,[bhT   Z:`9=?!Lʰގg=5 ~AA~f!"-@*  JoKpL +337737#''7'KLʰ+Y/hC!M.\'=5fp9KB]0K;n";@8 JfoKpL +3#73733#7737#''7'o65>?!Lʰ+Y/hC!M.\'=5 ~AA~fp9KB]0K;n"gV (@%JoK^pL  +371373X @6 G23,23} -@*foK]pL  +3#73733#3oIHZZT ~AA~r} )E@B e _wK _xL"!%$!)")  ## +73>323##"&547"!654&267!,\c6-[fl@c5=@hB4rRSyrWV{#cNBOeZGJ<*9 JK PX@" g_wK _xL@, g_wK_wK _xLY@,+! 31+9,9'% *!*  +"&54>326632#"&'2654&#"26654&#"E-]bH`B/19-H*^2* (:`9=<.O9 B {TQ+""*'@%>^4 o\=4C.1SfBO5^yEGJ< :g9@6ee]oK pL&! +37#7332##3#32654##CCmWi/RXL ]\P=CR_FI}4^>Up78}IzB:X%8@5~g]oKpL%# !&!& +7&&54663332###7#"%32654##+\JGWi/RYL56!=CR_F7U1P4^>Up7( B:Xb,4KPX@ JKPX@ J@ JYYKPX@h_wK pLKPX@$~h_wK pL@(~hwK]oK pLYY@42/-,,&!)%)! +!7#"&54776654#"566323332##32654##26\m 8">6P GWi/RXL5P=CR_FeR%- i70$* IO4^>Up7zB:X6O)<@9J_wK_xL#!))&# +'7'#"&54>327#''26654&#"0!X -]b0cLg"X8H:`9=<.O9 BQ$2{TQy\$)Q#W SfBO5^yEGJ<.Y'6?@<'$J_wK_xL)(0.(6)6&%&# +'7'#"&54>327>54&'7#''26654&#"# -]b0cL**10AMMB:`9=<.O9 B4SA{TQy\$.*; 54#"'66323733##7:D5,H#aJG\$L.0c/JD1MK/BM@  JKPX@oKpL@oKoKpKpLY@  +73336673#'m+B Қ 7  }4O!G"6hqZ1?@<& JoKpK_tL,+! 11 +"&'532677&&47##3366733667392). "  $/;P 4#W+:.PBz&\''Yp6=)_"B3[F'%=@:f  geoK pL&! +3#73733#32##732654##vIJ bb 0Pa+KQG*.>LX70d66d<0V9Ng2N;6QA=@:  heeoK pL&! +37#73332##3#32654## MMu0Pa+KQG __ F.>LX7>d(P0V9Of22d>F;6Q%@" JoKtL +77'7!>73S[J<]8  !#"LV((VV(Z#45w,h(VJ)D@AJg_wK_xL'%!  +"&'532677#737>32##32654&#"P,.(  aIBg;e}m5/=&YN$9@6Jg_wK_xL$$%%%& +52>54&#"3267#"&546632>hM+&1#5)! 3P]AzTvpm w$Ui7D)D*(+c jWQI}k<QK,PX@]oK_xL@g_xLY@  +"&5467#7!2'26654&#"E;BpR,[W:`9=<.O9 B {YLyWOSfBO5^yEGJ1{KPX@ J H@  JYKPX@f_oL@foK_wLY@%$ +336632&&#"3#p !T5  7W ]e4;WV6~I+K -2@/*J]oK_xL%#   +"&&54667&&5467#7!#"'32654&'5^;4\:&w}"" 5>;t**6;) +Q99^C*#1zz 4h733#7## H~fF-y GCHR->A%E@B  Je_wK_xL%%%$%# +73>32&&#"3#3267#"&5471]dC`/:#H-5P8CI)S)dA-~OSz/N/~ QP 'E@BJe_wK_xL$#"! '' +"&54777>32&&#"%32677#7!D|25ifk]8$H+Ji" DL%I1m -*ZP~I0{G7DZF12PSt~)@& JoKpL +3777317737#5OOOKLʰyg=5Yuf$Z"!" ,@)   JoKpL +3773737#7'36679NNK`v=8RRLcz=g M0I Y c Z 44E" T(  B@? Je]oKpL% +3?327#'#7&&#3267UON/h{ Q= O9w[J4} 1,G2K Z PN Z H\NN&(-=@:(&%J_wK_xL -- +"&'532654&'&'77&&546632&#"76Y,V-1C0& X 323732>54&#"Dc12g3L]  V:$I=&,TwK@Q&q;Rf/J3333L171F($&-=bEK~M92`BOc6@eq11G6[l6EG!v JKPX@& hoK_rK] pL@$g hoK] pLY@!!$! +3#"&54632733#3376654#"_B@9F3 +E]^'"6;):= M}#o. ʰ3+73 ʰ3+$y,@)JgcoL#($$ +&'#"&546323%3267&#" D(;EEE!    =47O /+#/;f% #KPX@ "J@ "JYKPX@~wK_tL@~wKoK_tLY@##%$%$ +&&#"'663233267#"&''v,  7A: "ɧ: 2!C> #'r 36;%s =@c#?@<Je]oK]pKtL#!,! +32##32654##32654&##f|RJ1:JOj3I5ASJcX7C,-QJUI^ E5Qg1+1C/74&)A 8&$P@&(P&,P,E&8P&/ &1 {" /@,e]rK]pL  +#737#737#7! )t^]y]"'")Y~,,0:KPX@ .J@ .JYK PX@'pg_zK` xLKPX@(~g_zK` xL@3~g_zK_ xK` xLYY@8631,*#! 00 +"&54>32##326732654&'3#"&'32654#"`x'MrK[\2/+F0!] 5*( 'QhUK35) hkEd;TAVh ,4&,>5  F5#."< , ,8[@X .'2(Jge_zK_xL,*%# 88 +"&'532654##732654&#"'663273267#"&'8`![54JVV=>?!*!Q(#0j=Fg &<#/@(=()4 y)9g$k+1!3.' S7> &  5.Rd,,(1M@J$-Jg_zK_xL*))1*1"  (( +"&5466774&#"5663273267#"&''2667W\D])2,)C/*V9Ws !;#/?: J{H3'! PA8H/ 72o[\$3.' U7> NNj&B)A+"@rKpL +33tt""&'P"+@(JrK_tL  +"'532673+3"- | .P v (+G2R1/@,fqK pL +3#73733733###zLKKJzzz@cUUUUc@"3@0 " J_qK_tL#*%% +7'776632&&#"7#"'53267,z.dP"9' !J,S .P=3"- 322654&#"62776654&#"=_5$HmI_p"Gm8$9"+!1$!6 3^AHc8rfE~b9>K,:&?K%xhFM̠)&f 3+#KPX@ J@ JYKPX@~qK_xL@~qKpK_xLY@## +"&'73267654&#"56632#7#  7W6  - >Jvp !T WVw KD!e4;6"=@:Jf%K_'L +"'53267#73733#,3"- 9LK//KK= -O v (+ aa2R1R"8@5h%K_'L  +"&5463333#'267#".>B[QuuJJ /MD#>,@F"M/J*R&+ gG)=)&f 3+")F')&f 3+"d@ JK2PX@~%K$K`'L@~d%K$LY@ +"&&54677#333267!;%Lt]5%'  $4)  E"K "h 0g K PX@ noU^NK PX@nU^N@U^NYY@ +7#73733#A 1 1`1 1 ::gG1=)&f 3+n9"&'Q ܰ3+."&' />T$@@=ggg_ pL$#!$! +732654&##732#732654##732#u '+:A37'O=\ ((/5+1"D2G5*#a7'7732&&'#"3267Ps<.IV)!=\+/$N&qc{D8-V-9B:e@9[C'qbhp 7jIM3:{  ":@7  JrK]pK_tL#& +#731#"'532677'37>7#,ti .O>3"+ { !r h4S0 v (+%uf:1 /2" *Z@WJrK^pK_  tK_  tL!!!*!*&%  +"'532677!733#37>7#766733"- tZ .O{  "h# 6! v (+%rTv94S0fq:1 . "b% &W/" )@&e]rKpL  +3!#3#s0-"_^-&7@4J_zK_xL&& +"&'532654&'.54632&&#"*E"C#&3%+gVRD)5($ .-8^ h%*9%I_%X""H1:L%-,'7GKPX@  $!J@  $!JYKPX@  _zK ` xL@(rK  _zKpK ` xLY@'98)(B@8G9G1/(7)7#"  '' +"&54>323736632373#7##"'#7#7267654&#"!2667654&#"DS$C\72;\ =+, qtn4(B)\F+? "."$Y4%%/# eaHh<)'F,,$FG)(8.G$-wX>..'/+FV+/'0K+$.#-+FV+V,W,$4BKPX@  !J@  !JYKPX@ _zK  ` xL@&rK _zKpK  ` xLY@#65&%=;5B6B.,%4&4  $$ +"&54>323736632#"&'#7#7267654&#"%26654&#"CT$C\72<\ B/3R/"DgE1A\F+? "."$^)9'0 eaHh<)'F,!3aDE~b9.G$-wX>..'/+FV+/'Bi;)7-HR%Z,x,)9KPX@  &#J@  &#JYKPX@ ]rK ` xL@%rK _zKpK ` xLY@+*31*9+9%$! )) +"&54>323733266773#7##"&'#7#7267654&#"CT$C\72<uC5"5' 0tp =) ,\F+? "."$ eaHh<)'F#<9`:e1>.G$-wX>..'/+FV+/',, 0KPX@ J@ JYKPX@]rK_xL@"rK_zKpK_xLY@"!+)!0"0   +"&&54>323736673#7#72667654&#"(F,$B]:4Aq6 %ī_K4%$ /# 'WHCk@,$F68B&G$-w0K+$.#-+FV+V(,)4KPX@ J@ JYKPX@"f]rK _ xL@*frK_zKpK _ xLY@0/$"))  +"&&54>32373373#7#72667654&#"%6677#(F,$B]:4Aq|X_K4%$ /#% %'L 'WHCk@,$F||G$-w0K+$.#-+FV+V#B&7#68,,.>KPX@$ J@$ JYKPX@#]rK _xK_tL@'rK_zK _xK_tLY@0/97/>0>+*.. +"&'532677'##"&&54>323733>732667654&#"" .C P8(F,$B]:4Aq;  ǫ00l:4%$ /#u<(S*5'WHCk@,$F8:12&yCH]0K+$.#-+FV+V,i7@4JfqKrKpL +3#73733#373#'|NM~~"ݨY;$GbOObB#i1@.  JqKrKpL +333737#''7'HݨB0NI<.I;$BB1C:F,B6;#i?@< JfqKrKpL +3#73733#3737#''7'|NM~~"ݨB0NI<.I;$GbOObBB1C:F,B6;#[ "@JqKpL  +371371TRB ^224Dq '@$fqKpL  +3#73733#|<;<;|GbOOb,!)E@B e _zK _xL#"&%")#)!!## +773>323##"&&547%"3454&267#BInJ_pLBGoN=_5'6a(8jAg:rfjBj>3^A >, )7@2 Z,,(6 JKPX@" g_zK _xL@, g_zK_zK _xLY@*) 1/)6*6%#( (  +"&&54>326632#"&'2654#"26654&#"=_5$HmIe68%<;(D) Fp#)9'!2  3^AHc8> H48S. Cm?S, ,/*Bi;)7-HR%Z,s, l',-@  JKPX@' f _rK _xKtL@+ frK _zK _xKtLY@ '%- -'$ +73336632#"&'#3##72>54#"\KpL5'G,$B]:26!/#@$2 %bbS#:'WIBj@."F@bNN+FV+V4NR'/,+;@ 9JKPX@- ~g_rK `xKtL@1 ~grK_zK `xKtLY@-,42,;-;+*2'$5 +&5466322336632#"&'#&"#"%2>54#")(VDRpL5'G,#DhE#8 ; !"\!0@(",B 5X4S#:'WI;oE (B%k) +GT(V/2 )1,8CKPX@(#J@(#JYKPX@_zK`xKtL@"rK_zK`xKtLY@B@;:88'$%. +7&'&&546776654#"56632336632#"#>54&#"40$5>  9#7<$(YpH9'G,-[] 0n%P\'24 fM6#l99 $01: S$9'WIKf:Cl>++T,A,!1KPX J JYKPX@' f _zK _xKtL@+ frK _zK _xKtLY@#"+)"1#1!!'( +73>1##"&&54>323733##72>54&#"| D3$C,$B]:1>qDC#1$ /#$b/+"1'WHCk@*&FbNN5OQ'/+FV+++,,+;@+JKPX@&_rK_zK_xKtLKPX@$g_zK_xKtL@(grK_zK_xKtLYY@-,53,;-;'( +'7661##"&&54>3237376654&'7#7'2>54&#"{& D3$C,$B]:1>qNl!**! ;A20D!##1$ /#$,!"1'WHCk@*&Fd6% ;B+3K15OQ'/+FV+++D"/@,Je]rKpL!%! +!'#7326654&##'32Vip1 &%?|VgP>id# iLGMPu,$B@?  J~_zK^pL$$%( +#77>54&#"'66323733##7.0, A+^A5C!F;K<=a+3$^!2&?$"@M5Bu:0@- JrKpL +37336673#'|/ yB  ٞH] CA"8=G"2?@<( JrKpK`tL/.$#22 +"&'5326774667##33>733>773A# (6 rqk%mu4,<>;7"(O.=2 "F)><OQ' 2@  JKPX@-qK]oK _zK _xK tL@+fqK _zK _xK tLY@"!*(!2"2 &% +#73733#3632#"&'#2>54&#"!DDll ?KDV=\>2> #/"% 0"!CbCCbCSbe54&#"RC  ?KDV=\>2>  /"% 0"!b2 LSbe773!< C2 .21/ ,*D@AJg_qK_tL(&"   +"&'53267#737>32##32654&#"%&-'K 9eOCS'H~P5Po518 $*x9Hey49gA-M.Of1yq5.>&2$-&9@6Jg_zK_tL&&&%%% +526654&#"3267#"&54>32dRS&5!3( #U`&Fb;Tg.>tsSAV/N.71m q^>pW2HVpK,c#2KPX@   JK'PX@   J@    JYYKPX@-  ~qK _zK ^ pLK'PX@9  ~qK _zK ^pK ` xL@5  ~qK _zK^pK  _ xLYY@!%$-+$2%2 ## +"&&54>3234667733733##7#7#72>54&#"(F,$B]:18%)P[PgQ[Qu K "2$ /# 'WIBj@."%0{sG$-w5OQ'/+FV+Vc 2@/~qK^pL  +333733##7-P[PgQ[Q{sc ,4  JKPX@*  ~  _rK^ pL@.  ~rK  _zK^ pLY@440.*)%#%$ +3336632366323733##7#654#"#654#"tp  S98> V9DD ))P[PgQ[QC0"5( /C0"5' 0"e1>;41>UH$4ěs?#<8_;?#<9`:c,"yJKPX@'~_rK^ pL@+~rK_zK^ pLY@""$$ +33366323733##7#654#"tp  S9FG )*P[PgQ[QC5"5' 0"e1>UH$4ěs?#<9`:KPX@ JK"PX@ J@ JYYKPX@$oK_rK_rKpLKPX@!oK_zK_rKpLK"PX@!_zK_rKpL@'rK_zK_zKpLYYY@#! +33366773&&'#7tp 'n>K[K1(FM\K:V7"e19 GGc"!L@IJ  ~  e ]rK^ pL!! +3323733##7#'#326654&##tVgP>3|PZPgQ[QV3+@:1 &%;"LGMPks/# .V)3KPX@ &'J@ &'JYK PX@+ p _qK_ rK_ xLKPX@,  ~ _qK_ rK_ xL@0  ~ _qK_ rKpK_ xLYY@20,*$" )) +"&547#?337>32####326732654&#"@N8J`@_ 3VAJTng-\\8%Cz %*. 7GH)st6W3LAWiO  n ,$ I".1@.& J_wK]pL-+"!/! +#7326654&''7&&546632#7654&#"5: S 1S3;J#*'!#AtNK1s/"f'N#32326654&#" 11@!EkJDg9>tQH 0;).):*d((hI3dQ1/Y=MwD71ir/K,&/0K+),"@rKtL +3!X,&h@JKPX@_rK_xKtL@ rK_zK_xKtLY@&&($($ +336632327#"&5477654#"!p  S9FG (4GM 5"5' ce1>UH$4# qD<#<9`:.,iKPX@ J H@  JYKPX@_rKtL@rK_zKtLY@ %$ +336632&&#"#p !T5  7Vie4;WV,"5@2J]rK_xL  +"&&5467#7!#"32679\6]K&=_B#,(%?"#N 'O>T(ss,GQ%+.x' -O@LJg h cqL"!)'!-"-   +"&5477#"&5463233#327376654#"GM.@9G6 9S__,(2"fD<;):=  yM# q#<H,\JKPX@a_rKpL@arK_zKpLY@$$ +33366323#7#654#"tp  S9FG (RC*]C5"5' 0"e1>UH$4?#<9`: ,%E@B  Je_zK_xL%%%$%# +773>32&&#"3#3267#"&&547CJlF1M"-3 +?uu,(%?"#N39\6cBf;p =/c //x(YHb,,5?KPX@ ;:10, J@ ;:10, JYKPX@"_zK_xK_tL@&rK_zK_xK_tLY@76.-6?7?-5.5*%(% +'77>323737#"&'532677>1##"&&5"7454&26672 Ce?5=q'HH=KkC=P%&U/3@ D3'C)'=$\, %Z KQ+%FZVc*/2%"1)UAQ9# '/&>$#++i-@* JqKrKpL +377733737#')CCeHݨ׷pY;$ Z BZ#k, ]@JKPX@_rKpL@rK_zKpLY@  $ +37773366327#7"7654)<<8p  S9FGFD72.&; Z e1>UH Z J:<,qKPX@JH@JYKPX@_rKpL@rK_zKpLY@ %$ +3777336632&&#"7)NN8p !T5  ,J. Z e4;77Z,*=@:%#" J_zK_xL** +"&'532654&'77&54632&&#"75M"%N)#6)Qnb/X*0A &)> } yZ !/UYiZ '^`Y|+5, O"" "3+,$3@0JH_xL$$ +"&5&667326654&#"76632kw`}7by?,'.   FS@u ss˟0R5zzm*?94M'&6taY\N;",@) JrK`tL  +"&54667336673'32677B:b3"- di -O v (+G))cO m ' )cO m ' p2R1 v (+2R1AMQK.PX@#$ 65J@#$ 65JYK.PX@<_qK _qK ]rKpK _ tL@9_qK _qK ]rKpK _ tLY@1NNCBNQNQPOIGBMCM=<9742.-,+(&! AA +"'53267#?6632&&#"376632&&#"3##"'53267#"&5463233"- dPY cP"9'" cP"9'"kki -O>3"- di -O~ */*,+tt v (+G))cO m ' )cO m ' p2R1 v (+2R1V!*,!'0"AEK.PX@#$65J@# $65JYK.PX@._ qK ]rKpK _ tL@2 qK_qK ]rKpK _ tLY@)BBBEBEDC=<9742.-,+(&! AA +"'53267#?6632&&#"376632&&#"3##"'53267#%33"- dPY cP"9'" cP"9'"kki -O>3"- di -O v (+G))cO m ' )cO m ' p2R1 v (+2R1&ILu&IOu 6a@^,J_qK]rK_xK_ tL0.)(#! 66 +"'53267>323#3267#"&547#?&&#"#3"- Ln@:gK8%C(@N8JQ 468 .P v (+fER%$p  n 7GH"N742R1 S%KPX@ : DE,-JKPX@ : D E,-J@ : D E,-JYYKPX@% _qK _zK_ xLKPX@/ _qK _zK_zK_ xL@8 _qK _zK]rK_ xK_ xLYY@IG><761/*(#"! SS +"&'532654&'&&54632&5466323#3267#"&547#?&&#"&&#"5M"%N)#6 237nb CpB$O.+2 ''FH932&&#"3267 qq%MzT:])7%?$6L(40%E,+V~hAlBtEl=99{)7#& D0)V"& G6 3+))O&z)1"& F1 3+))& B&F '@$]IK]JL ! +332#'326654&##{pRm$4N+4.%FrnoV{:gD57 &H 7@4e]IK]JL ! +37#73732#'326654&##3#1770pRm$4N+4.%NNzsooV{:gD77lzT"& G4 3+ &HF /@,e]IK]JL  +3!#3#3{`Fydyvz#& D  '& H 3+/"& G 3+#& F& A& Br 3+#& C& K 3+F& LF )@&e]IKJL  +3!#3#{^.Fyxz)>O >@;  Je_MK_NL    +"&54>32&&#"32677#73h~+XWkN8A%5K/21dB+ewtGg<*v.IS%:6du)>'& H3 3+)>"& F> 3+)#>O& -)>& BYF '@$fIKJL  +33373#7#{..{34FF;@8  f  eIK JL +3#73733733##7#37#ZHJHIZ34M ZAAAAZUl?Y"& F= 3+F "@ JIKJL  +#77'7! NMC(QMCU] VV U#& D'& H 3+"& F 3+& A& B 3+#& CwKF&\& K 3+F& LK& J 3+uK*F(@%JcIL  +"&''326736-"'2 tx8[z*<KZ(uK"& F 3+fF%@" JIKJL +3366773#'{7!xR,,F5#fF& 3F@IK^JL +333{cF4z#& DF&%BN N3+#F& F&y 3+F *@' JIK^JL  +37'7373()'l7(c*b2.bNzF'@$ JIKJL +3333#667###{ }@ Ӌ 9F] (KU*F"F$@! JIKJL +333>773##{ 7| 6Fq78 ;E#& DZ"& G` 3+#F& TKF8@5 JcIKJL +"&''3267##3366735-#!8  3{ 8ziu$ 32'26654&#"tv'PyRpt&MxE,F(.+$;*|hDk@}eEl@{DqC17*GX.i(N#& D?(N'& H5 3+(N"& F@ 3+(N& A(N#& C("& EY 3+(N& KM 3+(Rb!*=@: )(J H_MK_NL#""*#*&*( +'7&54>327#"'7&#"266545h@..'PyRL5)>-)&MxQJ5,$;*_,F(".8;]Dk@2.79WEl@ *GXDqC (R#& D?(Z& JD 3+,#Q&@JKPX@#e _MK _ NLKPX@-e _MK ]IK _ NLK"PX@8e _MK ]IK ]JK _ NLK'PX@6e _MK]IK ]JK _ NL@3e _MK]IK]JK _ NLYYYY@!&&  +"&54>32!#3#3!'267&&#"ot%M{W+a( %F ".J+/{d@pEzb{v{}> GtE1/F +@(g]IKJL %! +332##32654&##{N`+w')B-5#F.N1gm?0' H /@,hgIKJL %! +3332##732654&##{ K]+$1-6#$HV.O2glp)+!(uNO 8@5J_MK_NL  &! +'#"&54>3226654&#"ZH ms'QzRnt'O"& G 3+#F& 7N$7@4J_MK_NL$$ +"''32654&'&&546632&&#"]> W(#')!+66`>bL2A$(42x%&!I75P.*q!%K2Wb#& D"& G 3+N&z"& F 3+#N&  KR$KPX@ J@ JYKPX@~_MK_NL@#~_MKJK_NLY@ $$ +"'532654&##57&&#"#6632pR86<,514 \ $+4 LS|tI^2i0?tv-'#)ZN/>eh2L)WL8dd?F!@]IKJL +3#7!#gaa||60F/@,e]IKJL +37#737#7!#3#g)`aii)u||u?"& G 3+1F&z2#F& 5QF$@!IK`NL  +"&&5467332673SF '@$JIKJL  +333667336673#'&7#X   w gF#JC6H X#AC%G#>S#& D>S"& F 3+>S& Ah>S#& CR]F &@# JIKJL  +#373#'2a9pB6A7F"@JIKJL +37373v1f-0dA7#&  DA7"&  F 3+A7&  AA7#&  CF %@"]IK]JL  +#7#7!3"*]oz_z#&  D"&  G 3+&  BU 3+H GL<)&f 3+IM @W_O    +7"&&546632*2A71=O/$C+83=Q- oK PX@ofrLK PX@frL@U^NYY@  +73!73#'!-FS:SFS:GWWWWE )@&e]oKpL  +3#737#7!<&~}6Sk 0@-g]oKpL    %! +!7#"&&5463337#"?6?Pd/ݘO,E>O.6Y46{B5/+s0 ʰ3+ "@ J]pL  +#77'7![J<]LV((VV([(V-*@'(JoKpL-- +#33667336673#&67##4467#<M   $ī   #}.Z$'Yp6=)_"6+4"(I!o6=)_"A@>Jf#K$K_'L +"&'53267333##&$ '# ;;AB6R{<@64Xd)0#O@L Je  Q ]#K ] $L +367!333###3#7!3#`F?M@v==G䐥JB'[G--cb?MNnN_7NC0%-=@:(J R]#K^$L'&#" +3667&&5473667!3#7!6677#37`Fq&,875"C D}[G--.#B+X,$U,N<E<   Ib8 2uَ 60KPX@ J@ JYKPX@Q]#K_+L@!Q]#K$K_+LY@+"&'532667667!3#7##($ (&E,~fF-g}('!CR |77DLLy+U]#,J#XK.PX@a]#K]$L@#pb]#K]$LY@ V"$!#+7663232654&##7!7!76#"&#"<08".!+F{@P\':d@)N!>q b}c$C0=O' ,J+rK.PX@' e a]#K ]$L@-p e  b]#K ]$LY@*%$!# +7663232654&##77#737!7!3#76#"&#"<08".!+F\b@P\':d@)N!>q b|}c|$C0=O' S-@*J#K`+L +"&5467'7!3267AMTJ<]V*#B 7G "(VV(l # n r 8@5J~f#K$L $ +376677#73733##7654#"'tkxy|}FJ%$\R@O- , <J~_*K` +L:81/*("  >> +"&&54>32&&#"32677332>54&#"'6632#"&'-Nj5)PyO3M60!$=-:3')) ,3K22#0#"S">^3.YU4X"R KN]~Gn 3Xt@KOA@ n?rL\P)"&?I$;@8  Jg_*K]$L$"&%%+3654&#"'663232#'326654##Gr$H(AO!~q'14=^5  n 2CjoYxx|2SR <@9g]#K] $L    ! +3#7!32#!3%326654##|A:~qi"4=^&L~oYxx6|2S=@:f  g#K ] $L$! +333733#32###%326654##q~qlql "4=^&KKKHoYxx|2S>&KPX@!e_*K _+LKPX@%e#K_*K _+L@)e#K_*K$K _+LYY@!&&  +"&54>3233##'2>54&#"5z})U\z;A5Rr?)F3:44U36 yYN{64G?\$3)));=><98@5 Jf#K]$L+#33'.7!'<wGdZK 3<;==; F@C J~e]#K $L +#667'7!#'##7#7#!&&##"*{!TEmB >4`~t9j ')?4>GD CF\Rن/13 ?@<J f#K] $L  +3333!#3'.7!'>G?\RS $3));=><~]zK'PX@  J@  JYK'PX@]#K_+L@!]#K$K_+LY@%&+##"&'732673#7667#x # '[=*  ": o7f^f x.,6-Q:0 MK'PX@R#K^$L@a#K^$LY@ +333!FHa~}}&,IRL605@2 Q]#K]$L +367!#3#7!3#`F?M@nc[G--cb?MNn|0NCtKPX@ J@ JYKPX@]#K_+L@]#K$K_+LY@+"&'532667667!###($ (&E,~}('!CR |77D|LLy+U]#Z-@*J]#K$L+333!##>7###  }G F|R"MK)IV)<&2# #3+=*6I@F  g_*K _+L,+ 20+6,6&$* *  +"&54>32'26654&#"7"&546323"&54632F-]a,[W:`9=2,}[G--cb?MNjNC4,7@4J_*K_'L,,+"&'5326654&&'.546632&&#"Ae#c0/75%FzOCX"4$<'*5/0Aw(*'A(13$&O_0U6...W+59yXB^21'v*(") f%*.K-*@N_8'7kyZ+=@:)#Jh#K$L'%"!  ++ +%"&&5473326767332673##"&' ,Q375K% L $A /NCR#H@EJe]#K$K_'L## +"&'532677654&####7!#32+ .'..jF}n_` +j|@= #L~~XN9}mRZ5@2J]#K_+L +"&5467#7!#3267-ALSU*"B 7G "~~l # n %D@AJe#K^$K]'L %$ +"&5463!#"332654&#!333#l9;>.A@*:}}4bO2&.7C 9(#(L5#KC-:Z5@2Jc]#K$L +"&54667#7!##"3267V.3.G&n|[.2#   6,2F' ~~/!XgF3@0Jg]#K$L%#+!#"&&5477#7!#32673:(c3,Q3K,Q'DE: |  @@6f$9@6Jg_*K$L$$%%%#+36632&&#"6632#76654#"seP 7,'$$Y-,Q375K&G#D"cO }' TE:  @)J@G!Je #K^$K] 'L  )( +"&5463!!"3!2654&#!33333#59;>.9IA@*:r}}}}4aP2&.7C 9(#(LL5#KC-<&22<$)8@5' Jf*K+L&%"! +"&54>32374&'3467#%#66E-]b,[k".I $#"h%!%i=p&2O {TQyWOV$2G4D]W"A@>Jf%K$K_'L +"'532673373#7#,3"- |-,t/= -O v (+G2R1<""O@L Jf  Q ]%K ] $L +3667!37373##7#3#7!3#IA*>b%j--6רꇥ}91GA**E` C2Rz x2C[<U" (/=@:+J R]%K^$L*)&%  +3667.546773667!3#7!6677#37IBX  )$s" .?\GB**4P h;N6%3##  9BPj&Ht%$<Q,#KPX@JK"PX@J@JYYKPX@Q_,K_+LK"PX@!Q_,K$K_+L@"a_,K$K_+LYY@ ##+"'532667>323#7#&&#",*"0@bJ2ZXG@)R\(1%!,? y/N.EfB! \.ZC2XC&7] -8_@\$1J gg gW_O/."!53.8/8(&!-"-   +"&54>32#"&'6632"326654&"32654,|y9]X;C&UI%O#L0[Q-bF.I<"% : 4-*) =l@!5$C, 9~3 VA1V5,$  : 14);r" 8@5eU]M  +3#!#3667#3rUPS<54&#"H\,>xYGY*8uW*", @;eeU] M! +3#73733#32#'32654##V]~..@; JW_O"" +"&&5467373'26654&#"Gk:_XaF-1#Fk;(7-3%;!: ;g@Xb>3dQ1x/K*'=)F,94/""XK.PX@a]%K]$L@#pb]%K]$LY@ d"$!#+7663232654&##7#7!32#"&#"-*:##*&:njZfq ? ''sXXrbO=S]/"*rK.PX@' e a]%K ]$L@-p e  b]%K ]$LY@)#$!# +7663232654&##77#737#7!3#32#"&#"-*:##*&:tJVndK_jZfq ? ''sXairbyasO=S],&3@0#J_,K_+L! &&+"&5467>54#"'66323267YY\R-1)!C)2W-NXRW43!a.*L QDEZ#lBFDR&|/?""8@5J~f%K$L% +376677#73733##7654&#"kK ]] ]] 6<"Vrhn,b==b4V?k|"#vn:".6@3)J`+L$##.$. ""+"&&5467&&546736673'2654&'9K$EC03P Jf7-V6 & +D&?b82o;$&# A;\N1+M,6Y4f%'$5"+5,:R@O* ) 8J~_,K` +L64.,'%  :: +"&746632&&#"326773326654&#"'6632#"&'RfE{O+<.!%4(#')&8$8*4S1AxP8W'O e^yU o -FM43?j@+. j*XDsX0!.#Ge-#1G@DJg_,K_+L%$+)$1%1 ##+"&&5477654&#"'66326632'2654&#"`HU& %G)CP 4!VW2rL..!(", *H."6  n 1C 6U?0[:g/ ! %-"&| JKPX@"g]%K _ +L@&g]%K $K _+LY@ &&  +"&&54677#7!663273%2654#"aHS#*>03CV1itt`#,=$! *H- 1pNC3[: "],%:  ,  JK PX@)n  g]%K  _ +LKPX@(  g]%K  _ +L@,  g]%K $K  _+LYY@ &$, ,%# +333733#6632#"&&5477#%2654&#"f4!UX2raGV& *e\c.. )#+ ^^^qmU?0[:*H."6O]/ ! %,3,&sKPX@!f_,K _+L@)f%K_,K$K _+LY@!&&  +"&&54>32373#7#'26654&#"=_5$HmI_pW,t/KFpB)9'!2  3^AHc8rfCj>xBi;)7-HR%Z,"^JKPX@f%K $L@Ue%K $LY@ +33373#'##7#3'.'#u1[py,qh"4*(.1*"7@4 Jf%K]$L+#33'.5#3'<B[hM;"4*(.1*llR"!F@C J~e]%K $L  +#7>7'7!#'##7'7#3&&##2Z-5%`  .#|PgK0[W$.&,-733O?eeegLq " hJKPX@f%K] $L@"Ue%K] $LY@  +33373!7#3'.'#3'#u1[yt,]h"4*(.1*l"9@6  J]%K_$K'L%&+##"&'732673#7667#xv  G8 '  ֒D h5DHu}ޖ< <q" )@&R%K^$L +3333!IAH]\\te*2O<"5@2 Q]%K]$L +3667!#3#7!3#IA*>b%EGA**E` C2Rzq2C[,,"KPX@JKPX@J@JYYKPX@_,K_+LKPX@#_,K_,K_+L@%_,K]%K$K_+LYY@ ""+"'532667>32!##&&#",*"0@bJ4Z\(1%!,? y/N.EfB!yW.ZC2XC&"-@*J]%K$L+333!##667###s[<ύ 5"}yW&Fo)@ ,,+>@;g_,K_+L! '% +!+  +"&&54>32'26654&#"7"&54632=_5$HmI_p"Gm81<)2%6"4=$($&& 3^AHc8rfE~b9rDh8-@+CL!A5b!*,!'00k,'3I@F  g_,K _+L)(/-(3)3#!'' +"&546632'2654&#"7"&546323"&546320~VYQo:'Nv=NUoI=r[6zoX5GfSAIe)(())((),,' ]j ]<Q9@6 Q]%K]$L +36677333#7!3#IA*7[$3)]GA**E` C2JnL2C[,(7@4J_,K_'L((+"&'532654&'&&546632&&#"r+NL'87%..=jB7Q%0A00(&8?!*!Q(#0j=8Y3-6רꇥ}92 y)9g$k;1* $RdMJ"04@1 J]%K^$L00&&%' +!7667##"&'##"&546773326773326773C C*+D !P14J ,6.7 t-(11)9FH@v" Q" ec>"5;@ JKPX@7 Wg   U ]%K  ] $K_'LK)PX@8 gg   U ]%K  ] $K_'L@5 gg   Uc ]%K  ] $ LYY@98765543$!#%+"+3>7!36632#"&'532654##732654&#"1!3#IA+)I<e]=5:K3&&W\#<9" /66&''3A*E\E27QL&.&& !4> L $A !2CZG"(L@IJg]%K$K_'L (( +"'5326776654&#"##7!#66323"%%$5%'\ L1IE & .O v-' qq!HC$-4R0H*"5@2J]%K_+L +"&547#7!#3267,@N88%C 7Gpp  n _"$D@AJe%K^$K]'L $# +&54633#"332654&#!333#~?=@4;>)7t\\`%1ZF2&.7C :.'"O%(C G=(:)"5@2Jc]%K$L +"&54667#7!##"3267V.30J'N\\.2#   6,4F'oqqO/!XG"3@0Jh]%K$L'$+7!#326773#7667##"&54677GD>N t V8JRqq#>ec>ޔ-+WF9#&^@JKPX@g_%K$L@gg$LY@&&$)$#+36632&&#"36632#654#"|cP1"  " I1FG ;=4!7' *KcO t' *?%.UH$4!#<9`:j")J@G!Je %K^$K] 'L  )( +"&5463!#"3!2654&#!33333#0?=@4;>)7t\\\\d1ZF1&.7C :.'"OO,8& G>',s,&RRV,,"'3@0Jf,K+L'& +"&&54>3237354&'7#667#=_5$HmI_p"Gm};)78y* = 3^AHc8rfE~b9Pj 9%. h < 9) [(%@"#H_+L((+"&5467&&54>7'26654&'bsoW4D|a >:4FGF%3'8D& hWg~91=H%|  )'"dJR|Dx.F&,4^>(.&'L A,&6KPX@J@JYKPX@"_,K_+K_'L@&%K_,K_+K_'LY@('0.'6(6"!&& +"&'532677>1##"&&54>323732>54&#"=P%&U/3@ D3'C)$B\85=qwKk/!$/#%/2%"1*WECk@+%FVc*]/JT%'/+FV+++6K&XLj4x& 4x"*lKPX' J' JYKPX@%K`+L@%K$K`+LY@%# ** +"&547332667733266773#7##"&'#DD AC0"5' /C0"5' 0to U98= W UH$47#<8_;#<9`:e1>;41>O// +j/ +4j/ +j/ +j/ +j/ +aj +~j<3+3+3+ 3+ 3+3+3+X: aRgG5)&f 3+VgG5)&f 3+BK2PX@f5K6L@f5LY@ +333#;A~K QK2PX@~]5K6L@~]5LY@ +3##!#7#|T%}@5@}%R|L.Ұh@ J IK2PX@5K6K`:L@~5K`:LY@ +"&'73267#3773XkaDBb g=5KLʰގ W" { <1!"f>nDc.Q-"BK2PX@f8K6L@f8LY@ +333#u-0"oK QK2PX@~]7K6L@~]7LY@ +3##!#7#H"~<%<~"N">JK2PX@8K6L@]8LY@ +333#7667t- 't. "+S*|*R)HY0=@:Jgg_+L(&" 00 +"&546776632'2654&##732654&#"(vj2xrdmZR;H8hR767-4>$#53& mc: hg[PV` SC#UN2wO80*vA>!%2E2-,=zMF@C.J" HW_ODC<:MM+".5467>7>70327#"&&'0'0>54&&5461"32>18r_9=;+?&&*B2.\NbHmh@R*"JCBHK PX@6n~||W`P@5~||W`PY@YWGE?=(& hh +"&'7326'.#0.#"1'0>3276654.54>1032667#".1>32)4#8=/7F  %./&&2&*?>*-/! =S32!  )20"*:> :E" |E^f(=7; "-" %KVZ*!E>1@5 '3F. ")& TRCz,04 !OR@O@?)(J~~|W_O#"31"O#O#+#&+'0>323266710#".#"".1'76632326654&&54>73+6 <<6)"  +/% *66$<)'#1h  %24 $30 * :6$.+@@+ !!-=+| "."3S/-]Q &*#* 3267>54.#"1'06654&'.54667>32>32326732661'0654&&#"06677"&&554&#" #!)) !49-("."  ! ,=&)I4 #1@)#& " 5Z2,! D 0%<89&"6 .*   ') ) _@9& J;: H    ~  |||||| W_ORPECB@642/('" __ +"&'46&#"'>3232>54&#"1'7>54&&#"#"&&57032632>324  ,70C)VF+ /72 )% '' $!K&/&0!10.0# 0>$$!!LG4q 2! ! &@KKEK A,:!  l  *2'A3%/3DUU!!$ -T@Q(J~g_qK_tL&$  -- +"'532654&##77#"&&546632&&#"33rM)a,GY8MC=Q(5^>)51 "/&.S]'<=*/d1S3If7 j;+"/^ \Nn,R@O'J~gg_tL%# ,, +"'532654&##77#"&&54632&&#"33oK(^+EW6KB7>7&&#"32>53#"&54>7663267#"&&54667032676654&&'#"&54632>54&'.9 1"$  +$>YK**8*4"6#=q[59'$#K@)9O04:':98[(C/'7R' %&,HR' -1 % 7  !1# 1<(![ '*!(7&  QX-SY2%-$ $5F*)(.Rm?0%PF+44+I:( !. B(/H4$9)'QE*$ ,<"%095$P8   :V23NcuA(@h>:~@{W >(=\ sJ  ~  gggg W _ O}|wuecQOEC:8/- +".5463232667'0>776670#"&54>54&#"'0>3232>7667>32032>73#"&5467667>54&#"9A1"$  1+8J=%?P1C) O?2:4 AL%0:< 1CKH -" %. %\db+"%4nW/- +0,!#/43!:+$mvcD (,$ (1-;B(|E"3k]9 @MICG$PBSS@PH/J~ggW_O75('&% BB+".5463232667.54667663>54&1707>79A1"$  /)*E=68;oA73A00 !@) !A5`|CPa=:=pX3& '6/ (7'  5];32&#">32326670#"&'67>54&#"267&&#"";%=%&T)  BR+4[u@---):cI( 27=DLT-+&/UqCG(!<!HA%6D(&E +WBB<?,%<20x,9$J(4$3 %# H+  *;>5`K+  .Nb5,8 <}qZ4*$gl\3CY&D-!11!B5We0Dq"a 5@2eU]M  +3!##'3#3#aGxCCCC6k5`` m~@{z ~{xM = J  ~  gg  g g  WW_ Ovtkiba][QOCA;:42,* +"&&54>54&'"#"&54632>54'#".546332667667>7&&#"32>53#"&54>766326732>7%!   .0"01<(![T9A1"$  -$>PA* W>4"7"=q[;@-$#K@)9O04:-A?8[(C98om !# #3> #EF=  .C-^ NcuA(@h>&5. (7&  C}XCL%-$ ,AN*)(.Rm?0%PF+44+QE1 !. O(@h+"JD() 5=,)6 ʰ3+82F >@;  JgW_O +"&&546632&#"3267'~pIOo0]0PU+K>W)X*/VS) Zpl] 8" ; zSO|a ?@< eeU] M  +3333##'3#3#axCCCC:6[5``a &@# Jt +333#%5aL5M:"65bca\ ?@<JeeU]M %!+3!2##'3#3#6654'axBl=cxCCx$/Sm[>_65`1@32#"&5467232654&&#"#"&'32>54&#"&&5466320654&'&"=Y0EqC :b@0<" 0%"$#-"1!./K+5#9#A3&% (M?&$<$%?(%# (EX{% '4\;=hE ;-)O3.29))< ,73,36aC   6NZ//36H(%'6Z4$(B(#--O:!  h8@10d vV |YL JhI  ~  ~g g  g g  W _  O:9rpRPJICA9:(& 88+".54>3232670#".#"32>54&'7".5463326677'0>7>77667>320>7"!':\S3@.."%#  /&DCF(6mY6 2,#3Jk.;!1"$  -',F= 8R,E07QB %&  G N `5%$ !RSF+ %!.Oe6&% #7A< ')+#(7&  6^;A1 ":e_/ $Ze5$ 8"'!** UL@@=lHG+ JvHW`PigOMDB+"&5463232>7>70>7667>1326670#"&54>7>10#"&&70>7>7>H3(!%+ )%7_VW/?|i# %"9?>,="5U2062(* 8> (/0"(&,' 282 0Si9=Y#3D?1/$!SYP(QWg J:*;#  "=e~@Vj -HTM8 8NUPUfd'L#JUI^ E5Qg1+1C/74&)>D@AJe]oK_tL  +"&'53267#73!#3#- "HWWA'I-F rV|5||BM!7KPX@J@JYKPX@!g_wK _xL@)goK_wKpK _xLY@20/-)'77 +"&&546675&&54>32373#'#'26776654&#"33#"Bj?6a>9=.JV'Ta"|$Z'^kFJ:A=P 4`>? *R=@Q, I87M04,V6V,4~p+=A,((-v10-&/?@<Jg_wK_xL*('%!// +"&546675&&54>32'26654&#"33#" r6a>9=.L^1o;,`b?pFFL8C=P 4`>@ ]\@Q, I88M0IXPM~DmRT,((-v10.&>.|KPX@ +J@ +JYKPX@goK`xL@goKpK`xLY@*)('$".. +"&&546675.54677333#"32673#'#=b:9b@5>MT 6b@=&ZlL|e (R?=Q/ *;!*iz2-v22,"pf6V,4 #+K@H% JegWe_O   +"&54>323#773#2>54&&#"@Q"Bc@4D B'RCB/Qbo2 ;&KUyL.; ""(/5@2*(JgW_O  +"&54>3232676677&"#"6654'nq*SxNH`'.-^-,YI4% +{$)/L9A m]@}g><>Bd ?4 -'lo)H'< V M@Jg geU] M     +"&54632'2654#"3'3#"-2/L; qrnC\CP&%*7>323266730'>54#"&1 ,+CX3%! :fO NR#2@H#;L+0+" ),$5 # .3%! ::29++L/%77%<@4 /FF/(?S@P ,J~|gW_O*);931)?*?! ((+".5467'0>7>32>73'267&&546326654&#"%0 ,*>K(+.4!/0*@*<)"  +M7( ),$5 # .3%,TZ%.&'1' 1o+%^jM[{Q4GVg@d"% MJ~~g g W _OIH65HVIV?=5G6G*)$# 44 +"&&54>17#"&&5467'667663273>73026676654#"26677 !*2OXO2" 6$$&  A #;#^:)'.v'B. 3T6N2GXx!B: "01-#"51O/BF=&{",B>7*G )87@ &D"(;!(@:5 ?I1GH17T,"J (+EPN; -N2(3<@ , ,)L@I%J~_zKxK_tL#! )) +"&'532677.54>32&&#"3267+ ) 7X4&JlE1M"-3 /A",(%D", 2Tw&7*XFIf:p El9//1T4)-F@CJ~qK_zKpK_tL--$%#) +331366323#"&'532677#654#" J1FG *3%R@  OC5"5' 060%.UH$4ɮWPl!@?#<9`: N'7KPX@%&J@%&JYKPX@#cqK_zK_xL@'cqK_zKpK_xLY@41,*$" '' +"&&54>77312#"'##327326654&#"4X6)UZ/+^s3'CX0c&qS:J#0 (0"!6 64  -bMTa=iCEx\4PF!jD@3wo&*=_47@1*]@ZJ  e_qK]rK_ tL&%$#"!  ** +"'53267#737#?6632&&#"3#3#3"- ;OOPY cP"9'"kkkk@ -O v (+b^G))cO m ' p]b2R1Y,0KPX@J@JYKPX@!g_zK _xL@)grK_zKpK _xLY@-+*(%#00  +"&54675&&546632373#7#'2676654&#"33#"_`XH!0=]//@&qtn:IAL/-$6` :H S=FE  5'KPX@  *$JK'PX@  *$J@  *$ JYYKPX@'  f _zK _ xLK'PX@+  frK _zK _ xL@6  frK _zK_ xK _ xLYY@)87/.;:7>8>21.6/6(&  -- +"&547#73>32366733#3267#"&'#"3654&267#AT-(Da:58|1JJ  *.> EF$8`(5 `fYEi:0$, vCY> t &+ 1A0 '/G7],>,,<I|KPX@ )&JKPX@ )& JKPX@ )& J@  )& JYYYKPX@&  h _zK _ xLKPX@2  h _zK _ xK _ xLKPX@9  hrK _zK _ xKpK _ xL@C  hrK_zK _zK _ xKpK _ xLYYY@'>=.-DB=I>I64-<.<('$" ,, +"&54>323736632&&#"32#"&'#7#7267654&#"2654&##CT$C\72<\ M#/T)E+tvz;a92D\F+? "."$k#9;, eaHh<)'F,otUE=R*.G$-wX>..'/+FV+/' " & 3>:&@"JGt* +'7.55467733267'7654''$E-` #,' dF %E< r $ kJm , )I@FJg e _zK_xL(&#! %"$$ +'73>32##!!3267#"&'732654#"$*MnG[\Q /%+F0+Y:Wt XC.#G@Av\5TAIJ @!oVX !(:,5?b@_(34 Jgg g _zK_xL=;8620,*$" 55 +7"&&546632>32##3267#"&547&&#"32732654#"o6##K;.MrK\[3.+F0+X;`x#& UK36*/<2)J.?f;TAVh ,4oij_  5#."<IA,0?JKPX@  B.J+G@  B.J+GYKPX@) gg_zK _ tL@- ggrK_zK _ tLY@!A@21FD@JAJ:81?2?#"00 +"&546326677667##"&&54>32373&&'2>54&#"27&&#"OOSI3J K3(B)&DZ35=ql '" L!TC/$$;#%D6& 2!>7:H!$ ("4*WE V9DD  ! TC:A.K,0"5( /C0"5' 0 # "e1>;41>UH$4H JYP932> I#<8_;?#<9`:Q " S,(2@ JKPX@!~_rK_xL@)~rK_zKpK_xLY@*).-)2*2((*.$ +3336632&&'#"&546677654#"72677"tp  S9FG ! ! SD:A-K,5"5' 0 $ "e1>UH$4H JYP932> I#<9`:Q " ),)4u@0&)JKPX@#g_rKpK_tL@'grK_zKpK_tLY@ #-$%$# +&'#"&54632654#"#336632%3267&#" 4M=DFFC5"5' 0tp  S9FG C    5>45P A#<9`:"e1>UH$47*;g, ,)G@J'&% HKPX@ pL@ tY@)) +.'566554&'%726654&'&&'1K,   &2N)!&  -,+ %)m, #B(+OR._ :%.J$,"+6K@"J43+%$# HKPX@ pL@ tY@ -,,6-6 +'7&'566554&'%7#&&'7&&'&&'26654&'V%%   <%H&2N)/#6_ / t)*.9%)m \n#B(+OR. $ :%,*;")A@>(' "JH_zK_xL$##)$)*%, +'7&577&#"766327#"'72667f<*$J$$7(?.";$##GmI<1y'9%35.e9 ,F,$3@KPX@ "JKPX@ "J@ "JYYKPX@$  g_zK  _ xLKPX@/  g_zK _ xK _ xL@9  g_zK_zK _ xK _ xLYY@#54&%;94@5@-+%3&3  $$ +"&54>326632&&#"32#"&''26654&#"2654&##Yn"FjH4M$^E3U)E+tvz;`7;\R('6!% .$t#9;, pdHb8(&$*otUE=R*#$!&xBi;)7,GR%,0" & 3>FS09CMKPX@&%#70J$HKPX@&%#70 J$H@&%#70 J$HYYKPX@"g _zK _xLKPX@,g _zK_xK _xL@6g_zK _zK_xK _xLYY@21KIFD@>1929+$%%$& +'7&'#"&546336454&#"5663266327#"'"7&4'32>#"3266E#`@\[3.+F0+X;5[R<"C""FjH)"'6= .UK36*((2 $*TAVh ,4o#$!&/+*T7Hb8 Bi;\ ,GRM5#."<F,'09C KPX@% JKPX@%  J@%  JYYKPX@%   g _ zK _xLKPX@/   g _ zK_xK _xL@:   g_ zK _ zK_xK _xLYY@#)(A?<:8753,+(0)0#! '' +2#"&'#"&546336454&#"5663266"3454&3267#'#"3266Yn"FjH4M#`@\[3.+F0+X;5[R(%4$!%)5UK36*,pdHb8(&$*TAVh ,4o#$!&x<.,0)7H1 5#."<,t,.<C@@ ,J_zK _xL0/75/<0<+)#! .. +"&&54>32&&#"3267&54>32#"'%26654&#"6W4&JlE,? "5%* @$HmI_p"GmJp9&[6(:'!2  .ZAIf:m(BP(51 Hc8rfE~b9N$*xBi;)7-HR%Z,tG7@IV@S'&$ H85320J%H1G_zK _xLBAAIBI=;/-#! 77 +"&&54>32&&#"3267&54>327#"''7&'77&#"26654'6W4&JlE,? "5%* @$HmI-&@9"GmJ0(D&[ !2 I(: .ZAIf:m(BP(51 Hc8 (.$:gE~b9++, $*-HRBi; "'@$J]rKpL% +3766773#7654&#"kK+,6<"VrhnV?k|"#vn0"'1I@F$%Jg]rKpK_tL1/*(#! '' +"&546776654&###32327326654&##GL -7-+tVgL6& (4N1 &%O@> +6/.="LGJQ;8$43 # q# r' #@ JHrKpL  +333667tp @'+A4"e)7 TG,KPX@ J HK'PX@  J@  JYYKPX@_rKpLK'PX@rK_zKpL@~rK_zKpLYY@$%$$ +33366326632&&#"&&#"tp "V1$ %]. "& @ 614"e5:b/8  HA,",KPX@  JH@  JYKPX@ ~_rK_xL@$~rK_zK_xLY@$#('#,$, "" +"&54667336632&&#"&&''2677"g:A-K,@p !T5  7V ! TT # 932> ,e4;WV JYP[ " ,/9KPX@ )%-*JHK'PX@ )%-*J@ )%-*JYYKPX@"~_rK _xLK'PX@&~rK_zK _xL@-~~rK_zK _xLYY@10540919"  // +"&546673366326632&&#"&&#"&&''2677"g:A-K,@p "V1$ %]. "& @ 51! ! TT # 932> ,e5:b/8  HB JYP[ " I,F@ J HK PX@_rKpL@gpLY@ %A +3"#"&'7327Y -S()T,WUv q IV,*r@ J HK PX@g_rK_xL@gg_xLY@#!**  +"&5477"#"&'7327632'2654&#"]V & -S()T,WU1#HM*Z:%  \D"6 q  W?2Y8h0!%7@4J_qK_xL  +"&'532676632&&#"*"[UN(  Y\ rOZ r[aR6""mKPXJJYKPX@rK_xK`xL@rK]pK`xLY@  +"&546733266773#7#GD>C5!6( Kp  R UH9'#<9`:"e1>Y"!KPX J JYKPX@%p  frK _ xLK+PX@)p  frKpK _ xL@*~  frKpK _ xLYY@   +"&5467#73733733##7#'3267#GD??..==0p  R25#9 UH)dde1> V!*,!'0YUH$47#<8_;v#<9`:e1>;41>S, " "3+&X,(KPX@%J@%JYKPX@~zK`xL@#~zKrKpK`xLY@$#"! (( +"&54677654#"566323266773#7#FE  53A 5"5' 0tp S UH9 e 98+8u#<9`:e1>g,g,".KPX@!J@!JYKPX@#_zK`xK _tLK)PX@+rK_zK`xKtK _tLK2PX@)hrK_zKtK _tL@&h crK_zKtLYYY@$#*(#.$.""%$$$ +'&&#"'6632736632#"&''%2654&#"84=> 4"DI(Q@QU "#% t EOY}RC$H. 5L, K6+N0_*)3HD" *@' JrKpKtL  +373#'wt>qr:  HX""G@DJhrK_xKtL""%$ +3736632#"&''2654&#"wt>q4#>C$H68V    K6+N07JE*)32D"d@  JKPX@prKpK]tL@~rKpK]tLY@ +733267373#'#,F  t>qr:1B2( P $:"""*C@@J~rK`xK_tL&%!** +"&'532677>1##"&546733266773=P%&U/3@ G6GD>C5!6( NKk/2%"1UH9'#<9`:"Vc*,A,DXhFK)&f 3+hFN)&f 3+hFO)&f 3+FP)&f 3+3FQ̠)&f 3+2FS̠)&f 3+ FV)&f 3+-FW̠)&f 3+VmG`5)&f 3+F+G5)&f 3+RbmG5)&f 3+AG<5)&f 3+>G 5)&f 3+5]G55)&f 3+NmG 5)&f 3+"| @_wL +76654&&54663"% NF*N}T"&:&R5"<&:8- $2:@6! '7!MK,PX@_,K_+L@g_+LY@!! +"&&54>32'2>54&#"Mg4.Rk<>V6GS!3#+,#5#+ >jBL~\1(CR+eXx'@L&?9*CM":;- 9 JK,PX@ %K$L@ ]$LY@ +37667'%36 'SAzuR  5i7P@  JK,PX@_,K]$L@g]$LY@ '%(+#77>54&#"'66323;@$>.C2wLXUF(@A(LY7+u@$JK,PX@gc_,L@!ggW_OY@ +++"&'532654&##7326654&#"'66327b$(a-GY5IC$+P4&*,F>0kOccZT6L>:;$0l2*(!d ']FJg HD9gAX+ RJK"PX@%K^$L@f%LY@ +7!733#37>7#&}ZVV& *rYu,h@7 4N"!A@>Jgc]%L !!+"&'5326654&#"'!#6632:a$X083eq( 6X3?';&6 &f)TAJ{I+30G@D Jg_*K_+L! (& 0!0 +"&546676632&&#"36632'26654&#"hi&G11zR;3dtJ5IW7##"&54>3226676654&#"!@;!6L2 F2URN("+lR(aY:o}G/+2y.  )4/E#*,  :@7J_*K_+L   +"&54>32"7&2>7e`(PvM[h$Kw/$0% |rbQny_Ue2Q`/G2Tg5)*7 K- -7 Y7 X+ N" '/  X6"  X7 \:` `3+6{` `3+Qt` `3+Xu` `3+W'` `3+^(` `3+`;` `3+ {)` `3+X*` `3+Y<` `3+?:~ ~3+Go{~ ~3+t~ ~3+/u~ ~3+'~ ~3+7(~ ~3+D;~ ~3+M)~ ~3+3*~ ~3+A<~ ~3+G& q?d& '  GV& 'q ~& '  /j& '  & ' *GD& 'q 7N& ' G& 'q  G[& 'q G& 'q'  1sD3: ް3+}&){ ް3+}?3t ް3+u32u ް3+}<,' ް3+uC)( ް3+uK2; ް3+}a)) ް3+s=3* ް3+u@2< ް3+~>' d'  dd3+d3+4)8" %@"a]rL    +%"&&5463!'3# Ba5vf(VV)/n`v]?J)N" $@!a]rL ! +7!2#'3#J(fv5aCVV)v`n/]?b ,@)ea]oL  +3#3#3i<8710/-)'#"! 66! +"&547535!5!"&547!5!&54632!!'3654&#"2654'#5!5!535654&&5432  s8j$7$$E$/* ! + ¶8. #P# f------$ - %% - $ q  T------# '#P  &"44 #/;K#PX@&    g _wK _xLK,PX@$ g    g _xL@) g    g  W _ OYY@+10%$ 750;1;+)$/%/##     +"&54632"&54632!"&54632!"&54632"&54632%00%#//%00%#//%00%#// %00%#//%00%#//D&)+%%+)&&)+%%+)&&)+%%+)&&)+%%+)&&*+%%+*&4 #/;K,PX@& g  _wK _ xL@# g  c  _wLY@+10%$ 750;1;+)$/%/##     +"&54632!"&54632"&54632"&54632!"&54632%00%#// %00%#//%00%#//%00%#//%00%#//>&)+%%+)&&)+%%+)&&)+%%+)&&*+%%+*&&*+%%+*&\@ JK,PX@e]pL@Ue]MY@  +'5'7#53'75373#'I_hBhhCh_iBhhCh hCh_iBhhBi_hBh@GU]M +'7#5!!" 8@@4 #/uKPX@#  g_qK_ xL@!g  g_ xLY@#%$ +)$/%/##    +"&54632"&54632!"&54632"&54632c$11$#//$11$#//$11$#//$11$#//h&*,%%,*&&*,%%,*&&*,%%,*&&+,$$,+&4 #/tK,PX@#  g_wK_ xL@   g c_wLY@#%$ +)$/%/##    +"&54632"&54632!"&54632"&54632%00%#//%00%#//%00%#//%00%#//>&)+%%+)&&)+%%+)&&)+%%+)&&*+%%+*&J^y 3+)C@U]M +773vvd GK"PX@g_tL@gW_OY@  +52654#52d 81<<2738227  Ȱ3+ m 3+w}2@/HW_O  +77"&54632w'4&)+%%+)&&*+%%+*&&*+%%+*&H]KPX@8t^]PBA4 3  J$ IGKPX@8t^]PBA4 3  J$ IGK"PX@8t^]P BA4 3  J$ IGK.PX@8t^] P BA4 3  J$ IG@8t^] P BA4 3  J$ IGYYYYK PX@Q ~  ~   ||||| czKrLK PX@Q ~   ~  ||||| czKrLKPX@Q ~   ~  ||||| czKrLKPX@U ~   ~  ||||| czKrLKPX@Y ~   ~  ||||| czKzKrLKPX@_   ~  |   ~  ||||| czKzKrLKPX@e   ~  |  |   ~  |||||czKzKrLK"PX@e   ~  |  |   ~  |||||czKrLK)PX@q  ~  |  |  |  ~  |  |||||czKrLK.PX@s  ~  |  |  |  |  ~  |  |||||czL@y  ~  |  |  |  |  ~  |  ||||||czLYYYYYYYYYY@ ɼ&(,,,-. +70&&'667.546327.54632661.763270&&5463270&&5463270&&5463270&&5463210663266322663212663212663212663212>3216632&30                &  %&#) %$' ++ -, +,'( %/"60 )&/ ">:  5/!)%     )%  / &0  "+ 1!     %      #  $       &f@U]M +5!@@M'""M&"3++(3+#4@1]oK_zL    +#5!#7"&54632ֳoGG#!@]oL +#5!#ֳoGG<O$@!a]oL +!%35#<{O{G", #/;@ JKPX@#  g_wK_ xL@   g c_wLY@#10%$750;1;+)$/%/##  +"&54632'7%"&54632!"&54632"&54632"**"!**JIIJ -- ,,S -- ,,"**"!**J$%&$$&%$IJJI!''""''!!''""''!#&&$$&&#@GU]M +%!5!#@@hkGx@P+?@<J~_wK_xL! '% +!+%- +76676&&'&&546632&&#""&54632"$a'K#D#(0% o*"-7!(5"B+=a9"m+')&D2$+$=%")6#+ 0@-Ja_zL      +"&54632&&'73*"-7!(5  +$=%")67~5 4;x|WK)PX@]oK_zL@g]oLY@  +!#7"&54632xGx|@]oL +!#xKw}2@/HW_O  +'7'"&546326' m>+,>>4Y66Y44Y66Y4Z>,+>>+,>4 #/hK,PX@ _wK_  xL@  c _wLY@#%$ +)$/%/##    +"&54632!"&54632"&54632!"&54632%00%#//%00%#//#%00%#//%00%#//>&)+%%+)&&)+%%+)&&*+%%+*&&*+%%+*&7K"PX@Wg_OK'PX@"~Wg_O@)~~Wg_OYY@###" +7663232673#".#"7}\7{03> Q&)+%%+)&&)+%%+)&&*+%%+*&5 p #I@FJGgWg_O"    +"&54632&&#"56323267#"&"%%&&&%3<2K;/%4=2K;#&( (&# "q5  "q5 5P#E@BJHggW_O##$$$" +&&#"56323267#"&"&54632%3<2K;/%4=2K;(%%''- "q5  "q5 #&( !'%$5 &aO'n n3++"@]rKpL +!!5!kI+6@3&Jc_qL!  +!+  +%"&&5467&&546632&&#"'2654&'5U26-6_=1Q&(D+'(*vhS%-)#%B-1F22E$^ 4^V]`$+ ,-: #fK2PX@ g_qK_xL@gc_qLY@ ##    +"&54632"&54632"&54632#33##22"#33#"33$#33#"33O'+.%%.+''+.%%.+'',.%%.,'t 3+( " 3+ O 3+!6&   3+ '"4 -@*_wK_xL      +"&54632"&54632$11$#//#$11$#//;&*,%%,*&&+,$$,+&4 #ZK,PX@_wK_xL@c_wLY@ ##    +"&54632!"&54632"&54632%00%#//%00%#//%00%#//>&)+%%+)&&)+%%+)&&*+%%+*& !@ H_tL   +"&'73267`s-bBB~2"2`\PEEP^^+e00+7+Pw: 0+'7'77'%A-3,G$F)2)4:?+??3?;+:: #/;GAK PX@? g g _wK_rK _xK _ t LK#PX@= g g  g _wK_rK _ t LK)PX@: g g  g  c _wK_rL@8 g g g  g  c_rLYYY@3=<10%$ CAXI@&JtII%$ +56654.54>54.54>54.5467>@D''''''''''xc?D(''((''(('xP--&'.-''..&ORQ,-&'//'%,/%OR1$9BI@ GF@ ) (!J< I  ~  h g ]qK _oK ]qL;:&%?=:B;A4321-+%9&9$$! +&&54667533253&'67##2675'"&54675"327&5xBuMP+*P%F ,3,C9=#P)- Lx/7r0lq]OvC )( *(%- RsC ;58A p   ? \h`m8eHnz&4? Dt@U]M +&&'73g/ 7~5 4;C AdD@6 JHeU]M+D'#7'373#73'7#'#3DEEDCFFC-c22c--d22duxyuuyx,NVWNNWVn60+7'&&'6'6&'*%H(.G::$/ * .$::H/(H%Iy 7Y Y Y3+5W X X3+Pb; $@!U^N +33P_Tb $@!U^N +533^TjBP ;@]oL +3#P T @]oL +#53 jTB$b'  b&  +@(eU]M  +%".54>3!!"3!4hT32Ug5/XE))EX/B<54.#!5!2#/XE))EX/5gU23Th40/,,/0B<333FL ,3.  &KA # ;,F0< F n %b09k i-(G= k1H*:@7 Jgc_IL*)#"!  +'6677667'&54677654#7323"#/- <,F;OA&+23 'J=kj,)GD $k@.  F m!%b.:w]WFKPX@a]IL@eU]MY@  +3#3ZsYbbw7WFKPX@a]IL@eU]MY@  +73#737YtZҜbb H,@)]IK_NL  +73"&546325>h`) /0!%3~%&4!,,H'  w 0@-gU]M       +"&546323"%4)( .hz?5!+-%%5B~hW.KPX@ IL@ tY@  +&&54673]"ld|;]64Be?a<9W .KPX@ IL@ tY@  +6654&'38Yrp! ge^<97Be@P(?@< J~_MK_NL$"((%) +7>76654&#"'6632"&54632i ("( C%/'`3R_ 4 #Q)/0!%308'kH@+:)$&4 ,,qM 'D@A$%J~gW`P #! ' '   +"&54632"&54667>73327!%3)(!/R` 5"w (")5K/&`5 +-$%5BH!c"jK#PX@#]oK]rK] pL@!e]oK] pLY@""! ! +3332673#!32#6654&##q~Zz>M_|_FsW@EW* 3~821zXHX?Yv:8Z4,5"2*GP\kKPX@-.  WTJ FD J@ -.   WTJ FD JYKPX@*   W a_wK ^pL@1  ~ W a_wK ^pLY@"RQjha^Q\R\PNGGCBA?>=1/+)%%& +7&&5463236632366326654&'.546632&#"#7"'#7&''6654#""7665432776654&#"!$*3,/- #8-8$<64-:.:(& ,, +"''7677#"&54663232#"&'''3267376654&#"32654##77l>)a :H=3:<DRL8& J'R<:6"0G^r#A18J8BML<$M5>4PL[XAEA-& + "!1AO WA9/+|@"JK PX@'n~_oK] pL@&~_oK] pLY@++'"1 +#73&&546677323273&'#7&#"#733 5@CcC C&'%("C%$C8<-eU^~$sKOlmegp   {I2^=~= x ʰ3+#YX @ J Ht+&&'#467&&'7>737RkcLI)NF+bc,%(mM=0MI`pQ.CH%^i8+c`%aC:d).  ɰ3+ E+@(eU]M!#+35!54&##5323 *DO]l.MXSOV6u]X KPX@_wK`pLKPX@_wKpK`xL@"oK_wKpK`xLYY@      +"&546323"&54632Y$11$#//y(v$11$#//4&*,%%,*&6 &++%%++&E&@#U]M+!!5!#"MXX# ` `3+Uox(/@,eU]M +5!5!U##BBwCCaP@ HGt+5>554&''5#'2C'T3 ;f'%i,)4$}!]) J@s+ʮ00E ] ]3+Mp@]L+5!M#CCQ"XJKPX@rK`pKtL@rKpK`xKtLY@$% +33266773#7##"&'#!B#5% 0tn @'% !% :_7V.2 ; 8!1AQ_m{KPX@8   h g_wK _ pLKPX@<   h goK_wK _ pL@@   h goK_wKpK _ xLYY@Kona`SRCB32#"vtn{o{hf`mamZXR_S_KIBQCQ;92A3A+)"1#1!!  +"&54>3232>54#""&54>32!"&54>32!"&54>32%2>54#"!2>54#"!2>54#"DF/O:CI0Ps  FG1N8CI0N1FG1N8CI0N1FG1N8CI0N      RJ,eZ9NM+e[:6y'0* ʰ3+g/* ʰ3+<X ʰ3+;!;@8  JgW_O!! +%"&&54>327'2654&&#"bZH9cK!*rz0T5std=gZe5]Rŀ je7CeaXHJ'B@ 2 ?3 @ J~~   ~g  g g  W _ O)(=;750.(B)B#!'' +"&&54>32373#7#32>54&#""&546632&&#"32670(A-*KMH0`s! &5J*P:0 )4(1m:0!ME-,-6!24/F#6>:D=i? G +D$; K J'K@ < =+ * J~~   ~g  g g  W _ O)(A?:8.,(K)K#!'' +"&&54>32373#7#32>54&#""'532654&'&&54632&&#"0(A-*KMH0`s! &>(/2" "E=!;)(Qm:0!ME-,-6!24/F#6@L ( 58 A  ,":=G79K PX@  4JK PX@  4J@  4JYYK PX@=~ ~ ~g gW_ OK PX@6 ~ ~g gW_ O@=~ ~ ~g gW_ OYY@% 3210-+'&7 7 +"&546632&&#"32673"&546773326773#7#5J*P:0 )4(1s[,+']*!- \IF4k:D=i? G +D$; K 65- $&N7?''&r&g&H!K@H  JggU]M+35&&'66753&&'667!Q\`Mb;6*37/)%%nabkd_M  RUSC56C$& K PX@#     JK PX@#      J@#     JYYK PX@/  e  U  e ] MK PX@3   e  U  e ] M@/  e  U  e ] MYY@( +%3#7#!373#'!!#3#37667X9 };[0q9o"2(?X  6oX}~)J&#K'&r)M?LQ)8@5J~wK_xL%#))' +7'66327667>54&'"&546328 -2s4aZ>D*7  *"-7!(5W l"[G@V-0#+$=%")6.'6@  ! JKPX@7n   ~ f  g  W _ O@6   ~ f  g  W _ OY@)(0.(6)6''&%$#'&+3#73733733#36632#"&'###%2>54#"|DCy >2(F,"@\:4Aq|y|x-"@/!%I_PPPP_T!2&QAC~d;,$FIm%?K%V,CH'/6#R@OJ~eeW_O ## +"&'532654&##77#737#7!37a&@AKZ6HKqyS]M  <;)2d}h}`` \PPk6,vk}@8D? bR e J  ~  ~   ~  ~  ggg  h  gW_O~~|{us[YKIC@20-,*(#!kk+"&&5467&&5466776610&&#"327#"&54663232663206322&&546321#0>54'6654.#"227'"&#"767&&'72654&'"#"&',20H*A! !+   #(     " 2C+G2 $"e= 2 -$(% +b    yt$![040 )& $:!Y5)) )e  )  0/GU'BP(4$$%%2 7X!:$)A:%)> 2_ " 4+ V92U+4hJ@G  J~gW]M! +33273#'#7'#32654##k}+G+&C(+N:T&@G`$\h8Q9+8>>s89PVG@D~ ggU] M! +!##35332!6654&'c\a~7yd!\VT^n2QQiaoHl$T@Q #"JH~eU]M!+3327'7#'##337"0##4'6 EMZCIR?c(:T&-$'@14>Gd[QUkP* Eg$9K PX@40(JK PX@40(J@40(JYYK PX@gW]  MK PX@-~ ~gW_ O@gW]  MYY@%%%9%932,+*)'&$$ +"&'532654&'&&54632&&#"733#5467###!><)(8/9?;-6!/4%J}^^a[@e5`g = /'&9 5(.#41`+*!N\jxT@Q6*$ KE?J   gW_ OsqecWUNMIG$!'''''(!+53267.546323267.54323267.543233#"&'#"&'#"&'#7>54&#">54&#">54&#"">$BD% ?$"? "%?$"= " % <#7U !V67U !W66V !S4 !%% x $$ x %%  < "UY'so'VU# "UY''WU" #UX''WU" <sJN"POOP"NJJN"POOP"NJJN"POOP"NJjT@Q ee U ]  M +#5!#33#3#3333ve fņ||3?j*66`6Y4g6`6Znm.@+ JHt+73'736673#`5;j Қq+j@=V!G"6*P^T@QXIJH~ggW_ORQQ^R^DB97.,(&PP +"&&7667&&54>16676632#"&5463232>54.#"'2654&&'1Q."!&89&*2 (Y)R'?S)$FhES\*&*@(3B(.'.j40Z%50CH$7&62U62g0+\5:bK4'OaA46\#FBB?P @ J]$L+3773#5>BBzP @ J]$L+3753#>BB0P@"1%@"JH]$L+3'753,CĻ1P&#@ J]$L+!'773-BU/P"$@!JH]$L+!'73,nBƸ2P( LJKPX@]%K]$L@e]$LY@ +!#'73530Bɳ/P& %@"JH]$L +!5'73(kBsK4P&"@J]$L+!73.:B^.2P1%@"JH]$L+3573hBiSP '@$JH]$L +!753>B},WP -@*JHe]$L +!5#733;B7P '@$JH]$L +!773ԠBBPz  '@$JH]$L +!5'3>BrwP @ JH]$L+3'3#'X>ԠBBPN.@+J]$K]$L+3533#NBBBnP-N /@,J]$K]$L +35353#NBBBfPvN /@,J]$K]$L +35373#5NBBBPN /@,J]$K]$L +35353#NBBBqP1@J]$L+3'53#NנBBhPSi@J]$L+3'3#N<ܠBB xP %@"J]%K]$L+3'353##N;զBBwP @ J]$L+3'3#5P>BBPa- @ J]$L+3'3#'N<٣BB$P1"1@J]$L+3'73#5N,CC1P"@J]$L+3'73#N,éBBn2P&#@ J]$L+!''73-ȠB/UP& @J]$L+3'753#N(ȠBBk4KsP( *@'Je]$L +!5#'7330B/P&"@J]$L+!'3.hB2.^P1@J]$L+3'3#N8CC#i$P #@J]$L+3'3#]:kBB!P&@J]$L+3'3#N4ҢBB&i/P  @J]$L+3'753#P:˭BBy"]Py @ J]$L+3'3#5'N4ҢBB%iP' #@ Je]$L+3'33##_8BB#e(PF%1!@J]$L+3'73.CS3P'$@!Je$L+!#'73/n0P.&@#JH]$L+!'73'ɗB7q2*P% '@$JH]$L +!''753-ɠB2P% '@$JH]$L +!5'73)ɠBrx3sP% '@$JH]$L +!'73*ɠB93P1!@J]$L+373>CvGP@J]$L+373#=BBuCP%@"J^$L+3733>mBvnP @ J]$L+3753#=BBu9?P @ J]$L+3773#5>BBvzP @ J]$L+3753#>BBv!0P@N+@(J]%K]$L+!#5373}æBTnBPN+@(J]%K]$L+!#533vBBPN ,@)J]%K]$L +!5#533BOBPN ,@)J]%K]$L +!'#533B-B#P1"@J]$L+3573:CP#@ J]$L+!739B|#P!@J]$L+!'73:mBxcSP $@!J]$L +!7539B"@XP *@'Je]$L +!5#733:BP $@!J]$L +!773:B2P%1"@J]$L+3'73.C+1&P'#@ J]$L+!'73/B/0$P%!@J]$L+!'73-jB21 P% $@!J]$L +!'7753/B͠/P%"@J]$L+!573.;Bi1P" *@'Je]$L +!#'733Ȥ0BF-(P"1!@J]$L+3'3:P R>Cp'i ' " t/$0@-o# #3+mjfNj^:C^v^ZR!}^J ^/K^M#]EONJ]Q^L;6J&dD@U]M +D7!sgg"TT2dD@'U]M +D73!73e%d%$d%T;^ ;dD@0 JU]M    +D.'53!.'53.' .( ^47 "M% 47 "M% ^U GdD@<I~gW_O      +D"&54632"&5467332673T% !$.GTHX,6 I`"% (~J>S.%J[^.dD@#W_O!" +D6632#4#"c ]V;EUK&1^OV'=!="F4C ְ3+64Fv ְ3+V& ZdDKPX@noU^N@U^NY@  +D7#7373Jn mB)A@ ;_< ^P İ3+90SdDK PX@nU^N@U^NY@  +D73733 HAH B 87>B 0 ,[dD@P gg  h  W _  O"! (&!,",   +D"&54632663232673#"&&#""&54632w&!$>+3) 9"W1+ a&!$4"% (?A~"% (] \3WdD@Lg g W  g _   O331/+)'&$"$"$" +D663232673#"&'&&#"663232673#"&'&&#"B>+$  9@)'   V>+$   9@)&   7<  7<  7< 7<  %b 0dD@%JHGU]M +D'7377#i c j c\\>>\\>b%dD@Ht +D7#7#};pG;x__xe '/7?GKOW_gow$dDK PX@/-"&,-p6 e754 U  9 8  g;:g=<gA@g?>e!C B#g%#E$D"-#"g+)'H*G(F&,'&g20,..,U20,,.^K3J1I.,.NKPX@/-"&"-&~6 e754 U  9 8  g;:g=<gA@g?>e!C B#g%#E$D"-#"g+)'H*G(F&,'&g20,..,U20,,.^K3J1I.,.NKPX@6 e 754 e  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g/-+H*,+*g20,..,U20,,.^K3J1I.,.NKPX@54  ~6 e 7 g  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g/-+H*,+*g20,..,U20,,.^K3J1I.,.N@54  ~/-+&+-&~6 e 7 g  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g+H*,+*g20,..,U20,,.^K3J1I.,.NYYYY@ɤyxqpiha`YXQPLLHHA@9810)(!  }{xyuspwqwmkhoioec`gag][X_Y_USPWQWLOLONMHKHKJIEC@GAG=;8?9?530717-+(/)/%# '!'     L +D53#!5#53%53"5432"5432#"5432"5432!"5432"5432!"543253!53%"5432!"5432"5432!"5432"5432!"5432"5432#"5432"54325353!533353f_gLO;NQ6_5>y|b;g566fz.6ff6f66p.F3VF.p6gg666N0dD@% JU]M +D5667&&'53., 6886N3  2 D ~R9  3+^U BdD@7~gW_O   !" +D6632#4#""&54632X`OOJHX,6 ]% !$J[UPS-&R"% (e!# 0+''7'7731/12*31/1221/13*21/13p0dD@% JU]M +D.'5>73 6996 ., D 3  2(0dD@% JU]M +D5667&&'53|., 68863  2 D %dDKPX@ " J@ " JYKPX@U]M@~U]MY@%% +D5667&&'5375>73#&&'., 6886 D 3   3  2 D  @BB@ B@bW;  3+fNK"dD@  Gt +D'7'7'37'j83F> F >F38*0 B"FF"B 0*@o+VdD@KJg  W  _O! '% +!+  +D"&54632632#"''2654&#"32654&#"1<<1891<<187    8227''7228((2TZdDKPX@noU^N@U^NY@  +D7#733i{"3{"TCxCxJ@ 1dD@&W_O    +D"&'332673X\}m]4dFea,712h^K ; ;3+^&dD@U]M +D7!^ggSpi a 3+_4dD@)Wg_O#"#" +D66323273#".#""oB736886 .,N D 3  2x%dDKPX@ ! J@ ! JYKPX@U]M@~U]MY@%%  +D5667&&'537.'536673., 5995 3   3 3  2 D B@ B@ @B9^j'L>M>3+^@U]M+7!\^gg^\LG^Lwm<6 dD@  JKPX@"UWg_O@#Wge_OY@     +D"&543256673"&5432 E""! {=" E""H!'@ V !N'H!'m(6 }dDJKPX@"UWg_O@#Wge_OY@     +D"&5432&&'73"&5432 E""!s E""H!'@&O! $M" H!'^)dD@ Jt +D5>73#.'!TXP[c)a%[Z%II^ %)(53 'rH.0+'77'*r*rHOO=AOO=T0dD@%JHU]M +D'73B*xTMOA[ wdD@JHK PX@!pWg_O@"~Wg_OY@#+#" +D7363232654&'7#".#"OB#/*+1(I=*A4+",n@ 6&AK#$T0dD@%JHU]M +D537Dx*T[AOM ^k'L>M~>3+L&dD@JU]M +D'#53xLA[M""%dD@GW_O$% +D'6654#"76632>)*.%)3?K%;"2 V:45Z,'8dD@-J~W_O$. +D"546654&&546654#"'6632W !3     P6dD@+Wg_O  +D"&&547332>33#" :)[)6J3":77P5. _^&JJQ+]E #/UdD@Jg W  g _O%$ +)$/%/##    +D"&54632%"&54632!"&54632%2654&#"]6CC64HG#'#$$#'#$#]>66>>57>.%(#+%(#+pRNR",UdD@J+Jg  W  _O$#*(#,$,""  +D"&546326632#"&''27&&#"32654&#"7ED8C08%6E 7$%99&###%"RF;5JH(G9":#%"*D>#%##<;CT)dD@ Jt +D&'77366778 ! @+ L-C<55 52?m #DdD@9gW_O ##    +D"&54632"&546323"&54632#'#$$#'#$##'#$$%(#+%(#+%(#+  0+'7'77''7'77'%A-3,G$F)2)%A-3,G$F)2)4:?+??3?;+::4:?+??3?;+:?CdD@8 gW_ O??=;75$$"$$$$# +D>3232>3232>32#&&#"#".#"#".#"#F8$.-&&-.$5G%N*.%'/.'%. )8b=''''''''9b<0( dD@t   +D"&546773!!aTr)!,  *cC 2dD@'U]M  +D6654&'3!&&54673AAc"Q(' ((O! &%R$"N*' cC )7JdD@?U]   M***7*710))#"  +D6654&'3!&&546733&&5467336654&'3%A@B AAcS+' ((O! '(O""N*' &%R$"N*' "Q(' ((O!l N 3+hJG{l l3+hGEl l3+ G|l l3+hJG&l l3+ GI l l3+lG l l3+GJgRlG l3+3+ JGSl l3+ GMl l3+GFgXlG l3+3+l*FGZl l3+hGl l3+hGl l3+hGl l3+p  3+1E'.dD@#W_O#" +D6632#.#"E aWZKm9AmE]P"L?! !k/GdD@<$I HggWg_O$$$$$$$" +D76323267#"&'&&#"76323267#"&'&&#")4%#.)4%$.2(6%#-)4%".oN$ M$ N$ M$ hGl l3+hJGl l3+hJGl l3+hGl l3+h>JGl l3+hJGl l3+lFGl l3+hJGl l3+l FGl l3+lFGl l3+hJGl l3+lJGl l3+ JGl l3+hJGl l3+lJGl l3+lFGl l3+FGl l3+lFGl l3+h%FGl l3+"FGl l3+hJG`l l3+h'FCdD@8JeW_O +D"&54663!##7#"3267&5:* G)A)o#&#h%+&A'-:"%0hJGl l3+hJGl l3+lFG Ml l3+hG Nl l3+lG Gl l3+h JGl l3+l FG Tl l3+lFGTl l3+lFGXl l3+l"FGZl l3+M  3+B  3+ "W ð3+#BV ð3+NL1 3+rNL 3+sK Z 3+X'@%dDK"PX@gW_OK'PX@$Wgg_O@)ggWg_OYY@!  %% +D"&54&#"3"&546323265442@2@>52A2@?X:1#<=40@:0"<=30AO@dD@5JggW_O%$$$ +D72654&#"3267#"&546632 ;M!%5&@'ADILJ$G 52'<"R;klb0dD@%Ue]M +D7#733q63t6CxCxkcK 2dD@'U]M  +D76654&'3!&&54673 @h Ak O+( ((O! &%R$"N*'  +@(W_O      +"&54632#"&54632#'#$##'#$$&'#+&'#+8 @W_O   +"&54632 */*,+!*,!'0# %@" JU]M  +.'53n.( 56 "M% # @Jt  +56673< )21 U# ),) V# 1@.JU]M    +5>73!5>73P)) *43)) *43 37 ),) 37 ),) u#(@% JU]M +5>73#&&'u653a-7 47#T .' #)@& JU]M +&&'5366733a-7j64#T .' 47 ()@&W_O  +"&&547332673J:FUK&1\ ]'=!="OVj 1@.gW_O      +"&54632'2654&#"Z6CC64HH4>66>>57>E},@)Wg_O"""" +66323273#"&&#"}G3 .'TG4 -' OH4PG4@U]M +7!.ggPy~q" 3+]Q)  3+}m FT}[dDJK PX@pU_O@~U_OY@   +D"&547733267/**\  %* ?1 D|0dDKPX@noU^NKPX@nU^NKPX@noU^N@U^NYYY@ +D"&546336632##"  !0(,q]2dD@'Wg_O%$!+D732676632#6454&#"#q )B1/BY  [@^_ 6;   SdD@ Gt%+D&&5463254&'J FI)`PXDG/SA*,%*q&*N8.[A M9C<++9 *-9(hJGRl l3+0 HKPX@oU]M@U]MY@    +"&5463!#)#0(I^ FKPX@nU^N@U^NY@ "+536632#M !^I,BD/;fE/;E/;/;/;x/;f/;;qFE3+E3+3+װ3+3+3+3+q3+6dDK PX@)pneU^ N@+~|eU^ NY@  +D7!#7!73!7322a2a>a2Jllll e #'+/dDK PX@Xnp~! f  e     eU]M@X~~! f  e     eU]MY@V,,(($$  ,/,/.-(+(+*)$'$'&% # #"!   "+D7#73733#7!!7!7!!7!7!!7!33333-mmCnnEDE^EDE_EDEE}HH}HHHHHHHHHHHHOCCChJG,l l3+lFGil l3+lFGl l3+lFG>l l3+lFGJl l3+hJGDl l3+hJGl l3+hJGDl l3+h?JGFl l3+ JGl l3+hJGFl l3+hGGl l3+hJGHl l3+hGl l3+6^&MM JGJl l3+lGKl l3+&lGLl l3+hGil l3+lGNl l3+&lGOl l3+ G@l l3+lJGPl l3+lJGQl l3+hJGRl l3+4GU, 3+ lJGUl l3+lFGWl l3+hJGVl l3+huGWl l3+hFGXl l3+lFGYl l3+lFG[l l3+lFG]l l3+ZG9 )&f 3+g ˰3+;I@F  8J_wK_xK_tL64/-"  ;; +"&'&&'&&'532654&'.546632&#"#3267IM ,V-1C0&8$32##3267#"&547#32654#"u-_MrJ\[3.+F0+X;`xd08UK36*">d:TAVh ,4ohk <5#."<,&!H6l,%3j!JKPX@]rK `xL@rK_zK `xLY@'&.,&3'3 %% +"&546733266773632#"&'#%26654&#"SI>C5!6( 0&,_p"GmJ>^2FT(:'!2  UH9'#<9`: rfE~b93/-xBi;)7-HR%Z,$/@,#J_zK]pL$$%' +#73&&54>323#7654&#""4#GiFE_12I"71'<'aW?7gQ03Y7Mj@ahO4<$:D(/!)2@   JK PX@.no  h _oK ] pLK PX@-o  h _oK ] pL@,  h _oK ] pLYY@20,*)'$"!!1! +33733273#7"###732654##32654&##YC C+/JD*3hRC6C&/9E'c51;%&.eeiyD6I^ E5cnjbbb+1C/74&)H"@arL +%"&&546323!6Yh-4, !  ((b8]8?A#.  p0+'7'7?T?T@S@S(2+@(eU]M +"&54633#"33DHIC~~W*-~bE@AC1S+)1( 2<@9eeU]M +"&54633#"335!DHIC~~W*-~bE@AC1S+)1~11(2   3+()2   3+(2(@%eU]M!#!+53254&##532#(~W*-~~DHIC1S+)1E@AC()29@6eeU]M!#!+53254&##532#5!(~W*-~~DHIC~1S+)1E@AC~11(2 ZEE3+( 2 3+NT.@+W_O +!3"&54632BP?NT1@.W_O +!3"&54632BPNT1@.W_O +!3"&54632BP NT1@.W_O +!3'"&54632BPNT-@*W_O +!3#"&54632BPNT.@+W_O +33"&54632NBP@NT1@.W_O +33"&54632NBPNT1@.W_O +33"&54632NBP NT1@.W_O +337"&54632NBPNT-@*W_O +333"&54632NBPNT 3+NT 3+NT*@'U]M+333#NBBNT 3+NT$@!U]M+333NBBI_<hVC{ +-  X^ b'X8!bS$S!T<5B ' 'Y'''' '+'&''* <5<5<5MX-tld<<Kcep<b<aRENZ~Z^4[KlK'BR,S,R,-,uR \))8)\I,SR, .\62=c'c<5'P''8'+' *@1yJ2#<5B@1W'<5{8{HBqbH@{[pS2BG{G_/ttttttd<<<<<<<I1EEEE4[^R,R,R,R,R,R,h,,-,-,-,-,))))A#\I,I,I,I,I,<5I\6\6\6\6StR,tR,tR,d<,d<,d<,d<,,R,-,-,-,-,-,<R <R <R <R \\))))RKc)e88))wG\\\\<I,<I,<I,<r,aaa    O.R.QE\6E\6E\6E\6E\6E\6~Z=4[4[f'tR,h,1I q~oK oq**J$,$%$.$ H$o(tl<eXp <bR4[0<^0W4[^,\o/h:^,lL,\I1o/8<&2-I,.S,j,.h:,((:4,o/h:I,h:4,Rd<KcRSVtPlcTSp<bd<RV0<^ZRuPe eR,I5=, Y,-, \6\6BghI,\S,,l4SP44-#433CF-,\ , )))bb\B\4~Z=~Z=~Z=4[84m84]xDA 8??D#D1 }A''X'< k5oG/8YM]*{2{I{b{LM'd)))49?Z.ptR,M<,E4-,\6vT(Bt>[wDDC<T 0W(:<I,Z"2Z"2<P,<y,@`+vT(Bd<,a35u4P88bS=T deL,SBR$`<e,d<,R4[24%LER-ZPZ\P\    ckhZSPEtR,tR,h,-,!-!-cT +#\6\6<I,<I,<I,d VVVZSPu#4W^DR,`k,\`0C@[W<w,R.N!tR,tR,tR,tR,tR,tR,tR,tR,tR,tR,tR,tR,-,-,-,-,-,-,-,-,))<I,<I,<I,<I,<I,<I,<I,<,<,<,<,<,E\6E\6E4E4E4E4E44[4[4[R^^^$N.S?PS=N=dd<,2PR,R!T<NU2e8)<;\<>4|,2SaS.@2.R6EVP.++%&.S#"'"/ l,Q/R>tR,)<I,E\6E\6E\6E\6E\6tR,tR,h,<R <R e8<I,<I,+#l,<R \tR,tR,-,-,))<I,<I,aaE\6E\6u\,&u)tR,-,<I,<I,<I,<I,4[|,|,td<( J!l X-#Kc)<R,a4.G0R,SSR,R,-W,)R R M,"(\6\\)o/\)44\\I,;, ",z j#II))1 )Q \~!:2.##PW<WM,h)R8R,L,, ,.Z.G.~ Ja a AAA|A]Q448ma{3g3]llRomBqB~BBo1 ) pMFA3<Y7nNNNNLNNn4JJBLL,8D,11"f ,d0$?Z,qTI,XP,!+>Qqg,)<,^Sd<pSdd<d<R,~Z=h(-I )m?*-.rN I0I,%4s:RW4> >C.FF<F`F`3\F"FQF<FFF\NFFYjbEpR{TR7AARjRjEMA%0=(qALAA|R:.|T|R  SXGLWMV= MH~GRgMMgy:SR,u\S`zRA"\oSs~SR, 8)\S 2R,R,R,-, ))\6A:RE3wLU==X0T5$*ALALWAA|RR3 S3J[[M(&(&k&d|UlSlSlSd<,R,R,R,R,R-,-,-- -,u<R \\\\\)n)e8e8e8))))lpp\\\\<I,<I,<I,<I,bSbSaaaa     R.R.E\-@\E\E\6E\6NZ2NZ2~Z=~Z=^^4[\.=R,f^,^,^,^,^,^,^,^,mu5F5FMMr0zC 5F 5F\\\\\\\\0C5F5FPPo/o/o/o/o/o/o/o/0C5F5FPPI,I,I,I,I,I,$01C5F5Fh:h:h:h:h:h:h:h:EPFnF~P4,4,4,4,4,4,4,4,30@C5F5FPP^,^,\\o/o/I,I,h:h:4,4,^,^,^,^,^,^,^,^,U5WFS5WFXMXM\\\\\\\\k0sC5 F5 F P P4,4,4,4,4,4,4,4,0C-52F5FPP^,^,^,^,^,^,^,ttzp-**\\\\\R4+R!4AAQo/o/oo'o"o/ R4AAQh:h:h:h:SSh:h:4[4[R4CAAA"4,4,4,4,4,JR'4YR649A,+'$'\h,?WE{]{[{[j|Yj<8( <"< <'N<<X;d<'EZ@1zG)baRlE<2Z(=,RxfxUE$AI`PE^,^,^,^,^,^,^,^,o/o/o/o/o/o/o/o/h:h:h:h:h:h:h:h:o/o/o/o/h:h:h:h:Pomqpwwwwxxxx?:)8Zg[ijA' pt`BVU$bBR>bX1vlvdeeeg><<bzb6H<%ebN~ZNNT]mY<L+L%gd<[deX6IuKjslAtE))L,  -)))HffX,x))R))0)n)"/eA\f.% X-,,p,h(r,888[),,S])R,,DD=SS']c2,+[*.J+")v,'qR8\l`+F,R,-,)\)\6s ///////////))))))II II P)P)P)P)P)ddd\\\\\\\\\\/u/u--o(o(o(o(o(o(o(o(o(o(o(,  o( ?0?12U5U5U5U5U5U5U5U5U5U5U5<>>>>>!AAAAAHtI-EaSpX=S1@}?RK>ua]<=<ZRRAg<<hjlX7E6: [^~4N#o/i`+G-4D,hKCT* X,0,fG [MLGHXG\,I,L )R \644qORnVKeKccKlH=/ QJ &(F"A  ax tm2 a aa=av&$(aTe[8>C  W Wo 1\\D,\4 uj\t-^W,:-uR*\\,,I*m,rr,,iXMeII)\6\3i&((lHlH2XR,Q VF=RA(>5NX"L:-'''' 52',6E 1 '+''' ''K''''''' ''{{{{{{{{ {{{?{G{{/{{7{D{M{3{AG?sG/_Gi7GxGG{{{{{{{{{{4JKK`W]V;B'5%} h4S4>4Q4BJD5dw;;%'wP4wMM+##<M"lhP#xxw;;%'\S47-4'5'5'5+I:( !? 4P4`+::W>1#CnITPTTPTD$DHBBB XBMMt?M00))22@XXXXXXXXXXXX/Fa!(Cd=X#. xxUxxMb,8xxOl0g48;JJAG'g&|$`')Q^+,NVae NZD*NNNN "&"(&&#' NNNN""&&(&#'%'.%%%NNNN%'%%%""#""""'%'%%%??NNNN%GGGGG3%%%%%%%%"%%-****+NNNN*fo0j}J";F6VH9OD8?<:<NP{ 4,Q5}bb~epbKJS:r9Gwr "+po.>EM rs:u}Xy}}|qC&G6&&4 ZU+:6Dl'HaZ(Z(Z(Z(Z(Z(Z(Z(NNNNNNNNNNNNNNNTTTT X pL x @ l D `<,Xtph (8P< ,!(!"#d$%P%&@&'@'t(L()t*X+L+,-`..p//h/0H11P222234t5<56<7,789::\:l;; >`>p>???@@@A4AlAAAABB@BdBCC(CLCpCCCDD$DDDE EDEhEEFFFGG(GLGHHHI I$I<ITK|KKKKKL L$L<LTM(M@MXMpMMMNTO$O<OTOlOOPdP|PPPPQ Q$QHQ`QQQQQRR8RPR`SSSSST T8TPUDUhUUUUUVV4VLVdVVW8XX,XDXhXXXXXYY(Y@YdY|YYZ Z0ZTZlZZZZ[[h[[[\\,\P\h\](^^(^@^d^|^^`tbb4bLbdb|bbbbcc0cHc`cccccddpe`eeeeeff8fPftfffffgg4gXg|ggggh hiTjLkll,lPlhllmm|mn,noooppqqqrr(rPrtrrrrrsTsdstst0t@tPtttuuuv vvvwwxhxxxxxyy(z{\{|}P}~d L\`4,d(@XpDLpx,xd0@P`p,<LP<<TlL 0tL04h`x(8H`p 8 l8Pt8H0x,`0PL<`P<pè`,xpd00000000000000$44L҄8Ը0TxՐըLxppܸ@|<<d\p<<hX@(x \0DTl lP  L   P  L,L`DLT$4Xpt(`$4Xp  $ H `   !D!h!!"L"#$$%%%&'()*+P,-.x/ /0T01124444455,5P5h55556 6,6P6h6666777H7h777778848L8p888899,9L9p99999: :$:H:`:::::;;@;`;;;;;<<8 >$>H>l>>>>?x@@ABCHDLEEtEEFG0H4HDIXIJJKLTLdM,MNNO@OPPPQR(SSTTUHUVWXTXdXtY|Z\[,\\]\^^^_`xa8abdc<cdeDeefpgghhiijk$kXkpll$lPlplllllmm mDm\mmmmmnn4nLnpnnnnoo$o<oTolooppqqr r0rTrlrrrrs s$s<sTsxst`uu,uDuhuuuuuvv4vXvpvvvvw w$wHw`wwwwwxxyPytyzD{||h}<}~~,~D~\~t~~~~(@X|Hh8X\L4@tT @x  8Xh,\lXh   P` hhXP tT@dXL`<LxD\`L0΄ψДlӐӴ DhԌ԰Tլ,֬8Xטר׸8؈ش4Tِ8\ڀ t\ܬD݌ݸ$DdހX0l|$Hl| `h4 0@L\T<0\(`X<dD|@h LDxDh8\(Lp$Hl  D h      @ d      < `      0 P p     dDHx, 8!`" "$\%'l)l*H*,T-|.01$2|3D446L6\789;@?0@lA|AAAB B0BTBxBBBCC,CPCtCCCDD(DLDpDDDEE$EHElEEEEF FDFhFFFFGGG@GdGGGGHHH@HdIJ|JJJKK$K<KTKxKKKL L0LTLxLLLLMM,MPMhMMMMNN(N@NdNNNNOOO0O\OOOOPP$P<PTPlPPPPPQ Q8QPQtQQQQRR(R@RdR|RRRRS S$SPSpSSSST T$THT`TTTTU U,UDU\UUUUUVV4VLVdV|VVVVWW0WHW`WWWWWXX,XDXhXXXXXYY4YXYxYYYYZZ Z8ZPZhZZZZZ[ [H[p[[[\\\0\H\`\x\\\]]8]`]x]]]]]^^ ^D^h^^^__0_X_p_____```<`````aa(aPahaaaaabb(bPbxbbbbcc(c@cXcpccccd$d<dTdldddddee,eTe|eeeff4fLfdf|fffffg g$g<gTglgggghhh8hThhhi i<iliiij j(jDj`j|jjjk k<klkkkl,lLllllllm m,mXmmmnnDntnnnnoo$o<oXo|ooopp`ptppqdqqqqqr r0rTrxrstxuxuuuuuvv,vPvxvwhxLyyyyyzzz4zLzpzzz{{|} },}D}d}|}}}~~,~D~0h(Lxp8| l$hL(XT(\lhplt@tTPd\X 8Ph(@Xp0H`xxDPhƬ0$$͸pdhX٤<ܴۨݼިH`x`x$H` \tT<TH`@|@`hxhh$<Tl$4D    h   8  (4Xh<4L\P XHX d! !`!"x##$%'D(`)*,*+\,8-X.<../x0 12 3@4$5t6(78X99::;<@<=>X? @p@ABC<CLC\C|D DDDEEE0EHFhHDIIJKM$MMMMNNN@NXN|NNO<OTOPPPPQQQRR4RDRRRSSS4SXSpSSTTTTUUUtVV$V|VVVVWW0WHWlWWXX4XXXYY(Y@YdYZ<ZZZZ[[\H\`\\\\\] ]]^_`D`a\aabb0bbcc,cPchdhdee<eTeleeff@fXfpffffg gdghh8hPhhhi i$iHi`ixiijj,jPjk@kl l,lmHmnoplq4r(r8rsTsuuv<vwxyyzd{D{|T}(}}~~d DX\t8h4Txl$XPl(d,(l(XpL ,Dx 4X|xL\Ht(4pxǔǴlX4̨@ͨ͘͸d`dՠhpHڬPTdtT`߼PlP04|x<D xlt8`    @$@0Pp<`8HXhx<(Hh(Hh(Hh @`  ( H h    !!(!H!!"4""#0#|##$$T%&h' ((T++-@.p//`0d1h11202P2p23 343p484445x====> >(>>?0@@@AB BBClCCCDEEFxGPH$HIIIJKKKKLL LMdMMNP QRdRS@SSSST TdTTTUUVVTVVVVWWTWxWWWXXY@YZhZ[<[T[\$\]D^^`^t^t^t^t^t^t^t^t^t^t^t^t^t_`<`acd,elflfgg0gh|hhi@iijjm$mDmnnnnnoprdtHt`uvvvwxy|4|}~L~\0|p T0x \ l X\HH XPX$p@h0xP,(lH(|(p `P< llL(pTTH0pTT(pL8 p\L$8H\p@P4<L\l|<Tt`|Ll(l ,p,l\T,´èLʌʨ8pXPl|ά,ϐϰДHxѼ0,PӌӠӰԘxh@א״\ٌ٤ڄ`x$ޤ4 Dh 0h,p$Hl Dh@d@d<`` @L@D0 p,H\|,d8\d8$\4X| 0Tl Dh@d<\TH0dl  $ X  8 X x  T t   h @ xL<\ - m ^ ^ p < *  &| D * ( B8 >z <  4Copyright 2015 Google Inc. All Rights Reserved.Noto SansBold Italic2.000;GOOG;NotoSans-BoldItalicNoto Sans Bold ItalicVersion 2.000;GOOG;noto-source:20170915:90ef993387c0; ttfautohint (v1.7)NotoSans-BoldItalicNoto is a trademark of Google Inc.Monotype Imaging Inc.Monotype Design TeamDesigned by Monotype design team.http://www.google.com/get/noto/http://www.monotype.com/studioThis Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.http://scripts.sil.org/OFL2   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a  bcdefghjikmlnoqprsutvwxzy{}|~    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  NULLCRuni00A0uni00AD overscoreuni00B2uni00B3uni03BCuni00B9AmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflexCdotcdotDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGdotgdotuni0122uni0123 Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflexuni0136uni0137 kgreenlandicLacutelacuteuni013Buni013CLcaronlcaronLdotldotNacutenacuteuni0145uni0146Ncaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracuteuni0156uni0157RcaronrcaronSacutesacute Scircumflex scircumflexuni021Auni021BTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongs Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0218uni0219tonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammaEpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdanuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygrave afii00208 underscoredbl quotereversedminutesecond exclamdbl nsuperior afii08941pesetaEuro afii61248 afii61289 afii61352uni03A9 estimated oneeighth threeeighths fiveeighths seveneighthsuni0394 cyrillicbrevecaroncommaaccentcommaaccentrotateuni2074uni2075uni2077uni2078uni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni02F3OhornohornUhornuhornhookuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCuni0162uni0163uni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019Funi01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5uni01E6uni01E7uni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9uni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217uni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236uni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0242uni0243uni0244uni0245uni0246uni0247uni0248uni0249uni024Auni024Buni024Cuni024Duni024Euni024Funi0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Buni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268 iotaLatinuni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276 omegacloseduni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295 glottalturneduni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2dzliguni02A4 dzligcurltsliguni02A7 tcligcurluni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BAuni02BBuni02BDuni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DFuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF hookabovecombuni0374uni0375uni037Auni037Buni037Cuni037Duni037Euni03D0uni03D3uni03D4phi1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni051Auni051Buni051Cuni051Duni1D00uni1D01uni1D02uni1D03uni1D04uni1D05uni1D06uni1D07uni1D08uni1D09uni1D0Auni1D0Buni1D0Cuni1D0Duni1D0Euni1D0Funi1D10uni1D11uni1D12uni1D13uni1D14uni1D15uni1D16uni1D17uni1D18uni1D19uni1D1Auni1D1Buni1D1Cuni1D1Duni1D1Euni1D1Funi1D20uni1D21uni1D22uni1D23uni1D24uni1D25uni1D26uni1D27uni1D28uni1D29uni1D2Auni1D2Buni1D2Cuni1D2Duni1D2Euni1D2Funi1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Duni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D5Cuni1D5Duni1D5Euni1D5Funi1D60uni1D61uni1D62uni1D63uni1D64uni1D65uni1D66uni1D67uni1D68uni1D69uni1D6Auni1D6Buni1D6Cuni1D6Duni1D6Euni1D6Funi1D70uni1D71uni1D72uni1D73uni1D74uni1D75uni1D76uni1D77uni1D78uni1D79uni1D7Auni1D7Buni1D7Cuni1D7Duni1D7Euni1D7Funi1D80uni1D81uni1D82uni1D83uni1D84uni1D85uni1D86uni1D87uni1D88uni1D89uni1D8Auni1D8Buni1D8Cuni1D8Duni1D8Euni1D8Funi1D90uni1D91uni1D92uni1D93uni1D94uni1D95uni1D96uni1D97uni1D98uni1D99uni1D9Auni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB8uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7Funi1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni200Cuni200Duni200Euni200Funi2012uni2016uni201Funi202Auni202Buni202Cuni202Duni202Euni202Funi2034uni203Euni205Euni206Auni206Buni206Cuni206Duni206Euni206Funi2070uni2076uni2079uni2090uni2091uni2092uni2093uni2094uni20A0uni20A1uni20A2uni20A5uni20A6uni20A8uni20A9uni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20B9uni20F0uni2117uni214Duni214Euni2153uni2154uni2184uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C6Duni2C71uni2C72uni2C73uni2C74uni2C75uni2C76uni2C77uni2E17uniA717uniA718uniA719uniA71AuniA71BuniA71CuniA71DuniA71EuniA71FuniA720uniA721uniA788uniA789uniA78AuniA78BuniA78C dieresisacute dieresisgraveuniFE20uniFE21uniFE22uniFE23uni03B1030403130300uni03B1030403130301uni03B1030403140300uni03B1030403140301uni03B1030603130300uni03B1030603130301uni03B1030603140300uni03B1030603140301uni03B9030403130300uni03B9030403130301uni03B9030403140300uni03B9030403140301uni03B9030603130300uni03B9030603130301uni03B9030603140300uni03B9030603140301uni03C5030403130300uni03C5030403130301uni03C5030403140300uni03C5030403140301uni03C5030603130300uni03C5030603130301uni03C5030603140300uni03C5030603140301uni03B9030803040300uni03B9030803040301uni03B9030803060300uni03B9030803060301uni03C5030803040300uni03C5030803040301uni03C5030803060300uni03C5030803060301Eng.alt1Eng.alt2Eng.alt3 uni1FCD02C9 uni1FCE02C9 uni1FDD02C9 uni1FDE02C9dotacutecarondotmacrondieresis tildedieresis tildeacute macrongrave macronacute dieresiscarondieresismacron tildemacron dotmacron dotmacron.capuni030103060308uni030003060308uni030103040308uni030003040308 uni1FDE0306 uni1FDD0306 uni1FCE0306 uni1FCD0306uni0514uni0515uni0516uni0517uni0518uni0519uni051Euni051Funi0520uni0521uni0522uni0523uni0524uni0525uni0526uni0527cyrillic_otmarkuni20BAuni1EFAuni2C6Euni1E9ETurnedauni1EFCuni1EFEuniA722uniA724uniA726uniA728uniA72AuniA72CuniA72EuniA732uniA734uniA736uniA738uniA73AuniA73CuniA73EuniA740uniA742uniA744uniA746uniA748uniA74AuniA74CuniA74EuniA750uniA752uniA754uniA756uniA758uniA75AuniA75CuniA75EuniA760uniA764uniA766uniA768uniA76AuniA76CuniA76EuniA779uniA77BuniA77DuniA77EuniA780uniA782uniA784uniA786uniA78DuniA790uniA792uniA7A0uniA7A2uniA7A4uniA7A6uniA7A8uniA7AAuniA7ABuniA7ACuniA7ADuniA7B0uniA7B1uniA7B2uniA7B3uniA7B4uniA7B6Aogonek.loclNAVEogonek.loclNAVIogonek.loclNAVUogonek.loclNAVLcommaaccent.loclMAHNcommaaccent.loclMAHTurnedeafii10103dotlessafii10105dotless deltalatinuni2C78uni025Cuni025Duni01DDuni025Aiogonekdotlessuni0237uni1EFBuni1E9Cuni1E9Duni2C7A subscriptjuni2C79uni0249dotlessuni029Ddotlessuni02B2dotlessuni03F3dotlessuni1D62dotlessuni1D96dotlessuni1DA4dotlessuni1DA8dotlessuni1E2Ddotlessuni1ECBdotlessuniA723uniA725uniA727uniA729uniA72BuniA72DuniA72FuniA730uniA731uniA733uniA735uniA737uniA739uniA73BuniA73DuniA73FuniA741uniA743uniA745uniA747uniA749uniA74BuniA74DuniA74FuniA751uniA753uniA755uniA757uniA759uniA75BuniA75DuniA75FuniA761uniA765uniA767uniA769uniA76BuniA76DuniA76FuniA771uniA772uniA773uniA774uniA775uniA776uniA777uniA778uniA77AuniA77CuniA77FuniA781uniA783uniA785uniA787uniA78EuniA791uniA793uniA7A1uniA7A3uniA7A5uniA7A7uniA7A9uniA7B5uniA7B7uniA7FAuni1EFDuni1EFFaogonek.loclNAVeogonek.loclNAVlcommaaccent.loclMAHncommaaccent.loclMAHiogonek.loclNAVuogonek.loclNAVf_ff_f_if_f_lf_if_llongs_ts_ta.sc aacute.sc abreve.scacircumflex.sc adieresis.sc agrave.sc amacron.sc aogonek.scaring.sc aringacute.sc atilde.scae.sc aeacute.scb.scc.sc cacute.sc ccaron.sc ccedilla.scccircumflex.sccdot.scd.sceth.sc dcaron.sc dcroat.sce.sc eacute.sc ebreve.sc ecaron.scecircumflex.sc edieresis.sc edotaccent.sc egrave.sc emacron.sc eogonek.scf.scg.sc gbreve.scgcircumflex.scgcommaaccent.scgdot.sch.schbar.schcircumflex.sci.sc iacute.sc ibreve.scicircumflex.sc idieresis.sc idotaccent.sc igrave.scij.sc imacron.sc iogonek.sc itilde.scj.scjcircumflex.sck.sckcommaaccent.scl.sc lacute.sc lcaron.sclcommaaccent.scldot.sc lslash.scm.scn.sc nacute.sc ncaron.scncommaaccent.sceng.sc ntilde.sco.sc oacute.sc obreve.scocircumflex.sc odieresis.sc ograve.scohungarumlaut.sc omacron.sc oslash.scoslashacute.sc otilde.scoe.scp.scthorn.scq.scr.sc racute.sc rcaron.scrcommaaccent.scs.sc sacute.sc scaron.sc scedilla.scscircumflex.scscommaaccent.sc germandbls.sct.sctbar.sc tcaron.sc tcedilla.sctcommaaccent.scu.sc uacute.sc ubreve.scucircumflex.sc udieresis.sc ugrave.scuhungarumlaut.sc umacron.sc uogonek.scuring.sc utilde.scv.scw.sc wacute.scwcircumflex.sc wdieresis.sc wgrave.scx.scy.sc yacute.scycircumflex.sc ydieresis.sc ygrave.scz.sc zacute.sc zcaron.sc zdotaccent.scuni2071uniA78FuniA7F7uniA7FBuniA7FCuniA7FDuniA7FEuniA7FFuni0528uni052Auni052Cuni052EuniA640uniA642uniA644uniA646uniA648uniA64AuniA64CuniA64EuniA650uniA652uniA654uniA656uniA658uniA65AuniA65CuniA65EuniA660uniA662uniA664uniA666uniA668uniA66AuniA66CuniA680uniA682uniA684uniA686uniA688uniA68AuniA68CuniA68EuniA690uniA692uniA694uniA696uniA698uniA69Auni0529uni052Buni052Duni052Funi1C80uni1C81uni1C82uni1C83uni1C84uni1C85uni1C86uni1C87uni1C88uniA641uniA643uniA645uniA647uniA649uniA64BuniA64DuniA64FuniA651uniA653uniA655uniA657uniA659uniA65BuniA65DuniA65FuniA661uniA663uniA665uniA667uniA669uniA66BuniA66DuniA681uniA683uniA685uniA687uniA689uniA68BuniA68DuniA68FuniA691uniA693uniA695uniA697uniA699uniA69Bafii10066.loclSRBafii10068.loclSRBafii10069.loclSRBafii10081.loclSRBafii10084.loclSRBafii10090.loclSRBuniA66EuniA67FuniA69CuniA69Duni2126uni0370uni0372uni0376uni03CFuni037Funi0371uni0373uni0377 uni03D0.altCfrakturHfrakturIfrakturRfrakturZfrakturuniA762uniA763uni212Cuni210Buni2110uni2112PiDoubleStruckuni211BTurnedFuni212B CDoubleStruck HDoubleStruck NDoubleStruck PDoubleStruck QDoubleStruck RDoubleStruck ZDoubleStruckItalicDDoubleStruckGammaDoubleStruckuni2107uni212Auni2130uni2131uni2133uniA796uniA798uniA79AuniA79CuniA79EItalicdDoubleStruckItaliceDoubleStruckItaliciDoubleStruckItalicjDoubleStruckgammaDoubleStruckpiDoubleStruckuni210Euni210FscriptescriptoscriptguniA794uniA795uniA797uniA799uniA79BuniA79DuniA79FuniAB30uniAB31uniAB32uniAB33uniAB34uniAB35uniAB36uniAB37uniAB38uniAB39uniAB3AuniAB3BuniAB3CuniAB3DuniAB3EuniAB3FuniAB40uniAB41uniAB42uniAB43uniAB44uniAB45uniAB46uniAB47uniAB48uniAB49uniAB4AuniAB4BuniAB4CuniAB4DuniAB4EuniAB4FuniAB50uniAB51uniAB52uniAB53uniAB54uniAB55uniAB56uniAB57uniAB58uniAB59uniAB5AuniAB64uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209CuniA770uniA7F8uniA7F9uniAB5CuniAB5DuniAB5EuniAB5Funi2E2Fzero.lfone.lftwo.lfthree.lffour.lffive.lfsix.lfseven.lfeight.lfnine.lfzero.osfone.osftwo.osf three.osffour.osffive.osfsix.osf seven.osf eight.osfnine.osf zero.slash zero.tosfone.tosftwo.tosf three.tosf four.tosf five.tosfsix.tosf seven.tosf eight.tosf nine.tosf zero.dnomone.dnomtwo.dnom three.dnom four.dnom five.dnomsix.dnom seven.dnom eight.dnom nine.dnom zero.numrone.numrtwo.numr three.numr four.numr five.numrsix.numr seven.numr eight.numr nine.numruni215Funi2189uni2155uni2156uni2157uni2158uni2159uni215Auni2150uni2151uni2152uni2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni2042uni204Cuni204Duni2045uni2046caretuni2041uni2040uni2050uni2E36uni2E37uni205Cuni2E13uni2E16uni2E08downwardsancorauni2E0Euni2049uni2E2Duni2059uni2055uni2E10uni205Buni2058uni2027uni2043uni2E12uni2E18uni2054uni2E04uni2E1Cuni2E0Cuni2E02uni2E09uni2E20uni204Eonedotenleaderonedotovertwodotspunctuationuni2E19uni2E0Funi2047uni2048uni2E34uni2E33uni2E07uni2E06uni2E0Buni203Buni2E11reversedparagraphreversedquestionmarkuni204Funi2E01uni2E00uni2E05uni2E1Duni2E0Duni2E03uni2E0Auni2E21uni2E30squaredfourdotpunctuationuni2053uni2056uni2E1Euni2E1Funi2E1Buni204Auni2E39uni205Duni2E32uni2E38uni2E35uni2051twodotenleaderuni205Atwodotsoveronedotpunctuationuni203Funi2023uni2E3Cuni2E3Duni2E3Euni2E3Funi2E41uniA673 upwardsancorauni2E31uni208Duni208Ebrackhalfbottomleftbrackhalfbottomrightbrackhalftopleftbrackhalftoprightleftdoubleparenthesisrightdoubleparenthesisuni2E26uni2E27uni207Duni207Euni2E1Auni2010uni2011uni2E3Buni2E3Auni2E40uni2036uni2035uni2057uni2037uni2E42 braceleft.sc braceright.scbracketleft.scbracketright.sc exclam.sc exclamdbl.sc exclamdown.sc parenleft.sc parenright.sc question.scquestiondown.scuniA92EuniA67Euni205Funi2028uni2029uni2061uni2064uni2063uni2062uni2066uni2067uni2068uni2069uni2060uni20B6uni20BCuni20BDuni20AAuni20B7uni20B8uni20BBuni20BEuni2127uni2135uni214Buni2136uni2052uni2138uni208Cuni207Cuni2137uni208Buni207Buni00B5uni2031uni208Auni207AreversedSansSerifLsummationDoubleStruckturnedSansSerifGturnedSansSerifLturnedSansSerifYRotatedQuni2100uni2101uni2106uni2103uni2104uni213Buni2109uni2139uni203Duni2114uni2125uni214C prescriptionuni214Auni211Funi2108uni2120symbolforsamaritansourceuni2121uni2123 weierstrassuni02DEuni02E5_uni02E5_uni02E9uni02E5_uni02E5_uni02E6uni02E5_uni02E5_uni02E8uni02E5_uni02E5_uni02E7uni02E5_uni02E9uni02E5_uni02E9_uni02E5uni02E5_uni02E9_uni02E9uni02E5_uni02E9_uni02E6uni02E5_uni02E9_uni02E8uni02E5_uni02E9_uni02E7uni02E5_uni02E6uni02E5_uni02E6_uni02E5uni02E5_uni02E6_uni02E9uni02E5_uni02E6_uni02E6uni02E5_uni02E6_uni02E8uni02E5_uni02E6_uni02E7uni02E5_uni02E8uni02E5_uni02E8_uni02E5uni02E5_uni02E8_uni02E9uni02E5_uni02E8_uni02E6uni02E5_uni02E8_uni02E8uni02E5_uni02E8_uni02E7uni02E5_uni02E7uni02E5_uni02E7_uni02E5uni02E5_uni02E7_uni02E9uni02E5_uni02E7_uni02E6uni02E5_uni02E7_uni02E8uni02E5_uni02E7_uni02E7uni02E9_uni02E5uni02E9_uni02E5_uni02E5uni02E9_uni02E5_uni02E9uni02E9_uni02E5_uni02E6uni02E9_uni02E5_uni02E8uni02E9_uni02E5_uni02E7uni02E9_uni02E9_uni02E5uni02E9_uni02E9_uni02E6uni02E9_uni02E9_uni02E8uni02E9_uni02E9_uni02E7uni02E9_uni02E6uni02E9_uni02E6_uni02E5uni02E9_uni02E6_uni02E9uni02E9_uni02E6_uni02E6uni02E9_uni02E6_uni02E8uni02E9_uni02E6_uni02E7uni02E9_uni02E8uni02E9_uni02E8_uni02E5uni02E9_uni02E8_uni02E9uni02E9_uni02E8_uni02E6uni02E9_uni02E8_uni02E8uni02E9_uni02E8_uni02E7uni02E9_uni02E7uni02E9_uni02E7_uni02E5uni02E9_uni02E7_uni02E9uni02E9_uni02E7_uni02E6uni02E9_uni02E7_uni02E8uni02E9_uni02E7_uni02E7uni02E6_uni02E5uni02E6_uni02E5_uni02E5uni02E6_uni02E5_uni02E9uni02E6_uni02E5_uni02E6uni02E6_uni02E5_uni02E8uni02E6_uni02E5_uni02E7uni02E6_uni02E9uni02E6_uni02E9_uni02E5uni02E6_uni02E9_uni02E9uni02E6_uni02E9_uni02E6uni02E6_uni02E9_uni02E8uni02E6_uni02E9_uni02E7uni02E6_uni02E6_uni02E5uni02E6_uni02E6_uni02E9uni02E6_uni02E6_uni02E8uni02E6_uni02E6_uni02E7uni02E6_uni02E8uni02E6_uni02E8_uni02E5uni02E6_uni02E8_uni02E9uni02E6_uni02E8_uni02E6uni02E6_uni02E8_uni02E8uni02E6_uni02E8_uni02E7uni02E6_uni02E7uni02E6_uni02E7_uni02E5uni02E6_uni02E7_uni02E9uni02E6_uni02E7_uni02E6uni02E6_uni02E7_uni02E8uni02E6_uni02E7_uni02E7uni02E8_uni02E5uni02E8_uni02E5_uni02E5uni02E8_uni02E5_uni02E9uni02E8_uni02E5_uni02E6uni02E8_uni02E5_uni02E8uni02E8_uni02E5_uni02E7uni02E8_uni02E9uni02E8_uni02E9_uni02E5uni02E8_uni02E9_uni02E9uni02E8_uni02E9_uni02E6uni02E8_uni02E9_uni02E8uni02E8_uni02E9_uni02E7uni02E8_uni02E6uni02E8_uni02E6_uni02E5uni02E8_uni02E6_uni02E9uni02E8_uni02E6_uni02E6uni02E8_uni02E6_uni02E8uni02E8_uni02E6_uni02E7uni02E8_uni02E8_uni02E5uni02E8_uni02E8_uni02E9uni02E8_uni02E8_uni02E6uni02E8_uni02E8_uni02E7uni02E8_uni02E7uni02E8_uni02E7_uni02E5uni02E8_uni02E7_uni02E9uni02E8_uni02E7_uni02E6uni02E8_uni02E7_uni02E8uni02E8_uni02E7_uni02E7uni02E7_uni02E5uni02E7_uni02E5_uni02E5uni02E7_uni02E5_uni02E9uni02E7_uni02E5_uni02E6uni02E7_uni02E5_uni02E8uni02E7_uni02E5_uni02E7uni02E7_uni02E9uni02E7_uni02E9_uni02E5uni02E7_uni02E9_uni02E9uni02E7_uni02E9_uni02E6uni02E7_uni02E9_uni02E8uni02E7_uni02E9_uni02E7uni02E7_uni02E6uni02E7_uni02E6_uni02E5uni02E7_uni02E6_uni02E9uni02E7_uni02E6_uni02E6uni02E7_uni02E6_uni02E8uni02E7_uni02E6_uni02E7uni02E7_uni02E8uni02E7_uni02E8_uni02E5uni02E7_uni02E8_uni02E9uni02E7_uni02E8_uni02E6uni02E7_uni02E8_uni02E8uni02E7_uni02E8_uni02E7uni02E7_uni02E7_uni02E5uni02E7_uni02E7_uni02E9uni02E7_uni02E7_uni02E6uni02E7_uni02E7_uni02E8uniAB5B ampersand.scuni2129uni0308uni0307 gravecomb acutecombuni030Buni0302uni030Cuni0306uni030A tildecombuni0304 overlinecmbuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320hookpalatalizedbelowcombhookretroflexbelowcomb dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334strokeshortoverlaycombstrokelongoverlaycombslashshortoverlaycombslashlongoverlaycombuni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni034Funi0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035Auni035Buni035Cuni035Duni035Euni035Funi0360uni0361uni0362acutegraveacute acutemacronuni1DFEuni1DFF brevemacroncombiningconjoiningmacroncombiningmacronlefthalfcombiningmacronrighthalf dottedacute dottedgravedoublecircumflexabovegraveacutegrave gravemacronuni1DD0uni1DC4 macronbreveuni1DC6 ogonekabove snakebelowsuspensionmarkuni1AB0uni1AB1uni1AB2uni1AB3uni1AB4uni1AB5uni1AB6uni1AB7uni1AB8uni1AB9uni1ABAuni1ABBuni1ABCuni1ABDuni1DE7uni1DE8uni1DE9uni1DEAuni1DEBuni1DECuni1DEDuni1DEEuni1DEFuni1DF0uni1DF1uni1DF2uni1DF3uni1DF4uni1DF5uni1DFBuni1DFCuni1DFDuni2DE0uni2DE1uni2DE2uni2DE3uni2DE4uni2DE5uni2DE6uni2DE7uni2DE8uni2DE9uni2DEAuni2DEBuni2DECuni2DEDuni2DEEuni2DEFuni2DF0uni2DF1uni2DF2uni2DF3uni2DF4uni2DF5uni2DF6uni2DF7uni2DF8uni2DF9uni2DFAuni2DFBuni2DFCuni2DFDuni2DFEuni2DFFuniFE00uniFE27uniFE28uniFE29uniFE2AuniFE2BuniFE2CuniFE2Duraboveusabove zigzagbelowuni1ABEdieresiscomb.scdotaccentcomb.sc gravecomb.sc acutecomb.schungarumlautcomb.sccircumflexcomb.sc caroncomb.sc brevecomb.sc ringcomb.sc tildecomb.sc macroncomb.sc ogonekcomb.sc overscore.scuni0342uni0343uni0344uni0345uni0483uni0484uni0485uni0486uni0487uniA66FuniA674uniA675uniA676uniA677uniA678uniA679uniA67AuniA67BuniA67CuniA67DuniA69EuniA69FuniFE2EuniFE2FuniA670uniA671uniA672uni1DDBuni1DDEuni1DDFuni1DE1uni1DE2uni0363uni1DD4uni1DD5uni1DD6uni1DD7uni0368uni0369uni0364uni1DD9flattenedopenaaboveuni1DDAuni036Auni0365uni1DD8uni1DDCuni1DDDuni1DE5uni036Buni1DE0uni0366uni1DCAuni036Cuni1DE3uni1DE4uni036Duni0367uni036Euni036Funi1DE6uni2C7Duni2C70uni2C7Euni2C7FuniA7AEuniAB60uniAB61uniAB62uniAB63uniAB65ogonekcenteringsummationDoubleStruck.miruni20BFuni2E43uni2E44uniA700uniA701uniA702uniA703uniA704uniA705uniA706uniA707uniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716xx"-,-xx"-,-{{F-O-xxeeh-h-xxee--xx"-,-``HH)}h-3sh-``HHg-m-, UXEY KQKSZX4(Y`f UX%acc#b!!YC#DC`B-, `f-, d P&Z( CEcEEX!%YR[X!#!X PPX!@Y 8PX!8YY  CEcEad(PX! CEcE 0PX!0Y PX f a PX` PX! ` 6PX!6``YYY% CcRXK PX! CKPX!Kac CcbYYdaY+YY#PXeYY-, E %ad CPX#B#B!!Y`-,#!#! dbB #BEX CEc C`Ec*! C +0%&QX`PaRYX#Y!Y @SX+!@Y#PXeY-,C+C`B-,#B# #Babfc`*-, E Ccb PX@`Yfc`D`-, CEB*!C`B- ,C#DC`B- , E +#C%` E#a d PX!0PX @YY#PXeY%#aDD`- , E +#C%` E#a d$PX@Y#PXeY%#aDD`- , #B EX!#!Y*!- ,EdaD-,` CJPX #BYCJRX #BY-, bfc c#aC` ` #B#-,KTXdDY$ e#x-,KQXKSXdDY!Y$e#x-,CUXCaB+YC%B %B%B# %PXC`%B #a*!#a #a*!C`%B%a*!Y CGCG`b PX@`Yfc Ccb PX@`Yfc`#DC>C`B-,ETX#B E #B #`B `aBB`++"Y-,+-,+-,+-,+-,+-,+-,+-,+-,+-, +-),# bfc`KTX# .]!!Y-*,# bfc`KTX# .q!!Y-+,# bfc&`KTX# .r!!Y-, +ETX#B E #B #`B `aBB`++"Y-,+- ,+-!,+-",+-#,+-$,+-%,+-&,+-',+-(, +-,, <`--, `` C#`C%a`,*!-.,-+-*-/, G Ccb PX@`Yfc`#a8# UX G Ccb PX@`Yfc`#a8!Y-0,ETX EB/*EX0Y"Y-1, +ETX EB/*EX0Y"Y-2, 5`-3, EBEcb PX@`Yfc+ Ccb PX@`Yfc+D>#82*!-4, < G Ccb PX@`Yfc`Ca8-5,.<-6, < G Ccb PX@`Yfc`CaCc8-7,% . G#B%IG#G#a Xb!Y#B6*-8,#B%%G#G#a B C+e.# <8-9,#B%% .G#G#a #B B C+ `PX @QX  &YBB# C #G#G#a#F`Cb PX@`Yfc` + a C`d#CadPXCaC`Y%b PX@`Yfca# &#Fa8#CF%CG#G#a` Cb PX@`Yfc`# +#C`+%a%b PX@`Yfc&a %`d#%`dPX!#!Y# &#Fa8Y-:,#B & .G#G#a#<8-;,#B #B F#G+#a8-<,#B%%G#G#aTX. <#!%%G#G#a %%G#G#a%%I%acc# Xb!Ycb PX@`Yfc`#.# <8#!Y-=,#B C .G#G#a ` `fb PX@`Yfc# <8->,# .F%FCXPRYX +-o,:+?+-p,:+@+-q,:+>+-r,:+?+-s,:+@+-t,;+..+-u,;+>+-v,;+?+-w,;+@+-x,;+>+-y,;+?+-z,;+@+-{,<+..+-|,<+>+-},<+?+-~,<+@+-,<+>+-,<+?+-,<+@+-,=+..+-,=+>+-,=+?+-,=+@+-,=+>+-,=+?+-,=+@+-, EX!#!YB+e$PxEX0Y-KRXYcpB@ z\@,*B@mcSG3!*B@wh[M=**BA %   *BA @@@@@@@@ *D$QX@XdD(QXXDY'QX@cTXDYYYYY@oeUI5# *DdDD ttfautohint version = 1.7 adjust-subglyphs = 0 default-script = latn dw-cleartype-strong-stem-width = 0 fallback-scaling = 0 fallback-script = none fallback-stem-width = 0 gdi-cleartype-strong-stem-width = 1 gray-strong-stem-width = 0 hinting-limit = 200 hinting-range-max = 50 hinting-range-min = 8 hint-composites = 0 ignore-restrictions = 0 increase-x-height = 14 reference = reference-index = 0 symbol = 0 TTFA-info = 1 windows-compatibility = 1 x-height-snapping-exceptions = control-instructions = linphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/NotoSans-Italic.ttf000066400000000000000000016267101434616504300305540ustar00rootroot00000000000000 GDEF,GPOS_ GSUB S zOS/2l6 `TTFAQ+cmap8 s ^cvt \pNfpgm9&|d mgasp glyfx8TLheadJ&6hhea f'0$hmtx'T2loca8Y2maxp& name.Tpost88prepsB*l$=D]wwIUUW##::==?BDGIfknrR]SXYaacdffhh#=AEEIIYn   9ILNOQRUVYY[\^^adffqxz          ( . 0 0 6 6 : H L L Q Q S S [ \ b b l l o t v { }                                        , / 0 2 4 5 5 = ? A B C D E I J J K K N V X _ c c f f i v w w x    ,,4<DN\\d $&/R -   # % ) + , - . / 0 1  7  )F + ,] / 0_ 2 4a 6 <d ? ?k L Ll Q Qm X _n c cv i ow r v~ x z |     @ t y } !CCvvST  "#SS   . 4 7 : ; < > @ C L P V  X  4h = > A K N P R e i /         ! " # $ * + , Y [ n r s t u x y z | }   4nDFLTcyrlgreklatnkernmarkmkmk. u22԰,ؖL bXn $x~x&nnnnnnxxxxxxnnnxxxxxxnxF*d $F F8* * . $ :   nB x BB  B d*n44B"J0L0LBBB"d PP0LfB*B "0LBLRRRRXnnnnnnnnnnnnxxxxxxxx**FFFFFF****dddddddd****8FFddnx~~~~~~~~~~~< <* $%&'()*./2345679:;<=>FYZ\^cm} #%'56789:<>BDFUZ[\^_`bdfgkmnopqrsuv{}<=ADFGMPQSTUV\]ackmruvxz{|}   &(*,.02468:<BCDEFGHIJKLMNOPQ];<CDEFGHIJ}~          79:<#%'579LNP]!"$B A       m       -ZM(((-<7<#%'79LNP]-d79:<#%'579LNP] ; -2"PF mdkqmdkqq~<8Y[^bgnopru =abcdefopqrstuvq[\^dgk  =]v{~;<=>?@ABijklmnwxyz{| (v{;<=>?@ABwxyz{|0 v{};<=>?@ABwxyz{|2v{~ ;<=>?@ABwxyz{| S] SX9 MST[ack|~ "o~oqsoos  STW& dM]ack * dM]ackttMackX* M]ack + dMN]abcdkl XX\^X2W MSack| + MS]ack|;"6;"2,6;< "" "oqsq~brbnorso-K-<                                   <<     ZZ           BFTPY_((222222 22222222(<( PPF<( ((F2j<((d~~~2( $$&'./ 24 7>DEHINNRSUUWWY\^^#mm$}}%&,-.3:AEFKLVWXY\]^_`aefi jpqrs#)t++{--|//}11~335:<<>>BCEGUUY^``dgkkmmoprrtuxy}}==ABDGKKMQSWYY[[ _m ry{~!%124@EFIKMQRXYgioyz!!&>@@BQ]^CJWXgx} !  +-!27}~8:;FPVal |   p p     A;:;$$&&(''..F//'2233U4477288 9:$;;F<<==>>>ADDIIQNN9UU8WW.YZ [[9\\ ^^AmmO}}N( U ((((RF9'''R''  888##2$$.%%2&&.''2((.)) ++ -- // 11 33 55$66 7788 99::><<>>>>BBCCFFUUYYZZ[[ \\]] ^^``WddeeffVggkkmmTooWpp rrVttuu xxyy}}PPX #"/#10"0S/"##=<<  !!<!<"E$ $ $  :;;ON== AABBDD&EEFF%GGKKMMNN6OOMPP!QQ/SS1TTLUU1VVLWWKYYK[[0__``aabb6ccdd6efgghhiijjkkll6mm/rrss ttMuu!vvSwwxx@yyJ{{?||5}},~~05, @E?/" IHIH5,  ?GG   1=1=0"E@J5,,4+4+ 4+4++           &&((**,,..002244&5566&7788&99::&;;<<&==>> @@ BB%CCDD%EEFF%GGHH%IIJJ%KKLLMM NNOO PPQQ ]]2^^. CJWXghin ovwx}     T !!}}Q CBCB7- *  )  3 p p= D D D@ @ J,A,$$&&**224477288 9:$<<==F@@JDDFHIIYJJ+PQRRSSTTUUVVWW*XXYZ \\ ]]:``JmmW}}V]  ++++        ""##2$$*%%2&&*''2((*)) **++ ,,-- ..// 0011 2233 4455$66 7788 99::F;;:<<F==:>>F??:BBCCDD]EEFFGGIIUU8WW7XX6YY5ZZ3[[1\\4]]^^Gdd\ggGkk\pp%uu%vvwwxxyyzz{{}}XXTIT! !/0 # Z/"!#E    . D-C B B.$ $ $  AS@,S@,  ,WV^==%@@AABBDDEEFF GGKK LLMM?NN>OO(RRSS0TTVVZZ[[#\\]]"^^__`` aa?bb>cc?dd>eeff gghh iijj kk?ll>mmnn sstt(uuBwwxx({{||}}~~#(B!C  ZRQRQD!C"-"-POPO "-0E0E0E NN#   #/././."-(DDM M LKLK  !C            !!&&''(())**++,,--..//00112233445566778899::;;<<==>> ??@@ AABB CCDD EEFF GGHH IIJJ KKLLMM NNOO PPQQ ]]2^^*1%;BCD8EJKLMP=QR7SV<WXY^ _`6afghinop5qvw|}~3;1H4 G8 765   I%13 4}}~~Y[  U  ) '  &  9 p p q seD fxm$=D]ww45LIkUUW##::?BDGIfknrR]&S'XYfaahddiffjhhkl{!=AEEIIYn;KQRVX]^_`  b dg9iILNORRUVYY[\^^adffqxz   3  4  8  9 ( ,: . .? 6 6@ ; HA L LO Q QP S SQ [ \R o oT q sU v {X } ^ c e g h j k l m n p s w x             \XHe J>N]6B@\f 6\\Bt],2\\P\\\\\\vN]]]]]dffff 6,666666`|],],],],],\\\\\6\6&HHHH\28J`>DJ2PVe N\N]],]],]],HJbh@2@2j@2nt\\Vf\f\f\f\f\z&  \6`d\H `\Xe Jd\>N]6J\ X   t2"2],X\(0v],e @\*\6Xe ^N\\L>J]6H\*Jr8*6VlT]\\],Z\J]2Be p Jf\>6\R2 Jf"(.4:Xvd6@6@FLR2X^dJjpv||\66d  JrZ NT|$*06^<BH\6\6`&&NT\],\\]],dd***JrZ`flrx~6\RD$\6\6\6\6\6\6\6\6\6]],]],]],]],]],6666f\  \6*] t&\6,\2"8>dDJr*@2JP|V\Jf,b,\,,hxn^tz\6]],f\f\f\f\f\\6\6`t, N\6\6]],]],f\f\TJ\6P]],]],]],]], \He \jjx\ *L e66]"(.4::@FLRXXX^djpppv|\H]]ln42 ]]$*I06<Bd]H6HNTXHBZ`flrx]~\xD]. &,2t8>D.JP V\ b.. h.ntzL@"  ""  ."(..4::@FLRX^djpv|\Pe \JJt $*0>6<NBHNTZ`]],]],]],]],66flflrx@2~@2@2~\\f\f\f\ 6        \\]]]]]]X66H&& &&,28>],],],],],],DJPV\bhntz  ],],""""""""   \\\\fJxXX  Jf""""(R..4:@FLR]e e 6X^djpv|e           \$*0]66<B\NHN@TZD6\`flrxt]~]ttt],\t2\ &\,2>>>>>>>8>>>DDJPV\V\bbbbbbbbbh$eeenetttz eeeeeeeeee]JJJJJ]"Bt(.4:@F^LR]X^\djpv|],\Jf\>\\\ \$ZZ**06<BHNTZ`flrx~ID6E'#B########c#'#p#{,6|6~0DEFPI`pY0@OwDF ?DIG'w'EDA. ''D\FDF&EFEF NEF E DFFIEGOnDbF<`<<<hqDG^z<<QQDQQQ QQQQQQ_#'mJBsBc''rL:|:rY o0(B~nJ@[f3lS:s|[K& fp:pla:xu  w<Wz y I0-<@c+Zup {C[QQmmQQmm388:>EQQC5BHQQmmQm)m88DQ]QQQ mm88Q&Qm,mQzQQmQmsm8g8Q88QQYQQQQ+U>yRoiZ?>B$!/jeiWUB$b?`^kDH0f0DFDFF~F3;CC"~JF]T} ) EEFDBV V1)`;'*%'":dK!H#QVK~''-''l'#b"'Pbb#!#I9x#bf#fj#'nEAA k:E0FFFc%}&BSu'$eBJ1)&HWK( KN'+/27GKORW #$%&XZckoef&'()*+,-./0123]^ Qbz!%)1;@56_`abcdefwxyz{|}~?`NOab + 6 S X>D>,>V>>D>D>D>D>D>V>V>V>V>VX>,>,>,>,>D>V>D>V>D>V>>>D>V>V>D>V>D>V>D>V>D>V>D>V>D>V>D>V>D>V>D>V>D>V>XX>,>D>V>D>V>D>VXX>D>V>D>V>D>V>D>V>D>V>D>V>DF"(.4:@FLRXXXXX>,>,>,>,>D>V>D>V>D>V>D>V>>>>>>V>V>V>V>V>V^djpv|>V>V>>DN>,>V>,>,     >D>V$*006<BHN>,TZe H'+8teee^e`eQeJeeeKeCeIeeezeeeAeeeee  S P(f!e[     AnA6Aee F< Gm$=D]ww45LIkUUW##::==?BDGIfknrR])S*XYiaakddlffmhhno~!=AEEIIYn>NTUY[`abc  e gj9lILNORRUVYY[\^^adffqxz   6  7  ;  < ( ,= . .B 6 6C ; HD L LR Q QS S ST [ \U o sW v {\ } b g i k l n o p q r t w { |              9   ## *3 09z9z$ 9 $$ $2 # 92##$$#$ 9## 8# x:, x 8># ...:::DFFFJ9zvvvPrV"""x\V 444$@@@0bhLLLnIXttt"#t(9$2z##  .4,##&82 $@##x *$ *$ *$ 0 p#RXFL4^d$ ^d  9 89 8 |vt $#Z`r"j(."(. T(.4:@FL  9RvX4<^d9 9z #93 09z#&9 #*jpv|$ Z <r#~xx9H:, b:$,0j$  ##9#0 9h##39z# 9 9j# $$$2 9###r#x#69 # x:, #4Hz$#*:, $0$6<#BH#NTZ`Z`flr"x#9@ ~9$2 $H.4t:#6#*9zIX$:,5#FL 4<3p9h9 9:&t$2,#28>9zH 9 D  JPVP\$ #\#bH hntz#H#6v# # VP HZ9zIX ^"(.4L:@FLR$X#^djp2v|9$2l"(   .4$* $9z906<B<BHNTZFL`"`"flrx$2 $#~ x#9 8##$$#Id9h $ 9p9z $56&,8289 # $ 9hZZ$ >$2DJPV\bhntz":@FLvt $&,9z9 # "(.4:@2FLRX^djpvt|  ~,#   z$9 9 66#Zn# $9 $#$* xH$2$2:,# H$#~##r #$#~ #####IX*$***00#B#B6<<#~<B8#IXH NT:, nZZ`flb$r#6$x~<`:, :,4< IX  &,28> $9z:,D# JH$ $9z# PV\(bhntzHIXIX 6:, "(.4::@FLRX^d j pv|0rxxrlrN` Z$J #IX $* l0r< l66<BHNNrrTxZ`flrx~######   ### $ &,#### *$28 *$ *$>D3 JP 0 0 0 bhV\bhbhntz$ $   9 89 89 8 $# $# $#$ x  "(.  #4:@FL<<<<<<BB99RRRRXX^^^^^^dj4jpNNNNNNTTv|43,,nnxxxxxx~~ 09z;9z;9z  $*06<<<<<<BBHHHHHHNNNNNNTTZ`rfrlrrxxxxxx~~ $99 &,28>DJPV\bbhntz #:,$! "(.4:@@@@FFFFLLLLRRRRXXXX^^^^ddddjjjj#pv|4$#9$$### *9z9z$9h$z 0 # 0   $9   $ * 0 6#~#~ < 9 B H N#~#~#~$ T TIX Z `#~#~ r#~ f#~ l r#~#~:,###$9### x## ~#$ #B ## $2 $ $#98 !!!!!!!! !&!2!,!2!\M!8!>!D!J!PM!V!\!b!h!n!t!z!II!!!!!!!!!!!!!!!!!!!!!!!!!!!!"":""" """"""":"""("."4"4":"L"@"F"L"^"R"X"^"X"^"d"p"p"j"p"p"""v""|"""""""""""""""""""""""##1# #5##0##$9z#*#09 9z#6#<#B#H#N$#TH#Z##`#f##~#l9#r#x#~#################9h#####92$$492$$$$ $ $&$&$,$2$8$8$8$>$D$J$P$V$\3$b$h$n$t$z$$$$3|$$$$$$$$$$^/ls]WXe||+,1- W/C,F&g&&&FLI}{&&&*$zV{>d=cP]@m\<4a'+CgD,T7bKC.vnTA? ~Y @Wyk|G9AzOLf+#>bcH, [L n%sv4du, [%s;4~4},  [%s444rL4M}`sb`?Vem)uj5(D<gDW6W`R k2smQKmD.+Sc^*/.k8"eV!4$|1 5)0P`Y|`6aa a?aaxaMqYYt!9TL j)}@Z"%)6L&cN:{\]C'-O aeaU6aaaaa~aaaa7/7aa}kaaaa3aaaawa!aTaaaaaaaa,c#qevWoHgW!% R#qKW>7GJ}1#X#qR:6GE;I"R/K/$aB<$ho$uch$$_^srVPVVoq_[2zW_H  V,W]qr  ~V|WN{ri(-[jjjy,JuNR r"0!aZ2eaa'2 $#:]zWZQX J,;NsS; 6; _;!J;{"i;?E!B9?2;;k;!mC!! ;;;!+; ;;; !h;e;! ;*;f;Z _!.;7 <! ;a;A!;z !sU;R$;;! ; ;=!18 ;D I!CuEfGGK;?%IQ[aa4chN\&,xt7Uh>n+P5NO~g} (&) *:$=D]4:CINTY_du  &)?HIUUW[^_acefhikkmmoprrtu|}    ?BIIKLww !"#(*+./01"2$3[>AkLQo]^ueewxy|   ++QQSSVXbbzz|}%',..026689;;?BDDFGJP SSXYff9JCJQV_fo=@EEII_`       !#"")..*JL+NO.RR0UV1YY3ad4qx8}@GT\]w    * + 6 6 S S q q } }                      V  b h L * nT  J P t z ^ \  f   f,ff f  T ^ f,,,,,ffff     hf hf hf hf L   ****TfTfTf,,, J J J       f f f f f f t ^ ^   V L nT ^ z ^ , ,   V n L z ff,ff  t t t ^ > n  ff ^   ff,             ,,,,,,, f f ^ ^ ^ \  , f f f f f  hf ,, hfTf  ,, J J f f L  ,,,, ^  f f\\$*06< B  \ \f t HNTZ`fflrx~ > > >       & ff , 2 8 > > D J P V \ V \ V \  b hf L L L L L    **** n n TfTfTfTf,,,, f f J J J J          f f f f f P P t t z z ^           . " . ( . . 4 4,,,,,,  ,,         . " . ( . . 4 : @ F L R X ^ ^ d j p v |   \ \*  P 8T T   *T ,fff  ff f f  ff    $ $ $ $ $ $ $ $ $ $ $ * 0 0 0 0 0 0 6 6 < < < < < < < < < < B H H H N N T T Z Z Z Z Z Z ` f f f f f r r r r r r r r r r r l r x x x x ~ ~ ~ ~ ~ ~ t  ,f \         & , 2 8\ > D J P V \ b hSyPa#afaaTP DG 8`1:aaaaNJ{}a{aa~k.#ta\ana(Z^xzceRg@D50HpCU(RX[DMMZD^;;;N;;;\;;;;;y;;z; ;;;X;;;;;;@\piWo ]hmhhF\+ 8usTP $6HZZttV    $C  DE Z g"(.4>[$*06<E$E$q  .  T c*Q - : l tB$T6BTf      e ee S  "(.4((_/(|/#/#  2cQ - 0 : b l t f $6HZZttV  Z# xZ]b T$$ l   B"(.4x+H$*06<L>LD 9x Jefc*Q - : l t f<NZfr~n 2>Pbn HR +: 0T  h } %  }_ * h B;0;  X"(.4Zw,$*06<^*^D  @X   # % ) + , - . / 0 1  7  8 6 <: ? ?A L LB Q QC D     " $ ( * + , - . / 0  6  )E + ,Z / 0\ 2 4^ ? ?a Q Qb X _c c ck i ol r vs x zx | { E"(L.4:@F^LRX^jddjpv|  h3" &,28>DJPV\bhntz "(.4::@FLRX^djpv|44&&00QQ#4O98GFHcf"$&O"4%!Sp% , Mlfl GG7"[l$C ZlLlWl2 Vl1 KlJlQlT*lIl]lElFlHlNl  2p @    t y }  &,28&"^ |`  "CCvvST  "#SS   . 4 7 : ; < > @ C L P V  X  4h = > A K N P R e i #"# & ) , - . 0 2 5 6 ; ? E  G  ,W / 0o 2 4q = >t A Kv N P R U X _ c c i o r z | 6<BHNTZ`flrx~ J882\(DDt(P &D\,((28>DJPV\bhntz((((((((((((((((((((((((((((((((((((((((((((((( (((((((( (((((((((((((((("((((((((((((((((((M<WaPgVp_j`/:A^'LBqm<6=A2SOJiL4;(?0>E7|H)518$G;;Y;5;2;`;F;K;j;[;H;3K _3E>/G8F9`flrx~ &,28>DJPV\bhntz** ****"****$****(****.4:@FLRX^djpv|********* *$*=4krVl=L[\Inbc/LsJV^gKNHI__b*\\fiVXf&\K`+atn\XPZk\#c#J\P^dT gK]/Y1aHjybC~B4o_Ba`1_8d@ x clk}F}XR\wF3.0;vB{BBBfxBH[B}BtBBwBssBuB|B  j        $ * , Y Y [ [ n n r u x z | }  %$KjY+X^djv|pvv||  BBBB B7BBBBsBBBB "DFLTcyrlFgreklatn  SRB 2  "CAT JMAH tMOL ~NAV ROM       aaltc2sccaseccmpccmpccmpccmpccmpdnomfracliga lnumlocllocllocl"locl(locl.locl4numr:onum@ordnFpnumLrtlmRsmcpXsubs^supsdtnumj "#     $!%L`~llN > > ` $ J J b * j *D   5 `N  & ) , 0 3 7 Q Q9     ! " # N P$ n n' s u( y z+  - . / 0$')( 9012345678LM%g|K^0:DNj|",6@JT    LD   F 3     LE   G 4 :BJRZbjrz                            :BJRZbjrz - / 1 0 . 8 ; : 9 C E G F D = ? A @ > 3 5 7 6 4 , B < 2:BJRZbjrz e g i h f q s u t r | ~  } w y { z x k m o n l d p v j:BJRZbjrz I K M L J U W Y X V _ a c b ` Z \ ] [ O Q S R P H T ^ N:BJRZbjrz           ' ) + * ( ! # % $ "       & $ &  L L L L$(,28DHLRX#% n&0:DNX        $.8BLV`jt~            $.8BLV`jt~            {  }~34HI#$ ]^ >(>JJyOy/y n o p q r s2 0 1 2 3 4 5 6 7 8 9   KNOPQSVW$ :{tu'(;)*<   Q   "B*    $ $,$D2Rl|l|$2DR .        .      .           :     M          A B C D E F G H I J K L "$%&'()*+,-./0123456789:;<=>@^`cq  !#%')+-/13579:<>BDFH] 8     M          A B C D E F G H I J K L ">@DEFGHIJKLMNOPQRSTUVWXYZ[\]^`cq   "$&(*,.02468;=?CEGI^ B    6 "(ILIOILOI %XKXJ^2B  @)GOOG--%  J ~01ac67Y] AEbw~  OP\_'/=?EMWY[]} d q  !_!!,m,-.Ds}!.Ze/ 12bd78Z^ BFcz PQ]`(>@ HPY[]_ f t !!!,`,n-.@t~".0[  HJHP ~GtI=EvZ;9753210/-,+)(&%+Mc5ee`Y`baa^58<.>D<RfjHhTHI#$&'#$JKLMNOPQR  n u z s t y  y ~ z  { |<=>L R S T U Vpqrstuvw  <  =  >  ? ? = > w o p q r { v x i | } j k l m ~    AB+,!LMNOPQ*{|$%&' ()*   ^  R +,-./0 1 ? i 2 A @ : S = > b c K } k ; < ] m B x V N y Q M P E 3 456789: '(;)*<  BCDEFGH R R   T  x  UV - . /WX ' ( ) * + , !" % hijklmn"/-. o n Z s W p g f H [ t h Y r J a O j T F I Go U ` | X q z { \ u _ w L l v e d C D ~  I  J  K  L  M  N  O  P  Q R ! S " T # U $ V % W & X ' Y ( Z ) [ * \ + ] , ^ - _ t W f g h u . ` / a 0 b 1 c 2 d 3 e 4 f 5 g 6 h 7 i 8 j 9 k : l ; m v w b c:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ [\]^_` abcdefghijklmno{|}~p qr  s t u v w xy z  6 7 8 9 : ; < d e^*@'eU]M+3!%!!^563d/@,~oK_xL  +73"54632M[kK5$'$6 14m$@!]oL +3#3#`K#`KG@Df   eoK  p L +37#737#73733733#3##7#37#a;~0D</3232>54&#""&54>32'2>54&#"7< +B.9< -AN '&7< ,A.:< ,B&'&JGNRG+GBOUJ.6V'=B:)%$8A<PJFORF+GAOUK.B'=C9)%$8A<P !-8{@21JKPX@$_wK_xK_xL@!_wK]pK_xLY@/..8/8*( !! +"&5467&&5466326673#'>54&#"267'Qgb\-V=IV5Y4!.XF,ch7/kC.(&175P&%?(@ [WPh, K*3Q0MB2H8L(>e*N&2'4##,:3;Y+,=,3<m@]oL +3m#`K(bo@oL +&&5473p"&vQGn>=SzNtW>b@oL +>54&'3MGm>D"%wNtV>>Syg,3@ H Gt +''7'777't6{=W;W,å=5NY RFoS &@#Ue]M +3##5#5353UHHGGt@t +6673+6`H#;4 5~74@U]M +773QQy @_xL   +"54632H4%($4!23@oKpL +#3.Zt69#-@*_wK_xL##  +"&54>32'2>54&#"X[+FcA\X-Fb5(?/ /0&@1"0 ~u;j@woHe:K4Ypy:JO3Wlu6QW !@ JoKpL  +3667'73_   X)M.a 7B6,@) J_wK]pL'%( +377>54&#"'6632!BT(73,J#*)d@[a,dRFK@I#D/.1 >(XFLj P@8e? .@+JfoKpL  +!7!733#%37>7# #ecno#$P&N%QH $" % D@AJg]oK_xL    +"'5326654&#"'!#6632aB$S,FV'H>4 %aI<*YiAo 'U0O.=E PPeX-[L.?"0G@D  Jg_wK_xL '%0 0  +"&546676632&&#"36632'26654&#"Th D5+vM6&)_#T4JX=^71D$45$<*0 kpE4,0 L&-^V2cQ1J8Z38= .($?(Q2@]oKpL +3!7!Qu |NF|-(65@20J_wK_xL*))6*6$"  +"&&5467&&5466326654&#"26654&'EY+dY&/8a;AQ$/L,4=3lDG6.5A,2@34-I*B 1Q1Mh"F7;T--H)5I3 RA5]9C5+2=5-:%; 0G(?/4;/ .G@D Jg_wK_xL! )' .!.  +"&'53267##"&546632267654&#"?:#dwO0SY7>54&#"'6632"54632/$ 7"0.&I! )b/O^IF'+E4%(%+@6.8&,0CNOCd4(,#4!2356@N@" E =>JKPX@( g c_oK _r L@&  g g c_oLY@BAIGANBN;931*(  @@ +"&&54>32#"&'##"&54663232>54&&#"32672677&&#"eR@zlj?7a=8-D28AEsD0@/2#/jYVd66l,4p<0?! /K, [Pj[MSOYP6#%4HDQ{D '*EU+BlA=mSAUC|6\9,(,@) JfoKpL  +#3#'#3'&&'#9^XXt*6.&\#&W,)M#9@6Je]oK]pL#!! +332#326654&##32654&##)cqXI1BG|O4u,J,JpNVI[ H32&&#"3267Fw=]|NdJ$C,KmH#]R)O&'O @v]6(JCoC[i N)| '@$]oK]pL ! +332#'326654&##)aGLhJiWUʌ|oM^dna)* /@,e]oK]pL  +3!!3!!)j/7ONO)* )@&e]oKpL  +3!!!!)j5@OOH!>@;  Je_wK_xL !! +"&54>32&&#"32677#73W7gVAb,"&U3RL'R@&?.L-d bGNbo7Y4 N* '@$foKpL  +33!3#!*Y@:@XXFF.6M} "@ JoKpL  +#77'73 UzL VzM 1@221dB(@%JcoL  +"'732673P2$,D Ym M7A5eX) %@" JoKpL  +3373#)YJ>lʩbS<BpZ=)@oK^pL +33!)YP(L'@$ JoKpL +3333#667###(~E=ZZ GL [G6Aj rCK#S($@! JoKpL +3336673##(_ TU_ R$o26PDO$|H-@*_wK_xL  +"&54>32'2>54&#"Q1^Ty-YT<^B#ZN<`D$\ \I]}HO`qCDLgSybRY;5HV$8@5J_wK_xL$$&# +'"#"&54>32'2>54&#"l1^Ty:pO=_A"ZN<`D$\\IgNNd[ECLʵMr'sWE=0*7@4J_wK_xL** +"&'5326654&'.546632&&#"4O#S6-L.5A&;!@k@8U&"N)>N."+A% X:.(9%2@/B]0L@:("2B1knZM!@]oKpL +3#7!#ć{OOO$@!oK`xL +"&5467332673,pm\Y]EDYVcYdEt g_1L28@\Y)Nr=\ !@JoKpL  +3336673MX-&c<)T$$O/6k!'@$JoKpL!! +333667336673#&&5#X([ _[ M)X('e,^/f%&Z,6!K?17a &@# JoKpL  +#33#4#Zab^fzP5\Y"@JoKpL +333:tYUe7 f?= %@"]oK]pL  +#7!7!!  COF4PGPbr"@a]oL +3#3 nnhH(Hl&@oKpL +33jPj6b2"@a]oL +73#73HnnHH& 'dD@Jt +D3#&2N <gb@&dD@U]M +D7!\DD^6 &dD@ Jt  +D.'53+& ^ '^68 %M" 0&"&g JKPX@_zK_xL@!rK_zKpK_xLY@ &&  +"&54>32373#7#'26676654&#"AP'F`:5B CrF #[$H94,'B2, ]ZKg<8%Sc,AI6[:2/<1Ul;66'JKPX@qK_zK`xLK)PX@!qK_zKpK`xL@!_zKpK`xLYY@!'' +"&'##336632'2>54#" 6C!BX& $U8BQ$D`D(B/U!E:#2 7%R"B*,A\ZIi=I3Vl8l1Ul;0<0"7@4  J_zK_xL  +"&54>32&&#"3267Yn'Ih@#E7 9V0>8#?E ddHb9 I ML=CJ0U& JKPX@qK_zK_xLK)PX@!qK_zKpK_xL@!_zKpK_xLYY@!&&  +"&54>32366773#7#'2>54&#"AP$Ea=6A %WH $V!D:#/5(A0 \[Ii<7%">$c-@I1Tl<0<3Vl8l0"'>@;Jg_zK_xL&$  +"&54>32##3267326654&#"]e%Fc>LMAn;>&D(&I#%0Y m]@}g>B;5U3 =GK91)!Y$w@JKPX@!_qK]rK_tL@g]rK_tLY@!  $$ +"&'53267#?>32&&#"3#0 $( n]d 0F14"'. s spLJ;7 %!0EM C ,>3CS^'"$3@JKPX@"_zK_xK_tL@&rK_zK_xK_tLY@&%.,%3&3! $$ +"&'532677>1##"&54>323732>54&#"1SY+>O  #V8AQ$Ea=6A!Cz{;"D9"-5(B0Q32#654&#"X+4C+>IFXG !%"OC1A 2"ED(P-!%/jX MK-PX@_wKrKpL@grKpLY@       +"&546323.$rXrq&-#  e@  JK-PX@_wKrK_tL@grK_tLY@      +"&54632"'53273-$% =WIq&-# Jb]KX G JK)PX@qKrKpL@rKpLY@  +3333#'YAhaqH(PW90K)PX@ qKpL@ pLY@  +33X3"0V JKPX@_rKpL@rK_zKpLY@00%&&% +333>323>32#6654&#"#654&#"rH 1@)7<7D)>DEYH!#!H?3XG # J@1c2"@77#D@*P!$0fPP-!%/jX%LJKPX@_rKpL@rK_zKpLY@ %$ +3>32#6654#"sN 4C,@JG^HD"MC2c3"G@0T@/hX0-@*_zK_xL  +"&54>32'26654&&#"]k"CeB^j"Cd>/O/0(7Q,=o_>zd=o`=zd=IC~X9%K}K>D!&hJKPX@_rK_xKtL@!rK_zK_xKtLY@ &&&$ +336632#"&'#2>54#"H #X8AP$Da<6B1(B/U!E:#2d,A\[Ji<7%9/3Vl8l1Tl<0<0&"'gJKPX@_zK_xKtL@!rK_zK_xKtLY@" ''&( +7>1##"&54>323732>54&#"*0 #V8AQ$Eb=5A!B!D9#.4(B05),A\[Ii=8%S/2Ul:,@3Vl8l"cKPX@ J H@  JYKPX@_rKpL@rK_zKpLY@ $% +333>32&#"rH .:& 2N4 4c2 OBf5"&7@4J_zK_xL&& +"&'532654&'&&54632&&#"1IP*;>&49:cS1S@,,2#5"6q Q.&' D3DTF ) '(5%QT,lf@ JK PX@n]rK_xL@]rK_xLY@  +"&547#?33#32676J =SZ>4>" 2 4A '#(&ozC ! C 7&PJKPX@rK`xL@rKpK`xLY@  +"&546733266773#7#=IFYH &"NE1WrH 3D DA(G+ %0jXc2"0!@JrKpL +333>73n>X  ^F=@@'9"'@$JrKpL"" +333>7336673#&65#OV _ (u]j 7Y99/%a&&g7"(J/%?3 &@# JrKpL  +#373#'%tYSf{Y[0@- JrK_tL  +"&'53267733>73 (:#QX# ^)XG20> !LE@D&KK %@"]rK]pL  +#7#7!3 ME <BGqB b,7@4Jgc_oL+* ,, +"&546776654#726776633"3GJf:F !SS,3 # ::'$)"59!i FI*3O?I/3B  6' "f  IL0K)PX@ qKtL@ tLY@  +3HbY-1@.Jgc_oL-,&%$# +526776675&&546776654˜3"#%07 # 9;'#.)HKf:E ! .J8I/3A  5'!g  I59!j FI*38>F<dD@1JHGWg_O$$$" +D&&#"56323267#"&!$/>0H:.$/>1G:? "N5  "M6 J" /@,~_zL       +"5463234%'#܆:U4!24k  e@ JK1PX@g]oKpL@gW_OY@ %& +7&&5466773&&#"3267=I.>!?/>55?0?>2FF21DD>>/>49/?0??/?65>2D12EE21D<h>@; Jf  eoK  p L +37#737#73333#3#mcVT`oCMCwfCMCLLK)PX@]qK]tL@e]tLY@ +33HHH{}}2>[@9-JKPX@_qK_pL@g_pLY@!22 +"&'532654&'&&5467&&54632&&#"6654&'2IP)?C+68BD. a\/P!@"0+55E?1 k33=:3 K+(%A89G0#:S? #?63O,"FY:&- 7("-w 3dD@(W_O      +D"&54632#"&54632 w""1&?edD@Z0<1=Jgg g W _O(':842/-'?(?&&  +D".54>32'2>54.#"7"&546632&#"3267Pc66cPLe96cP@pV0.SqDZP.SrScb.ZAA:2+;A9B92 6cPPc66cPPc65.UrEArV1Q[ArV1Z{eAe9=UIMR @ R"K&PX@ J@ JYK%PX@c_LK&PX@gW_O@+~~gW_OYY@"" +"&546632373#7#'2>54&#"%3%G5$-E/0) /979hB,8$5#5;"2N(" *6  0+7'57'57p+Ri/L6 +&F%@"U]M +#5!5GqG41&4=idD@^/J ~g  ge W _ O''=;75'4'43210*(&&  +D".54>32'2>54.#"'32#'#532654&##Pc66cPLe96cP@pV0.SqDZP.SrERL0tVd>2',(,1 6cPPc66cPPc65.UrEArV1Q[ArV1_A@/7 ­' # e;&dD@U]M +D7!eCC7u 9dD@.gW_O      +D"&54632'2654&#"HWUJFYYE0-/.1..UDDVVDDU;4*,44,*4FV 1@.ee]pL   +3##5#53535!UHHGGGGGU2@/  JgU]M$' +776654&#"'66323G <8/0C()36.h 7n4D''//!8<&W=QV'M@J!JggW_O '' +"&'532654##732654'6632!;>21T. ..8@-D*<<60"&S A,%@7&#;0:+-7 1":Ns^G &dD@Jt  +D5>73s*&d7>^ 46 99, _MYJK)PX@~]qL@~U]MY@ &" +#"&&54663!##s)>\37cB:f .l[_n.)Q=+ +3+i:dD@/ JW`P  +D"'53254&'73( R%08)K45X9!%-=`K @ Jt  +7667'735 4>[? "/\TVs PK%PX@c_L@gW_OY@  +"&546632'26654&#"4F%I7?9"G5!-##.$AB4_=H84`?4-H')%+F(() 6  0+7'7'7'7'7+R=p/L=i6+%V'F'{'/a3+a3+X'H'{tla3+a3+7'u~''l`~3+`3+?d!" 3+&$C3+&&$v3+&$Jp3+I&$Qm3+&$j-3+&$O3+`?@<ee ]oK] pL +#!!!!!!7#3#:06/֋G)ONO.MHx&&z)*&(C3+)*&(v3+)*&(Jj3+)*&(j'3+}&,C3+&,vh3+&,J3+&,j3+!| 7@4e]oK]pL ! +3#7332#'326654&##3#)AIIEaGLhJiWU4;MB|oM^dnaM(&1Q3+H&2C3+H&2v/3+H&2J3+H&2Q3+H&2j}3+T> 0+''7'72244>33349$0=@: .-J H_wK_xL&%%0&0%+) +'7&&54>327#"'7&&#"2>54&'r9B1^T]C?:H-YW`A a7#<`E$<_B#9).R#\5\I4O.X[4]}H.;hN==S_0&&DCg0&&Dv0&&DJM0&&DQJ0&&Dj 0&/&DOt0"0;MKPX@  -*#$J@  -*#$JYKPX@%h  _zK _ xL@-hrK  _zKpK _ xLY@#=<GE323736632##3267#"&'#7#326654&#"26676654&#":G'E]60:!8Q65P@n@; J Hg_xL$#-+#2$2"" +"&54663276&''7&&'77'2>54&&#"Yg?rN:D+r#/1~ s#-BnL2D)3*geW_O      +"&546325!"&546321!! !!  "" GG "" <"-=@: +*J H_zK_xL$##-$-%+( +'7&54>327#"'7&&#"26654&'R56""CdBI1158!BdBE2 "7S-w/Q0 '&)C2N>zd= =(>D">|g?x2 KzF*cBzT 7&&XCm7&&Xv7&&XJS7&&Xj&\vw*lJK)PX@!qK_zK`xKtL@!_zK`xKtLY@#!**&' +336632#"&'#2>54#"X& $V8BP$C`=6D1(B/U#J< 1+J,A]ZIi=7%:/3Vl8l7_<.-0<&\jW&$L3+0&&DLi'&$M}3+0&&DMZ'&$Pm0'&"&DP1Hx&&v#3+0&FvHx&&J3+0&FJ*Hx&&N"3+0&FNH&&K3+0&FK.)|&'K3+0&G%d!|0.  JKPX@'fqK _zK _ xLK)PX@+fqK _zKpK _ xL@+f _zKpK _ xLYY@! )' .!.  +"&54>3236677#73733##7#'2>54&#"AP$C`=7C WIIH $V!D:#14(B/ \[Ii=7$132##32673267#"&5467326654&#"]e%Fc>LMAn;>&D()C!  &34(r#%0Y m]@}g>B;5U3 =GK!G&!7')+=91)!Y)5&(Kn3+0&HK6H&*J3+'&JJIH&*M3+'&JMVH&*N63+'&JNH#&* '&J&9*&+J3+ &KJ3+);@8  f  eoK pL +3#7373!733##!!7!)n[[Y:Z[]nZFFW: HxxxxHMn %h JK)PX@!fqK_rK pL@!f_rK pLY@%%%' +3#73733#3>32#654&#"KIX4C+?HDXF "%"OE.\BZZB9]3"GD(G+"'0kW&,Q3+&'QW&,L3+Z&'L&,M3+i&'M'}&,P9'&LP }&,Ng3+B^&,-D&LMdB&-J3+[&)J)#&. B# &N ' %@" JrKpL +3333#'qYhaqH(%D9)&/vc3+&OvN3+)#&/ (#&O )&/%o а3+&O%)&/N 3+_&ONv Ű3+  *@' JoK^pL  +37'737!(54dQZFp/:9?8\P% =@ JK)PX@ qKpL@ pLY@  +3'737=7fTXF:hJ!7>"7>(&1v-3+&Qv(#&1 s#%&Q 5(&1K3+ &QKY)\&QN(C<@9 J IcoKpL +"&'73267##336673^+ $+D  RT_ PVnM2<P&o7|*q5w6eX "'m@JKPX@_rKpK_tL@ rK_zKpK_tLY@'' +"&'53276654&#"#33>32 <V"%"ND1XrH 3C+>IVIJc!$/jXc2"D@+lJXHW&2L3+0&RLVH&2M3+0&RMGH&2R3+0L&RRfH$IKPX@#e _wK _ xLKPX@+e _wK]oK _ xL@3e _wK]oK]pK _ xLYY@$$  +"&54>32!!!!!!'27&#"Q1^TH+_06'"v'5<`D$\ \I ONOO - ;gN_i0D"(3AT@Q & Jg _zK _ xL54<:4A5A20+)$" (( +"&54>326632##3267#"&'326654&#"26654&#"Yj"DgDIP$d;AR?n<=%D((M,?Y` 8^:P)687Q,;o_>zd==.4:=@5U3 =GM64/971+!UJP8DK{HBE)9&5v3+&Uv)#9&5 D#"&U )A&5Kz3+&UK&6v3+&Vvm&6JJ3+&VJ&6z"&Vz&6KN3+&VKW#M&7 '#l&W ZM&7KV3+,&W%#SM/@,e]oKpL +3#737#7!#3#E33EGIOOIl%@ " # JK PX@'n e]rK _ xL@& e]rK _ xLY@  %% +"&54677#737#?33#3#32676JCCSZ>4v w" 2 4A$hBy(&ozCyBi ! C O&8Q3+7,&XQPOW&8L3+7&&XLoO&8M3+7&&XM`O&8O3+7&/&XOzO&8R3+7e&XRO'&8P7'&&XP1k&:J3+9&ZJ\Y&<JV3+&\J\Y&<j3+=&=v3+&]v=&=N3+&]N~=&=Ke3+&]KX@JKPX@_qK_tL@g_tLY@ +"&'53267>32&&#"@ - /G16"(. LJ8:EM C ,>vS^!#I@FJe_wK_tL  ## +"&'53267#?6632&&#"3#? . b\d YJ5!(. s tcKJ8:$!EeM C ,>HB)S^- (2\@Y J g   ee pL /.*)$"((    +56673&54632#'#2654&#"3'&&'#Q=n 8?8U@,0>LWuJ  #= E -.w+1871-&J I'0& "7I@ 4. JKPX@6 g g _zK _ xL@> g grK _zK pK _xLY@.98$# CA8I9I3210,*#7$7""    +56673"&54632'2654&#""&54>32373#7#'26676654&#"C>n 8@1<@-.>>. AP'F`:5B CrF #[$H94,'B2,> D --712762271|]ZKg<8%Sc,AI6[:2/<1Ul;66`&vb3+0&v:9&v63+&v#&6 #"&V s^)dD@ Jt +D5>73#&&'s85U27)=^ 59'O 0/^)dD@ Jt +D&&'53667315(>986^&P 0/ 59p^&dD@U]M +D7!p ^GG^2dD@'HW_O  +D"&54473326736F4$,..7T^97  !H>q 'dD@W_O   +D"&54632.$q&-#^b/ 9dD@.gW_O      +D"&54632'2654&#"0<@,0=>/ ^721761372'z8dD@-JW_O  +D"&546733267.&3;-=+!  ')/>&.!7p^4dD@)Wg_O"""" +D663232673#"&&#"p:+2* 5:+/+ ^>B>Bs^ ;dD@0 JU]M    +D5>73#5>73)'Z6>*'Z 7?^ 46 99 46 99-^& &dD@Jt +D5>73-  ^ *4^CDCIwF !dD@ JK PX@$n~W`P@#~W`PY@ !!    +D56673"&546323"&54632,bH&z   T$ S#/""&$S 3+Q=+ +3+0&(_S 3+&&+MS 3+&',S 3+&&27S 3+'<S 3+0'SE 3+3yF&T$)M%)*;K1PX@]5K6L@]5LY@ +3!!)jO)*(==*+H#>@;e_=K_6L  # #"!  +"&54>32'2>54&#"7!R2^Ty.YT=_B"ZO=`C$\ ]H]}GO=jJ_j;hM^kMM},). :JK1PX@ 5K6L@ 5LY@ +#3#&&'9_YX5*7)Y%&V0A(L0(1* eK1PX@ e]5K]6L@ea]5LY@   +7!7!7!]:B{OONNOOH2)>K1PX@]5K6L@]5LY@ +3!#!)ZƆ6{)733 KJK1PX@]5K]6L@a]5LY@ +#77!!!`I(GOOZM7\Y<H$tJK1PX@#hW5K ] 6L@#hW ] 6LY@$# +7&&5466773'>54&'iuDx[gv#N^4RQd/MOe0IJ nrgSUXXqe7m[:n?h=GQBg>APa;`"NK1PX@h5K6L@h5LY@""$!! +!7#"&&547733333266773##.Q_( )],ZZYDV0 2]3Fd.9Z2-7*}\.S7HrB&,j3+\Y&<j3+0;&&{S&&S &&S3N&&Sm@F&T70;"%7m@ " JKPX@_@K_6L@8K_@K_6LY@'&1/&7'7! %% +"&54>3236673327#"5#'26676654&#"@Q&E_99@H  #$Z"R$E73,'B0, \[Lg;6&1JZ, $@c):I5Z:4/<1Tl;766-L@I +Jg_?K_6K:L'%!--*$ +>32#"&'26654&##732654&#"4aPU`4I8kM:F=6H#@N #XN1309 [G?h?YP& OKEm?#.0K*{=%^)+a=}D#, .9@6(J_?K_6L"!!.".  +"&&5467&&54632&&#"'26654&'BX-s"4aS:X#G+1..(4H;uM3J'*:a:< 4W4qH2CW%? .%&2 *iOI}MI6[8;I9[@=E!+J@G()Jg_@K_6L&$ +++"&54675&&546632&&#"33#"3267YYV=,:[13K @%2F.1=9)D)08.S"\ N32.{"%"ND1XrH 3C+>Iz@!$/jXc2"D@/0&9@6e_?K_6L! && +"&5467>32!6654#"267!PT MqOIY Np}UC[DX"+ fh,b.xZbu%b8q["E)I!JC3+@(J8K`6L+"&5467332674GKXL  3 4A%d  C  %KPX@" #J@" #JYKPX@_?K_6LK1PX@_?K6K_6L@~_?K_6LYY@ %%+"&''&&5#'&&#"'66323267*-]0 & &L= 9 ! -: P&"? F,6FLM B0:JK1PX@ 8K6L@ ]8LY@ +336673rBX#_kWItW(L%YnL(65{K&PX@  J@ JIYK&PX@g]7L@ pg]7LY@55(&%#+6654&'&&54675&&54667##7!#"33#",.//DBje.;+I-, /+WH,DL.&;lD/;7#4'9 QDQv =3)B. JD';'16D%M<.4 2$.E0R&vKPX@ J@ JYKPX@]8K_6LK1PX@]8K6K_6L@ ~]8K_6LYY@ +"&547###77!#3267.1 AdYdm`hB $ 0(/:3$'K D !#<@9!J_@K_6K:L##&$+>32#"&'2>54&#"u 4`N\c>aC1E ,B,38BC'<+;jAmf9xg@"'E*/1Q_/?J`P 00"&)@&J_@L&&%.+6654&'.54>32&&#"-.!4"B+#GiF%B8"/G/1%5 2"(@.RAguZWJL@/i\9IFrC/JDqCNG&5@2J]8K_6L +"&5467#77!#32676J8_;9" 3 4A2$'K( C @$@!8K`6L+"&5467332>53Y` 7X7 8/.A+ X #@g ]S-**-941Rfkb$GqC0w!)7@4 J_@K6K:L'%+7.54676632>54&#"14R0SQ8DA2APNHI"JxW0A'E2@/J7K8K6K:L+7&&5467733>532c[5X53AV^e&ZJf1 eO0-5EK z̆tT0.:@7,J~8K`6L*(  ..+"&5467332677332>54'3#"&'EFB=\5M((4 V=)9$ W!8S9.: E dQYXQa>0D?$ O>an0WE$Q$+iiW50'+,3\&jo@&j 0&&RS@&&S0&&SY)*&(j'3+ZZ#KPX@ J@ JYKPX@ e]#K_+L@$e]#K$K_+LY@## +"&'5326776654####7!#32& $2/ dLY*}[ZZ N 74$"Q{OONI1%ZT)*&`v3+Hx#F@C   !Je_*K_+L ##+"&&54>32&&#"!!3267FTq9=]|NdJ$C,HlH,[T)O&(P D{R@v]6(J?j?O$]f N6},&,j3+dB-E&.KPX@ JKPX@ JI@ JIYYKPX@ g]#K_+LKPX@*g]#K]$K_+L@(g]#K]$K_+LYY@.,)'&& +"&'532667>7!32###%3254&## -)%&%M?3T^'HS "!4LHB;C NA<.x<0P0Rh2z1uyo+?\1W;0)O3@0h#K] $L&! +33!332##!%3254&##)Y@@Y@5S^'HRFFyHB;C-0Q0Rh2NL;/ZZ-@*e]#K$L$!+3#7!#32#7654##r*ZU*Z- eL{OONL*'"Q)&v3+&$*3+)D )@&#K]$L +7#3!3#)͗Z;Y'{6$**1@.e]#K]$L !+3!!32#'3254&##*i/FS_'HSMZB;VO0P1Qh2L;0)M%)*`En3@0Q]#K]$L +3>7!3#7!!#T83-]UC#[9U'K(}vx=KQ >I )8)*(j,@) J#K$L+#333##2K`HVH3jcKVKxRTTdd +J@G$Je_*K_+L +++"'5326654&##7326654&#"'6632rO$k7BS(RKf_1Y:<2-W)$3kEVbcU8I@710 @""WHLg NF7#)UThTT:wLEI6"KC)&$p3+) %@" J#K$L +333#)YJQlfJ[bqKPX@ J@ JIYKPX@]#K_+L@]#K$K_+LY@+"&'532667>7!## -)%&%cZ "!4L NA<.x<6z1uyo+?\1(L0*+H2)l)73Hx&ZM70@- J#K`+L +"&'5326736673S.*6K+ZV  e+MS W ?F#C!9MES$Hqa;(E )@&R#K^$L +7!3!33(#Z,ZS:zw+@(Jh#K$L&#+!#"&54677332673>"d;KQ5Y4(02a(HZ#GB$! %'\6( %@"#K^$L +333333(ZZYzz6(E-@*R#K^$L +7!333333'ZZYS9zzZ=1@.e]#K]$L !+3#7332#'3254&##s@AT^'HSGTB;Q{O0P1Qh2L;0) 6@3f#K]$L    ! +3332#!3%3254&##)Y@@S_'HSUY SB;P0P1Qh26L;0) +@(f#K]$L !+3332#'3254&##)Y@AT^'HSGTB;Q0P1Qh2L;0 , F@CJe_*K_+L   +"'53267!7!4654&#"'6632_K)O7t'ST/L"%`8t(X 'VyO Sj JYN))KPX@!f_#K _+LKPX@%f#K_*K _+L@)f#K_*K$K _+LYY@" ))  +"&&547##33>32'2>54&#"a[q3FYY@\Yp,UP=\<VJ9[A"!H L~K!!ZR]~GO@mFbf;gN7[6B8@5Je]#K$L'+#&&546633##3#"'(6JzIZ?lG9W2P/BFJ@Sd-6(t ?66C0&"D1C!35@2JHg_+L#"+)"3#3!!+"&54>7>76632'2>54&#"\c%;U9ERJ XY@[\;LK=a<,<%**%C52 so-u{pR H x_ :_U=u^8I1MU#<8$16CS0!*?@< Je_,K_+L! '% *!*+"&546632326654&#"2654&##neCZV^L;+9)`m,M/*1G`cB=>C~E xe^YEC;B  34%M39+$*X8&&&ME!)7@4&'J_,K_+L$" ))+"&&546676654&#"'66323267.O06R+3236454&#"'6632'2667&&#"WZ ?]=;@BD:N$nf/Hd72L4 <.-M.- kX9ta:3"{v A ={c:JLzF0AFuH4F0"Hi"JKPX@()7 8J@()7 8 JYKPX@&   f_,K _ +L@.   f%K_,K $K _ +LY@#FEDCBA<:53-+&$  JJ+"&'732>54&#"566323733>32&&#"3267#"&5447##7#,! +C.,4$VVR1Q2O &=W9"  +B,*4 $VVS2P3O )?W H2Qa/:LHma (UH-G2Qa.:MIlb  -XH*"*J@G$Je_,K_+L **+"&'532654&##7326654&#"'6632+SS5?K)32#&&#"  .)*5N:"@pYe *2*5IL*\KCbA '^USr;'@$ J%K$L+3333#667###qr8{pPA E=>W3%M ;R%; '@$f%K$L +33373#7#rW.0XqY230R%Q!S0"F3"P\0r$B@? J$I_,K`+K'L+7&&5466773>54&'1[bEd.Q.YfK_1VFY*?6M0@3 vbT[ pdZY1Ko=GP.IV+IK[7F&(4@1'J`+K]%L((%(+7&&5##"&5467332667733267(4C+>IFYH &"NE1WP 81/1DA(G+ %0jX M+@(Jh%K$L&'+!7667##"&5467733266773F W8DEY'"#I@ Xr#B(4FC0{#(&_X87N0],$JKPX@%K`+L@%K$K`+LY@*(#"! 00 +"&5467332667733266773#7##"&'#>DEYG!#!H?4XH #!J@1XrH 1@)7<7D D@*F!$0fP-!%/jXc2"@77#7FN<;@8; J R%K`+L<<%%&&( +7&&'##"&'##"&54673326677332667733267'1A(7<6E)>DEYG!# I?4XH #!J@1XP% 91/2 @77#D@)F $/fQ+"$/jX &)#?@< Jg]%K_+L## +"&54677#7!6632'2654&#"FP^027SR-]B=8331* KO4KKC1S3H@(+"&)1>%p JKPX@g%K `+L@!g%K$K `+LY@%%  +"&54673663273%2654&#"P^?Y27QR-\qXr=8411+ KO4*ID2T2 >?),!%+/>!8@5 Jg%K`+L!! +"&546736632'2654&#"U^?Y3< [N1bAB;.98) LO3*P>1S3H>*$)&&4 !F@CJe_,K_+L +"&'53267#7354&#"'6632(D<#G[=: ;J0ebAi O \RJLE C seAy`9&KPX@!f_%K _+LK"PX@%f_%K$K _+L@)f%K_,K$K _+LYY@ &&  +"&5465##336632'26654&#"^a{3XrW.w S_`!A`9-I+51*?*4tZ sst[A{b:IF~U8D1Pa1:H&K(PX@ J@ JIYK(PX@ g]%K_+L@$g]%K$K_+LY@" && +"&'53267667&&54633#7#"37#"  !*"0-jjqW-A/,%3X%t5::F#. 2 B+Ia..(8$3,-#0&Hj/@"JKPX@+n f _ %K$K_ 'LK1PX@* f _ %K$K_ 'L@( f  g$K_ 'LYY@'% // +"'53276654&#"##73733#36632#<R"%"ND-XJIX  WA?HRI K c !%/jX\BZZB2#?&)CFD(KX&v0"!F@C  Je_,K_+L !!+"&54>32&&#"3#3267Yn!DjI#E7 8^:=';E dd=}j@ I OQJ  6JJ"VL\&'jo M.=@$%JKPX@!g_%K _+L@,g_%K_+K _+LY@0/64/=0=)'"  .. +"&54677&&#"#"&'532667>326632'2654&#"P^/*(5I:  .)'0H7>/7SR-]B=8332* KO4&_USr;L*\K?aC# KC1S3H@(+"&)1(rJKPX@h%K _ +L@"h%K$K _ +LY@!((  +"&5467##33736632'2654&#" P_2YsW/0X28RS-^B>7322+ KO4KC1S3H@(+"%)0 &v&\$7& tJKPX@!~%K`+K'L@%~%K$K`+K'LY@    +"&546733266773#7#773=IFYH &"NE1WrH 3DR(I- DA(G+ %0jXc2")F\%@"]#K$L+3!73!)U1ʒFKPX@#K]%K$L@]%K$LY@ +3373#r"Y0c*k&:C3+9&ZCk&:vr3+9&Zvk&:j3+9&ZjN\Y&<Cp3+&\C"5@U]M +77!QQ5@U]M +77!sQQ5"P7dD@,eU]M +D7!7!KYZ@@@@<@oL +'6673?H#C6 5~7;4<@oL +6673<8`H$;4 5~7t@t +667316`H#;4 5~7p@]oL +&&'73U65 ;9<$@!]oL    +'6673!'6673J#B7H#C6 5~7;4 5~7;4<$@!]oL    +6673#66736aJ$8`H$;4 5~7;4 5~7Mt*@'U]M    +6673#66737`I"6`H#;4 5~7;4 5~7i >@ JK)PX@ qKpL@ pLY@  +37737'zhFPXX I@ JK)PX@ qKpL@ pLY@  +377777737'7'}A2 iA4 WXXWa? @W_O   +7"&54632/@@/.AA:BC99CB:y&'P:&8J[lKPX@2    h g_wK  _pL@:    h goK_wKpK  _xLY@;]\LK:9('fd\l]lUSK[L[CA9J:J1/'8(8 &&  +"&54>3232>54&#""&54>32!"&54>32%2>54&#"!2>54&#"7< +B.9< -AN '&7< ,A.:< ,B7= ,B.9< ,A'&y'&JGNRG+GBOUJ.6V'=B:)%$8A<PJFORF+GAOUK.JFORF+GAOUK.B'=C9)%$8A<P'=C9)%$8A<PD#@oL +3D`D'*60+7'57p+R6+ 60+7'7'75+R=o6+ 'q@oKpL +#3O6EhGQ5)&f 3+G7@4ee]oK pL +37#73!!3#3#MTTnc6 BOOlBB&M@JJe  e_wK ]  p L&&%$! %# +#7667#737#7376632&&#"3#3#! 5<]^a`e]2X"K#7;  +(_J HACMCsR\E<5gCMC6CP 0@ - . JKPX@. f g]oKrK _  pLKPX@1~ f g]oK _  pL@5~ f g]oK pK _ xLYY@+)$#"! 00 %! +332##326654##"&5477#?33#3267ime?f4_323!7>54&&#" %=/ZSZv98_<Cl>$N?Qo97/O%|`Nm>I|NTm'OF(oX7Y3WT]z/F=C"D@A"JgeW_O  +"&&54>32!3267!5&&#"@Tt;.K\.JuDlN-IV"#:T#L41HN~HHhD C|U%<6%>%>&"U'E'{~*g^~3+^3+7"'u}'v*]}3+]3+D('z'(|*\|3+\3+H'8'*Y])}]3+}3+ @ JK1PX@5K^6L@b5LY@ +#73%!&5 \\I s?& $6l5Or`I(P/^*LKPX@nW`P@W`PY@ +"&547332673[UIM'..> T`^C6 #%6FTX 6JK)PX@ qL@ tY@  +5>73 S",X 48 6:^ 6JKPX@ qL@ tY@  +5>73",1 ^ 6: 48KO aJK PX@oU^N@U^NY@  +7#733#'37667 G;= ?l a4;a\/ _MG@DJegW_O  +"'532654&#"'73#6632;,726.! <   7@"MC3*"">i>7%I0qM$@!U]M +#7!q  q;0XU".9@6)JgW_O$##.$.  +"&547&&546326654&#"654&'8GnKD::5.$U2!$%+#%3%92X))#1A:((:0%7I "$")) *#)E +:JV^bfosw}aK PX@C / .JK PX@C / .J@C / .JYYK PX@4 1 p(&"%%&p 5 32e  U W76  g  g=<e:U;98!g#!?$>"&!"e/-+)%''%U/-+)%%'^D0C.B,A*@ '%'NK PX@4 1 p(&"%%&p 5 32e  U76  g  g=<e;:98!g#!?$>"&!"e/-+)%''%U/-+)%%'^D0C.B,A*@ '%'NKPX@4 1 p(&"%%&p 5 32e  U W76  g  g=<e:U;98!g#!?$>"&!"e/-+)%''%U/-+)%%'^D0C.B,A*@ '%'N@4 1  ~(&"%"&%~ 5 32e  U W76  g  g=<e:U;98!g#!?$>"&!"e/-+)%''%U/-+)%%'^D0C.B,A*@ '%'NYYY@~~xxttppcc__LK;;-,!  ~~x}x}|{zytwtwvupspsrqomigcfcfed_b_ba`^\YWRPKVLV;J;I><7631,:-:'% +!+   E+5#53%53!5353#75353!53"&54632%"'5326553%32#%2654&#"%32654##53!53%32654&##53!535353!533!53!53!53_^5`6B>>BB>>p =6T575." ""  +655B$"5`6_5`6^Q^6^6666^6^^66㄄QBCPPCBQ22- ) "',/3--33--3~򄄄5__5555555)d+A@>JHU_O! '% +!+%)+ 354676654&#"66322654&#"6g!++\P*X"(!>!& 8))((6{">1CJW"7'#%%#}&)K<\9EY@VJ~~ g_?K` 6L:::E:EA?4321,*&%  99 +"&54676654&#"7663232>7&&546323#6654&#"SJ    $(( $*-D2"aO[XF@1Hc0,,+[ P9#?#  C -"63 )3Q_Z!\UO`m  C0qo\8SS:,@3\fo@ JK&PX@_5K6LK1PX@5K_=K6L@_=K]5LYY@ %'+33>76632&&#":tYU?:7(   ?Q,6 2o`('GdM4>3I@FJ~]8K ` 6L.-&$33  +"&5467#77!##"&''2677332>54&'!DL82`s7X>1< F#,3 U= 5%,>$ XVFG$'K<9{jA1+,0IK8VI%O0Qe5<CO24(L&0v]3+3&Pva&$C0&"&DC 9dD@.gW_O      +D"&54632'2654&#"I1;;1/AA/622771271HC*c JK)PX@qK_wK_xL@_wK_xLY@$"**  +"&54>326673'2>54&#"Q1^TIl ,%`NG -YT<^B#ZN<`D$\ \I73F5 CV>#]}HO@; J_zK_xL" ''  +"&54>326673'26654&&#"]k"CeBj5/%_MI"Cd>/O/0(7Q,=o_>zd=FL2 BW'4=zd=IC~X9%K}K>DOc TJK)PX@qKoK`xL@oK`xLY@    +"&54673326736673/qo\Y] @IUZcY&5 _rQCDr i_3K49E[[`BD ObNr=7j$] JKPX@rK`xL@rKpK`xLY@ $$ +"&5467332667736673#7#=IFYH &"NE1W (3 _rRXH 3D DD(G+"%0jX=CD Pabc2"N4*@' JW_O%& +76654&#"56632 +/ 3?7/ NX 3(((/ 4)*&(C3+)&C3+0&HCL7&&XCmb%(@%$ J#K$L%%+&&73366766733673#.'"+] ( ]V+^/zP"_zEL)4l0T8w94;@,#(@%" J%K$L##+3.7336677&67336673#&&'% W'j XHr%W&qR Ch_251j1Q32&&#"!!3267YyFYY@>[xLcL%D,HkI,^Q)O'(P #$>pX2(J@i@N#]f N")KPX@& ' J@& ' JYKPX@" f_%K _ +L@* f%K_,K$K _ +LY@$" )) +"&547##33>32&&#"3#3267Yn{3XrW.yEiH$D7 >Z@8&<D bf?j@ I VJJ ;EJ0 0@-Jf#K$L +#3####3'&&'9PxX47ESH9  !7MMI D=$ 0@-Jf%K$L +#3#'##7#3&&'.9o\W&+1R6+r -5.P$-O) 8@5J f#K $L +3333######3'&&')Y@ϰQwX47ERH:^ijF  -7MMMG,8 A#8@5J f%K $L +33373#'##7##7#3'rX/n\W%*2R5*[|31p7!:@7 Jg]#K$L! !# +#7>7'7!#'&&####"7!(h5M>{;; Z &4 IYH%0&h87P/DD0P4=:V5.E:@7 J~]%K$L+#7>7'7!#'&&##"7#6_,B6l  -0U'/6Q606X #:&33(<(1*(2<)$'r@  JKPX@h ]#K $L@%pf ]#K $LY@'&$$! +33!'7!#'&&###"#7667#7!)Y@9y:; Y (5IYH$1&h]h!FDD0P4=;V4-#9!$A@> Ipf ]%K $L$#!! +333'7!#'&&##"#7667#7#rX/l  -0V'.7P4/4W\_3߬33(<(1*(3" <0 TU@4);5.&#"AUJGK(PX@)WgeW]M@.pWgeW^NY@SQPN%%%!-R +&&#"#"&54667>54&##7326654&#"'667&&'536632&&#"32632~1%(W2BH5`@&J<$RKf_1Z9<2-W)$*W2+=#9' $2FcU8I@710 @!= *%78&& LALg NFI^1 @U@5*<6/'$#BUJGK-PX@)~gfU]M@0~~gfU]MY@ SN%%%!.R+&&#"#"&&54>7>54&##7326654&#"'667&&'536632&&#"32632O))E01:!7E$'J0.7I1!@+(,!A'=!%=#8( $,1A=(3BnB7<9+J%+ -&/*("'J ('#C6 +$89$ <-6H  8->F$ % `sEH$9@6e_*K_+L $$ +"&54>32!6654&#"2667!Q1^Ty-YZN>bE?bCb] \I]}H  _i>nkCtI_i0#9@6e_,K_+L## +"&54>32!4654&#"267!]l"CeBcg"De78zd=pW>|g?@  9CX\R  >D\O@ JK(PX@_#K$L@#K_*K$LY@ %,+337667>32&&#"MX- %~ 2<0  -!;)R&#Y& CT(H>C/ ,@)J_%K$L$,+3336677>32&&#"n?Y*L'3( ,O2._ 1;@$&p\&a p3+) &b H&2\06&R\3H28@5("/JgW_O20&$ +"&'&&54>766327>54&'#"&'632?Xc,SuJ $WcKe %=@^3:3 $ ?^478-3=$oVxM !us{ hXR_fYLg&0M18@5+& JgW_O*( +"&'&&54>76632'6326654&'#"'IL8S8"HG/`I"@&8D!" & !<'&6 nU5k]C kNMf~" h-A  DlH4EG"a|@yQ.P/ A _ J~   ~gg _ *K  `+L$#][TRNLEC@?<:31-+#a$a#!""+663233#".#"7654&'&&5432"&54>32&&#"32677332>54&#"'632#"&' G4(DF* ':/. 9  /!nc4LbC)8 +[) 4(-J:(5..$BOY\7Qj@=IO>9?% $!MnD~c9,F5Xkn/O[' $7]pu3IJH+wkEg<)*1o#]|@yM.L/ ? [ J~   ~gg _ ,K  `+L%$YWQOJHCA>=:820-+$]%]#!""+663233#".#"7654&'&&54632"&54>32&#326773326654&#"'6632#"&'J G4)DF* ':0.0 9  !>ZV&D_9D&-%'=*-1#* "X# -6O*&#!:!GICgH+@A=9>8%  "%8oeC{a8C1O\-?I OL2C E iSCj?bv&M3+@,&N]Hx6@3  J_*K_+K'L&$'+7&&54>32&&#"32672n|=]|NdJ$C,KmH#]Q$EyAw]6(JCoD[g0"6@3  J_,K_+K'L%%&+7&&54>32&&#"32671Wj!EjH#E7 9V0>80Fc`?j@ I MN>; 84v 0+'7'77'77''?X"a#Y>Y$b#$P<:*7*75310.,))'%#"  ""(+D6632#&&#"6632#&&#"!6632#&&#"6632#&&#"!6632#&&#"6632#&&#"!6632#&&#"6632#&&#"2205'% 2205'% 2205'% |2205'% 2205'% K2205'% 2205'% 2205'% Y+45*,35*,35*,35*,35*,35*,35*,35*D#,5>GVdD@K ;:10,('$#"75JeU]M???G?GCB+D'6673'&&'7'7667&&'5&&'57&&'7%'6676673 !/ '!40"U'"$P&X&)^$)^$&X&50(y"$P0"U D !-&X&)^$"U'"$P0(!5 !/ D D !$P0"U' !40')^$&X&(D)#JKPX@+n~    h#K$L@*~    h#K$LY@))('&%  +"&5473326737#>7##3333UIM&//= T anvPT:hUThZ}C6 #%6FT@"KCwLEI7F00 KPXJ JYKPX@)n    h%K `$LKPX@(    h%K `$LKPX@/  ~    h%K`$L@3  ~    h%K$K`+LYYY@!00/.-,(&"!  +"&5473326737#7##"&5467332667733aUIM'..> T`sbE 2D+;KFYH F"NE1WbTq^C6 #%6FTb2!BC+G+E0jX2)9@6fg#K] $L! +3#73733#32#'32654&##)sKKYBsc)8\pCDF$NXXNaPryMJP60=({JK PX@%nfg _ +L@$fg _ +LY@" ((  +"&'467#73733#6632'2654&#"V\GIIX:@VR3b?=A34= NM2PCwwCKC4S0H:.(%&Z*8>@;  Jg]#K$L(!+332'##3267'76654&##*tb|$A(7B@;K>& @# #CDLgS=KQ bFJD1;5!.w@! JKPX@_%K_+K'L@!%K_,K_+K'LY@(&..*$+336632'#"&'#27'7>54#"H #X8AP"A.1;46B1 .<.*U!E:#2d,A\[HhW"]7%9/R"SUf5l1Tl<0<$& -@*e]#K$L +3#73!!3#)BGGEf3B;MBPM -@*e]%K$L +37#737!#3#2II2#2BKB*+#v@JK1PX@#g]#K$K_'L@ gc]#K$LY@##+"&'532>54&#"#!!6632(@:"<7510%&$$+7&&5447##7##"&'732>54&#"566323733>32&&#"3267%LLS2P3O )?W8! +C.,4$VVR1Q2O &=W9"  +B,*4& 7k\  -XH*H2Qa/:LHma (UH-G2Qa.:M' &['"&[*E.@+ Ja#K$L+7##333("JZZJQlT9b[F.@+ Jb%K$L+7##3733W'(8XqW6gY8 *7@4 J~]#K$L+3377373#'#7'*YJ^Blfp D6Ja}&rҸ+}X7@4 J~]%K$L+3377373#'#7'pX6:"@}g dX@$#88Px6^7)3@0 Jf#K$L +3#73733#3#)tIHYZZ'QlfJ$NXXN[b` JKPX@nf%K$L@f%K$LY@ +3#73733#3#KJWFni8\CYYC Z +@( J]#K$L +3#733#sJQmfJ{O[b&` +@( J]%K$L +3#7!73#na6gd8K *D0@-fa#K$L +7#!#3!33'VFFYY@:@XT9M.F;0@-fa%K$L +7#7##33733(I23XrW.0XbJ82)L -@*f]#K$L +33!3##!)Y@:@XFF.OM -@*f]%K$L +3337!##7#rW.0aY23K3*%z@JK1PX@$g]#K$K_'L@!gc]#K$LY@%% +"&'532>54&#"#!#!6632T(@;!32&&#"3227&&54>323267'>54#"1(D,v5bP =-HzK[O 7Q6@L$L; "%1>65!)bzC L Tyek#Y2=wa:UaAp'R!eu7bHyH(F0)"5B@2-3JKPX@(g_,K_+K_+L@%gc_,K_+LY@=;0.(& 55 +"&'#"&54>32&&#"3267&&5466323267'6654&#"8.VpBfG2 $2F*=: 'P=5CC?  'h/4%oo;vb;H0O_.@O@(:kCB@G(G~n72H 3H'x&&[0'"&F[ZDM *@'a]#K$L +7##7!#3'VvP8{OOFH"?i@ >)!JKPX@ a_%K$L@  a%K_,K$LY@??+&%%+ +7&&54677654#"#654&#"#33>323>323267'', D!H?3XG # J@1XrH 1@)7<7D)>D) 7(( -E0fPP-!%/jXc2"@77#FD' \Y<0"@J%K'L+7336673F2HX 1^2$a7PR9Y7(.4Y1HZR:#H@  ##\MF"4@1Jha%K$L""&'+7#7667##"&54677332667733i(K W8DEY'"#I@ XbJ8"B(4FC0{#(&_X82vA@>J~~h#K$L+!#7"&54733736673>;! CRR;Y:*/$D#:HZ# GC$# \6L!nJK PX@$~ph%K$L@%~~h%K$LY@!!+!7667##7"&5467737366773F :!>?IY ?>+I Xr&@1 yEC)*Eea9*;+@(Jg#K$L%#+336632#654&#"*Z=$`7PR:Z:)-4Y0IHC"$ K<,8@ )*JK PX@'ph_*K_ +L@(~h_*K_ +LY@75/-'%! ,, +"&&5467&&547333>32##326732>54&#"Sq:5BO  kUej+d~3bN=\10_gN!>1Y E{R02.# $cO[L5V>!ibR(9#--|$;"*5K@H'(J~g_,K_+L42-+%# ** +"&5467&&546733>32##3267326654&#"V\e3@ M /FfAGS=r:>%G&(N8gA( 0Y m]  *3+ !%BnB=@1V6 =GK91*Y32##326732>54&#"P&bf5BO  kUej-g!bN=\1*S;$gN!>1Ym02.# $cO[L6W= ibRL(9#--|$F;".9@ )*JK PX@, p h _,K_+L@-  ~ h _,K_+LY@861/..%%# +7&&5467&&546733>32##3267326654&#"%@D2A M FfAGS=r:>%G&#A%#Q8gA( 0YfL  -3+ !BnB=@1V6 =GK1*Y},j&$3+i&$* g@JK1PX@]#K$K_'L@c]#K$LY@  +"&'532>54&#"#33'@; J]%K$K_'L   +"&'532654&#"#373 5.UaG7#.XqW6g+K/ @c R tGL.XEBx\6E!KPX@ JI@ JIYKPX@]#K_$L@(~]#K$K_+LY@!!%(+7###"&'532667>7!3|vV "!4L: -)%&%cY{z1uyo+?\1NA<.x<F#h@ JK"PX@_%K_$L@!_%K$K_+LY@##'$&#+7#&&#"#"&'532667>323+bVe *2*5I;  .)*5N:"@`Tr'^USr;L*\KCbA >*j@ JK1PX@f#K$K_'L@fc#K$LY@ +"&'53267!#3!3)@B$hh8FYY@:@X" U oz .i ;A@>Jf%K$K_'L +"&'532677##3373"37?E,3XrW.0Xoo T\hy*D6@3~f#K$L +7#!#3!33vWFFYY@:@X\|M.F;6@3~f%K$L +7#7##33733dbU23XrW.0XbTq2vE7@4Jh#K]$L%#+37#"&547332673#8Q-$a7PR;Y:(.4X1IZU) GC#$\6aF&!4@1Jha%K$L!!%'+37667##"&5467733266773#7I W8?JY !%$MA WqJ(M"5#&2ED(+"%'aX5(EL6@3 J~#K$L+7#667####3333>vVZGL [T~E=Z{AlrCM!SGG6@3 J~%K$L+7#667####3333bHA E=>Mqr8{aPp3%M ;R%W3},T&$$*3+01&D$&$j-3+0&&Dj `0")N&($$3+0&H$(s*C@@Jg_*K_+L! %# *!* +"&&5466336654&&#"56632'267#"I\+U3%K:&D(&I3]e%Fc90Y# B;5U3 =GKm]@}g>IYQ1)!(s&jG3+&jj&j3+i&j &j3+&j">@;Je]#K_+L +"&'5326654&##77!7!*^#&\.FT%K@NUo? T.H(:5FPGVXAk?@@=J~]%K_'L +"&'5326654&##77!7!_7KS6?T)WL% k 1Q0B|Q2V5HBJg#K `+L$"((  +"&'#"&546633332773%2677#"8B T8MbM]B@Zr)%T2[1_>?"7^t3 ,%((VVPo:.%'jWcOC(O[+30'6N@K $J~_,K `+L)(1/(6)6"  '' +"&54>32366773326773#"&'#'2>54&#"CO$C`=7C %Wv((44 V aX@O $`$!D:#14(B/ ]ZIi=7% >( (.@@g]=4/BI0Tl=0<3Vl8lw2KPX@ J@ JYKPX@e_*K_+L@&~e_*K_+LY@/.+) 22+"&54654&##7326654&#"'6632326773P\7%/+9@gSI"1L@I J~e_,K_+L.-*( 11+"&54654&##7326654&#"'6632326773PP./N6$>&'-"B$+T+LZ@?Y&)35 W ` EA  ,J'#&C@>5HX "&@?g]sD*@@= Jea_*K$L**%%!&+7#76654&##7326654&#"'66323}(V%@Fvf1W58.3W*%1nCZ_fR?@R9 /5I!C3.0@ $YFPaG6#ZKG")@@=Jea_,K$L))%%!&+7#76654&##7326654&#"'66323"'J..O5#?'(,#A$+T+LZ+0K7t &J'#&C@>n5)$ %Q0B@?J~]#K_+L('!00+"&'532667>7!326773#"&5467# -)%&%NhQ34.X2dTG^ U "!4L NA<.x<#$ L:?gUHL'+1uyo+?\12~@$JK"PX@ ~_%K_+L@+~_%K_+K_+LY@.-*("  22+"&5467&&#"#"&'532667>32326773JU9 *2*5I;  .)*5N:"@FP60 X# -S IH!'^USr;L*\KCbA O?@8S/*aKPX@f#K_+L@ f#K$K_+LY@  +"&54677!#3!3326773G_FYY@:@ZmR35 .Y3d HL"w. L:?gU'oKPX@#~f%K_+L@'~f%K$K_+LY@  +"&54677##33733267733KU3XrW.0XIQ50Y# .S IH ' P@?8S/H#>@;  Je_*K_+L ##+"&54>32&&#"32667#7!O|8fV@b,"&P7SK#L@DV1  ,Ho ^}GNal:[45dDO>CtV00%">@;  Je_,K_+L +"&54>32&&#"3267#73 it)RwN6T#!B.Mf3CFMO   vgDy]5FGzJ@SUBI0sZ`5@2~]#K_+L +"&5467#7!#326773aG`Z]R34.W1e HL!OON L:?gU*5@2~]%K_+L +"&547#7!#326773 KU 79Q6/Y# .S IH$KK O?@8S/9B,J@G)*Je_*K_+L'% ,,+"&&54675&&546632&&#"33#"3267La.lb4<>i@A]&."F.:U@LXV=`8FF6Z($b 0S2Uk L6BZ.$!?C@2@IA85AO!<*KPX@JI@JIYKPX@c]#K_$L@*~c]#K$K_+LY@'&%$ ** +"'53277###"&'532667>7!3$* Y "!4L: -)%&%cM ; I 9@z1uyo+?\1NA<.x323R ) Xe *2*5I;  .)*5N:"@aL;I9@'^USr;L*\KCbA =C8Q&$ 60Q&"&D 9&$Ht3+0&4&DHQ&$Sm3+0,&DSJ&$Tm3+0&,&DTJ &$Um3+0^[&DUJ\%&$Vm3+09s&DVJQ&$' 6Jp3+0Q&&D' 9JM'&$Wq3+0&L&DWN'&$Xq3+0&L&DXN' &$Yq3+0&n&DYNX%&$Zq3+05s&DZNQ'&$'M} 63+0Q&&D&MZ 9)Q*&( B0Q"&H 7)*&(Hh3+04&HH5)C&(Qg3+0 &HQ/)&(Sg3+0j,&HS/)*&(Tg3+0,&HT/){ &(Ug3+0C[&HU/)V%&(Vg3+0s&HV/)Q*&(' BJj3+0Q&H' 7J2&,H3+N4&'HQ}&, Q&L HQ&2 0Q&R KH&2H3+04&RHDH&2S3+0r,&RS7H&2T3+0,&RT7H &2U3+0K[&RU7H%&2V3+0&s&RV7HQ&2' J3+0Q&R' KJ:HC&Dv3+0&EvHC&DC3+0&ECUHC&DH3+04&EHDHC&DQ3+0&EQ8HQC&D 0Qj&E 9OQ&8 7Q&&X 6O&8H3+7&4&XHSOc&Fv3+7&GvOc&FC3+7&GChOc&FH3+74&GHVOc&FQ3+7&GQHOQc&F 7Qj&G B\QY&< &\  3+\Y&<HG3+4&\H \Y&<QS3+&\Qb&Baz^;, ?@<J~qL    +566737>73#&&'t3W +/0-[ -".D 6 %% _ ,.K +-1^, ;@8J~qL    +&&''37>73#&&'.G !80-[ -".D8 9 _ ,.K +-z^[&l@  #JK PX@pgqL@~gqLY@&& 3& +76654&#"566327>73#&&'h!"  %0)#0-[ -".D/ (#e ,.K +-{^s)A@>&J g hqL))#"$!$! +63232673#"&'&&#"7>73#&&'U$ 2S$ E0.[ -"/Dl l   ,.K ,-^L kJK&PX@~cqL@"W_OY@     +56673"&'332673*V .4:I6*8N 4 %% D=  8I^L jJK&PX@~dqL@"W`PY@     +&&'73"&'3326734G " 0;H6*8N8 ; D=  8I^n# JK PX@pgcqLK&PX@ ~gcqL@*~|gW_OYY@! ##%& +76654&#"56632"&'332673!!   &/)#:I6*8N0 ($vC>  8I^s%wK PX@"g h c qL@- ~g hW_ OY@#" %%$!$! +63232673#"&'&&#""&'332673V"  3R&  [;I6+8Nl l B=7H'`N@ JKPX@`'L@W`PY@  +"&'732654'3 "< >7$0,94?<K0@-JW_O +"&'5326773"  M!@I B0NM&7zl&Wzs. JKPX@'fqK _zK _ xLK)PX@+fqK _zKpK _ xL@+f _zKpK _ xLYY@! (& .!.  +"&'###73733#36632'2>54#" 6C!BFFX$U8BQ$D`D(B/U!E:#2 7%R]BYYB#L7,A\ZIi=I3Vl8l1Ul;0<P'0vJK PX@&pe]oK]pL@'~e]oK]pLY@0.*('% 5! +3#"#&&546332#326654&##32654&##u$(KNScqXI1BG|O4u,J,Jp}#% @ONVI[ H54#" 6C!B $U8BQ$D`D(B/U!E:#2 7%RJj"B*,A\ZIi=I3Vl8l1Ul;0<E2@/foK_xL  +"&5467332'2654&##lbkY@gOd.?uPHaOHf WI0R3Il32'2>54#"SgiX' 3@'IN#EeB,D.U'G62 \_/81 2#_\Gh=I6Yh3o8Yg.8;)&q ʰ3+HZ+K@H ()Jg_wK_xL&$ ++ +"&54>3276632&&#"&&#"3267Fw=]|N9-A+  Q/KmH#]R)O&'O @v]6 !A1I zCoC[i N0I*t@'(JKPX@ _qK_zK_xL@g_zK_xLY@%# ** +"&54>3276632&&#"&&#"3267Yn'Ih@ B+  )6!9V0>8#?E ddHb9nB0I!ML=CJ!|P [K PX@p]oK]pL@~]oK]pLY@ 5! +3#"#&&546332#'326654&##u$(KNSāaGLhJiWU}#% @O|oM^dna0> 9@6g]oK]pL      +!"&546337!7!'3#"rd@/L7>\pGbOsxO6MJQ610U( JKPX@"]qK_zK_xLK)PX@&]qK_zKpK_xL@$e_zKpK_xLYY@#!((  +"&54>3236677!7!#7#'2>54&#"AP$Ea=6A H $V!D:#/5(A0 \[Ii<7%">$eJc-@I1Tl<0<3Vl8l ~' 3+ /@,e]oK]pL  +37!7#7!7!7!60jONO6(s!F-J@G*+Je_wK_xL(&" -- +"&&546677&&546632&&#"33#"3267 Qg07_=0;?lC-B;$)%K6>TDNY]i|HF2i/.e 1R2ET,  G8C[.=DD0;KHT4<R*>@;Je]oK_tL  +"&'53267!!!!%  & j5RF L#/OOTIHZ/R@O Jge_wK_xL,+*)&$ // +"&54>3276632&&#"&&#"32677#73W7gV(A ?,  /[3RL'R@&?.L-d bG 'A0I }bo7Y4 NG"1@. JoK_tL"" +"&546736673'26654&'.5<8_Y4  *fD< ">/1vG_)Q$#R'scGIbN"2*3M#W1ĵJKPX@qK_zK_xLKPX@!qK_zKpK_xLK)PX@%qKrK_zKpK_xL@%rK_zKpK_xLYYY@.-*( 11 +"&546776654&#"#33>3232673'WZ "!J?1XX+1A);F_:@EYEm ND   !%/jXA 2"ED(TIOAxc3+@(JoK`xL +"&5467332674GqXr  3 4A%  C }3@0JfoKpL +#77#737'733# U7SR3L V3TT6M 1N22N1)T@  JK(PX@_oKpL@oK_wKpLY@ %% +33776632&&#"#)YJ>'6  "bS<B(GpZ= Y@JKPX@_qKrKpL@grKpLY@ %# +36632&&#"33#'>?!%haqH(qEG IPW9 HK)PX@fqKpL@fpLY@  +3#7333#L@@HXIBBKbBTB+KPX@(!  )J@(!  )JYKPX@_qK_xLKPX@_qKpK_xL@gpK_xLYY@%$ ++ +"&''&&5#''7&&#"'663273267*.]0hm &:>ag9 ! -: P&"? ? <"F,/= BD)]&!JKPX@oK`xL@oKpK`xLY@%#  )) +"&547332673326673#7##"'#F\kYk,+RX`Yk++9I-\XA?ro'd ON+0d^)06`@6R\d31:@7 JoKpK_tL  +"&'53267336673##&!  % _ TU_ fA L#/$o26PEP$ FL "H_H$4g JKPX@_wK_xL@_wKpK_xLY@&%.,%4&4 $$ +"&54>326632#654#"'2>54&#"O2^R@c &d6PSjYkX-J-XQ9[@#YI9^D%[ ]I.+(1SHX'=M]}HOJI_zK_xKtL%$-+$2%2###&)$ +654#"#"&54>32663226654&&#" }A4"Bc@Ye"CeBc0P'?A}-L/)#7Q,6IG+=zd=o_>zd=G%%H@1C~X9%K}K>DP bK PX@!pg]oKpL@"~g]oKpLY@ $5! +3#"#&&546332##32654&##t$(KNStb@;K>`qCDL}#% @OgSybRY;5&5~@!JKPX@&_qK_zK_xKtL@$g_zK_xKtLY@('/-'5(5&&&)%# +6632&&#"36632#"&'#2>54#"7>54&#"'66323267_r)DU,=L%:2&G#'^1Yf;e@ET&B:4U%"[ ZS9L2#(5**1L`IBV9'1).6N "*7@4'(J_zK_xL%#** +"&54>7>54&#"'66323267S^"7A9>+(B"&S)LV2X9#=%/1+J! J ME):' !#EG=6A' &"(M3n !-m@ JKPX@g_qK_tL@gg_tLY@#")'"-#- !! +"&5467#"&54632326737654#"4G:7AF78  3 %4A'S=+4C>0  _  C [  ),l'@$JK PX@'n]rK_xK_tL@&]rK_xK_tLY@"  '' +"&'532677&&547#?33#3267o  4E =SZ>4>(+AI!b4? '#(&ozC ! B0PtNK PX@p]oKpL@~]oKpLY@ %! +3#"#&&5463!#\%'KNSć{!% @OO,'w@$%JKPX@!_qK]rK_xL@g]rK_xLY@"  '' +"&547#?6632&&#"3#32676J =S\GI& $<>" 2 4A '#('?MM IDUC ! C ZM5@2J]oK_tL  +"&547#7!#3274@ė0 (96OO6/ LJ&4@1J]oK_xL && +"&&54667#7!326654&'7!#XZx<8`; Dk>'P?Om87/&O(]@ JKPX@_oK`xL@oK_wK`xLY@(( +"&5467332676654#"56632,pm\Y]EDYVA1!//DCEt g_1L28@\Y/ 6N 8=  Nr=PmeKPX@ J@ JYKPX@_wKpL@oK_wKpLY@ $# +3&#"566323:L $/ ;e7 Z#G#/`?"(d@ JKPX@_rK_tL@rK_zK_tLY@#! (( +"&'53267733>776632&&#" (:#QX# n/% )XG20> !LE@D8)AKK=3@0e]oK]pL +#7#737!7!3#!  uOF GPGGP3@0e]rK]pL +#77#737#7!3#3 _E}_ 7'7!!#"3267un/Pe6ɭ ">mCMI.e/.g hR@;Jg]rK_tL  +"&&54667'7!!#"3267F`1OM t 7>e;M<6N"!X5\:Ws;@J=(TAAGM(@@=%&J~]rK_tL#! (( +"&546676654&&##77#7!3267mQW#UJR^)E+2 k Ja=-+-/MP>5(6# 5@%(=J@QI]]  L  dJKPX@ e_qK]pL@ge]pLY@ !%% +#7#736654&#"'66323#!,'/',J#*(b;8O)'*aGKF3V1&/$Je]oK_xL  +"&'532654&###7!#322`%&a.^gO@e;N,lm R[TB> PPjWwA@>Jg]rK_xL  +"'5326654&##7#7!#32m4Q2%G/L?D)?YAO#x #O8/+)JJu,G&Yq'f@ JK PX@n]rK_xL@]rK_xLY@'' +"&'532654&'&&5477#?33#,SL,J=,%SZ>415t O7!')%[(&ozC] >1OX!JJKPX@_rKtL@rK_zKtLY@ $ +336632>54&#"H U=KI?pW1@AoT.'/+J6 e,B_MIhC 37Sc5.=Ak?>.0K)PX@ qKpL@ pLY@  +33>N>&@`K)PX@ f eqK  p L@ f e  p LY@ +3#737#73733#3#96N69 H_HH_H)&''=K3+)v&'']K0&G']CKW)B&/-)&/M&OM(B&1-(&1MP&QMC;&$Kt3+0&&DKQ&,K3+}&'KH&2K3+0&RK>O&8K3+7&&XKWO&8z3+7&D&X#O&8z3+7&g&X#O<&8z3+77&X#O&8y3+7&g&X";&$@3+0&D&D9&$0&D&D`W&L_3+0&LH)P@M  Je e_wK_ xL&%$#"!  )) +"&54>32&&#"32677#737#733#W7gVAb,"&U3RL'R@&?!??-d bGNbo7Y4 EGGNG'")9@  JKPX@,e _zK  _pK_ tL@0erK _zK  _pK_ tLY@!+*31*9+9'&%$#" )) +"&'53267#737667##"&54>323733#2>54&#"1SY+1< #V8AQ$Ca=6A!C~AL k$"C8"-5(B/,QG/@,AXZIg<8%SGA:9/Rh:0<2Tj861H&*K3+'&JKM)&.K3+ &NK3+H'&2P0'&RPH'W&2'LP3+0'&R&LVP"&KQ3+&K)&'=)R&']0&G]CH&*v73+'&Jv)giKPX@ foKrK_xL@$foKrKpK_xLY@  +"&5477!#3!332673UMOFYY@@Xn#$.0EYE1N MC.)%DNAOa+wJJKPX@_oKtL@oK_wKtLY@ % +33>32>54&#" H2J84O.1Dud=7Pkj 51X:̗5=+h?Aw(&1C3+&QCo&$ 3+0&&D c&$ ?3+0&&D )*&( 3+0&H H)*&( 93+0&H }&, 3++&' &, 3+\&' H&2 3+0&R PH&2 3+0&R  )9&5 3+&U .)9&5 E3+&U O&8 3+7&&X iO&8 y3+7&&X "L=+#@ J# G_wL +7>54&''>54&#"'66324[~H/'&P)>nW1:6;f4BB4V4TH%9=kV7HbB*7  H"+>-(,@( @1IfM7FoUA2")#@ J" G_zL +7>54&'5>54&#"'6632DHl?! G&IqB/+,Q)3c1.O0F8"*QO*>S7'-  B,B1&&@?08Y<6NpN4*&+K3+ &KK3+)TJKPX@_oKpKtL@oK_wKpKtLY@ $& +6654&#"#3366327?AeEOYJ"sI[c #7=K|Gu:F]\$+C02AL@#K0JGK)PX@0gqK_zK _xK `xL@0g_zK _xK `xLY@CB43IGBLCL<:3A4A$*&* +'667&5##"&54>32366773>32#"&'%2>54&#"2654&#"@ 2FP%AP$Ea=6A %Wu)?/87XF-!D:#/5(A01'&4i B P)B&\[Ii<7%">$-7"9*>2=1Tl<0<3Vl8l2(,$1<@9JgoK`xL&%,*%1&1$$ +"&&54667&&54773326773'2654&#"Ag<2V6")Z=7KYYK@-?PVasNB;^7F 1_C@`?A2XX56XDX^FaZ?Wv:;N(@ j\`;,~ -)EP~RWP<9iT1I=e=:D:b=32632#"&'72654&#"P;8 "%"ND1XrH 3C+>I77RA*: " 0O  ($/jXc2"D@ -cB,=E#,?*l+y@)JK PX@%ng]rK_xL@$g]rK_xLY@! '% +!+$" +'667667#?33#632#"&'72654&#" ;98SZ>4586RA*: " 0O  (&ozCB,=E#,?*0S(7Fy% JK)PX@!qK_zK  ` xL@!_zK  ` xLY@!98*)@>8F9F20)7*7#! (( +"&54>3236677336632#"&''2>54&#"!2>54#"HS$Ea=6A %X& $U8BQ$Fe@DS 4P+0H131(A0(B/U!E:#6 \[Ii<7%">$"B*,A\ZIi=A<:%I1Sj:0A3Vl8l3Vl8l/Sk;4=1 T" 3+'t@ JK)PX@" fqKoK pL@" foK] pLY@ +#373#'###37&&5#'9^5BkEXC.t-y*p A7e).,&W,{{Hx!+u@ $ JK)PX@ qK_wK_xL@ _wK_xLY@(%!!%)( +7&54>3273&&'3267#"'"#"WBQ=]|NB# $#):)O&'O8F60 ! KmH#K}I@v]6&1 J N\TL1Coo!)@$ JKPX@ oK_zK_xLKPX@ _zK_xLKPX@ oK_zK_xL@ _zK_xLYYY@'%!!%)( +7&&54>3273&&'3267#"'#"a'IgAMBS  &#?E)5(P@ 9V0K3Hb9I JP%YM -@*foK]pL  +3#7333#!)FPPBYC5LG7GPW"jJKPX@ n ]oKpL@ ]oKpLY@ +#7!733##7737g=H=3YXZ/c1!OXXOdHH"8M@J  56J_zK_xK_tL31,+! 88 +"&'&&'&&'532654&'&&54632&&#"#3267G@  P*;>&49:cS1S@,,2$44Cof !   &KR$#Q.&' D3DTF ) &A8OV (-'H =@:J]rKpK_tL  +"&'&&##7#7!3267K=%-% ME.* # 'ON+(<BGoDA,( I C)@&  J_wKpL%( +3>54&#"'6632CD(SG,:2$P#!)b7\eEsF9B#1D/1=HhRGfG$")@&  J_zKpL%( +37>54&#"'6632?(OA'<.(@##)U8Wem$2D-1@EfRh#]M+I@F Je e]oK] pL+*)('%!! +37#7332#326654&##32654&##3#)$KKccqXI1BG|O4u,J,JpNNVI[ H foK _ xL$$  +"&54677#733!33#'2677!,pmII=Y=G=Y32733267#"&'3277"6654'7#0+%Fc> sH}8bmR1&D(&I32u#Y8V"+ -/6U@}g>!GAcK _6 ,!S  dB!:@7JfcoL  +"'73267#7333#P2$,D ALLDYDKKBm M7A1NBNeX  @  JK-PX@&f _wKrK_ tL@$ gfrK_ tLY@    +"&54632"'5327#73733#-$% =CEE.W.EEEIq&-# Jb=GGKXH(;@ %&JKPX@"_wK_xK_tL@&oK_wK_xK_tLY@*)53);*;#! (( +"&5477667##"&54>323733267266776654&#"G1E aFux0]TD\$I  &>[< YJ=`C"T4;g%)=YJ@/d L49a3237332672>54&#"0?#V8AQ$Eb=5A!B )!D9#.4(B01=?@,A\[Ii=8%S C /2Ul:,@3Vl8l97@4 Je]oKpL! +3#7332##32654&##)>PPIQavbfu>NNd[ECL'LWMr'sWE=0 "{KPX@ JH@ JYKPX@f_rKpL@ frK_zKpLY@$% +37#73733>32&#"3#5FF.H .:& 'A1pv5Gc2 O(C(G5Y4@1JfoK pL +3'#73'33733#7#:?\7!YTeW;_7c NNd(H@E J frK_ tL$#  +"&'532677#73'33733#3>77# (:#,P6X^^`AY)X xG20>(GGKK8@D.!LE7 !+u@JKPX@f_rK_xL@#frK_zK_xLY@)'"   +"5473363232677>54&#"J=EaCX2Re3Q2&&H#%YlD0W70&9Y ^LVJF8H' *#B32$'KN0&"D"DB 3+$3@ JKPX@"_qK_zK_xLKPX@&_qK_zKpK_xL@$g_zKpK_xLYY@&%-+%3&3 $$ +"&'##6632&&#"136632'2>54#" 6C!B=?! $U8BQ$D`D(B/U!E:#2 7%RpEH IV2$,A\ZIi=I3Vl8l1Ul;0<"7@4J_zK_xL  +"&'7326654&#"56632~#E7 9V0>8#?E)Yn'Ih  I ML=CJddHb9"&2L@I0%JIGg_zK_xL('.,'2(2$'%+ +'667&&54>32&&#"6632#"&'72654&#",6''Ih@#E7 +F3%Q/B>fS,K23  F .1 <0Hb9 I ,J]1#&I/HH(=)#"0U+:@ ()JK)PX@&qK_zK_xK_tL@&_zK_xK_tLY@-,53,:-:&$ ++ +"&5477667##"&54>3236677332672>54&#"0?  $V8AP$Ea=6A %W )!D:#/5(A01=?A-@\[Ii<7%">$ C /1Tl<0<3Vl8l0#2@ JKPX@"_qK_zK_xLKPX@&_qK_zKpK_xL@$g_zKpK_xLYY@%$-+$2%2 ## +"&54>32366776632&&#"#7#'2>54&#"AP$Ea=6A ++-SM J-M1*G,EX:a;B{`99AF-#1)"!0 "*?@< Jg_zK_xL&$ ** +"&546632'26654&##732654&#"mhG`P]><)2>i='A(<;)@R..I^- t_bZH=4D  7-;O'I-#+"H'2 $J|I=@:JfrK_tL  +"'5327#73733#@% =CEE.W.EEEI Jb=GGKX3B@)*JKPX@+_qK_zK _xK_tL@)g_zK _xK_tLY@54=;4B5B.,'%33 +"&'532677>1##"&54>32366776632&&#"2>54&#"1SY+>P  $V8AP$Ea=6A @;  Je_zK_xL  +"&&546632&&#"3277#738Z4Ha0LB!I^/s6-j;)N.]Fa[GI{IG'1@.! JrK_tL'' +"&54673366773'26654&'/6A(U[(  .] :(  >+7h5U! U 7,Q"'H/I$1.2-"/;B@?"6)#J_zK_xL100;1;'%  // +"&5467'&&#"5632366776632&&#"'2654&'-3<%U  # 0 \+ 6# & 6*6T& A h)(jA(B#>'I)$77&'@$rK`xKtL%) +7>1##"&546733266773*0 3D+=IFYH &"NE1W5)2"DA(G+ %0jX *b@JKPX@_qK_zKpL@g_zKpLY@**%*%# +36632&&#"3>32#654&#"=?! 2C+>IFXG !%"OC1pEH IV42!ED(P-!%/jX 5@'JKPX@%_qK_zKpK_tL@#g_zKpK_tLY@-+!55 +"&'5327654&#"#6632&&#"3>32 <V !%"OC1X=?!3C+>IVIJc-!%/jXpEH IV62"ED(lJX  iK-PX@!f_wKrK pL@gfrK pLY@      +"&546327#73733#.$5GG.X/JJ4q&-#GG3@ "@ JrKpL  +#77'73 MUG LUF 444s4~aJK)PX@ghqKpL@ghpLY@"#"" +3&#"#6632332673#"'HO  4:+CXM  5:+Ft>B=>B Xi JK)PX@g hqKpL@g hpLY@$! +3#"&5463233#376654&#"Q?98=9 :XTaa?():)/? yHq I@ JK)PX@qK_tL@_tLY@  +"&5467332674GX  34A)& " C \!@JK)PX@(~qK]rKpK_tL@(~]rKpK_tLY@ !! +"&'5326654&##77!#3!7KS6?T)WL% bXX0w 1Q0B|Q2V5HB<2@/U=MwE7N0],$JKPX@rK`xL@rKpK`xLY@*(#"! 00 +"&5467332667733266773#7##"&'#>DEYG!#!H?4XH #!J@1XrH 1@)7<7D D@*F!$0fP-!%/jXc2"@77#7N43@0 JrK`xKtL44%&&) +7>1##"&'##"&5467332667733266773R0 1@)7<7D)>DEYG!#!H?4XH #!I@1X5)2"@77#D@*F!$0fP-!%2lX3";z@-%JKPX@_rKpK_ tL@#rK_zKpK_ tLY@31+)$#"!;; +"&'53276654&#"#654&#"#33>323>32. <W!#!H?3XG # J@1XrH 1@)7<7D)>DVIJc!$0fPP-!%/jXc2"@77#D@*lJX "(m@ JKPX@_rKpK_tL@ rK_zKpK_tLY@"  (( +"&'5326733>32#6654&#",!H 3C+>IFXG"%"NDF>I c2"D@/P!$/jXEG "-m@*+JKPX@_rKpK_tL@ rK_zKpK_tLY@(& -- +"&5476654&#"#33>3232670@V"%"ND1XrH 3C+>IU )1= !$/jXc2"D@/s  C F$@! JrKpL +33>73#qfBUqe ?P00 6 240`0))KPX@ JK"PX@ J@  JYYKPX@#e _zK _ xLK"PX@+e _zK]pK _ xL@3e _zK]rK]pK _ xLYY@$"))  +"&54>327!#3#3!7'26654&&#"]k"CeB5II$ B2N-0(7Q,=o_>zd=($EIII0IF}S :%K}K>D-"/C@@J~_zK`xL+)#!//  +"&54>32#"'#'2677332>54&#"KL4d[Vw>6W=nH)-: U& '5 _\RL bSOf:CuJ2iY6Z,.IF<'%(BQ)ScI`i0rqfKPX@ J@ JYKPX@rK_xL@rKpK_xLY@  +"&'73266773#7#2N4 4XrH .: OBf5c2 KPX@ J@ JYKPX@qK_xLK)PX@qKpK_xL@pK_xLYY@  +"&'7326673#7#2N4 dXH .: OBf5c2 q(B@? %&JrK_xK_tL#!(( +"&54677667##"&'7326677332670?.:&2N4 4X )1= ?A2 OBf5 C "cKPX@ J H@  JYKPX@_rKtL@rK_zKtLY@ $% +33>32&#"H .:& 2N4 gc2 OBf5"$|KPX@!"JH@!"JYKPX@_rK_tL@rK_zK_tLY@ $$ +"&54733>32&#"3267v0@H .:& 2N4 C )1=_c2 OBf5  C j")@& J_zKpL%$ +3>32&&#"P1G.+ @P{AI J \( ")@&  J_zKtL$% +6654#"5632(3' */=?h 5 K?7 3@0 Je]rKpL! +332#'#32654&##rNX)D*b]Xn-&49:cS1S@,,2#5"6q_* (/@.&' D3DTF ) '(5%QTG  C ~X@JKPX@_qK_tL@g_tLY@  +"&'53276632&&#"? ?HL" "* MJrPaI:8S^~"w@JKPX@ e_qK_tL@ge_tLY@ "" +"&'5327#736632&&#"3#? ?@DD9HL" "* 7GGAMJr-G PaI:8GS^ "%7@4"#J_zK_tL  %% +"&5476654&#"566323267{0@_ )0@_ )1=  C 1=D C =~%u@ JKPX@!g_qK _tL@gg _tLY@" %%  +"&546336632&&#"3#'267#"B>C[R1}HL" "* zLK1@'" +0<1ABLPaI:8HBIH+5"("WG 3+lf@ JK PX@n]rK_tL@]rK_tLY@  +"&547#?33#32676J oSZ>4p" 24A ' (&ozC ! C @&t JKPX@  frK _ xL@#  frKpK _ xLY@$#   +"&54677#73733733##7#'32667#=I CD-Y--W-GG6H 3DX &F@ DA(,GGc2" %&SE6+(E 4@1J]rK_xL    +"&5467#73326654&'73#ei4<`IM==/Q0+&q@=yy_L0IEe>N4oX4REI@]TW@%"'qKPX@ JH@ JIYKPX@_rK`xL@rK_zK`xLY@'' +"&54673326776654#"56632a]>X>2;BN1( 7G `Q$)"432##732654&#"; MEL%;>*29aUUE:+,1X <BGq4H%;(A?)k! .%0[@X )#J G~g]rK_tL'&,*&0'0 %% +"&54632654&##77!7!&&''267&#"]KbcM4Y$ WL% k 1Q0& ?  "b;&IBN2(1@ACD$#%HBO=/("HB% @E@  JKPX@_qKpL@gpLY@ %' +3>54&#"'6632@M?c97) H!!&\1T_+eXCkBX7-6B\H3l^UG@  JKPX@_qKpL@gpLY@ %' +3&&546632&&#"CG7*pBHf5 ?QI:^nK@  JK)PX@qK_xL@_xLY@  +"&'732654&'37M,9$:S?DLYDF832&&#"3267_^ 0HfC%E83O:$;5#:@wq/u}t]7 I 9b{AVJ KH&2*+ +3+) 9@6Je]rK]pL  ! +332#32654&##32654&##)q\Wy0&zb.a:6.5NTd??)3`A:j @YR:*/#u<1#"(?@<Jg_zK_xL$"!((  +"&54675&&54632'26654#"33#"XmKD*1o]rr@YGT%6=<; JV> LG;D  21JTi\VKGrA,&+!H/2'*0t.@JKPX@(e_qK_zK_xL@&ge_zK_xLY@+*)(%# .. +"&54663276632&&#"&&#"32677#73ZpD_*@,  '?F[,:8-o:)N kh_]sB0I!}IJzG@HG;O  #{K-PX@'h _wKrK _ tL@% ghrK _ tLY@!  ##    +"&54632"&5463333#'267#"-$<@[S qWrLKF< 5q&-#=0@CH[MH2."()@& JrKpKtL +667##73773EhaqH(Y>PW29g@rK^pL +333rXc1I0'6}@ JKPX@&_qK_zK_xKtL@$g_zK_xKtLY@)(1/(6)6''%)&( +7>1##"&54>32366776632&&#"2>54&#"*0 #V8AQ$Eb=5A 54&#"'66323#@#SR?c97) H!!&\1T_+eXdd"I~BX7-6B\H3l^PIO_@ JKPX@e_qKpL@gepLY@%' +37#737&&546632&&#"3##dcG732366773!3!7#'2>54&#"#AP$Ea=6A %W0U U $V!D:#/5(A07L \[Ii<7%">$GqBc-@I1Tl<0<3Vl8l05D\KPX@.$ J@.$ JYKPX@1  ~qK _zK  _pK_ tLKPX@?  ~qK _zK_zKpK  _xK_ tLK)PX@=  ~qK _zK]rKpK  _xK_ tL@=  ~ _zK]rKpK  _xK_ tLYYY@!76?=6D7D,+*)"  55 +"&'5326654&##77##7##"&54>32366773!2>54&#"7KS6?T)WL% bH $V8AP$Ea=6A %W0[ 1Q0B|j!D:#/5(A0Q2V5HB<2c-@\[Ii<7%">$@/U=MwE/1Tl<0<3Vl8l0,;>H=64-;.;$#&$ +'667#7##"&54>32366773!3>32##%2>54&#"#32654&#"f; $V8AP$Ea=6A %W0UL%;?)29aUU`!D:#/5(A07L:+,1X c-@\[Ii<7%">$Gq4H%;(A?)h1Tl<0<3Vl8ll! .,$7@ JK PX@ n_rK ]pLKPX@_rK ]pL@*_zK]rK ]pLYY@'%10%7'7 $# +3"&547#?336632&&#"#'3254&'&&547#6J ;SZ>4!1S@,,2#5"6q_y&49:<4A '(&ozF ) '(5%QJHJ' D3)" ! ,f+7@!" JK PX@3p_qK ]rK _xK_ tLKPX@4~_qK ]rK _xK_ tL@2~g ]rK _xK_ tLYY@-,10,7-7&$ ++ +"&'53277#"&547#?3376632&&#"267# ? +6J =SZ>4 HL" "* M,)S>Jr9 4A '#(&oz4PaI:8S^. ! ,<H@$%. F J:IK PX@*n  g_rK _ xLKPX@)  g_rK _ xL@4  g_zK]rK _ xLYY@#>=DB=H>H8620)'"  << +"&547#?33#3267&&54>32&&#"6632#"&'%2654&#"6J =SZ>4>K.'Ih@#E7 +F3%Q/B>fS8P:W723  J 1 4A '#(&ozC ! "!Hb9 I ,J]1 $I/HH'!)I)(JKPX@  3 2J@  3 2JYKPX@*_qK _rK pK_ tLKPX@5_qK _zK ]rK pK_ tL@3g _zK ]rK pK_ tLYY@GFED@>750.&$ JJ +"&'53267#?>32&&#"33>32#"&'53276654&#"##0 $( n]d 0F14"'. 3C+=JVIK <V"%"NE1Xd{pLJ;7 %!0EM C ,>3c2"D@+lJXJc!$/jXS^[*KPX@J@JYKPX@qK_zK_xLK)PX@ qK_zKpK_xL@ _zKpK_xLYY@ ** +"&'#332654&'&&54632&&#"ZHi- XX#cF;>&49:cS1SA++3#5"6r (z!.&' D3DTF ) '(5%QT: RK)PX@qK]rK]pL@]rK]pLY@  +33!3%#X0U MGqB<O I@F J ]oK] pL    +37373#'37373#'|CDxHGCDxHGzPPB?@<~e]oK pL  +7!#7#7!#7#11X 11X 闗闗1"0wKPX@ J@ JYKPX@_zK_xKtL@ rK_zK_xKtLY@00)%+) +7>1##"&546776654&#"56632326677350 3C,=I$ )0@% !%"OD1W5)2"DA(  C 1=+ %0jX1"AKPX@  >?J@  >?JYKPX@!_zK_xK_tL@%rK_zK_xK_tLY@<:43/-$"AA +"&54677667##"&546776654&#"56632326677332670?3C,=I$ )0@% !%"OD1W )1= A<2"DA(  C 1=+ %0jX  C GGK5)&f 3+GG05)&f 3+GM5)&f 3+GLgGU5)&f 3+ %aGC5)&f 3+ %aGE5)&f 3+GaGK5)&f 3+Z$aGZ5)&f 3+aG\5)&f 3+<<<paD 0dD@%gW_O  +D72654Ha % $2LD2#35*9;v? *dD@gW_O +D"&5463"3$3L8 (?5)8=2#`S6dD@+  JW_O%% +D7654&#"'6632x /<">?IL XM  2>,4H4\e8dD@-  JW_O%& +D7&&54632&&#" 1,RG"6,(++/5@%;F1 * - Tte 3+t!e 3+n"d 'dD@Jt +D73#'nhu'YO"뼼o"e / /3+nT&dD@U]M +D73n#O#Ts^Gv^6C46V 3+mL` 3+4CT ְ3+4vS ְ3+ ,dD@!JU]M +D'37:v;苋o dDt +D'3: 3+0 3+~  3+*~  3+  3+!L dD@U]M +D3#, L<q;W 0+'7'77'!H.3/H"I/4.;*<;+<<+;<*;GaG-5)&f 3+GGO5)&f 3+8AgGV5)&f 3+aG[5)&f 3+lnG_5)&f 3+NT,dD@!U]M +D!#5!nBPNT0dD@%Ue]M +D!#5353BBPNT0dD@%Ue]M +D!#533BFB(PNT0dD@%Ue]M +D!5#533BBPMS,dD@!U^N +D3533MBBnPNT,dD@!U^N +D733NBĦjBNT0dD@%Ue]M +D733#NBĦjB9KU ۰3+pW%5 53+<8'dD@Jt +D'373K`K:vRކ 'dD@Jt +D73#'D0`K9w܄0+'7% k1kH@=0+77'7D H@=Bk1MC 3+M : 3+.R 3+C3QW 3+@ ,Ұ3+&NdDK PX@oU]M@U]MY@  +D53#q5q&NdDK PX@oU]M@U]MY@  +D5#53qq5ENdDK PX@nU^N@U^NY@  +D7533E5qq5-NdDK PX@nU^N@U^NY@  +D75353-q55q0QdDK PX@nU^N@U^NY@  +D53!538M9Хcc0NdDK PX@nU^N@U^NY@  +D53!8ХcBK 1dD@&JH GU]M +D'57!!O1;D734",0  6: 48$q Qd"!0"&FNg g3+"&!Ng g3+&1" ->X@U+J gg_?K _6L/."!75.>/>)'!-"-   +"&54676632#"&'663226654&#"2>54&#"_] ,oAS/Q3(K' !a8JK=`@-$$$D54&#"'63233267  *3X2*! -?DFV4*! )@I!eE?3p0'emg*('D JH.u,W'dng*+$B0L"\JK1PX@g_=K6L@g_=LY@"")+37.54>32726654&#"8Y5(TZTs;VWRf0^R?Z8` BhE@zb:BtLkP~FvIW]+GY.e[0!2@/J_@K]:L!!(+7&&54>3226654&&#"4@H"CeB^j>\=2!/O/0(7Q,=hM>zd=o`:uc@1C~X9%K}K>D`uY@  JK1PX@g_=K6L@g_=LY@ %%%+!7&&546632&&#"33%h|Zi:S$$C+LvCYl+/zkcXJCtHSS0:"*4@1Jc_@L**+"&'532654&'.54>32&&#"%43!66 5 E1#GiF%B8"/G/79:>h H - $ +J:  &67 iH $<-%=,H4-C D 1,&G7F8&0!EU!O@  JK1PX@_=K6L@_=LY@ %/+!667'754&''7&&#"'6632:-G~' '9%0Q&*cAzG=IXKAf1wAxF xd* @  H:L+>7'74&''7&&'5-L0 &{DvVYMDQJCPGPDOL[R̀u Y",KPX@)*J@)*JYKPX@_@K_6LK1PX@ 8K_@K6K_6L@#~8K_@K_6LYY@'%,,+"&5467#>54&#"'632332678BX2*! %?DFV4*! # EI3p0'emg*('CJH.u,W'dng*+$E!'6<@94J_@K_6K:L)(1/(6)6''&-+654&'.5476632#"&'#2>54&#"-3F$|Zc#DeC2I-%J/B/F.92?T3@ #OFMukhE}_7*  .1 '# //Na3J>\_&0"F MH_0" !)Hx&(LO JK1PX@~5K6L@~5LY@ +3333#667###(n~!|Z]  I \64g h9NZ JK1PX@~8K6K:L@~|8K:LY@ +33#667#'_ffqV@ FZ s 2?C !*J@G(J e_@K _6K:L%#**&$ +73>32#"&'3##7%2>54&#"SNU 4`N\c>aC1E W,B,38BC'32&&#"3267fh'MqJ(I:3M5C$#Co\C|b:F 0O`1BD H  '@$]rK]pL ! +332#'326654&##qfxRi.5Mj8RF9rdfLG?pIII 7@4e]rK]pL ! +37#73732#'326654&##3#2JJ0fxRi.5Mj8RF9!ooHrdfLG?pIIIH /@,e]rK]pL  +3!#3#3q/ $HGH(J@G"Jg_zK_xL (( +"'732654&##732654&#"56632dM5E"2>7)<#Ob4+%Q/+P*_WRM+(l79+)&&J6/$&P R::J  ;)IM:)@&crKpL  +33"&54632qXqN#'#6+@(JrK_xL  +"&'532673B$ !+ TXRR G #)xPJ%@" JrKpL +3366773#'qX3 !jer.,"[ *@' JrK^pL  +37'7373$J32'26654&#"bj FpO?[1$InD9W1:82I0<w`54&#"56632(I:3M5C$"D+fh'MqF 0O`1BD H o\C|b:4"[R<&@ &3+.5ZF8@ 3+ v :&@ &3+,@!p 3+ #/@@= JhrK_xL%$+)$/%/ ## +"&5467&&546773326773'2654&#"RbF@WQ16 X 4+ '>l@DK3,CK5WICY/  HH G<.HH2I>+A\1GJ>).F<+25 !@_zL%# +>32#654&&#"5 BnL^j[0(.G0  G}No`"%(9%5[90 $@!_xL  +"&5467332673]kZ=7<][)BZo_$$&>Dka5cO. +@(g]rKpL %! +332##32654##qVQEqC++:$@U`1N?GT%6@I 8@5Je]rKpL & +#7&&54633#7#37#"#0xhqX-H<&==C:7RW*6S$ ;@8JgrK^pL    +3"&5467'3373'37#"TUH?]aQO-Xq<&;!<'1L>BSG.'$&:!@]rKpL +3#7!#qbbHH0:*$@!rK`xL +"&5467332673YbFYI90>C IXI 8_ TF I +1I9Y:X2 BX8@ ܰ3+@ ߰3+zP@ 3+< !@JrKpL  +3336673FY&`2.AH!'@$JrKpL!! +333667336673#'&&7#X_  O  xhk  vEC,EG#137 %@"]rK]pL  +#7#7!3 Kb ;I@EX*pKPX@ J@ JYKPX@]rK_xL@]rKpK_xLY@  +"'53267667!##*: :pZb&# !F E /?Cp7~n QDG$5)&f 3+gG5)&f 3+PG%5)&f 3+B&fK%PX@" e]K] L@ g e] LY@&$ ! +7#737323##'32654&##32654&##P.< =*x@J-> IeM"L+>'-BMR7<(1H,/36, #BC&'-*(PG'5)&f 3+PG(5)&f 3+8Gm5)&f 3+dG*5)&f 3+PG+5)&f 3+(-G,5)&f 3+G-5)&f 3+PG.5)&f 3+PFG/5)&f 3+OZG05)&f 3+OG15)&f 3+O>JK%PX@KL@LY@ +33#7667'Ob534Y?.4;DAV8CLMAn<=%E(&I3#%0Y `Q$)"454#" 6C!B 4:+ X  5:+  R=BQ$D`D(B/U!E:#2 7%Rz >B:k>B%G!+;\ZIi=I3Vl8l1Ul;0<00?@-  JKPX@1hqK_oK _zK  _ xLK)PX@5hqK_oK _zK pK  _ xL@5h_oK _zK pK  _ xLYY@#21:81?2?,+)'%$"  00 +"&54>3236677&#"#66327332673#"'#7#'2>54&#"AP$Ea=6A  4:+  W  5:+ |H $V!D:#/5(A0 \[Ii<7%":$5 >B:k>Bc-@I1Tl<0<3Vl8l<@"#. JKPX@3 g  g_qK ]rK_ tL@1g g  g ]rK_ tLY@!86431/-,+*'%  << +"&'53267&#"#66327#?>32&&#"3#32673#"'0 $( C 4:+ ]d 0F14"'. s s(  5:+ :LJ;7; >B%!0EM C ,>3C>BS^"3?M@7 GD:0-*'JKPX@'~| _rK pL@+~|rK _zK pLY@A@54@MAM4?5?33&% +3#67733>323>326673#7&&'#7&&'"7654&!"76654&9($4*H 1@)7<7D)>D'$ 4'Y'.o;.X2I -!%'R@ U!$z%'4}@ /+$ JKPX@#~|_rKpL@'~|rK_zKpLY@)((4)4''$ +#66773>326673#7.'"76654:&(4 S6+N 4C,@J')4 U8'^((FD%2']"'CA$1!GNc3"G@0O0"HN  AL  S@!0?@ $/JKPX@/g h _rK  _xK  t L@3g hrK _zK  _xK  t LY@21971?2?00"(&$" +7&#"#6632336632#"&'#32673#"'2>54#" 4 :, {H #X8AP$Da<6B  59+ (B/U!E:#2| >BHd,A\[Ji<7%9Y>BJ/3Vl8l1Tl<0<"+KPX@JH@JYKPX@$g h_rK  p L@(g hrK_zK  p LY@++"%$%"" +37&#"#6632733>32&#"32673#"'2 4:+ 2H .:& 1M5   5:+ ' >Bc2 O@c4>Bj"'E@BJgg_zK pL''"#%%"" +37&#"#66327>32&&#"32673#"'1 4 :, 1G.+ A  59+ & >BOAI J \>B"7g@d2J~|||_zK_xL/.,+#!77 +"&'532654&'&#"#667&54632&&#"26731IP*;>%4.+4 6% cS1S@,,2"6' #( 5 4#q Q.&- 8?$DTF ) ) #6? QTl5@(2 3 JK PX@/n g  g]rK _ xL@. g  g]rK _ xLY@0.'%#"  55 +"&5477&#"#66327#?33#32673#"'32676J  4 :, SZ>4(  59+" 2 4A 'T >B(&ozC>B# ! C $I@F!Jgg]rK ]  p L$$"#"$ +#77&&#"#66327#7!32673#"&'3  4<.!}E 5>-# < >B BG>B B)$3{@ JKPX@ g_zK_xL@$g_zKpK_xLY@&%-+%3&3 $$ +"&'##6632&&#"136632'2>54#" 6A!Cz{`1SY+>O  #V8AQ$EaD(B0U"D9"- 8%S7q`Q Jg]rK_tL %%  +"&54667'57!7!'26654&#"doCzRX  i2.AfC?M#@5AQ%Cj[UEBB@N%a>4eR1I>c8=?>b5?AFDZ@=7 C> JK PX@-p   qK_rK ] pLKPX@.~   qK_rK ] pLKPX@2~  qK_rK pK _ x LK)PX@=~  qK_zK]rK pK _ x L@:  _zK]rK pK _ x LYYYY@DDB@;965%# +7&547#?33#336632#654&#"#3267#"'f =SZ>4>QX+ C">IFXG !%#ND1X= " 2(JKk/ '#(&ozC !|# ED(P-!%/jX"C M@3@0JfrKpL +#?#737'733# M&FGG LKL&F 4G44G4=@:JfrK_xL  +"&54677#73733#32674GGG-X.\\  3 4A%DGGG  C J!'1@  JKPX@*   f _rK _xK tL@.   frK _zK _xK tLY@ )(! -,(1)1$# '!'#$ +#7373366323##"&'#"36542667!gDD.H #X8AP:4DeA6B1"-[(@/ 2Fd,A\[FIuE7%9VClg2T4''0<>"A@> frK _ xL""  +"&5467#73733733#'2677#Y_CC.Y/.X.BA 8_B>C 3 YF/FF?:X2GI9:#,,3E$-C@@J  e]rK _xL,+)'$$# +'73667#73!54&'73#3##"&5473267! 1 1'`4F+&q!5+ CoMeiX==<_F-NIEQ;4REIP+FEpBy_">NS[:&5KPX@ J@ JYKPX@$cqK_zK `xLK)PX@(cqK_zKpK `xL@(c_zKpK `xLYY@('/-'5(5 && +"&'53277#"&'##3366322>54#" " 0 6C!BX& $U8BQ85 =:(B/U!E:#2I6B7%R"B*,A\Z\4?;3Vl8l1Ul;0<0:U&5KPX@ J@ JYKPX@% cqK_zK _pLK)PX@0~ cqK_zKpK _xL@0~ c_zKpK _xLYY@('0.'5(5#"!  && +"&'53277#7##"&54>3236677332>54&#"U" 0 $ $V8AP$Ea=6A %W/ $Q?;1Tl<0<3Vl8l5@+  *JKPX@1  e  g_qK]rK_ tL@/g  e  g]rK_ tLY@21/-(&#"!  55 +"&'53267#?>32&&#"3#3#"&'53277#0 $( n]d 0F14"'. s sI*<4" 0 LJ;7 %!0EM C ,>3C?;I6ytS^"5D@ +*JKPX@2fg _zK  _xK_ tL@6fgrK _zK  _xK_ tLY@!76?=6D7D21/-(&#"! 55 +"&'532677>1##"&54>323733#"&'53277#2>54&#"1SY+>O  #V8AQ$Ea=6A!CY* <5" 0 {;"D9"-5(B0Q323>323~" 0 /H!#!H?3XG # J@1XrH 1@)7<7D)>D6) =I6DP!$0fPP-!%/jXc2"@77#D@*?;: "+}@JKPX@"~c_rKpL@&~crK_zKpLY@('! ++ +"&'53277#6654&#"#33>323W" 0 0G"%"ND1XrH 3C+>I7+ <I6DP!$/jXc2"D@/?;!%4@ JKPX@%g_rK _xKtL@)grK_zK _xKtLY@'&.,&4'4%%#%($ +336632#"&'53277#"&'#2>54#"H #X8AP93 <5" 0  6B1(B/U!E:#2d,A\[\4?;I6C 7%9/3Vl8l1Tl<0<:""KPX@JH@JYKPX@!~c_rKpL@%~crK_zKpLY@ "" +"&'53277#33>32&#"3" 0 1rH .:& 2N4 %, =I6Dc2 OBf5?;:"6K@H"# Jc_zK_xL'%  66 +"&'53277#"&'532654&'&&54632&&#"" 0 1IP*;>&49:cS1S@,,2#5"6! <I6?Q.&' D3DTF ) '(5%*Ai?;~+@! JKPX@&eg_qK_tL@$geg_tLY@('%# ++ +"&'53276632&&#"3#"&'53277#? ?HL" "* `*<4" 0 MJrPaI:89?;I6ytS^0:=@:JcrK^pL  +"&'53277#33>733" 0 >X  ^v <I6DF=@@'.?;:H@E J I~crKpL  +"&'53277#'#3733" 0 [ftYSfZ2 =I6DǕ?;:=@:Jc]rK]pL  +"&'53277!7#7!3" 0  ME <I6D<BGq?;0:&"(:KPX@ %&J@ %&JYKPX@! d_zK _pL@0~ drK_zKpK _xLY@*)42):*:#! (( +"&54677#7##"&54>323733326726676654&#"/9! #[8AP'F`:5B Cc-" "$H94,'B2,//  Ac,A]ZKg<8%S1I6[:2/<1Ul;660:&"x07F@#$ 45JKPX@+_qK_zK _xK_tL@)g_zK _xK_tLY@98A?8F9F20(&! 77 +"&5477667##"&54>32366776632&&#"32672>54&#"0?  $V8AP$Ea=6A 32##32673267326654&#".: ]e%Fc>LMAn;>&D($" "#%0Y//  <m]@}g>B;5U3 =GI1)!Y:!=]@Z2:;Jgc_zK_xL860.)'&$  == +"&54677#"&54675&&546632&&#"33#"32673267n.:$YYV=,:[13K @%2F.1=9)D)08.S"#" "//  >N8#?E)Yn'Ih@ " "//  ML=CJddHb9?I:~*n@'(JKPX@c_qK_xL@gc_xLY@%# ** +"&5467732676632&&#"3267.:  + WHL" "* VHC " "//  1APaI:8dP\:I7:&/KPX@ ,-J@ ,-JYKPX@drK`pL@&~drKpK`xLY@*($#"! // +"&54677#7##"&54673326677333267/9$ 3D+=IFYH &"NE1Wc." "//  Ac2"DA(G+ %0jX1I:-S@P"*+J~c]rK_xL(&  -- +"&54677326654&##77#7!#"&'32674.:%Q2)E+=@?fMCs4" "//  4+(#ADAO9VjCIGgG5)&f 3+TagGF5)&f 3+.agG"5)&f 3+QG5)&f 3+4DgG#5)&f 3+UGI5)&f 3+aG)5)&f 3+EgG+5)&f 3+YaG/5)&f 3+<G25)&f 3+VaG35)&f 3+$aG45)&f 3+$aGd5)&f 3+Gg5)&f 3+:G75)&f 3+Gn5)&f 3+GaGi5)&f 3+GIgG;5)&f 3+Y[aG:5)&f 3+gG<5)&f 3+GgG=5)&f 3+GaG>5)&f 3+TeG?5)&f 3+TGB5)&f 3+AgGL5)&f 3+-GM5)&f 3+R"G5)&f 3+:aGS5)&f 3+OaGT5)&f 3+[aG5)&f 3+_gGU5)&f 3+EaGV5)&f 3++RaG]5)&f 3++RaGZ5)&f 3++naG[5)&f 3+ ZaG\5)&f 3+TG5)&f 3+)M&%N3+&EN)QM&% lQ&E I)mM&%L 3+m&EL 3+Hx&&'zv#3+0&F'zv)|&'N3+0U&GN)Q|&' k0QU&G E)m|&'L 3+0mU&GL 3+)|(?@< J]oK]pK_tL(&!$,! +332#"'53254&'7#'326654&##)Lk )KF R%/GLhJiWUʌmu@!%-=45YM^dna0U,;@ *"!JKPX@'qK_zK _xK_tLK)PX@+qK_zKpK _xK_tL@+_zKpK _xK_tLYY@.-64-;.;&$  ,, +"&54>32366773#7##"'53254&'772>54&#"AP$Ea=6A %WH &))KF R%+!D:#/5(A0 \[Ii<7%">$c'P!%-=45PI1Tl<0<3Vl8l)3|&'J հ3+08U&GJ ڰ3+)*#&(:3+0q&H)*#&(:3+0q&H8*&( #:"&HJ ܰ3+>*&(Q 3+ E"&HQ 3+)*&('Mwz3+0&H'zM?)*&)N3+&IN3+HW&*L3+'&JLe*&+N3+ &KNN3+*Q&+ ~Q &K C*&+jf3+ &Kj3+&+z- &Kz*C&+M 3+5 &KM װ3+H}&, H&LQ 3+&,3+ng&')&.v3+ &NvO3+)Q&. NQ &N 3)m&.L 3+m &NL 3+)Q&/ 4Q&O )QW&/' 4L3+Q&O' L3+)m&/L 3+m&OL; 3+8&/ 8&O x(L&0N\3+3&PN`(QL&0 Q3"&P (&1N,3+&QN(Q&1 Q%&Q A(m&1L" 3+m%&QL 3+(8&1 X8%&Q H#&23+0 q&R H&23+0R&R H#&23+0q&R H#&23+0q&R )7&3v3+&Sv)7&3N3+&SN)9&5N3+&UN)Q9&5 PQ"&U )Q9W&5' PL3+Q&U' L4)m9&5L 3+m"&ULK 3+&6N3+&VNlQ&6 Q"&V 2&63+&V &63+J&VQ&6'N 3+Q&V&Nl ZM&7N3+,lX&WN@xx3+ZQM&7 ,Ql&W *mM&7L 3+ml&WL 3+8M&7 8l&W OQ&8j ڰ3+)Q&&XjM ڰ3+JH&8 P%H&&X +O8&8 b%8&&X O#&8z3+7&q&X#O&8z3+7&R&X#\&9Q\3+0&YQ\Q&9 /0Q&Y k&:Nq3+9&ZNkQ&: 9Q&Z a&;N3+&[Na&;j 3+&[j\Y&<N3+&\Nv=&=Ja3+&]JQ=&= -Q&] m=&=L 3+m&]L 3+m &KL 3+,R&Wjxx3+9/&ZO/&\O/0&&D &@N3+0;&{0;&{#0;&{0;&{0;&{ 0;&{0LV&{ 0;V&{&$ ΰ3+&$# ΰ3++e&$dM ΰ3++e&$dP ΰ3++e&$dM ΰ3++e'Y$d ΰ3++e$'X$d ΰ3++e$&$dX ΰ3+&n&#u&&&&=x&(N ΰ3+S|&(R# ΰ3+='(Q ΰ3+S'(V ΰ3+='(Q ΰ3+S'(V ΰ3+ & &# & && & OV& 2V&=&+M ΰ3+S&+Q# ΰ3+={'+Q ΰ3+S}'+V ΰ3+={'+Q ΰ3+Su'+V ΰ3+1{$'+P ΰ3+1{$'+P ΰ3+3 &3 &#3?&Y36&S3x&j3f&d3V&g+V&J=&,g ΰ3+S&#,n ΰ3+=f',Q ΰ3+Sh',V ΰ3+=',Q ΰ3+S}',V ΰ3+1~$',P ΰ3+1~$',P ΰ3+0&R0&R#0&R0&R0&R0&R=&2G ΰ3+S&2I# ΰ3+='2Q ΰ3+S'2V ΰ3+=t'2Q ΰ3+Sm'2V ΰ3+@&@&#@&@&@&@&@DV&@'V&R&#< ΰ3+R^'U< ΰ3+Rc'U< ΰ3+1x$'P< ΰ3+0&0&#0&E0&?0&V0&P0V&S0V&6=&Q ΰ3+I &S# ΰ3+='Q ΰ3+S'V ΰ3+='Q ΰ3+S|'V ΰ3+1S$'P ΰ3+1S$'P ΰ3+0;&{3+0;&{" &3+&" &3+ &"3&P3+3@&"k0&R3+0&R"@&3+@&"0&<3+0&"W0$;&{'0$;&{'#0$;&{&0$;&{&0$;&{& 0$;&{&0$LV&{& 0$;V&{&6&$&2 ΰ3+6&$&#2 ΰ3++&$d'M ΰ3++&$d'P ΰ3++&$d'M ΰ3++&$d'Y ΰ3++$&$d'X ΰ3++$&$d'X ΰ3+ &'b &'#b &&b &&b&&b && bOV&& b2V&&b=&+M& ΰ3+S&+Q&# ΰ3+=a'+'Q] ΰ3+Sc'+'V_ ΰ3+=a'+'Q] ΰ3+S['+'VW ΰ3+1a$'+'P] ΰ3+1a$'+'P] ΰ3+0$&'P0$&'#P0$&&EP0$&&?P0$&&VP0$&&PP0$V&&SP0$V&&6P=1&&Q- ΰ3+I3&S&#/ ΰ3+=''Q ΰ3+S''V ΰ3+=''Q ΰ3+S''V ΰ3+1|$''Px ΰ3+1|$''Px ΰ3+0;&{Ma0;&{Lp0$;&{&3+0$;"&{0$;&{&" 0;&{QQ0$;&{&QQ'&$M}3+W&$L3+&$2 ΰ3+&$" ΰ3+6&$2T%dD@Jt)+D7667&&54632KT%!6>$/ Q"T^Q3h-V+QdD@Fg h W _  O! '% +!+"!"! +D63232673#"&&#""&546323"&54632V.) 3R.*  ll## &&b3+ "&b &&"b0&QT0&&QTbCm&(C ΰ3+3h'"(> ΰ3+C&+B ΰ3+3'"+= ΰ3+*&+TdDKPXJKPXJJYYKPX@W_OKPX@U_O@U]MYY@)+D7667&&546327&&'73K!U T%!6>#Q "S TdDKPXJKPXJJYYKPX@ tKPX@t@tYY@)+D7667&&54632756673KY \:T%!6> N% "O#JCV#UdD@JJ~g hW_ O##"!"! +D63232673#"&&#"7667&&54632V.) 3R.* Fll! -13i&M3Z&L,k &[3+3w &g&Q.V&c&,M3+W&,L3+C&,\ ΰ3+3'",p ΰ3+TadDKPXJJYKPX@W]M@U]MY@ %+D&&54632&&'73P',!"-a!U T0%%4!#Q "S TXdDKPX@ J@ JYKPX@ t@tY@ %+D&&5463256673P',!"- \:T0%%4! N% "O#JCV%GdD@<g hW_O%$ "!"! +D63232673#"&&#"&&54632V.) 3R.* +)ll) ) @&MY@&Lh@ &3+@ & & &#@%&QI@*V&\Y&<Mc3+\YW&<Lr3+S'< ΰ3+G'<" ΰ3+S&3R# ΰ3+c !kdDK"PX@UW_O@We_OY@ !!    +D&&'73"&546323"&54632m!U   j#S "U ""c !dD@ JKPX@nW`PK"PX@W`P@#~W`PYY@ !!    +D56673"&546323"&54632;!a="i  j U# Q#""6^ &dD@U]M +D&&'73q V ^#Q "S 0$&&<P3+0$&P0$&&"WP0&Q0$&'QPS'2T ΰ3+8'"2' ΰ3+S&` ΰ3+*&4" ΰ3+&@^ &dD@Jt +D56673@ ];^ N% "O#T dD@ Gt%+D&&54632',!"-T0%%4!{t@U]M +3*{m!@  Ht +'7'77'>RRSSRR>>RQSSQR>d{ 4@1JHU]M  +3'7'7#@ll@y=kk=H+{ 4@1JHU]M  +#'73yAmmA=kk=!;A@U]M +7!;AII&__nk$@!]oL    +&&'73#&&'73DUU65 ;965 ;9{Z$@!U]M +3#迅'H,{Z$@!U]M +#53'!{t&@#U]M +#53#__b{t UKPX@e]rL@eU]MY@  +#53#3#__b'&{t UKPX@e]rL@eU]MY@  +#535#53#__b&'Du&Re;q[ #/K)PX@+ g_wK _rK_ xL@) g g_wK_ xLY@#%$ +)$/%/##    +"&54632"&54632"&54632"&54632$$%%$$%%$$%%$$%%M $%%$ $&&$ $%%$ $&&$ {t\KPX@e]rL@"eU]MY@ +#535#53#3#______b&''&{t WKPX@e]rL@eU]MY@  +#53#35#__bb{u@Jt +'3``}{u@Ht +#7#`uu`b{u @ Ht +'77'`uu`>>>sYkkY777{t YKPX@e]rL@!eU]MY@  +#535#533#_____b&'&eY1@.gW_O  +"&54>32'2>54#"9>+G3y-G,*8)ML-gZ:7lY5>,IV)Q*ER),/jS'K@H  JggW_O" ''  +"&546632&&#"36632'2654&#"5C3kT# !32&&#"37!#3#30ZZ"@^<$=3(<)36+"VYAtZ4 D -J[-95BBC9S(19y@30'$JK-PX@hgqL@%hW_OY@.+((A +7&5466773623273&'667##7&&'7&&#"'9=F/KPX@  ,!-J@   ,!-JYKPX@g_wK`xL@&~g_wK`xLY@*)%# // +"&54>32&&#"336632&&#"267"nv8UsJ]F$=)EeB &&I< @/  (8" 'G"$I @v]6(JCoC@[\M"3J0N, N35@ ' JK PX@(n_rK pK_rLKPX@'_rK pK_rL@$_zK pK]rLYY@55&&#% +333>32736632#6654&#"#7#7654&#"rH 1@)-9 F\+>DEYH!#!H?3X#G # J@1c2",(̏ D@*P!$0fP-!%/jXD#'+Y@VJf   eoK  p L+*'&%$#"!  +3#737#73733733#3###3'#3'#37#37#!6HHHH4f!o4N4HHHH6e"m6a'0W ;=Y"(@R@@R@yRRRt! =@- . JKPX@+e]oK _zK]  pL@/e]oK _zK pK_ xLY@20+)== ! +332##326654&##"&'532654&'&&54632&&#"KF?\3Y>N=0B!1/7>+AD$24(,*XF*F3""(**`ʵIv's,G)=0zQ.&+ @3DTE ) )&3%LY"&*.14h@e"J  f   eoK p L4310.-,+*)('&%$#!  +3#737#73733733733#3###3737#37#37#7#7#I QF9/ Nb_T cVOZ-7CM]Y i`%4MsT9x7P0!."@R@@R@әRRRRRٕ2@/U]oK pL +3#7333733###)GQPBYClʺbC<NB:!BN1+M2@/ J]oKpL +37'77'77#7!#77!sr/"xw.G5caG5bOOK5faJ5f/<@: *JKPX@:~  g_wK` pK _ pK tL@7~  g_wK^ pK _xK tLY@10750<1<//(&!%&! +33326654&&#"'6632#6632#"&'#2654&#".{Vk eG:lI+`$$n:dQgtYR]QPL*XF1  -C$"(4 (B ZbEg9GL^ePcR>.[=5-,D;#(384''3C@@" J_wKpK_tL/-'' +"&'5326654&''#7&&54632>54#"" !"( /UP0n \G5>!4=Q5$,3J)B%0bAć{Kc.}rB5,ZYS#]0`rL"SZ,9Ta#@#Y!'-T@Q e e  g ]oK p L-,*('&#"!! +3#737#737323#3###3&##3667#3267#$OP PP P=L'I < < E#a91y j@- -/5Zw6Y6&@(636LB773&&'3#'2677#_j3^PA5P&""L,9L8e9 yGn?;&=.vPLm^}L$$NF5 feEe U"J@GJ f   eoK  p L"!  +#7#737#7333#3##'#3&&'3'#9E [,u _! u Y DX~`*g @R@ @R@-Z&&WR)5^@[2 3 Je   e_wK _ xL0.)('&#"!  55 +"&5467#73667!7!654&#"'66323#3!3267_r /Z"^7h :2&G#'^1Yf0WR6B:4U%"[ ZS/@&@ 7*1L`I2&@'@).6NHx$q@ JK-PX@ oK]qK`xL@goK`xLY@ +7&&54>773&&'667'`j,XZAO;$:%z&H#%K2 y?\=APKqNV%% I  M G6 Hk|>Lc\D>@;   geW_O"" +373267#73&&##7!#3# 1RX FI$  #nf 4dQF082@46@@ 7'@(I0j6k pp3+1&1:X@U ~gge W _ O'':842'1'10.*(&&  +".54>32'2>54.#"'32##532654&##Pc66cPLe96cP@pV0.SqDZP.Sr>RLV>RF',)+E 6cPPc66cPPc65.UrEArV1Q[ArV1_EDCL%*(#! :@+ , JK PX@:   ~ ~f  gW_ OK PX@3   ~f  gW_  O@:   ~ ~f  gW_ OYY@$0.)'::   +333#'#73'&&5"&'732654&'&&54632&&#"XN뇿@.< n6QN   45&*"'C=7-!&%T6rXbb^  ; .0;5 *#72 .@+eU]M +#737#7373%/XrHHO'?'{uma3+a3++)'|'t~u`~3+`3+"!<@9f eoK ]  p L +37#737#7333#3#!)/PPPP7Y8GYGGYGP `K)PX@ f eqK  p L@ f e  p LY@ +37#737#7333#3#5EEEEB/ >BP)7=@:g]oK]rK pL$! +3#73732##32654&##3#)`OP&sc@;K>`qCDLNgSybRY;5fN9'I@F Je]oKpK_tL'%!  +"&546732##326732654&##1EQavbfuO  &:Nd[ECL1; Mr' LcWE=00/'3KPX@1 J@1 JYKPX@"qK_zK_xLK)PX@*qKrK_zKpK_xL@*rK_zKpK_xLYY@)($"  +"'#7&&54>3273373#7#'&#"26676654' dFo#'F`:#sE  CrF #[o 'B2T$H9 P;Kg<Sc,A0u1Ul6[:2*0"%@ $ JK PX@*pqK ]rK_ xLK)PX@+~qK ]rK_ xL@( ]rK_ xLYY@###%#% "" +"&'#&547#?33733#32677,nE =SZ>4ApEp" 2 L  '#(&ozCC ߘ*DF"`JK)PX@aqK_zKpL@a_zKpLY@""(% +7#654&#"#33>323)QG !%"OC1XX+4C+>I6P8P-!%/jXA 2"ED()D.@+ JaoKpL +7##3733( SlʇG8Z=BF W JK)PX@aqKrKpL@arKpLY@ +7#'#3333L) qH(YYAhlI89PWF= +@(]oK]pL  +7!7!7!!3) CQ9F4PGF +@(]rK]pL  +7!7#7!!) ME6<BGqH) JKPX@_wK_xLKPX@oK_wK_xL@!oK_wKpK_xLYY@" ))  +"&&54>32373#7#'266776654&#"1Hh9,XZJZ$IG#_9E\6  XIDc?$F C{RVOC,d6c/>NAd5k4FREp@:Z40"O@ JKPX@_rKpL@rK_zKpLY@ #, +33366776632&#"n>X  %}1($ 63S -& @6\k,W@ 'JK(PX@]oKpL@oK_wKpLY@,,$+ +333667336676632&#"#&&5#X([ 8/ #  ^ M)X('e,^/f%(Z+W,2G%!H"?179 "-W@ 'JKPX@]rKpL@rK_zKpLY@--$+ +3336673366776632&#"#&&7#OV _ &T5#!   m6[L5/736654&#"}%/!EA78V<  !! ^Y -C.GM=,A[B<@>(Q 8&5! )#@ foKpL +33!!)Y@FO#@ frKpL +333#rX03J0w!%,@)J_zKxL#! +"&54676632'>54&#"!rSQ8DA2APNHI$P32P^_VBmBK" }""Km$@!W_O+663"RwbGKa[G8=TK$@!W_O+&HC rpK7>G_]_\ .@+JW_O %"+6632&&#"!oD3U)*V57C_A? C $L` 0@-JW_O   +"&'7326734U(+U48B6'k` C "C=0;j&{X0;j&{W0;j&{W0;j&{W0;&{N0;&{N0;&{N0;&{N3Tj&3j&3Sj&3tj&3f&3&3f&3v&@j&P@j&O@j&O@j&O@&F@#&F@&F@&F3k&3k&3x&3x&@&F@&F@&F@&F)C&g@JKPX@c_#K$L@c#K_*K$LY@&&+"&'732676654&#"#336632^+ $+B b7?AeEOYJ"sI[c anM1=#7=K|Gu:F]\$+3eX(C Q-| JKPX@*~_#K_#K_+L@(~_*K]#K_+LY@%# --+"&54673326776654&#"#3366324spWHG\\-7?AfEXKJ"sI[c .Gw g_)&8@\Y#7=JzGbu:F]\$+Nr=VjKPXJKPXJJYYKPX@gU^NKPX@!gU^N@(~eU^NYY@) +7667&&546327&&'737!K!U  %!6>#Q "S lGGVjKPXJKPXJJYYKPX@U^NKPX@#U^N@'U^NYY@) +7667&&546327566737!KY \: %!6> O$ "O#lGGVjrKPXJJYKPX@eU^N@eU^NY@%+&&54632&&'737!',!"-a!U  0%%4!#Q "S lGGVj|KPX@ J@ JYKPX@U^N@!U^NY@%+&&54632566737! ',!"- \: 0%%4! N% "O#lGG^'NYv^J&NjK?j3+mR ^KPX@g]oL@gU]MY@     +"&546323"&546327!  ""GG]R ,G@D  g c_oL ,,*(&$"!     +"&546323"&5463266323273#"&&#"  6.+$%3A))$ ""5=14>]q D@AJ d_oL    +5667366323273#"&&#"4e196.+$%3A))$  <" ..5=14>mq [JKPX@]oL@U^NY@  +&&'537!a9[ $ K !C! sGGmq [JKPX@]oL@U^NY@    +566737!4e19  F" 33rGGY)n JK$PX@ _oL@!W` PY@%#)) +&&'536673"&546323"&546321 25(>985 &P 0/ 59""YD^K$PX@e_oL@eW_OY@  +7!"&546323"&54632!  GG""]D;@8e c_oL  +7!66323273#"&&#"=5 7.*$%3A))% GG5=14>^D*@'e_oL  +7!"&54632$ .$GG'-# /@,eW_O  +7!"&54632 .$GG'-#Z #/@ JK&PX@)  h  _ 5L@/  h W `  PY@&%$ +)$/%/##   +56673"&5332673"&546323"&54632(_A" B94$,..7T A 0 5p9DH5w##Z #/@ JK&PX@*~ e g  _ 5L@0~ e g W `  PY@&%$ +)$/%/##   +&&'53"&5332673"&546323"&546323)W>B94$,..7T A5 1 p=@H5w##Y %JK$PX@# f  _5L@) fW_  OY@" !%%  +566737!"&546323"&54632 (_A"  A 0 5eGG""Y %JK$PX@!e e  _5L@'e eW_  OY@" !%%  +&&'537!"&546323"&546323)W  A5 1 eGG""O(|KPX@ J@ JYKPX@d5L@d5LY@&%#!((% +&&5463256673"&'332673',!"- \:I;I6+8N0%%4! N% "O#;=7AO(rKPXJJYKPX@ed5L@ed5LY@&%#!((% +&&54632&&'73"&'332673 ',!"-a!U ;I6+8N0%%4!#Q "S ;=7AO'KPXJKPXJJYYKPX@  d5LKPX@!  d5L@%  d5LYY@%$" '') +7667&&54632756673"&'332673KY \:\;I6+8N%!6> N% "O#;=7AO'KPXJKPXJJYYKPX@ g d5LKPX@ g d5L@&~ e d5LYY@%$" '') +7667&&546327&&'73"&'332673K!U ;I6+8N%!6>#Q "S ;=7A%KPX@J@JIYKPX@]#K]$L@]#K$K_+LY@%%+"&'532667>733### -)%&%ab^fb#a "!4L NA<.x<5z1uyo+?\1!'@ JK"PX@_%K]$LK-PX@_%K$K_+L@!%K_%K$K_+LYY@!''+"&'532667>3273#'#'&&#"  .)*5N:$Rf{Y[fU *2*5IL*\KCbA '^USr;*O:@7  Jg]#K$L"!+33273###32654&##*tbb^Q@;K>`qCDLgSZCbRY;5!,@  JKPX@]%K _$K'LKPX@#]%K$K _+K'L@'%K_,K$K _+K'LYY@&$,,$$ +33663273#'#"&'#2>54#"H #U;BOfYaD\96B1(B/U!E:#2d,AXV=^67%9/3Vl8l1Tl<0<S J@GJe e ]#K] $L  ' +#&&54663!!3!!!#3#"'(6JzI/7?lG9W2P/BFJ@Sd-ONO(t ?66C"7AJ@!  45JKPX@+g   g  _%K_ +LKPX@5g   g _%K _%K_ +L@3g   g _,K ]%K_ +LYY@#CBFDBJCJA?:820+)$"  77+"&&55#"#"&'53267667&&54633632##3267326654&#"37#"=S+E/,%3'  !*"0-jj :ILMAn<=%N&I#%yX%t5:: 5[9..(8F#. 2 B+Ia3=B;5U3 @GK91)!3,-#*-@*  J#K$L+337'773'#*YJL9FklM:FfJi+_nh+_b-@*  J%K$L+337'773'#qW7;06Jn=08Ui8L%EML'HU ;KPX@1#J"I@1#J"IYKPX@)g]#K_$K_ 'LK1PX@-g]#K$K_+K_ 'L@*g c]#K$K_+LYY@530/'% ;; +"&'532>54&#"###"&'532667>7!6632A)?:!326632!.-8O+<7 0Xe *2*5I;  -**5N:"@1!3R1@c R CuJDM'^USr;L*\KCbA 1aHBz_7*)@ JK1PX@(f  g#K$K_ 'L@%f  g c#K$LY@#!)) +"&'532>54&#"#!#3!36632b(@;!323(KG"%"ND1XrH 3C+>I6L8P!$/jXc2"D@/(E\4@1Jga#K$L#%+7#654&#"#366323(V:)-4Y0IZZ=$`7PR)R:"$HC F"6@3Ja_,K$L""(%+7#654&#"#33>323(KG !%"OC1XX+4C+>I4J8P-!%/jXA 2"ED(cV EKPX@o]#L@]#LY@ +'7!#'##'#: [# [#cT T2222 ?@< J~oK`xL  +"&'??37726673%6Ejji j'Z 32h0Q!!S)/S2HH  74UbbXbHxW[fGWCx R#G54?A%'cU2LvB\J ^NHk;W$X ʰ3+>j&3@0J H_rK_xL&& +"&&54667326654&#"76632.Pk5[]:B6C^2:2) *6Q,)Qv AsM~0CД3Q0GsCAFF 7\9Dy^6",@) JoK`xL  +"&5466736673'32667'w15+]Lz 9OK-2>9&XK*=>CC=3/V6a*8@?C<1-T6T %@"c]oL  +"&546633#"33(dp=uVgV_ZQD  e\KsAPdTAE2*@>@;JfcoKpL  +"&'53267!#3!3G'&,=CFYY@:@XcL:G>.HlfZ&7 3+!F$H@E !"J~_wK_xL $$ +"&&546675'7732&&'#"3267 Qg0AtK ;\5)+B+ +i|HF2i/.e 1R2M_1 9x&=\DHT4<R;@8JeoK_xL  +"&'532677!7337667#   ' wepL    M(//QWV!:u$ ," +K(PX@J@JYK(PX@%~ foK_ xL@)~ foK pK_xLY@!!!+!+'&%# +7733##"&'532677'37667#7>73weWLG   '    ' W6QMN&WVM(//N:u$ , 7: %T"&$$#.*JKPX@!f_oK _xLKPX@%foK_wK _xL@)foK_wKpK _xLYY@%$##&# +#36632#"&'#%2>54&#"%31'&&'#9^-gy-YWu tg<^B#ZN<`D$\ &ʸWl]}H|lE?)T} *I@F& JfoKpK_tL"!    +"&'532677'##33>733'&&'# ""6*tb^0 cS)Y(N)-Nd ME;=LJ/M#&O-))3@0 JfoKpL +3#73733#73#)tTTY__'>lʩbS<&OUUOBpZ=)-@*  JoKpL +33737#''7')YJ>l;stWbArt4S<Bƌ?6@ͣ?6@=);@8 JfoKpL +3#73733#737#''7')tTTY__'>l;stWbArt4S<&OUUOBƌ?6@ͣ?6@=x (@%JoK^pL  +37137!Xa 323##"&547"!654&2667!+^\y0)Ya`>cDZ@cBb\JF\VFaY 326632#"&'2654&#"2>54&#"Q1^TI[ D;AJ!G:$0YL.3"%(;(<^B#ZN<`D$\ \I()#.K6.V8 WsBD/#*`qCDLuK gSyWKubRY;5z#8@5~g]oKpL#!$!%$! +3#"#&54663332##32654&##l;,,I#OALtb@;K>`qCDL9) ,O1hgSybRY;5j,5KPX@ JKPX@ J@ JYYKPX@g_wK pLKPX@'g_wK_wK pL@%g_wK]oK pLYY@53/-,,$!*%*! +!#"&546776654#"566323332##32654&##;NP'  60MLtb@;K>`qCDL?E,E -AD3 @ChgSybRY;5@V,<@9J_wK_xL&$,,&% +'7'"#"&54>327#''2>54&#"1h(1^Ty:pO!pdAr#V=_A"ZN<`D$\5)<\Ig,-4(V432%66544#''2>54&#"(.1^Ty:pO>-#%8BCLLr$U=_A"ZN<`D$\'OE\Ig(&8&!)704L0e654&#"'66323733##7 AR%'' @'+.V=FW$M=VTTV<:K>% ( ;%*K=0MO4tKL@  JKPX@oKpL@oKoKpKpLY@  +73336673#'|4X"5&c_R ]N)T$$O/6k0?@<& JoKpK_tL,+"!00 +"&'5326677&&5##33667336673~ !%%) \X([ _4JN "$T!K?17M)X('e,^/f%&Z,(E+)pK PX@(fgoK ]rK pL@&f  egoK pLY@$! +3#73733#32##732654&##)PO Y cb Hsd?(?_rCDM[A..A4fTytRX<4!=@:  fgeoK pL$! +37#73332##3#732654&##)PO|YHsd? bb6?_oCDM@AInfTt{2A@MS<4%@" JoKtL +77'736673M UL VM (o#M 1022#C" @,+1 'D@AJg_wK_xL%#  +"&'732677#7376632##32654&#"& $*71 POANsY42k3/41 L9CJ4Lb@AUVg^7%!27R*9@6Jg_wK_xL**&%%) +52>54&&#"3267#"&54>32}MkE'2+1B"21"3OS=]?al1[ K/N]_P.K,7W28@A jO4cO/~p9lBH!QK-PX@]oK_xL@g_xLY@!!  +"&54667#7!2'2>54&#"Q#G7Gy-YT<^B#ZN<`D$\ Jy)N]}HO7&&5467#7!#"'326654&';^5*FW-x05!$8?BwB>.E'*%2]< -U;=\B+ .,JJ%"%(jBSzD=>5Z81C#5[+0Q ʰ3+`/ ʰ3+;*e@ JKPX@_oK_xL@ oK_wK_xLY@***%*$ +3366323267#"&54676654#"E&kCRUA0  )75Bh\k^26NL2 8E:24'eyj1aKPX@ J H@ JYKPX@_oL@oK_wLY@ 3$ +336632&&#"@ ^J %Kpf3=QekK5@2J]oK_xL  +"&&54667#7!#"3267LZq6@lB^j^]VP-.M FtChiNN_nXjNp %@"foKpL  +!!3!3FmQY@:@XM}.6(7-@*JaoKpL +33366733#7##(_ TU?;W+G R$o2PDO$|x%E@B  Je_wK_xL%%%$$$ +73>32&&#"3#3267#"&547/=]|OdJ$C,LpG]R)O&'O8w;LAx^6(JFtDL[i N)E@BJe_wK_xL&%$#  )) +"&54777>32&&#"%32677#73W 3 6fWAb,"&U3S$ 'R@&?.L-d ;7 > UINdVm=o>E7Y4 N )@& JoKpL +37737737#)?[ [KYG-l bD<)>c0<=9T2"-@*"!  JoKpL +3773737#7'#36677(=R RM_t=U9P PQ_|@RyM #  >l>KDO$o20)uB@? Je]oKpL% +3?327##7&&#3267)[X X/= 1RVvbfu>D>NS[  =ܛ >A_'|$3)>3&/=@:*('J_wK_xL" // +"&'5326654&'&'77&&546632&&#"%4O#S6-L.5A  @k@8U&"N)>N() ,5 X:.(9%1>@-B]0L@:).<>(G;knG6@3~e]oKpL%$! +3#"#&546633!3#!",,I$O@z@:@XXFF9* *O3.6ML=-&6{@ JKPX@ g_wK_tL@$goK_wK_tLY@('0.'6(6#" && +"&'532677>1##"&54>323732>54&#"2 !b6 &  ).1),$DQD_A87B(I3`M%?@<Je]oK]pKtL%#,! +32##326654&##32654&## ʹcqXI1BG|O3u,J,JpNVI[ H32##326732654&'3#"&'326654&#"]e%Fc>LMAn;>&D(=2?#' B#%0Y m]@}g>B;5U3 =G$0( ?R 91)!Y""9[@X"0)!3*Jge_zK_xL.,'% 99 +"&'532654&##7326654&#"'663273267#"&'+SS5?K)9FP 8/Je"!(4M@J-# Jg_zK_xL*))4*4! (( +"&546774&#"5663273267#"&''2667JL32&&#"7@ - L@k+/G16"(. Q{YLJ8:h"48EM C ,>+4AYS^$w@JKPX@ e_qK_tL@ge_tLY@!  $$ +"&'53267#737>32&&#"3#@ - v??/G16"(. xLJ8:0HEM C ,>HS^0"1@@= Jg_zK_xL0.*%""  +"&54>32'26654&&#"66;6654&#"]k"CeB^j"Cd=0="0(7Q, = o_>zd=o`=zd=60$qK9%K}K#+/hz{ZFM̠)&f 3+$KPX@ J@ JYKPX@_qK_pLKPX@_qKpK_pLK1PX@gpK_pL@~g_pLYYY@$$ +3"&'7326676654&#"'6632#7#2N4 >"&+GE vH .:OBf5&  EA4 1Q0B|X7KS6?T)WL% q>" 2 4A '#(&oz@/U=MwEQ2V5HB< ! C !$L@I !"Jg_zK_tL $$ +"&&54667'7732&&##"3267F`1L}I ,J =! 7Fi73& %* }fb}~K!   # W7H$1QP6NVPJ>:u$* 9; %W$( )@&e]rKpL  +3!#3#(r-$/GH %7@4J_zK_xL%% +"&'532654&'&&546632&&#"(BA-5:"/,:/T6Q?:*1!$2>l T0+(>5/H($F-& >7OX0R");M@ &#  JKPX@  _zK ` xL@(rK  _zKpK ` xLY@'=<+*GE323736632373#7##"&'#7#'26676654&#"!26676654&#":G'E]60:!8A/.-  CrF  1;&2; (R$E8+&%@1$`$E8+&%@1$ ^XLh;8%SE#,8%Sc2&&Bc1323736632#"&'#7#'26676654&#"%26654&&#":G'E]60:!8H-D\!A`>1<; (R$E8+&%@1$w-L/(!%>- ^XLh;8%SE#,gk=zd=$&Bc1z@ )&  JKPX@ ]rK ` xL@%rK _zKpK ` xLY@.-86->.>('$" ,, +"&54>323733266773#7##"&'#7#'26676654&#":G'E]60:!D<#$JC1WrH )8$&: ; (R$E8+&%@1$ ^XLh;8%S2 '10jXc2"&&Bc1323733>73#5#'26676654&#":G'E]60:!CA  $$ ay(R$E8+&%@1$ ^XLh;8%S4L">;(c132373373#5#'26676654&#"%3>77#:G'E]60:!C,fzay(R$E8+&%@1$ $$ N  ^XLh;8%Sc1;%)4L0(",>@"  JKPX@#]rK _xK_tLKPX@'rK_zK _xK_tL@'rK_zK _xK_tLYY@.-86->.>(',, +"&'5326775##"&54>323733667326676654&#" !)9)(R7:G'E]60:!C@1a[3B'$E8+&%@1$G5-Fc1<^XLh;8%S'=O$}&<"/6\:1/<1Tl;76" _JK)PX@fqKrKpL@frKpLY@ +3#73733#33#'IHYhaqH(\BZZBPW9 P@  JK)PX@qKrKpL@rKpLY@  +33337#''7'YAh,hmKa3UZ'H(PW^C1Fq71:V9 h@ JK)PX@fqKrKpL@frKpLY@ +3#73733#337#''7'IHYh,hmKa3UZ'H(\BZZBPW^C1Fq71:V9i1 ;@ JK)PX@ qKpL@ ]pLY@  +371371tT_NXC_ [!o!0SH HK)PX@fqKpL@fpLY@  +3#73733#E FXF G^@ZZ@<!*E@B e _zK _xL#"&%"*#*!!## +'73>323##"&547%"!654&&267! ;CgD^j93CjH]k k@o` BBsGo_ZF9%k]V>D0#(7KPX@ J@ JYKPX@" g_zK _xL@, g_zK_zK _xLY@*)20)7*7$"((  +"&54>326632#"&'2654&#"26654&&#"]k"CeBd3<'5:H@ Bl&&/O/0(7Q,=o_>zd=>$<88P  ExJh,.!C~X9%K}K>D0v l!. JKPX@' f _rK _xKtL@+ frK _zK _xKtLY@! (& .!.&$ +73336632#"&'#3##7%2>54#"JHH #X8AP$Da<6BW(B/U!E:#2Agd,A\[Ji<7%9FA``3Vl8l1Tl<0<h!*9@ 7JKPX@- ~g_rK _xKtL@1 ~grK_zK _xKtLY@,+31+9,9*)2&$& +4&546632336632#"&'#&&#"%2>54#"9"J<VH #X8AP$Da<1H  W> $(J(B/U%L<BB+K/d,A\[Ji<"A%3+3Vl8lAT(V^ 1g&!Tl3U.L4 XB-Y,C61&K-!,<d/>\[Mf:QSl@f0&" / JKPX@' e _zK _xKtL@+ erK _zK _xKtLY@"!*(!/"/ &( +737>1##"&54>323733##7'2>54&#" #V8AQ$Eb=5A!BIHW_!D9#.4(B0@C5),A\[Ii=8%S@aa2Ul:,@3Vl8l0",;|@,JKPX@$g_zK_xKtL@(grK_zK_xKtLY@.-64-;.;&( +'%661##"&54>323737>544#7'2>54&#"u #V8AQ$Eb=5A!BV]$'#%8B4.?W+v!D9#.4(B0#%,A\[Ii=8%SlR +$!)8/08(qc2Ul:,@3Vl8l9/@,Je]rKpL!$! +!'#732654&##'32MhRD?1.NHRY=EXE:1*HHD;Wu""B@?  J~_zK^pL""'%' +#77>54#"'66323733##7 ,F'@4*&I7CK&M8cUTTT>)D?"?=$E>-KN2[I0@- JrKpL +7333>73#'!},X4  ^sU]}]O7/ @@'I9/?@<& JrKpK_tL,+"!// +"&'532677&65##33>7336673 !.1 hV _ (u]XG6/23(J/%?37Y99/%a&&g7{=F"2 JK)PX@+fqK _zK _xK tL@+f _zK _xK tLY@$#+)#2$2""&' +#73733#36632#"&'#2>54#"L LX  $V8BP$C`=6D1(B/U#J< 1N?[[?+J,A]ZIi=7%:/3Vl8l7_<.-0<"2 JK)PX@+ eqK _zK `xKtL@+ e _zK `xKtLY@$#+)#2$2""&' +73336632#"&'#3##7%2>54#"Q PX& $V8BP$C`=6D W(B/U#J< 1>I+J,A]ZIi=7%:H>aa3Vl8l7_<.-0<!@JrKtL +36673Y<  &jV2 8;"'s@ JKPX@"_wK_rK_tL@ g_wK_tLY@%#  +"&'53267#7376632##32654&#")$ &0 knn TRDDfe3nX103/ J28G/JbH;GXNY3(!187 (9@6Jg_zK_tL(('$%& +52>54&#"3267#"&&54>32[;wd=<2*A-Z$-(.K."Bb@[r$>_H.nNH*GW-n C*WD5lY6pw3x|rZ50c!0@  JKPX@-  ~qK _zK ^ pLKPX@9  ~qK _zK ^pK ` xLK)PX@5  ~qK _zK^pK  _ xL@5  ~ _zK^pK  _ xLYYY@!#"+)"0#0 !! +"&54>323667733733##7#7#'2>54&#"AP$Ea=6A %W\A\m\B]K $V!D:#/5(A0 \[Ii<7%">$PHc-@I1Tl<0<3Vl8lcb ZK)PX@ ~qK^pL@^pLY@  +333733##7X2\A[l\B\PHc":  JKPX@*  ~  _rK^ pL@.  ~rK  _zK^ pLY@::64/.*(&% +333>323>323733##7#6654&#"#654&#"rH 1@)7<7D)>D6%[B\m\A\cH!#!H?3XG # J@1c2"@77#D@*HP!$0fPP-!%/jXc%&yJKPX@'~_rK^ pL@+~rK_zK^ pLY@&&%1$ +3>323733##7##6654#"sN 4C,@J7\A\m\A\^HD"MC2c3"G@0HT@/hXKPX@  JK(PX@  J@  JYYKPX@ ~oK_rKpLKPX@ ~_rKpLK(PX@$~zK_rKpL@$~rK_zKpLYYY@ +3336773&&##7rG KfM?M(2b@a=[6dgS i\c!L@I J  ~  e ]rK^ pL!! +3323733##7#'#32654&##rNX)D*CM\B\m\B\aXn-4OH:IkacYc>" 283.( 4A '#(&oz#F]:=AS- ! C #5*/,1@.# J_wK]pL+)-! +3732654&''7&&54632#76654&#"KR!2"0`NLG 5!),7iJ" +)"'(-HH>*:>"O6P\J=+C;'T8<`81#9 'A&*8/'0@- Hg_xL#!'' +"&54>3&&'7'2>54&#"^fCkM1CD$!J%Gf9.C+21DU'p`9jU2.`2$6&!L P->sZ5I.IS%8C=f?"yKPX@ J H@  JYKPX@_rK^pKtL@ rK_zK^pKtLY@$% +33>32&#"3#H .:& 2N4 %2c2 OBf5E:%<@9Jgg]pL %%* +377'&&54>32!26654&#" i1/AfFdoCzRX qBP%C:@L#@@N%a>4eR1j[UEBBj>b5?A>c8=?@rKtL +3X '",e@ JKPX@_rK_xKtL@ rK_zK_xKtLY@,,*%*% +33>323267#"&54776654&#"H 3C+>I'*  '50*"%"NDcc2"D@/ /B4-!$0jW)"cKPX@ J H@  JYKPX@_rKtL@rK_zKtLY@ $% +33>32&#"H .:& 2N4 ec2 OBf5/5@2J]rK_xL  +"&7467#7!#"3267ecYM}k5Bc@ >6"A D p[^$HH/N`2@BK&p$1@!"JK)PX@!g h cqL@)g hW_ OY@&%-+%1&1 $$ +"&54677#"&5463233#3267376654#"1A+98;4 :XUaa,  ,a&e2<':)/? uD!C  (<%hJKPX@ ~_rKpL@$~rK_zKpLY@%$ +3>323#7#6654#"sN 4C,@J709R)32&&#"3#3267#"&5475JjC#E7 =[>8#?E)YnACj= I ZFA=CJddN",6?@;1, JKPX@"_zK_xK_tL@&rK_zK_xK_tLY@87.-7?8?-6.6*%'% +'77>323737#"&'532677>1##"&545"%654&2667 0 DlG6A!C$KKH{`1SY+>O  #V8AQ)C1  -%J:? QQ8%S?q`Q327#"756654,F F:N 4C,@JA @@^:1C??c3"G@ ? 0$OB/@"kKPX@JH@JYKPX@_rKpL@rK_zKpLY@ $% +377733>32&#"7,R S8H .:& +E3 0? c2 O1P--?."+=@:&$# J_zK_xL++ +"&'532654&'77&&54632&&#"71IP*;>%/ xcS1S@,,2 / }q Q.&&*?4"DTF ) %+?3%QT6|1! O 3+2'0@-JHg_xL!'' +"&54>7326654&#"76632al,aoSuL*5@2E$+* 4I'?q skI-7+lsq`!@J=g>6>J3V5VN",@) JrK_tL +"&546733>73'326670.vgIX# ^%=85 !1%FY2+Df!LE@D&@A_FHI0'&"&D 0'"&HP&O r%&Q '&L 7'&&XPD@'(98JKPX@'_qK ]rK _ tL@%g ]rK _ tLY@!A@=;75210/,*%# DD +"&'53267#?>32&&#"37>32&&#"3##"'53267#0 - n]d 0F14"'. /E24!(- tspKI& , npLJ1A %!0EM C ,>34DM C ,>3CS^ J3? S^DOS@'( 98JKPX@9_qK _wK ]rKpK _ tLK-PX@7 g _wK ]rKpK _ tL@5 g  g ]rKpK _ tLYY@1PPFEPSPSRQLJEOFOA@=;75210/,*%# DD +"&'53267#?>32&&#"37>32&&#"3##"'53267#"&5463230 - n]d 0F14"'. /E24!(- tspKI& , npL;-$sXsJ1A %!0EM C ,>34DM C ,>3CS^ J3? S^a&-#DHK-PX@'(98J@' (98JYKPX@._ qK ]rKpK _ tLK-PX@, g ]rKpK _ tL@3  ~g ]rKpK _ tLYY@)EEEHEHGFA@=;75210/,*%# DD +"&'53267#?>32&&#"37>32&&#"3##"'53267#%30 - n]d 0F14"'. /E24!(- tspKI& , npLXJ1A %!0EM C ,>34DM C ,>3CS^ J3? S^L&IL>S&IO>8@/!"JKPX@+_qK]rK_xK_ tL@)g]rK_xK_ tLY@31,+&$88 +"&'53267>323#3267#"&547#?&&#"@ - ;M*4K3>! 26J =S] 7+5LJ8:GM0{C ! C 4A '#('b)AvS^SKPX@; CD-.J@; CD-.JYKPX@% _qK _zK_ xLKPX@0 _qK _zK ]rK_ xL@.  g _zK ]rK_ xLYY@HF?=8720+)#"! SS +"&'532654&'&&54632&&566323#3267#"&547#?&&#"&&#"1IP*;>&49:cS$dYIX>" 26J =S\ 0 0B@,,2#5"6q Q.&' D3DT H_A){C ! C 4A '#('e47,!F ) '(5%QT;,@) JfIKJL  +#3#'#73'&&7#97dGWV}  ;ţyD #A! & D& H3+& F& A & C& K3+';& L0& I 3+& D||3+& J;?@<ee ]IK] JL +#!#3#3!7#737#0r#'#i6 ;LKL & Ds;"9@6Je]IK]JL" ! +332#32654&##32654&##yT[F;(4:fA#T3G-4I[[?F/8P;AA;H  9/32&&#"3267do&MvQW@"8$8T7G?#@"#Dwf@lA$G2Sf4FN L -  & D*-!!& G3+-C&z-!& F3+-& B; '@$]IK]JL ! +332#'326654&##ykyOm19Ml8OB>;qseXKFyMPJ ; 7@4e]IK]JL ! +37#73732#'326654&##3#5BA3kyOm19Ml8OB>$tsQqseXKFyMPJQ "& G 3+ ;; /@,e]IK]JL  +3!#3#3y2"(;LKL & D& H3+!& G3+& F& A& Bl  3+ & C& K3+';& L; )@&e]IKJL  +3!#3#y1'2;LL-C >@;  Je_MK_NL    +"&54>32&&#"32677#73 hw-SvI4S$!D)Cf9EH/"o>%VrjLe9JNOASJ -& H"3+-!& F3+-#C& %-& B); '@$fIKJL  +33373##yX22WxX77;^;;@8  f  eIK JL +3#73733733###37#YIIXWFGXX77GCSSSSC[PU)!& F3+:; "@ JIKJL  +#77'73 G]? H]@ 122N1t & Dx& H3+i!& Fn3+l& A:& B  3+: & Cpb;&i& K3+':;& LD& Ji3+vb;(@%JcIL  +"'732673H) %5 vYx[ K,5,XKvbX!& F]3+;%@" JIKJL +3366773#yX: iak?/; ! +#;& V;@IK^JL +333yXj;Mj & D;&%C C3+#V;& w;&y Ӱ3+V; *@' JIK^JL  +37'7373+7g32'26654&#" kq(LoGep%ImD@; ('J H_MK_NL"!!)")&*( +'7&54>327#"'7&#"26654'N.8+(LoGQ5/,5)%ImHS6  5=Z132!#3#3!'267&&#" kq(OtM"5-"((&X-I`/IxgHg;LKLLOMGN; +@(g]IKJL $! +332##32654##ycT|1.=.GTe7;VDah"=?S; /@,hgIKJL $! +3332##732654##yX0cT|2%6GMe8;rXDaff:?V-x,D"8@5J_MK_NL""&# +'"#"&54>32'26654&#"hUjp(LoGep-W@l>X.D;=Z1ExgHg;zhNfONFMNOFM; 3@0Je]IKJL ! +332#'#32654&##yCHdaST1@8HF237;AX.>5+& & D!& G3+#;& C%7@4J_MK_NL%% +"'532654&'&&546632&&#"S8G,2H+1.:4X7.I @!/8'*5>uT-0,B84K(H /'!' A9UZ & D!& G3+C&zv!& F3+#C&  C&KPX@ J@ JYKPX@~_MK_NL@#~_MKJK_NLY@ && +"&'532654&##57&&#"#6632F>6<,5<4 -/?@LXOne=U,4M.U X9///R]$CLsig,I+]K@4U2;;!@]IKJL +3#7!#miiLL3;/@,e]IKJL +37#737#7!#3#m3mm''rr3GLLG;!& G3+%;&zz.#;& 3);$@!IK`NL  +"&547332673[[ JXK23BBOWOpVK)\%).DDn`m3) & D 3)& H 3+3)!& F3+3)& A3) & C3_!& E3+3)& K3+3');& L3)R& I  3+3)& J3+< ; !@JIKJL  +3336673z>W" `;!G@"_F;!(@%JIKJL!! +333667336673#&&7#aU  U \^;!GM"I"PK#KQ>7&F & DpF!& FJ3+F& A^F & CL; &@# JIKJL  +#373#'6gZKbp_P/ =;"@JIKJL +3733o/aYAd-c= &  D=!&  F3+=&  A= &  C; %@"]IK]JL  +#7#7!! [b @LASM &  D!&  G3+&  B\  3+QGL?)&f 3+J @W_O   +"&54632#&48&.'".F%%5!' XKPX@noU^N@U^NY@  +73!73#'!'74'474'TTTT0 )@&e]oKpL  +3#737!7!@6d/OO6W& 0@-g]oKpL   &! +!#"&&5466333#"5;EO\);gK<+*@'&JoKpL++ +#33667336673#&&7##467#9Be "^   _e   ` "Q+V$$^/5OK6A;0\#$W+SA@>Jf#K$K_'L +"'53273!3#!2% =Y@:@XXFUJ Jb.6MkM[ED&O@L Je  Q ]#K ] $L"! +3>7!!33##!3#7!!#T83-]UC#@ @YJQlfJYF5[9U'K(}vx=KQ >I.[bM )8Av&-L@I) J  g R]#K ^$L+*('$#  +3667&&5473667!3#7!667#37]9W7$77!3#7## -)%&%cP9W)S "!4L NA<.x<z1uyo+?\1'=$K"PX@c]#K]$LK(PX@#Wc]#K]$L@$gc]#K]$LYY@ !%"%!#+7663232654&&##7!7!36#"&#"  C;7%2)., DaN&QB.@5-H&F4PGJ9(D) '=,K"PX@( e  c]#K ]$LK(PX@- e W   c]#K ]$L@. e g  c]#K ]$LYY@*('% %!# +7663232654&&##7#737!7!3#36#"&#"  C;7%2)., sˆaN&QB.@5-H&F GPGGJ9(D)  [}-@*J#K`+L +"&5467'733267H9cL Vg="0 H7 22 ;F ! 9@6Jfg#K $L % +376677#73733##7654&#"!'ZS^ Z%WOZg&v_LaaLejY),EHa_,$G@D JI~]#K_+L$$ +"&&5466733'2654&'#7nbO:h^{iebGpoJTZRb+~ 4aB9`A oFAl@OXG1H *D+AFG>R@O. - <J~_*K` +L:81/+)"  >> +"&54>32&&#"32677332>54&#"'632#"&'nc4LbC)8 +[) 4(-J:(5..$BOY\7Qj@=IO nD~c9,F5Xkn/O[' $7]pu3IJH+wkEg<)*M=%;@8  Je_*K]$L%# &%%+3654&#"'663232#'3254&##sq%(3L@AT^'HSGTB;Q"G I?-V0P1Qh2L;0Zs<@9e]#K] $L ! +3#7!32#!3%3254&##@@S_'HSUY SB;P|N0P1Qh26L;0* =@:f  e#K ] $L &! +333733#32###%3254&##*YY@T^'HSssSC:P```J0P1Qh2 L;0I*KPX@!e_*K _+LKPX@%e_*K$K _+L@)e#K_*K$K _+LYY@$"**  +"&54>3233##'26654&&#"Bv1[}L\p3?YYG 3TuGLo< H<:[> V ]~GL}L  /6LAzb:Oan8Z6@mFag*4@1Jf#K $L +33!3###!3'&&'*Y@']}W4aFע  13MMmD** 7@4 Jf#K]$L  +#33'&&'!'9^X֮+h[O6Ny)\$&V.Ȱ"D@AJ e  e]#K $L! +#7>7'7!#'##7#7!!&&##"(h5I>{7; Z #Y#N6^R*3e7;@O'DD1Q4ޥl962* ?@<J f#K] $L  +33!3!!3'&&'!'*Y@']}uFע  l(13MmD**~HzK(PX@  J@  JYK(PX@]#K_$L@!]#K$K_$LY@%'+##"&'732673#&&7#Յ .?8D.$   "2&^gX* p,`/:R-J>F6@2S(D )@&#K^$L +33!3!L9LZ,Z)( {z6EV5@2 Q]#K]$L +3>7!#3#7!!7#T83-]UC u[9U'K(}gx=KQ >IO E8uKPX@ J@ JIYKPX@]#K_+L@]#K$K_+LY@+"&'532667>7!!## -)%&%qZ "!4L NA<.x32'2>5&&#"7"&546323"&54632[3`W~/\V>bD%_R>dG%a%"# \I]}HO77333#7!!#T83+ZRB1U,φ[9U'K(}vx=KQ sO>T-!?$.J,6(9$D{Y5W3,@4AW8I@710 @""WH4Q5[bL=)U:b&'jGB$! %'$! %'\6#<&2:a@^ J gg   U ]#K  ] $K_'L65432210"!$$*"+3>7!36632#"&'532654&##73254#"!!#T83-]UC#56 07(%8OC737"'7 %K)%(}vx=KQ >I +&+ 43; @"81!  )8ZZ"H@EJe]#K$K_'L "" +"&'732677654####7!#32V' '+< 5 eLY*ZU5`N 3>'"Q{OONL*WaZM5@2J]#K_+L +"&547#7!#3267>9L \]$* DD *OOJ J($ȵJKPX@$e#K^$K]'LKPX@!ea#K^$LKPX@$e#K^$K]'L@!ea#K^$LYYY@ $# +"&5463!!"3326654&#!3!3#l4072 $:#-*Z,Z5im+#%15 '$!%zm JOV>M5@2Jc]#K$L +"&5467#7!##"3267s/4V;}ć,85.  5)=IQOO8"+<Y 3@0Jg]#K$L&#+!#"&54677#7!#32673>&nAKQ$$(07l,HZ#GB$NN! %'\6)9%9@6Jg_*K$L%%&%%#+36632&&#"6632#76654&#")ldW! "63"d;KQ5Y4(02a(HnhO@DYGB$! %'*((Ե#JKPX@&e #K^$K] 'LKPX@#e a #K^$LKPX@&e #K^$K] 'L@#e a #K^$LYYY@"!  (' +"&5463!!"3!26654&#!33333#4072l }#;#-*iZZY8hn+#%15 '$!%zzq!MOVH&22H#)>@; Jf*K_+L'&%$"! +"&54>32374&'3667#%#>Q1^Ty-Y3Rr>72S<656:[> \I]}H 8Nd LbEp;A@>Jf%K$K_'L +"'53273373#7#@% =W.0XqY2DI Jb]KXFK"O@L Je  Q ]%K ] $L +3667!3733##7#3#7!3#R7$Il#0/W7ni8X4&D7R((gS{ YY sF#+L@I& J  g R]%K ^$L)(%$"! +367&&54677366733#7!677#37R7H)""1&K$,2cB7P((EL!g$(R,#3? 7.   4@M08v8F#n@ JK"PX@Q_%K_+L@!Q_%K$K_+LY@##+"&'532667>323#7#&&#"  .)*5N:"@aH6Q(Qe *2*5IL*\KCbA :'^USr;DK+6_@\#.J gg gW_O-,! 20,6-6'% +!+  +"&54>32#"&'6632"32654&"32654&ev8XyOOE(WI5S!#V5TP(ZM<]"R D9&SHCrDD0(C( A=M>)Q6B3-&#)%DI;&$- 8@5eU]M  +3#!#3667#3YNKU;;TK+Wtvv,#0 1@.gW_O  +"&&54>32'2>54&#"CQ&"BcA_X!Ab?*?*)7(?++ 8b@?}g=un=zd32&&#"3267+|#K{XCs "a:Ud,bU>` ^v;lU2J!W[P e$@!U]M+3!7!{j|N6W *@'U]M +3!####rtYdcXcf33d> 4@1eeU]M !+3#7!32#'32654&##}_cg\jt|<>4CiJDOAJbG/+&+E>@;eeU] M! +3#73733#32#'32654&##Wcut0X0cg\jt|<>4CiIIOAJbG/+&+/#>@; JW_O## +"&&5466733'26654&#" L`/7eDuYii5@)Kd;:T-D69V2D <`7DsNd@9gR/K=b7D,4-XS B?5B.2'FD7HN$%$P3!8@5J~f%K$L!!& +37677#73733##76654&#")XHKX@>JMhMASSAP UF!Jd  2/CHd'!,6@3'J`+L#"",#, !!+"&5467&&54673>73'2654'UYM[0-X!'4K6[<\E+&c_/8@G5/ KI>p=7_3(#$ )M-,YuTXu30]&UmJ65EF5K&&*1!9R@O) ( 7J~_,K` +L53-+&$ 99 +"&54>32&#326773326654&#"'6632#"&'ZV&D_9D&-%'=*-1#* "X# -6O*&#!:!GICgH+@A oeC{a8C1O\-?I OL2C E iSCj?5##2G@DJg_,K_+L%$+)$2%2 ##+"&546776654&#"'66326632'2654&#"FP^"&+GE 7SR-]B=8331* KO4  EA?),!%+/-  JK PX@)n  g]%K  _ +LKPX@(  g]%K  _ +L@,  g]%K $K  _+LYY@ &$- -%# +333733#6632#"&54677#%2654&#"XY#6QR-]GP^1dw>7321*jjjEID2T2KO4->?),!%+/0&KPX@!e_,K _+LK"PX@%e%K_,K _+L@)e%K_,K$K _+LYY@!&&  +"&54>32373#7#'26654&&#"]k"CeB^jv/XrW2oCiC/O/0(7Q,=o_>zd=o` AqFIC~X9%K}K>D^JK(PX@f%K $L@Ue%K $LY@ +33373#'##7#3'&&'#rW3dfU#}^.x   &EE? 7@4 Jf%K]$L+#33'&&'#!'9Idf   'VR.QFEE"F@C J~e]%K $L! +#7>7'7!#'##7#7#!&&##"6_,B6l  -0UQz@ R%',Q-5#:&33(<(xxxx<*%$ hJK(PX@f%K] $L@"Ue%K] $LY@  +33373!7#3'&&'#!'#rW3df.x   &/EE?ܗKPX@  JI@ J IYKPX@]%K_+K'L@ ]%K$K_+K'LY@%(+##"&'732673#'&&5# 7)A2  '`]W |T7%gLCH2 (?F( )@&R%K^$L +3333!R7DbXbbXqt(2F5@2 Q]%K]$L +3667!#3#7!3#N7+Yx SN7U((iTw@R_|EwD"JK"PX@_%K_+LK-PX@_%K$K_+L@'_%K]%K$K_+LYY@""+"&'532667>32!##7&&#"  .)*5N:!"dY=' *2*5IL*\KCbA F.'^USr;-@*J]%K$L3+3333'!##667###qr8 RbPA E=>WF.3%M ;R%0)>@;g_,K_+L %#) )  +"&54>32'26654&&#"7"54632]k"CeB^j"Cd>/O/0(7Q,=J0"%"o_>zd=o`=zd=IC~X9%K}K>D0./3%*6I@F  g_,K _+L,+ 20+6,6&$* *  +"&'4>32'2667.#"7"&546323"&54632ap$FgEgt%HkC4W58.:U/B"x#yi:s_9te(FtEHN$"&"0t' ]p ]F9@6 Q]%K]$L +36677333#7!3#1N7+Xw-P+bN7U((nTv>P^z2D#)7@4J_,K_'L))+"&'532654&'.546632&&#"w;L"S)DR'+35bD3J!':#AF/(/5gQPA2D+:G/>]4AN80D&8H5=d<"5KPX@-*'J@-*' JYKPX@$ e]%K] $L@, e%K_,K  $K_ +LY@10/.,+)(&%$# 55 +"&'532654&##7326654&#"'66323733##7#+SS5?K)O_|2+&+ 43; @"81! D==$L@IJg]%K$K_'L $$ +"'53277654&#"##7!#6632e#<-&%-L"-Yc'$L1=Q-I K c'#FF!CH KX+5@2J]%K_+L +"&547#7!#3267D> ?@ ( C4'%FFF (1$ȵJKPX@$e%K^$K]'LKPX@!ea%K^$LKPX@$e%K^$K]'L@!ea%K^$LYYY@ $# +"&54633#"3326654&#!333#m4072 $:#-*rXbbYi+im+#%15 '$!%/BOV >5@2Jc]%K$L +"&5467#7!##"3267l/4G3[c85.  5)7F FF.8"+<="3@0Jg]%K$L""&'+!7667##"&54677#7!#3266773 "a>CF  '"&QG Xr#B(4FC06FF=#(&_X8 *9@6Jg_,K$L**%*%#+36632&&#"3>32#654&#">F) #4C+>IFXG !%"OC1qGE I BA 2"ED(P-!%/jX((Ե#JKPX@&e %K^$K] 'LKPX@#e a %K^$LKPX@&e %K^$K] 'L@#e a %K^$LYYY@"!  (' +"&5463!!"3!26654&#!33333#4072E  #;#-*rXccXccXi"hn+#%15 '$!%//@OV0v&RRr0#(3@0Jf,K+L(' +"&54>323734654&'7#667#]l"CeBce"Cde!1E`"#c/Jfo_>zd=pW>|g?@ S=  ,= z  _! XJ#:*%@"%H_+L**+"&5467&&54>7'26654&'[qsf 7BrPAsW236DMAtL7K&2-\_; cahA*5?!K !0!*hLP{GI8[65KsY;FZ&'L'"$3@JKPX@"_,K_+K_'L@&%K_,K_+K_'LY@&%.,%3&3! $$ +"&'532677>1##"&54>323732>54&#"1SY+>O  #V8AQ$Ea=6A!Cz{;"D9"-5(B0QDEYG!#!H?4XH #!J@1XrH 1@)7<7D D@*F!$0fP-!%/jXc2"@77#T+/ +j/ +4j/ +j/ +j/ +j/ +aj +~j<3+3+3+ 3+ 3+3+3+k' a!PaG5)&f 3+]aaG5)&f 3+*BK1PX@f5K6L@f5LY@ +333#*Y@FOK QK1PX@~]5K6L@~]5LY@ +3##!#7#̈/K@ @K/|.) h@ J IK1PX@5K6K_:L@~5K_:LY@ +"&'73267#373Ca U5Il!Slʩ," MJXZ=BpywdB-VBK1PX@f8K6L@f8LY@ +333#rW.3IU> QK1PX@~]7K6L@~]7LY@ +3##7!#7#c#L22L#bLA>JK1PX@8K6L@8LY@ +333#>7rTF YlrSC  /1 <43 @R61=@:Jgg_+L(&" 11 +"&54677>32'26654&##732654&#"!bm44aPU`4I7gF-@!@N #XN1309 3? qg*?h?YP& OKEm?H0K*7>70327#"&&'0'0>54&&5461"32>18r_9=;+?&&*B2.\NbHmh@R*"JCBHK PX@6n~||W`P@5~||W`PY@YWGE?=(& hh +"&'7326'.#0.#"1'0>3276654.54>1032667#".1>32)4#8=/7F  %./&&2&*?>*-/! =S32!  )20"*:> :E" |E^f(=7; "-" %KVZ*!E>1@5 '3F. ")& TRCz,04 !OR@O@?)(J~~|W_O#"31"O#O#+#&+'0>323266710#".#"".1'76632326654&&54>73+6 <<6)"  +/% *66$<)'#1h  %24 $30 * :6$.+@@+ !!-=+| "."3S/-]Q &*#* 3267>54.#"1'06654&'.54667>32>32326732661'0654&&#"06677"&&554&#" #!)) !49-("."  ! ,=&)I4 #1@)#& " 5Z2,! D 0%<89&"6 .*   ') ) _@9& J;: H    ~  |||||| W_ORPECB@642/('" __ +"&'46&#"'>3232>54&#"1'7>54&&#"#"&&57032632>324  ,70C)VF+ /72 )% '' $!K&/&0!10.0# 0>$$!!LG4q 2! ! &@KKEK A,:!  l  *2'A3%/3DUU!!$0@JKPX@&ee_qK_tL@$gee_tLY@+*(&! 00 +"&'5326654&##77#"&546632&&#"33k*^#&\.FT%K@N]UY4\<"2&4",2Uo?T.H(:5FWF=`7 D9*,*GVXAk?.@JK$PX@&gg_oK_tL@$ggg_tLY@)(&$  .. +"&'532654&##77#"&546632&&#"332]1Q #Q8PfGM@ +Y`+Q8!+/548 AT*QKE:?>JL4W5 = F11+7/M.qwO@QURO$\ on J  ~  |  |gg g  WW_ OxvgeMKB@9842(& +".546332667>7>7&&#"32>53#"&54>7663267#"&&54667032676654&&'#"&54632>54&'.9 1"$  +$>YK**8*4"6#=q[59'$#K@)9O04:':98[(C/'7R' %&,HR' -1 % 7  !1# 1<(![ '*!(7&  QX-SY2%-$ $5F*)(.Rm?0%PF+44+I:( !. B(/H4$9)'QE*$ ,<"%095$P8   :V23NcuA(@h>:~@{W >(=\ sJ  ~  gggg W _ O}|wuecQOEC:8/- +".5463232667'0>776670#"&54>54&#"'0>3232>7667>32032>73#"&5467667>54&#"9A1"$  1+8J=%?P1C) O?2:4 AL%0:< 1CKH -" %. %\db+"%4nW/- +0,!#/43!:+$mvcD (,$ (1-;B(|E"3k]9 @MICG$PBSS@PH/J~ggW_O75('&% BB+".5463232667.54667663>54&1707>79A1"$  /)*E=68;oA73A00 !@) !A5`|CPa=:=pX3& '6/ (7'  5];32&#">32326670#"&'67>54&#"267&&#"";%=%&T)  BR+4[u@---):cI( 27=DLT-+&/UqCG(!<!HA%6D(&E +WBB<?,%<20x,9$J(4$3 %# H+  *;>5`K+  .Nb5,8 <}qZ4*$gl\3CY&D-!11!B5We0Dq"a 5@2eU]M  +3!##'3#3#aGxCCCC6k5`` m~@{z ~{xM = J  ~  gg  g g  WW_ Ovtkiba][QOCA;:42,* +"&&54>54&'"#"&54632>54'#".546332667667>7&&#"32>53#"&54>766326732>7%!   .0"01<(![T9A1"$  -$>PA* W>4"7"=q[;@-$#K@)9O04:-A?8[(C98om !# #3> #EF=  .C-^ NcuA(@h>&5. (7&  C}XCL%-$ ,AN*)(.Rm?0%PF+44+QE1 !. O(@h+"JD() 5=,) ʰ3+2F >@;  JgW_O +"&&546632&#"3267'~pIOo0]0PU+K>W)X*/VS) Zpl] 8" ; zSO|a ?@< eeU] M  +3333##'3#3#axCCCC:6[5``a &@# Jt +333#%5aL5M:"65bca\ ?@<JeeU]M %!+3!2##'3#3#6654'axBl=cxCCx$/Sm[>_65`1@32#"&5467232654&&#"#"&'32>54&#"&&5466320654&'&"=Y0EqC :b@0<" 0%"$#-"1!./K+5#9#A3&% (M?&$<$%?(%# (EX{% '4\;=hE ;-)O3.29))< ,73,36aC   6NZ//36H(%'6Z4$(B(#--O:!  h8@10d vV |YL JhI  ~  ~g g  g g  W _  O:9rpRPJICA9:(& 88+".54>3232670#".#"32>54&'7".5463326677'0>7>77667>320>7"!':\S3@.."%#  /&DCF(6mY6 2,#3Jk.;!1"$  -',F= 8R,E07QB %&  G N `5%$ !RSF+ %!.Oe6&% #7A< ')+#(7&  6^;A1 ":e_/ $Ze5$ 8"'!** UL@@=lHG+ JvHW`PigOMDB+"&5463232>7>70>7667>1326670#"&54>7>10#"&&70>7>7>H3(!%+ )%7_VW/?|i# %"9?>,="5U2062(* 8> (/0"(&,' 282 0Si9=Y#3D?1/$!SYP(QWg J:*;#  "=e~@Vj -HTM8 8NUPUfd'JpOQ DV0NVI[ HTDN  i|H 1R2ET,  G8C[.B,d6c1JBf6k4GUDD0;KHT4<!v/?@<Jg_wK_xL+)(&!// +"&&546677&&546632'2>54&#"33#" Ue.7_=0;>kBy+Yb@fF%YM%@'DN  i|G 1R2ET,  G8D[.ZJJ=jMbj=.0;KHT4<I,i@ (JKPX@goK`xL@goKpK`xLY@'&%$ ,, +"&&546677&&5477333#"326673#7#G\-7_=G; Y!NI%i|BAJd; ZYG:R 1R2CR+  M479KIX1:Bf66c1 #+K@H% JegWe_O   +"&54>323#773#2>54&&#"@Q"Bc@4D B'RCB/Qbo2 ;&KUyL.; ""(/5@2*(JgW_O  +"&54>3232676677&"#"6654'nq*SxNH`'.-^-,YI4% +{$)/L9A m]@}g><>Bd ?4 -'lo)H'< V M@Jg geU] M     +"&54632'2654#"3'3#"-2/L; qrnC\CP&%*7>323266730'>54#"&1 ,+CX3%! :fO NR#2@H#;L+0+" ),$5 # .3%! ::29++L/%77%<@4 /FF/(?S@P ,J~|gW_O*);931)?*?! ((+".5467'0>7>32>73'267&&546326654&#"%0 ,*>K(+.4!/0*@*<)"  +M7( ),$5 # .3%,TZ%.&'1' 1o+%^jM[{Q4GVg@d"% MJ~~g g W _OIH65HVIV?=5G6G*)$# 44 +"&&54>17#"&&5467'667663273>73026676654#"26677 !*2OXO2" 6$$&  A #;#^:)'.v'B. 3T6N2GXx!B: "01-#"51O/BF=&{",B>7*G )87@ &D"(;!(@:5 ?I1GH17T,"J (+EPN; -N2(3<@ 0:"*J@G  )#"Jc_zK_xL'%  ** +"&54>32&&#"32671#"&'73277Yn'Ih@#E7 9V0>8&B F:  ,  ddHb9 I ML=CADI4?: -z@JK)PX@&~cqK_zKpL@&~c_zKpLY@--%%#( +333>323#"&'73277#654&#"X+4C+>I7) D7  * 4G !%"OC1A 2"ED(ADI4FP-!%/jXr(9@ %JKPX@#cqK_zK_xLK)PX@'cqK_zKpK_xL@'c_zKpK_xLYY@53-+#! (( +"&&5466773632#"&'##326732>54&#"+M0Pb2X.%'mf"A\:6C!B\aw7.'015%>+?I(&),_Mwx{a@x_87%R,DIF90<,K]1AR2,@JKPX@+  e_qK]rK_ tL@)g  e]rK_ tLY@)('&%$#" ,, +"&'53267#737#?>32&&#"3#3#0 $( E]\]d 0F14"'. s sqpGLJ;7F@%!0EM C ,>3C@S^!!4@JKPX@!g_zK _xLKPX@%g_zKpK _xL@)grK_zKpK _xLYY@/-,*&$44  +"&54675&&546632373#7#'2676654&&#"33#"PNV=,0R4CI CrF [,>] 5.+C.1 )D), N32366733#327#"5#"36654&2667#@Q) (Ea:9@H  I J$Z"RF7S3"B6, \[&$>Ek=6&1JX+>b $@c):\G /LKPX@ )& J@ )& JYKPX@&  h _zK _ xL@.  hrK _zKpK _ xLY@'@?.-FD?L@L86->.>('$" ,, +"&54>323736632&&#"32#"&'#7#'26676654&#"%2654&##:G'E]60:!8 P/'JD+@f|xTT;L; (R$E8+&%@1$-Od!7 ^XLh;8%SC%(L^IQMCX(#Cc132##!!3267#"&57326654&#" *)E_32##3267#"&547&&#"3277326654&#"j2;KG-Ff@LMAn;>&D(&I3]e% +#%0YB?0>JAmBB;5U3 =GKm]"!=1)!YI'"-<H@@ +)JKPX@+g_zK _pK _ tLK-PX@/grK_zK _pK _ tL@- ggrK_zK _ tLYY@!>=/.DB=H>H75.32373&'2>54&#"267&&#"CY,I,=O  #V8AQ"Cb@6A!Cr  =S"D9"-5(B0+:D)/,,04$1#05(,A[SAi?8%S1R" D.Oe765/Qd4l0@ JK)PX@,~~ghqKpL@)~ghpLY@00$)"$)# +3&&#"#"&54632332654&'&54632#"'Z]  &M=8X>   F?X0  2)QF . 0)LLe6@)*  JK)PX@2 h g  gqK_rK p L@2 h g  g_rK p LY@66420/-+(&""$"# +3#&#"#66327'&#"#663237332673#"'32673#"'H:  4:+  4:+2X;  5:+  5:+1>Bn>B>Bn>B8!H@  JK)PX@qKzKpL@zKpLY@  +37&&546677374&'66`2(2*G+.X2'0)E*/K+'5+'3N0,H-L/+H.~1=)/=N"?I@   JKPX@$  g_rK  ] pL@,  grK_zK pK  _xLY@A@ED@IAI??%&/&% +333>323>32&&'#"&546776654&#"#654&#"%2677"rH 1@)7<7D)>D   E;+=aN!#!H?3XG # J@1 -.c2"@77#D@*t 6 YJ(7=Cy!$0fPP-!%/jX4&.,%"+5@JKPX@!g_rK _xL@)grK_zKpK _xLY@-,10,5-5++&/% +333>32&&'#"&546776654&#"72677"rH 3C+>I  F;+=bO"%"ND1 -.c2"D@/t 6 YJ(7=Cy!$/jX4&.,  "-8y@ 4+-JKPX@#g_rKpK_tL@'grK_zKpK_tLY@ #-%'$$ +&'#"&546326654&#"#33>32'3267&#" <%0@F8*#N"%"ND1XrH 3C+>IN ) #' $813Eq!$/jXc2"D@/"3Ga',)G@J'&% HKPX@ pL@ tY@)) +.'566554&'%726654&'&&'1K,   &2N)!&  -,+ %)m, #B(+OR._ :%.J$,"+6K@"J43+%$# HKPX@ pL@ tY@ -,,6-6 +'7&'566554&'%7#&&'7&&'&&'26654&'V%%   <%H&2N)/#6_ / t)*.9%)m \n#B(+OR. $ :%,/#,;@8'&  #JH_zK_xL&*%- +'7&'7&&#"766327#"'4'3266H*3Y &)@6P31*5"CdCM323/O/%<+;0 &W )9%?2I=zd=';C~0K"$2@Y@V " J  g_zK  _ xL43&%:83@4@-+%2&2  $$ +"&54>326632&&#"32#"&''26654&#"2654&##Yj"DgDIP&h@'KD,@frTSBW`<:P)687Q,;$)Oe!7o_>zd==.86L^ISKCY72/9IJP8DK{HBE- -03?I+1:CNZ@W&$=9'>81J%Hg _zK _xL32MKFDA?2:3:*$%%%& +'7&'#"&5466336654&#"5663266327#"&'"&4'3266#"326+- $d;AR?n<=%D((M,?Y`AO3-+4"DgD#7:P) :&7Q, 8^#84:=@5U3 =GM64/9,7#@1E>zd= JP$4$K{1+!U2"(1:EK(PX@ &  J@ &  JYK(PX@%   g _ zK _xL@*  U  g _ zK _xLY@#*)DB=;9864-,)1*1$" (( +2#"&'#"&5466336654&#"5663266"!654&3267!'#"326oYj"DgDIP$d;AR?n<=%D((M,?Y`8^ o_>zd==.4:=@5U3 =GM64/9I\JBE8DcJ1+!U06",;H@E  *J_zK _xL.-64-;.;)'! ,, +"&54>32&&#"3267&54>32#"'%26654&&#"Yn'Ih@"  9V0>89"CeB^j"CdC]6!P /O/0(7Q,= ddHb9IML=C)4>zd=o`=zd=8 KC~X9%K}K>D06<5>HZ@W%$" GF98 31.J#H0/G_zK _xL@??H@H<:-+! 55 +"&54>32&&#"3267&54>327#"''7&'7&#"26654&'Yn'Ih@"  9V0>89"CeB8+ /"8"CdC3)/ !P!7Q,t/O/ ddHb9IML=C)4>zd=267c=zd=23 2 WK}C~X# '@$J]rKpL& +376773#76654&#")+X,HKX@>JLz UF!\v  2/BIv'0I@F%&Je]rKpK_tL0.*(#! '' +"&54776654###32326732654&##66`l-XrNXM7!,  t?M01x>6YHAFN 4/-y 1E 28!&g" #@ JHrKpL  +333667rH E4.G/ 4c'BQCa2Q#KPX@ JHK-PX@ J@ JYYKPX@_rKpLK-PX@rK_zKpL@"rK_zK_zKpLYY@#%%$ +33366326632&&#"&#"rI #N( C   %3""H;3d86/!S /cL"#-KPX@ !JH@ !JYKPX@ g_rK_xL@$grK_zK_xLY@%$)($-%- ## +"&546733>32&#"&&''2677"D+=aODH .:& %?1"   E: -. (7=CAc2 O'@O'# 6 YJ>&.,#-7KPX@ '%+(JHK-PX@ '%+(J@ '%+(JYYKPX@"g_rK _ xLK-PX@&grK_zK _ xL@0grK_zK_zK _ xLYY@/.32.7/7! -- +"&54673366326632&&#"&#"&&''2677"D+=aODI #N( C   %3""G;(  E; -. (7=CBd86/!S /bM6 YJ>&.,,k#G@ J HK PX@_rKpL@gpLY@ $# +3#"&'73267b'J#IKP-t F ,#,s@ JHK PX@g_rK_xL@gg_xLY@%#,,  +"&54677#"&'732676632'2654&#"]FQ/'J#IKP-4.AA&O7,)##& KO4 F KC1S3H@(+"&)1~X@JKPX@_qK_xL@g_xLY@  +"&'53276632&&#" ?XHL" "* WM JrPaI:8eS^7_JKPX@rK_xK`xL@rK]pK`xLY@  +"&546733266773#7#=IFYH &"QEWEH 3D DA(G+ %.i[c2"3( JKPX@%p  frK _ xLK PX@)p  frKpK _ xL@*~  frKpK _ xLYY@&%"   +"&54677#737367733##7#'32667'=IDD5Y5WBB/H 3DX &B= DA( B (Bc2" %E:+8O <80JKPX@$~ ]rK`  xLK"PX@(~ ]rKpK` xL@,~rK _zKpK` xLYY@ 64/.-,(&  < <  +"&54632"&5467332667733266773#7##"&'#.$>DEYH!# I? X#!IA1XrH 1@)7<6E'-#FD@*F!$0fP0 !%/jXc2"@77#3" k 3+1",zKPX@ (J@ (JYKPX@_zK_xL@ rK_zKpK_xLY@'&%$  ,, +"&546776654&#"566323266773#7#=I$ )0@% !%"OD1WrH 3C DA(  C 1=+ %0jXc2"   !-K@H  Jg_rK _tL#")'"-#-!!$#& +'&&#"'66323632#"&''%2654&#"q* &90]#:/?L>2AQFP:,!- 0KPX@+ #"J@+ "#JYK PX@ p_rK`tLKPX@!~_rK`tL@,~_rK`tK_tLYY@0/%$&" +7332677'&&#"'663233267#"&&''#(5  &90]!  !*/l01#O(+&AC: ''B$>'2<"v@ JIKPX@hrK_xL@ hrKpK_xLY@""  +"&''#3736632'2654&#".<2ftYSf,7 ,;F2 -8tb:/=K?()" *@' JrKpKtL  +373#'sYSf{Y[@""I@FJIhrK_xKtL""$$ +3736632#"&''2654&#"sYSf,7 ,;F8.<2b:/=K.7t@&()d@  JK PX@prKpK`tL@~rKpK`tLY@ +733267373#'#(4 $tYSf{Y["42!O%/c/<+C@@J~rK`xK_tL('#!++ +"&'532677>1##"&5467332667731SY+>O  3D+=IFYH &"QEWL{QBy4 ,0+95 )2,:9d3+.<  #0 MK$PX@_,K_+L@g_+LY@  +"&&54>32'2>54&#"CU*&HgA_^%Fe?*C//7(C13 8b@?}g=un=zd54&#"'6632!GN-$#L),8e;IQq_&G5C5 &% ?,&N:Kw>WNS1+u@$JK PX@gc_,L@!ggW_OY@ +++"&'532654&##7326654&#"'6632:X'#a4JSI>;A.N06'.H%))`CS\]W:C4nUMA=AI#C0.1 >(WGLj NA9d?X& yJK PX@%K^$LK$PX@f%L@U^NYY@ +7!733#37667#&eaoo&/  P7N+P $" D A@>Jgc]%L   +"'5326654&#"'!#6632aB$S,FV'H>4 %aI<*XjAo'U0O.=B SPeX-[L.* 0G@D  Jg_*K_+L '%0 0 +"&546676632&&#"36632'26654&#"Th D5+vM6&(_#U4IY=^71D$45$<) 0 kpE4,0 L&-^V2cQ1J8Z38= .($?(,N @]%L+!7!,u |NF| (65@20J_*K_+L*))6*6$" +"&&5467&&5466326654&#"26654&'EY+dY&/8a;AQ$/L,4=3lDG6.5A,2@34.H*B 1Q1Mh"F7;T--H)5I3 RA5]9C5+2=5-:%; 0G(?/4;T2.s@ JKPX@gc_,L@"ggW_OY@! )' .!. +"&'53267##"&546632267654&#"w?:#dwO0SY32"&2>7X[+FcA\X-Fb2.H5&-E1 ~u;j@woHe:HuC&dBnCX0 _& 40 S1 X& D " 6N   T2  >:` `3+@ {a a3+6t` `3+?u` `3+>'` `3+ F(` `3+D;` `3+\)_ _3+;*a a3+9<` `3+J{: 3+uA{ 3++rt 3+7ru 3+1p' 3+E~( 3+S; 3+[ ) 3+;w* 3+I{< 3+S& CJ6& 'u S& 'C +=& ' 7 & 'a 1& 'u S& 'C E& 'u S!& 'C S& 'C S& 'C'  v"7: ް3+"}){ ް3+~3t ް3+w4u ް3+~-' ް3+w+( ް3+w*1; ް3+:+) ް3+w$3* ް3+w1< ް3+~"' R'  RR3+R3+6&' %@"a]rL  +%"&5463!'3# glqkcc&tvCmX&I $@!a]rL ! +7!2#'3#Xkqlgcc&vtCmbr ,@)ea]oL  +3#3#3 nFnnFnhHDHb2 ,@)ea]oL  +73#73#73HnFnnEnHJFHHQ!@JtL +3#'X([8BAc"@JtL +3#']:69#SGI ) )3+I&  Ai ;@ JK)PX@ qKpL@ pLY@  +3773zhF PX9\ 9JK)PX@ qKpL@ pLY@  +37737'\ hFP8X' #/;KPX@. g e     gqKpLK$PX@. g e     g]qL@3U g e     g]MYY@.10%$ 750;1;+)$/%/##    +!5!3!!"&54632!"&54632"&54632!"&54632hALB  &   TLSL" !!  !! 4!!!!LIfK)PX@ goK_rKpL@ggoKpLY@  +33"&54632"&54632L>@C65/ c@  JKPX@c_oL@gW_OY@   +"&546325%%5"&54632|P6;;&51=P@MJ~~ gc_wL32972=3=&$  11 +.5463232654&'&&54632#"&'&&#"7"&54632)8 &# G;&4   ' 9"  "(.85?   07 ;%49 0+%&'&&77Ew33!"#|)N)S!33wE")N)|# ( 5@KOSWf@* JHYXGe e  g    egee e]oLTTPPLLBAcaTWTWVUPSPSRQLOLONMGFAKBK=;760/.,(&"! 55  ! +"&547535!5!"&547!5!&54632!!'3654&#"2654'#5!5!535654&&54632 n6$7$$E$/* ! + ¶5#O  'd++++++% + $$ + % o  U++++++  #N&"4'T@Q    g _wK _xL!  %# '!'   +"5432"54323"54323"5432"543299:99:99:99:99:a;<<;;<<;;<<;;<<;<<<<5'T@Q g  _wK _ xL!  %# '!'   +"5432!"5432"5432"5432!"5432n99:&99:99:99:99:a;<<;;<<;<<<<<<<<<<<<5@2 Je]pL +'5'7#53'75373#';?~,~~,~?,~~-~-~?,,?~,~@GU]M +'7#5!!}/445Y #/I@F  g_qK_ xL%$ +)$/%/##    +"&54632"&54632!"&54632"&54632C       q   !!  !!  !! 4I@F  g_wK_ xL    +"5432"5432!"5432"543299:99: 99:99:a;<<;;<<;;<<;<<<<DFy 3+3@U]M +773NNd GK PX@g_tL@gW_OY@  +52654#52d'D1<<'" B(8227  ư3+ u 3+{oHKPX@_rLKPX@W_OKPX@_rL@W_OYYY@  +77"&54632{-:|.k34>&0+7%SRT$>R0+7%SRT$t@Ht +7:|.k51>@;J~|c_wL&$  11 +.5463232654&'&&54632#"&'&&#")8 &# G;&4   ' 9"  "(.85?   07 ;%uBK)PX@eqKtL@etLY@  +33#7$1$-  3+ yx58@5_wK_xL    +"5432"5432!"543299:99:o99:a;<<;<<<<<<<<H] KPX@8t^]PBA4 3  J$ IGKPX@8t^]PBA4 3  J$ IGK"PX@8t^]P BA4 3  J$ IGK-PX@8t^] P BA4 3  J$ IG@8t^] P BA4 3  J$ IGYYYYK PX@Q ~   ~  ||||| crLKPX@U ~   ~  ||||| crLKPX@Q ~   ~  ||||| crLKPX@Y ~   ~  ||||| czKrLKPX@_   ~  |   ~  ||||| czKrLKPX@e   ~  |  |   ~  |||||czKrLK"PX@e   ~  |  |   ~  |||||crLK-PX@q  ~  |  |  |  ~  |  |||||crL@w  ~  |  |  |  ~  |  ||||||crLYYYYYYYY@ ɼ&(,,,-. +70&&'667.546327.54632661.763270&&5463270&&5463270&&5463270&&5463210663266322663212663212663212663212>3216632&30                &  %&#) %$' ++ -, +,'( %/"60 )&/ ">:  5/!)%     )%  / &0  "+ 1!     %      #  $       &l@U]M +5!44Mt'""M&"3+33+#4@1]oK_zL    +#5!#7"&54632߼s55#!@]oL +#5!#߼55<O$@!a]oL +!%!!<{O{52 #/;_@\ J  g_wK_ xL10%$750;1;+)$/%/##  +"&54632'7"&54632!"&54632"&54632.2.45.2.[^   .65/4..6   @GU]M +%!5!#}44iWGx@S*?@<J~_wK_xL! &$ *!*%- +76676&&'&&546632&&#""54632'6S )'<(9G" Q4%($*)%H:@Y/?A.,8%*I84!23~& 0@-Ja_zL      +"54632&&'734%(% [3"2354 #UW&x|WK&PX@]oK_zL@g]oLY@  +!#7"&54632xw5x|@]oL +!#x5xHKPX@_rLKPX@W_OKPX@_rL@W_OYYY@  +'7'"&54632Dڷk.|ƅ>&0+'b$S%R%S$>R0+'b$S%R%S$t@Ht +'7@k.|518@5~|c_wL,*'% 11 +&&54676654&#"#"&5466323276632A/8 (   3';G "& 9%; 70   ?58.("  "BK)PX@eqKtL@etLY@  +#533J71f*@'c_rL  +%"&&546632'26654&&#"3T22T33S22S3#8""8##9""92T34S22S43T2<"9##8""8##9"5C@@ _wK_  xL    +"5432!"5432"5432!"543299:f99::99:99:a;<<;;<<;<<<<<<<<cu,@)gW_O#"#" +7663232673#".#"c nK;ywr57@ 4 oK9zyt37@ MO$3%MN$4%5>@;g_wK_xL    +"5432"5432"543299:99:99:a;<<;;<<;<<<<FE #J@GJIGgWg_O"    +"&54632&&#"56323267#"&.!$/>0H:.$/>1G: "N5  "M6 F}#F@CJIHggW_O##$$$" +&&#"56323267#"&"&54632!$/>0H:.$/>1G:9? "N5  "M6 F&aOL_ _3+$@]rKpL +!!5!eCT!/^@ )JKPX@c_qL@gW_OY@#""/#/ !! +%"&&5467&&54632&&#"'26654&'4Q-D. a\/P!@"0+55E)P7 +3=:-'C(9G0#:S? #?6.O/A,- 7(*H #gKPX@ g_oK_xL@gg_xLY@ ##    +"&54632"&54632"&54632$$&&$$&&$$&&3 $%%$ $%%$ $%%$ t 3+)  3+ 2 3+33&   3+y'5 -@*_wK_xL      +"&54632"&54632o    Y   !! 58@5_wK_xL    +"5432!"5432"5432n99:99:::9a;<<;;<<;<<<< !@ H_tL   +"&'73267]l*bB@3!4^^PNNP`\;e0+7;>%@ 0+'7'77'$C**)@$@*+(-75#55-55#5\= #/;Gr@o g g  c _qK_zK _pL=<10%$ CA54.54>54.54667LAQ,,,,,,,,,,6];AQ,,,,,,,,,,6];,#,$&,+%%+-"2?;,#,$&,,%%*-#2? :&<FL@&54JIED? + *# JKPX@*   h gqK _oLK)PX@(   g  h gqL@0   g  h W _OYY@ >=('C@=F>F/-'<(<&&%$"!! +&&54667533253&&'667##2675'"&54675"2327&ByCxM=21="?1;: =23It,5p2pzgQ>jA7v/+m/> OpB D@EL F   9 & \gck 9eHEi<Pz:T*t$@!JU]M +&&573Y\ 65 49C AdD@6 JHeU]M+D'#7'373#73'7#'#3DEEDCFFC-c22c--d22duxyuuyx,NVWNNWVz*/0+7'&&'6'6&')N)"Ew33!"#*#"!33wE")N)Qy8N P P3+7M P P3+Pb4$@!U^N +33P@:b$@!U^N +533@:~HP4@]oL +3#P:@]oL +#53~:H(bI'  b&  KPX@a]rLKPX@eU]MKPX@a]rL@eU]MYYY@  +%".54>3!!"3!4hT32Ug5/XE))EX/B<54.#!5!2#/XE))EX/4hT32Ug50/,,/0B<33"3|R/7 "@5#( 1/  ~[ +@ 8F (d2<E$x)2 -  OE;,1@.Jgc_IL,+%$#" +526776675&&546776654˜3"#!)) 1/!% }S/8 ">7~E#y)1 ,  N E\ Q  7F!(^7=5>"@a]IL +3#3 YyYBB>"@a]IL +73#739YyZB:BB;/@,~IK_NL  +73"&54632@Bgj@!#"n**;'  q 4@1~W_O       +"5463230""!j?BC.),.n5> @IL  +&&54673\ h`NVp~1|Ca]Cy3> @IL  +6654'3=Vn/C76654&#"'6632"&54632g&'5"$<"T(DQ>5 "<"#"%4*3'#B@@8M%!*)h2 'D@A$%J~gW_O "  ' '   +"&54632"&5467>733267" "DQ=6 "G&'5##<"TA*+'@@8L&"&4*3'#B[ &@#W_O ""+76632#&&#",~GSr 5 OB4hS=MC'1)/^< `!Y$KPX@ ! " J@ ! " JYK PX@!n]rK _ xLKPX@ ]rK _ xL@$]rKpK _ xLYY@ $$ +"&547###?33733#32676J =dXcSZ>4D4>! 2 4A '#+(&ozzzC ! C "2@/~hoKpL""!!$ +#7>337332#76654###7#"3Ed.Z.Q_( )],6Z5DV1 2HrB9Z2-7*}.S7X=@: ee ]oK pL%! +37#737#7332##3#32654##(WWWWJplKhTNGlvZAYL\SNOwAYAnL\h-!A@>~]oKrK^ pL!!  ! +3332673#!32#654&##qQbJ]hQh>hO[[_860;1;+)  // +"&''7677#"&5463232#"&'''3267376654#"326654&##77]X[ I]9DJ43!8DC.%% Z1Y@C9$81'  08q+T SN:/J;Hn=8GM1')/I'RW$PRHK? N  =#$@/+%2p#ZOKu/+|@ "JK PX@'n~_oK] pL@&~_oK] pLY@++'12 +#73&&546677363273&'#7&#"#733 8EB}X<<(#&&-53 '^j6@4ICO&I 6AC'1N$@..Q!4AYP^c+I4=V.jf)7|x1554&''5#'7D% T1 ;G(%H!0+5'! D)G@j+Ӵ">5 T T3+IU5K#PX@ ]L@U]MY@ +5!I 44,\@ JKPX@rK`pKtL@rKpK`xKtLY@%% +33266773#7##"'#XG !%"ME3VrH 2B)7 +"%/jWc3!.0. P&8J\m~KPX@8   h g_wK _ pL@@   h goK_wKpK _ xLY@Kon^]LK:9('xvn~o~ge]m^mUSK\L\CA9J:J1/'8(8 &&  +"&54>3232>54&#""&54>32!"&54>32!"&54>32%2>54&#"!2>54&#"!2>54&#"7< +B.9< -AN '&7< ,A.:< ,B7< ,A.:< ,B7= ,B.9< ,AK'&x'&y'&JGNRG+GBOUJ.6V'=B:)%$8A<PJFORF+GAOUK.JFORF+GAOUK.JFORF+GAOUK.B'=C9)%$8A<P'=C9)%$8A<P'=C9)%$8A<P P P3+K>WP ,@)Ue]M  +5#53533#ll4ll>o4oo4o$@!U]M+#7!3'ZPz6l H@E JIeeU]M +55!!!7#!5!=6-KU?b77q1>4* ˰3+`/ ʰ3+<# ʰ3+3 ;@8  JgW_O  +%"&5466327'26654&#"esʂ {ZjUt_ig oH_@^^Z^[[&A@ 1 >2 ? J~~   ~g  g g  W _ O('<:64/-'A(A!&& +"&546632373#7#32>54&#""&546632&&#"3267%3%H4 #-E//O") /S:G-Q7.%0>&*/979hB,8$6#5;"2N(" A?A=b: 7 WF'" 9 =&J@ ; <* ) J~~   ~g  g g  W _ O('@>97.,'J(J!&& +"&546632373#7#32>54&#""'53254&'&&54632&&#"%3%H4 #-E//O,) /,>%6?)!A4"8( +G979hB,8$6#5;"2N(" CA-+,5 5 )#17Q7t@q4J ~ ~g gW_ O 3210-+'&7 7 +33"&546632&&#"3267"&546773326773#7#QO@:G-Q7.%0>&*/(2*G,#BDF;26k?A=b: 7 WF'" 9 ,)%AO.!7&r&S & K@H  JggU]M+35&&546753&&'67!%IORFBB..1524,.,,l[Zjlh3 4w<; P>@R(&@#     JK PX@/  e e  U ] MK PX@3   e e  U ] M@/  e e  U ] MYY@( +!3#'#!33#!!#3#3'4&'PlX8c bPd9a^g@q<6A   3rX  MMB C7w&r)MLM(A@>J~_wK_xL$"((' +7'66327676654&'"54632H$ 0X6RW>4 ,"1#$4%(% DUH+FB&&+ "C!(.n4!23(7" JK PX@7n   ~ f  g  W _ O@6   ~ f  g  W _ OY@*)1/)7*7(('&%$&(+3#73733733#36632#"&'###%2>54#"M MXX  $U8AP#D`<6C!Br(A.S!E:#1X?aaaa?%2+,?\ZGe;7%RX?1Sg6l/Qh90<#P@MJeeeW_ O ## +"&'5326654&##77!737!7!3*^#&\.FT%K@NUo?T.H(;4FPPGGVXBj?,vk}@8D? bR e J  ~  ~   ~  ~  ggg  h  gW_O~~|{us[YKIC@20-,*(#!kk+"&&5467&&5466776610&&#"327#"&54663232663206322&&546321#0>54'6654.#"227'"&#"767&&'72654&'"#"&',20H*A! !+   #(     " 2C+G2 $"e= 2 -$(% +b    yt$![040 )& $:!Y5)) )e  )  0/GU'BP(4$$%%2 7X!:$)A:%)> 2_ " 4+ V92U+4)IJ@G J~eU]M! +33273#'#7'#32654&##)QaE_E[*]^/w>NNd[ECLʵMrOPkkvsWE=0cG@D~ ggU] M! +!##35332!6654&# hBB}Aofbs$LLgdcu<YGTKJ)o9i#W@T ! JIH~eU]M!+3327'7###33&##4'66)-%0\Qavbf3A>NNn"L"dF@F4-Dr*tMr'Hsm; Q ,8c%:_@\51)J~ ~gW_ O&&&:&:43-,+*('%% +"&'532654&'.54632&&#"733#5467###57'(((1"G;3-E)(47M^^a[@e5`c 5 )#00 1 2+,34`/ (N\jxT@Q6*$ KE?J   gW_ OsqecWUNMIG$!'''''(!+53267.546323267.54323267.543233#"&'#"&'#"&'#7>54&#">54&#">54&#""F#?>~% I$"G"}}$G$"E ! ~}% D#7U !V67U !W66V !S4 !%% x $$ x %%  3 "X['sm'XV%  !X\''ZW"  $WZ''ZW" 3jOQ$TRRT$QOOQ$TRRT$QOOQ$TRRT$QOjT@Q ee U ]  M +#5!#33#3#3333ve fӔB?j*66`6Z6d6`6oi/@, JHt+3'736673#':X'˰&c_ g>y:-L!B$O/6Q*P^T@QXIJH~ggW_ORQQ^R^DB97.,(&PP +"&&7667&&54>16676632#"&5463232>54.#"'2654&&'1Q."!&89&*2 (Y)R'?S)$FhES\*&*@(3B(.'.j40Z%50CH$7&62U62g0+\5:bK4'OaA46\#FN(@%JU]$L+!#533BnB-PN )@&JU]$L +!'#5353BĪBPN )@&JU]$L +!5#533BBKPN )@&JU]$L +!#5353Bb BP1$@!JH]$L+373?C P@JH]$L+373#=BB P%@"J^$L+3733?sBnP  @ J]$L+3753#>BB?P @ J]$L+3773#5>BBzP @ J]$L+3753#>BB0P@"1%@"JH]$L+3'753,CĻ1P&#@ J]$L+!'773-BU/P"$@!JH]$L+!'73,nBƸ2P( LJK&PX@]%K]$L@e]$LY@ +!#'73530Bɳ/P& %@"JH]$L +!5'73(kBsK4P&"@J]$L+!73.:B^.2P1%@"JH]$L+3573hBiSP '@$JH]$L +!753>B},WP -@*JHe]$L +!5#733;B7P '@$JH]$L +!773ԠBBPz  '@$JH]$L +!5'3>BrwP @ JH]$L+3'3#'X>ԠBBPN.@+J]$K]$L+3533#NBBBnP-N /@,J]$K]$L +35353#NBBBfPvN /@,J]$K]$L +35373#5NBBBPN /@,J]$K]$L +35353#NBBBqP1@J]$L+3'53#NנBBhPSi@J]$L+3'3#N<ܠBB xP CJKPX@]%K]$L@e]$LY+3'353##N;զBBwP @ J]$L+3'3#5P>BBPa- @ J]$L+3'3#'N<٣BB$P1"1@J]$L+3'73#5N,CC1P"@J]$L+3'73#N,éBBn2P&#@ J]$L+!''73-ȠB/UP& @J]$L+3'753#N(ȠBBk4KsP( *@'Je]$L +!5#'7330B/P&"@J]$L+!'3.hB2.^P1@J]$L+3'3#N8CC#i$P #@J]$L+3'3#]:kBB!P&@J]$L+3'3#N4ҢBB&i/P  @J]$L+3'753#P:˭BBy"]Py @ J]$L+3'3#5'N4ҢBB%iP' #@ Je]$L+3'33##_8BB#e(PF%1!@J]$L+3'73.CS3P'$@!Je$L+!#'73/n0P.&@#JH]$L+!'73'ɗB7q2*P% '@$JH]$L +!''753-ɠB2P% '@$JH]$L +!5'73)ɠBrx3sP% '@$JH]$L +!'73*ɠB93P1!@J]$L+373>CvGP@J]$L+373#=BBuCP%@"J^$L+3733>mBvnP @ J]$L+3753#=BBu9?P @ J]$L+3773#5>BBvzP @ J]$L+3753#>BBv!0P@NKJKPX@]%K]$L@e]$LY@ +!#5373}æBTnBPNKJKPX@]%K]$L@e]$LY@ +!#533vBBPN LJKPX@]%K]$L@e]$LY@ +!5#533BOBPN LJKPX@]%K]$L@e]$LY@ +!'#533B-B#P1"@J]$L+3573:CP#@ J]$L+!739B|#P!@J]$L+!'73:mBxcSP $@!J]$L +!7539B"@XP *@'Je]$L +!5#733:BP $@!J]$L +!773:B2P%1"@J]$L+3'73.C+1&P'#@ J]$L+!'73/B/0$P%!@J]$L+!'73-jB21 P% $@!J]$L +!'7753/B͠/P%"@J]$L+!573.;Bi1P" *@'Je]$L +!#'733Ȥ0BF-(P"1!@J]$L+3'3HGgqK_zL!!  +"&54473326732#4&#"#66b6F4$,.-7S\6F4$,..7T^97  !H>397  !H>D ,6KPX@10J@10JYKPX@$_MK_NK_NL@!_MK]JK_NLY@.--6.6)'   +"&5467&&546326673#'>54&#"267'HZOJTN=L)F+c$ W;$Qf)$T1!%(';p*<0KH?T?>R>7(:-=5X"j6#g ' *%,6.%-3"7 3+wj%qN~^"C^v^7RQ^J9^K?^ME^/Okb^Q^LL7J&dD@U]M +D7!V~GG-TT2dD@'U]M +D73#73o#O##O#T^c 3dD@( Jt    +D.'53#.'53+% ` '+% _ &^68 %M" 68 %M" ^K GdD@<I~gW_O      +D"&54632"&5447332673b-#&6F4$,..7T'-#~97 "#H>^0dD@% GW_O(" +D6632#4&#"DT?6F4$,..^H>97  !yJ4C ְ3+o4Cv ְ3+V& ZdDKPX@noU^N@U^NY@  +D7#7373Jn mB)A*8dD@-JW_O  +D"&'532773" 0 R =I6?;H*8dD@-JW`P  +D"&5467733267 /9 Q" "//  I]Q 'dD@W_O   +D"&54632#&#*Q;jN ڰ3+CO 3+K# &dD@Jt  +D5>73K S", 59 6;Zzq'P4 3+&uQdDK PX@oU]M@U]MY@  +D7!#7#$c$BڪnnOndD@ JKPX@nW`P@W`PY@  +D"&546733267332673#"&'-16,%/-&6'6%& !)( ) " * "%4#.9cK ۰3+8EJ ڰ3+2GQM 3+"FA f 3+HfQ 3+ c<L 3+f&dD@U]M +D7!  @@";JOQp p3+;I 3+S?&dD@U]M +D%7!FFo0+''7m$%<=dD@t +D3N-:0$Gc 3+&uQdDK PX@nU^N@U^NY@  +D73373$BB$ڪnn5'2dD@'eU]M +D73'37#++WW5^OthdD@  JKPX@oW_O@W_OY@!!"" +D6632632#6#"#6#" K06 7(06 6%0 7&;9##)) B "B "; 0+'7'77' !H/3/H"H/4/;*<;+<<+;<*;@b0dD@%gW_O +D72676&&7663" !P: "O@3&,#/2$-#0^ հ3+^zCD#^v}P7QdDK PX@oU]M@U]MY@  +D7!#7!$$BPnn"G-48 W 3+b(NdDK PX@oU]M@U]MY@  +D7#73@w 'x<9CdD@8JHGWg_O"'"$ +D'7&&#"#6632732673#"&'7-$ 4:+ -! 5:+ 9;>B 87>B A !-[dD@P gg  g  W _  O#" )'"-#- ! !  +D"&54632663232673#"&&#""&54632z:+2* 5:+/+ d9">B>Bm"]&V/WdD@Lg h W  g `   P//-+'%$#!$!$! +D63232673#"&'&&#"63232673#"&'&&#"<V" 3R& PV"  3R&  l l l l %b 0dD@%JHGU]M +D'7377#i c j c\\>>\\>b%dD@Ht +D7#7#};pG;x__xe '/7?GKOW_gowױdDKPX@6 e 754 e  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g/-+H*,+*g20,..,U20,,.^K3J1I.,.NKPX@54  ~6 e 7 g  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g/-+H*,+*g20,..,U20,,.^K3J1I.,.N@54  ~/-+&+-&~6 e 7 g  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g+H*,+*g20,..,U20,,.^K3J1I.,.NYY@ɤyxqpiha`YXQPLLHHA@9810)(!  }{xyuspwqwmkhoioec`gag][X_Y_USPWQWLOLONMHKHKJIEC@GAG=;8?9?530717-+(/)/%# '!'     L +D53#!5#53%53"5432"5432#"5432"5432!"5432"5432!"543253!53%"5432!"5432"5432!"5432"5432!"5432"5432#"5432"54325353!533353f_gLO;NQ6_5>y|b;g566fz.6ff6f66p.F3VF.p6gg666N0dD@% JU]M +D5667&&'53., 6886N3  2 D S& 3+^K BdD@7~gW_O "" +D6632#4&#""546325S?>=6",.-P-#H>EA"#g-#&e!# 0+''7'7731/12*31/1221/13*21/13p0dD@% JU]M +D.'5>73 6996 ., D 3  2(0dD@% JU]M +D5667&&'53|., 68863  2 D %dDK PX@ " JKPX@ " JKPX@ " J@ " JYYYK PX@U]MKPX@~U]MKPX@U]M@~U]MYYY@%% +D5667&&'5375>73#&&'., 6886 D 3   3  2 D  @BB@ B@kX+ 3+$qNK"dD@  Gt +D'7'7'37'j83F> F >F38*0 B"FF"B 0*@v+VdD@KJg  W  _O! '% +!+  +D"&54632632#"''2654&#"32654&#"1<<1891<<187    8227''7228((2TZdDKPX@noU^N@U^NY@  +D7#733i{"3{"TCxCxd( 1dD@&W_O    +D"&'332673QDpb;I>_]7>8=a[K 1 13+^&dD@U]M +D7!^GGSpN a 3+_4dD@)Wg_O#"#" +D663232673#".#"aD3[V[47; 6]B4\VZ47; _A?"C=$X .dD@#W_O "" +D6632#&&#">wDpb;a[_]7>8=N%3.dD@#JHGU]M +D5!7!5 [}><>\S40+''77v"v"SA@?LA@?T$dD@GU]M +D'73#&"T?KGN0dD@% JU]M +D.'5>736886 .,N D 3  2}%dDK PX@ ! JKPX@ ! JKPX@ ! J@ ! JYYYK PX@U]MKPX@~U]MKPX@U]M@~U]MYYY@%%  +D5667&&'537.'536673., 6886 3   3 3  2 D B@ B@ @Bo^d'L?M?3+:^c@U]M+7!^GG:^VLH^dLw# !dDK(PX@  J@  JYK(PX@Wg_O@+~~Wg_OY@ !!    +D"&5463256673"&54632 "X9d #C P% "Q#"w # !qdDK(PX@Wg_O@#Wge_OY@ !!    +D"&54632&&'73"&54632 !U #C#T #T "^'dD@Jt +D7>73#&&''bdXd[o8/  ^721761372##a1",UdD@J+ Jg  W  _O$#*(#,$,""  +D"&546326632#"&''27&&#"2654&#"*76+45+55+34$ # a:/+<$$'9.)>#% (18 ! 77TH)dD@ Jt +D&'773667=8( 21'<T8.# #4wy #DdD@9gW_O ##    +D"&54632"&546323"&54632 #""|  0+'7'77''7'77'$C**)@$@*+($B**)A#?*+)-75#55-55#57-75#55-55#5ACdD@8 gW_ OAA?=97$$"$$$$$ +D>32>32>32#&&#".#".#"!8+ + *$$* + +8!A!&."'..'".&!C?)********)?CEI********IEq'dD@JU_O +D"&5467734,/F&" d_ - ,dD@! JU_O +D"&54773"&544773",/F,.F&" n_ 9&" n_ #(dD@t +D3N) #( dD@t   +D"5467734"$fN~%41 3c:C 2dD@'U]M  +D6654&'3!&&54673#( <!# $F#( cF&/.&G.%HF&/cC )7JdD@?U]   M***7*710))#"  +D6654&'3!&&546733&&5467336654&'3;#(<!# $F#(O $F#( #( <!#cF&/.&G.%HF&/.%HF&/F&/.&G N 3+hFG{l l3+hGEl l3+ G|l l3+hFG&l l3+ GIl l3+lG l l3+GEgRlG l3+3+ FGSl l3+ GM(l l3+GBgXlG l3+3+lBGZl l3+hGl l3+iGl l3+hGl l3+o  3+*[1I'.dD@#W_O#" +D6632#.#"IBJl:D{VUPJ[-( (+h/GdD@<$I HggWg_O$$$$$$$" +D76323267#"&'&&#"76323267#"&'&&#" *2%#- )2&".0 (4%"- *2&!.g:$ :# :$ 9$ hGl l3+hFGl l3+hFGl l3+hGl l3+h-FGl l3+hFGl l3+lBGl l3+iDGl l3+lBGl l3+lBGl l3+iEGl l3+kHGl l3+ FGl l3+hFGl l3+lFGl l3+lBGl l3+"BGl l3+lBGl l3+hBGl l3+"BGl l3+iEG`l l3+h CCdD@8JeW_O +D"&546633##7#"3267(18'G-(-j&0h((&?&B- hFGl l3+hFGl l3+lCG Ml l3+hG Nl l3+lG Gl l3+iEGl l3+lCG Tl l3+lBGTl l3+lBGXl l3+lBGZl l3+b  3+%  3+'P 3+#R 3+NL1 3+NL$ 3+K Z 3+T#1%@dD@5gW_O!  %% +D"&54&#"3"&546323265441=! 2<=21>  1>=T91$""0;2/=:/ #!"1;10=80dD@%JgW_O$$$& +D76654&#"327#"&546324 &-!  ,@/-2@4V/-1/-5G45*VAWZdDKPX@noU^N@U^NY@  +D7#733m8.m7CxCxSkK 2dD@'U]M  +D%6654&'3!&&54673 #(<!# $F#(kF&/.&G.%HF&/ +@(W_O      +"&54632#"&54632 ##  @W_O   +"&54632.$&-#k @ Jt  +.'53Z,% ^ 'k68 %M"  k @Jt  +5>73 ((d7?k 45 :9|O 3@0 JU]M    +5>73#5>73*'Z 7?)'Z6>| 46 99 46 99r!@ Jt +5>73#&&'76U27)=r 59'O 0/r!@ Jt +&&'536673#27)=:76r'O 0/ 59y*@'HW_O  +"&5447332673U6F4$,..7Ty97  !H>uF 1@.gW_O      +"&54632'2654&#"z0<@,0=?. u722661372s1,@)Wg_O"""" +663232673#"&&#" :,2) 59+0, s>B>Br@U]M +7! rGG']Pkq 3+^Q* *  3+wFT<$ WdDJKPX@nW`P@W`PY@   +D"&547733267*"*I  $# ?1 :v:]dDKPX@noU^N@U^NY@ +D"&5433632#C,+D:;.%=sc,dD@!Wg_O%$!+D732676632#6454&#"#s *E5,:?$%b8c? ,, ]idD@ Gt%+D&&54632;;" :]8%!  ]sdD@Gt*+D5654&'&&54328  /!;]%  $!&85r2dD@'Wg_O"""+D663233#"&&#"D B566=&9T@4 @:>9B:QTdDKPX@oU]M@U]MY@ +D"&'463!2#"&7!:  hFGl l3+hBGl l3+5lG(l l3+ BGl l3+hBGl l3+hBGl l3+hBGl l3+lCGNl l3+^$k'dD@ JKPX@noW`PKPX@oW`P@W`PYY@ %#+D7#"&546773326773M" ( @ #>\kF  #( >o!jdD@ !JK PX@nW`P@W`PY@ +D'7&&5466773'76654&'>6:.S8B7:/S70.6[/8 I;8X4 G99Y6M;$.O;#-hFGRl l3+: HKPX@oU]M@U]MY@   +"&5433#%C:;5hFKPX@nU^N@U^NY@ !+53632E,+Dh5.%=,D/;fE/;E/;/;/;x/;f/;;qFE3+E3+3+װ3+3+3+3+q3+(,NdD@C~|eU^ N  +D7!#7!73!730h0H(0HH0L g #'+/dD@~~! f  e     eU]M,,(($$  ,/,/.-(+(+*)$'$'&% # #"!   "+D7#73733#7!!7!7!!7!7!!7!33333( 0 ^ ; c ; ; c ; ; c ; cC0CC0CC0CE00000000000000G;;;iEG,l l3+lBGil l3+lBGl l3+lBG>l l3+lBGJl l3+hFGDl l3+hFGl l3+h FGDl l3+h$FGFl l3+ FGl l3+hFGFl l3+hGGl l3+hFGHl l3+hGl l3+_^h&MM FGJl l3+lGKl l3+5lGL(l l3+iGil l3+lGNl l3+4lGO(l l3+ G@"l l3+lFGPl l3+kHGQl l3+iEGRl l3+?GU2 3+lFGUl l3+ lBGWl l3+hFGVl l3++hsGWl l3+hBGXl l3+lBGYl l3+lBG[l l3+lBG]l l3+\G9 )&f 3+g ʰ3+ At@ ! 5 >JKPX@ _wK_xK_tL@c_wK_xLY@<:41%# AA +"&&'&&'&&'5326654&'.546632&&#"#"&'32672="S6-L.5A&;!@k@8U&"N)>N."+A%w   ,,0*E('X:.(9%2@/B]0L@:("2B1kn - F =^@ JKPX@]oKpK_tL@c]oKpLY@  +"&&'&&##7!7!3267H2>%).D >:7#$ * <*4&F4PG<5)-J1 "@ JrKpL  +#77'73 ?X9 AX: 112g0%p JKPX@grK `xL@!grKpK `xLY@%%  +"&546736632%3%2654&#"P^?Y26QR-]qXr`>7321* KO4*ID2T2 >?),!%+/"$/KPX@ J@ JYKPX@"h _rK_ xLKPX@/UgrK _zK pK_xL@0fgrK _zK pK_xLYY@.,'%$$%%%# +333>32##3267#"&547#326654&#"rW.zFe?LMAn;>&D(&I3]e|3#%0Y?j@B;5U3 =GKm]/1)!Y"&!H7f'6@ #JK"PX@]rK `xLK(PX@rK_zK `xL@*rK_zK `xK `xLYY@)(1/(6)6! '' +"&5467332667736632#"&'#%26654&&#"?JFYH &"NE1W@$^j"CdCF`>Tk/O/0(7Q,=FC(G+ %0jXo`=zd=@9:&OC~X9%K}K>D /@,J_zK]pL &% +#73&5466323#76654&#" o;F{PbqEAoMQ=93M+" LHiUKpaM2LA0SAD8b@>Y$?'bN@ JKPX@`tL@W`PY@   +"&546733267*&3!2!  ')#C#C!7f E@B  JIe]#K]'L +5!!5!#!'!6=K9UN77?w:>)%/!*3@   JK PX@.no  e _oK ] pL@,  e _oK ] pLY@31-+*($"!!1! +33733273#7"###732654&##32654&##)U<<(.SD,=gT<7<]W?Y8@Gw`PW9EReehwH8I[ HCCkC30CC0L,C709^ '^<F'k''P'<'*@1SR*<F9@1e7<FaGaQsI_QaSV V%X72222228KH))))DDDD!(HHHHH<T9OOOO\7)H8080808080804000000:+C3030303030<F3C7C7C7C7C280280280KH0KH0KH0KH0)e0!C0)0)0)0)0)0HCHCHCHC*C)CDDDDDUd2)))) )) (C(C(C)(CH30H30H30[Hb0=)=)=)WL'ZL,SLOC7OC7OC7OC7OC7OC7Xk9\\'28084093s]pn/ps2-2Qa0&&&!0732X)))*HD2)2I((H)7) Z\H`D\<0C73D@<0E(,#0C#0730(30b&=0H0&D@0E073D@30D@0)Z)KHDDd)Z2)>)27*X))v)))2)I(*H)7)KHZ>H(w((wZ)-)J )?80!10600MC7C7 Y30CC0k0U7/Mk7}7X)> > 0C02CC7)Xk9Xk9Xk9\<<pf<f<i xafPDD* xE''' _6k="UI7ODH22aKa_aqaXM'd))<E\%4I(k280MH80Or7))0C7b@LF-,_)_Q)Y)#`EH307\/7\)H0HG0G1b@KH0a8(G7*) =8*C$b*?Qb*?*2)}Z8&*])*6HX0KH0Zy\04DZ,v)Mv/L*C<W$<W$DM|* #*Y*]vDaS(D280280840)0((M)C7)C7H30H30H30K  >>>w/M*)> 7-C0B0T0HwIBsK.*fHQ0ZS* 9280280280280280280280280280280280280)0)0)0)0)0)0)0)0DDH30H30H30H30H30H30H30H80H80H80H80H80OC7OC7Or7Or7Or7Or7Or7\\\Cz1z{NLCP7*CRE=BKKH0!P;0C0*(!H(Gn73D2)DCHH0PC=)  %L,PL,ZJOP!7<>>@)X)0))((E280DH30OC7OC7OC7OC7OC7280280840HHC2)H30H30)X)0HC)w(C280280)0)0DDH30H30=)=)OC7OC7?*C)C0,B'280)0H30H30H30H30\U[012KHC$X!2)dHC0= 5'7808CC0C0 )0CC0-C7CC 73&\M jk7k7kCCd30+0-0>>( =LLC3(X@2 @UH))#0YOC0O000,,,$o@:NNxGxGG  ^GZ0<f<<pav ` \nons]((o0 * !q0GG8:lNNNNMNNpf<JJMM.@E-11"y010\0b L30H`0)<,BU*b ?0H0 7)CKHI(=KKHKHC0Xk94 06%KaP04.`, 3530$:B:K W<HCa?/mPBPNPN8dP(mP7P#OOOdRqPtPFph,{qTfYqTExGxTDTDE%H*-xE/BG9GxGnT&0nWnTx'RxY~J9Y0T.z&.OiLTJx0z.tJNACC0>kCC>LCGp&7CA3CC0kCC08080C00@C7qG&T9.sQ!4xExY<V$$:G9G9YxxGGnTTRx:nOx[_0!+!+S+. dTX)CX)CX)CKH0)C0)C0)C0)C0)C0)0)0# )0)>HC*C*C*CC*CDD2)2)2))))I(kI(k(C(C(C(CH30H30H30H307)C7)C=)=)=)=)ZL,ZL,*LLOC)JC%OC%OC7OC7(\0(\0Xk9Xk9\CL,980<0<0<0<0<0<0<0<022++++++P=TS=S=SCCCCCCCC=Sw=ySw=qSw1w1737373737373737+=S-=/SJ=DSE1E1303030303030=S=S=SD@D@D@D@D@D@D@D@RRR100000000-=/I=S=Sx1x1<0<0CC73733030D@D@00<0<0<0<0<0<0<0<0ii++++++CCCCCCCC=S=S=S1100000000d=fI=S=S11<0<0<0<0<0<0<02222iA**CCCCCEC@3C3*AAQ73737,7377.DDC3AAQD@D@D@D@==D@D@\\SGSAAA600000%S8<S*A@+';'fk,.De [aeaja_qDn:D'H'9'>k'#l2+''#H2KH'\j@1!OK+ 7)=8L*T])H0lk9 )0RUlxey``?A*#SzTL<0<0<0<0<0<0<0<07373737373737373D@D@D@D@D@D@D@D@73737373D@D@D@D@)(Qm*+3**+*E(EA' *I( 2W>>HT*Z!( &&!K2)2)2)6x)MHH7zj@xH H(Xk7)74-RH+!++`KKp(K2 =)G?-+/vqXG2)DO)(,#d030zO;' s2C,(d0V0u0000Yi300C!;C0099CCG70,:/d/&CCCE122800CC7~@@g------%/%//-/-/-/-/-333vvPPPPPPQ-Q-Q-Q-Q-Q-Q-Q-Q Q Q--Q-H;3;%./3/3/3/3/3/3/3/3/3/3/3<FFFFF=====Q!J'0"WI8x[! ,GwMoZ#*I*2*D*HHHv3wZZY)*HHY!XDE#.#ekudOED/ 73o1X5.*08Y7F_30T30EbM=+L  =C'030,#CC7k7k7qTBPT]*XK)2 dU^ER=/ QJ ]"A  ax 2m2 a aa=a&$(a!2)[8!!IC  W Wo 1CCD0C>32E;< 40>C}\8kCC,,3e0aae0e0o"^2t,O,C7Ck8kN#+"+"C80^uxx4{YP3TxGAAC**/9d'''''.<( ' #+ *,'9_"6a a@aaaa aaaaaJaua+a7a1aEaSa[a;aISXJS[+&7:1S:E&SS Saa"aaaaaaaa^6X""USi\'L;5554=5<564D5d{>>5''45\MM3##<E2WiSxx>>5'<f=5c5'F'F'F$T H) '545p;@\fL:*#Cz QMPMMPM(A999 X9DB& !i@iBtXXXXXXXXXXX9F<9KKa%B"tRRRxRRIIPRRKl4`3[=Q7= c(*7M",=)c=)J *8((D*NNNN "&"(&&#' NNNN""&&(&#'%'.%%%NNNN%'%%%""#""""'%'%%%??NNNN%GGGGG3%%%%%%%%"%%-****+NNNN*073%~b-JoVH9OD9>]*CKZq.2" J;o5#}-bbepk$KdSNo::H_ESq)*Ib'S ****Ak vsB5og_54? +g\7Nl')61Z(Z(Z(Z(Z(Z(Z(Z(NNNNNNNNNNONNONTTTT LpH4l| X H h @(d@|DDXtlxp t8!!"#P$<%P&&'H'())*0+ +,-<-.///001123,3,34`556h678t9::;(;8>? ?0??@d@AdAABBPBpBBBCC$CHCCDD4DXD|DDDE EEEFF,FPFtFGGGGHH8HIIJ J$J<JTJlKLL(L@LXLpLLLLMMMMNN N8NOOOOPPQQ QDQ\QQQQQRR(R@RdR|RRRRSTHTlTTTTTUVDVhVVVVVWW4WLWdWWX<YY<YTYxYYYYYZ Z8ZPZtZZZ[ [D[h[[[[[\\\]]0]H]`]]]^X_@_d_|____a(b8b\btbbbbccc@cXcpcccccdd0deeeeff(f@fdf|ffffg g$gHg`gggggh h8hijl@ldl|llllmTmnnpnoDop8pqrr,rLrprrrss,sDsTsdsssstttu0u@uPuvvhvxvw wxxxxyy0yHy`yxyz{t{|}p~~0t0dp ( 0$DTx<`PXt$4tpL 0@ H8$\$<P`p,4Lph0tH( , D\ \|<0˜LÄü,Ĩ4ŘƬTǠhhhhhhhhhhhhhh`,DTp<@Xpш Өh8լ $h8۸0Xިߠ\dt4 0H`xH D\|@Tx| 4$ ,p<0Ph p`D    ` <   0@d|P| ,Xh$<,Ph@Xhx,Ph 8    !"4"#P#$@$P%%''()p*D+X, ,-.@./\0(081X2|22223 3$3H3`333344$4H4`4444455@5`5x555566,6D6h666667$7D7h77777888@8X8|88889 989X9|9999: :0:H:`:x::::;; ;D;\;;;;;<<<@>?@A<BLC<CD8DPDhEFFGHtIPIpJ<K0K@KLMMN N0OOPlQRhRSTTTU0VlWHWWX Y YZ[\P]]]^_`Ha8abdc@cdeDeefdgghi ijk@kklHlXlllllm m$m<mTmxmmmmnn,nDnhnnnnnoo4oXopoooopqrr(rLrprrrrss(s@sXspsst|u$uHu`uuuuuvv8vPvtvvvvww(w@wdw|wwwwxx0xytyyzl{|}~D~T~x~~~~~ 8\t8X44|(H4d`( 0P 0p,<LD8t(8l|(\HH0Px$l|l,„8DT@ŨȄ0@ψԜT|؀@dڈڬ(8HXۼܔ8Xݤ$Ddބޤ @`߀ߠ8\84|8Xh$PpX0|l|@P,<d,Pdt,,P`pTP<`$h T(0x@h\D<    ( L $ H l      D h   8 \    4X| 0Tx,Pt(Lp$Hl0Ppxpd|8 @!x"4#D#h$ &H&'X()0)+p,.8/01$2h3h456|78,89`::< => ?(@8A ABCDDEE@EdEEEEFF<F`FFFFGG8G\GGGGHH4HXH|HHHI I0ITIxIIIJJ,JDJ\JtJJJKK,KDK\KtKKLtMNN(NLNdNNNNOO$OPOpOOOOPP<PTPlPPPPQQ,QDQhQQQQRRR@RdR|RRRSS4SLSdSSSSST T$T<T`TTTTTUU,UPUhUUUUVVV@VXVpVVVVWW@WXWpWWWWXX,XLXpXXXXYY Y8Y\YYYYYZZZ@ZXZ|ZZZZ[[[0[T[l[[[[\\ \8\P\t\\\\] ]$]H]`]x]]]]]^^,^P^t^^^__(_@_X_p____``(`P`x````aaa0aHa`aaaab bHbpbbbbbcc(c@cXc|cccdd@dhdddddee eDeheeeff f8fPfhfffffgg<gdg|gggggh h$hHhlhhhi i4i\i|iiiijj(j@j`jxjjjjkk(kDk`k|kkkkl(lTlllmm0mPmpmmmmnn0n\nnnooHoxooopp p<pXptpppqqHqxqqrr r8r`rxrrrrss4sXspssstttuu4uLuluuuuvw wxxxyy y8yPytyyyz{L|| |8|X|p|||||}}@}h}~dp 8X|T<@XL\ `8Dd@$x|h $\(t@xXp4@T$h,Lx@4Ld| $<Tl,D\t4L0@DhlPhDLĈ| 40($XΐϐМ0X<մLظ8ܐ4ݰTް`l4xX$<pD`ppl(H0H,t$Tt||0x$0@X4Tt,(4Ld|         ,<\lt$HT !#`#p$ $%&&x'@(|()*,$-,.T./t/013 3l3|4h567P89p:;d<=><???@AxACCDEFtGHH HHHI IJPJhJJJJJL<N8PP,PDQS\SSTTT4TLTlTTTTUhUV$VVVWW$W<WX0XPX`XXXYY0YHYhYYYZ ZZZ[[ [x\\$\x\\\\] ]$]<]\]t]]^ ^^^^__,_P_`(````aab(b@b`bbbbbccce$effg$g<g\gthh4hThlhhiij\j|jjkk4kTktkkkkkll<lm8mPmpmmmnDn\n|nnooo8oXo|opXpq(qHqrXrstuvxxxy0yz{|4|}~ hL@`LXXT`,<L\ tXX,8@tXt4H`xh,P`L\ 0lLx|HxʴH̼ϰ8М4$xԄ֨ؤ4<޴ߌX$@d, <\LH `pH  ( $   $D@P,pD <\|@dhx4@`8 4  !!! !0!@!P!`!p!!!!"" "@"`""""## #@#`####$$$8$X$x$$$$%%8%`%%%%&& &@&`&&&'0''(@((())p)+\, ,-.80,0D112P23l4404h44556 646p7<7778P????@ @(@@A4BXBBCtCDDEEEFFGDGHtHItJ,JKKDL4MM(MHMhMMN NNOOPPQpS8STTdTtTTTU<U|UUUVWWX XDXTXdXXXYYHYpYZZ[t[\\p\\]D]^D__`_t_t_t_t_t_t_t_t_t_t_t_t_t`a4abd(dfgg$ggh0hi@i`ij$jDjkTmmnn\noo8oXoqLrsstv v$v4vx8x{||$|}}~Ht@4d< 0|t l ` Htl\T$hL\4xd P,t,$plP@$x(lDh0(x hP8,`<h,|\PD@(<Pdx$@ ,(xLPp\DTd(Hh d\p,L`p0Xà ȔȰ@x`P$ͤ(lΌ p4Ш8t҈ҘӘt`0|֠Hp؈hDڰ`ܼݠ,޼߰<`(`$d<`8\4X4X| 0TTt4l|<\$|,X0@d|$ Dh@d0Tx,Pt(Lp TlX$Dx P p   d    x  P    \Ll - m ^ ^  p 4|    ` D~ * ( B >V <  4fCopyright 2015 Google Inc. All Rights Reserved.Noto SansItalic2.000;GOOG;NotoSans-ItalicNoto Sans ItalicVersion 2.000;GOOG;noto-source:20170915:90ef993387c0; ttfautohint (v1.7)NotoSans-ItalicNoto is a trademark of Google Inc.Monotype Imaging Inc.Monotype Design TeamDesigned by Monotype design team.http://www.google.com/get/noto/http://www.monotype.com/studioThis Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.http://scripts.sil.org/OFL2   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a  bcdefghjikmlnoqprsutvwxzy{}|~    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  NULLCRuni00A0uni00AD overscoreuni00B2uni00B3uni03BCuni00B9AmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflexCdotcdotDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGdotgdotuni0122uni0123 Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflexuni0136uni0137 kgreenlandicLacutelacuteuni013Buni013CLcaronlcaronLdotldotNacutenacuteuni0145uni0146Ncaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracuteuni0156uni0157RcaronrcaronSacutesacute Scircumflex scircumflexuni021Auni021BTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongs Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0218uni0219tonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammaEpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsi IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdanuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygrave afii00208 underscoredbl quotereversedminutesecond exclamdbl nsuperior afii08941pesetaEuro afii61248 afii61289 afii61352uni03A9 estimated oneeighth threeeighths fiveeighths seveneighthsuni0394 cyrillicbrevecaroncommaaccentcommaaccentrotateuni2074uni2075uni2077uni2078uni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni02F3OhornohornUhornuhornhookuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCuni0162uni0163uni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019Funi01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5uni01E6uni01E7uni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9uni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217uni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236uni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0242uni0243uni0244uni0245uni0246uni0247uni0248uni0249uni024Auni024Buni024Cuni024Duni024Euni024Funi0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Buni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268 iotaLatinuni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276 omegacloseduni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295 glottalturneduni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2dzliguni02A4 dzligcurltsliguni02A7 tcligcurluni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BAuni02BBuni02BDuni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DFuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF hookabovecombuni0374uni0375uni037Auni037Buni037Cuni037Duni037Euni03D0uni03D3uni03D4phi1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni051Auni051Buni051Cuni051Duni1D00uni1D01uni1D02uni1D03uni1D04uni1D05uni1D06uni1D07uni1D08uni1D09uni1D0Auni1D0Buni1D0Cuni1D0Duni1D0Euni1D0Funi1D10uni1D11uni1D12uni1D13uni1D14uni1D15uni1D16uni1D17uni1D18uni1D19uni1D1Auni1D1Buni1D1Cuni1D1Duni1D1Euni1D1Funi1D20uni1D21uni1D22uni1D23uni1D24uni1D25uni1D26uni1D27uni1D28uni1D29uni1D2Auni1D2Buni1D2Cuni1D2Duni1D2Euni1D2Funi1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Duni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D5Cuni1D5Duni1D5Euni1D5Funi1D60uni1D61uni1D62uni1D63uni1D64uni1D65uni1D66uni1D67uni1D68uni1D69uni1D6Auni1D6Buni1D6Cuni1D6Duni1D6Euni1D6Funi1D70uni1D71uni1D72uni1D73uni1D74uni1D75uni1D76uni1D77uni1D78uni1D79uni1D7Auni1D7Buni1D7Cuni1D7Duni1D7Euni1D7Funi1D80uni1D81uni1D82uni1D83uni1D84uni1D85uni1D86uni1D87uni1D88uni1D89uni1D8Auni1D8Buni1D8Cuni1D8Duni1D8Euni1D8Funi1D90uni1D91uni1D92uni1D93uni1D94uni1D95uni1D96uni1D97uni1D98uni1D99uni1D9Auni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB8uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7Funi1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni200Cuni200Duni200Euni200Funi2012uni2016uni201Funi202Auni202Buni202Cuni202Duni202Euni202Funi2034uni203Euni205Euni206Auni206Buni206Cuni206Duni206Euni206Funi2070uni2076uni2079uni2090uni2091uni2092uni2093uni2094uni20A0uni20A1uni20A2uni20A5uni20A6uni20A8uni20A9uni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20B9uni20F0uni2117uni214Duni214Euni2153uni2154uni2184uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C6Duni2C71uni2C72uni2C73uni2C74uni2C75uni2C76uni2C77uni2E17uniA717uniA718uniA719uniA71AuniA71BuniA71CuniA71DuniA71EuniA71FuniA720uniA721uniA788uniA789uniA78AuniA78BuniA78C dieresisacute dieresisgraveuniFE20uniFE21uniFE22uniFE23uni03B1030403130300uni03B1030403130301uni03B1030403140300uni03B1030403140301uni03B1030603130300uni03B1030603130301uni03B1030603140300uni03B1030603140301uni03B9030403130300uni03B9030403130301uni03B9030403140300uni03B9030403140301uni03B9030603130300uni03B9030603130301uni03B9030603140300uni03B9030603140301uni03C5030403130300uni03C5030403130301uni03C5030403140300uni03C5030403140301uni03C5030603130300uni03C5030603130301uni03C5030603140300uni03C5030603140301uni03B9030803040300uni03B9030803040301uni03B9030803060300uni03B9030803060301uni03C5030803040300uni03C5030803040301uni03C5030803060300uni03C5030803060301Eng.alt1Eng.alt2Eng.alt3 uni1FCD02C9 uni1FCE02C9 uni1FDD02C9 uni1FDE02C9dotacutecarondotmacrondieresis tildedieresis tildeacute macrongrave macronacute dieresiscarondieresismacron tildemacron dotmacron dotmacron.capuni030103060308uni030003060308uni030103040308uni030003040308 uni1FDE0306 uni1FDD0306 uni1FCE0306 uni1FCD0306uni0514uni0515uni0516uni0517uni0518uni0519uni051Euni051Funi0520uni0521uni0522uni0523uni0524uni0525uni0526uni0527cyrillic_otmarkuni20BAuni1EFAuni2C6Euni1E9ETurnedauni1EFCuni1EFEuniA722uniA724uniA726uniA728uniA72AuniA72CuniA72EuniA732uniA734uniA736uniA738uniA73AuniA73CuniA73EuniA740uniA742uniA744uniA746uniA748uniA74AuniA74CuniA74EuniA750uniA752uniA754uniA756uniA758uniA75AuniA75CuniA75EuniA760uniA764uniA766uniA768uniA76AuniA76CuniA76EuniA779uniA77BuniA77DuniA77EuniA780uniA782uniA784uniA786uniA78DuniA790uniA792uniA7A0uniA7A2uniA7A4uniA7A6uniA7A8uniA7AAuniA7ABuniA7ACuniA7ADuniA7B0uniA7B1uniA7B2uniA7B3uniA7B4uniA7B6Aogonek.loclNAVEogonek.loclNAVIogonek.loclNAVUogonek.loclNAVLcommaaccent.loclMAHNcommaaccent.loclMAHTurnedeafii10103dotlessafii10105dotless deltalatinuni2C78uni025Cuni025Duni01DDuni025Aiogonekdotlessuni0237uni1EFBuni1E9Cuni1E9Duni2C7A subscriptjuni2C79uni0249dotlessuni029Ddotlessuni02B2dotlessuni03F3dotlessuni1D62dotlessuni1D96dotlessuni1DA4dotlessuni1DA8dotlessuni1E2Ddotlessuni1ECBdotlessuniA723uniA725uniA727uniA729uniA72BuniA72DuniA72FuniA730uniA731uniA733uniA735uniA737uniA739uniA73BuniA73DuniA73FuniA741uniA743uniA745uniA747uniA749uniA74BuniA74DuniA74FuniA751uniA753uniA755uniA757uniA759uniA75BuniA75DuniA75FuniA761uniA765uniA767uniA769uniA76BuniA76DuniA76FuniA771uniA772uniA773uniA774uniA775uniA776uniA777uniA778uniA77AuniA77CuniA77FuniA781uniA783uniA785uniA787uniA78EuniA791uniA793uniA7A1uniA7A3uniA7A5uniA7A7uniA7A9uniA7B5uniA7B7uniA7FAuni1EFDuni1EFFaogonek.loclNAVeogonek.loclNAVlcommaaccent.loclMAHncommaaccent.loclMAHiogonek.loclNAVuogonek.loclNAVf_ff_f_if_f_lf_if_llongs_ts_ta.sc aacute.sc abreve.scacircumflex.sc adieresis.sc agrave.sc amacron.sc aogonek.scaring.sc aringacute.sc atilde.scae.sc aeacute.scb.scc.sc cacute.sc ccaron.sc ccedilla.scccircumflex.sccdot.scd.sceth.sc dcaron.sc dcroat.sce.sc eacute.sc ebreve.sc ecaron.scecircumflex.sc edieresis.sc edotaccent.sc egrave.sc emacron.sc eogonek.scf.scg.sc gbreve.scgcircumflex.scgcommaaccent.scgdot.sch.schbar.schcircumflex.sci.sc iacute.sc ibreve.scicircumflex.sc idieresis.sc idotaccent.sc igrave.scij.sc imacron.sc iogonek.sc itilde.scj.scjcircumflex.sck.sckcommaaccent.scl.sc lacute.sc lcaron.sclcommaaccent.scldot.sc lslash.scm.scn.sc nacute.sc ncaron.scncommaaccent.sceng.sc ntilde.sco.sc oacute.sc obreve.scocircumflex.sc odieresis.sc ograve.scohungarumlaut.sc omacron.sc oslash.scoslashacute.sc otilde.scoe.scp.scthorn.scq.scr.sc racute.sc rcaron.scrcommaaccent.scs.sc sacute.sc scaron.sc scedilla.scscircumflex.scscommaaccent.sc germandbls.sct.sctbar.sc tcaron.sc tcedilla.sctcommaaccent.scu.sc uacute.sc ubreve.scucircumflex.sc udieresis.sc ugrave.scuhungarumlaut.sc umacron.sc uogonek.scuring.sc utilde.scv.scw.sc wacute.scwcircumflex.sc wdieresis.sc wgrave.scx.scy.sc yacute.scycircumflex.sc ydieresis.sc ygrave.scz.sc zacute.sc zcaron.sc zdotaccent.scuni2071uniA78FuniA7F7uniA7FBuniA7FCuniA7FDuniA7FEuniA7FFuni0528uni052Auni052Cuni052EuniA640uniA642uniA644uniA646uniA648uniA64AuniA64CuniA64EuniA650uniA652uniA654uniA656uniA658uniA65AuniA65CuniA65EuniA660uniA662uniA664uniA666uniA668uniA66AuniA66CuniA680uniA682uniA684uniA686uniA688uniA68AuniA68CuniA68EuniA690uniA692uniA694uniA696uniA698uniA69Auni0529uni052Buni052Duni052Funi1C80uni1C81uni1C82uni1C83uni1C84uni1C85uni1C86uni1C87uni1C88uniA641uniA643uniA645uniA647uniA649uniA64BuniA64DuniA64FuniA651uniA653uniA655uniA657uniA659uniA65BuniA65DuniA65FuniA661uniA663uniA665uniA667uniA669uniA66BuniA66DuniA681uniA683uniA685uniA687uniA689uniA68BuniA68DuniA68FuniA691uniA693uniA695uniA697uniA699uniA69Bafii10066.loclSRBafii10068.loclSRBafii10069.loclSRBafii10081.loclSRBafii10084.loclSRBafii10090.loclSRBuniA66EuniA67FuniA69CuniA69Duni2126uni0370uni0372uni0376uni03CFuni037Funi0371uni0373uni0377 uni03D0.altCfrakturHfrakturIfrakturRfrakturZfrakturuniA762uniA763uni212Cuni210Buni2110uni2112PiDoubleStruckuni211BTurnedFuni212B CDoubleStruck HDoubleStruck NDoubleStruck PDoubleStruck QDoubleStruck RDoubleStruck ZDoubleStruckItalicDDoubleStruckGammaDoubleStruckuni2107uni212Auni2130uni2131uni2133uniA796uniA798uniA79AuniA79CuniA79EItalicdDoubleStruckItaliceDoubleStruckItaliciDoubleStruckItalicjDoubleStruckgammaDoubleStruckpiDoubleStruckuni210Euni210FscriptescriptoscriptguniA794uniA795uniA797uniA799uniA79BuniA79DuniA79FuniAB30uniAB31uniAB32uniAB33uniAB34uniAB35uniAB36uniAB37uniAB38uniAB39uniAB3AuniAB3BuniAB3CuniAB3DuniAB3EuniAB3FuniAB40uniAB41uniAB42uniAB43uniAB44uniAB45uniAB46uniAB47uniAB48uniAB49uniAB4AuniAB4BuniAB4CuniAB4DuniAB4EuniAB4FuniAB50uniAB51uniAB52uniAB53uniAB54uniAB55uniAB56uniAB57uniAB58uniAB59uniAB5AuniAB64uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209CuniA770uniA7F8uniA7F9uniAB5CuniAB5DuniAB5EuniAB5Funi2E2Fzero.lfone.lftwo.lfthree.lffour.lffive.lfsix.lfseven.lfeight.lfnine.lfzero.osfone.osftwo.osf three.osffour.osffive.osfsix.osf seven.osf eight.osfnine.osf zero.slash zero.tosfone.tosftwo.tosf three.tosf four.tosf five.tosfsix.tosf seven.tosf eight.tosf nine.tosf zero.dnomone.dnomtwo.dnom three.dnom four.dnom five.dnomsix.dnom seven.dnom eight.dnom nine.dnom zero.numrone.numrtwo.numr three.numr four.numr five.numrsix.numr seven.numr eight.numr nine.numruni215Funi2189uni2155uni2156uni2157uni2158uni2159uni215Auni2150uni2151uni2152uni2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni2042uni204Cuni204Duni2045uni2046caretuni2041uni2040uni2050uni2E36uni2E37uni205Cuni2E13uni2E16uni2E08downwardsancorauni2E0Euni2049uni2E2Duni2059uni2055uni2E10uni205Buni2058uni2027uni2043uni2E12uni2E18uni2054uni2E04uni2E1Cuni2E0Cuni2E02uni2E09uni2E20uni204Eonedotenleaderonedotovertwodotspunctuationuni2E19uni2E0Funi2047uni2048uni2E34uni2E33uni2E07uni2E06uni2E0Buni203Buni2E11reversedparagraphreversedquestionmarkuni204Funi2E01uni2E00uni2E05uni2E1Duni2E0Duni2E03uni2E0Auni2E21uni2E30squaredfourdotpunctuationuni2053uni2056uni2E1Euni2E1Funi2E1Buni204Auni2E39uni205Duni2E32uni2E38uni2E35uni2051twodotenleaderuni205Atwodotsoveronedotpunctuationuni203Funi2023uni2E3Cuni2E3Duni2E3Euni2E3Funi2E41uniA673 upwardsancorauni2E31uni208Duni208Ebrackhalfbottomleftbrackhalfbottomrightbrackhalftopleftbrackhalftoprightleftdoubleparenthesisrightdoubleparenthesisuni2E26uni2E27uni207Duni207Euni2E1Auni2010uni2011uni2E3Buni2E3Auni2E40uni2036uni2035uni2057uni2037uni2E42 braceleft.sc braceright.scbracketleft.scbracketright.sc exclam.sc exclamdbl.sc exclamdown.sc parenleft.sc parenright.sc question.scquestiondown.scuniA92EuniA67Euni205Funi2028uni2029uni2061uni2064uni2063uni2062uni2066uni2067uni2068uni2069uni2060uni20B6uni20BCuni20BDuni20AAuni20B7uni20B8uni20BBuni20BEuni2127uni2135uni214Buni2136uni2052uni2138uni208Cuni207Cuni2137uni208Buni207Buni00B5uni2031uni208Auni207AreversedSansSerifLsummationDoubleStruckturnedSansSerifGturnedSansSerifLturnedSansSerifYRotatedQuni2100uni2101uni2106uni2103uni2104uni213Buni2109uni2139uni203Duni2114uni2125uni214C prescriptionuni214Auni211Funi2108uni2120symbolforsamaritansourceuni2121uni2123 weierstrassuni02DEuni02E5_uni02E5_uni02E9uni02E5_uni02E5_uni02E6uni02E5_uni02E5_uni02E8uni02E5_uni02E5_uni02E7uni02E5_uni02E9uni02E5_uni02E9_uni02E5uni02E5_uni02E9_uni02E9uni02E5_uni02E9_uni02E6uni02E5_uni02E9_uni02E8uni02E5_uni02E9_uni02E7uni02E5_uni02E6uni02E5_uni02E6_uni02E5uni02E5_uni02E6_uni02E9uni02E5_uni02E6_uni02E6uni02E5_uni02E6_uni02E8uni02E5_uni02E6_uni02E7uni02E5_uni02E8uni02E5_uni02E8_uni02E5uni02E5_uni02E8_uni02E9uni02E5_uni02E8_uni02E6uni02E5_uni02E8_uni02E8uni02E5_uni02E8_uni02E7uni02E5_uni02E7uni02E5_uni02E7_uni02E5uni02E5_uni02E7_uni02E9uni02E5_uni02E7_uni02E6uni02E5_uni02E7_uni02E8uni02E5_uni02E7_uni02E7uni02E9_uni02E5uni02E9_uni02E5_uni02E5uni02E9_uni02E5_uni02E9uni02E9_uni02E5_uni02E6uni02E9_uni02E5_uni02E8uni02E9_uni02E5_uni02E7uni02E9_uni02E9_uni02E5uni02E9_uni02E9_uni02E6uni02E9_uni02E9_uni02E8uni02E9_uni02E9_uni02E7uni02E9_uni02E6uni02E9_uni02E6_uni02E5uni02E9_uni02E6_uni02E9uni02E9_uni02E6_uni02E6uni02E9_uni02E6_uni02E8uni02E9_uni02E6_uni02E7uni02E9_uni02E8uni02E9_uni02E8_uni02E5uni02E9_uni02E8_uni02E9uni02E9_uni02E8_uni02E6uni02E9_uni02E8_uni02E8uni02E9_uni02E8_uni02E7uni02E9_uni02E7uni02E9_uni02E7_uni02E5uni02E9_uni02E7_uni02E9uni02E9_uni02E7_uni02E6uni02E9_uni02E7_uni02E8uni02E9_uni02E7_uni02E7uni02E6_uni02E5uni02E6_uni02E5_uni02E5uni02E6_uni02E5_uni02E9uni02E6_uni02E5_uni02E6uni02E6_uni02E5_uni02E8uni02E6_uni02E5_uni02E7uni02E6_uni02E9uni02E6_uni02E9_uni02E5uni02E6_uni02E9_uni02E9uni02E6_uni02E9_uni02E6uni02E6_uni02E9_uni02E8uni02E6_uni02E9_uni02E7uni02E6_uni02E6_uni02E5uni02E6_uni02E6_uni02E9uni02E6_uni02E6_uni02E8uni02E6_uni02E6_uni02E7uni02E6_uni02E8uni02E6_uni02E8_uni02E5uni02E6_uni02E8_uni02E9uni02E6_uni02E8_uni02E6uni02E6_uni02E8_uni02E8uni02E6_uni02E8_uni02E7uni02E6_uni02E7uni02E6_uni02E7_uni02E5uni02E6_uni02E7_uni02E9uni02E6_uni02E7_uni02E6uni02E6_uni02E7_uni02E8uni02E6_uni02E7_uni02E7uni02E8_uni02E5uni02E8_uni02E5_uni02E5uni02E8_uni02E5_uni02E9uni02E8_uni02E5_uni02E6uni02E8_uni02E5_uni02E8uni02E8_uni02E5_uni02E7uni02E8_uni02E9uni02E8_uni02E9_uni02E5uni02E8_uni02E9_uni02E9uni02E8_uni02E9_uni02E6uni02E8_uni02E9_uni02E8uni02E8_uni02E9_uni02E7uni02E8_uni02E6uni02E8_uni02E6_uni02E5uni02E8_uni02E6_uni02E9uni02E8_uni02E6_uni02E6uni02E8_uni02E6_uni02E8uni02E8_uni02E6_uni02E7uni02E8_uni02E8_uni02E5uni02E8_uni02E8_uni02E9uni02E8_uni02E8_uni02E6uni02E8_uni02E8_uni02E7uni02E8_uni02E7uni02E8_uni02E7_uni02E5uni02E8_uni02E7_uni02E9uni02E8_uni02E7_uni02E6uni02E8_uni02E7_uni02E8uni02E8_uni02E7_uni02E7uni02E7_uni02E5uni02E7_uni02E5_uni02E5uni02E7_uni02E5_uni02E9uni02E7_uni02E5_uni02E6uni02E7_uni02E5_uni02E8uni02E7_uni02E5_uni02E7uni02E7_uni02E9uni02E7_uni02E9_uni02E5uni02E7_uni02E9_uni02E9uni02E7_uni02E9_uni02E6uni02E7_uni02E9_uni02E8uni02E7_uni02E9_uni02E7uni02E7_uni02E6uni02E7_uni02E6_uni02E5uni02E7_uni02E6_uni02E9uni02E7_uni02E6_uni02E6uni02E7_uni02E6_uni02E8uni02E7_uni02E6_uni02E7uni02E7_uni02E8uni02E7_uni02E8_uni02E5uni02E7_uni02E8_uni02E9uni02E7_uni02E8_uni02E6uni02E7_uni02E8_uni02E8uni02E7_uni02E8_uni02E7uni02E7_uni02E7_uni02E5uni02E7_uni02E7_uni02E9uni02E7_uni02E7_uni02E6uni02E7_uni02E7_uni02E8uniAB5B ampersand.scuni2129uni0308uni0307 gravecomb acutecombuni030Buni0302uni030Cuni0306uni030A tildecombuni0304 overlinecmbuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320hookpalatalizedbelowcombhookretroflexbelowcomb dotbelowcombuni0324uni0325uni0326uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334strokeshortoverlaycombstrokelongoverlaycombslashshortoverlaycombslashlongoverlaycombuni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni034Funi0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035Auni035Buni035Cuni035Duni035Euni035Funi0360uni0361uni0362acutegraveacute acutemacronuni1DFEuni1DFF brevemacroncombiningconjoiningmacroncombiningmacronlefthalfcombiningmacronrighthalf dottedacute dottedgravedoublecircumflexabovegraveacutegrave gravemacronuni1DD0uni1DC4 macronbreveuni1DC6 ogonekabove snakebelowsuspensionmarkuni1AB0uni1AB1uni1AB2uni1AB3uni1AB4uni1AB5uni1AB6uni1AB7uni1AB8uni1AB9uni1ABAuni1ABBuni1ABCuni1ABDuni1DE7uni1DE8uni1DE9uni1DEAuni1DEBuni1DECuni1DEDuni1DEEuni1DEFuni1DF0uni1DF1uni1DF2uni1DF3uni1DF4uni1DF5uni1DFBuni1DFCuni1DFDuni2DE0uni2DE1uni2DE2uni2DE3uni2DE4uni2DE5uni2DE6uni2DE7uni2DE8uni2DE9uni2DEAuni2DEBuni2DECuni2DEDuni2DEEuni2DEFuni2DF0uni2DF1uni2DF2uni2DF3uni2DF4uni2DF5uni2DF6uni2DF7uni2DF8uni2DF9uni2DFAuni2DFBuni2DFCuni2DFDuni2DFEuni2DFFuniFE00uniFE27uniFE28uniFE29uniFE2AuniFE2BuniFE2CuniFE2Duraboveusabove zigzagbelowuni1ABEdieresiscomb.scdotaccentcomb.sc gravecomb.sc acutecomb.schungarumlautcomb.sccircumflexcomb.sc caroncomb.sc brevecomb.sc ringcomb.sc tildecomb.sc macroncomb.sc ogonekcomb.sc overscore.scuni0342uni0343uni0344uni0345uni0483uni0484uni0485uni0486uni0487uniA66FuniA674uniA675uniA676uniA677uniA678uniA679uniA67AuniA67BuniA67CuniA67DuniA69EuniA69FuniFE2EuniFE2FuniA670uniA671uniA672uni1DDBuni1DDEuni1DDFuni1DE1uni1DE2uni0363uni1DD4uni1DD5uni1DD6uni1DD7uni0368uni0369uni0364uni1DD9flattenedopenaaboveuni1DDAuni036Auni0365uni1DD8uni1DDCuni1DDDuni1DE5uni036Buni1DE0uni0366uni1DCAuni036Cuni1DE3uni1DE4uni036Duni0367uni036Euni036Funi1DE6uni2C7Duni2C70uni2C7Euni2C7FuniA7AEuniAB60uniAB61uniAB62uniAB63uniAB65ogonekcenteringsummationDoubleStruck.miruni20BFuni2E43uni2E44uniA700uniA701uniA702uniA703uniA704uniA705uniA706uniA707uniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716YYII-"-YYII-!-\\LL;-D-EE>>h-h-EE>>--YYII-"-::,,+~a-4wa-::,,a-g-, UXEY KQKSZX4(Y`f UX%acc#b!!YC#DC`B-, `f-, d P&Z( CEcEEX!%YR[X!#!X PPX!@Y 8PX!8YY  CEcEad(PX! CEcE 0PX!0Y PX f a PX` PX! ` 6PX!6``YYY% CcRXK PX! CKPX!Kac CcbYYdaY+YY#PXeYY-, E %ad CPX#B#B!!Y`-,#!#! dbB #BEX CEc C`Ec*! C +0%&QX`PaRYX#Y!Y @SX+!@Y#PXeY-,C+C`B-,#B# #Babfc`*-, E Ccb PX@`Yfc`D`-, CEB*!C`B- ,C#DC`B- , E +#C%` E#a d PX!0PX @YY#PXeY%#aDD`- , E +#C%` E#a d$PX@Y#PXeY%#aDD`- , #B EX!#!Y*!- ,EdaD-,` CJPX #BYCJRX #BY-, bfc c#aC` ` #B#-,KTXdDY$ e#x-,KQXKSXdDY!Y$e#x-,CUXCaB+YC%B %B%B# %PXC`%B #a*!#a #a*!C`%B%a*!Y CGCG`b PX@`Yfc Ccb PX@`Yfc`#DC>C`B-,ETX#B E #B #`B `aBB`++"Y-,+-,+-,+-,+-,+-,+-,+-,+-,+-, +-),# bfc`KTX# .]!!Y-*,# bfc`KTX# .q!!Y-+,# bfc&`KTX# .r!!Y-, +ETX#B E #B #`B `aBB`++"Y-,+- ,+-!,+-",+-#,+-$,+-%,+-&,+-',+-(, +-,, <`--, `` C#`C%a`,*!-.,-+-*-/, G Ccb PX@`Yfc`#a8# UX G Ccb PX@`Yfc`#a8!Y-0,ETX EB/*EX0Y"Y-1, +ETX EB/*EX0Y"Y-2, 5`-3, EBEcb PX@`Yfc+ Ccb PX@`Yfc+D>#82*!-4, < G Ccb PX@`Yfc`Ca8-5,.<-6, < G Ccb PX@`Yfc`CaCc8-7,% . G#B%IG#G#a Xb!Y#B6*-8,#B%%G#G#a B C+e.# <8-9,#B%% .G#G#a #B B C+ `PX @QX  &YBB# C #G#G#a#F`Cb PX@`Yfc` + a C`d#CadPXCaC`Y%b PX@`Yfca# &#Fa8#CF%CG#G#a` Cb PX@`Yfc`# +#C`+%a%b PX@`Yfc&a %`d#%`dPX!#!Y# &#Fa8Y-:,#B & .G#G#a#<8-;,#B #B F#G+#a8-<,#B%%G#G#aTX. <#!%%G#G#a %%G#G#a%%I%acc# Xb!Ycb PX@`Yfc`#.# <8#!Y-=,#B C .G#G#a ` `fb PX@`Yfc# <8->,# .F%FCXPRYX +-o,:+?+-p,:+@+-q,:+>+-r,:+?+-s,:+@+-t,;+..+-u,;+>+-v,;+?+-w,;+@+-x,;+>+-y,;+?+-z,;+@+-{,<+..+-|,<+>+-},<+?+-~,<+@+-,<+>+-,<+?+-,<+@+-,=+..+-,=+>+-,=+?+-,=+@+-,=+>+-,=+?+-,=+@+-, EX!#!YB+e$PxEX0Y-KRXYcpB@ z\@,*B@mcSG3!*B@wh[M=**BA %   *BA @@@@@@@@ *D$QX@XdD(QXXDY'QX@cTXDYYYYY@oeUI5# *DdDD ttfautohint version = 1.7 adjust-subglyphs = 0 default-script = latn dw-cleartype-strong-stem-width = 0 fallback-scaling = 0 fallback-script = none fallback-stem-width = 0 gdi-cleartype-strong-stem-width = 1 gray-strong-stem-width = 0 hinting-limit = 200 hinting-range-max = 50 hinting-range-min = 8 hint-composites = 0 ignore-restrictions = 0 increase-x-height = 14 reference = reference-index = 0 symbol = 0 TTFA-info = 1 windows-compatibility = 1 x-height-snapping-exceptions = control-instructions = linphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/NotoSans-Regular.ttf000066400000000000000000015710241434616504300307450ustar00rootroot00000000000000 GDEF,GPOS(GSUBD7jOS/2lU`TTFAQcmapŸ .cvt ~50tfpgm:(| mgasp(glyfr0Lhead`6hhea ($hmtx-3V2loca3X2maxpQ name義Q4\postWprep6`Z$=D]IUUW  ((==@@BEGJLinquU`V\\cdfikptt&@DHH\q   <FGLOQRTUXY\\^_dgiit{}        * 1 3 3 = K N O V V \ _ c c e e o o r r t x z ~                                        ) , - / 1 2 2 : < > ? @ A B F G G H H K S U U V V W \ ` ` c c f s t t u    $,44BBJS ;w-# ((    # % ) + , - . / 0 1 7  =  &E ( )\ , -^ / 1` 3 9c < <j I Ik N Nl U \m ` `u f mv o s~ u w y    = m q v z ~ ST  %&VV   , 2 5 8 9 : < > A J N T V  1f : ; > H K M O b f ,         ! ' ( ) X k m o p q r u v w y z | } ~   4nDFLTcyrlgreklatnkernmarkmkmk. o2̾\l4׆B V   |ff h 4Ff rt*0f 4 Bffff ^ Xf h h h ^ h h r r rF"` | h F  F4 " h" `" X  $ r   *0`F"\\br6vzv""FFFFFF""""````````""""4FF``fffffffffffp.^ ^^^l.....<FdZ $%&'()*./2345679:;<=>FYZ\^cm} #%'56789:<>BDFUZ[\^_`aceghklnopqrstuwx}    ?@DGISTVWXY`dfpuxy{}~   !#)+-/13579;=?EGIKMOPQRST`>?@ABCDEFGHIJKLM !"#         79:<#%'579OQS`. "$@`B D         m        -ZM(-279:<#%'579OQS`-d79:<#%'579OQS` ;-nPP-2"PF m]elrlmnopq]  l m n o p q        m]lmnopqru ?^achopqsw @r]^aehl lmnopqm]x|}>?@ABCDElmnopqz{|}~    x}>?z{ x} >?z{3x} >?@ABCDEz{|}~  V`9 VW^df"paprtp;ops a VWZ& 2 `df  * 2 `df  Qegodf[a*  `df  + 2 Q`defgo  [a[an_aZ Vdf  +  V`dfn-2-<;,;2,;2  0  "" " prtru cscopstp                                      <<<<              )ZZ                9>BFUUY^`aehllnnpqssuxz{}}     @@ CE GJNNPTVZ\\^^apu|/7:FHTXZ\^`dem{}$)ACCET`a>MZ[j{ $6>RUV`b $glmx      ! !<;<$$&&+''((..C//*2233U44774889:';;C<<===DDIIPKKPQUU:WW0YZ [[D\\ mmN}}M+U ++++QCD***Q**      :::##4$$0%%4&&0''4((0))++--//113355'66 7788 99::=<<=>>=BBCCDDFFUU YY ZZ[[\\]] ^^ ``Waa eeff ggVhh llnnTppWqqssVuuvv wwxxzz{{}}OOX &%1&32%2 S1%&& B$$R$R%' ' '  ;!<  !  <NM  @@CCDDEEGG)HH"II(JJ NNPP QQ8RRKSS$TT1VV3WWJXX3YYJZZI\\I^^2aabb ccddee8ffgg8hijj kkll mmnn oo8pp1uuvvwwKxx$yySzz{{>||H7/27/>1%GFGF7/EE3B3B  2    %>H7//6.6.6.6..          !!##))++--//11335577)88"99)::";;)<<"==)>>"??)@@"AACCEE(FF GG(HH II(JJ KK(LL MM(NN OOPP QQRR SSTT ``4aa0>?@EFM Z[jklq ry z{   T !"#$$P#L@?@?99 -  , 5 A A A : : ?,;,$$ &&**224477288 9:"<<===@@?DDEEFHIIJJ*KKNOPQRRSSTTUUVV9WW(XXY\]]5``?mmL}}K Q    ****    9  9##2$$(%%2&&(''2((()) **++ ,,-- ..// 0011 2233 4455"66778899::=;;5<<===5>>=??5BB CCDDQEEFFGGII9]]^^3aa3eePhh3llPqq#ww#xx yy+zz{{)||}} M+)M I> I  01!N0 ! /  . -/ < <.""";:,  :  ,,LK@@#CCDD EEGGHHII JJNN OOPPQQ7RR'UUVV1WWYY]]^^!`` bbcc ddHee7ffHgg7hhii jjkk llmm nnoo7ppqq vvww'xx<zz{{'~~!/'<  NGFGF - -EDED -111 CC!/   !/0.0.0. -'B B A@A@                       ""$$))**++,,--..//00112233445566778899::;;<<==>>??@@AA BBCC DDEE FFGG HHII JJKK LLMM NNOOPPQQRRSSTT``2aa(#>? @E NO+PS&Z[\ajk)lqz{ |6 &+)      3 >#OJ88 %  $ 4g hne$=D]4KIjUUW  ==BEGJLinquU`'V(\\gcdhgijkpmttst $@DHH\q BRXY]`ef  g ikn<pFGLOQRUUXY\\^_dgiit{}   :  >  B * /C 1 1I > KJ N OX V VZ \ _[ c c_ t x` z ~e j l n o q r s t u v w z {             ^0NJH_JP^8KJ_n^b._n^_8TP.^^^^^^8_J_J_J_J_JTKP_n_n_n_n8888P._n_n_n_n_nP_8_8_8_8PP^^28._n_n_n8_nNNN>.888DJbPV\b.hn.tf(_J_n_J_n_J_nz_^^^^_8_8_8_8_8 TKPKJ.J.J.JTP^J*^24J_J^H^_J0P^KN8NTK.(Z.(b_n.Z,XTZ_nZT2.^22N`_8KK.BH_J0P^N8l.~_nf_\G_nPZ J_n(8b*.PjTTTKPTbHT_nD8K_nj"@(.4:8@FLRNXTP@D@D^djpv|ZJZ ..*DKKlPl K`f$*06<*BHNTZ^^_nD,D,`f_8_\KK_J_nTPTPPPPlZrx~82J*l.^^^^^^^^^ _n_n_n_n_n_n 8_J_n_J_n_J_n_J_n_J_n_J2_J2_J2_J2_8 &PKPKP.,2822@28l22>"D_8J\PV\bhn(Ttz TK_\  ^8_J_n_8_8_8_8_8^^N N.^^_n_n88_J_n_J_n_8_8"(.4:@F^$*_J_n_J_n_J_n_J_nKPLGRX^^^djpKP222j2,,.v|.(^(((PT_8PZ_J $*06t<BHNTZ`flrx~ZJbTP~TJ.PPP_?.._\dd> &,28>DJJPV\bhn""tz":^@vj .^(P^"(.4v:@ FLLvvRXX^^djpv| _n_n$*0^N6<BHNTZ`8flrx ~ ~H._J_n_J_n_J_n_J_nPP^^_8_8_8_8 &T,288KPJ.>DJ.^TPJ^^V\lbb......hhntzz((((((((_n_n_n_n_n_nZZZZZZZZTTTTTTTT "(..((_n_nZZTT44L:@FLL((((((((RX^djpv|^^^^(((((~ZZZZZZKKT  22P$*06<BHKD^N TZZZZZZZZZZZZ`f_lrx~.K_J_JPK=_8aJ^ 8b_\&,28>bDJJPV\bbtbhn tz...J "...(..4444:@LLLFL^^^^^^^R^X^ddjp|||v| 92$*06<NBH_J_JNTZ`flFrx~@G@K^_8^ &&,28>DJPV\bhntzK==D$,$#2BVJ# #,##|#5#{H#~###l$##8==nDsDZ8F\9GDR Ir&*>}b$BDJGF DG$ODF[?G t$DGCm89bcdefghiz{|}~ !BceGQRde . \p? F?n??\??????????\ Fp?np?np?np?n???????\?\?\???????F?? ???????????????\\\ pp?n??????\\ ????F????????pF?V &,28>DJP V\ \ \ \ \ FFFFFp?np?np?np?n?????????\?\?\?\F?\bhntz?????????\Fp??pp?n ?n?n"((..44444:::::::::@@@@?FLRX^djppvee T( vmeeeMeFeVeOee_e'e(eee[eZePeceYeeeseeeAeee > 4 1    #,<5   ATAvAeeG$ Hne$=D]4KIjUUW  ==@@BEGJLinquU`*V+\\jcdkgimkppttvw $@DHH \q "EU[\`chi  j lnq<sFGLOQRUUXY\\^_dgiit{}   =  A  E * /F 1 1L > KM N O[ V V] \ _^ c cb t xc z ~h m o q r t u v w x y z } ~             :~ "n < $$0 #| "::$:l#R$2<h $< I: $B$$ ; $r :Z 5Bl$$J ",,,888HJJJNdtttd$$   TrZ5$<222>>>"`PPPf;zzz l&r Nx~  I,2$B`ZD $>$0 0     z " z " V\JPb$ b :l:Z:l:Ztz#R LRdV\b\b &,2JP :~8>DJ:~ P h: $0 #| ":l:#R:l<:V\bhnt$N .(z#";;N $r8I, $#R:~ # :#v#|:#R ":l<=#$ I#I#: I#; $<$<I##  x# r $"$(.4:#@FLRLRX^dj:>6jp:~ :;#R ,v2|r5#:; 4HN H$r $* :  #p\ "$<:l$,,~I*0$N#^;#^$B6<6< BH #dH#R$NN#^vTZ `flrx~ F<:;  ~&, 28>zDJ4PV "\bh(nt:~ z & I,2  $:;"("(.4:@JPFLFLRX^d$r#R jpv|v|:l:Z  :~ I#p$*h   ":$$ *p#F0:l:#d<<6< :BHNT RZ`flrx~ 8>JPtz$r*0:;z &,28>ZDJPV\bhntzh I#:~ "6 :l #R $$$$r$  $r$rI  F"($H v I,, ..$#4: @FFF L;R<xXxxx^^ dd vdjp ;v,5|I$H$:#4X  2:X$ $< *06<B;HN2TZ`fl;$<$:rx$ "#|8;~ "~:$r25I## ;;;I#   :2$ &,28>>DJPV\bhnn|tz#:jpjjF ^$$  # ; : |"(. p#j#4 p44#4:@FFjjLpRX^djpv| n n n n  I I$B $B$B $$*0$0 $0  6<   BH#| NT " " " Z`flrx~$ $  :l:Z:l:Z:l:Z#R  &,258>DJPV\bhh$BntzXXXXXX^^:~:~5."4TT:**057h 45.4z"7h4(.4:@@FFLLRRXXXXXX^^dd|jpv||$N:~:~ $*06<BHHNTZ`flrx~~4t #j #j$$$B$0 $ &,2 (8>>>>DDDDJJJJPPPPVVVV\\\\bbbbhhhhn "tz#|5:$$0$0$0 ::::$$ " "$0 " : 7:~  #R  " ( v v . 4 : @ F L v v v R X X; ^ d v v p v j v p p v v | $r$r # I $ !!! !!!!$!*!0!"""""!6!B!B!"D"J"P"V"V"\"n"b"h"n""t"z""z""""""""N"""""""""""""""""## ####"#(#.#4#:#@#F#L#R#X#^#d#j#p##v#|::#:###$H##############I#####:~$$$$ $$$$$$*$0$6$<$B:$H$N$T$Z$`$f$l$lI$r$x$x$x$~$$$$$$$$$$$$$$$$$$$$$$%%%%*>|1//>i1W1))2{nW5n51"n@F#7% %F%%4F*4/%4%%158 A,JG\  ]BR> Vr&U}8$JFO[j`wKz>>1\rw&WBB/=8W88]3 % AfHd%>5>,> g>s>L>n>q1511,1 g1s5/5/, /g/s445n55QQ5QQ552|L4 6 W$&pRyn5Dn5g>D>EWW/$R)K>1//77n5n5sP*>/D//EE0 5I<3? 4?n':aaaa'k~3)E<cl== 5d@nY['.aaaaaZa7a|aaadaaaak>aaaahaTa0aaaTvaaaaaaaaBBR|i111*W)sssgTW|5#/q/R//=L=L77WRYn5n5n#5qn5R,,$$""R11* MFO_*Y***'4Hq*01-*O* /477\fPR\FF\ONWM\cFI9D7777>>WFFFxg \W444 /44\Wz k7j7j4j44i5 u8mAc@o.=41|.'TZTTaq>L4)Du5( <"<Uh<^8#"#<"<<A<AA"ACA,""<<v"<vv<<p<88"8<8<<<<<"<>=<<v<<<<"<"<,<<"<+<****"**<*U*<<"<<"<"Tv n]hrbZ9A*|L,@u#aaB<>n&T,7 4Th7d+3a{>4h%PY7/) *.2$=D]4:CINTY_du  &(?HIUUW[^_bgijllnnpqssvw~   BELLNN$&()*+%,'6UADeOTi`aoppqrsv  ((..2255<<FFTTVVY[eekk}}  (*03599;<>> BG IJMSVV\\cciilptt#$%&)*,<ZFMTYbir  !@CHHbceg"%*+  , .02 411:99;FG<MO>QRAUUCXYD\\FdgGt{KSYfno      - . N N z z                    t @ R  ~ , rVD .D  F R X      hV | h  @ R R R RVDDDDD X X X X hVVVVVhhhh    @ @ @ @  R R R R R  ~ ,,,,VhVhVhDVDVDV    F F F F R R Xh Xh Xh Xh Xh Xh F   t R D rVD . R   V V R F ~ t R r D . @ R V |  r  R @   R DV             R R R R R R R R DVDVDVDVDVDVDV Xh Xh R X DV Xh Xh Xh Xh Xh  DVDV Vh  R R DVDV   Xh Xh  R DVDVDVDV  @ , R X R  " " (h D.4 Z: @FLRX ^  @ @D djpv|  * x     $ * 0 6 < B ^ H N T Z `h f |  | l r x ~ Z t t t @  R R R R R   ,,,, r r VhVhVhVhDVDVDVDV . | . |     F F F F F R R R R Xh Xh Xh Xh Xh     n      &  , 2 2 8 VVVVVV \ > D J P V \ b hVV n t z        " R ( ,DD . 4 :DD  V @ V F L  R X,V ^ d j p  v |  hh h hh &&&&&&&&&&                      $ $ $ $ * * * * * * 0 0 0 0 6 6 6 6 6 6 6 6 6 6 6 < B B B B B H N N N N N T T T T Z ` f f l rD x ~        &xm&S 2 \aa>aaKa4;;>@nQDA|\PLaOgbaX?ava|au, =ajJaYa=jkJY<+aia6ma aO6(tXi%5TV;Oxlv`bfg("_\'"HLbvKxuI@dAZaWNIu+- )<<<2<<<<<C<<<<<[<<d<<<<@<<<<<<w(W\hXhh?m|+4VZDy"~ &8J\n|B   Dg 31 $ &b   -"(.47<7}<}$*06<E>E0>q T  f-T 0 = o rB$T6BTf      e ee /  "(.4''71'<17<  \4fT 0 3 = e o r c &8J\bb|(j   >aT1 $ }   /"(.47<7<$*06<=/=D D> P if-T 0 = o r c BT`lx,2@T`r0 ?P<H3  l  2X  ) O * :4&zF |" <<( / ?"(.4Yb]$*06<T TD !G  L((    # % ) + , - . / 0 1 7  8 3 9: < <A I IB N NC  D((    # % ) + , - . / 0 1 7  =  &E ( )Z , -\ / 1^ < <a N Nb U \c ` `k f ml o st u wy y | E..("^(....(..^.LL@^4:@FLRX^djpv||Y3dXX""(.4:@FLRX^djpv| N$*B06<B~HNTZ`flrx~#40*YPQ$&98GFHmf"&O"4%!o% ,l%ll(  ( " "l;l!$ MlVl ll  l llll l l l  2p =   m q v z ~  &,28&"^  V    $ ST  %&VV   , 2 5 8 9 : < > A J N T V  1f : ; > H K M O b f ~ $ %& & ) , - . 0 2 5 6 ; ? E G  L  )V , -n / 1p : ;s > Hu K M O R U \ ` ` f m o w y .L4:@LLFLjRX^djpv|rrrr`rlrflfrr`rrr`r``r```rfrrr6rrrrrrll6frrf``rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrf $*`0H6<BHNTrrrrrrrr``Zr`frrrrrrrrrrrrrrlrrrrrrrrrrrrrrrrrr${~! ."+ j16|=`<!< <<'<&<<%<<?`flx~rx~d &,28h>>DJPV\^bhnttz^ "(.4:@@FLRX^djpv|5$:+\8cM61JLb?\&O# 7FH|C B.pBBBBC;B87)Ab*)7)F   BMBBVBB B B  !BBt B B  l (         ! ' ( ) X k m o p q r u v w y z | } ~  $*i(RX^jdvjpvv|hwhBBCrBBmByBzBp;+BBT^BdBPt|BtBxBbB *DFLTcyrlFgreklatn  SRB 2  "CAT JMAH tMOL NAV ROM       aaltc2sccaseccmpccmpccmpccmpdnomfraclnumlocllocllocllocl loclloclnumronum"ordn(pnum.rtlm4smcp:subs@supsFtnumLzeroR!#      $"%&NbnnT  D D f  6 J $   $ 2 p p P P.B   2 fT ((   & ) , 0 3 7 N N9     ! " # K M$ k k' p r( v w+ | |- ~ ~. / 0&*,+"#<3g456789:;LM(5jN^0:DNj|",6@JT    LG   I 3     LH   J 4 :BJRZbjrz                            :BJRZbjrz + - / . , 6 9 8 7 A C E D B ; = ? > < 1 3 5 4 2 * @ : 0:BJRZbjrz c e g f d o q s r p z | } { u w y x v i k m l j b n t h:BJRZbjrz G I K J H S U W V T ] _ a ` ^ X Z [ Y M O Q P N F R \ L:BJRZbjrz           % ' ) ( &  ! # "        $ ' )  I I I I$(,28DHLRX&( n&0:DNX        $.8BLV`jt~            $.8BLV`jt~            } "#34HI#$ `a  4R<R^(^jjyOy/yy2 - . / 0 1 2 3 4 5 6   KNOPQSVW$ ={tu*+>,-?   Q   "B*    ! $,$D2Rl|l|$2DR .    .       .        :     J          > ? @ A B C D E F G H I "$%&'()*+,-./0123456789:;<=>@^`cq  !#%')+-/13579:<>BDFH` <     J          > ? @ A B C D E F G H I ">@DEFGHIJKLMNOPQRSTUVWXYZ[\]^`cq   "$&(*,.02468;=?CEGIa B      FXKX^2B @)GOOG@--%    ~01ac67Y] %&AEbw~  OP\_'/=?EMWY[]} d q  !_!!,m,-.Ds}!.Ze/ 12bd78Z^ &'BFcz PQ]`(>@ HPY[]_ f t !!!,`,n-.@t~".0[ KMHS {JL@By]><:8654320/.,+)(.Jf2ee`\`eaa^2;?.>D<RfjHhTHI#$)*&'JKLMNOPQR  k r w ~ p q v | v { w | x } y?@AO O P Q R Sstuvwxyz  ?  @  A  B  < : ; t l m n o x s u f y z g h } i j {    DE./$OPQRST-~'()* +,   -   [  O ./0123 4 < f 5 > = 7 P : ; _ ` H z h 8 9 Z j ? u S K v N J M B |6 789:;<= *+>,-?  EFGHIJK U U   W   ! XY * + ,Z[ $ % & ' ( )"#$% " klmnopq%20!1 l k W p T m d c E X q e V o G ^ L g Q C F Dr R ] y U n w x Y r \ t I i s } b a  @ A ~ {  L  M  N  O  P  Q R ! S " T # U $ V % W & X ' Y ( Z ) [ * \ + ] , ^ - _ . ` / a 0 b r T c d e s 1 c 2 d 3 e 4 f 5 g 6 h 7 i 8 j 9 k : l ; m < n = o > p t u _ `=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] ^_`abc defghijklmnopqr~ s  t u  v wxyz {|  } ~  3 4 5 6 7 8 9 a b^*@'eU]M+3!%!!^563dH@]K_L$# +7#34632#"&9kt$%%$l%%$ AW$@!]L +#!#77lG@D  fe  KL +3##7##7#537#5373373337#)G)'F&~ (H((E(CCBB>")0p@0*$#  !JK-PX@~hL@!W`PY@"" +5&&'55&&546753&&'56654&'7h "j3c\gX@5W$ M(BX-h_@63-|;60A:kURGJTXWJ +?2FW o۱*!(++"&&1 %/KPX@, h g _  K _L@4 h g K _ KK _LY@+'& -+&/'/!%%      +2#"&546#"32542#"&546"3254IMIMGKFtM&##&MhIMIMGKFL&##&Mujjwwjju 64QPPRujjwwjju?PPQQ5+5z@&5JKPX@#_K_K_L@!_K]K_LY@! 31 +!+  +26673#'#"&5467&&546"6654&32670P]Q>! Y0&wW/tShySG 7bS*5&$;30R6=J>@[ QI?X$Q/Am)T*4f^L^($R7ISH,'$=%"<)$. B67B*A@]L +#7(b @L +4673#&&(GLSFGGERLGz[^wt^Xb @L +#6654&'3GLREGGFSLGyX^tw^[)63@  GK)PXLtY@  +7''7'7'BwVUMYu6\//\62oS &@#Ue]M +3##5#5353AHHGG)t@U]M +7#6670At 5694(3@U]M +753(NNHy @_L$" +74632#"&H$&&$6%%$  j@KL +#jV 61  @_K_L$$%# +#"&54663232654&#" 0iUys/hUxv~CQPEEPQCftXítWYc @ JKL +!#467'73cVL.I+4>;0*@' J_K]L'%( +!!57>54&#"'6632!(5K&F85N)/*mDdt.R7iI6TP1;=$ ;#1fX8b`5-*@@=$J%Ig_K_L%$!$%* +#"&'532654&##532654&#"'6632QCUU:y_8`,-h0`Ui_EFX[F<:R(,&qHpm#HU XG?`6RKBC;KJ=49"<,d( 1@.JfKL   +%##5!533#5467#(hUP[h ֢K#4I!,?D@A  Jg]K_L  +2#"&'532654&#"'!!66m~8`!$g/OaV]H,f:ndoSKOFK QP7 ,>@; Jg_K_L &$, ,$'%$ +4>32&&#"36632#"&&2654&#"7Ge3.E\5R@]r{hDnA?NEE/F'"D1MyHK.Ph;#1qhpDQUDP'=+U7, %@"J]KL +3!5!%zPDz1 (56@33#J_K_L-+((  +2#"&54667&&5466"6654&32654&''^x%>%,H+jt{)D'5H8`<8F#<$4GFJMIMRDBEXS+@14F1Zie[0I4UB6L(G52%2#>625(4EE74EH2,>@; Jg_K_L &$, ,%'$$ +#"&'532>7##"&546632'"326654&&Ge6'1F[6RA\q8gEDn@>OCF0F'"DMyHK .Oi:"1qgKl:ERTEO'< +T8H& @_K_L$$$" +4632#"&4632#"&H$&&$$&&$&&$ x%%$ & "@a_L  $" +4632#"&#>7F$%%$j 1B &&$ 55%XU#2t `0+%%5% )yt2N8/@,eU]M +5!5!86GGGG2t `0+7%%52y)N2 +:@7J~_K_L*($"%+ +754667>54&#"'66324632#"&% &,>;1L#(a<_h5$"" ]#$$#&71 -*04F^Q-?5**%%$ :I?M{@ G /0JKPX@&gc_K _ L@$  ggc_LY@KI%'%%&(%%$ +#"&'##"&546632326654&&#"3267#"&&54>3232677&&#"I,@,.5F5MR4_A,U %+KSrQ=o++kAvY:nch]2,81 (1<e.XG+5"%2fTAf: 5!3T4\D^jDXt]uAVA9TC}0K~1@. JfKL +!'!#3'.'!VU[QP Q3-*- ;aT"5@2Je]K]L"!"$!,  +32##32654&##32654&#a̅FB-I*sZ[ES[v_JMcPa?S &F7aj;:;3J<8E=Y7@4  J_K_L  +"3267#"&&546632&&s{{.T)(U;mIOnqT$!Q NZpl]*La @]K]L!%!" +##324&##3 ŰkV_ua"lPua )@&e]K]L +!!!!!!!q#5ONa #@ e]KL +3#!!!!Z"OO= ;@8Je_K_L%%&# +3#"&&546632&&#"32675#:vKoOWuiU@"D"a@K^L +33!aZ8Pa*'@$ JKL +!##333#467#SYri9OI64f a@JKL +!###33.53iSh}TQ#h7q@K!=@_K_L$%&# +#"&&54663232654&#"KkoHHpkKryyqpyzrfo\\om\[oa* 2@/e]KL       +2####32654&5}kRZ[HfdYnd;g@MBOED=V +@(J_K_L$%&A +#'"#"&&54663232654&#"ig oHHpkKryyqpyzrf"\om\[oa_;@8Je]KL  +2####32654&&*A$iZfkWPTef9L. 'NECF;3).@+J_K_L%,%" +%#"&'532654&&'&&546632&&#"u5#0(!`S8R,M9/$0'5J !@]KL +!##5!#CZ{OOZ!@K_L#$ +#"&533265<|^Z]^aW2JwEw1W`gQX !@JKL  +#3667XZ^66,M##N- '@$JKL +#.'#36673667[   [^o  ~] n6;- U./L.V&'\,N.\"%W/F @ JKL +!##33Ff_d_6tV6@JKL +3#3aZbk_K& )@&J]K]L +!!5!5!!xD6PDPb0@a]L +#3#30hH( k@KL +#` W6b@a]L +3#53#VH& 'dD@Jt +D3#&2N <gf dD@U]M +D!5!@@(^ &dD@ Jt  +D#.'5 !%;:1 75 99 .!&u@JKPX@e_K_L@#e_KK_LY@$"  +2#'##"&5467754&#"'663265 b^@#NCI`~[:5*L!#`OdM7+C[!V^L,*MRPW C4B83-*KNU0!JKPX@K_K_LK)PX@!K_KK_L@!_K]K_LYY@!!$' +36632#"&'##"32654P?dyzc?P?VAAXHG";".. Dbgcijd7"7@4  J_K_L  +"&&546632&&#"3267,Hn?ArH(M@ML+DA :z_c|: I ag N7" JKPX@K_K_LK)PX@!K_KK_L@!_K]K_LYY@"" +"&546323&&553#'#'26554&#"dxyd>OXG P1UEBYGGG .! 3H"0I]^dkq_`j7"C@@  Je_K_L  +2!3267#"&&5466"!&&$Dd5YP3O*)P7MtA;kF?I>"zZX~DHQHDU\@JKPX@_K]KL@g]KLY@ %% +###5754632&&#"3LX^^[S6*,+,)h[ E ;?#7"+@  JKPX@"_K_K_L@&K_K_K_LY@ &$+ +  +2373#"'532655467##"&546"326554&5U Fu{vKOwEO6phuusCJHGQJL"()Gst"Q*QF - QJkcciWam`UPJK)PX@K_KL@_K]LY@ "& +36632#4#"#Y4acWxZCX(#)*]gWe^N K PX@_KKLK PX@_KKLK-PX@_KKL@gKLYYY@       +2#"&546#AX @ JK PX@_KK_LK PX@_KK_LK-PX@_KK_L@gK_LYYY@   $" +4632#"&"&'532653N8&   *XHG#1kKUU O@  JK)PX@KKL@]KKLY@  +3>773#'# gj=Ws4 5U(K)PX@ KL@ ]LY +3#3XXUV"!]JKPX@_KL@K_KLY@ !! +2#4#"#4#"#336632366[ZWmNCWnQ>XG U0~&\"^gYZVYd^I*)Z.,U"PJKPX@_KL@K_KLY@  +2#4#"#3366W`bWxYDXG \"^gWd^I*)7'" @_K_L$%%" +#"&&5463232654&#"'sGo@tHp?kKRQLLRRJ A}YA{Y_oo__llU0"#k JKPX@_K_KL@!K_K_KLY@## +2#"&'##3366"326654&Tcyyd>QXH N1RDAX0@G". 4I#0J\^ck5^<\n7""hJKPX@_K_KL@!K_K_KLY@""$' +5467##"&5463237326754&#"Q@ay{b?P FRFDWHFF0"00#I/[^fiq__kU"fKPX@ J@ JYKPX@_KL@K_KLY@  +2&&#"#3366O" )H+XH S"Q-Q6b,@3").@+J_K_L%,%" +%#"&'532654&&'.54632&&#"tb8Q [/C<954J(oZ1U%"J'69=33H&NPP+$ (8,DJF#(9S@@=J]K_L  +%267#"&&5#57733#* 4*G,LM#4/>C HA8*#r{D1/OLJKPX@K_L@KK_LY@ "$ +#'##"&533265H \4abYwYEG*']f_d^!@JKL +333>73^rr^6235< "'@$JKL"" +%&&'##33>7336673 \ `d[J  _`\  KZ.)NN*+X27.#PX. @ JKL +373#'#Թdcdc'@$JK_L%# +336673#"&'532677^tm_XN$ .8)H!Q)0LZF4+G' )@&J]K]L +!!5!5!!x p#:DBnb\,@)Jgc_L +&&554�\\j?;;?nX4;mm:5NP3+I*2PNH+1gg1+8(K)PX@ KL@ LY +3#II b`2@/Jgc_L +56655475&554&'53 4;mm:5\j?;;?nH+1gg1+HNP3+I*2PN2 <dD@1JHGWg_O$$$" +D&&#"56323267#"& $/>0H:.$/>1G:? "N5  "M6 HJ" @a_L$" +#"&546323#$$$$\:l&&$ [!n@ JK1PX@hgKL@#hW_OY@!!$% +&&#"3267#5.546675a%FBRMOL,A:'C;W00W;P I fgh_ M ad T  H@EJe_K]L   +2&&#"3#!!56655#53546N7W#J(9<* +8``oF;BBh=;PJ ABiBYd;B!1=@: J HGc_L&*/( +467'766327'#"''7&&7326654&&#"ZB1B9 7C0@#?/C8@0B0AC";$$;##;$$;"a9D/@@/C9?1B/@#@/B9$;"";$%;##;,3@0 fe KL +33#3##5#535#533\|Vz]m]@R@@R@w8,1>8(32'2>54.#"7"&546632&#"3267Pc66cPLe96cP@pV0.SqDZP.SrScb.ZAA:2+;A9B92 6cPPc66cPPc65.UrEArV1Q[ArV1Z{eAe9=UIMR @  4$@JKPX@!_K_K_LKPX@g_K_LK%PX@gc_LK&PX@"ggW_O@)~ggW_OYYYY@"  +2#'#"&547754&#"'663265AB/ 9%/88*3@h0<*3-6;*12c! 1 /((8  0+7'77'(??ƪ>>$% $%2%@"U]M +#5!5GqG(31&4=idD@^/J ~g  ge W _ O''=;75'4'43210*(&&  +D".54>32'2>54.#"'32#'#532654&##Pc66cPLe96cP@pV0.SqDZP.SrERL0tVd>2',(,1 6cPPc66cPPc65.UrEArV1Q[ArV1_A@/7 ­' # : dD@U]M +D!5!B7u 9dD@.gW_O      +D"&54632'2654&#"HWUJFYYE0-/.1..UDDVVDDU;4*,44,*42 V 1@.ee]L   +3##5#53535!AHHGGGG3U0@- JgU]M%( +!57>54&#"'663232s))%1#E+@I;8Q6p'1&!.?71N5MAU(M@J&%JggW_O#! (( +2#"&'532654&##532654&#"'66GH+'/UX%@F>40:4992/)5$DU>0)3 2):I ?")#$!7'.(^ &dD@Jt  +D#5>7 19:$"  99 57U\@  JKPX@K_KL@KK_KLY@"$ +#'##"&'##33265GO8'8XXxYDH(*=(d^7%QJK)PX@~]L@~U]MY&# +####"&&54663!%:f:'>\37dA?.l[_n.H+ +3+2dD@'JW`P$" +D#"'532654&'73JJ  $&5&+:$3057V5(%L '@$ JU]M  +#467'7G  6#LT*  '1\ Y >K%PX@c_L@gW_OY$$$"+#"&5463232654&#"YVHCXTIGU+21,,11,)QYWSRWVS;::;;99'8  0+'7'7'7'7ժ>>ǩ>>%$ %$"$ddD@Y !JU e f]  M +D33467'735#533#'35467~KL#  6#IkI==} 6*  '1\T`4<`]8 1*bdD@W Jh eU] M**)("  +D33467'7357>54&#"'66323`KL  6#Is(*%2#E+@I<7Q6*  '1\T6p'1&!.?71N5M>(,7@dDKPX@"= 0 J@"= 0 JYKPX@7g g  U g   f ]  M@>~g g  U g   f ]  MY@+--))98-7-7654321/.),),+* (( +D"&'532654&##532654&#"'66323!5#533#'35467%@F>40:4992/)5$D/GH+'/U@KLI==}  ?")#$!7'.>0)3 2):I6`4<`]8 1@"" 3+~&$C3+~&$v3+~&$Jo3+~&$Q^3+~&$j3+~n&$O==3+58@5ee ]K]L +!!5##!!!!!%3#5k]S:ONM=Y&&za&(C3+a&(v3+a&(Jb3+a&(j3+'*&,C3+(7&,vF3+U&,J3+6&,j3+ ?@<e ]K]L   +2###53#3#3 4&=kWűJJnZ"Pr:NBMNa&1Q3+=&2C3+=&2v#3+=&2J3+=&2Q3+=&2je3+@> 0+''7'72244>3334= )<@9$# JHG_K_L',*# +#"''7&&5466327&#"4'326KkpI0=4,,Hp4Y%.=3^?4Nzr3E*yqfo\/D(J1Wm\B)Gc>c%#J:Z&8C3+Z&8v 3+Z&8J3+Z&8jL3+6&<v3+a* '@$eeKL$"!# +###33232654&##*4}mQZZ`~HiaWbY~54&#"#46632 ** &%6>hR/HM(70)5?.))G8#=%X:d?awi"3' $K;UNO.($1#);(,! &*&/*HCO#J.&DCn.&Dv.&DJJ.&DQ9.&Dj.1&DO.-",3>@$*#  JK(PX@%  e _ K _L@*  U e _ K _LY@#.-<:6410-3.3(&! ,, +2!3267#"'#"&5467754&#"'663266"34&3265[A^3OJ1M&(M2>#[MIax|Z=3(M!#d1>QT5:C9K^H3*?U"YU&QQU7'&RC7'&Rv7'&RJ`7'&RQO7'&Rj 2y G A@>geW_O      +"&546325!"&54632!! !!  "" GG "" 7'6&<@9"!JHG_K_L&,)" +#"''7&&546327&#"4'326'sI8(:-!tI:';-"k $4RJ:"4QL !8'>$dA$8&?#c>&A2l_J1oO&XCO&XvO&XJfO&Xj&\vU0&]JK)PX@K_K_KL@_K_KLY@ %$'" +#"&'##3366324&#"3260yc?PXXN@cy[FJSDAXJE .  " - "0ee\\ckk&\j~W&$L3+.&DL\~&$M3+.&DMZ$~&$P.$!&DP,=Y&&v3+7&Fv=Y&&J3+7&FJM=Y&&N#3+7&FN=Y&&K3+7&FKKa&'K3+7&G'z7^*  JKPX@'eK _K _ LK)PX@+eK _KK _ L@+e _K]K _ LYY@&$** +"&546323&&55#53533##'#'26554&#"ewyc?OXLLH P/TEBYGFF .! 3=BYYBH"0I\]ehn``iaW&(Lt3+7&HL`a&(Mr3+7&HM^a&(N3+7&HNa$&(P7$")0@%&JKPX@(e_K_K_L@%ec_K_LY@+*.-*0+0"&&%! +3267#"&5467#"&&546632!3267"!&&-52)'MtA;kGDd5YP3O*)+b?I>t-82,"?>zZX~DW&,L3+&*LJ&,M3+!&*M($*&,P\$&LP(*&,NQ3+(B &,-SN&LMB4&-J3+,&,Ja#k&. WU# &N U &@# JKL +#'#336677iB]]  6(L  P&/v(3+E&Ov3+a#&/ 9A#&O a&/' Ұ3+UQ&O'a&/N# 3+U:&ON Ұ3+  ,@) JK^L  +35'737!a1#TZ$8<2Q?dP  ?@ JK)PX@ KL@ ]LY@  +3'737N3$WX@%e ;8,;Da&1v3+U&Qva#&1 U#"&Q Ba&1K3+U&QKd_&QFaB;@8J IcKL +"&'532667##33&&53& &0mSh}TfL0,QFP%} p8t#"&U a_&5Kf3+G&UK3&6v3+3&Vv3&6JN3+3&VJ!3&6z3"&Vz3&6KL3+3&VK #!&7 ##S&W !&7KE3+&W' !/@,e]KL +3#535#5!#3#蕕ߔEJPPJS R@OJ e]K _L   +%267#"&&55#535#57733#3#* 4*G,DDLM#4/>C HA|Bz*#r{DzBz1/Z&8Q3+O&XQUZW&8L3+O&XLxZ&8M3+O&XMvZ&8O3+O1&XOZ&8R3+O &XRZ$&Z@JKPX@K_K_L@cK_LY@ #&%" +3267#"&5467#"&533265352 '.Z]^aWY,,,*k843= w1W`gQ2?j$2EO$&XPP &:J3+ &ZJ6&<JL3+&\J06&<j3+&&=v3+'&]v&&=N3+'&]N&&=KQ3+'&]KUjG@  JKPX@_KL@gLY@   +"#4632&&)/XaP1*4?AgU E ]#N@K Je_K_L ## +2&&#"3##"&'53265#575465),(WP$  *(iiXD /=ED*bNI,;)AhN (1[@X. J  ~gf L *)$"((    +56673&54632#'!2654&#"3'&&'0j .61=0/A]RN ZT= D..`?2881. 8;. ">I@< ; ) JKPX@< gh e _K _L@@ gh e _KK _LY@,$# GEA?9742.,('#>$>""    +#56672#"&546"32654&2#'##"&5467754&#"'663265 8@?0/@?01<<1  b^@#NCI`~[:5*L!#`OdM7+C[ $$ 5713882271V^L,*MRPW C4B83-*KN5&v3+.-&vS=&v$3+7'&v3#&6 3#"&V (^z)dD@ Jt +D#&&'#5>7 -1>86<0, 75 // 57(^z)dD@ Jt +D.'536673 ,0<88>1- ^64 00 46(^Q'dD@U]M +D!5QGG(^_ .dD@#W_O "" +D#"&'33267_QHJK63.'9)'(q (dD@W_O   +D2#"&546\(^1 9dD@.gW_O      +D"&54632'2654&#"1<<1/@?0  ^822771382($,dD@!JHW_O%! +D3267#"&54667p-52+0""t-82,7, 5(^4dD@)Wg_O"""" +D663232673#"&&#"(9/5028/51^;E;E(^ =dD@2 JU]M    +D#5>7##5667 .62  P .621 99 47 99 V"^% -dD@"JU]M +D5>73 ^#^BD CGwF !dD@ JK PX@&n~W`P@%~W`PY@ !!    +D#56672#"&54632#"&5467* sF S# T"l~&$S ˰3+H&y3+E&(US ˰3+&+US ˰3+&,}S ˰3+&2.S ˰3+'<S ˰3+&u%S ̰3+HF&Ta~$aT%a;K1PX@]8K9L@]8LY@ +!#ZP1L@ JK1PX@8K^9L@b8LY@+!5&&'O} l67t-T$$S*a(&=a+=/@,e_@K_9L$%&#+#"&&54663232654&#"!5KkoHHpkKryyqpyzrfo\\om\[o[NN(*,ak._ :JK1PX@ 8K9L@ 8LY@ +#&&'#^_ _6.T$$T-=a*0a1# eK1PX@ e]8K]9L@ea]8LY@   +5!5!5!7llX{OONNOO=2ay>K1PX@]8K9L@]8LY@ +3!#!aY6{a*3$"T@ JK1PX@]8K]9L@a]8LY@ CA+355!#*'263!$0, %L(J+ KPO !76<3'jK1PX@! gg 8K9L@! gg ]9LY@ ' '"! +#5.5466756654&&t8FvYY[wD9sQ^(fqYtc(_XHxH0_M0nn1O^.FxJX0T7Yh~kV9S/F;5NK1PX@g8K9L@g]8LY@ +##5".553332655DwZXZwCZfpXrd3^J+,J]2]]\][&!VJK1PX@_@K]9L@a_@LY@ !!+"!53&&5466323!56654&xpJ]@XLijLW@]KqrdCHO1pbONbq1OHBcr6&,j3+6&<j3+7X%&}S+%&SU%&S"RH%&S^O/F&T7X"#0l@ JKPX@_CK`9L@;K_CK`9LY@%$,*$0%0 ## +"&54632366733267#"&'#'26554&#"bzxl;O E  %/ M4RFAXIFE *((HP$@$."0I_gdijeeeUJ.N@KJg_BK_9K=L*('%!..  +2#"&'#466"32654&##532654&DCh;NJ[]s:O X>lC(D)#K3VN`N5+NHN+T@HT  b[gnQe0IF?TLGOIJJ=><"@ J;K=L+#466733>7 _ [q j)[W"T])@;<@6&!/3@0J_:K_9L+)!!+2&&#"#"&&54667&&54632654&?Eb'&%T3-*:2[aqHo?7_;8Dg[A8#QILSC"E)'(1uWx~4dH@`@ L8EG/G3BTYSCU+"(E@B   Je_CK_9L('%,%#!+#"3267#"&546675&&54632&&#"3cIR<7V!V>sn!6 -7s[:S(!!E/ySF;H\1(MYC(3 ;1DJFL,&7=&@J]:L_+#6654&&'&&54>7##5!'W&36_b0Sl;,A"xtg}>(L6LA(,PJ gcK}nh5IBWnZ(9; 7U"|JKPX@_CK9K=LK1PX@;K_CK9K=L@_CK];K=LYY@ +2#4&#"#3366W`bW:>XEXG \"^gG@Ad^I*)7 7@4e_BK_9L%#+#"&546632'"!&&267!1kX|u0kV|xLG2ILNJFyy]εz\˃RH)@&J;K_9L%#+3267#"&&5%(/ 6,E'00C IA{U 'KPX@ "J@ "JYKPX@_BK_9LK1PX@_BK9K_9L@~_BK_9LYY@ ''&%$+#'&&#"566323267#"&''.'#&+ #3@* %+I zK+1G$J:6A%,;9#P'Uw2JK1PX@ ;K9L@ ;LY+33>53#[k >EW%[S_B?>aiS6=6/@, Je]:L!%!>+746675&&546670##5!#"33#"#6654&&'&&6'@%28)C(#7Z9kDJYSV[`*N6L=&S$55ad2M3 A9/=% ID"D337CXA34 9(,PJ b7'"R sKPX@J@JYKPX@];K_9LK1PX@];K9K_9L@ ~];K_9LYY@ +%267#"5###57!#8#oWmHi>EY1&#I#O&"6@3J_CK_9K=L#"+#"&'##4632'"32654&&m)LYnDi;L(OEE G-@|rƢhege7="#+@(J_CL##+2&&#"#6654&&'.54669(M>!WLKFM=&S%467Y5Bt" I ni7Q!@];K_9L%%%#+%#"&546633#32654&'#"&8oRoGY'0lJSSJ+(ddHtDd|8I%l>UlfO@p+^ 5@2  J];K_9L%#+#3267#"&&5#575+/7.N0IJ:.B HC3'#O/$@!;K_9L +"&&5332654&'33Yc(XFMPLX CsG%VbrxFn<;oJ7"(2@/"J_CK9K=L((++4632#5.546">54&D%-/N.TJ>X0K{HVJuD:h,3R0=/1kFIW(*[^CwOa}@:xbQ0?1]F^c!#A@>! J_;K_=L ##+233267#"&&''#'&&#"566Z2/D^]%%  -9%G^R" ?;#..D!=*(-EO0@-J:K;K9K=L+6654&'3#5.53UgWJ|LVLvCW0P.JfjEuFDwBg};8xd KV&8)4@1 J~;K`9L))#%#&+#"'##"&&5467332655332654&',U>l" m>U,Z<11/T2-2<FNPzDZZDzPNFFOacJ8>DcaOFH&j`O/&j7'%&RS O/%&S8%&Saa&(j3+ KPX@ J@ JYKPX@ e]&K_.L@$e]&K'K_.LY@  +"'53266554&####5!#321+-:FYٿdnf N 0.@:8{OO]XFe`a&`v3+=fF@C  Je_-K_.L +"!!3267#"&&546632&&j \}y1X*NqtFPqAc)%#TvtN N\on\M36(*,6&,j3+B-#,VK PX@ JKPX@ JKPX@ JKPX@ J@ JYYYYK PX@ g]&K_.LKPX@+g]&K_.K_.LKPX@ g]&K_.LKPX@+g]&K_.K_.L@(g]&K]'K_.LYYYY@,*&$## +"&'532667>7!32###%32654&##B# "T;iy4~  &??]X`d0 K/I'(o6\9_r{J4D]0XACE8a3@0g&K^ 'L%! +33!332##!%32654&##aZ2[:iy4~?\X`c0.6\9_rMMACE8 -@*e]&K'L#!+#32#54&####5dkZ8CZP]X:7zPaj&v3+ p&&3+aDy #@ &K^'L+!##5#3!3y\ZeYz~$a4 1@.e]&K]'L !+3!!32#'32654&##ajkv.vwn`NVg_O5[;boMACE8aT%a`D3@0Q]&K]'L +3#5!#3>7#!B[VV7$A2 /9 M >OQ:6a(T%@" J&K'L+333### dVdgVgo[ZZjj&)@@=# J$Ie_-K_.L%$!$%)+#"&'532654&##532654&#"'6632\MZ^:i-.p1`cthfajiP@DX*+*{Msy#IU  YF^vRHBD>KG<6:"=+db@J&K'L+333#4667##bTdTvdx"QEO6$UFb&&n3+aj @ J&K'L+!##33jlZZ;fjZcQ@ JKPX@]&K_'L@]&K'K_.LY%'+!###"&'532667>7!cZ  &?3# #{J4D]0K1I$&oa*0a+=2ayma*3=Y& !7 p-@* J&K_.L%$+#"&'5326733667p AXD1.7Bc G_/Y 0=w  3rF;aD )@&R&K^'L +%#5!3!3VZeYOzPY)@&Jg&K'L##+!##"&5332673YZ:e>dnZ=D;^;Z%]X:9Za @&K^'L+!!3!3!3ZZ[zzaD-@*R&K^'L +%#5!3!3!3VZ[ZOzz 1@.e]&K]'L !+3#5332#'32654&##qdt1z}tWRZ]f{O6\9_rMACE8a 6@3e&K^'L    ! +3332#!3%32654&##aZnct1ykZqVRY\d6\9_r6LBCE7aO +@(e&K^'L !+3332#'3254&##aZcw4`\{6\9_rME8;F@CJe_-K_.L+"'6632#"&'53267!5!&&2R!%)j8sJLt>V**V0Z K\fu\N Om{a"KPX@e_&K_.LKPX@#e&K_-K_.L@'e&K_-K'K_.LYY@ $%##+#"&&'##33>3232654&#"GgfJZZJcgIksukjttlfo\Ti_M[o3@0Je]&K'L'+#.54633##"338i&C*ZlVZX\h(8 .P@ag6(U;DBH.!D9!+1@.'JHg_.L$"+++4676673>32#"&2654&#"9jvA|6#XVAL1E,hj>oHoAP=F-G1 #?B Mlp(kZw;aTfR_'21\I+U!/@,Je](K]'L!#!%!)+##324&##324&##326yFDBF28 9&#.('U@](K'L+##XJ2F1 3@0Q](K]'L  +3#5!#3667#3NUT+EEw!5#2_|ED07"H,@) J(K'L+###33dRd`R!"(J@G'&Je_/K_.L$" ((+2#"&'532654##532654&#"'6\m7. 6!ov:^"]78I~=5$>0LRդ/,17&Hj *@ JK1PX@* e _ (K]'K_ *L@( e  g]'K_ *LY@%# ** +"&'532654#"##53533#36632 $wYDYLLXZ4bbDH#0d^]AZZAX&)*]ggLUU&v7"F@C  Je_/K_.L +"&&546632&&#"!!32676JsBDuI)NCMPPN-FD 9yad|9 H NPHYV L3"VNL &*j_M!KPX@ JGK"PX@ JG@ JIYYKPX@! e](K_'LK"PX@+ e](K_'K_'L@) e](K]'K_.LYY@!!$#$! +32####"'53267#32654&|iees} .L8 6Blo:IEMKKYΩ^Aګ(00#U@8@5 e (K^'L$! +32##5##335#32654&xkebtZZnp:IDLKKYګ(00# U&v&\&UG #@ (K^'L+!#3!3##XXV2a]%@"]&K'L+!#!5ZH]ʓUFKPX@&K](K'L@](K'LY@ +##35X, &:C&3+ &ZC &:vm3+ &Zv% &:j3+ &Zjg6&<Cp3+&\CT(3@U]M +75!(NN(3@U]M +75!(NN(3"*dD@eU]M +D!5!!5!aaZ@@  @]L  +'66730A  45&WU#  @]L  +#>7 1A  55%XU#t 3+  @]L  +#&&'7r A0#UX%55  [$@!]L    +#'667##'667[_ 1x^/:4 46:4 46 [ $@!]L    +#>7##>7T 1B [0@  55%XU# 55%XU#nt  3+A 7@ JK)PX@ KL@ ]LY +'#5'37dd W<@@ JK)PX@ KL@ ]LY +%7'#75'75'37'eeUUUUM+ @W_O$" +4632#"&M@/.AA./@mC99CB::Hy&' 1h %1;EKPX@2   h g_K  _ L@:   h gK_KK  _ LY@;=<32'& CA %H&A@@KL +#@LK67]gGQ)&f 3+-7@4ee]K L +35#53!!!!3#UU痗AOOnA!&Z@WJ  e e_ K]L#"!  && +2&&#"3#3#!!5665#535#53546P8W"I)5?& /5aaaa_E:BXANBACPJ JFBNABguL ,@* !JKPX@/ eg ] K  K`LKPX@3 eg ] K  KK`L@6   ~ eg ] KK`LYY@% ,,)(%#      +2####32654&3#3267#"&55#577҅v1tfW)\[RHnn( + 7@MNnd>6"*A@>) J~gW_O&$""+%2673#"&&5556675466324#"67#43KI)F+0.?4:E1V6'S<&z:-:RY!LAq;(D(MFAlO2;\3)@_!-1]@ZJg  gU] M..#".1.10/)'"-#-!!+3330.53##0%"&54632'2654&#"5!_eEOb@TPG@URD,&&,,''V&<@6N'=CtXRRWWRRX:97855879EEjC@@ J  U]M +33#5467###!#5!#E^^a[@e5`e fj`/ (*66&u28"I@F!JgeW_O"" +"&&54>32!32675&&#"5Us;.K\.IvDlN-IV"#;TTL42GN~HHhD C|U%<6%>%>&" )5B@ =$JKPX@#h ]K _ L@'h ]KK _ LY@"766B7B1/)) +33467'73"&5467&&5466326654&#"2654&''nKL  6#InIN- !&?%7P*'/SA%$ $(*(-& !"(6*  '1\T@8)6+&$157%06)8C !"$& )$(,EQ^0KPX@" Y@3 J@" Y@3 JYKPX@5  h  g_K_K _ LKPX@9  h  gK_K_K _ L@=  h  gK_K_K K _LYY@+SR.-))R^S^MK;9-E.E),),+* (( +"&'532654&##532654&#"'66323"&5467&&5466326654&#"2654&''%@F>40:4992/)5$D/GH+'/U]KLIN- !&?%7P*'/SA%$ $(*(-& !"( ?")#$!7'.>0)3 2):I6@8)6+&$157%06)8C !"$& )$#";GT@  O6) JKPX@5  h  g]K_K _ LK1PX@9  h  g]K_K K _L@7 g  h  g]K K _LYY@*IH$#HTITCA1/#;$; "" +33"&'532654&#"'73#6632"&5467&&5466326654&#"2654&''KL7 FE-550% CZTsIN- !&?%7P*'/SA%% $(*(-& !"(6 C',&*7mD@FM@8)6+&$157%06)8C !"$& )$0 #/<@ 7JKPX@+ ~h]K _  L@/ ~h]K K _ LY@$10 0<1<+) # #   +33#5!"&5467&&5466326654&#"2654&''aKLI'IN- !&?%7P*'/SA%$ $(*(-& !"(6p<1@8)6+&$157%06)8C !"$& )$e^ &@#W_O ""+#"&'33267W_bQR.5.5KMLL6%'4X6 >JK)PX@ ]L@U]MY@  +#5>760  69 56#@ ^: >JKPX@ ]L@U]MY@  +#5>7:W! 58 69 UO 6@3JUe]M   +##5#533#5467U=KI= P``4]8 1u@LB@?JegW_O$%$# +#6632#"&'532654&#"'7+ CZTR FE-550%L7mD@FM C',&*CL*@'JU]M +#5!O'p<1ET$18@5JgW_O,*$$  +2#"&5467&&5466"6654&32654&'7P*'/SBIN- !&?$ $(%%.!"()*(-&T57%06)8C@8)6+&$17" !)$$& )E +:JV^bfosw}!K PX@B / .JK PX@B / .J@B / .JYYK PX@5  p)%!&&%p  4321e  U W  g7  geU6"g$"#!%"!e/-+(&''&U/-+(&&'^<0;.:,9*8 '&'NK PX@5  p)%!&&%p  4321e  U  g7  ge6"g$"#!%"!e/-+(&''&U/-+(&&'^<0;.:,9*8 '&'NKPX@5  p)%!&&%p  4321e  U W  g7  geU6"g$"#!%"!e/-+(&''&U/-+(&&'^<0;.:,9*8 '&'N@5   ~)%!&!%&~  4321e  U W  g7  geU6"g$"#!%"!e/-+(&''&U/-+(&&'^<0;.:,9*8 '&'NYYY@~~xxgg-, ~~x}x}|{zywvutsrqpgognjhfedcba`_^\YWUSOMJH=;7631,:-:*($"   =+53#5!53!53!5353##53#53#"&54632"'532655332##'32654&#"32654###53#5332654&##53#53533!5353!53!5335355^j5566G>BB>>BB>2 =6T575.e "" "" ' +T66j55B$266j55`6^x_56^666666^6^BQQBCPP22-! ) "',-33--336K򄄄_55_555555)d+C@@JGU_O*($"%-+ 54676654&#"663232654&#"56!++\P*X"(!>!& ())(66{">1CJW"7'##%*&,K `3=U@RJ gg _ BK_9L54;:4=5=.-(& 33 +23##"&&54654&#"'663232654&'.546"&& nzF@;y]GQ  11(.8X]}CSX++0m] XI i^3Q-)M> 4'"Q-*B  8^:?VH,&%;#go7K(PX@  J@  JYK PX@_@K9LK PX@_8K9LK(PX@_@K9LK1PX@8K_@K9L@8K_@LYYYY$&+>76632&#"#3/-2(  &,+[be1nb&&F!I\b. 9,D@A J~] ;K`9L(&#"#% +##"'##"&5467#57!32655332654&9{c]l"#k\c"F$;200U4,2:I;{;usZZsu;|:&#I5|;]GJ8YY>DH\;za*&0vb3+UV&Pv{~&$F.!&DFc8 9dD@.gW_O      +D"&54632'2654&#"1<<1/A@0 821981382=%$QJK)PX@K_K_L@_K_LY$(&# +#"&&546632665332654&#"JkpHHpR|)*_ 9?-qzzonz{qfo\\om\71 L4 ;dU|7j#/@, J_K_L$)%" +#"&&54632665332654&#"'sGo@t4Z -^9:kJSSJLRRJ A}Y$! N2 E] M.eiieeffZ2Q JK)PX@KK_L@K_LY@ #) +6653#"&53326651]%H=8x_Z_`AO$aM; /Q7 JwEw0V`/S5OkYJKPX@K_L@KK_LY@"$ +#'##"&5332653665$G5_\3L)4l0T8w94;#(@%J(K'L##+#&&'#.'336677&&'33667ZVR<vP*N3W)7  J X"46I4;Ch_251j1QBV o9@6ee&K^ 'L! +3#53533#32#'32654&##[^iy2zmc^T\eTLffL|6\9_rLBCD8 E@@=  e](K^'L#! +3#32###535#32654&թgtuu揑;MGlIKYIln(00#a}%KPX@" # J@" # JYKPX@" e_&K _ .L@* e&K_-K'K _ .LY@  %% +"&&'##33>32&&#"!!3267mJZZ Sh8d'$"O1k~R}w/T)(V Ug^OLxqN~ NU"$KPX@! " J@! " JYKPX@" e_(K _ .L@* e(K_/K'K _ .LY@ $$ +"&&'##33>32&&#"!!32672FoCXX DlC)L?MN ,A@ 4nWRg0 H MPJ L *@'f&K'L +#####3'&&*_FRF]*+#' 6JJX.2Xa<7 *@'f(K'L +#'##5###3'&SZc5O7aZ8;.2JI=a2@/ e &K'L +#######333'&&x)bDQE`ZZ~+#$ 6MMM.X<#Z`:U2@/ e (K'L +#'##5##7##337#3'&&#Yd5O4dZeVV_8  ;9?G2  =@: Jg] &K'L +#'.##"#7>7'5!BL,B\B72Z25A`B+KAB.Q8/4S4/7Q/BQx <@9 J~](K'L+#'.##5"#7>7'5!86<"?X? +&Q)* @W?"<63'@("&'"(?'3Ha"%K"PX@ ! % J@ ! % JYK"PX@ g ] &K'LK-PX@%  Ug ] &K'L@&g  e ] &K'LYY@$#""  +#'.##"#767##3!'5!BL+CYC"61[26 A^FZZ[B.P814R4-QBQUK#&x@" & JKPX@g ] (K'L@&~e ] (K'LY@%$## +#'.##5"#7667##3!'5! 6<;953.(#UU +2&&#"326632&&#"#"&&54676654&##532654&#"'667&&'53> 5X\aNZd78"2,>8",2 61 4=,EM exbYxdfbiiP@=^*,%V5?@<*2U9*! ^CIV  VG^o U#;#Ti@fLQIF E$J%G~ gga_.LNMCA=;:842,'"TT +2&#"32632&&#"#"&&546676654&##532654&#"'667&&'53>y 1;E8/ 7"oz43".5]%!& ,#h7;B"RFHXMNF:ES?;'H(:!6@4*39&C419  4(CY J #5!7#(2.+H%-&&F ; 0,5tO=7@4e_-K_.L&#+#"&&546632%"!&&267!KkoHHpkKpr  oprq-qfo\\om\[rryy7'" 7@4e_/K_.L%"+#"&&54632'"!&&267!'sGo@tHp?JJ8MHMJM A}YA{rQOOQgZVVZR@  JK(PX@_&K'L@&K_-K'LY@+"#36677>32&&g#$g^ P'90! @F71N&'X1GX(JfK-PX@ J@ JYK-PX@_(K'L@(K_(K'LY@ +2&#"#336677>   {u\ F!/D(*z*?G#4:&d ^3+&e )=6E@B!6/.J_-K(K_.K_*L%#$%&#+#"&&54663232654&#"%336673#"&'532677EdgBBhdEemncbnne)]wk]ZM$ .9fo\\om\[o+'H"Q(2LZF4+G7G"&R\I=26@3-'!JgW_O+) +"&'.54676632'66326654&'#"&'%[}@%$W}CB|X%a$$ \]]\$$[__;bbabbatsst7\L-.@+ +&JgW_O***$+#"&'&&546766324&'#"&'663266\pc7_uqd _t[;>@::A2 ?: t0usrQddSSf$e= !X~@{J-I. =: W J~   ~gg _ -K  _.L#"VTNLGEA?<;8620+)"X#X""#+5463233#"&&#"5654&&54632"&&546632&&#"32675332654&#"'6632#"'P=5&IR38XD4);!<]|=>tP&K"4RZfe7Z9 ef[R4"K&Pt>=|\hGG56>9$ #'9_omYCCYmo_>>:Nq U@G*F+ :7 JS I~   ~gg _ /K  _.L"!QOKIDB><9853/-(&!U"U   +233#"&&#"#54625654&&54"&54632&&#"32675332654&#"'6632#"&'&IQ39XC4@MD"4X6"DK?=*9"dr|j8QPq?955k"(9 # &C je^ll^ej C(""(~s&P3+&Qc3+<f:@7J_-K_.K*L +2&&#"3267#5"&&54668i)%"S2vv,Z|FSM_kl^7":@7J_/K_.K*L +2&&#"3267#5"&54669(OCSQRP ,XCu" I bii` 意d|93-t 0+'''7'77'7;Z"d!Y<:*7*75310.,))'%#"  ""(+D6632#&&#"6632#&&#"!6632#&&#"6632#&&#"!6632#&&#"6632#&&#"!6632#&&#"6632#&&#"2205'% 2205'% 2205'% |2205'% 2205'% K2205'% 2205'% 2205'% Y+45*,35*,35*,35*,35*,35*,35*,35*D#,5>GWdD@L75,+('# <;21JeU]M???G?GDC+D#'667'&&''766&&'%&&'5&&'%'66#667 D !40'"$P0"U)^$&X&&X&)^$2(!5j0"U'"$P' !/ )^$&X&k$P0"U'!50( D !/ !/ D"U'"$P0'!4A&X&)^$aD %L@IJ g   a&K'L%%$#"!   +"&'3326737#4667##3333bPQ.5-6SXsMSudTcbHKM6%'4KMALKw!MDPUG "M@JJ g   a(K'L""!    +"&'3326737#4667#333IbPQ.5-6SXH=RlSl\?^LL6%'4KM=2/? K24>@;e e &K^'L%! +3#32###535#3254&Zl~5JJbf`ZN5\:bo"NZD8 >@; e e^'L$! +3#32###535#32654&okhtLL掐Cw(00#a16@3Je]&K'L(2!'+'###323267'76654##12993G0CWZċO$27A_9dK+] nB*S9+U0"-z@('&% JKPX@_(K_.K*L@!(K_/K_.K*LY@$"-- +"&'#0#336632'"327'76654&S>QXH N@cy.)74?!8RDAX:7=G . "* I#0Tu"K)T \^ckK)OR8ee -@*e]&K'L +!3###53ZJJPN:NB -@*e](K'L +#3##5#535XLLJDDag z@ JK1PX@#g]&K'K_*L@ gc]&K'LY@   +"#!!6632#"&'532654&7Z;DyM.?>"Z]:PmF P {xwzU  G@DJg](K'K_*L $%%#+#6632#"&'532654&#"#":b<&9:#?CUX"XJay8N `ficDu8@5 J&K^'L +3#5###33K_V2VgdVjjo[ZZG 8@5 J(K^'L +3#5###33ݯbU0Rd`R&$&^!$"&^aD1@. J&K^'L+3#5##3\lV;ZZ;jZUF1@. J(K^'L+5##333+XX`Zaj.@+ Je&K'L+%#5'#375373#'5==ZZ==f5lɌDCb"U4@1 Je(K'L+73#'#5'#375%m`fz@8XX8S|6\?@ j3@0 Je&K'L +53533#3## TZ^^;fDlZ&OUUOZj& =@: Je]'K(K'L +3#3###535`fXLLZA]AZ +@( J]&K'L + ###53DlZ;jzPZk +@( J](K'L +###5!XeVHaD0@-ea&K'L +5#!#3!33YZZoYVM.UGw0@-ea(K'L +5#5!#3!533 PXX#XO2a' -@*e]&K'L +33!3##!aZnZ.PMU -@*e](K'L +33!5!##5!UX#XH0a"w@JK1PX@$g]&K'K_*L@!gc]&K'LY@""4%%2 +6632#"&'532654&#"#!#`7CwN/>>"Y]ul9YZkG P |vxyyU B#I@FJg](K'K_*L##$%&# +6632#"&'532654&#"#!#!FqB5[8$64 9@9")#JKPX@) g_/K_.K_.L@& gc_/K_.LY@433>4>-+'%! 22 +2&&#"3267&&54632327#"&'#"&&546"6654&!) $P?F9$ZCAU9'  #%D2%Om8v "#$ ("Gne9Z3X:\VT^DaF J|K;51KK43;=$Y&&^ 7$"&F^ D! *@'a]&K'L +5##5!#3BZVyQQG *@'a](K'L +#3#5##5ƮOVPIzI6<@J(K*L+#5336673*X\po\(YX)&61@. Jf&K'L+35#535333#baO_KO/@,Jf(K*L+3##5#5333667цX\nkCC.N!!Q/Dh/@, Ja&K'L+5##3332_d_ݺV6tVF/@, Ja(K'L+5#'#3733(c¹dcN DG1@.]&K^'L +5!#5!#!33 dZYyQQzG1@.R](K^'L +5!#5!#!33lBXQII{1PD8@5Jga&K'L##+3#5##"&533267YVVZ:e>dnZ=D;^;%]X:9ZJF_8@5Jga(K'L"#+3#5#5#"&55332675OWP-W=SZXg2R+2!VH\PY;@8Jge&K'L+##5"&53353667Y["F&=otZ@I='G 6# Z[9: \J<@9 Jge(K'L+#5#5#"&553536675X8 ; RXX_;8yrWGYaj)@&Jg&K'L##+36632#4&#"#aZ:k8dnZ=D;^;Z]X:9UK#*@  JK PX@(ph _ -K_.L@)~h _ -K_.LY@%$('$*%*! ## +2!3267#"&'#"&546733366"!4&j;zyCn.+nP 7FK6au]Zg3{R?5" 1P|wqf!#*@  JK PX@(ph _ /K_.L@)~h _ /K_.LY@%$('$*%*  ## +2!3267#"&'&&5467333>"!4&Fc5VP:L*)P7tADH3 Ac:>J@!J@o67 1sn?(TeAsI#8ZZ6nE P zxPg2>U =@:Jg(K'K_*L$%#+%#"&'532654&#"#373=e:$76!ax9L `gg_7wDb@ JKPX@a]&K_'L@!a]&K'K_.LY@ %'+%3#7###"&'532667>7!caHgNZ  &?3# #P{J4D]0K1I$&oGHKPX@ J I@ J IYKPX@Q](K_'LK(PX@a](K_'L@!a](K'K_.LYY@ ##+%3#7###"'53267!\@Y=X .L9 6@CJϩ^Cad@ JK1PX@e&K'K_*L@ec&K'LY@%$+#"&'53265!#3!DwN/=>#[`ZZo|nD O uz .U (;@8 Je(K'K_*L%$+#"&'532655!#3!5(5[8$659=XX"av6 N XhaD*@'ea&K'L+%3#7#!#3!3bIgNYZZoYPM.UG0@-ea(K'L +33!533#7#5!UX#X\@Z>X2PDY2@/Jga&K'L##+!##35#"&5332673YWVS:e>dnZ=D;^;Z ]X:9ZJF8@5Jga(K'L"#+##35#"&55332675OVM-W=SZXg2R+!VH\aD/@, Ja&K'L+!##3333#7#4667#S߄aHhOYrECFICAUG0@- Ja(K'L+%#7#467####33@Z>OJOuuJV-Q-/Q2(*,~&$&!3+.&D&~&$j3+.&Dj5.-"a&(&3+7&H&;C@@Je_-K_.L+"56632#"&&55!&&267!LDp0,kOpNJik; ybbyU&XR\po[[o"y}vKm;3");&j:3+3&)jT&j3+&jS&&j3+!&j#A@>  Jg]&K_.L$%$+#"&'532654&##57!5:g-/n2a`qiCGcc^xRJCC>IPA@> Jg](K_*L$%&+#"&'532654&##57!5DpBAwQ;^ !a;M`o[;@5aIGm=PYMTK=JbW&L3+U-&Lb&ji3+U-&j(=&2je3+7'&Rj =b7'"c=&bje3+7'&cj ;&j3+&j pW&L3+&\LB p&j$3+&\j p&R3+&\R[PY&j03+J&j aD (@%a]&K'L +!3#5#UUZPUG (@%a](K'L +#3#5#OWPIza&j3+U&j_:&{_L:R@OJ  ~ e d](K'L  +"&'53255#5#535!#3#3 0VLLIM9I4FDJDAD:aF@C J~d&K'L +"&'53255##333 02_d_ݻN9I4F6tVڑAD:F@C J~d(K'L +"&'53255#'#3733 0-c¹dcI9I4F΅ADF/@, Jf&K'L+3333####=d`f`ם)O6R/@, Jf(K'L+3'3733##'#7#8dcdc6D> 2@/e&K^'L      +!"&5466333'3#"Cw3vcmZdf_WUg^9b<.6MD>C<7G>+'7@4Jg&K_.L$"%$## +326553#"&'#"&546633#"32665940:Yba=MP?ns:~fEF_a/795:7Qj,&%,hfA`6.>K163"/I@FJ~ _/K_.L$#*(#/$/""$%## +326553#"&&'#"&546323&&55"326754&,A74Yd^2>% UKcxv_=KFBCFPA>@J@Ab_+(8.! 2jeee\^dj#.+KPX@(J)I@(J)IYKPX@e_-K_.L@&~e_-K_.LY@&$  +++2326553#"&'&&##532654&#"'66 mu[GT[3;95Xh^[pkaa]abK<;V&-)vcMJV  VJF=;@b`_kKAII<6:"<+&"(L@I&%J~g_/K`.L#! ((+232553#"'&&##532654&#"'66Xl3,1=19iWGGE8AL;7&E&(S"JC19  :4-5¡1+H%-&&F#Db#J@G J!Ie_-K]'L ##+23#5#54&##532654&#"'66qx`JX`ZV[wjceghO@>\(-)zcMJU  XGzD>II<5;#<+&G !$F@C"!Jea_/K'L $$+23#5#54&##532654&#"'66Zn6-5 RVPJMJ;FS@8(L$ *_!JD17  5)J.0I%,&'F)K(PX@ J@ JYK(PX@ ~]&K_.L@*~]&K_.K_.LY@))%'##+32553#"&&5##"&'532667>7Q56lXi[9Y3  '?2# "@8{b`&VGJ5C]0K/J'(p 6@3J~](K`.L##""+%32553#"&5##"'53267!28eX]f .L9 6@;?=_d^CaZKPX@e&K_.L@ e&K'K_.LY@## +326553#"&&55!#3!o5665Xh[9Y2ZZ[@9;@b`&WG.UDhKPX@#~e(K`.L@'~e(K'K`.LY@"# +!5332553#"&55!#X38eW\gX@=`c6= 3@0Je_-K_.L%%&#+!#"&&546632&&#"32665#lOTw;r-"&f45nTL\(r+Yrn\MUI9eA7M"3@0Je_/K_.L$%$#+3#"&54632&&#"3265#P{:\) T/ggYdXM"~Eo`\qRD p0@-~]&K_.L##+#326553#"&&5#58758Yk[:Z5QD@9;@b`&VGQ?0@-~](K`.L"#+#326553#"&5#54825W[iH@=7!3< 0Y  &?3# #N9I4F{J4D]0K1I$&oAD:;!K"PX@JIK(PX@JI@JIYYK"PX@d](K_'LK(PX@#d](K'K_.L@*~d](K'K_.LYY@ !! +"&'53255###"&'53267!3 0X .M9 6ACN9I4Fϩ^B,ADP~&$ n.P!&D C~&$Kp3+.5&DKF~&$Vf3+.,&DV>~&$Wc3+,&DW>~ &$Xa3+.g&DXB~&$Yf3+.s&DY=P~&$' nJo3+.P&D&JJ =~&$Zj3+.L&DZA~&$[i3+.L&D[@~&$\i3+.n&D\D~&$]f3+.q&D]@P~&$'M n3+.P&D&MZ 2aP&( V7P"&H Wa&(KY3+75&HKVa&(QQ3+7&HQ=a0&(VQ3+7),&HVJ&&(WR3+,&HWKa &(XP3+7 g&HXEa&(YN3+7s&HYEaP&(' VJb3+7P&H&JN W(*&,K3+<5&*K(P*&, NP&L =P&2 7P'"&R W=&2K3+7'5&RK^=&2V3+74,&RVU=&2W3+)',&RWU= &2X3+7'g&RXU=&2Y3+7's&RYT=P&2' J3+7P'&R' WJ`=%&Gv#3+7&Hv=%&GC3+7&HC=%&GK3+75&HK^=%&GQ3+7&HQO=P%&G 7Pj&H XZP&8 OP&X PZ&8K3+O5&XK]Z2&Iv 3+O&JvZ2&IC3+O&JCZ2&IK3+O5&JK`Z2&IQ3+O&JQUZP2&I OPk&J OP6&< D&\  3+6&<KI3+5&\K*6&<Q;3+&\Q7f^&Bu@^, 3@0J~L +6673##&&'#56673X42'1441:\9* 3_** B""B^s, A@> J~L    +&&'5356673#&&'93W':]:2443 + _ A""A **@^g$q@ !JK PX@pgL@~gLY@$$  +2#'6654&#"56#&&'#5667.2$) C91441:g"#'? )}"B ** B">^xs%C@@"JWg a L%%! """" +#"&&#"#66323267#&'#56670(-+-/(/+D:03550;s/=.>"@ !/) @"C^jL kJK-PX@~dL@"W`PY@    +#5667#"&'3326741wJFGG5/+&4L 5 + k^xq#wK&PX@" ggc L@- ~ ggW_OY@##!"""" +#"&&#"#66323267#"&'332670(-+-/(/+IGGF5/+&4q.<.<;FD=)'$D@  JKPX@`*L@W`PY#%+4&'3#"'5326l#!>431t9 =+,48 :P/@,Jc]'L +"&'53255#53H 0 Y9I4FPAD !&7zS&Wzg 0) JKPX@'eK _K _ LK)PX@+eK _KK _ L@+e _K]K _ LYY@$"))  +"&'###53533#36632'2654#"S?P?LLXP?dyzpHGVAA . D]BYYB";".Ijdbgci '0vJK PX@&pe]K]L@'~e]K]LY@0.*('%!5! +3#"#&&546332#32654&##32654&##OEH慊FB-I*s[ES[v_JMc} @>Pa?S &F7aj;:;3J<8Ea4U0#JKPX@"]K_K_LK)PX@&]K_KK_L@$e_KK_LYY@##  +"&'##!!36632'2654#"S?P?P?dyzpHGVAA . DJo";".IjdbgciZH2@/eK_L  +"&&5332'2654&##U]n0Zdv4x|PG`\{K 8cA8^9_xMGCE<|NAR-b JK)PX@K_K_L@_K_LY@  +"&5336632'2654#"BnXQ?dy~lHGUBQ ";".Ijdbg`j;7@4J_K_L  +"&'532654&#"'6632;U()R/s|{0Q!$)jAmIN N LZpl]=Z(L@I%  Jg_K_L$" (( +2&&#"&&#"3267#"&&546632546| /$!M0s{{.T)(U;mIOn;:5ZH03N NZpl]@F7"(y@ %&JKPX@ _K_K_L@g_K_LY@#! (( +"&&54663254632&&#"&&#"3267,Hn?ArH!5> /@ML+DA :z_c|:[AE I0|I ag N [K PX@p]K]L@~]K]LY@5! +3#"#&&546332#'3 4&##OEHkVŰma"u} @>PrM3 9@6e]K]L    +!"&5466335!5!'3#"?5}k\eRfdXh^8b7$ JKPX@"]K_K_LK)PX@&]K_KK_L@$e_KK_LYY@ $$ +"&546323&&55!5!#'#'26554&#"dxyd>OG P1UEBYGGG .! 3JH"0I]^dkq_`j6 &"\ 3+< /@,e]K]L  +35!5!5!5!5!<5#ONO6;6&*K@H$JIe_K_L ** +2&&#"33#"3267#"&&54675&&546:Jv)+(T@DBHR5];LZ  UIMd>@;Je]K_L  +"&'53265!!!!-   "KL#/OO~SJ=Z-[@X*  Jge_K_L)'! -- +2&&#"&&#"32675#53#"&&546632546 /"&_37v`/B:vKoOWuHA6ZH05NUI PYqo[@F:"2@/ JK_L"" +"&546736673'254&'6>(^_!">6"L9,t6_,M##O,sAq%8MN9H#%EUU#ĵJKPX@K_K_LKPX@!K_KK_LK)PX@%KK_KK_L@%K_K]K_LYYY@  ## +"&554&#"#33663232653jl58R=XXT0[\~A=Xk ]gA@e^(#)*]gEPDxcZR+@( JK_L  +"&&533267,E(Y%(/ 7 IA-00J "07@4 JfKL +3#!57#535'5*TZZTTZZT4N44N4akK(PX@ J@ JYK PX@_KLK PX@_KLK(PX@_KL@K_KLYYY@  +2&&#"##36677>)  $jOZZA|'$FkVM#J$ U ^@JKPX@_KKL@gKLY@ +2&&#"3>773#'#4#  gj=W I4 5q HK)PX@eKL@e]LY@  +3#5333#UFFXFFbBTB,KPX@ '  J@ '  JYKPX@_K_LKPX@_KK_L@gK_LYY@ ,,&%' +#''7&&#"5663273267#"&''.'#jc" #9Amf %+I z@ ?G,.!? 6A%,;9#P'Z#] JKPX@K`L@KK`LY@ ## +"&533265332653#'##"'#e`Z:?YLZ:A\GZGd5+i gsFFd^FFnh6R..d319@6JKK_L  +"&'5326533.53##-   h}TiDL#/@K!6Q#h7SJU"=b=*TJKPX@_K_L@_KK_LY@ $%#$&# +#"&&5466326632#4&#"32654&#"GgjEEjIp'e8daZ:?f%5jqqiipqkfo\\om\/,.-gsFF=\7"&3@0 J_K_KL$%"#%" +#"&&546326632#4#"32654&#"mCj=mj?L*PPW\@#EKKFFKLD A}YH%#^gI,@__oo__ll z"bK PX@!pe]KL@"~e]KLY@" %5! +3#"#&&546332##32654&##OEH׌5}kRHfdY^[} @>nd;g@cBOEDU0#1@JKPX@&_K_K_KL@$g_K_KLY@%$,*$1%1! ## +2&&#"36632#"&'##4"326654&$ NAcyyd>QXRDAX0@G IP4#0. 4a\^ck5^<\na_;@8JeeKL! +##33232654&##ZZk*A$\kWPTXfd'def9L. sECF;/)7@4'&J_K_L$" )) +23267#"&54667>54&#"'66Cg:][AN#QP9k$"fVj_8J5'0$/9M-"(7@4&%J_K_L#! (( +23267#"54667>54&#"'66ah(J44:OS#|@ JKPX@!_K]K_L@g]K_LY@ ## +2&&#"3#3267#"&&5#57546* .!7!:@@4=J@^W9X3 P 0p@ JKPX@ e_K]L@ge]LY@  +23#!!5#536654&#"'66]ldv(>2/G%/'e`U*O,FFIF.K*46" ;#1#A@>Je]K_L  +"&'532654&&###5!!32:g-/n2a`/P2|^*KwE? RRL2@ PP3bGCi;!A@>Je]K_L  +"&'532654&##5#5!#32:^"]7Z*#r{D\" C=NPU"_ JKPX@_KKL@K_KKLY@  +2#3366"6654&K<^6^kXH J+L@G"2dI_VI#0J\^kDS0K)PX@ KL@ ]LY@  +33N&A`K)PX@ ee  KL@ ee  ]LY@ +3#3###535#535)NH`H H`HHa&''=K!3+a&'']K7&G']gKaB&/- a&/M U&OMaB&1-a&1MU&QMj~&$Km3+.&DKHS&,K3+*&*K=&2K3+7'&RK^Z&8K3+O&XKdZ&8n3+OD&X5Z&8n3+Og&X5Z#&8n3+Oq&X5Z&8n3+Og&X5~&$>3+.D&D~&$>.E&D5W&L03+.-&L=(X@U  &Je e_K_ L%$#"!  (( +"&&546632&&#"32675#535#533#yKWuN9gEF~SSa&1C3+U&QC~&$ d3+.&D ?~&$ >3+.&D Q&( W3+7&H Ca&( 13+7&H 1&, 3+&* E&, 3+&* =&2 3+7'&R U=&2 3+7'&R /W_&5 ]3+&U a_&5 73+T&U Z&8 3+O&X [Z&8 n3+O&X 5&L ) @" G_L +5>54&''>54&#"'6632'qW&=5#L(_v7F>;`-+9yB8gA?4";%cմV54&#"'6632-(C%X_$92,M%.^16Y5/+,?hO"mU3;  B/6".1@!IsZNZn_ZGFR}C]]ygY\.7U*7CHKPX@A (JK(PX@A (J@A (JYYKPX@+  gK_K  _LK(PX@7  gK_K  _K  _LK)PX@3  gK_K _K _L@3  g_K _K _LYYY@98,+?=8C9C31+7,7$%$( +'67&&'##"&546323&&55366#"&'%26554&#"%2654&#"3Q SPdyyd>OX$a2=AZh'UEBYGGG8.'8 iR;  43.! 35>.;-4J9]^dkq_`j,':e+<@9JgK_L! '% +!+  +"&&5467&&553326553'2654&#"MN}HNG:7ZINOIZ8;DSF}S_XY_^WW 9lMRccEXXDXXDXXFbcQMl9NWMMTTMMW2"*cJK)PX@gK_L@g_LY@ &$* *  +"&&5467&&553326553'2654&#"(Go@D>00X=AA=X10OXQ?dyp8d=IKBYGGHLGUBH .! :"";".}AOXQ?dyp8dLGUBH3IKBYGGH".! :"";".}A 81 "#F4+(:DBn LA,(H-@* J_KL +2#6654&#"'66gv^hZXi?C#W!#efYK1BnF4?H "-@* J_KL  +2#56654#"'66gj$RFXXb{#M!#["fY-^V bnFyBT*I@F Je e]K] L*)('&$ ! +35#5332#32654&##32654&##3#aRR̅FB-I*s[ES[v_JMcNPa?S &F7aj;:;3J<8EVN 5@2 eK_L$ +533!33##"&5532655! PZsYQQ<|^Z]^aWbNNfJwEwdgW`gQf_haKPX@.o   eK ]K  ]LK)PX@-   eK ]K  ]L@-   e ]K  ]LYY@ +!##7#!733#3#337#37#Ah A 1E8kAw8"AcKK..ONM70&+/@*. JK)PX@-  fK _ K_L@-  f _ K_LY@',,''! ,/,/'+'+$# &!&  +273#3267#"'#7&&5466"37&4&'7$KCR7=< *3O*)P74,ICS18;kF?Ip5 w,)/"qN5 M!uRX~DHQH*CFW0B:@7JecL  +"&'532665#5333#$$-RRZQQfL1.6NBNgb #@ JK PX@%e_KK_ LK PX@%e_KK_ LK-PX@%e_KK_ L@#geK_ LYYY@  # #$" +4632#"&"&'53265#53533#N8&   *KKXKKHG#1KGGKU= #2@ JKPX@"_K_K_L@&K_K_K_LY@%$+)$2%2 ## +23733267#"&55467##"&&5466"3266554&&kIrG   CKpP_EE`bllcX]$$^8.\/#LJSg$.8\on[N6_??_67u""/@ JKPX@"_K_K_L@&K_K_K_LY@$#*(#/$/ "" +23733267#"&&55467##"&546"326754&?P F $6 Q@ay{nHFFJRFD"0#I;%C IA>0"0Iq__k[^fi _A@>Je ]KL  +2####53#32654&&*A$iZWWfkWPTef9L. ''LWNECF; "{KPX@  JH@  JYKPX@e_KL@ eK_KLY@%$ +##5#535336632&&#"+~XKKH S8" 8X ?GGb,@QPC65@2J fKL +##'#53'33737#,f~Ze;EbCDaFMJNNߑ&9@6J fK_L#"%# +33733##"&'532677#5336677#^C?_C9TXN$ .8t[>GLZF4+G"GQ)89)HQ"D1 3+7"g JKPX@_K_L@!K_KK_LY@  +"&54632373#'#'26554&#"dxyd>OFG P1UEBYGGG .!EH"0I]^dkq_`jU0"!g 3+U0 +@JKPX@"_K_K_LKPX@&_K_KK_L@$g_KK_LYY@"!(&!+"+  +2&&#"36632#"&'##4"32654% P?dyzc?P?VAAXHG I8";".. Dqbgfjjd!"7@4  J_K_L  +2#"&'532654&#"'66Fkdyyd>OX 'UEBYGGGIA?3 !..! 3;%C /]^dkq_`j7u -@ JKPX@"_K_K_LKPX@&_K_KK_L@$g_KK_LYY@"!)'!-"-  +"&546323&&55432&&#"#'#'26554&#"dxyd>Oy$ G P1UEBYGGG .! 3O IH"0I]^dkq_`j3"GH4@3">@;Je_K_L  +2#"&&55!&&#"566!326MtA;kGDd5oYP3O*)P>C?I">zZX~DKLM*ES7"QA45 4)-J,Jhehd/0,%H!0".=@:JeK_L  +"&'53265#53533#&   *KKXKKHG#1KGGKU6u-:@'JKPX@+_K _K_K_L@)g _K_K_LY@/.53.:/:%# -- +2&&#"#"'5326554665##"&546323&&554"326554&4# u{vKOwEO6phuth4VDIIFQJL Ist"Q*QFQ()4HkcciWam`7"J7"F@CJe_K_L  +2&&#"32675#53#"&&5466Cm> F ML$3s1_:Hn?Hy"$LagG:z_c|:%2@/  JK_L%% +"&546733>773'2654&'4?&^f" f^"?4G52\3 U 667;\*4HIBA"0<9@6) J_K_L86#!00 +2&&#"#"&5467'&&'&56632366776632654&   y$ ?44?""  *=   =,"E1B+3==4+D.C&T),V "+-Q Kj 3+U b@JKPX@_K_KL@g_KLY@ "(%" +3432&&#"36632#4#"Uz$ Y4acWxZCq I^(#)*]gWe^U+@ JKPX@%_K_KK_L@#g_KK_LY@)(%#++ +2&&#"36632#"&'532654#"#4$ Y4acy# xZCX I^(#)*]g?I e^q  K PX@! e_KKLK PX@! e_KKLK-PX@! e_KKL@g eKLYYY@      +2#"&546##5#5353KXKKX^GGRH$ &@# JKL  +#57'5PPPP4s444uh@  JK)PX@ggKL@gg]LY@"#"# +3&&#"#6632332673#"' 39/ X 28/r;E<:FTi JK)PX@g gKL@g g]LY@%! +3#"&54663233#354&#"*>6/'  Xhh{#);.0 yHq  U=I@ JK)PX@K_L@_LY@   +"&&533267,E'W$)& (IAA00C Uo@JK)PX@(gK]KK_L@(g]K]K_LY@  +"&'532654&##57!#3!f;^ !b:M`o[;XXqAxPYMTK=2@omGm=QRP 3+QR$4@1 JK`KL$$""$' +5467##"'##"&533265332653U0~&]4[ZWmNCWnQ>X3 *)Z.,^g]ZV(d^UV",z@)#  JKPX@_ KK_L@#K_ KK_LY@(&"!  ,, +2#"&'532654#"#4#"#336632366[Zy$ mNCWnQ>XG U0~&\"^g?I ZVYd^I*)Z.," m@JKPX@_KK_L@ K_KK_LY@    +2#4#"#"&'532653366W`bWxYD$=#%  G \"^gWd^AI C%;`I*)U" a@JKPX@_KK_L@K_KK_LY@ $%%" +3267#"&&54#"#336632 &#<$xYDXG \3`bH;%C IAd^I*)^gU#@KL +33.53#UlSmP00 6 357'"c7,"$KPX@ JKPX@  J@  JYYKPX@#e _ K _LKPX@6e _ K ]K]K _L@3e _ K]K]K _LYY@ $$  +25!!3#!!5#"&&546"32654&0^>` P1Go@rEWKRQLL"A7III6!A}YJl__oo__l8"'C@@ J~_K`L#!''  +2#"'##"&5466"32655332654&kMb]l" m]bKpy<11/T2-2<|"NbkZZkaOIrRTJ8>DTRr6HU 3+HKPX@ J@ JYKPX@K_LK)PX@KK_L@]K_LYY@  +"&'7326653#'#N# *G+XH R Q-Q6b,@#B@? !JK_K_L ## +"&&55467##"&'73266533267m!9#R8# *G+X (IA\3 ,@Q-Q6;%C U"fKPX@ J@ JYKPX@_KL@K_KLY@  +2&&#"#3366O" )H+XH S"Q-Q6b,@U" |KPX@J@JYKPX@_K_L@K_K_LY@   +2&&#"3267#"&&53366O" )H+#) !,E'H S"Q-Q600C IAab,@RH"+@(J_KL  +2&&#"#4660 &##W(H" K ,0{AI"+@( J_KL  +2#4&#"'66h0H(W##& 0"IAj0, K U3@0 Je]KL! +332#'#32654&##UVh$9 f~>E4>RL0>$,/%1UGM@ 3+3"7B@?)*  J_K_K_L%,&%$" +%#"'3267#"&&5532654&&'.54632&&#"tb."$( !,E' [/C<954J(oZ1U%"J'69=33H&NPD00C IA+$ (8,DJF#(9X@JKPX@_K_L@g_LY@ +2&&#"#"&'53265466%  $=#%  $= C%;bAI C%;BH%w@JKPX@ e_K_L@ge_LY@!  %% +"&'53265#5346632&&#"3#)%  KK$=#%  KK$= C%;@GBH C%;GAI"7@4  J_K_L  +"&&54&#"566323267#=$  #!8!  %IA1(C D=3;%C (u@ JKPX@!g_K _L@gg _LY@$"((  +"&5463346632&&#"3#'2655#"!@ODM2$=#%  KK'>- ,(B47CVBH C%;HBIH%,Y"Wi 3+S@@=J]K_L  +267#"&&5#57733#* 4*G,LM#4/C HA*#r{D1/ `l JKPX@  eK _L@#  eKK _LY@$ +75353!533##'##"&553265! EYXKKH \4abYwYEGGG*']fD>c[? 5@2 J]K_L    +"&&5467#5332654&&'53#-Go@630HKRQL"8j =qNJl'IEuVPcdP3\D EIUwQ"qKPX@ J@ JIYKPX@_K_L@K_K_LY@  +2#"&53326554&#"566.D%otvmXCHHC& &"HAyyu}0aKM_7% KY 3+ Z 3+\ 3+#@ JKL +#537Xd;'!H@E J]K]K_L  +"&&55!5!5!!3267#:" p# &IAI:DBn;%C '";@8 JGg]K]L$"$" +'667#5!5!36632##732654&#"C  pS)X>4BJ[a3E- 2W:DBnQN>*7D&j,'2b@_*%"J I#Ggg]K _L)(.,(2)2'' +"&&546326654&##57!5!&''27&&#".Y:XN;o5o[;DpB &9$!&nDa9/W-/&>?26D%$  TK=J@5aI5)6( "&I4# $ I@  JKPX@_KL@gLY@   +2#6654#"'66gj$RFXXb{#M!#[fY-^U!kmGyBGa@ K@  JK)PX@K`L@`LY@   +"&'73254&'35[#!M#{bXXFR$j ByFnp V^-Yf7"7@4  J_K_L  +"&54>32&&#"3267,|y'E[4(M@2G%#E1+DAr[' I ?su; N=&2yU-!"%?@< Jg_K_L" %% +2#"&546675&&546"33#"32654xr!6 .7sdSE*S>\U"[C(5  91CJJK-%HZ2(fh7Q+@(  JKPX@(e_K_K_L@&ge_K_LY@'% ++ +2&&#"&&#"32675#53#"&&546632546 / F ML$3s1_:Hn?HyK5*6 I0LagG:z_c|: ^AEU( 'K PX@'g _KK _ LK PX@'g _KK _ LK-PX@'g _KK _ L@% ggK _ LYYY@! #!''    +"&54632"&5463333#'2655#"ndyyd>O#9"' UEBYGGG3 !..! 34BH C%;/]^dkq_`j e@ JKPX@e_KL@geLY@  +23##5#5356654#"'66gj#O@llX[[Q_{#M!#[fY-^U!PII~mGyBe@  JKPX@e_KL@geLY@  +2&&#"3##5#535.5465[#!M#{_Q[[Xll@O#jByGm~IIP!U^-Yf7'*2KPX@  (JK-PX@  (J@ (JYYKPX@ K_K ` LK)PX@5K_K]K ^K ` LK-PX@5_K]K ^K ` L@0_K]K^K _ LYYY@*)#!'' +"&546323&&553!!!'#'26554&#"!dxyd>OX#2 P1UEBYGGG9 .! 3BnDH"0I]^dkq_`j72?KPX@*!  JKPX@*!   J@*!   JYYKPX@1  gK _K  _K_ LKPX@;  gK _K_K  _K_ LK)PX@=  gK _K]KK  _K_ L@=  g _K]K]K  _K_ LYYY@#43;93?4?,+)('& 22 +"&'532654&##57!#'##"&546323&&553!26554&#";^ !b:M`o[;G P?dxyd>OXDpBAx'UEBYGGGPYMTK=2H"0.! 3@5aIGm=/]^dkq_`j7)69CiKPX@  7JGK-PX@  7JG@   7JGYYKPX@)  gK  _K ^LK)PX@?  gK  _K  ]K ^K `LK-PX@?  g  _K  ]K ^K `L@9  g _K ]K ^K _LYYY@+*B@<:9820*6+6$"$$ +'667#'##"&546323&&553!36632##%26554&#"!32654&#"C  P?dxyd>OXS)X>4BJ[a;UEBYGGG9E- 2WH"0.! 3BnQN>*7D&e]^dkq_`jp,$8K(PX@ J@ JYKPX@_K ]LK(PX@*_K]K ]L@'_K]K ]LYY@'%54%8'8 $# +3"&&5#5773!6632&&#"#'32654&&'.5467#*G,LM#4 *1U%"J'69<43H&tbC<954J( /HA.*#r{F#(9+NFH $ (8, 1/-6@&'1JKPX@3~_K ]K _K_L@1~g ]K _K_LY@/.32.6/6%$%%# +#"&'532655#"&&5#57733546632&&#"267#$<$& 1*G,LM#4$<$& (/IAI C%;LHA8*#r{>BH C%;1/46A@' @4 JKPX@)  g_K _ L@4  g_K]K _ LY@#87><7A8A20+)#! 66 +"&&5#57733#3267&546632&&#"6632#"&'%2654&#"*G,LM#4,1M$ArH(M@$U7HQ5Q*>c!*aL)5"*)E!( HA8*#r{D1/)6Pc|: I %%D62?') 0I$ "< 5KPX@!"  +J@!"   +JYKPX@)_K _  KK_ LKPX@4_K _ K ] KK_ L@2 g _ K ] KK_ LYY@0.*)&$ 55 +"&'532654#"####5754632&&#"336632}" 'wYDXX^^[S6*,+X4bbFG#1c^,)h[ E ;?#I*)]gRKUUi,KPX@J@JYKPX@K_K_LK)PX@ K_KK_L@ _K]K_LYY@  ,, +"&'#332654&&'.54632&&#"s;`+XXdgVF:44J(oZ1T&"J'69<43H&  z5+$ (8,DJF#(9+NPU4 ^@  JK)PX@K]K^L@]K^LY@  +33!!%!UX#yBnD; N@K J~ | KL    +#'#3737#'#3737cKQSIbFBTFPFEcKQSIbFBTFPFPPU0@-~e]KL +!#5##!#5##UXXXX闗闗""~KPX@ J@ JYKPX@_K_KL@ K_K_KLY@ "" +232653#547##"&554&#"5660#;#xZCXXZ4bb &"<1ځe^(#)*]g#E"/KPX@-,J@-,JYKPX@!_K_K_L@%K_K_K_LY@*(#! // +2326533267#"&&5547##"&554&#"5660#;#xZCX $6 Z4bb &"<1ځe^;%C IAH(#)*]g#E7]GK)&f 3+7]G3)&f 3+vGM)&f 3+7gGU)&f 3+ aGF)&f 3+ aGH)&f 3+7aaGN)&f 3+ aGZ)&f 3+LaG\)&f 3+  [  ? *dD@gW_O +D2#52654#1<<1 8822727? *dD@gW_O +D"&5463"31<<1 8?821827 8dD@-  JW_O%% +D5654&#"'6632Py%&5<&EH:BTH'4D2,D4G!@ 3+! 3+"  'dD@Jt +D73#'mm'TT"뼼 " / /3+(Tx P(^v(^C(4x P 3+(mQL 3+(4 (4 H,dD@!JU]M +D'37YYY苋HdDt +D'3Y( 3+( 3+~  3+~  3+  3+K dD@U]M +D#53<(; JaG0)&f 3+7pGO)&f 3+!gGV)&f 3+ LaG[)&f 3+ Gb)&f 3+NT,dD@!U]M +D!#5!nBPNT0dD@%Ue]M +D!#5353BBPNT0dD@%Ue]M +D!#533BFB(PNT0dD@%Ue]M +D!5#533BBPNT&dD@U^N +D!!533TBBnNT&dD@U^N +D33!NBBNT*dD@Ue]M +D33##NBBB(9zK ۰3+(W*5 53+ [ (v'dD@Jt +D'373OXYNކ(v'dD@Jt +D73#'(1OXY܄(#0+'57#k1kE>?(#0+57'5(E>?Ek1(C 3+(GR@ 3+(R 3+(CQ 3+HҰ3+(&dD@U]M +D3##(q55(-dD@"U]M +D#5#55q5( ,dD@!U^N +D333(5q 5( &dD@U^N +D3#5353Φq55(0IdDK PX@nU^N@U^NY +D!53!53B8M9Хcc(0FdDK PX@nU^N@U^NY +D!53!B8Хc(K 1dD@&JH GU]M +D'57!!#O1;D54&#"'663233267  0DJ Z!5# &NU X!1( 9I4gppD%ew?TEEpvC ^%ew?XAAD=T JK1PX@_@K]9L@a_@LY@ +2#5.5466"32654&hI?{ZZ]z!WLKFM=WD % 5eSe> I niA 6C   <92 &H>72 `&I0,=@:) *J_BK_=L'%,,+"&546756654#"'6632%3267KB ed+ (<=G^t  # E88&L7#?+ E ;.I1I:")E O@  JK1PX@_@K9L@_@LY@ %.+!6654''7&''7&#"'6632q'(4H5V*'aGLH$!pCC+D$F EJ@  H=L+6654&''7&&''7&&''"&+ 54&#"'663233267#"&5467I!5# &NU X!1( KR %ew?TEEpvC ^%ew?XAEpvD7&"$3F@C Jg_CK_9K=L&%/-%3&3$$ +2#"&&'#32#654&&#"&&546"32654&>Di;m'5+"<.CEM ')Oe0L\(J4OEE"@|Z FJ  BI #hegf7"FM=b7""a*U0=Y&a*P JK1PX@~8K9L@~]8LY@ +3333#467###azyYJ66j#!l7RUX JK1PX@~;K9K=L@~];K=LY@ +33#467#'UhcTH R34&"&J@G$Je_CK _9K =L" &&%# +5#534632#"&'#3#2654&#"OKKnDi;m)LOEEMLTFi@|Z@)FT/hegeƢ;h=Y&&y;&hyk=V47"T : Z ,@) JfKL  +33#'#73'&' M[A?[9 桡 8@5ee ]K]L +!!5##!#3#3%35#ԩP]|MHG32",7>@"'!  JK"PX@#  e_K _L@( U  e_K _LY@=;9864$#%"$%#" +3267#"&'#"&&55!&&#"5663266324&#"766#3262x|Z=3(M!#d1>QT6A^3WOJ1M&(M2>#[MIa[3*?UK^H9=:CPW"A4B)-).yFDBF:E+DYE;3+L&#.(';7@4  J_K_L  +"3267#"&546632&&FQ]VX"E#"D.>xV+S#"Bm^^m H Q|EE V@]K]L!$!" +##324&##32~\d\M>d^v$ ?@<e ]K]L     +2##5#535#3#3254&}88H9dHGHd^V )@&e]K]L +!!!#3#3AHG!"(J@G"Jg_K_L (( +"'732654&##53254&#"56632gT(C,;?SE:HS<7]"^:vo!6 .7m %F&&-%HZ2)O[C(5  91CJV7)@&cKL  +#2#"&546X+2 +@(JK`L  +"&'532653t $'XR G -xPJV&@# JKL +3366773#'VYgg+#% ,@) JK^L  +35'7373V#AY`$:&7R;@ ;3+HEF#@ 3+Z$:@ :3+4|" 3+9*9@6JgK_L &$* *#) +#"&&5467&&55332655"32654&#)/8xd>b950%%Y1562hA==AA>>H1EJ8X`+R;9IH.HH.<<.H955<<559; ? $@!_L  +2#4&#"#466>}]OUVP]9s }_ll_S{E;? !@_L"# +#"&&533265?;sTWs8]PUUP S|EE|S_nn_V 2@/g]KL       +2####32654&ne*bT3Y:+HE=TM-N1G-5./8@5Je]KL' +37.54633#5#35#"0ijXXDG;>< $<-IP(--1;@8JgK^L  +!"&54667'3353'35#"ji0fXXGD@<>PI-<$ G1--(@]KL +!##5!#YHHQ!@K_L#$ +#"&533265/bJjmY@AC=8Z4m[W=BH7Y&/HQ&H@ H3++@ ܰ3+(JP(@ 3+FY7336673#.'#RC  VWT  DQ]T  W,X37."PX..:. /:']!A@>  J~]K_L$%' +#"&'532654&##57!5fWov:^"]77!Y<8   :6tj&WG H,BqNG$)&f 3+G)&f 3+?G%)&f 3+ &lK%PX@# e]K]L@!e e]LY@&%!%! +535323###5732654&##32654&# 2VZ'QAVK;U<,6;L]>02@-/;2- ';?-$"#,$")?G')&f 3+?BG()&f 3+'*Gp)&f 3+(G*)&f 3+?G+)&f 3+G,)&f 3+vG-)&f 3+?G.)&f 3+?DG/)&f 3+?G0)&f 3+?G1)&f 3+?=JK%PX@KL@]LY@ +333#5467#?7D6FbT!?(G2)&f 3+&G)&f 3+?hG3)&f 3+?G5)&f 3+bG7)&f 3+:G8)&f 3+TG:)&f 3+8fGD)&f 3+5OgG )&f 3+$XgG!)&f 3+!gG)&f 3+7lGE)&f 3+$XGG)&f 3+$MgGH)&f 3+!KgG))&f 3+$gG*)&f 3+!gG)&f 3+$XgGJ)&f 3+8{aG)&f 3+7UGN)&f 3+7+gGP)&f 3+7^gG )&f 3+$fgGR)&f 3+gG$)&f 3+&veG)&f 3+&vG)&f 3+7lgGS)&f 3+ GW)&f 3+3ZaGX)&f 3+;|JG )&f 3+5(aG<)&f 3+JaGY)&f 3+ >eG)&f 3+7}G~)&f 3+MaG)&f 3+#fG)&f 3+$gG)&f 3+bbG)&f 3+3vZFL)&f 3+7FU)&f 3+3ZFX)&f 3+JFY)&f 3+7}kF~)&f 3+MF)&f 3+3fF)&f 3+$F)&f 3+bF)&f 3+Qo"'.y@ JKPX@"   e]K_L@&   eK_K_LY@(((.(.&##$%" +%!3267#"&'#"&53326536632'&&#"oYP3O*)P7Bc#YGjmXAACC?I[_M.4)6m[W=BH7Y8%O 32) X 21* G P1UEBYGGG .! 3S;E;k;EH"0I]^dkq_`j0@# /JKPX@. g  g_K]K  L@,g g  g]K  LY@00.,*)#%%"" +3&#"#66325#5754632&&#"3#32673#"'m 32) ^^[S6*,+ 21* ;E)h[ E ;?#D;E"-6@@  =. )& JKPX@) g  g  _K L@- g  gK  _K LY@777@7@;95420--$$ +35#6675336632366326673#5"&'#5&&'54#"54#"U30-G U0~&\5[Z20/W=AW@=nFA:mNC=.; I*)Z.,^gM/<  -,MG +NZVy""+@)& JKPX@#~|_KL@'~|K_KLY@$##+$+ "" +26673#5.'#5#66753366"54W`b21+W.`^)X35*G \&NGAE"^gP7; 8: I*)JMI%I0".<@ ! JKPX@/g g _K  _K  L@3g gK _K  _K  LY@0/75/<0<..($$"" +5&#"#6632336632#"&'#32673#"&'26654&#"U 32) H NAcyyd>Q 21* 0@GJRDAy;EHI#0. 4K;EI/5^<\n\^ck",KPX@ +JH@ +JYKPX@$g g_K  L@(g gK_K  LY@,,"%%$" +35&#"#66325336632&&#"32673#"'U 32) H S8" )H+ 21* ;Eb,@Q-Q6!;EH"*K@H )Jgg_K L**"%%%"# +35&&#"#6632546632&&#"32673#"'R 32) (H00 &## 21* ;ENAI K ,0|;E"8g@d !4J~|||_K_L10.-%#88 +"&'532654&&'&&#"#667&54632&&#"26738Q [/C<94 D3&$oZ1U%"J'69QXKKH N1JE#F>DGC"qrF. 4FI#0JLMGRghRZ` JA@> eK _ L  +"&55#5353!533#'2655!(jmGGYXGG/bGC=@m[BFFD8Z4GH7DD=B S$/S@P JI  e]K _ L.+)'  $$ +"&&545#53667#53!&&'53#3#%326545!-Go@-6 0%$=&=#J6,KRQL =qNF-GIEK85PEI;UFwPcdPU:0%1KPX@!J@!JIYKPX@$cK _K_LK)PX@(cK _KK_L@(c _K]K_LYY@'&.,&1'1%%#%(' +36632#"&'53255#"&'##"32654P?dy%"9< 0!&?P?VAAXHG";".Km#rADI4F . Dbgcijd7:>$1KPX@ !J@ !JYKPX@%cK_K _ LK)PX@0~cK_KK _ L@0~c_K]K _ LYY@&%-+%1&1 $$ +"&546323&&5533#"&'53255#'#'26554&#"dxyd>OX,9< 0$ P1UEBYGGG .! 3QADI4FH"0I]^dkq_`j:&@JKPX@+~ c_K]KL@)~g c]KLY@#"!  && +"&'53255##5754632&&#"3#3| 05^^[S6*,+,9I4F)h[ E ;?#DuAD7"/<@ ' JKPX@2eg  _ K _K_L@6egK  _ K _K_LY@!10750<1<+)! // +23733#"&'53255##"'532655467##"&546"326554&5U F9< 0u{vKOwEO6phuusCJHGQJL"()GcADI4w>st"Q*QF - QJkcciWam`U:"x@ JK)PX@!~cKKL@'~c]KKLY@""%# +3>7733#"&'53255#'# gٲ:9< 0=Ws4 ADI4F5(:]@  JK)PX@~cKL@~c]LY@ %# +3#"&'53255#,9< 05PADI4FU:"0@$ JKPX@%  ~ c_ KL@)  ~ cK_ KLY@-,)'#! 00 +"&'53255#4#"#4#"#336632366323  04mNCWnQ>XG U0~&\5[Z,9I4FYZVYd^I*)Z.,^gADU:E""}@JKPX@"~c_KL@&~cK_KLY@ "" +"&'53255#4#"#3366323 04xYDXG \3`b,9I4FWd^I*)^gADU0"%3@" JKPX@%g _K_KL@)gK _K_KLY@'&.,&3'3!  %% +2#"&'53255#"&'##3366"326654&Tcy%"9< 0!&>QXH N1RDAX0@G"Kn#rADI4F . 4I#0J\^ck5^<\n(:""KPX@J@JYKPX@!~c_KL@%~cK_KLY@ "" +2&&#"3#"&'53255#3366O" )H+,9< 05H S"Q-Q6։ADI4Fb,@3:":L@I*)#Jc_K_L.,'%!:: +2&&#"#"&'53255#"&'532654&&'.5461U%"J'69=33H&9< 0(8Q [/C<954J(o"F#(9+&:aADI4DP+$ (8,DJ.@$#JKPX@&eg_K_L@$geg_LY@(&! .. +2&&#"3#"&'53255##"&'53265466%  9< 0$=#%  $= C%;&ADI4{~AI C%;BH:=@:JcK]L  +"&'53255#33>733a 0^rr^9I4F6235<.AD:@@=  J~cKL%# +3#"&'53255#'#37,9< 0 c¹dɊADI4F':@@=  Jc]K]L%# +!#"&'53255!5!5#9< 0 BnADI4F:D.:n!+6KPX@ ()J@ ()JYKPX@(  e d_K _LKPX@/  ~  e d_K _L@3  ~  e d_KK _LYY@-,20,6-6&$!  ++ +"&55#'##"&5467754&#"'6632332672655.2?#NCI`~[:5*L!#`4b^,  #C[OdM7=J?L,*MRPW C4BV^܇#EKN083-*7:""/KPX@J@JYKPX@!d _ K_L@0~dK _ KK_LY@$#*(#/$/ "" +237333267#"&55#'##"&546"326554&>OF,  #2?$ P?dxypGGGHUEB".!E1#E=J?H"0Iq_`j]^dk7u0=@ -.JKPX@+_K_K _K_L@)g_K _K_LY@21971=2=+)$" 00 +"&&55467##"&546323&&55432&&#"326726554&#"/6 O>dyyd>Oy$  $UEBYGGGIA?3 !..! 3O IA;%C /]^dkq_`j7:K"$+V@S#Jec _K_L&%)(%+&+  $$ +"&&546632!32673267#"&55"!&&9MtA;kGDd5YP3O*  #1@,K?I> >zZX~D QHDU+:!"5]@Z +23Jec_K_L0.)'$"!55 +"&55#"&546675&&54632&&#"33#"326732671@,8sn!6 -7s[:S(!!E/ySF8IR<7V!  #=J> YC(3 ;1DJFL,&H\1(#E!:"5]@Z!+23Jec_K_L0.*( 55 +"&5532654##532654&#"'632#"'32672?"]7=Jivte\ov5  #=J.42)AIGSFKeA#E7lgG")&f 3+$#gGF)&f 3+>gG%)&f 3+$fG)&f 3+!gG&)&f 3+ GI)&f 3+aG,)&f 3+$XgG.)&f 3+5ZaG2)&f 3+G5)&f 3+5aG6)&f 3+aG7)&f 3+aGg)&f 3+Gj)&f 3+7G:)&f 3+Gq)&f 3+7aGl)&f 3+7+gG>)&f 3+5(aG=)&f 3+]gG?)&f 3+7gG@)&f 3+7daGA)&f 3+$fgGB)&f 3+#GE)&f 3+!gGO)&f 3+GP)&f 3+ G)&f 3+aGV)&f 3+vaGW)&f 3+5OaG)&f 3+5\gGX)&f 3+JaGY)&f 3+aG])&f 3+baG])&f 3+ZaG^)&f 3+ .aG_)&f 3+$_G)&f 3+aT&%N3+U0&ENaPT&% eUP0&E lamT&%L 3+Ul0&EL 3+=Y&&'zv3+7&F'zva&'N3+7&GNaP&' 7P&G Nam&'L 3+7m&GLt 3+a'7@4J]K]K_L!,!6$" +#"'532654&'7"##324&##3 JJ  $&5&) kV$3ua"057VPr;(Ώ7*7@ ) JKPX@'K_K _K_LK)PX@+K_KK _K_L@+_K]K _K_LYY@,+31+7,7$"** +"&546323&&553#'##"'532654&'7726554&#"dxyd>OXG 0!$3KI  $&5&&UEBYGGG .! 3H& 5(&057LI]^dkq_`ja8&' V78&G $a+&(73+7q&Ha+&(53+7q&Ha8&( ,78"&H -aC&(i7G"&HQ= 3+a&('zMr3+7&H&M^za&)N3+&IN3+=W&*L3+7&JLla&+N3+Q&KN)3+aP&+ UP&K `a&+jQ3+&Kj`3+%&+z&Kz aG&+ sUG&K 5Ha&, H8&LQ 3+=&,3+g&*ak&.v3+E &Nv3+aPk&. tUP &N 5amk&. JUe &NLj 3+aP&/ VLP&O PW&/' VL3+P&O' L3+as&/Lo 3+p&OL 3+a8&/ ,<,&OJ ް3+a*&0Nm3+UV&PNaP*&0 UPV"&P a&1N#3+U&QNaP&1 UP"&Q _ae&1L 3+Ua"&QL} 3+a8&1 |U8"&Q 5=#&23+7'q&R/=&23+7'R&R/=+&2|3+7'q&R+=+&23+7'q&R(a*&3v3+U0&Sva*&3N3+U0&SNa_&5N3+U&UNaP_&5 rIP"&U aP_W&5'Lz r3+IP&U&L3 ag_&5L  3+o"&UL 3+3&6N3+3&VN3P&6 +3P"&V 3&63+3&V3&63+3R&V3P&6'N +3+3P&V'N  !&7N3+SY&WN>xx3+ P!&7 @PS&W  q!&7LY 3+mk&W 8!&7 3~&WJ հ3+Z&8jL3+O&XjZ&8Q3+O&XQUZ8&8 lO5&XJf װ3+Z#&8n3+Oq&X5Z&8n3+OR&X5X&9QO3+&YQPX&9 UP&Y & &:Nx3+ &ZN0 P&:  P&Z F&;N3+&[NF&;j3+&[j6&<N3+&\N&&=JS3+'&]J&P&= S'P&] &m&= 'm&] Um&K 5SR&Wjxx3+ 1&ZO1&\Ok.&DUj&@N3+7X&}7X&}&7X&}7X&}#7X&}7X&} 7X\&}7X\&} ~&$ ΰ3+~&$& ΰ3+&$ed ΰ3+&$mn ΰ3+&$PP ϰ3+' P$Q ΰ3+*'q$U ΰ3+*&$U q ΰ3++&+&&+&+&+&+& A&(Q ΰ3+A&(Q& ΰ3+'(d ΰ3+'(n ΰ3+'(P ΰ3+'( P ΰ3+U&U&&U&)U&2U&%U& &U\&$U\& &+K ΰ3+&+T& ΰ3+]'+d ΰ3+V'+n ΰ3+f'+P ΰ3+_'+ P ΰ3+o*'+q ΰ3+i*'+ q ΰ3+NH&GH&&H&eH&nH&aH& bH\&`H\& T&,u ΰ3+&,o& ΰ3+/',d ΰ3+ ',n ΰ3+?',P ΰ3+?', P ΰ3+A*',q ΰ3+B*', q ΰ3+7'&R7'&R&7'&R7'&R7'&R7'&R &2) ΰ3+&2B& ΰ3+'2d ΰ3+'2n ΰ3+p'2P ΰ3+q'2 P ΰ3+O/&O/&&O/&O/& O/&O/& O/\&O/\& &&< ΰ3+O'n< ΰ3+c' P<- ΰ3+*& <V ΰ3+8& 8&&8&h8&q8&d8& e8\&c8\& W&u' ΰ3+ &u7& ΰ3+'ud ΰ3+'un ΰ3+'uP ΰ3+'u P ΰ3+*'uq ΰ3+*'u ΰ3+7X&} 7X&}%-+&+&%U&U&%<H&WRH&%x7'&R7'&R%%O/& O/&%*8&Z8&%{7$X&}' 7$X&}'& 7$X&}& 7$X&}&# 7$X&}& 7$X&}&  7$X\&}& 7$X\&}&  &$'u ΰ3+&&&$u ΰ3+"'d&$e ΰ3+,'n&$o ΰ3+'P&$Y ΰ3+' P&$[ ΰ3+"*'q&$_ ΰ3+"*' q&$` ΰ3+U&'gU&'&gU&&)gU&&2gU&&%gU&& &gU\&&$gU\&& g&+W&< ΰ3+~&+Q&&6 ΰ3+'+'d ΰ3+'+'n ΰ3+ '+'P ΰ3+ '+' P ΰ3+*'+'q ΰ3+*'+' q ΰ3+8$&' c8$&'&c8$&&hc8$&&qc8$&&dc8$&& Yc8$\&&^c8$\&& Wc&u9&G ΰ3+&uG'U& ΰ3+)'u'd ΰ3+)'u'n ΰ3+ 'u'P ΰ3+'u' P ΰ3+*'u'q ΰ3+ *'u' q ΰ3+7X&}Mx7X&}Lz7$X&}&  7$X"&} 7$X&}&%- 7X&}QW7$X&}&QW ~&$M3+~W&$L3+~&$F 3+~&$%' 3+&$GT*dD@GW_O$+D5665"&54632G"3T%"5<$UdDJKPX@nW`P@W`PY@ %"+D3267#"&55C !$+>6*:.1?GTq^QIvh\!-QdD@Fg g W _  O#")'"-#-!!"""" +D663232673#"&&#""&546323"&54632v4/60-3/51 5=4>U&&gU"&gU&&%<gU&QfU&&Qfg7&(G" ΰ3+4&(D% ΰ3+&+L, ΰ3+&+V% ΰ3+a-&+TdDKPX@JG@JGYKPX@~W_O@%~~W_OY@ $+D5665"&546327&&'53"46` T%"5<#Q "R TdDKPX@JG@JGYKPX@~W_O@%~~W_OY@ $+D5665"&54632756673"4I `6T%"5< R" Q#vJ\$KdD@@Gg gW_O """" +D663232673#"&&#"5665"&5432v4/60-3/51H"415=4>! +)1H&MH&LH &cH &`H&QH\&aJ&,M3+>W&,L3+&,d" ΰ3+&,k% ΰ3+TdDKPX@JG@JGYKPX@~W_O@%~~W_OY@ %+D&&54632#&&'53:4"n7` T<5"#Q "R TdDKPX@JG@JGYKPX@~W_O@%~~W_OY@ %+D&&54632#56673:4" `6T<5" R" Q#vJ\$KdD@@Gg gW_O$#! """" +D663232673#"&&#"&&54632#v4/60-3/51=14"5=4>1)+ O/&MuO/&LwO/ &O/ &O&&O&&&O/&QTO/\&6&<M\3+6W&<L^3+'<" ΰ3+'<% Ͱ3+&3V& ΰ3+{c !ddD@  JK"PX@W_O@ ~W_OY@ $$$&+D#&&'53'4632#"&74632#"&R*7` j#S "T{c !{dD@ JK"PX@W_O@#~W_OY@ !!    +D56673"&546323"&54632 `6pj T" S#^R dD@ Jt+D#&&'53R*7` ^#Q "R8$&&Zc8$&c8$&&%{c8&Q8$&'Qc&2O+ ΰ3+&2 % ΰ3+.&uE# ΰ3+ &u!% ΰ3+&V&u^| &dD@Jt +D56673 `6^ R" Q#MT*dD@GW_O%+D&&54632#;3"T<5"{t@U]M +3*{m!@  Ht +'7'77'>RRSSRR>>RQSSQR>d{ 4@1JHU]M  +3'7'7#@ll@y=kk=H+{ 4@1JHU]M  +#'73yAmmA=kk=!(A@U]M +5!(AII&__n [$@!]L    +#&&'7##&&'7)A1 Z@/4:65 4:65 {Z$@!U]M +3#迅'H,{Z$@!U]M +#53'!{t&@#U]M +#53#__b{t UKPX@e]L@eU]MY@  +#53#3#__b'&{t UKPX@e]L@eU]MY@  +#535#53#__b&''b /@,]L   +3!333ZfZvZ:qH #/K)PX@+ g_K _K_ L@) g g_K_ LY@#%$ +)$/%/##    +"&54632"&54632"&54632"&54632$$$$$$$$$$$$$$$$M $%%$ $&&$ $%%$ $&&$ {t\KPX@e]L@"eU]MY@ +#535#53#3#______b&''&{t WKPX@e]L@eU]MY@  +#53#35#__bb{u@Jt +'3``}{u@Ht +#7#`uu`b{u @ Ht +'77'`uu`>>>sYkkY777{t YKPX@e]L@!eU]MY@  +#535#533#_____b&'&JT 1@.gW_O      +"&54632'254#"MNJQMOJRTT,&&sljsrkju?OQPOLT)J@G JggW_O%#)) +2&&#"36632#"&54>"32654&# "6>6);JRED]/T ,1(&/*T;)F*F@FP^b/ZH+-/-.&+IV'J@G  JggW_O#!''  +2#"'532667##"&546"32654&D]-TB% 6= 3(@JRE$/'*+3-V\c/[I,<,G(H@AS9-+&.-*;8FD)&f 3+$MFH)&f 3+$fFR)&f 3+ LF[)&f 3+!KF))&f 3+$ P@MJe g_ K]L   +2&&#"35!#3#3!5"&5466$G 5;DEPxt1_@ dXZWBBCuKs@3)%.4{@0-' !JK-PX@ghL@%gW`PY@*(%%! +7&'#7&&546773273&&'267#'&#"'+#?87vo?*?!#  a(K$$M5c  ]"WO:A:[p)Zy[SWbH " JX5ItRc8.K"PX@#$J@#$JYK"PX@g_K`L@&~g_K`LY@(&! .. +2&&#"336632&&#"667#"&&5466^6^'$I0?]3D>G;&  +@1G $L5c>CHGYmh8#KD8 JZpl]UV&KPX@ %"J@ %"JYK PX@! n_ KLKPX@ _ KL@$ K_ KLYY@$#! && +2#4#"#5#54#"#336632736[ZWmNCWFnQ>XG U0t*[F?'"^gYZVض d^I*)Mň 2#'+]@Z( Jf   eK  L+*'&%$#"!  +3#535#53533533#3###3'#3'#3'#3'#XNNNNh_vONNNNi_v**^BC__*@R@@R@|RRRtS?@0 / JKPX@, e ] K_ K ]L@0 e ] K_ KK _LY@#42-+ ??  +2####32654&2&&#"#"&'532654&'.546mj1_}[U@BB8<i+@ 1#&%6#6R[&BJ 0($5#7Xef9K/ 'LECF=\C$)*:+FWQ,#)):,BL "&*.14h@e"J  f   eK L4310.-,+*)('&%$#!  +3#53'#53'33733733#3###3'37#3'#37#7#7#-XL>3+P'k.\/k%O)1< IT*Z0w-R-< T_ D= V)?(@R@@R@әRRRRRٕ<8@5JIfK L +3#533333##fWWUbβe8NB::BN>*6@3J]KL +35'75'75#5!#77u$u$u$u$P5idP5iOOQ5ieQ5i/<@: *JKPX@:~  g_K` K _ K L@7~  g_K^ K _K LY@10750<1<//(&!%&! +33326654&&#"'6632#6632#"&'#2654&#".{Vk eG:lI+`$$n:dQgtYR]QPL*XF1  -C$"(4 (B ZbEg9GL^ePcR>.[=6-,D;#(384 $1C@@  J_KK_L,*$$ +"'532654&''#7&&546326654&#"n$''$'3rS>RPU,%M>>I68-1R>(!  K,*wȆz [2URLE=YgWJQKLw,(*6IR 2#+1b@_  eeg ]KL 0.-,'&%$"!# #  +23#3#####535#535#3&#36654#326drKACQui8SWWWWF$53;NJD6   6=Qw6Y6JDDzY {D =!'@'JK-PX@) eK_K_L@) e_K_LY@%$#" +5&&5466753&&'3#667uIc@:h-"$Y03h>-`Mhr*<PG âf_ &#NFMzM : _"D@A  fe KL"!  +3#3##'##7#537#533&&#3^`cLU_SR_UKb`.iB@R@@R@ b$U01TR 9^@[6 7 Je   e_K _ L42.-,+&%$# 99 +"&547#53>7!5!6654&#"'66323#!!3267"u~ /TDI8~@D2V"&m5eu7WCJ< :{9k$"r ^_&@!@/5OSU)@!@tQ=Y"K-PX@"  J@"  JYK-PX@!K_K_L@%_KK_LY@ +&&'667#5&&5466752\%$G*(K$#F.@E`^lee#L N GG Šda &uxyOD@AJ~ gU]M"" +!53267#53&&##5!#3# OV UL0 aPJ,.<@82@@'C@JP h pp3+1&1:X@U ~gge W _ O'':842'1'10.*(&&  +".54>32'2>54.#"'32##532654&##Pc66cPLe96cP@pV0.SqDZP.Sr>RLV>RF',)+E 6cPPc66cPPc65.UrEArV1Q[ArV1_EDCL%*(# :@+ * JK PX@9~  ~ f  g  W _ OK PX@2~ f  g  W _  O@9~  ~ f  g  W _ OYY@ /-(&::   +#'##3#3'&'2&&#"#"&'532654&&'&&546NE#y$D? IId"";5):)ND<?"* #8(;rbbX6_  ^ 5 0(,4 =   0#)6_ .@+eU]M +3535#5353YHH9@  * )3JKPX@*  hg ]K_  L@.  hg ]K K_ LY@".,'%!99 +33467'73"&'532654&##532654&#"'6632fKL  6#Ip%@F>40:4992/)5$E.GH+'/T6*  '1\T ?")#$!7'.>0)3 2):I-F&KPX@   7 6@ "!J@   7 6@ "!JYKPX@2  h  e  g_K_ LKPX@6  h  e  gK_K_ L@:  h  e  gK_K K_LYY@$;942.,+)%#FF%( +57>54&#"'663233"&'532654&##532654&#"'6632s))%1#E+@I;8QËKL%@F>40:4992/)5$D/GH+'/U6p'1&!.?71N5M>6 ?")#$!7'.>0)3 2):I!"$ <@9e eK ^  L +35#535#5333#3#!aWWWWZ8GYGGYGP `K)PX@ e eK  L@ e e ]  LY@ +35#535#5333#3#UKKKKXKKKKGYGGYGB@? Jgg K^L""" +32673#"'!!&#"#6632 21* $ 32) :FPT;E/ *kK-PX@%e]K]K L@#ee]K LY@%! +3#53532##32654&##3#aWW5}kRHfdY^[Nqk@kAOEVLGpNa_#N@KJe]KK_L##  +2##3267#"&5#32654&&*A$i   CKfkWPTef9L. '/#LJSNECF;.0 &.5KPX@$32J@$32JYKPX@(eK_K_LK)PX@,eK_KK_L@,e_K]K_LYY@ 5) +#7&&54677&#"'663273#'##4&'7"6657CA/:eh2 *L!#`4DCH^@#MD)JO>BY74)K>HU B#L,*m$/ =KMB$ 30"@!JK)PX@*~K ]K_L@' ]K_LY@ " "%" +#&&5#5773373#3267#"&'7hCQ LM#4[GCJw+* 4( F5#8*#r{9C aDUFh`JK)PX@aK_KL@a_K]LY@"& +366323#5#4#"#Y4acOWOxZCX(#)*]gWe^aD|/@, JaKL +%#5##366773|V%IZZ>iPU@"D"UF#_@ JK)PX@aKKL@a]KKLY@ +%#5#'#33>773#W)=WW gٰJ5s4 &D 5@2J]K]L  +5!5!5!!gxD6PD'F 5@2J]K]L  +5!5!5!!X p#:DBn=#g JKPX@_K_L@!K_KK_LY@##  +2373#'##"&&5466"3266554&&kIrGGpP_EE`bllcX]$$^8.\6\.8\on[N6_??_6e )rK(PX@ #J@ #JYK(PX@]KL@K_KLY@ )) +2&#"#.'#3667366766  [   [^o  ~]~ R6I$';- U./L.V&'\,N.\"%W/E=7 *"0W@ *JKPX@]KL@K_KLY@00%+ +333>73366776632&&#"#.'#[J  _`\  2 0% hg\    `,X37."PX..#A\.:. /: '!,H@E( JgK_KL#"",#,!! +23>73#'#5267&&546"654&5F>:w^e[5$ I3 @'=58W06235<I:;?D! ,Ba#@ eKL +33!!aZ2OU#@ eKL +!!#XJ7"$-@*J_KL"  +"&54674632'>54&#"c:,D%-/N.TJ>X0O13R0=/, Q6/1kFIV'([^CwOaAK0\F^c0?JB0+7'%'%4$$$$5555( ,@)W_O +##"&54632 I4(0=G #@ J GW_O$"+#"&54632'7453(^AG "@gU]M$"+#"&54632!5!YI(S2FK PX@nU^N@U^NY@ +5353(5S5qFH@t+##5#LF:z@t+'3533L:HHe$@!W_O$#+#34632#"&9kt$%%$F'%%$ HRo &@#~W_O$"+#"&546323#$%%$[9k+%%$ HzV V3+( 0+5''5'(f5a :33#"|"tVj)KCR'H7=K '@$W_O  +2#&&##5Vt"F)j'RC=7H_ (@% JW_O""+&&#"#66322g9<93 ]H8f2~ !C= ` (@% JW_O""+32673#"&'2g9<93 ]G9f2 !D< 7Xj&}p7Xj&}p7Xj&}p7Xj&}p7X&}t7X&}s7X&}t7X&}tHj&Hj&Hj&Hj&H&ÿH&¾H&H&O/j&mO/j&mO/j&mO/j&mO/&qO/&pO/&qO/&qH&H&H&H&O/&lO/&lO/&kO/&lbBv#g@JKPX@c_&K'L@c&K_-K'LY@##+"&'5326654&#"#33>32%&/LZHQ!ZG?K%qlL0,[P6_?N\.tz d`aB [o%|JKPX@*~_&K_&K_.L@(~_-K]&K_.LY@ %%+"&553326554&#"#33>32bZYY\RLZHQ!ZG@K%p:w w W`gQ[P6_?Jb\.tzJwE4V\jKPX@J@JYKPX@!~g b@L@(~~g b@LY@  +25665"&546#&&'5!54:" *6j"5<% "R #Q GG4V_jKPX@J@JYKPX@ ~gb@L@'~~gb@LY@$ +5665"&546327566735!O"3H `7(%"5< R" Q#lGG4V\jKPX@ J@ JYKPX@!~g b@L@(~~g b@LY@  +2#&&546#&&'5!5w":4 *7j%<5" "R #Q GG4V^jKPX@J@JYKPX@ ~gb@L@'~~gb@LY@% +&&54632#566735!:4" `6(<5" R" Q#lGG}^ Z@ JKPX@K_L@_LY@     +5>73'"&54632#! j 2:u^ 57 99W^R f@ JKPX@gL@ ~W_OY@      +"&54632.'536673A ,0<88>1- 64 00 46lmR _KPX@g]L@gU]MY@     +2#"&54632#"&546!5^KRGGN]R -G@D  g c_L --+)'%#"     +"&546323"&54632663232673#"&&#"^4/60-3/515=4>X]q D@AJ d_L   +56673663232673#"&&#"A8j 2:1+3-20,2. >" ,-5=4>lmq [ JKPX@]L@U^NY@    +.'535!:2 i9)11 "G sGGlmq [JKPX@]L@U^NY@    +566735!A8j 2:) G" 11sGGWYq*n JK$PX@ _L@!W` PY@ &$* * +.'536673"&546323"&54632. ,0<88>1- 0/ && /0kYD`K$PX@e_L@ eW_OY@  +!52#"&54632#"&5467DGGX]D;@8e c_L  +5!663232673#"&&#")1+3-20,2.GG5=4>k^E*@'e_L  +!52#"&546הEGGwk0@-eW_O  +!52#"&546זGGw.Zb #/@ JK&PX@)  h  _ 8L@/  h W `  PY@&%$ +)$/%/##   +56673"&'332673"&546323"&54632W4FI71*#6 9QA + 5pE8!7Fw.Zb #/@ JK&PX@)  h _  8L@1  h  W  ` PY@&%$ +)$/%/##   +#&&'5#"&'332672#"&54632#"&54614QDFI71*#6  + 5 b7FE8!4Z\ %JK&PX@# f_  8L@+ f  W  _OY@" !%%  +#5667!52#"&54632#"&546*417 5 + GGf4Z\ %JK&PX@# f  _8L@) fW_  OY@" !%%  +&&'535!"&546323"&546324W(A5 + eGG.Ob'KPX@J@JYKPX@& ~g dBK8L@-~ ~g dBK8LY@%$" ''% +&&54632#56673"&'332673:4" `66FI71*#6 9Q<5" R" Q#A7!7A.Ob'KPX@J@JYKPX@& ~g dBK8L@-~ ~g dBK8LY@%$" ''% +&&54632#&&'53"&'332673:4"n7` FI71*#6 9Q<5"#Q "R A7!7A.Ob'KPX@J@JYKPX@& ~g dBK8L@-~ ~g dBK8LY@%$" ''$ +5665"&54632756673"&'332673O"3H `77FI71*#6 9Q%"5< R" Q#A7!7A.Ob'KPX@J@JYKPX@& ~g dBK8L@-~ ~g dBK8LY@%$" ''$ +5665"&546327&&'53"&'332673F"46` FI71*#6 9Q%"5<#Q "R A7!7A#KPX@J@JYKPX@]&K]'L@]&K'K_.LY@##+"&'532667>7!3###B# "_f_  '? K/J'(p6tJ5C]0K(PX@ J@ JIYK(PX@](K]'L@](K'K_.LY@ +"'53267373#'#'#5 6@cdc…r .LC]a":@7  Je]&K'L#!+33273###32654&##abs3_f!lNRHfdY^[2eLX$cBOEDU ")v@  JKPX@](K _'K*L@'(K_/K'K _.K*LY@$"))#$ +33663273#'#"&'#26654&#"UH NAVscd sY>Q0@GJRDAI#0iimo. 4/5^<\n\^ckPJ@GJe e ]&K] 'L' +3.5463!!!!!!#3#"&C*#5thlVZX8 .P@agONO(t ;DBH="")2KPX@ J@ JYKPX@*e   e _(K_ .LKPX@4e   e_(K _(K_ .L@6e   e_/K ](K'K_ .LYY@!+*.,*2+2(&$# "" +"&'##7.546336632!3267!&&#"35#"ug }f:$hVK/Dd5YP3O*)P>C?It=5E rm$>0LR: $7!6632/>=#Y]ul9Z  &?3# "T7Cx P |vxy{J4D]0K/I'(okG ,K"PX@#JI@#JIYK"PX@)g](K_'K_ *L@-g](K'K_.K_ *LY@'%"! ,, +"&'532654&#"###"'53267!6632=&9:"?CTY#X} .L8 6B #:bN `ficΩ^Aay8a&{@ JK1PX@'eg &K'K_*L@$egc &K'LY@&%4%%1 +6632#"&'532654&#"#!#3!37CxM/>=#Y]ul9ZZZnZkG P |vxyM.U J&K-PX@J@JYK-PX@" g(K'K_ *L@'U g(K'K_ *LY@! && +"&'532654&#"#5##33536632r&9:#?CUX"XXXX":bN `ficay8aD *@'a]&K'L +5#!#!3yYZV{UGi *@'a](K'L +%#5#!#!iWPXJ32aD8@5 Jga&K'L##+5#4&#"#366323iY=D;^;ZZ:k8doV:9]XUGh6@3Ja_/K]'L&"+5#4#"#3366323OxZCXXY4acOWe^(#)*]gV] Y JKPX@oU]M@U]MY@ +#'##'##'5([Z'T2222TG@D J~K`L  +"&'57557537726653*kkkkV[j.V#O~F%A$O$A$GAGOG@HK~NEd:a *@'K^L   +!3!!3TZ8{ZP6a@6@3JcKL%# +#"&'5325467####33^V("hKR6b^Is(P&lNW9TZ(KPX@J@JYKPX@g_K_L@#g_KK_LY@$#  (( +2#"&'532654&##57&&#"#466hct?b84nW5\))a,UJVV>F:\TY:xWK1Z@?a8RKD@CA&)gQ2JwE$ ʰ3+=%-@*JH_K_L&%$( +32654&#"56632#"&&5466MCMm9/`Ige^K- /#Lo=Bf`KH{0 [bDpBpX\`F ?uPMLS]rj \",@) JK`L  +"&54667336673'326678:;gAa   ^*FIY'0"5T1 >13M2 7CUe,qHG2FB#E@Bgg]K_ L##  +#532654##532#532654##532:KmLRVT'UX 8Ki49UN$R*14YCOC*E*a*,6UCPA'C)1 %@"c]L  +"&54633#"33Ly}ibv^ }gewPSW2a@>@;JecKL  +"&'53265!#3!3)#48ZZlZdL:G?.Hme  &7 3+1!I@F  J~_K_L !! +"&54667'572&&##"3267I?j?Gp1!.X+Sfha_2k1+h ocCV-CxESCD>BHS#A@>JfK_L%# +#"&'532655!55467#KD% !&OWVK)./>H:u$+#h +K(PX@ "'J@ "'JYK(PX@* fK _ K_ L@' fK ]K_ LY@!!!!+!+&%    +"&'532655!533#5467##5>7B% !&O_K  )2  K)./>HK&WV:u$+t %T" 7:&$$#L($JKPX@! f_K_LKPX@% f_KK_L@) fK_KK_LYY@ ( (&# +7#36632#"&'732654&#"'&&'Qb]XkKKkNryyqpyzrgPQp[oo\|j<@dJKPX@fK_L@fKK_LY@  +"&''!#3326533'&&'n|( Qb]0G7[^ZdP dj8J%c]2w3<@}1@.JfKL +%6673#'!#3'&&' ]cPQb]CPQ=;M6\<@ 7@4JfKL  +1333##3'&&'6677#aqp]gou., "A6?|}<@@@P#b}&D@A" JfKK_L&&$" +#"'532677'!#36673'&&'l#o^;*16<LPb] ]4PQ)afR 7?41=;M_<@;&hyT3+ k4@1 JeKL +53533#66773## TZ^^>i&jIZ&OUUO"D"mU@&ak'@$ JKL +%7'#3667737#'ceIZZ>igc)fjrb|Q@"D"ɎR2TњQ k5@2 JeKL +%7'##53533#667737#'ceIZTTZ^^>igc)fjrb|Q@&OUUO"D"ɎR2TњQaN (@%JK]L  +7!!Y:lY#[N#P -@*eK^L  +3#53533#!aKKZmm8+MRRM%P #E@B e _K _L! #### +53>323##"&&'"!&&267!>LidO::NgkKJpr qpsq-sJFbQPcFgTUf@;e e ]KL%! +75332##3##5#32654&Q5}kRddZ[HfdYuK nd;g@VKuuBOED{$pK PX@#pg ]KL@$~g ]KLY@ $$  +2####"#&&54633#32654&o5}kRZ+'JJT[HfdXnd;g@)  JGg_K_L$%&E +%66544#''7'"#"&&54663232654&#"ig/-##&54&#"'6632!533#U{,E(;8&L%1,bA[kGHUcc5'CB'*3 ;%*PO?i>wKXXKPX@ J@ JYKPX@KL@KKKLY +36673#'#73L9 _Z886R^yN!,M##N-6,4@1& JKK_L$" +#"'532677.'#36673>73 z^8(16A ^Zp sZ  xZ wnR 6@83/LP0^&#i45$W56(}KPX@)e eK ] KL@'e   e eKLY@  +2####53533#2654&##)~`ZYYZmmO]dV[_(ider[A..A3?QEA(=@:  e eeKL! +73##5#53332#'32654&##mmZYYZn~`O]dV[_7A@@AInjcerG=RDB-[$@!  JKL +#57'536673OOOO &e{55$551b S++,(@@="  J#Ie_K_L%$!$$) +#"'532654&##532654&#"'6632OFPTvzO*d,RbfbTUdUL<3T*$/pD_v#GVYFem)QEF@ALB=6: @%'^'>@;Jg_K_L$"''%#$# +354632###"&'532655#%2654&#"VPHV\T:[U- !1/+.&")4JdN@LRg^N8CJ,&)27.4!>@;  Jg_K_L !! +"3267#"&546632#52654&$KKDC2:)`n9kJJqA~LZKGL H tgHm>DiKr QK1PX@]K_L@g_LY@  +#"&&5467#52654&#"lHInmI<=yynssxq1g\nn]]n\/NpXH\1xKPX@ J@ JYKPX@e_L@eK_LY@  +2&&#"3##3366% %ZhYDhQekfJ/f1?9,.@+&J]K_L! ,!,!& +#"&&54667&&5467#5!#"2654&'_;T,sFl=;\27+a+ GM?>,P2T!EX=m}4bEG\5 #6)$ JJ<KSM?L% *J;GO9S ʰ3+/  ʰ3+\; ^@ JKPX@_K_L@K_K_LY@ $%$! +%3267#"&54&#"#336632xM!!5LJOPi`YEmMuw]J[M4^Zyj^26~\1dKPX@ J@ JYKPX@_L@K_LY@  +2&&#"#3366% %ZhYDhQekf1?7R1@.J]K_L%%! +5!#"3267#"&&54677ZI{v,U+(Z7gKiN|NNMWMUhp(a @fKL +!#!3!3Z:ZlZN|.a7&@#JaKL +!###33.533#RSh}T@WQ#h7q@K!YE@B Je_K_L%"$# +53>32&&#"3#3267#"&'?SiqT$!Q0k zq.T)(U; ;LcT*LwLr N&D@A&%$ Je_K_L%'#% +32675#5!# 57>32&&#"%+C5vFTIKhXp=k'@$  JKL +57366773%##^Z>ijIZ>d"D"I=@U@)"-@*"! JKL +73737##77'#3.55^hɱTZZiեS^Ok>J!~"#h@K!;i?@<  Je]KL,% +!###575327'%&&##27_gZ^^vGAGC [JYf  =NN > Da&4){&U /6@3 /."  !J_K_L%-%' +7&&546632&&#"%#"&'532654&&'&''(:fD:b)%X.CD6C@Cu&QD_jV>5#0(0 \K PX@!pe]KL@"~e]KLY@ %! +!#!##"#&&54633!3ZZ'+'JJTnZM)  ;T.&=#3|@JKPX@ g_K_L@$gK_K_LY@%$.,$3%3 ## +"&'532655467##"&&5466323732>554&#"X?y7;|>eghR_|=E}UNe N9N.h_bd)WVct2/BXanP;/`ij(BS*8ux|JuCGv JK-PX@& gK_K ^L@$g gK ^LY@$! +%!#"&54632533#54&#"3Gd4>?B3$ZggZ$MM3?.5862& .k ʰ3+ "7, ʰ3+<W@JGK PX@cK_L@gcLY##$) +7&'#"&5463233267&#"(8B/8EA6*&Z)!" $1G)"!&<45<)'!|$KPX@"!J@"!JYKPX@_K_L@K_K_LY@ $$ +23327#"&&''#&&#"566d"+i` ((0$f`z  0'_@(*B "H;b&*.C ^J#;@8Je]K]KL#""$!+  +32###32654&##32654&#^ӊ~J@JQtZZVLWVwVRV[[Y@Q  OMhf<;;8GC=A= $~&$Pa$&(P($*&,P/Z$&8Pa&/ 7a&1 :{ /@,e]K]L  +3535#535#5!:AHGHU*,6&5#%,KPX@ J@ JYK PX@( pe _K`LKPX@) ~e _K`L@3 ~e _K_K`LYY@'&*)&,',%%$"%$$ +%#"&'#"&546632!326732654'"!&&?;+%I/z:lLirXR1N/!$ ?O@"2EVFo4\a" 6SQG]!"!"8[@X76  "!Jge_K_L42.,+)&$ 88 +273267#"&'#"&'532654##532654&#"'6Lfg *=)@%4' 6!ov:^"]75huiTC3O*)P7^j *< (A"ry/: ? ub@EM^Z<," B7GH&=I"J;IU@KL +3#3XX$&*P+@(JK_L  +"&'532653&   *XHG#1kKUNK)PX@ eKL@ e]LY@ +3##53533533###XQQXXPPXHIggggIHjD@ JKPX@_KL@gLY%$ +754632&&#"7#QaP1*)/j X2-gU E 4?R;8LZv jV@ JKPX@e_KL@geLY@ %" +36632&&#"3### H`P1*)/XHCfT E 4=H7'" #/0@-Jg_K_L$$)*%" +#"&&546326654&#"4546324&#"326'sGo@tHp?LRRJ<1/@7   A}YA{ T7_ll_4R2772vZFM)&f 3+JKPX@ J G@  JYKPX@_K_LKPX@_KK_L@gK_LYY%%%$ +!#'##"&'732654&#"'6632JE N8" DU$."8PQ^17SbPW12D MY=@:Je(K_*L +"&'53265#53533#&   *KKXKKHG#1KGGKU8@5g(K_*L  +"&5463333#'2655#"(@; J~_K_L$$# +%'572&&##"3267#"&546&Kj1/R7Fub\]2k0UťCv BSDPBDOO)teem>@;JK^K_L%# +#"&'532655!535467#HL* ")REUH'.Q>/:u$*^ +KPX@"'J@"'JIYKPX@*K^ K _K_LK)PX@(K^ K ]K_L@& eK^ K_LYY@!!!+!+&%%# +!#"&'532655!533%35467##5>7FM*!"(R["' )2 JI]H'.Q>/HH:u$*] %W$ 9;O~ #@ e]KL +3#!#3#L/@?("'.@+J_K_L%+$" +%#"'532654&'.54632&&#"hV^<"N+3=D7$?&aO)K%#=,75$)A%HOH,(**%;-DM=*%"'9.8#2=H@0/  JKPX@%   e_ K  _L@)   e_ KK  _LY@!FD@>;953-+)'$" 22 +267754&#"'6632#'##"&'#"&547754#"'663265%3265`c>i[:5*L!#`4b^@#NC7T3>,P\bp%M',^OT\5.FVYOdM7+C[#]aE C4BV^L,*-.!(RN}B2;,,QI083-*KN.y#!-8K(PX@J@JYK(PX@"e_ K _L@-e_ K_ K _LY@640.,*&$ !! +2632#"&'##"&547754#"'6632654&#"3265~,ByHp?sEm fFX^bp%M',^KRQLLRRJ\OT\5.FV#ONA{Y>::>RN}B_oo__lle2;,,QI.L#(3KPX@&%J@&%JYKPX@"e_ K _LKPX@&eK_ K _L@*eK_ KK _LYY@1/+)#! (( +232653#'##"&'#"&547754#"'663265`c9=WGXF [=@W"_773#'##5353c gj=WLLWA4 5]AZZU N@  JK)PX@KKL@]KKLY +77'#33>7737#'EC=WW gIh'jzjQEi6[5s4 `Q1Rm5  #g@#" JK)PX@eKKL@!e]KKLY@  +77'##53533#3>7737#'EC=WLLW gIh'jzjQEi6[5]AZZA4 `Q1Rm5U;@ JK)PX@ KL@ LY@  +7#XXX!#! AK)PX@eKL@e]LY@  +3##53533#XFFXEE^@ZZ@t" E@B e _K _L  "" +55366323##"&&'7"!&&267!H jd @?mDkBIJ6LHLLLBrzzrBz;rQUMMUgaTTa5#&2KPX@ J@ JYKPX@" g_K _L@, g_K_K _LY@('.,'2(2" &&  +"&&546326632#"'254&#"2654&#"*Ho>x7U 6):A6&:n;PKLPMNN D}V#%G7!9$OyDjB#$%p]_mic^o7"&RR0"+ JKPX@' e _K _KL@+ eK _K _KLY@&$++$$ +53336632#"&'#3##5"326654&SH NAcyyd>QXRDAX0@GAgI#0. 4;A``h\^ck5^<\n#)6@&.# JK PX@,pg _ K_KLKPX@-~g _ K_KL@1~gK _ K_KLYY@+*31*6+6%$")) +2#"&'#&&#"#&&546323366"32654er6hJ1MX !"LEL  I R)SD /;JH#S~F&/%# =KJ'-J_^m_ /#+8KPX@ - *'J@ - *'JYKPX@_K_KL@!K_K_KLY@ $55$%& +%.554#"56632336632#"&'#5&326654#")AJ= &3AIS;erAc.Xs0IY(SDDbImPFyPWJ'-UG6_<_7d"* JKPX@' e _K _KL@+ eK _K _KLY@&$**$' +535467##"&546323733##5'26754&#"Q@ay{b?P FRRXRFDWHFF@D0"00#I@aa[^fiq__k7 ")6@(JGKPX@%g_K _KL@)gK_K _KLY@+*20*6+6))$* +5'7467##"&5463237376654&#'226754&#"&Q@ay{b?P Fb',(@M'RFDWHFF% "00#IR`%5 %)5F9$3//[^fiq__k )@&Je]KL$!  +32#'#532654&## bk<2btc8@=:dPM:KA2.0+'u6" :@7 Ja_K]L&$' +#5!576654&#"'66323533#S3F.%<<0#R;LXE=gTbc>4W2%+3:'PC>a;iI1@. JK]L +3'#7333>73:5R^]x8 r^NhIF35< 38@5-"JKK_L32)($# +#"'532677.'##33>7336673a:I2$ .5 Z  X]ZL  ][X JX[f) H@A#&,1) )2.1_>2 ,,Zb90!.v JK)PX@)eK_K _KL@)e_K _KLY@-+$'" +#"&'###53533#366324&#"3260yc?PXMMXN@cy[FJSDAXJE .  " N?[[?F- "0ee\\ckk0!.~ JK)PX@* eK_K _KL@* e_K _KLY@-+&$!!$' +##5#53336632#"&'#4&#"326eXMMXN@cyyc?P(FJSDAXJE[>WW>S- "0.  " Kiee\\ckkU @JKL +#3673XXf 801I#&?@<  "!Je_K_L$,$#!! +4##53254&#"'6632#"&'5326xMLJ9TM ,f>7Z6BBJQ|:\)\[U[ G<@7A!)TAD\^LmyP/R&m@ JKPX@"_K_K_L@ g_K_LY@#!&&$#$" +#"&'53265#5354632##72654&#"KV0 !!.)xxUOMP\S:7,-% ")INYG 28G/JbL@CSG)&&18)5#!>@;  Jg_K_L !! +"3267#"&546632#526654& HIFA/8'`n9jJqWr8OgXST K ztQyBHMz7c,@  JIKPX@-  ~K _K ^ LKPX@9  ~K _K ^K ` LK)PX@5  ~K _K^K  _ L@5  ~ _K^K  _ LYYY@!! (& ,!, +"&546323&&5533733##7#'#'26554&#"dxyd>OX8CDCzCED` P1UEBYGGG .! 3PHH"0I]^dkq_`jUc SK)PX@~K^L@^LY@  +%33##7#33+DCzCEDtX;HPUc?"+("JKPX@*~ _ K^ L@.~ K _ K^ LY@!'%!  ++ +23733##7#4#"#4#"#336632366[Z+CDCzCEDcmNCWnQ>XG U0~&\"^g뜜HYZVYd^I*)Z.,Uc"JKPX@'~_ K^L@+~ K_ K^LY@  +23733##7#4#"#3366W`b9CDCzCEDqxYDXG \"^g뜜HWd^I*)UKPX@  J@  JYKPX@]K_KLKPX@g_KL@gK_KLYY@  +3&&'#7#33667A2, 6@B?AKXF S9S¿ ^G^14Uc!L@I J  ~  e ]K^ L!! +3323733##7#'#32654&##UVh$9 h6CDCzCEDL~>E4>RL0>$H,/%1#.@JKPX@-  ~ _ K_ K`LK1PX@1  ~ _ K_ KK`L@/  ~   g_ KK`LYY@!%$+)$.%.  ## +2####3267#"&5#577335462654&#"4>KUUXP*5GOLP 4K31#!G9?P-aD K[7*!v{"G`-$-4$A*8@5%J_K]L * *  +2##532654&''7&&546"6654&SY0%.=vpGO;19=J`Q+/66).UL7P&9D.XfO2<1?>'RBGYE.*,8 '7$(25"!)@&Hg_L!!% +#"&&5467&'72654&#"-/wHo>~yU<>/)bxQJMOMNK*gF{?sNt[d$:&!FGhUPfaXRhU#~KPX@ J@ JYKPX@_K]KL@ K_K]KLY@  +2&&#"3##3366L" DUXF M#ScOE ^170 eJ 3+U@KL +#3XX U#^@  JKPX@_K_KL@K_K_KLY@ %$$# +%54&#"#3366323267#"&;?SGXF \5^cA(GBHC_c H+'_ePGPU#fKPX@ J@ JYKPX@_KL@K_KLY@  +2&&#"#3366L" DUXF M#ScO ^17 5@2J]K_L  +"&5467#5!#"3267-w:6q_jVP%D F yPoHHkjWeNQ(x@ JK)PX@  ggcL@( ggW_OY@%#(($#$! +#"&5463233#327#"&"3354&1>??3 Xgg$, /QE9#+ 3<.47 g6/1 FU.'U<P"U JKPX@a_KL@aK_KLY@ $" +!#4#"#3366323#32&&#"3#3267#"&&'GDkB(M@MH+DA.El@ARg1 I AX] N7qY_"%,6@0/*)%  JKPX@!_K_K_L@%K_K_K_LY@'&42&,',(#'# +766323737#"'532655467##"&'"%&&4532654tg5U FMMu{vKOwEO6p\p 8@I% J[H>QJ ()G?st"Q*QF - Qqg aY6F>7NSWaN@ JK)PX@KKL@]KKLY +7737667737#'#5RW  gj,WRs ,& 1?+&f"Y@  JKPX@_KL@K_KLY@ $ +773366327##5"%54RG \3`bMMWXRGWE I*)]d?4`[4"kKPX@  J H@   JYKPX@_KL@K_KLY%$ +'7336632&&#"7#5WH S8" )H+XW b,@Q-O50?0"+6@3 +*!   J_K_L%-%& +7&&54632&&#"7#"&'532654&'{$'oZ1U%"J'698Gs#&tb8Q [/C<3I7+DJF# '*?8+NPP+$++UJ~:N# RV 3+5"#+@(JHg_L$%$( +32654&#"56632#"&&5466zKf=NQLLGB &avrLp=J7(jvw5`v^QO`FpsEVf,@) JK_L +"&546733673'32667b01@. JfLKML +13#'#7'&&'c`9<78>£0.& A&MK$$3+"&J;$$3+& >& @&LM$$3+$>&P8U&Ov$$3+&& F A3+3+&Q*$$3+<8@5 e  e]LK]ML +%##!#3#3!#3[Ea'r>HHfbShhAIhiAJ"&KP$$3+ <J< )@&e]LK]ML +!#3#3!JHJ&N$$3+D& @J&L4$$3+J$<&P{J< #@ e]LKML +!#3##JGWk%&N9$$3+& @q%b<&#&&L$$3+%$<&PI&Q$$3+b<(@%JcLL  +"'532653,.)WV D34-_Vb#"&J$$3+J< @ JLKML +366773##JX de@X<" 3J#<& J<@LK^ML +3!< I<J& AqJ<&'mC C3+J#<& J<&N 3+< ,@) JLK^ML  +%!5'7373%XWv&II";7 M:cJ<%@"JLKML +#4667####33RLOy<g1*  +2<0J$<$@! JLKML +###33&&5$kOk!<Q <2N#HJ$& A-J$"&Kp$$3+J#$<& CJb$<4@1 JcLKML$# +#"'53267##33&&5$WM-(*Oi#<_V D),Q 0H& @0H"&R$$3+0H&L$$3+0HT!*<@9&% JHG_PK_QL'-*# +#"''7&&5466327&#"4&'326H:v[U:"0#(';xZ+J&/'&%D)<[Wb%=]UXJ#1"1'sIWJ48&qG/LUxc/Hy0H& A33+0H&Q]$$3+0C%@ #"JK"PX@#e _ PK _MLK'PX@.e _ PK ]LK _MLK.PX@8e _ PK ]LK ]MK _QL@3e _ PK]LK]MK _QLYYY@ %%  +2!#3#3!#"&&5466"3267&&7.5,Vu;;uZXVTY% &CGGGJXVIIufhuJ< -@*e]LKML    +2###32654&##kjplGWW>AK>AKAK>AK"UbkCXrfWb=SGJ[L 0-248~ $MJhhsKF <!@]LKML +###5WE& @ E"&R$$3+E&Ln$$3+E$<$2@/JcLK_QL#&%! +3267#"&5467#"&5332653p-52.!(muXIFDGXF-,t-82,$CshiIMMGgp:;EEU&O$$3+E&QK$$3+< !@JLKML  +336673`|  |a<#39v<'@$JLKML +!&&'#36673>73b  b_VV  [U]  ]Uk<9<#QC(Y"H,)n& Ax"&J$$3+& >\& @b< @ JLKML +7#373#b`ad'<#@ JLKML +#53Y`<b&  A"& J$$3+&  >&  @< /@,J]LK]ML  +35!5!!/78I9FI&  A"& K!$$3+& N$$3+3vGL)&f 3+E @W_O$" +4632#"&E/'&//&'/a/,,//,,2,2 @  3+G)@3G3]@a*0 ʰ3+(* @ JL +!!57'5!*TTTT444P)*@'%JKL)) +336673>73#.'#&&'bo sY  yY] ]i oP0^&#i45$W55630L6g#&^0P4~2@/Jec&K'L%#+3!3#!#"&'532^ZlZZK@! <=/6NwIHID{&O@L Je Q ] &K ]'L&% +!33##!3#5!#3>7#!B0Z;fDlZ[VV7$A2 /9 M9ZjI >OQ:6@ 'M@J # J  g R]&K ^'L%$"! +3667&&53667!3#5!667#!5l*DKUW&'PW6U7 z&tg&&Z2 XK\_XG,0X@K PX@ JKPX@ JKPX@ J@ JYYYK PX@Q]&K_'LKPX@a]&K_'LKPX@Q]&K_'LKPX@a]&K_'L@"a]&K'K_.LYYYY@%'+%#5###"&'532667>7!WT  &?3# #M{J4D]0K1I$&o"'%"K"PX@JG@JGYK"PX@a]&K]'L@#pb]&K]'LY@ U"$!#+5663232654&#!5!5!32#"&#"G :6F?;-6?{LX&WS>e GH$ !=?N=$B*;L "'%*K"PX@  J G@  J GYK"PX@' e a]&K ]'L@-p e  b]&K ]'LY@)$$!# +5663232654&#!57#537!5!3#32#"&#"G :6F?;-6?ʫ屈LX&WS>e GH$ !=LN=L$B*;L -G6#@,s'@$ J&K`.L%%+7'533267#"&{OO&.$6LQ55!1/F MZ9@6 J~f&K'L# +53533##54&#"#54675ZquZ_]Z`ZzpLaaLa m`da_p a= -@*J~&K_.L#&+3#"&5467332654&'#5awwwwc1pxyn]\Za[sUd||dUsCPOD?JM=6S@P( ' 5J~_-K_ .L42,*%# 66 +"&&546632&&#"32675332654&#"'6632#"'S]|=>tP&K"4RZfe7Z9 ef[R4"K&Pt>=|\hGG _omYCCYmo_>>p3@0Je_-K]'L$!$#$"+4&#"'63232##732654&##%1&.>TUkZiVWaV_4=GTe|ibeoL@HL4+ <@9e]&K] 'L    ! +3#5!32#!3%32654&##j[ZiTW^V`|Nibeo6L@HL4a=@:e  e&K ^ 'L$! +333533#32###732654&##aZZjɓiTW^V````Jibeo L@HL4=&KPX@e_-K_'LKPX@#e&K_-K_'L@'e&K_-K'K_.LYY@ &&##+4663233###"&&%4&&#"3266=Dh^KZZGdhE.`KJ`--_LK_.fl]N^.6NfW]mVHHVXHHa4@1Jf&K 'L +33!3###!3'&&'aZ&y]\y{\w+53JJpE** 1@.f&K]'L   +!3'&&!'vu.FCFE3TCD$E@B  J ~e]&K'L $# +%##5##7>7'5!#7!"!.#FQ1[F/J:A:I/H[0>)'>0:P,44,N801-2a ?@<J f&K] 'L  +33!3!!3'&&'!'aZ&y]uw+F^\53JpE** ~`@ JK(PX@]&K_'L@ ]&K'K_'LY@ %'+%#&&'####"&'532673]n Y &%8.  #$]@2S(p,b-t9X3J>FDG@Dx5@2Q] &K]'L +!3#5!#3>7#!xZVV7$A2 /9 MM >OQ:6U@ JKPX@]&K_'L@]&K'K_.LY@ %'+!###"&'532667>7!!c\  &?3# #{J4D]0K1I$&oMaF-@* J]&K'L+!##33!!#467#SYri9OIM4f =&24 43+='3I@F  g_-K _.L)(/-(3)3#!''  +"&&546632'2654&#"7"&546323"&54632oHHpkKKkyqpyzrr \om\[oo\N=' ./ .D9@6Q] &K]'L +3#5!#3>753#!B[VV7#?2!U/9 M =MQ:63+7@4J_-K_*L+++"&'532654&&'.546632&&#">c&(c7I].L..T5>i@;`++N)AR*H,1Y8@rV XQ2G:DbJJk9ITM5F6!G]DRs;&5@/ J0 IKPX@# e ] &K]'L@+ e&K _ -K'K_.LY@42-+'%$%$ +333####"&'532654&##532654&#"'6632*%Z;fDlZE:i-.p1`cthfajiP@DX*+*{Msy#0F3ZjN.V^vRHBD>KG<6:"=+dK#1@.!Jg&K'L##$"+!##"&'#"&533267&5332673Zn`2P@~5bkZ?F2V7 Z?C4R4Z)0[[;=#<<Y9g/7KPX@!J@!JYKPX@0 ggc ] &K ]'L@5 ggUc ] &K ]'LY@!7610.-,+'&%$#"  //+%2#"'532654##53254&#"!#3>7!36#!/?: #CC<+1$*R'&N#(W5-L4 \N&θ*4W\ %15 (,%  Rr @9 ;@8Je]&K'K_*L#$!"+%4&####5!#32#"'53265C;?ZSU0#*1=1|NNŶSe L 8; +@( J]&K_.L$#+7#5!#3267#"& 3- .S^NNIG:L e`(%ȵJKPX@$e&K^'K]*LKPX@!ea&K^'LKPX@$e&K^'K]*L@!ea&K^'LYYY@ %$ +"&5463!!"3!2654&#!3!3#10//>11D09XZaZ&(#QF,%%.5 &+".|m@))D( >=@:Jc]&K'L +"&5467#5!##"3267/?G6 60+8  848DQNN-9< 7@4 Jg]&K'L##+5!#32673##"&55  CG5_@ZZBr4dl|NN;=Y6)[[ak7@4Jg_-K'L"%%"+4632&&#"6632#4#"#adW& #/9@m2dmZ4[=ZmiO>F_[[ xa('Ե!JKPX@&e &K^'K] *LKPX@#e a &K^'LKPX@&e &K^'K] *L@#e a &K^'LYYY@  '& +"&5463!!"3!2654&#!3!3!3#q10//Y%1D0:%Z[ZN#QF,%%.5 &+".||l#V)D(==#;@8!Jf-K.L &#+#"&&546632&&'!355!#66KkoHHpkKa^]]_`__`fo\\om\[Bh ~n A@>Je(K'K_*L +"&'53253!53#5!$ CXXXDIZeK\F#O@L Je Q ] (K ]'L#"+3533##5#3#5!#3667#3X`fXNUT+EEw!5#_|ED0F?&M@J ! J  g R](K ^'L$#  +367&553667!3#5!675#35M%~M#'JQuPKw[)V1 5=/6 @M08;k8F8KPX@ J I@ J IYKPX@Q](K_'LK"PX@a](K_'L@"a](K'K_.LYY@%#+%#5###"&'53267!8QS .M9 6ACFϩ^B.5)5V@S"-Jgg gW_O+* 1/*5+5&$) )($&# +%#"&54>32#"&'6632"3254&"32654&*XD|)LkBNO^J+W$ `*[b3MQ d,J"UG5B69+H+üTlv,7" "@gW_O$%%"+#"&&5463232654&#"_O3N+]Q3M,'+*((+*' 6zg6zen``nn]]7A":@7  JgW_O +"&&546632&&#"3267}cPR`=h $X*{spm?_($W :z_c|: I gdag NT%@"U]M+#!5TXs6JU, *@'U]M +3!####UXX22C 9@6eeU]M   $!+32###5#32654&֔nkft;GBDMKKYJ(00# EC@@ e eU]M#! +3#32###535#32654&թgtuu揑;MGIKYI(00#1'-@*JW_O$#&+3#"&5467332654&#".^]Zz~~zY\^LQRKNNOO^nn^MdcNP^a#)"K(PX@JGK-PX@JG@JGYYK(PX@c](K]'LK-PX@#Wc](K]'L@$gc](K]'LYY@ !$"$1#+5663232654&##5!5!32#"&#"3&$(-!+?.G"w|jWUO7P!.F #$6G9gHD=G #)*K(PX@  J GK-PX@  J G@  J GYYK(PX@( e  c](K ]'LK-PX@- e W c](K ]'L@. e g  c](K ]'LYY@)'&$ $1# +5663232654&##57#537!5!3#32#"&#"3&$(-!+?.Ginwxe{|jWUO7P!.F #$6@G9@HD=G -"GV@RHP9@6 J~f(K'L# +53533##54&#"#54675{XZcXFIJFXbXASSAN f`hdHCCHdh_f N" -6@3'J_.L"!!-"-  +"&5467.53>73'2654&'U\<>-: X2.*7[%G516)O:+602&+6 WE7aB)EDM27LD,.YsTVq:,JD%,H+C0)+O,'8,+3:N#4T@Q& % J2I~_/K_ .L0.*(#! 44 +"&54632&&#"32675332654&#"'6632#"&' k{rd"8)=>MD"4X6"DK?=*9"dr|j8QP C je^ll^ej C(""(l#3@0Je_/K]'L!###%"+4&#"'663232##%4&##326!-$4OPknbB>{FۘMYE/11$UF@C  e] 'K](K ^ 'L#! +333533#32###732654&##UXXknޛ;CD>iiiEMY,E/11$7"GD@U#*@JKPX@# e_(K ] 'LKPX@)  pe_(K ] 'L@1  pe(K_/K'K _ .LYY@! &$ *!*  +"&547##3!54#"'6632#'#'2655MZ+XXp%M',^1`c?&P'GUOT\4 RN=&#}B]aP/+FQI12;,, 7@4 Jf(K]'L+133'&&'#!'d  65.4QCD^!L@I  J ~e ](K'L!  +%##5##7667'5!#7"!&&#H)Q=BCuuuuBF--?Hء - T? 9@6 f (K]'L  +373!7###3'&&!']d^X c@3A@D56CѪa@J IKPX@](K_'K*L@](K'K_.K*LY@ $(+!#'&&'###"'532673\N  X !42  `(?T6&gLBH2 F#Gx@F5@2Q] (K]'L +#3#5!#3667353#NUT+EEt!5Fw_|1DpK"PX@ J G@ J IYK"PX@](K_'L@](K'K_.LY@%#+####"&'5326735` .M9 6AF-ϩ^BU-@* J](K'L+##467####33'OJOuF-V-Q-/Q7'" %>@;g_/K_.L!%%  +"&&54632'2654&#"7"&54632-Go@tHp?qQLLRRJKV A}YA{YIo__ll__o5B# %1-@*g_/K_.L$$$$$%%"+#"&&5463232654&#"4632#"&74632#"&BNuBQu?HU]\UW[YX+ D}VD}U^op]_mi[7"' ` `F19@6R] (K^'L +3#5!#366753##3NUT+@EPvv!5#2YsED0+#).@+J_/K_*L%,%"+#"&'532654&&'.54632&&#"og7W#&Z*AE <)0J*rY/T*E);>;.,J,cqLE:+;.8M>^cAB4(3*8P!"4KPX@3 2  JK"PX@3 2  J@3 2   JYYKPX@$  e ] (K]'LK"PX@,  e(K _ /K'K_.L@1 U   e(K _ /K'K_.LYY@0.*('%"  44 +23533##5##"&'532654##532654&#"'6\mX`fXov:^"]7.4KPX@!J@!JYKPX@0 ggc ] (K ]'L@5 ggUc ] (K ]'LY@!430/-,+*'&%$#"  ..+%2#"'532654##53254&#"!#3667!36#!.<8#@A='0")O&&J'R'>C3;%82U%"1 $.3 '*$  Y.|sQ#I@FJg](K'K_*L##%%%# +#6632#"&'5326554&#"##5Ӳ#W8]aEI(%9:5P XF!\_EUH'.A:F+@( J](K_.L%#+7#5!#3267#"&Ʊ). 1VK6FF81GYU(^#ȵJKPX@$e(K^'K]*LKPX@!ea(K^'LKPX@$e(K^'K]*L@!ea(K^'LYYY@ #" +"&54633#"33254&#!3!3#10//%r+;XX!OD,%%.5 V#*/8%)D(>=@:Jc](K'L +"&5467#5!##"3267/?D4/0+8  846EFF--9<1@. Jg](K'L##+#326753#5#"&55#5!ӳ553Z/XX.e=S[u72 VSzFU#3@0Jg_(K'L#)%"+4632&&#"36632#4&#"#UNF0$#!W=ZaX:"A{YA}YLPFI WM6&*@H_.L&$.+#"&&5467.546632654& hO>3cjqHo?dY!8!OA)M1QILSOM  0w^r|5gLZv$1#4B( ,P?HVWMJV+yG/ .j/ .j/ .j/ . j/ .j/ .}j .}j<3+3+3+ 3+ 3+}3+}3+k' ^! aG)&f 3+7`aG)&f 3+a:K1PX@e8K9L@e]8LY+##33ZZN^ JK1PX@~]8K9L@~]8LY@ +!###!#5#kZI@I|.baff@  JIK1PX@8K9K_=L@]8K_=LY@ +"&&'53267#36673:0V@)c=VnSZZ%i0MCQiE,jB-U`BK1PX@e;K9L@e];LY@ +3##XE JK1PX@~]:K9L@~]:LY@ +!###5!#5#5YzLLxU-UJ,=@:Jgg_.L(&%#,, +2#"&5466"32654&##532654&DCh;NJ[]wv>lC(D)OUSN`N5+NHN+T@HT  b[gnngLQe0IF?GLLGOIJJ=><=zKG@DG.J" HW_OCB;:KK+".5467>7>7327#"&&''2>54&&547"326678r_9=;+?& &*B2.\NbH me@ HO)! JA@8HK PX@6n~||W`P@5~||W`PY@VTEC=;'% ee +"&'7326'.#.#"'>3276654.54>732667#"&&'>32)4#8=/7F $!"+*(&2&%8 :E" |E^f(=7; %(%KVZ*@;2! @5 '3F. ")& TRCz,04 IX@U:9#"J!I~~|W_O-+II#&#&+'>3232667#".#""&&''76632326654&&54>73*3<<6)" HN *66$<)-) 1h  %24 $30 "" :6$. 1=9&77-=+| "."3S/-]Q &*#' 3267>54.#"'>54.54667>32>3232673267'6654&&#"06677"&&554&#"  40  !49-&'%  %! ,=&)I4 8L1#& " 5Z2,!% D0%<=="6 .* 7%  !>fNKg@(((+(' %$)/( ' +F(E2%& -& tz:+C<LP 71tE"+,3-P V@5$# J76 H  n  ~  |||||hW_ ONLA?><20.,&%! VV +"&'4&#"'>3232>54&#"'7>54&#"#"&&5732632>324  ,70C)VF"2 /72 &: 00 $!K &/&0!10.0# 0>$8^vq :1  &@KKEK A,:!!  l  *2'A3%/3oT#,@  + JKPX@&eg_K_L@$geg_LY@*($" ,, +"&54632&&#"33#"&'532654&##57hjdP"5#/g+1k2b^jtC]YS^ @ 74;:<dffwSP?=JC+@ J*IK$PX@&eg_K_L@$geg_LY@)'#! ++ +"54632&&#"33'"&'532654&##57^S$9&-C@PTQN#[ nm J  ~  |  |gg g  WW_ OwufdLJA?8731'% +".546332667>7>7&&#"32>53#"&54>7663267#"&&54667032676654&&'#"&54632>54&'3< 1"$  +$>YK**8*4"6#=q[59'$#K@)9O04:':98[(C/'7R' %&,HR' -1 % 7  !1# 1<(![ .2(7&  QX-SY2%-$ $5F*)(.Rm?0%PF+44+I:( !. B(/H4$9)'QE*$ ,<"%095$P8   :V23NcuA(@h>:~@{W >(=\ sJ  ~  gggg W _ O}|wuecQOEC:8/- +".5463232667'0>776670#"&54>54&#"'0>3232>7667>32032>73#"&5467667>54&#"9A1"$  1+8J=%?P1C) O?2:4 AL%0:< 1CKH -" %. %\db+"%4nW/- +0,!#/43!:+$mvcD (,$ (1-;B(|E"3k]9 @MICG$P>OO@LD-J~ggW_O43&%$# >>+".54632#"32667.54667663>54'77>75? 1"$#22*E=-:;oA73A00 !@) !A5`|CPa=:=pX3& !21(7'   '5]; 5C8ld)32&#">32326670#"&'67>54&#"267&&#"";%=%&T)  BR+4[u@---):cI( 27=DLT-+&/UqCG(!<!HA%6D(&E +WBB<?,%<20x,9$J(4$3 %# H+  *;>5`K+  .Nb5,8 <}qZ4*$gl\3CY&D-!11!B5We0Dq"a 5@2eU]M  +3!##'3#3#aGxCCCC6k5``^~@{z ~{xM = J  ~  gg  g g  WW_ Ovtkiba][QOCA;:42,* +"&&54>54&'"#"&54632>54'#".546332667667>7&&#"32>53#"&54>766326732>7p $!   .0"01<(![U9A1"$  -$>PB) W>4"6#=q[;@-$"L@) 9O04:-A?8[(C98om !# $3> #EF=  .C-^ NcuA(@h>&5. (7&  C}XCL%-$ ,AN*)(.Rm?0%PF+44+QE1 !. O(@h+"JD() 5=,) ʰ3+~n2F >@;  JgW_O +"&&546632&#"3267'~pIOo0]0PU+K>W)X*/VS) Zpl] 8" ; zSO|a ?@< eeU] M  +3333##'3#3#axCCCC:6[5``a &@# Jt +333#%5aL5M:"65bca\ ?@<JeeU]M %!+3!2##'3#3#6654'axBl=cxCCx$/Sm[>_65`1@54&#"&&546632254&'&"=Y0EqC ;d@0<" 0%"$#-'"1!./K+5#9#A3 3(M?&$<$%?(%# (EX{ % 4\;=hE 4*,S5.29))< 073,36aC   6NZ/556H(%'6Z4$(B(#--O:!  P8@10d vV |YL JhI  ~  ~g g  g g  W _  O:9rpRPJICA9:(& 88+".54>3232670#".#"32>54&'7".5463326677'0>7>77667>320>7"!':\S3@.."%#  /&DCF(6mY6 2,#3Jk.;!1"$  -',F= 8R,E07QB %&  G N `5%$ !RSF+ %!.Oe6&% #7A< ')+#(7&  6^;A1 ":e_/ $Ze5$ 8"'!** UL{@@=HG*Jr[6HW`PfdMKDB{{+"&5463232>7>7>766766732667#"&54>7>7#"&&54667>7>H3(!). )%7_VW/?|i# #9?>,="8f062 "$8> @@"(&,' /40#TyG=Y#3GF00$!SYP(QWg J:*;#( "=e~@Vj 5HNG2 8NUPUfd'8( DdV5-=&4^lFC2Zki'6whA  )2JK PX@( p g]K]L@) ~ g]K]LY@***2*1-+)'#! !,!% +7&&5463332###"732654&##32654&#EH̆FB-H+s\DS[v^KMc @>0Pa?S &F7ajO;:;3J<8E4A@>Jec]L  +"&'5325#53!!3#/" 2?BJHB1)?@<Jg_K_L%#" ))  +"&54675&&54632'2654&'"33#"B[YOKpIIr|vuyQSWg)*1S naGZQERf[nn]N=5=AJ0;J#i@  JKPX@gK_L@gKK_LY@ ## +"&5475.55333#"32653#'#:p6P+[iu*ZJisYIl f\ *ODXKJ=Av}6a*A #+K@H% JegWe_O   +"&54>323#773#2>54&&#"@Q"Bc@4D B'RCB/Qbo2 ;&KUyL.; ""(/5@2*(JgW_O  +"&54>3232676677&"#"6654'nq*SxNH`'.-^-,YI4% +{$)/L9A m]@}g><>Bd ?4 -'lo)H'< V M@Jg geU] M     +"&54632'2654#"3'3#"-2/L; qrnC\CP&%*[[>S$(++CF ?Uaa(4?@< J~gW_O1/%$!((+".5467'>76632326673'>54#"&1 ,!!7C%! :fO NR#Q\-;L+2-% ),$5 (&AN! ::2(+L/*U9<@4 2KO#;S@P 'J~|gW_O%$64.,$;%;##+".5467'7>32>73'267&&546326654&#"%0 ,PLR'+.4!/0*@*<)"  +M7+ ),$5 _$B),TZ%.&'1' 1o+%^jS.'{Q3FUg@d!$ LJ~~g g W _OHG54GUHU><4F5F)(#"33 +"&&54>77#"&&5467'667663273>73026676654#"26677 !*5S_W"$3$&  A #;#^:)'.v'B. 3T6N2GXx!B: "01-#"51O/BF=&{",!DA9+ G )87@ &D"(;!(@:5 ?I1GH17T,"J (+EPN; -N2(3<@ 7:"&J@G  %Jc_K_L#! && +"&&546632&&#"3267#"&'53255,Hn?ArH(M@ML+D9< 0 :z_c|: I ag ADI4@U:E$@JK)PX@&~cK_KL@&~c_K]LY@!  $$ +"&'53255#4#"#3366323 04xZCXXY4ac,9I4FWe^(#)*]gAD6r $1@$1JKPX@"cK_K_LK)PX@&cK_KK_L@&c_K]K_LYY@ %($&%" +3632#"&'##327#"&546732654&&#"4X-1j9sl,2?(KM'0Hd]j^*'()B2,+2-!"g<%i@ "JKPX@gK_L@gKK_LY@!  %% +"&&54675&&55333#"32653#'#3R11718S?K C8>4XFTAV #E13F BAlfA7B2,+2d]M'0a"*18@  '!JKPX@'  f _K _ L@+  fK _K _ LY@)32,+652838/.+1,1%# ** +"&'#536632366733#3267#"&'#"!&&267!_x46 vc;O E PP %/ M5AE%DMPFE >sz*((HP$>@$."0SPPSgY_\\.O#)4;g@d  ' J  e  e_K _ L65+*985;6;0.*4+4%#  )) +"&547754#"'66326632&&#"!#"&''2655265!M_an%L'+^1|(bD5R*.N2QYrqhBj#c;DRNR[5D@P RN%w@],1Hc[4o><==FQI12;,,^FQS:&@#JGt* +'7.55467733267'7654''$E- ` #,' dF %E< r $ yJm 3#!'H@EJ  ee _K_L#"%$"'#'%"% +734546632!!!3267#"&'#"!&&2:lLdrcV@1N/*S4g9$k  > VFzo7@@Hg`"m.?".5K"PX@()J@()JYK"PX@( gg _K_L@-W eg _K_LY@0/32/505$$%%"$" +746326632!3267#"&&'&&#"327#"&"!&&SD0 ~_Ec5YP3O*)P7KrC*&/##$ DC?I>=GlyK.2,4TA+  :799"$%!&,*{MxDSIAcXZb-O4Uay#2}@ 2JK)PX@+~~ghKL@+~~gh]LY@ $(#$)# +3#&&#"#"&54632332654&'&5432#"&'X  !  3H.  X   2?3  # D9<9 " B88?z7@)* 6 JK)PX@2 g  g  gK_K L@2 g  g  g_K ] LY@775310.,(&#"$"# +3&&#"#66325&&#"#66325332673#"'32673#"'  3<-  3<-X  3;,  3;,$=D p %>D $AA o #@B j7@  JK)PX@ KL@ ]LY +3#5&&546753'54&'66X6DE5X6CD5%$$$ U==:@;@29390.! )) +"&55!&&#"5663266327#"''7&'&#"2654'267!etdSM5M()M5Dh fBO:!4'rD3'4+ ^$;NHOF 8#>59[29ffeJ/NJESC$!(/6j@g   J  e  e _K _ L10*)#"430616-,)/*/&%"(#( !! +"&55!&&#"566326632#"&'"!&&267!267!etdSM5M()M5Dh fBFm?r?d^>GI+HFIFI32 -+*(JH)G_K _L988@9@64'% .. +"&&54632&#"3267&546327#"''7&'&#"2654&'-Lo=u,%IPNR$=!x5,7-0w8.7@N$MNPK G}RG jc^nBZ'($uM'*?/MV ip]2NP"@ J]KL# +7#546753#54&#"XbXXZcXFIJFvvz_f f`zvHDDW]'D@AJe]KK_L #!' '!%%" +%3267#"&554&###32#32654&B'GC?HQXbi7..3cd9>>JPGPDLKNM7CPE2.0+U3 &@#J H~KL +#336673>HXF >1bI^,3UB#wKPX@ J@ JYKPX@_KL@K_KLY@  +2&#"&#"#33663266" -6!:!:#XF I,&A#S ,O4^53/!# *KPX@ J@ JYKPX@g_K`L@#gK_K`LY@(&"!   +2&&#"&&'#"&54673366"3265" CV2:E1@HPF M8"#ScOM 6 OT753=C^17x!%/#,6KPX@*)" J@*)" JYKPX@!g_ K`L@%gK_ K`LY@42.-'%!  ,, +2&#"&#"&&'#"&546733663266"3265F" -8!:"9#2:E1@HPF I,&@t8"#S ,O4M 6 OT753=C^53/!x!%/c#?@ JHK PX@_KL@gLY#! +3267##"&'VR!V/X"+T)#   _#&m@ J HK PX@g_K_L@gg_LY@%#  +"&5#"&'5326763274&#"326Sc"+T)VR!V/)91J))L)5()45('6 hX F +,I-0L,)79))87fM@JKPX@_K_L@g_LY%#%" +7#"&'5325432&&#"CI&B& BBTGPڔGPPZJKPX@K_K_L@K]K_LY@ #$ +#'##"&5332655F [=YaX9=WGGH*(`d_HCb`Eds JKPX@(  eK_K _L@&  eK]K _LY@$ +353533##'##"&55#3267%LXXPPF [=YaL9=NI ((BH*(`d$HCONPF# .+%JKPX@$~] K`  L@,~K_ KK` LY@ *($#"! . .  +2#"&546"&5332655332653#'##"'#ZZX47ICXkKAXFS0|'X#]g_FBXYq_cH(*X,,TJ#  3+Q#Z@  JKPX@_K_L@K_KK_LY@ $%$# +32653#'##"&554&#"56329=WGXF[GHCb`H*(`d*&B J! x !-K"PX@ J@ JYK"PX@ h_K_L@$hK_K_LY@,*&$ !! +"&''#'&&#"566323663274&#"3267 @_-K_.L$$%#+#"&54663232654&#"0iUys/hUxv~CQPEEPQCftXítW# !@ J&K'L +3467'73L.I+4>;6&0@- J_-K]'L'%(+357>54&#"'6632!&5K&F85N)/*mDdt.R7iI6TP1;=$ ;#1fX8b`5P-*@@=$J%Ig_-K_.L%$!$%*+#"&'532654&##532654&#"'6632QCUU:y_8`,-h0`Ui_EFX[F<:R(,&qHpm#HU XG?`6RKBC;KJ=49"<,d( 7@4Jf&K'L   +!5!533#'5467#kP[hhU ˢK#O4L2?D@A  Jg]&K_.L +2#"&'532654&#"'!!66m~8`!$g/OaV]H,f:ndoSKOFK QP7 ,>@; Jg_-K_.L &$, ,$'%$+4>32&&#"36632#"&&2654&#"7Ge3.E\5R@]r{hDnA?NEE/F'"D1MyHK.Ph;#1qhpDQUDP'=+U7%@"J]&K'L+3!5!d%zPDz:(55@20J_-K_.L*))5*5#! +"&54667&&5466326654&#"2654&'')s|)C(4I8`=^x%>%,H+j4GF:7G#62552%2#E74EH74E2,>@; Jg_-K_.L &$, ,%'$$+#"&'532>7##"&546632'"326654&&Ge6'1F[6RA\q8gEDn@>OCF0F'"DMyHK .Oi:"1qgKl:ERTDO&< +T86# -@*_/K_.L  +"&&54632'254#"$Pj4{vOj5ywJ G~S~F}RJdj6# 1 JK-PX@ (K'L@ ]'LY+!#467'736Y c+J>'K I;,#*@' J_/K]'L'%(+!!57>54&#"'6632!64?;>)U(.3oA[h#C1?G$2.!,4 "=,%UI-D<"eS#(<@9$#  Jgc_/L$%!$%(+#"&'532654&##5326654&#"'632ƋSNv?Y'$^4SYg[=>0O0H53I+(U\uw  RJdoOJD>:J9048  Jgc](L +2#"&'532654&#"'!!6k}8^#/]/Q_\RD!(j?ghqrPHLGG UN 8'>@; Jg_-K_.L!''$$%"+462&&#"36632#"&&2654&#&8-. Y4dnxlRl6CKGB(H-%D/I./ugmPURFO&=#-T6]%@"J](L+!5!snM8}1 (46@32#J_-K_.L-+(( +2#"&54667&&5466"6654&32654&'^x%>%,H+jt{)D'5H8`<8F#<$4GFJMIMPVBEXS+@14F1Zie[0I4UB6L(G52%2#>625(4EE73E!H/T#(;@8 Jgc_/L" ((%%%"+%#"&'53267##"&546632'"326654&&03tVAam7fGqDJGC'G-"DI2.qgHk` `3+C,` `3+E-` `3+I?` `3+J=~ ~3+%{~ ~3+3t~ ~3+Au~ ~3+ U*~ ~3+@+~ ~3+L>~ ~3+C,~ ~3+E-~ ~3+I?~ ~3+%i& )G& 'g %& '( & 'c =& 'l  $& '^ %& ') $& 'g %& '( %& '( %& '('  vJ2= ް3+%~*{ ް3+~33t ް3+vA3u ް3+ ~U-* ް3+u@*+ ް3+vL2> ް3+~C*, ް3+vE2- ް3+vI4? ް3+sM' ' = Q==3+=3+6&' %@"a]L  +%"&5463!'3# glqkcc&tvCmX&I $@!a]L ! +7!2#'3#Xkqlgcc&vtCmOb+ &@#ea]L +#3#3#3+܍hEDb ,@)ea]L  +53#53#53HJFHH{Q!@JL +3#'(<_fFJ @GW_O$" +'632&&#"A*aa)&JIFPNNJ&  ><" 4@ JK)PX@ KL@ ]LY +#5'3YY  O} 4@ JK)PX@ KL@ ]LY +5'37'#XX5O ' #/;KPX@. g e     gKLK$PX@. g e     g]L@3U g e     g]MYY@.10%$ 750;1;+)$/%/##    +!5!3!!"&54632!"&54632"&54632!"&54632hALB  &   TLSL" !!  !! 4!!!!LIOK)PX@gK_KL@ggKLY@ $$$# +3#4632#"&4632#"& >@=Z65/ R@  JKPX@c_L@gW_OY$+$" +4632#"&5%%54632#"&|;;&51=P@MJ~~ gc_L32972=3=&$  11 +.5463232654&'&&54632#"&'&&#"7"&54632)8 &# G;&4   ' 9"  "(.85?   07 ;%49 0+%&'&&77Ew33!"#|)N)S!33wE")N)|# ( 5@KOSWf@ JH[ZGeeg    e  geee]LTTPPLLecTWTWVUPSPSRQLOLONMJIEC?=984321-+&%$"* +47#"&535!5!4632!!#"&547!5!&73654&#"32654'#5!5!535654&&54632n6-W$$E$$7 *  + ¶,n5O  '#++++++K$$ + %% +  m  ++++++N  #H&"4'2@/g_K _ L'%""""""""! +432#"432#"%432#"%432#"432#"\9::99::9Z9::99::99::9<<;<<;;<<;;<<;<<<5'2@/g_K_ L'%""""""""! +432#"%432#"432#"432#"%432#"59::9`9::99::99::9W9::9<<;;<<;<<<<<<<<<<5@2 Je]L +%#''5'7#53'75373~-~?~,~~,~?,~~,~-~?,,&@#GU]M +!'7#5}`4/45Y #/I@F  g_K_ L%$ +)$/%/##    +"&54632"&54632!"&54632"&54632C       q   !!  !!  !! 4-@*g_K_L"""""""! +432#"432#"%432#"432#"\9::99::9Z9::99::9<<;<<;;<<;<<<H:3+(3d GK PX@g_L@gW_OY@  +52#52654d1<<1'6(8227'" B  ɰ3+JG @ 3+{oHKPX@_LKPX@W_OKPX@_L@W_OYYY@  +77"&54632{-:|.k34>&0+7%SRT$>R0+7%SRT$t@Ht +#75:|.k51>@;J~|c_L&$  11 +.5463232654&'&&54632#"&'&&#")8 &# G;&4   ' 9"  "(.85?   07 ;%uBK)PX@eKL@eLY@  +33#7$1$)9  3+8yt5#@ _K_L"""""! +432#"432#"%432#"X9::99::9W9::9<<;<<<<<<<H]K PX@=nYXL?>1 0$ ¼ J# IkHKPX@1 0$ ¼ Jk# IKPX@=nYXL?>1 0$ ¼ J# IkHKPX@1 0$ ¼ Jk# IK"PX@1 0$ ¼ Jk# IK-PX@1 0$ ¼ Jk# I@?n YX L ?>1 0 $¼ Jk# IYYYYYYK PX@W  ~  ~  ||||| gKLKPX@[  ~  ~  ||||| gKLKPX@W  ~  ~  ||||| gKLKPX@_  ~  ~  ||||| g KKLKPX@e    ~  |  ~  ||||| g KKLKPX@k    ~  |  |  ~  |||||g KKLK"PX@k     ~  |  |  ~  |||||gKLK-PX@w    ~  |  |  |~  |  |||||gKL@}    ~  |  |  |~  |  ||||||gKLYYYYYYYY@"~|zyrp(+++-. +7"&'667.546327.546327.76327.546327&&546327&&546327&&54632>32663226632266322663226632>326632"' #                &  %/#) %' +F -, !11'% %+!60 )$, ">:  5/!)%    )%  / &*  " 8 1 +  #   %    #   $      l@U]M +!5`44 ='"" ^&")3+H3+#'@$]K_L$# +##5!#4632#"&5G55#@]L +##5!#555<O)@&a]L +!!<{O{F2 #/;_@\ J  g_K_ L10%$750;1;+)$/%/##  +"&54632'7"&54632!"&54632"&54632.2.45.2.[^   .65/4..6   @GU]M +%!5!#}44^LGx@#G"@,&G@x|GK&PX@]K_L@g]LY$# +#!#4632#"&5K5x|@]L +#!#55xHKPX@_LKPX@W_OKPX@_L@W_OYYY@  +'7'"&54632Dڷk.|ƅ>&0+'b$S%R%S$>R0+'b$S%R%S$t@Ht +'7@k.|518@5~|c_L,*'% 11 +&&54676654&#"#"&5466323276632A/8 (   3';G "& 9%; 70   ?58.("  "BK)PX@eKL@eLY@  +#533J71f*@'c_L  +%"&&546632'26654&&#"3T22T33S22S3#8""8##9""92T34S22S43T2<"9##8""8##9"5'@$_K_L"""""""! +432#"%432#"432#"%432#"59::9`9::99::9W9::9<<;;<<;<<<<<<<Ou2@/gW_O  +2673#".#"#66327@ 4 oK9zyt37@ 4 nK;ywr3%MN$4%MO$5)@&g_K_L"""""! +432#"432#"432#"]9::99::9(9::9<<;<<;<<<2 E #?@<JIGgWg_O$$$$$" +4632#"&&&#"56323267#"&"$/>0H:.$/>1G:  "N5  "M6 2} #?@<JIHggW_O$$$$$" +&&#"56323267#"&4632#"& $/>0H:.$/>1G:O? "N5  "M6 u2 &aO_ _3+$@]KL +!#!5!CeCB -L@ + JKPX@c_L@gW_OY%+$) +467&&546632&&#"#"&&732654&&'B2')$2U5YO%F%7:D4.O0[JE`1KCM+*E< '*@6"/;":&%"&'8+CM+G4,=/$" .H #OKPX@g_K_L@gg_LY@ $$$$$" +4632#"&4632#"&4632#"&H$&&$$&&$$&&$w%%$ %%$ %%$ Lt 3++  3+J  3+)8'  3+Hy' 5 @_K_L$$$" +4632#"&4632#"&5       !! 5#@ _K_L"""""! +432#"%432#"432#"59::9W9::9:99:<<;;<<;<<<J @ H_L$" +3267#"'@&JH&*_bPNNPDH0+7D%W  0+''7'77V34$44#53$23c43$34"53$33C2h #/;Gn@k g gg g  c _L=<10%$ CA54.54>54.54667,,6];AQ,,,,,,,,,,6];AQ,,,,,,,,-#2? ;,#,$&,+%%+-"2?;,#,$&,,%%*:&<FL@&54& JIED? + *JKPX@)  h gK _LK)PX@'  g  h gL@/  g  h W _OYY@>=('C@=F>F/-'<(<! +33253&&'667###&&546672675'"&54675"2327&B=21="?1;: =23=yCxM8It,5p2pzYG9_97v/+m/>@EL F    OpB  & \g[j ;aCEi<Pz:T1tG@C AdD@6 JHeU]M+D'#7'373#73'7#'#3DEEDCFFC-c22c--d22duxyuuyx,NVWNNWVz*/0+7'&&'6'6&')N)"Ew33!"#*#"!33wE")N)Hy'7O P P3+7O P P3+Pb4$@!U^N +33P@:b@U^N +3#53@H:P4@]L +#3#@:@]L +#53~:H(b'  b&  zKPX@a]LKPX@eU]MKPX@a]L@eU]MYYY1613 +3!!".54>3!!")EX/4hT32Ug5/XE)b,/0B<54.#!5!2#/XE))EX/4hT32Ug50/,,/0B<,@)Jgc_LL +72655467"&554&',2I^+1',*)1+^I5)#&z>=A$s.21/v#@:Bv'$ >,@)Jgc_LL +%#566554675&&554&'523 )4K]+1*))*1+[M1,$'vB:@#v/1.2s#A>>z&#<B@a]LL +3#3#=B@a]LL +#53#53kk>==5<@]LK_QL$# +3#4632#"&9aC9eCL^`JI ZD'2@/  J~_PK_QL$#$) +7467>54&#"'6632#4632#"&t+#! 4):E)L.PZ21!D,=$!&'#=JC5D!*+ }md (5@2J~gW`P%+$" +#"&546323267#"&546766553 +#" 6(=#(M.OZ21 D-<$"%&=IB5D!** [ &@#W_O ""+76632#&&#"zGRz5YB4`MCGI)/)/w ]!LKPX@J@ JYKPX@'_ K]K` L@$]K ]  K`LY@%" +#57733733#3267#"&5#aLP 4 5O*5GO*{{{{FaD K[7-;3@0Jg]KL +35466753#54&&'#;; J    g_K _LTSfd`]S[T[QOIIDCB?=<.,'%$%& +5&&546323663236632654&&'.546632&&#"#5#"'#5&&''54&#"7"5432754&#"q*1 ""/B1.0M*O63T1=hC4a1.R)>N(G08W2QO6"&6 >e/  %!> 6+")Oy;&,31<6!K*4(2L;#$%$(%"$% +7'7&55#"&54632!2#"&'''3267#"&'4&#"333265&&#!77%Y NaI8=MO^*10"2UJV[L?%9Fdz&"%+.}"ANjFP^/K9InAL7EWh5U1>O#PRJK? NSO17#)1$0@p& ZOK7H/)>@; Je]K]L&1A +73&&54675363253&'#5&#"#533!7Mfqm<<)')'<53 '^j6@4ICO&I 6AC'1N$@..Q!4AYP^c+I4=V.jf)7|x1554&''5#'7D% T1 ;G(%H!0+5'! D)G@j+Ӵ">#/1 P P3+#/5K&PX@ ]L@U]MY@ +5!# 44 - (6DNXbKPX@8   h g_K _ L@@   h gK_KK _ LY@KZYPOFE87*) `^YbZbVTOXPXLJENFN?=7D8D1/)6*6#!((     +2#"&546#"32542#"&&546!2#"&&546!2#"&&546"3254!"3254!"32546D MMHNJtK~&##&L15D LM5CK5E MM5BK5E MM5BK&##&L&##&L&##&L:dAmtvkkt 68NOOP:e@mt;eAls:e@mt;eAls:e@mt;eAlsCMPOOMPOOMPOO#/ P P3+#>/P ,@)Ue]M  +5#53533#ll4ll>o4oo4oG/ @l H@E JIeeU]M +55!!!7#!5!=6-KU?b77q1>E* ˰3+/  ʰ3+6<6 ʰ3+)4@ 3+ (A@2 >3 ? J~~  ~ gg g g  W _ O*)<:750.)A*A&$  +2#'#"&547754&#"'66#3265"&54632&&#"3267AB/ 9%/88*3@LK0<*3-JEZ]F4+Y+*/,6;*12c! 1 6ʪ/(SX\R 7 s7: <  w(N@A B/ . J~~  ~ gg g g  W _ OFD?=31,*&$  +2#'#"&547754&#"'66#3265#"&'532654&'&&54632&&#"AB/ 9%/88*3@LK0<*3-#7=%&&,27I994',06;*12c! 1 6ʪ/(Tf 8 +,/+ 2  &(/2K PX@   JK PX@   J@   JYYK PX@:    ~  ~g g  W _ OK PX@6   ~  ~ g g  W _ O@:    ~  ~g g  W _ OYY@$//,*('$" +#"&54632&&#"3267#'##"&55332655zLKEZ]F4,Y+*/-=&<@AH@1(6SX\R 7 s7: < +1:@G957&r&r & E@B JggU]M+!!5&&546753&&'67!&IORFBB..1524,8,..,l[Zjlh3 4wR@R; Pab&@!    JK PX@-   e e U ] MK PX@1   e e U ] M@-   e e U ] MYY@ &% +!'##3!##33#!#3#%&&'3166`Xg}adrsaYK73 rX MMB C7S&r)cNL)B@?J~_K_L%#))& +7'6632'67>54&'"&54632&(0Z4^m5(! (% C5! 9[S-A7#3' /-49_  %2g@d J ~   e g  W _ O'&-+&2'2! %%+"&'#####53533533#36632'2654&#"@8T>WLLWXP9irr{GKJGSAB -&IXX?aaaa? B,.|Iebdaf_ \a!U@R   JIe egW_O!!$$% +'"'532654&##57!537!5!~?xSqN._-[YfpBi<g`Bd9"PSDACAKL<,v`r@5;6 ~}vWI Z J  ~  ~   ~  ~  ggg  h  gW_Ous|wsuqpjhQPB@:7/-*)'% ``+"&&5467&&546677.#"327#"&5466323266326322&&54632#>54'6654.#"227'"&#"767&&'72654&'"#"&',20H*A! "$    #(     " 2C+G2  $ !"e= 2 -$(% +b    yt$![040 )& $:!Y5)) )  )  /GU'BP(4$**%2 7X!:$)A:%)> 2_ " 4+ V92U+4a_J@G J~eU]M! +33273#'#7'#3254&##aLE98Ufh[AFTsNf_XYffEc]ZkksJ:cG@D~ ggU] M +!##3532!6654&# hBBAofbs$LL^mcu<YGTKJao_i &_@\$# JH~eU]M   +27'7#####3023&4'66&/'y*A$if7h`Zfk=0752=44Do*9L. SNI A;8c%:_@\51)J~ ~gW_ O&&&:&:43-,+*('%% +"&'532654&'.54632&&#"733#5467###57'(((1"G;3-E)(47M^^a[@e5`c 5 )#00 1 2+,34`/ (N\jxR@OoaSJ>8,&   J    g W _Owuig[YNLEC'''(!$$$ +#"&'#"&'#"&'##53267.546323267.54323267.543233>54&#">54&#">54&#"7U !V67U !W66V !S4"F#?>~% I$"G"}}$G$"E ! ~}% D#  %% M  !%%Z  $$ 3 "X['sm'XV%  !X\''ZW"  $WZ''ZW" $QOOQ$TRRT$QOOQ$TRRT$QOOQ$TRRjT@Q ee U ]  M +#5!#33#3#3333ve fӔB?j*66`6Z6d6`6oXi)@&JHt+'76673#'#3_ _Z=7T^:3L#@#N-6R<u,O]C@@TH9JH~ggW_O\Z,))$'-+6676632#"&5463232>54.#"#"&&5467&&54>4&&'326*2 (Y)R'?S)$FhES\*&*@(3B(.'.j40Z%50C61O.$!)=@"$7&6'OaA46\#FBB?P @ J]'L+3773#5>BBzP @ J]'L+3753#>BB0P@"1%@"JH]'L+3'753,CĻ1P&#@ J]'L+!'773-BU/P"$@!JH]'L+!'73,nBƸ2P( LJK&PX@](K]'L@e]'LY@ +!#'73530Bɳ/P& %@"JH]'L +!5'73(kBsK4P&"@J]'L+!73.:B^.2P1%@"JH]'L+3573hBiSP '@$JH]'L +!753>B},WP -@*JHe]'L +!5#733;B7P '@$JH]'L +!773ԠBBPz  '@$JH]'L +!5'3>BrwP @ JH]'L+3'3#'X>ԠBBPN.@+J]'K]'L+3533#NBBBnP-N /@,J]'K]'L +35353#NBBBfPvN /@,J]'K]'L +35373#5NBBBPN /@,J]'K]'L +35353#NBBBqP1@J]'L+3'53#NנBBhPSi@J]'L+3'3#N<ܠBB xP CJKPX@](K]'L@e]'LY+3'353##N;զBBwP @ J]'L+3'3#5P>BBPa- @ J]'L+3'3#'N<٣BB$P1"1@J]'L+3'73#5N,CC1P"@J]'L+3'73#N,éBBn2P&#@ J]'L+!''73-ȠB/UP& @J]'L+3'753#N(ȠBBk4KsP( *@'Je]'L +!5#'7330B/P&"@J]'L+!'3.hB2.^P1@J]'L+3'3#N8CC#i$P #@J]'L+3'3#]:kBB!P&@J]'L+3'3#N4ҢBB&i/P  @J]'L+3'753#P:˭BBy"]Py @ J]'L+3'3#5'N4ҢBB%iP' #@ Je]'L+3'33##_8BB#e(PF%1!@J]'L+3'73.CS3P'$@!Je'L+!#'73/n0P.&@#JH]'L+!'73'ɗB7q2*P% '@$JH]'L +!''753-ɠB2P% '@$JH]'L +!5'73)ɠBrx3sP% '@$JH]'L +!'73*ɠB93P1!@J]'L+373>CvGP@J]'L+373#=BBuCP%@"J^'L+3733>mBvnP @ J]'L+3753#=BBu9?P @ J]'L+3773#5>BBvzP @ J]'L+3753#>BBv!0P@NKJKPX@](K]'L@e]'LY@ +!#5373}æBTnBPNKJKPX@](K]'L@e]'LY@ +!#533vBBPN LJKPX@](K]'L@e]'LY@ +!5#533BOBPN LJKPX@](K]'L@e]'LY@ +!'#533B-B#P1"@J]'L+3573:CP#@ J]'L+!739B|#P!@J]'L+!'73:mBxcSP $@!J]'L +!7539B"@XP *@'Je]'L +!5#733:BP $@!J]'L +!773:B2P%1"@J]'L+3'73.C+1&P'#@ J]'L+!'73/B/0$P%!@J]'L+!'73-jB21 P% $@!J]'L +!'7753/B͠/P%"@J]'L+!573.;Bi1P" *@'Je]'L +!#'733Ȥ0BF-(P"1!@J]'L+3'3UI:L!!>(:EA=1F3L4]#o?"%e/##%0/,.4 [ 3+swjq4N^C^pv^RZY^J1W^K/e^M=^p1Ol^Ql^LD0J dD@U]M +D!!_JGT('dD@U]M +D#5(PTx4dD@)U]M +D#53#5(PP^a =dD@2 JU]M    +D#.'5##.'5126. O126. "V 99 "V 99 d^L EdD@:~gW_O      +D2#"&546#"&'33267QHJK63.'9Lh)'d^ 2dD@'W_O    +D2#&&#"#66JK62.'97QH>)'0661<6708=;9##::B "B "X9K0 ۰3+W8J/ ڰ3+dGM< 3+dF 3+HHQ 3+lmLD 3+ f dD@U]M +D!5!@@1"3NPQ&q q3+0I 3+h@&dD@U]M +D%5!GGo0+'7%=:dD@t +D#L@08 *dD@gW_O +D2#52654#51<<1 8822727N&IdDK PX@nU^N@U^NY +D!53353BBڪnnC*dD@eU]M +D!5!!5!z ȓ^CO;dD@0JW_O!!"" +D4632632#4#"#4#">0661<6708;9##::B "B ";f 0+77''7f*<;+<<+;<*;+<<+;<*;;*<@@b*dD@gW_O +D463"#52654&&?E:D;#/2$-#03&,02 ڰ3+`^)C8^v&PIdDK PX@oU]M@U]MY +D!#5!#BBnn0"24x 3+]FdDK PX@oU]M@U]MY +D3#5#]Bx5=4>Q% 0dD@%JH GU]M +D7355#}d}}d\>>\\>>]%dD@Ht +D5#7# ;\\;x__xe '/7?GKOW_gowױdDKPX@6 e 754 e  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g/-+H*,+*g20,..,U20,,.^K3J1I.,.NKPX@54  ~6 e 7 g  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g/-+H*,+*g20,..,U20,,.^K3J1I.,.N@54  ~/-+&+-&~6 e 7 g  9 8  g;:g=<gA@g?>e!C B#g%#E$D"+#"g)'G(F&*'&g+H*,+*g20,..,U20,,.^K3J1I.,.NYY@ɤyxqpiha`YXQPLLHHA@9810)(!  }{xyuspwqwmkhoioec`gag][X_Y_USPWQWLOLONMHKHKJIEC@GAG=;8?9?530717-+(/)/%# '!'     L +D53#!5#53%53"5432"5432#"5432"5432!"5432"5432!"543253!53%"5432!"5432"5432!"5432"5432!"5432"5432#"5432"54325353!533353f_gLO;NQ6_5>y|b;g566fz.6ff6f66p.F3VF.p6gg666NP1dD@& JU]M +D#5667&&'5I6886 ., D 3  2S?&3+d^H BdD@7~gW_O "" +D6632#&&#""&54632QHJK62.'9e)'d!_ 0+77''7_*31/12*31/1K*21/13*21/1P0dD@% JU]M +D.'5>73E6886 ., D 3  2P1dD@& JU]M +D#5667&&'5I6886 .,( D 3  2H%dDK PX@ ! JKPX@ ! JKPX@ ! J@ ! JYYYK PX@U]MKPX@~U]MKPX@U]M@~U]MYYY@%$ +D#5667&&'5>73#&&'#6886 ., D 3   3( D 3  2@BB@ B@S?&3+qN[j#dD@  Gt +D37''7'7#F >F3883F>&F"B 0*@@*0 B"@+JdD@? JgW_O*($"  +D2632#"'#"&54632654&#"4&#"326S891<<1871<<  5  ''7228((8227iT_\dDKPX@noU^N@U^NY@  +D#7#73_"3{"3xCxC 1dD@&W_O    +D"&'332673*F#k)F3_]6>7=dXK 1 13+q, dD@U]M +D!5!,Xq3,  3+_4dD@)Wg_O#"#" +D#".#"#66323267 ]G9gdg9<93 ]H8fdh9<9D<!C=!~O 2dD@'W_O    +D2#&&#"#66*F#k)F3O_]6>7=dX%.dD@#JHGU]M +D!55!}}za>\\>&S0+'77Bv"v"@?LA@?LhT%dD@GU]M +D#'73{"C?KNP0dD@% JU]M +D.'5>73E6886 .,N D 3  2H%dDK PX@ ! JKPX@ ! JKPX@ ! J@ ! JYYYK PX@U]MKPX@~U]MKPX@U]M@~U]MYYY@%$ +D#5667&&'5#.'5366736886 .,{ D 3   3( D 3  2@BB@ B@l'LMMM3+3+u'@U]M+!!NAr.L3+rL3+Cw# !dDK(PX@  J@  JYK(PX@Wg_O@+~~Wg_OY@ !!    +D"&5463256673"&54632 `7uC S# T#Uw# !dDK(PX@ J@ JYK(PX@Wg_O@,~~Wg_OY@ !!    +D2#"&546#&&'52#"&546}Z *7,##S #T C\^)dD@ Jt +D#&&'#5>72$rK<^PWZ;*gh\26 // &))&S0+'77'I"v"vSL?@AL?@h^%dD@HU]M +D#'73"{^K?C idD@ JKPX@nhW_O@hW_OY@ #+#" +D#363232654&'7#".#"#>. #/(((0:0$7-)'-?m, 9*/6($l^%dD@HU]M +D37#{"C?KN2dD@'eW_O"" +D5!32673#"'%1(%39LDd%A.#"/ENRlT%dD@GU]M +D3'#"{K?C< ,dD@!JGW_O$! +D4#"56632'66/ .86+,"%1;4.(I7,2'4dD@)JgW_O$. +D"546654&&546654#"'6632,W !3     H`6dD@+Wg`P  +D"#"&5533266333RI&5=@4DX8659>^R'JJ^ 7 #/UdD@Jg W  g _O%$ +)$/%/##    +D"&54632'2654&#""&54632!"&546322>?11@?2 "! ^;21;;12;1 !! \F#.UdD@J,Jg  W  _O%$+)$.%.##  +D"&'#"&546326632%27&&#"2654&#"s(67$2A?4%65)2?3.(($"%% +*+\,-?50C)!-@45!3E"!((('D!$TlH2dD@' JU]M +D667#&&'55,"22",H #44# rv| #/dD@$gW_O$$$$$" +D4632#"&4632#"&74632#"&.`H2  0+''7'77''7'7755#47!75#45 45#56!65#46a55#57!75#55!55#57!75#55ACdD@8 gW_ OAA><86$$"$$$$$ +D>3232>3232>32#&&#"#".#"#".#"6+ *)$$), 4;5(&,#&,,%#,#C?)********2: 80??2:cDC #//dD@$U]M +D4673#&&%#654'34673#&&%#654'32002200220022002:0@>2: 80??2::0@>2: 80??2:3 3+hFG}l l3+h|GEl l3+ G~|l l3+hpFG)l l3+lkGIl l3+l_G l l3+}(zFgRlG l3+3+ |FGSl l3+ QGPl l3+q(oBgXlG l3+3+^lCGZYl l3+hdGl l3+hzGl l3+hoGl l3+ip  3+4'O(dD@W_O"$ +D#.#"#6632OB4MZ,EwNA bU% (+UP H]/adD@V+J*IHGggWg_O$$$$$$$" +D&&#"56323267#"&&&#"56323267#"& "("2( $(#1' "(!3( $(#1'Q :$ :#v :$ 9$hxGl l3+lxBGl l3+lbBGl l3+"BGl l3+alBGal l3+hdFGl l3+l|BGl l3+ifBGl l3+lBGbl l3+lsBG{l l3+hzFGl l3+lpBG~l l3+ |FGl l3+hgFGl l3+llBGl l3+lyBGl l3+"BG|l l3+lnBGl l3+flBG@l l3+d"BG>l l3+hzFGcl l3+@hCdDK-PX@ J@ JYK-PX@eW_O@"~eW_OY@ +D"&54633##5#"3267R0>@1O'G#" h59:3O&*hdFGl l3+hrFGl l3+lrCG P~l l3+hlG Ql l3+lG J{l l3+thFGNl l3+rhGG WLl l3+lBGWl l3+}lBG[zl l3+qlBG]Kl l3+wG @  3+  3+ 3+  3+ M3L 3+M L 3+K' ְ3+;[8%3dD@(gW_O$$$" +D326544#"&54&#"3"&54632! 1>=32$!!1;10=91$""0;2/=:t79dD@.  JGgW_O$$$" +D4&#"327#"&54632566?#  -9?11@78+&"1:10f0H] Ȱ3+kkK %dD@U]M +D%4673#&&%#654'3k2002)2002:0@>2: 80??2: @W_O$$$" +4632#"&74632#"&Q @W_O   +"&54632tu^ @ Jt $ +.'53@<q ,.u9877 r @Jt  +#5>7 19:$"  99 57u! 1@. JU]M    +566733566739g699g69u P,83 P,83~r!@ Jt +#&'#5>7\@#:;487;,*P& /:=, 57~r!@ Jt +#&&'5367-* h@#;86: 68 P& 5<,p &@#W_O "" +#"&'33267TFIQ86,)7BKJC-+Y7 "@gW_O$$$" +#"&546324&#"326A25?@41B7#"!"4<<33<<3  nt,@)gW_O#"#" +#".#"#66323267>,'$"4>.'$#BB!#AC""z@U]M +!!0E$$@!JHW_O%! +3267#"&54667Y-52+0""t-82,7, 5:qH^Q *8' Ұ3+IwFT$e5:ZdDKPX@noU^N@U^NY@ "$"+D#"&546336632#/h28c6dD@+Wg_O +D2#4&#"##532665956@ QdDKPX@oU]M@U]MY@ "2!+D#"543!2#"'&(()('n./33/.hjFGl l3+ltBGyl l3+lIGl l3+ |BGl l3+ulBGml l3+lBG[l l3+lwBGl l3+`lCGQXl l3+V .dD@#W_O ""+D#"&'33267QVVNB/4123@=6##$"ku':dD@/JUh]M##+D#7#"&55332677u4> %(? ! 'F)/)( >6{4dD@) JU]M+D#5&&5467576654&SWVT>RYVU1881>177^LL^_LL^A77CB77AhFGUcl l3+f; FKPX@oU]M@U]MY@ ""+#"5433</9g34;gg FKPX@nU^N@U^NY@ !+53632#)/"g;,2$D/;fE/;E/;/;/;x/;f/;;qFE3+E3+3+װ3+3+3+3+q3+L,>@>|3+|3+ l #'+/dD@ee  e     eeU]M  /.-,+*)('&%$ # #"!  +D3##5#53535!!5!5!!5!5!!5!3#3#3#00~;c;';c;';c;200000000'000000000000~;;hkFG/l l3+ldBGll l3+lBGfl l3+lrBGA|l l3+lBGMl l3+hdFGDl l3+]hFGHl l3+KhGGG6l l3+nhGGIYl l3+ gFGl l3+hgFGFl l3+hnGGl l3+hrFGHl l3+hzGl l3+^&MM nFGJl l3+lqGKl l3+l!GLl l3+hyGll l3+l~GNl l3+lGOl l3+ldG@l l3+^lFGP8l l3+lqFGQl l3+hzFGRl l3+VGU 3+l`FGUl l3+lpCGZl l3+haFGVl l3+hRtGWl l3+hoBGXl l3+l}BGYl l3+lyBG[l l3+lbBG]l l3+G9)&f 3+#3 >JKPX@ ]L@U]MY@  +#5>73!0= 79 59bj ʰ3+3 Al@-,JIKPX@_K_K_L@c_K_LY@ -,%&<# +&&#""&'3267#"&&'&&'&&'532654&&'.546632/R)>M'G08W3f 4$#06@)"  +g:FV*L23S1("-V>9'2$1L;7=+ J N8"(+8#!4@1I_K]L !! +"#53&&5466323#56654&2PV@;܆5?9mONm9?5:BWdS\wAA"y[Cj??jC[y"AAw\Sd0 @ JKL +3#57'53;;;;0  00 _ S 6@3eK^L    ! +!332#!373254&##BXh\fn7Xx=:QIOWD_1(S"#KPX@  JK"PX@  J@  JYYKPX@# e _ K_LKPX@' e _ KK_LK"PX@+ eK _ KK_L@1  eeK _ KK_LYYY@! ##  +2!3267#"&'##3366"!&&&Dd5YP3O*)P7nXX ~_?I>"bMn:xElbNgcX9GCQ[KQPKLPLO)3E}T?84Cad^IEYa ^rs]^qkR E@B  JIe]&K]*L +5!!5!#!'!6=K9UN77?w:>a/"+4@    JK PX@/n o   e _K ]L@-    e _K ]LY@,,,4,3/-+)%#""!! +5#3533253#5"##32654&##32654&#\\@"@?A=:(?%OF@7B\R=KRNhSBDXbbeehrOE?S &F7Nckbb;:;3J<8E(&@#U]M +7"&546323![Y+"(UC-,#  !N("0+'7'7S+G+G**(2+@(eU]M +"&54633#"33DHHD~~__~bDAAC*Z[*('2<@9eeU]M +"&54633#"335!DHHD~~__~bDAAC*Z[*w**(2   3+(02   3+(2(@%eU]M!"!+53254##532#(~__~~DHIC*Z[*DAAC(029@6eeU]M!"!+53254##532#5!(~__~~DHHD~*Z[*DAACw**(2 ZEE3+('2 3+NT.@+W_O +!3"&54632BP?NT1@.W_O +!3"&54632BPNT1@.W_O +!3"&54632BP NT1@.W_O +!3'"&54632BPNT-@*W_O +!3#"&54632BPNT.@+W_O +33"&54632NBP@NT1@.W_O +33"&54632NBPNT1@.W_O +33"&54632NBP NT1@.W_O +337"&54632NBPNT-@*W_O +333"&54632NBPNT 3+NT 3+NT*@'U]M+333#NBBNT 3+NT$@!U]M+333NBBww_<zC{ +-   X^ HA<>?15A,(,')<2 )B( Ht <1<Y<0<-<<?<7<,<1<2 H <2<8<2 :ax=a,aa=aS(ka aaa =]a =na%3, ZX J6<&IPt I<&(1.gU7g747Xg7jUNUUUjU]7gUg7U3ijO '|'| <2 H<[< <;<';D@1e (<2B(@17<2^^(oU7 H^%x '" qx=,a,a,a,aS'S(SSa = = = = =<@ =ZZZZ6]awU1.1.1.1.1.1.`.747474747E]7jU]7]7]7]7]7<2]7jOjOjOjOgU1.1.1.x=7x=7x=7x=7ag7i7,a47,a47,a47,a47,a47=g7=g7=g7=g7ajj SSSS(S(d(NkaUU PE aA aU a U ajUajUajUajU =]7 =]7 =]7=6naUna>naG%33%33%33%33, i, i, iZjOZjOZjOZjOZjOZjO  66<&'<&'<&'FU<]1.q`. =]7%33((y(((,((((AA H:;3Za aE,a<&a =S(ka`aa+# =a]a:$, 6$3J$5&S6g7+jUZReOg7wU\6+7jUS7ZRUoU#6]7 ]O7i7 eO7'O8ZeO]7eO8,a  a=%3S(Sa jap agaa a,aVL&bbjaaa =a]ax=, p $3JaP aaZaaya1.W9@UUE47!UUUBU}U]7pUgU76xUeJUU URUDU/47j U73NJvUj UrUaU      6(((  g g A<xMH1''6(6'HA7<-<!L<5A6_&k2> >>#>0Ae^ ^^^M< d)) o<X aU1.M8=h7 ZO,ab47U z aU<a U aQUK $5O=]7{{=G75=7=:<7a3aUgR eagUaUL&!a-UjaUj  oaU/aU a_U =7x=7, 66w(^ PmJP^JajUQQS(Va0UKamUaUPeJaUS(1.1.q`.,a47;43;43VL&!H#bUbU =]7=]7=]7yp p p PeJaUZa Ux&Jg>g7>6#)&y#&[aU=7 M5+J1.1.1.11.1.1.1.1.1.1.1.,a47,a47,a47,a47,&4,a47,a47,a47S(<S(N =]7 =]7 =]7 =]) =]7 =]7 =]7=h7=h7=h7=h7=h7ZjOZjO ZO ZO ZO ZO ZO666i7@@>CCC> , ig  gagU{ZdRxx=7* g3g7\6,<;L6=:U\ZS"kaUZjU=3=M7 gUna%/-:$ei@ i, %Z6<&'H#H7":0H#!$KUP+A H aa=7aaU aalU1.S =]7ZjOZjOZjOZjOZjO1.1.q`.=g7=g7ka =]7 =]7H# aa=7=g7aaajU1.1.,Q47,a47SS =]7 =]7nWnaTZjOZjO?&aja_7:T2<&'1.,a47 =]7 =]7 =]7 =]76}U77x=7 , 3'  `,a47=g7n  61Qg7gUgU!0g7g74343+X7g6g7>7jQjUjU ZR@$z^UUQQUjjUxU]7`786UUZRZ/U/U3iij ]eQ ''  7 =@UX->7}U Ug7 777^^U[UNUss77 7  k7L g !! ((((y(((BHBH(( (J77!X NNNNNNN((g ((K(K((((( H(((((((A!7! N2<6=]7e=7aV,0Q ]77=7]agUx=aU]xx=x =g7   `3@  ;TVT$V!V[2VVjVz; #^^^4'9z;z;VTQj&j( '!,V TVV5C=? ?i?i'(??U?N???(&??i:]mm5$2!7$o$o!:>$8[7`77$8&&7 3`5JH 7Q#$f3 73J7Q3$fQgg7XjgZi"gU7J*@$Zg T ] gUg7X 7U(UjUgU(3'1.g7g747+!3N!jO!78$Y$? $5577`7`577$#7! 55J22`D $agUagUagUx=7ag7ag7ag7ag7ag7,a47,a47,a47,a47,a47aX=g7ajQajUaj%jajUSSkaEkaUkaU aL  a aaUaUajUajUajUajU =]7 =]7 =]7 =]7]agU]agUnaUnaInaIna%33%33%33%33%33, i, i, i, iZjOZjOZjOZjOZjOXX    JJ6<&'<&'<&'jUi 1.FUg7g7g7g7g7g7g7g7++++++}} jUjUjUjUjUjUjUjU09ZNZGZZZZZZXIhhjk]7]7]7]7]7]76OeOeOeOeOeOeOeOeOOc888888885Eg7g7++jUjUZZR]7]7eOeO88g7g7g7g7g7g7g7g74>(*44jUjUjUjUjUjUjUjU'&/%88888888;;!g7g7g7g7g7g7g7GAGQqDvjUjUjUjUjUsp1;?aAAQvZZZZZZSSAAQveOeOeOeO]O]OeOeO66A{A{A88888\-S/h&AM+<('g ,H' H^^^mo$$X o!<$<3<8U< AS <<< < =`% x=<O@1>!   ] na1.iavUa/U<&'= $  aU7R5(e(i(Z(FF H H H((( H2 Q Qsm|g7g7g7g7g7g7g7g7ZZZZZZZZeOeOeOeOeOeOeOeOZZZZeOeOeOeOba[4444}WlNXllWkXkk..44....)&a2UpjaU2CakUaUaUAV<aaZ=\ zF1aB O1+#n#T}}xk kak ia  =C=]T  ==J-9^ZZW-),V4 \S9S9 \\p7aagfl#R L&=ak, !^=,aS(Z aa:U\65!!433UFF ]77UPEF+iU(-.NO(......! U YUt57gf g77 B' ggUK57UPU UUUAW5UJ0UUU vUgiwU:VP51.47#jUjOZZUH3 J......FJF FJF JJJHJJJDJJJP/P/P/P/P/WJWWJ#%#%####%#%##%#JJJJJJJJoJoJoJoJoJoJx0x0x0x0x0x0x0x0x0x0x00JJx0JJJJ))))))WE     TETETETETETETETETETETE35E.2]3aS(_{A"A"#-,Z ==Ka=aa Ta = =8=#3&0KR " `" V a aC==rSJ.5E7b7Uyz X1##-ZRoP":c 1UD7LUe>TxU]7w57E+!HuUiUU7]7\6q+B 7a|bfaUUwU=/ QJ $F"A  ahx2 a aa =av&$(aL6ka[8  11JC  W Wo 1jgD7jUA6KO+X-O<e.:6N b5zUiUiU,,C-6CC55oP"WFUPUx fiPiPT'G/rryiPg77[77`7777! ~"g#7 AH7+&<-<<?<7M:<2K6",8/;?8 <1?/<1#"#I#/#'##0#(# #%#"^^%^^^ ^^^^^^^%^^^ ^^^^^%d%J[B %60%%%^^%^^^ ^^^^^^6XDODva]<'L;55H54=5<564 HB(5d]{>>5'')45W   ) H##<E2^#,xx>>5'<f=5O5;2<2<2F$B H L+ J')H545apDCfL:1#Cz H'MPMMPM('BB(B( ((>('Z<89<556q qBwtXXXXXXXm*;F U" p7a%"tR#R#xR#R#-R#R# lE 6)  ](7= caj7NR,lacTay*8(^D,NNNN "&"(&&#' NNNN""&&(&#'%'.%%%NNNN%'%%%""#""""'%'%%%??NNNN%GGGGG3%%%%%%%%"%%-****+NNNNH_)[sYWel0ddNOsNCXWddHl 1N0o:NCC0`&0HHXQdH@~&hHCU\&hllHr2t33}q^]afd@tr}qw ;kNQtEN~N~M5n;HI58Hu`V6fgL]Kn^b#3A"d+#0%S6S[.Ol<a(J(Z(Z(Z(Z(Z(Z(Z(Z(NNNNNNNNNNNNNNNTTTT0x0@x< | ` H $P`D04@$\XL,d0ph !"@#@#$%&4&t'8'(@))*+D+,d,-d-.<.//\/0t0t012L3$3355678@8899:l:;X<L>?4?x@XADBCC<C`CCCCDpDDDDEE<E`EEF8F\FFFFGGLHH8H\HHHI8J\JtJJJJJL<LTLlLLLLLLMMMNNN4NLNdOOOOPPP4QQQ@QXQ|QQQQRR$R<R`RxRRRRST4TXTpTTTTUVV8VPVtVVVVWWW4WXW|XXY Y$YHY`YYYYYZZ ZDZ\ZtZZ[[@[X[p[[[[\T\\]]]0]T]l]^(^__0_T_l__`abbb0bHblbbbbbcc,cPchccccd4dee eDe\eeeeefffgg0gTglgggghh,hDhij|ll<lTlxlllm,mmn@noopppqrrr@rdrrrrss s0stt,t<tLttuu|uuv8vHvvwPw`wpxTxdyyyzz z8zPzhz{x|P|}~H~8H HDd\t`dt 4XTdtP`p4dX|D D\P$l|p@hD\`x 0@Xh0@Xp x,Ph8|$ \|,|$H0@P<0¬LÜhhhhhhhhhhhhhh $ $<TΜ<Ф8\tь0X֔נ0لڸۨXhx߰` D\Ld4`4@DH(`4 d(XL\Xhp| p 8   `  , (8Hl8Pt 4Xp,DhL8H,<@| , !t""x"##$&(&@&X&|&&&&' '0'H'l''''( (0(H(l(((() )$)<)`)x))))**,*P*h****++,+D+\+t++++,,(,@,d,|,,,--(-@-d-|----. .$.<.T.x....//,/D/h/////00(0L0d0|1 123L44567D7779 : :;,;>?@AA8AABlBCDxEF FGHLHIJKXKhKxLLLMNO\PPPQR`RSTHTUVPWLWX@XPXYZ\[0[\d] ]^ ^$^^^__<_T_l_____``,`D`h`````aa4aXapaaaabbb<bTc ddddeee4e`eeeeeff(f@fggghh hDh\hhhhhii4iLipiiiijj$j<j`jxjjkPkll4lnop|pppppqq4qLqpqqqqqrr0rttuvwxyzz{|\|}H~~~L|d@$<l(8t`H XDdd,LpT$\@p4@ 8`x4@P <pHDx ø Dhƌư@dtDŽǔǤT(tɔɤɴ,|ʨ(H˄˘˼(L̘@͔hΈΨθHpϘϸ8|PѼ |Ҍ(8HlӐӠԐԸ`נD٨۔@ܼ(8H<Ld|(pL p,\\XT|P8x ,Pt(Lp4X| 0Tx,Pt(Lp$Hl Dd$Ddhp0P $    (  8xT,,d T!x"#$%&'()*+p,@,d,,,,--<-`----..8.\....//4/X/|///0 000T0x00011,1P1t111122(2T2t222233(3585P5h5555566(6L6x666777@7d7|77778 8$8<8T8x888899,9D9h9999::8:P:t::::;;;4;L;p;;;;<<$<<<`>,>P>h>>>>>??<?\?????@ @$@H@l@@@@@A A8A\AtAAAABBB4BLBpBBBBCC$C<CTClCCCCCDD DDD\DtDDDDDEE(ELEpEEEFF$F<FTFlFFFFFG$GLGtGGGGGHH,HDH\HHHHIIDIlIIIIIJ J$J<JTJxJJJKK<KdKKKKKLLL@LdLLLMMM4MLMdM|MMMMNN8N\NtNNNNNOOO@OdOOOPP,PPPhPPPPPPQQ(Q@QXQpQQQQQRR4RPRlRRRS S8SdSSSTT(THThTTTTUU@UpUUVV0V`VVVVVWW,WHWtWWXX0X`XXXXY Y$Y@YXYtYYYZZZ|[[[$[\\4\T\l\\\\]]4^^___```0`H`l```abc\ctcccccddd@dddddefffg g(g@g`gggghhXhhiPijjPjhjkk\kl$llmm n0noDooppqqrrrrss8su v@wpxLyz{4{} }~D`\T(d$h\l@D<hHh$P\44 8Ph(@Xp0H`xdt\XHD8`0p<0\XPlT0Lè0Ĵ@@ ȬLT˸\͘ͰϨ<ѬPdӸՔttT\ܠ0ݤ\ ߰d$xD`8TdtHhDD,D\tl|xd (l $(x$<\   8  P   ( LT(LX4$ , !|"D"d"#L#$t%d&&'(x)4)****+,,,,D,\,t,,,,,--,.l011(1H1h111112$2D223|44 4@4X4x445l55566(6H6h666667<7788(8@89 9@9999::$:<:T:t::;;(;;;;<<0x>??(?H?h????@@@B`BC@CDLDdDDE@EXExEEEFFG\G|GGH H$HDHdH|HHHIxIIJJJJKKKlKKKL L$LLLLMMDMlMMMN NO@PPRXSHT`TxTUlVVWXXYZDZ[|\\\]^8^^__`dabDbd@de8fpfghi\ilj jkhl0m nnooopDpqXqs tttu@uvwtxxxyzt{0{||}$}~`t hp T@(d$0\P@<,,<`8@DČPƘ<Șɤˀ̴͸|p4XҔӬԄՠּ \۸Pߨ߸|\l$4Tt@d$t0Xd<x`|4T ,<\|<\|<\|4Tt<\|<\D4L0$Xt<  , T  T    X\h d|0h4X|x|Hlx0Pp 8  ! !D"|#\%%4%&&$&D&d&&''\'t'(X),)x)))**(*@*d*|***++,,,l,---x--..\//00$0$0$0$0$0$0$0$0$0$0$0$0$112024H45667@7`78x889<99:<< ^ <  4nCopyright 2015 Google Inc. All Rights Reserved.Noto SansRegular2.000;GOOG;NotoSans-RegularNoto Sans RegularVersion 2.000;GOOG;noto-source:20170915:90ef993387c0; ttfautohint (v1.7)NotoSans-RegularNoto is a trademark of Google Inc.Monotype Imaging Inc.Monotype Design TeamDesigned by Monotype design team.http://www.google.com/get/noto/http://www.monotype.com/studioThis Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.http://scripts.sil.org/OFL2   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`a  bcdefghjikmlnoqprsutvwxzy{}|~    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                           ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                            ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~  NULLCRuni00A0uni00AD overscoreuni00B2uni00B3uni00B5uni00B9AmacronamacronAbreveabreveAogonekaogonek Ccircumflex ccircumflexCdotcdotDcarondcaronDcroatEmacronemacronEbreveebreve Edotaccent edotaccentEogonekeogonekEcaronecaron Gcircumflex gcircumflexGdotgdotuni0122uni0123 Hcircumflex hcircumflexHbarhbarItildeitildeImacronimacronIbreveibreveIogonekiogonekIJij Jcircumflex jcircumflexuni0136uni0137 kgreenlandicLacutelacuteuni013Buni013CLcaronlcaronLdotldotNacutenacuteuni0145uni0146Ncaronncaron napostropheEngengOmacronomacronObreveobreve Ohungarumlaut ohungarumlautRacuteracuteuni0156uni0157RcaronrcaronSacutesacute Scircumflex scircumflexuni021Auni021BTcarontcaronTbartbarUtildeutildeUmacronumacronUbreveubreveUringuring Uhungarumlaut uhungarumlautUogonekuogonek Wcircumflex wcircumflex Ycircumflex ycircumflexZacutezacute Zdotaccent zdotaccentlongs Aringacute aringacuteAEacuteaeacute Oslashacute oslashacuteuni0218uni0219tonos dieresistonos Alphatonos anoteleia EpsilontonosEtatonos Iotatonos Omicrontonos Upsilontonos OmegatonosiotadieresistonosAlphaBetaGammauni0394EpsilonZetaEtaThetaIotaKappaLambdaMuNuXiOmicronPiRhoSigmaTauUpsilonPhiChiPsiuni03A9 IotadieresisUpsilondieresis alphatonos epsilontonosetatonos iotatonosupsilondieresistonosalphabetagammadeltaepsilonzetaetathetaiotakappalambdauni03BCnuxiomicronrhosigma1sigmatauupsilonphichipsiomega iotadieresisupsilondieresis omicrontonos upsilontonos omegatonos afii10023 afii10051 afii10052 afii10053 afii10054 afii10055 afii10056 afii10057 afii10058 afii10059 afii10060 afii10061 afii10062 afii10145 afii10017 afii10018 afii10019 afii10020 afii10021 afii10022 afii10024 afii10025 afii10026 afii10027 afii10028 afii10029 afii10030 afii10031 afii10032 afii10033 afii10034 afii10035 afii10036 afii10037 afii10038 afii10039 afii10040 afii10041 afii10042 afii10043 afii10044 afii10045 afii10046 afii10047 afii10048 afii10049 afii10065 afii10066 afii10067 afii10068 afii10069 afii10070 afii10072 afii10073 afii10074 afii10075 afii10076 afii10077 afii10078 afii10079 afii10080 afii10081 afii10082 afii10083 afii10084 afii10085 afii10086 afii10087 afii10088 afii10089 afii10090 afii10091 afii10092 afii10093 afii10094 afii10095 afii10096 afii10097 afii10071 afii10099 afii10100 afii10101 afii10102 afii10103 afii10104 afii10105 afii10106 afii10107 afii10108 afii10109 afii10110 afii10193 afii10050 afii10098WgravewgraveWacutewacute Wdieresis wdieresisYgraveygrave afii00208 underscoredbl quotereversedminutesecond exclamdbl nsuperior afii08941pesetaEuro afii61248 afii61289 afii61352uni2126 estimated oneeighth threeeighths fiveeighths seveneighths cyrillicbrevecaroncommaaccent commaaccentcommaaccentrotateuni2074uni2075uni2077uni2078uni2000uni2001uni2002uni2003uni2004uni2005uni2006uni2007uni2008uni2009uni200Auni200BuniFEFFuniFFFCuniFFFDuni01F0uni02BCuni03D1uni03D2uni03D6uni1E3Euni1E3Funi1E00uni1E01uni02F3OhornohornUhornuhornhookuni0400uni040Duni0450uni045Duni0460uni0461uni0462uni0463uni0464uni0465uni0466uni0467uni0468uni0469uni046Auni046Buni046Cuni046Duni046Euni046Funi0470uni0471uni0472uni0473uni0474uni0475uni0476uni0477uni0478uni0479uni047Auni047Buni047Cuni047Duni047Euni047Funi0480uni0481uni0482uni0488uni0489uni048Auni048Buni048Cuni048Duni048Euni048Funi0492uni0493uni0494uni0495uni0496uni0497uni0498uni0499uni049Auni049Buni049Cuni049Duni049Euni049Funi04A0uni04A1uni04A2uni04A3uni04A4uni04A5uni04A6uni04A7uni04A8uni04A9uni04AAuni04ABuni04ACuni04ADuni04AEuni04AFuni04B0uni04B1uni04B2uni04B3uni04B4uni04B5uni04B6uni04B7uni04B8uni04B9uni04BAuni04BBuni04BCuni04BDuni04BEuni04BFuni04C0uni04C1uni04C2uni04C3uni04C4uni04C5uni04C6uni04C7uni04C8uni04C9uni04CAuni04CBuni04CCuni04CDuni04CEuni04CFuni04D0uni04D1uni04D2uni04D3uni04D4uni04D5uni04D6uni04D7uni04D8uni04D9uni04DAuni04DBuni04DCuni04DDuni04DEuni04DFuni04E0uni04E1uni04E2uni04E3uni04E4uni04E5uni04E6uni04E7uni04E8uni04E9uni04EAuni04EBuni04ECuni04EDuni04EEuni04EFuni04F0uni04F1uni04F2uni04F3uni04F4uni04F5uni04F6uni04F7uni04F8uni04F9uni04FAuni04FBuni04FCuni04FDuni04FEuni04FFuni0500uni0501uni0502uni0503uni0504uni0505uni0506uni0507uni0508uni0509uni050Auni050Buni050Cuni050Duni050Euni050Funi0510uni0511uni0512uni0513uni1EA0uni1EA1uni1EA2uni1EA3uni1EA4uni1EA5uni1EA6uni1EA7uni1EA8uni1EA9uni1EAAuni1EABuni1EACuni1EADuni1EAEuni1EAFuni1EB0uni1EB1uni1EB2uni1EB3uni1EB4uni1EB5uni1EB6uni1EB7uni1EB8uni1EB9uni1EBAuni1EBBuni1EBCuni1EBDuni1EBEuni1EBFuni1EC0uni1EC1uni1EC2uni1EC3uni1EC4uni1EC5uni1EC6uni1EC7uni1EC8uni1EC9uni1ECAuni1ECBuni1ECCuni1ECDuni1ECEuni1ECFuni1ED0uni1ED1uni1ED2uni1ED3uni1ED4uni1ED5uni1ED6uni1ED7uni1ED8uni1ED9uni1EDAuni1EDBuni1EDCuni1EDDuni1EDEuni1EDFuni1EE0uni1EE1uni1EE2uni1EE3uni1EE4uni1EE5uni1EE6uni1EE7uni1EE8uni1EE9uni1EEAuni1EEBuni1EECuni1EEDuni1EEEuni1EEFuni1EF0uni1EF1uni1EF4uni1EF5uni1EF6uni1EF7uni1EF8uni1EF9uni20ABcircumflexacutecombcircumflexgravecombcircumflexhookcombcircumflextildecombbreveacutecombbrevegravecomb brevehookcombbrevetildecombcyrillichookleftcyrillicbighookUCuni0162uni0163uni0180uni0181uni0182uni0183uni0184uni0185uni0186uni0187uni0188uni0189uni018Auni018Buni018Cuni018Duni018Euni018Funi0190uni0191uni0193uni0194uni0195uni0196uni0197uni0198uni0199uni019Auni019Buni019Cuni019Duni019Euni019Funi01A2uni01A3uni01A4uni01A5uni01A6uni01A7uni01A8uni01A9uni01AAuni01ABuni01ACuni01ADuni01AEuni01B1uni01B2uni01B3uni01B4uni01B5uni01B6uni01B7uni01B8uni01B9uni01BAuni01BBuni01BCuni01BDuni01BEuni01BFuni01C0uni01C1uni01C2uni01C3uni01C4uni01C5uni01C6uni01C7uni01C8uni01C9uni01CAuni01CBuni01CCuni01CDuni01CEuni01CFuni01D0uni01D1uni01D2uni01D3uni01D4uni01D5uni01D6uni01D7uni01D8uni01D9uni01DAuni01DBuni01DCuni01DEuni01DFuni01E0uni01E1uni01E2uni01E3uni01E4uni01E5uni01E6uni01E7uni01E8uni01E9uni01EAuni01EBuni01ECuni01EDuni01EEuni01EFuni01F1uni01F2uni01F3uni01F4uni01F5uni01F6uni01F7uni01F8uni01F9uni0200uni0201uni0202uni0203uni0204uni0205uni0206uni0207uni0208uni0209uni020Auni020Buni020Cuni020Duni020Euni020Funi0210uni0211uni0212uni0213uni0214uni0215uni0216uni0217uni021Cuni021Duni021Euni021Funi0220uni0221uni0222uni0223uni0224uni0225uni0226uni0227uni0228uni0229uni022Auni022Buni022Cuni022Duni022Euni022Funi0230uni0231uni0232uni0233uni0234uni0235uni0236uni0238uni0239uni023Auni023Buni023Cuni023Duni023Euni023Funi0240uni0241uni0242uni0243uni0244uni0245uni0246uni0247uni0248uni0249uni024Auni024Buni024Cuni024Duni024Euni024Funi0250uni0251uni0252uni0253uni0254uni0255uni0256uni0257uni0258uni0259uni025Buni025Euni025Funi0260uni0261uni0262uni0263uni0264uni0265uni0266uni0267uni0268 iotaLatinuni026Auni026Buni026Cuni026Duni026Euni026Funi0270uni0271uni0272uni0273uni0274uni0275uni0276 omegacloseduni0278uni0279uni027Auni027Buni027Cuni027Duni027Euni027Funi0280uni0281uni0282uni0283uni0284uni0285uni0286uni0287uni0288uni0289uni028Auni028Buni028Cuni028Duni028Euni028Funi0290uni0291uni0292uni0293uni0294uni0295 glottalturneduni0297uni0298uni0299uni029Auni029Buni029Cuni029Duni029Euni029Funi02A0uni02A1uni02A2dzliguni02A4 dzligcurltsliguni02A7 tcligcurluni02A9uni02AAuni02ABuni02ACuni02ADuni02AEuni02AFuni02B0uni02B1uni02B2uni02B3uni02B4uni02B5uni02B6uni02B7uni02B8uni02B9uni02BAuni02BBuni02BDuni02BEuni02BFuni02C0uni02C1uni02C2uni02C3uni02C4uni02C5uni02C8uni02CAuni02CBuni02CCuni02CDuni02CEuni02CFuni02D0uni02D1uni02D2uni02D3uni02D4uni02D5uni02D6uni02D7uni02DFuni02E0uni02E1uni02E2uni02E3uni02E4uni02E5uni02E6uni02E7uni02E8uni02E9uni02EAuni02EBuni02ECuni02EDuni02EEuni02EFuni02F0uni02F1uni02F2uni02F4uni02F5uni02F6uni02F7uni02F8uni02F9uni02FAuni02FBuni02FCuni02FDuni02FEuni02FF hookabovecombuni0374uni0375uni037Auni037Buni037Cuni037Duni037Euni03D0uni03D3uni03D4phi1uni03D7uni03D8uni03D9uni03DAuni03DBuni03DCuni03DDuni03DEuni03DFuni03E0uni03E1uni03F0uni03F1uni03F2uni03F3uni03F4uni03F5uni03F6uni03F7uni03F8uni03F9uni03FAuni03FBuni03FCuni03FDuni03FEuni03FFuni051Auni051Buni051Cuni051Duni1D00uni1D01uni1D02uni1D03uni1D04uni1D05uni1D06uni1D07uni1D08uni1D09uni1D0Auni1D0Buni1D0Cuni1D0Duni1D0Euni1D0Funi1D10uni1D11uni1D12uni1D13uni1D14uni1D15uni1D16uni1D17uni1D18uni1D19uni1D1Auni1D1Buni1D1Cuni1D1Duni1D1Euni1D1Funi1D20uni1D21uni1D22uni1D23uni1D24uni1D25uni1D26uni1D27uni1D28uni1D29uni1D2Auni1D2Buni1D2Cuni1D2Duni1D2Euni1D2Funi1D30uni1D31uni1D32uni1D33uni1D34uni1D35uni1D36uni1D37uni1D38uni1D39uni1D3Auni1D3Buni1D3Cuni1D3Duni1D3Euni1D3Funi1D40uni1D41uni1D42uni1D43uni1D44uni1D45uni1D46uni1D47uni1D48uni1D49uni1D4Auni1D4Buni1D4Cuni1D4Duni1D4Euni1D4Funi1D50uni1D51uni1D52uni1D53uni1D54uni1D55uni1D56uni1D57uni1D58uni1D59uni1D5Auni1D5Buni1D5Cuni1D5Duni1D5Euni1D5Funi1D60uni1D61uni1D62uni1D63uni1D64uni1D65uni1D66uni1D67uni1D68uni1D69uni1D6Auni1D6Buni1D6Cuni1D6Duni1D6Euni1D6Funi1D70uni1D71uni1D72uni1D73uni1D74uni1D75uni1D76uni1D77uni1D78uni1D79uni1D7Auni1D7Buni1D7Cuni1D7Duni1D7Euni1D7Funi1D80uni1D81uni1D82uni1D83uni1D84uni1D85uni1D86uni1D87uni1D88uni1D89uni1D8Auni1D8Buni1D8Cuni1D8Duni1D8Euni1D8Funi1D90uni1D91uni1D92uni1D93uni1D94uni1D95uni1D96uni1D97uni1D98uni1D99uni1D9Auni1D9Buni1D9Cuni1D9Duni1D9Euni1D9Funi1DA0uni1DA1uni1DA2uni1DA3uni1DA4uni1DA5uni1DA6uni1DA7uni1DA8uni1DA9uni1DAAuni1DABuni1DACuni1DADuni1DAEuni1DAFuni1DB0uni1DB1uni1DB2uni1DB3uni1DB4uni1DB5uni1DB6uni1DB7uni1DB8uni1DB9uni1DBAuni1DBBuni1DBCuni1DBDuni1DBEuni1DBFuni1E02uni1E03uni1E04uni1E05uni1E06uni1E07uni1E08uni1E09uni1E0Auni1E0Buni1E0Cuni1E0Duni1E0Euni1E0Funi1E10uni1E11uni1E12uni1E13uni1E14uni1E15uni1E16uni1E17uni1E18uni1E19uni1E1Auni1E1Buni1E1Cuni1E1Duni1E1Euni1E1Funi1E20uni1E21uni1E22uni1E23uni1E24uni1E25uni1E26uni1E27uni1E28uni1E29uni1E2Auni1E2Buni1E2Cuni1E2Duni1E2Euni1E2Funi1E30uni1E31uni1E32uni1E33uni1E34uni1E35uni1E36uni1E37uni1E38uni1E39uni1E3Auni1E3Buni1E3Cuni1E3Duni1E40uni1E41uni1E42uni1E43uni1E44uni1E45uni1E46uni1E47uni1E48uni1E49uni1E4Auni1E4Buni1E4Cuni1E4Duni1E4Euni1E4Funi1E50uni1E51uni1E52uni1E53uni1E54uni1E55uni1E56uni1E57uni1E58uni1E59uni1E5Auni1E5Buni1E5Cuni1E5Duni1E5Euni1E5Funi1E60uni1E61uni1E62uni1E63uni1E64uni1E65uni1E66uni1E67uni1E68uni1E69uni1E6Auni1E6Buni1E6Cuni1E6Duni1E6Euni1E6Funi1E70uni1E71uni1E72uni1E73uni1E74uni1E75uni1E76uni1E77uni1E78uni1E79uni1E7Auni1E7Buni1E7Cuni1E7Duni1E7Euni1E7Funi1E86uni1E87uni1E88uni1E89uni1E8Auni1E8Buni1E8Cuni1E8Duni1E8Euni1E8Funi1E90uni1E91uni1E92uni1E93uni1E94uni1E95uni1E96uni1E97uni1E98uni1E99uni1E9Auni1E9Buni1F00uni1F01uni1F02uni1F03uni1F04uni1F05uni1F06uni1F07uni1F08uni1F09uni1F0Auni1F0Buni1F0Cuni1F0Duni1F0Euni1F0Funi1F10uni1F11uni1F12uni1F13uni1F14uni1F15uni1F18uni1F19uni1F1Auni1F1Buni1F1Cuni1F1Duni1F20uni1F21uni1F22uni1F23uni1F24uni1F25uni1F26uni1F27uni1F28uni1F29uni1F2Auni1F2Buni1F2Cuni1F2Duni1F2Euni1F2Funi1F30uni1F31uni1F32uni1F33uni1F34uni1F35uni1F36uni1F37uni1F38uni1F39uni1F3Auni1F3Buni1F3Cuni1F3Duni1F3Euni1F3Funi1F40uni1F41uni1F42uni1F43uni1F44uni1F45uni1F48uni1F49uni1F4Auni1F4Buni1F4Cuni1F4Duni1F50uni1F51uni1F52uni1F53uni1F54uni1F55uni1F56uni1F57uni1F59uni1F5Buni1F5Duni1F5Funi1F60uni1F61uni1F62uni1F63uni1F64uni1F65uni1F66uni1F67uni1F68uni1F69uni1F6Auni1F6Buni1F6Cuni1F6Duni1F6Euni1F6Funi1F70uni1F71uni1F72uni1F73uni1F74uni1F75uni1F76uni1F77uni1F78uni1F79uni1F7Auni1F7Buni1F7Cuni1F7Duni1F80uni1F81uni1F82uni1F83uni1F84uni1F85uni1F86uni1F87uni1F88uni1F89uni1F8Auni1F8Buni1F8Cuni1F8Duni1F8Euni1F8Funi1F90uni1F91uni1F92uni1F93uni1F94uni1F95uni1F96uni1F97uni1F98uni1F99uni1F9Auni1F9Buni1F9Cuni1F9Duni1F9Euni1F9Funi1FA0uni1FA1uni1FA2uni1FA3uni1FA4uni1FA5uni1FA6uni1FA7uni1FA8uni1FA9uni1FAAuni1FABuni1FACuni1FADuni1FAEuni1FAFuni1FB0uni1FB1uni1FB2uni1FB3uni1FB4uni1FB6uni1FB7uni1FB8uni1FB9uni1FBAuni1FBBuni1FBCuni1FBDuni1FBEuni1FBFuni1FC0uni1FC1uni1FC2uni1FC3uni1FC4uni1FC6uni1FC7uni1FC8uni1FC9uni1FCAuni1FCBuni1FCCuni1FCDuni1FCEuni1FCFuni1FD0uni1FD1uni1FD2uni1FD3uni1FD6uni1FD7uni1FD8uni1FD9uni1FDAuni1FDBuni1FDDuni1FDEuni1FDFuni1FE0uni1FE1uni1FE2uni1FE3uni1FE4uni1FE5uni1FE6uni1FE7uni1FE8uni1FE9uni1FEAuni1FEBuni1FECuni1FEDuni1FEEuni1FEFuni1FF2uni1FF3uni1FF4uni1FF6uni1FF7uni1FF8uni1FF9uni1FFAuni1FFBuni1FFCuni1FFDuni1FFEuni200Cuni200Duni200Euni200Funi2012uni2016uni201Funi202Auni202Buni202Cuni202Duni202Euni202Funi2034uni203Euni205Euni206Auni206Buni206Cuni206Duni206Euni206Funi2070uni2076uni2079uni2090uni2091uni2092uni2093uni2094uni20A0uni20A1uni20A2uni20A5uni20A6uni20A8uni20A9uni20ADuni20AEuni20AFuni20B0uni20B1uni20B2uni20B3uni20B4uni20B5uni20B9uni20F0uni2117uni214Duni214Euni2153uni2154uni2184uni2C60uni2C61uni2C62uni2C63uni2C64uni2C65uni2C66uni2C67uni2C68uni2C69uni2C6Auni2C6Buni2C6Cuni2C6Duni2C71uni2C72uni2C73uni2C74uni2C75uni2C76uni2C77uni2E17uniA717uniA718uniA719uniA71AuniA71BuniA71CuniA71DuniA71EuniA71FuniA720uniA721uniA788uniA789uniA78AuniA78BuniA78C dieresisacute dieresisgraveuniFE20uniFE21uniFE22uniFE23uni03B1030403130300uni03B1030403130301uni03B1030403140300uni03B1030403140301uni03B1030603130300uni03B1030603130301uni03B1030603140300uni03B1030603140301uni03B9030403130300uni03B9030403130301uni03B9030403140300uni03B9030403140301uni03B9030603130300uni03B9030603130301uni03B9030603140300uni03B9030603140301uni03C5030403130300uni03C5030403130301uni03C5030403140300uni03C5030403140301uni03C5030603130300uni03C5030603130301uni03C5030603140300uni03C5030603140301uni03B9030803040300uni03B9030803040301uni03B9030803060300uni03B9030803060301uni03C5030803040300uni03C5030803040301uni03C5030803060300uni03C5030803060301Eng.alt1Eng.alt2Eng.alt3 uni1FCD02C9 uni1FCE02C9 uni1FDD02C9 uni1FDE02C9dotacutecarondotmacrondieresis tildedieresis tildeacute macrongrave macronacute dieresiscarondieresismacron tildemacron dotmacron dotmacron.capuni030103060308uni030003060308uni030103040308uni030003040308 uni1FDE0306 uni1FDD0306 uni1FCE0306 uni1FCD0306uni0514uni0515uni0516uni0517uni0518uni0519uni051Euni051Funi0520uni0521uni0522uni0523uni0524uni0525uni0526uni0527cyrillic_otmarkuni20BAuni1EFAuni2C6Euni1E9ETurnedauni1EFCuni1EFEuniA722uniA724uniA726uniA728uniA72AuniA72CuniA72EuniA732uniA734uniA736uniA738uniA73AuniA73CuniA73EuniA740uniA742uniA744uniA746uniA748uniA74AuniA74CuniA74EuniA750uniA752uniA754uniA756uniA758uniA75AuniA75CuniA75EuniA760uniA764uniA766uniA768uniA76AuniA76CuniA76EuniA779uniA77BuniA77DuniA77EuniA780uniA782uniA784uniA786uniA78DuniA790uniA792uniA7A0uniA7A2uniA7A4uniA7A6uniA7A8uniA7AAuniA7ABuniA7ACuniA7ADuniA7B0uniA7B1uniA7B2uniA7B3uniA7B4uniA7B6Aogonek.loclNAVEogonek.loclNAVIogonek.loclNAVUogonek.loclNAVLcommaaccent.loclMAHNcommaaccent.loclMAHTurnedeafii10103dotlessafii10105dotless deltalatinuni2C78uni025Cuni025Duni01DDuni025Aiogonekdotlessuni0237uni1EFBuni1E9Cuni1E9Duni2C7A subscriptjuni2C79uni0249dotlessuni029Ddotlessuni02B2dotlessuni03F3dotlessuni1D62dotlessuni1D96dotlessuni1DA4dotlessuni1DA8dotlessuni1E2Ddotlessuni1ECBdotlessuniA723uniA725uniA727uniA729uniA72BuniA72DuniA72FuniA730uniA731uniA733uniA735uniA737uniA739uniA73BuniA73DuniA73FuniA741uniA743uniA745uniA747uniA749uniA74BuniA74DuniA74FuniA751uniA753uniA755uniA757uniA759uniA75BuniA75DuniA75FuniA761uniA765uniA767uniA769uniA76BuniA76DuniA76FuniA771uniA772uniA773uniA774uniA775uniA776uniA777uniA778uniA77AuniA77CuniA77FuniA781uniA783uniA785uniA787uniA78EuniA791uniA793uniA7A1uniA7A3uniA7A5uniA7A7uniA7A9uniA7B5uniA7B7uniA7FAuni1EFDuni1EFFaogonek.loclNAVeogonek.loclNAVlcommaaccent.loclMAHncommaaccent.loclMAHiogonek.loclNAVuogonek.loclNAVf_ff_f_if_f_lf_if_llongs_ts_ta.sc aacute.sc abreve.scacircumflex.sc adieresis.sc agrave.sc amacron.sc aogonek.scaring.sc aringacute.sc atilde.scae.sc aeacute.scb.scc.sc cacute.sc ccaron.sc ccedilla.scccircumflex.sccdot.scd.sceth.sc dcaron.sc dcroat.sce.sc eacute.sc ebreve.sc ecaron.scecircumflex.sc edieresis.sc edotaccent.sc egrave.sc emacron.sc eogonek.scf.scg.sc gbreve.scgcircumflex.scgcommaaccent.scgdot.sch.schbar.schcircumflex.sci.sc iacute.sc ibreve.scicircumflex.sc idieresis.sc idotaccent.sc igrave.scij.sc imacron.sc iogonek.sc itilde.scj.scjcircumflex.sck.sckcommaaccent.scl.sc lacute.sc lcaron.sclcommaaccent.scldot.sc lslash.scm.scn.sc nacute.sc ncaron.scncommaaccent.sceng.sc ntilde.sco.sc oacute.sc obreve.scocircumflex.sc odieresis.sc ograve.scohungarumlaut.sc omacron.sc oslash.scoslashacute.sc otilde.scoe.scp.scthorn.scq.scr.sc racute.sc rcaron.scrcommaaccent.scs.sc sacute.sc scaron.sc scedilla.scscircumflex.scscommaaccent.sc germandbls.sct.sctbar.sc tcaron.sc tcedilla.sctcommaaccent.scu.sc uacute.sc ubreve.scucircumflex.sc udieresis.sc ugrave.scuhungarumlaut.sc umacron.sc uogonek.scuring.sc utilde.scv.scw.sc wacute.scwcircumflex.sc wdieresis.sc wgrave.scx.scy.sc yacute.scycircumflex.sc ydieresis.sc ygrave.scz.sc zacute.sc zcaron.sc zdotaccent.scuni2071uniA78FuniA7F7uniA7FBuniA7FCuniA7FDuniA7FEuniA7FFuni0528uni052Auni052Cuni052EuniA640uniA642uniA644uniA646uniA648uniA64AuniA64CuniA64EuniA650uniA652uniA654uniA656uniA658uniA65AuniA65CuniA65EuniA660uniA662uniA664uniA666uniA668uniA66AuniA66CuniA680uniA682uniA684uniA686uniA688uniA68AuniA68CuniA68EuniA690uniA692uniA694uniA696uniA698uniA69Auni0529uni052Buni052Duni052Funi1C80uni1C81uni1C82uni1C83uni1C84uni1C85uni1C86uni1C87uni1C88uniA641uniA643uniA645uniA647uniA649uniA64BuniA64DuniA64FuniA651uniA653uniA655uniA657uniA659uniA65BuniA65DuniA65FuniA661uniA663uniA665uniA667uniA669uniA66BuniA66DuniA681uniA683uniA685uniA687uniA689uniA68BuniA68DuniA68FuniA691uniA693uniA695uniA697uniA699uniA69Bafii10066.loclSRBuniA66EuniA67FuniA69CuniA69Duni0370uni0372uni0376uni03CFuni037Funi0371uni0373uni0377 uni03D0.altCfrakturHfrakturIfrakturRfrakturZfrakturuniA762uniA763uni212Cuni210Buni2110uni2112PiDoubleStruckuni211BTurnedFuni212B CDoubleStruck HDoubleStruck NDoubleStruck PDoubleStruck QDoubleStruck RDoubleStruck ZDoubleStruckItalicDDoubleStruckGammaDoubleStruckuni2107uni212Auni2130uni2131uni2133uniA796uniA798uniA79AuniA79CuniA79EItalicdDoubleStruckItaliceDoubleStruckItaliciDoubleStruckItalicjDoubleStruckgammaDoubleStruckpiDoubleStruckuni210Euni210FscriptescriptoscriptguniA794uniA795uniA797uniA799uniA79BuniA79DuniA79FuniAB30uniAB31uniAB32uniAB33uniAB34uniAB35uniAB36uniAB37uniAB38uniAB39uniAB3AuniAB3BuniAB3CuniAB3DuniAB3EuniAB3FuniAB40uniAB41uniAB42uniAB43uniAB44uniAB45uniAB46uniAB47uniAB48uniAB49uniAB4AuniAB4BuniAB4CuniAB4DuniAB4EuniAB4FuniAB50uniAB51uniAB52uniAB53uniAB54uniAB55uniAB56uniAB57uniAB58uniAB59uniAB5AuniAB64uni2095uni2096uni2097uni2098uni2099uni209Auni209Buni209CuniA770uniA7F8uniA7F9uniAB5CuniAB5DuniAB5EuniAB5Funi2E2Fzero.lfone.lftwo.lfthree.lffour.lffive.lfsix.lfseven.lfeight.lfnine.lfzero.osfone.osftwo.osf three.osffour.osffive.osfsix.osf seven.osf eight.osfnine.osf zero.slash zero.tosfone.tosftwo.tosf three.tosf four.tosf five.tosfsix.tosf seven.tosf eight.tosf nine.tosf zero.dnomone.dnomtwo.dnom three.dnom four.dnom five.dnomsix.dnom seven.dnom eight.dnom nine.dnom zero.numrone.numrtwo.numr three.numr four.numr five.numrsix.numr seven.numr eight.numr nine.numruni215Funi2189uni2155uni2156uni2157uni2158uni2159uni215Auni2150uni2151uni2152uni2080uni2081uni2082uni2083uni2084uni2085uni2086uni2087uni2088uni2089uni2042uni204Cuni204Duni2045uni2046caretuni2041uni2040uni2050uni2E36uni2E37uni205Cuni2E13uni2E16uni2E08downwardsancorauni2E0Euni2049uni2E2Duni2059uni2055uni2E10uni205Buni2058uni2027uni2043uni2E12uni2E18uni2054uni2E04uni2E1Cuni2E0Cuni2E02uni2E09uni2E20uni204Eonedotenleaderonedotovertwodotspunctuationuni2E19uni2E0Funi2047uni2048uni2E34uni2E33uni2E07uni2E06uni2E0Buni203Buni2E11reversedparagraphreversedquestionmarkuni204Funi2E01uni2E00uni2E05uni2E1Duni2E0Duni2E03uni2E0Auni2E21uni2E30squaredfourdotpunctuationuni2053uni2056uni2E1Euni2E1Funi2E1Buni204Auni2E39uni205Duni2E32uni2E38uni2E35uni2051twodotenleaderuni205Atwodotsoveronedotpunctuationuni203Funi2023uni2E3Cuni2E3Duni2E3Euni2E3Funi2E41uniA673 upwardsancorauni2E31uni208Duni208Ebrackhalfbottomleftbrackhalfbottomrightbrackhalftopleftbrackhalftoprightleftdoubleparenthesisrightdoubleparenthesisuni2E26uni2E27uni207Duni207Euni2E1Auni2010uni2011uni2E3Buni2E3Auni2E40uni2036uni2035uni2057uni2037uni2E42 braceleft.sc braceright.scbracketleft.scbracketright.sc exclam.sc exclamdbl.sc exclamdown.scguilsinglleft.scguilsinglright.sc parenleft.sc parenright.sc question.scquestiondown.scuniA92EuniA67Euni205Funi2028uni2029uni2061uni2064uni2063uni2062uni2066uni2067uni2068uni2069uni2060uni20B6uni20BCuni20BDuni20AAuni20B7uni20B8uni20BBuni20BEuni2127uni2135uni214Buni2136uni2052uni2138uni208Cuni207Cuni2137uni208Buni207Buni2031uni208Auni207AreversedSansSerifLsummationDoubleStruckturnedSansSerifGturnedSansSerifLturnedSansSerifYRotatedQuni2100uni2101uni2106uni2103uni2104uni213Buni2109uni2139uni203Duni2114uni2125uni214C prescriptionuni214Auni211Funi2108uni2120symbolforsamaritansourceuni2121uni2123 weierstrassuni02DEuni02E5_uni02E5_uni02E9uni02E5_uni02E5_uni02E6uni02E5_uni02E5_uni02E8uni02E5_uni02E5_uni02E7uni02E5_uni02E9uni02E5_uni02E9_uni02E5uni02E5_uni02E9_uni02E9uni02E5_uni02E9_uni02E6uni02E5_uni02E9_uni02E8uni02E5_uni02E9_uni02E7uni02E5_uni02E6uni02E5_uni02E6_uni02E5uni02E5_uni02E6_uni02E9uni02E5_uni02E6_uni02E6uni02E5_uni02E6_uni02E8uni02E5_uni02E6_uni02E7uni02E5_uni02E8uni02E5_uni02E8_uni02E5uni02E5_uni02E8_uni02E9uni02E5_uni02E8_uni02E6uni02E5_uni02E8_uni02E8uni02E5_uni02E8_uni02E7uni02E5_uni02E7uni02E5_uni02E7_uni02E5uni02E5_uni02E7_uni02E9uni02E5_uni02E7_uni02E6uni02E5_uni02E7_uni02E8uni02E5_uni02E7_uni02E7uni02E9_uni02E5uni02E9_uni02E5_uni02E5uni02E9_uni02E5_uni02E9uni02E9_uni02E5_uni02E6uni02E9_uni02E5_uni02E8uni02E9_uni02E5_uni02E7uni02E9_uni02E9_uni02E5uni02E9_uni02E9_uni02E6uni02E9_uni02E9_uni02E8uni02E9_uni02E9_uni02E7uni02E9_uni02E6uni02E9_uni02E6_uni02E5uni02E9_uni02E6_uni02E9uni02E9_uni02E6_uni02E6uni02E9_uni02E6_uni02E8uni02E9_uni02E6_uni02E7uni02E9_uni02E8uni02E9_uni02E8_uni02E5uni02E9_uni02E8_uni02E9uni02E9_uni02E8_uni02E6uni02E9_uni02E8_uni02E8uni02E9_uni02E8_uni02E7uni02E9_uni02E7uni02E9_uni02E7_uni02E5uni02E9_uni02E7_uni02E9uni02E9_uni02E7_uni02E6uni02E9_uni02E7_uni02E8uni02E9_uni02E7_uni02E7uni02E6_uni02E5uni02E6_uni02E5_uni02E5uni02E6_uni02E5_uni02E9uni02E6_uni02E5_uni02E6uni02E6_uni02E5_uni02E8uni02E6_uni02E5_uni02E7uni02E6_uni02E9uni02E6_uni02E9_uni02E5uni02E6_uni02E9_uni02E9uni02E6_uni02E9_uni02E6uni02E6_uni02E9_uni02E8uni02E6_uni02E9_uni02E7uni02E6_uni02E6_uni02E5uni02E6_uni02E6_uni02E9uni02E6_uni02E6_uni02E8uni02E6_uni02E6_uni02E7uni02E6_uni02E8uni02E6_uni02E8_uni02E5uni02E6_uni02E8_uni02E9uni02E6_uni02E8_uni02E6uni02E6_uni02E8_uni02E8uni02E6_uni02E8_uni02E7uni02E6_uni02E7uni02E6_uni02E7_uni02E5uni02E6_uni02E7_uni02E9uni02E6_uni02E7_uni02E6uni02E6_uni02E7_uni02E8uni02E6_uni02E7_uni02E7uni02E8_uni02E5uni02E8_uni02E5_uni02E5uni02E8_uni02E5_uni02E9uni02E8_uni02E5_uni02E6uni02E8_uni02E5_uni02E8uni02E8_uni02E5_uni02E7uni02E8_uni02E9uni02E8_uni02E9_uni02E5uni02E8_uni02E9_uni02E9uni02E8_uni02E9_uni02E6uni02E8_uni02E9_uni02E8uni02E8_uni02E9_uni02E7uni02E8_uni02E6uni02E8_uni02E6_uni02E5uni02E8_uni02E6_uni02E9uni02E8_uni02E6_uni02E6uni02E8_uni02E6_uni02E8uni02E8_uni02E6_uni02E7uni02E8_uni02E8_uni02E5uni02E8_uni02E8_uni02E9uni02E8_uni02E8_uni02E6uni02E8_uni02E8_uni02E7uni02E8_uni02E7uni02E8_uni02E7_uni02E5uni02E8_uni02E7_uni02E9uni02E8_uni02E7_uni02E6uni02E8_uni02E7_uni02E8uni02E8_uni02E7_uni02E7uni02E7_uni02E5uni02E7_uni02E5_uni02E5uni02E7_uni02E5_uni02E9uni02E7_uni02E5_uni02E6uni02E7_uni02E5_uni02E8uni02E7_uni02E5_uni02E7uni02E7_uni02E9uni02E7_uni02E9_uni02E5uni02E7_uni02E9_uni02E9uni02E7_uni02E9_uni02E6uni02E7_uni02E9_uni02E8uni02E7_uni02E9_uni02E7uni02E7_uni02E6uni02E7_uni02E6_uni02E5uni02E7_uni02E6_uni02E9uni02E7_uni02E6_uni02E6uni02E7_uni02E6_uni02E8uni02E7_uni02E6_uni02E7uni02E7_uni02E8uni02E7_uni02E8_uni02E5uni02E7_uni02E8_uni02E9uni02E7_uni02E8_uni02E6uni02E7_uni02E8_uni02E8uni02E7_uni02E8_uni02E7uni02E7_uni02E7_uni02E5uni02E7_uni02E7_uni02E9uni02E7_uni02E7_uni02E6uni02E7_uni02E7_uni02E8uniAB5B ampersand.scuni2129uni0308uni0307 gravecomb acutecombuni030Buni0302uni030Cuni0306uni030A tildecombuni0304 overlinecmbuni030Duni030Euni030Funi0310uni0311uni0312uni0313uni0314uni0315uni0316uni0317uni0318uni0319uni031Auni031Buni031Cuni031Duni031Euni031Funi0320hookpalatalizedbelowcombhookretroflexbelowcomb dotbelowcombuni0324uni0325uni0327uni0328uni0329uni032Auni032Buni032Cuni032Duni032Euni032Funi0330uni0331uni0332uni0333uni0334strokeshortoverlaycombstrokelongoverlaycombslashshortoverlaycombslashlongoverlaycombuni0339uni033Auni033Buni033Cuni033Duni033Euni033Funi0340uni0341uni0346uni0347uni0348uni0349uni034Auni034Buni034Cuni034Duni034Euni034Funi0350uni0351uni0352uni0353uni0354uni0355uni0356uni0357uni0358uni0359uni035Auni035Buni035Cuni035Duni035Euni035Funi0360uni0361uni0362acutegraveacute acutemacronuni1DFEuni1DFF brevemacroncombiningconjoiningmacroncombiningmacronlefthalfcombiningmacronrighthalf dottedacute dottedgravedoublecircumflexabovegraveacutegrave gravemacronuni1DD0uni1DC4 macronbreveuni1DC6 ogonekabove snakebelowsuspensionmarkuni1AB0uni1AB1uni1AB2uni1AB3uni1AB4uni1AB5uni1AB6uni1AB7uni1AB8uni1AB9uni1ABAuni1ABBuni1ABCuni1ABDuni1DE7uni1DE8uni1DE9uni1DEAuni1DEBuni1DECuni1DEDuni1DEEuni1DEFuni1DF0uni1DF1uni1DF2uni1DF3uni1DF4uni1DF5uni1DFBuni1DFCuni1DFDuni2DE0uni2DE1uni2DE2uni2DE3uni2DE4uni2DE5uni2DE6uni2DE7uni2DE8uni2DE9uni2DEAuni2DEBuni2DECuni2DEDuni2DEEuni2DEFuni2DF0uni2DF1uni2DF2uni2DF3uni2DF4uni2DF5uni2DF6uni2DF7uni2DF8uni2DF9uni2DFAuni2DFBuni2DFCuni2DFDuni2DFEuni2DFFuniFE00uniFE27uniFE28uniFE29uniFE2AuniFE2BuniFE2CuniFE2Duraboveusabove zigzagbelowuni1ABEdieresiscomb.scdotaccentcomb.sc gravecomb.sc acutecomb.schungarumlautcomb.sccircumflexcomb.sc caroncomb.sc brevecomb.sc ringcomb.sc tildecomb.sc macroncomb.sc ogonekcomb.sc overscore.scuni0342uni0343uni0344uni0345uni0483uni0484uni0485uni0486uni0487uniA66FuniA674uniA675uniA676uniA677uniA678uniA679uniA67AuniA67BuniA67CuniA67DuniA69EuniA69FuniFE2EuniFE2FuniA670uniA671uniA672uni1DDBuni1DDEuni1DDFuni1DE1uni1DE2uni0363uni1DD4uni1DD5uni1DD6uni1DD7uni0368uni0369uni0364uni1DD9flattenedopenaaboveuni1DDAuni036Auni0365uni1DD8uni1DDCuni1DDDuni1DE5uni036Buni1DE0uni0366uni1DCAuni036Cuni1DE3uni1DE4uni036Duni0367uni036Euni036Funi1DE6uni2C7D commaaccent2uni2C70uni2C7Euni2C7FuniAB65uniA7AEuniAB60uniAB61uniAB62uniAB63summationDoubleStruck.miruni20BFuni2E43uni2E44uniA700uniA701uniA702uniA703uniA704uniA705uniA706uniA707uniA708uniA709uniA70AuniA70BuniA70CuniA70DuniA70EuniA70FuniA710uniA711uniA712uniA713uniA714uniA715uniA716[[II-"-[[II-"-\\KK<-E-\\KK<<-<D-HH>>h-h-HH>>--[[II-"-;;,,*~a-2va-;;,,a-g-۰, UXEY KQKSZX4(Y`f UX%acc#b!!YC#DC`B-, `f-, d P&Z( CEcEEX!%YR[X!#!X PPX!@Y 8PX!8YY  CEcEad(PX! CEcE 0PX!0Y PX f a PX` PX! ` 6PX!6``YYY% CcRXK PX! CKPX!Kac CcbYYdaY+YY#PXeYY-, E %ad CPX#B#B!!Y`-,#!#! dbB #BEX CEc C `Ec*! C +0%&QX`PaRYX#Y!Y @SX+!@Y#PXeY-,C+C`B-,#B# #Babfc`*-, E Ccb PX@`Yfc`D`-, CEB*!C`B- ,C#DC`B- , E +#C%` E#a d PX!0PX @YY#PXeY%#aDD`- , E +#C%` E#a d$PX@Y#PXeY%#aDD`- , #B EX!#!Y*!- ,EdaD-,` CJPX #BYCJRX #BY-, bfc c#aC` ` #B#-,KTXdDY$ e#x-,KQXKSXdDY!Y$e#x-,CUXCaB+YC%B %B%B# %PXC`%B #a*!#a #a*!C`%B%a*!Y CGCG`b PX@`Yfc Ccb PX@`Yfc`#DC>C`B-,ETX#B E #B # `B `aBB`++"Y-,+-,+-,+-,+-,+-,+-,+-,+-,+-, +-),# bfc`KTX# .]!!Y-*,# bfc`KTX# .q!!Y-+,# bfc&`KTX# .r!!Y-, +ETX#B E #B # `B `aBB`++"Y-,+- ,+-!,+-",+-#,+-$,+-%,+-&,+-',+-(, +-,, <`--, `` C#`C%a`,*!-.,-+-*-/, G Ccb PX@`Yfc`#a8# UX G Ccb PX@`Yfc`#a8!Y-0,ETX EB/*EX0Y"Y-1, +ETX EB/*EX0Y"Y-2, 5`-3, EBEcb PX@`Yfc+ Ccb PX@`Yfc+D>#82*!-4, < G Ccb PX@`Yfc`Ca8-5,.<-6, < G Ccb PX@`Yfc`CaCc8-7,% . G#B%IG#G#a Xb!Y#B6*-8,#B%%G#G#a B C+e.# <8-9,#B%% .G#G#a #B B C+ `PX @QX  &YBB# C #G#G#a#F`Cb PX@`Yfc` + a C`d#CadPXCaC`Y%b PX@`Yfca# &#Fa8#CF%CG#G#a` Cb PX@`Yfc`# +#C`+%a%b PX@`Yfc&a %`d#%`dPX!#!Y# &#Fa8Y-:,#B & .G#G#a#<8-;,#B #B F#G+#a8-<,#B%%G#G#aTX. <#!%%G#G#a %%G#G#a%%I%acc# Xb!Ycb PX@`Yfc`#.# <8#!Y-=,#B C .G#G#a ` `fb PX@`Yfc# <8->,# .F%FCXPRYX +-o,:+?+-p,:+@+-q,:+>+-r,:+?+-s,:+@+-t,;+..+-u,;+>+-v,;+?+-w,;+@+-x,;+>+-y,;+?+-z,;+@+-{,<+..+-|,<+>+-},<+?+-~,<+@+-,<+>+-,<+?+-,<+@+-,=+..+-,=+>+-,=+?+-,=+@+-,=+>+-,=+?+-,=+@+-, EX!#!YB+e$PxEX0Y-KRXYcpB@ o_C/ *B@vfVJ6$ *B@{n^P@- *BA *@%@ @ @ *BA @@@@@@@@@ *D$QX@XdD(QXXDY'QX@cTXDYYYYY@xhXL8& *DdDD ttfautohint version = 1.7 adjust-subglyphs = 0 default-script = latn dw-cleartype-strong-stem-width = 0 fallback-scaling = 0 fallback-script = none fallback-stem-width = 0 gdi-cleartype-strong-stem-width = 1 gray-strong-stem-width = 0 hinting-limit = 200 hinting-range-max = 50 hinting-range-min = 8 hint-composites = 0 ignore-restrictions = 0 increase-x-height = 14 reference = reference-index = 0 symbol = 0 TTFA-info = 1 windows-compatibility = 1 x-height-snapping-exceptions = control-instructions = linphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/NotoSansUI-Bold.ttf000066400000000000000000011364641434616504300304670ustar00rootroot00000000000000GDEF5( GPOS  GSUBF # `OS/2V~`cmapO'hcvt ''8fpgm6  . gasp# glyf|J_Xzheadw[z6hhea$ 7T$hmtxU%locaNf9%maxp x nameC postif prep#T5\rW"_< 4(   m  o pRT_\K33f GOOG X ^ Ju+-X5?R!R=\?XH?=HuNJyNN#dH7HBHuH?XXX-f`w{dwBhP^w^wHh^) 3V1N 3/JLV\\\)\Bqq}qB\\\y/B 7'h'RXJuRqhjd/RX=dm\X/;LH=qHu\9R . . Z=w{{{{*B9/^w^w^w^w^w^w    VVVVVVVV\\\\\qqqq\B\\\\\X\BBBBVVVw\w\w\w\\/1\{\{\{\{\{\w\w\w\w\BBq?qqBq^BqBhq}PqqcqmqBBB;B^w\^w\^w\w\HHcHSh^\h^\h^\h^\)y/)y/)y/ B B B B B B171717VVV^w\h^\qT Hu B`}D9{1^wBP3R^wN)\VmJ79-\qNBB)-\H\qN\B\BH\Fy\9\N))V\mB )F)m{q)}jwh^B9hq)`N`} {/^`^ww)N\V?m?NHRV\P\qN)L\7\m/\ ?{J\B1\\qqq}B`RRRJH??!{!qbu ??!RRu wbf#R#B\?))9/#J7f : ; Z C;D9 )Xd%qLXXVXX))hq}^^N T;-VyHTTq}) 3V xXhX))wo\{\wbLf 7o) )qm^w\ w )\wo\w\wjw1\hu))Tj/y/;/^qN RP% 7w?\w\)f/ q)T/om{m?{TBJLm?{JhBVVVV{v\XX/^qN99^w\^w\^w\NHJNNNm?{}?y/) V \\h\b\N9JND{XfwN\)//XqNVVVVVVVVVVV{\{\{\{\{{\{\{\BquBq^w\^w\^w\^w^w\^w\^w\wo\wo\wo\wo\wo\ B B1\- -11111 V)y/Z JHw\/ \\\{y/Xdw397PHB^w wo\ HhVNNy/ y/)J73179PPb79qNJ!!!fJu y \+ 'Vq^w\ B B B B BXVVVVw\w\P^w\^w\99 y \w\bhBVV{P\{\sq q^w\^w\HqH B B/^B\!m\17V{\^w\^w\^w\^w\{N)/\\w\/)\7` 3{\hq}3w\H\?f\\\XX\qNqNN\q}\\\BBBqB){=qBB\\^\+++//+\qqqqFyBy/B/3\7791\^wN\LqF\1 \\\//\/)mJBB``' 1-'T+T+!H,H,HH#+oo#++oo++T+T+++++1-dj+DDXXXXXXXL++oHu)))NVw[CNw====LLF)))9U6}NNwwc!YaV V5}/}V;cUmBYBF%T=TT'w=??BB???))?\?R?Z+ \)^w\w\m'h\f\\wV'1D9P9w\Bm )^\q}^w1\JwHwHu^w\}VXZ'yFDVqJT+'V3y)H?\?\?%Xu\\1oo+7NDN;b)VDd91m-uwm\qT'sqbmu'%ZhbhhbhXoHfh9Fhh1 `wfh wh\B/}y1/qBD/\)\qqBq\ 7V\\\qNqNXq?qBqN5^Z^RHHqB^uLFLF=^^^1#)RRRL-^^5u99JT```w\\\\\\{\{\{\{\{\d)w\BBBBqBq?qPPPqqqqBBBB^w\^w\^w\^w\HHHHh^\h^\h^\h^\h^\)y/)y/)y/)y/ B B B B B33V V 171717By/V-\-\-\-\-\-\-\-\{{qNqNqNqNqNqNNN{{BBBBBBBBHHBBBBB,BBBJ={{jj\\\\\\dd))))))))Lmmmmmmmmqq)-\-\qNqNBBB;B\\))mm-\-\-\-\-\-\-\-\     BBBBBBBB 3 3   ` ` X Xmmmmmmmm ; P   V j -\-\-\-\-\-\-\==BBBBBRF `BBBBBB?);))))yy));;ummmmm 7=!LRhNmHu)3+Zhh'b+JJT))w3wd? = : ? wHudX#f{mmb))s+jVFR?;?;fBhfqHVy/Z173wV\!)))HbfJy)} VVVVVNVRVNVNVFFV5V5VPV-VH-V%V%V%V'V/%VVV5V5V/)VPVLVLVLV^LVVVVVPVLVFVLVLLV/V9V?V?V??V5V5V5V5V55VLVLVLVLVLhVLVFVLVLVLLVVVVVVVVVXVVVV\V7V7V7V7V77VHVFVFVFVFFVVV9V9V99VVVVVLVVVVVLVLVLVLVPPV/V5V5VV)V/V'V%V%V%%VHV-VPV5V5-VFVNVNVRVNFVVVV-\-\-\-\-\-\-\-\BBBBBBBB))))))))BBBB)))) 93F3F3F3FdhR=R=RVRVRJRJRJ9# =` 9P uLXX# \`` ~ac67"#ou~  OP\_'=?EMWY[]}  " & / 0 4 : < > D ^ p y  !!!!"!&!.!N!T!^!!!"""""""")"+"H"a"e###!%%% %%%%%$%,%4%<%l%%%%%%%%%%%%%%%&<&@&B&`&c&f&k&o),m,w.!# bd78#$tz PQ]`>@ HPY[]_  & * 0 2 9 < > D ^ j t  !!!!"!&!.!M!S![!!!"""""""")"+"H"`"d### %%% %%%%%$%,%4%<%P%%%%%%%%%%%%%%%&:&@&B&`&c&e&j&o),`,q. ^IvedAeY @SQzKIzx?wZI we%" {ob`aa` kLP>:6&$&8F0(.@@<"$ IJ$%KLM`abVPQWXYRh X Y Z [ \ ]STUV     ;<=>r i,-03PQ45Z[@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! , `E% Fa#E#aH-, EhD-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,CRX!!!!!F#F`F# F`ab# # pE` PXaFY`h:Y-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -, %EPX ED!!EDY-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-, T#T[XCPCT[X!!!!H+YCPCT[XH+!!!!YY-, T#T[XCPCT[X!!!I+YCPCT[XI+!!!YY-, #KSKQZX#8!!Y-,%%Ij SX@`8!!Y-,%%Ij QX@a8!!Y-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZXB TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY&QX@cTX@C`BY@cTXC`BYYYYYYCTXBY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,CPCT[X!# Y-,Y+-,-@ [PZU?ZOZZXUYPXU0X@XPXXWPVU V@VPVVVTUUPTUpTT0T@TTTT0MMNUOPNU3NNKUJPIUIKUGdFU?FFFKULPKUKK?KKSPRU;RRPUQPPU@% F!3 U     U3U?~]}xxwsAvsAus#ts+swsss3U3Uyvz9qqqppwnnli#ki+ji6fiviigi3UU3U?yEńEUZjF_{{{rrhZgjggF fffYfifff eeied]@ZYcycc'cOb_bbba]3`_P_]"^]<]]dU3UU3U0UdUoTS++KRK P[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYss++ss++++s++++ssst+stst+ssss+tusstss^st+++sst+++stsstssss+++stt++++ss+st++ssst++++s+st++s++++s++sstt++st+s++s+^<u^{VT3 `F5 + T?4HHHH <`$`Z D 8 h  j HtB>22r0hX..H*n !!"#\#$%%& &&'^'(V()* *+:++,,-t-../l0001(123`3456 67P7789"9::;\;<,<<=H=>>>>? ?@@0@^@@ABbBBBCCFCvCCDDEEJExEEFFGRGGGHH>HIIIJ$JJJrJKKLL:L^LLLLM NNBNhNNNNOPPPQQ:Q^RR4RbRRRSS,SZS~SSST$TRTvTTTUUVV>VbVVVWW0WTWWWWX&XLXrXXXYZZZ[[<[j[[[\ \:\b\\\] ]0]]^^8^`^^^_ __``>`f```aab.b\bbbcc,d0eReeeef$fJfxfffgg>glggghh:hiniiijjBjfjjjkkDklkkkllDlrlllmm@mn6oDpppq"qFqlqqr@rxrs s~st^tuuuuv@vvww\wwwwx2xBxRxbxryJyZyjyyyzzzz{x{{|p|} }~ ~<~`~~~~l& :Z@`"Jr"v4DH88 j|JBl| `X`p*:zLlpBf`<bHzD|L^<f tjúDr2Ţhƶ`Ȝ@ɪvʠnˬ,̲B͈VVVVVVVVVVVVVVӈZԈԮՖHֆׄF٢ٴ8hژܢ|DH$|vN 4 > "z&LR\.N,HNvBz"& B   \ v   B$Bdf2@VxDd !@!!"("L"~""""##$$$%%@%r%&<&''2'd'''()))**B*p***++D+v++,Z,,,-.\//0~01123p4@456b6789@9:n:;;<=8=^===> >6>h>>>@@B@@@AADAnAACC,CjCCCDDDDrDDDE"EJE|EGG(GhGGGHHBHhHHHIIBIvIIIK<KdKKL L0L`LLLMM,MRMxMMMNNFNlNNNOODOhOOOPP2PXPPPQ0QRRSVSTRTUUVHVnVWdXXYLYZ[[\j\z]"]^~_B__`azbZccddejfftglh hhhi|j:jkl|m(mmno:opTpqzrrzssttuLuvwx>xyz&zVzp{{&{d{{{||F|l|||}}>}d}}}~ ~@~j~~*bH~6^lp$Jr>dp Nv*\<j FzDf@v(Px|J4\FV(P X ZB:tH`F^HZ.j0><L@|tf²Ú2z&ƘZ ȐVʊj6͐͠ΰ$ϚЎдѸҌҜp*ՠF.bٮښ|ܰݨd߸F  hRb t0&VhzH<P0t6nR"4 R|Rdv .@Rx8 >vV F`r^0h@hH|BT ndfn & T   P  J   F   `B>NNLfZ2h^ $ !"x##$$$$$$$%%%&&&''0'''''(\)2*z+d+,F--../L/0B01,12$23$35 56<6778<889n:v;\;<8<=N=>>?(?r?@@@ABrCBCD&DE0EFFdFGGGHbHIzIJnJKKL8LMhNvOOP6PQR@RSFSTVTUNUVVnVWnWXHXY4YZZ[\L\]J]]]]^^^^_`abd:e:fJg(ghijklklmnVnopqtrHs&stuvvwxpyyz{:{|z}~~RB.HdV<RrH ~BV 6 j"(xP8zjlTT.^F6\d DlF4b:`LNx$\$df <dh @j*X~.V|0f >fBp(`0lF$P~DlBp6l*Pvf*Px.`Bh*Z&ZDj’&ZÎBfČĴ.VŀŰH|ư,RzǠ@pȢ:`Ɉɰ$Jrʢ Hn˖˾8`̺̊R͆ͺ:`΄Ϊ>fόϰ HЀк.fўTҘ$jӰPԊ>xղ(lְ@׈$xز*h٤Zڞ$h۬>܌ܰ 2hݎ"N|ޤXhxTN~6:(Nx N~0v<d@n6T|DrB\Jt$nnX6z*"f8lB4<Z B     v  2     0 @ x N>.f^:F p0XvF>"zBj2`#*011P1~112 2P2~23,5v56"6708Z9D:V;<<=p=>(>?N@2@ABJC"CEEEFGGHHI6IJ`K KL^LMxMN<NO O>OOPVPPQ QBQpQRBRpRS6SST&TnTUUpUVV^VVWFWWX(XxXYYlYYZFZZ[@[[\"\x\]$]f]]^D^^_<_~_``r`a a`aabDbbc.ccddfde e`eef<ffg.gghhVhhiPiij2jjk$krklllllmFmmnFnnoohoppbppq@qqr6r~rssdsttLttu>uuv,vvw wbwwxPxxy8yyz(z|z{{V{{|L||}.}~}~~f~~H@j<d,T|*T~&Pz2vB  >V,$t.bp8z 8Ndz @  ?2/3993310!!7!!IhyJhuC@'     }Y ??+9/_^]]q993310#!4632#"&3ZZVS[\RT\TVXRO[YB@  ?32993310#!#))))-@J   !?O 0 @   ?3?39/]333339/]333339333333333333310!!####5!7#5!333337#/MNLJ/!MMNN/LjjggXD &,{@F'% *!!-.$ **%  tY +sY P?/]]q33+3/_^]33+393339333333310#5&''.546753&'4&'65DωW`Cƥˉ^åMDDn=DɟQ+B6N R@Kng*:,9? "-M@* )#)./ +0+@+ + + &&?3?3??99//3]393333103254#"#"&5!2%#3254#"#"&5!2;-2``2-Y+-2``2-Y}{}J}{}R&1}@B3#'- / $''$ 23#/ $#!*lY!iY?+?+?9/9999939/9/933333333310)'#"$5467.5463267!%3274&#">syKDúߊG4>$~Pe~e:CgH9CM_V\q࿉TV]wYucVf=J,`5=@;Xj0]?9310#))Ry @   $??99331073#&R1 ή27=d @  $??993310#654'3d1:1?V0@     ?9/3933310% '%7)u!㜉'm)hy9w)hpX9 J@.    eJ9/o/]q]]]]33333933310!5!3!!#}}dz?$@@ H/+]]9910%#7!4|A$ =V"@O/]q399105!=u9 @  }Y ?+991074632#"&uZVS[\RT\TVXRO[YD??9910 !D!JJH (@  sY sY?+?+993310!"!2326&#"H5]nl`akm^|so  yN &@   ??9/99933310)?'3NMNMwNP6@ vYvY?+9?+99333310)5>54&#"'>32!Pod,aQUWlhtG}sn;XVNH\L)dteNB&@O"  '(tYE:*" I  $$sY$ sY ?+?+9/_^]^]q]+]q]q+993333310!"'32654&+532654#"'6!2UdoqH[o$O+6hsgVYl0;Ր#q F@#  tY O??9/]33+399933333310#!!5!3!5467#q" %4//i>RNkd5=@sYvY sY?+?+9/+993333102!"'3 54!"'!!76fO^5({7#=O *5 BHP$B@! %& tYtYsY?+?+9/+99933310!2&#"3632#"&2654&#"Hon}GYWd ccjcd^}m` {k{zQw7P'@vY?+9?93103!!%/ HJ".L@'&, ,/0!!)) #uY uY ?+?+93999333333102#"$5467.54$32654&'">54&J|}nxhsrqOaMebNdɿpEXr̻}JOkV`cQCuBbQD<_2.`?EPBJ%B@" #&'  tYsYtY?+?+9/+9933310!"'532>7##"&5432%"32654&JCT\j :r `lbd^}FPV[ë^L|j|{Pwus (@ }Y }Y ?+?+99331074632#"&4632#"&uZVS[\RT\ZVS[]QT\TVXRO[YTVXRQYX?s8@ @ H }Y ?+/+_^]]9310%#74632#"&4|A$/ZVS[]QT\ TVXRQYXX9&@o/]]/993310%5 9TX9:@# G P`WH?_/]3]]]2]9933105!5!X'{X9&@o/]]/993310 5XT=J%J@,  &' ##}Y# ~Y?+?+9/_^]]933105467>54&#"'6324632#"&RmiC`Vmd`3ZVS[\RT\J`PK^:ADb}ƥndGJ<2 88 +2%+%?3?39/3339/]3933333310#"&'##"&543232654.#"!2$7! 4$!2326?&#"\oJs1V QCL@LԞ'psWZY_ .E}ݐG:D?tg610&RB)=9Jb)y%@ iY?+3?9310)!!!sPs^%@  iY?+?3993310# 5!3265^5N!}3 @   ??393310!!!67991 @JM(\"@    ?3?3393310)&'!!>7!>7!H 50 11+%* ,1),63ݢ9B37QNHV 4@    ?3?39993333310) ! ! !V:V;5N5)+"@  ??39932310 !!!1NP\Z/1q 8@ iYiY?+9?+993310)5!!!qVDs"@ $?3?3933310!!#3s B??9910 !!!J3 @ $?3?39333103#5!!3qT/d@ ?2999103# /J}NH/33310!5!NRL!@  _/]9910.'5!?DV?,BeV;u"W@0 ""#$ gYO _    `Y_Y?+?+_^]?9/]_]+999333310!'##"&546?54#"'6323265f;M®evjaK 1QeXZze8@   !  ]Y]Y?+?+??999933102#"'##!36"32654&p31 kpqhkt^opsЏ{E!\s&@  ]Y]Y?+?+9310 !2.#"3267f šZH|>XKJ=-L%/2/$\q<@  !  ]Y]Y?+?+??9999333310"323&5!#'#'26754&#"o 2; hjumo}fqr23}bf쑥!\bs`@6   fYL\   _Y `Y?+?+_^]9/_]]]+_^]99333310"!. 32!3267oanr6/ebP{qq{R*3*.(')uA@  ]Y `Y?+33?+?933333310!!#5754632&#"! ϨϞ{N\NA:yyRR/M1GO^} <@   cY ]Y?+??+_^]_]9333210"'53265!432#"FuTFIMG1pSSVT)kGO7@     ?3??93993333107! !!!Ņ9XD1`TiJ@ ??9310)!1Bs#>@  $%    ]Y?3+3?33?99933310)4&#"!33>323>32!4&#"QWuj)-nY-nQWpoyy^MWNV'yys.@     ]Y?+?3?9993310)4&#"!33>32V^r)3ryy^QS\s (@  ]Y]Y?+?+99331032654&#"!"&5!2m{zkl{zl1̍0s<@  !  ]Y ]Y?+?+??999933310"'#!33632"324&p+kiqhkte;J!R\qs D@# !" ]Y ^Y ?+?+??9/39/9333310%26754&#""3237!!47#otlo{kj< 1ۅ%14PT=kQTws'@  dY  ???+9993102&#"!33>>)%5-4s ^^s\s%:@  &' _Y _Y?+?+999333310#"&'53254.'.54632.#"zKUQ,lZy7ʿ\TLWz:L! (6`$-9&6\wWX$.I)<;5\x/7L>@   @`Y]Y?+?+3399333310%27#"&5#5?3!!wPprX9I#3fA>^.@  ]Y?+??3993310!'##"&5!3265!)1s1V^r1NUsyy^ @   ??393310!!367!V?! '?V^h}qt}^"@ ?3?3399910! #!!36?!3>7!7Vr 0'P # (+^XLU7:Y3 ^ /@      ?3?3999333310 !! ! !ZZ};#d^)@   ]Y ?+?393992310!367!#"'5326?N  G'AOL7AQy"^Rpg[u cd77^ 5@ ^Y^Y?+9?+99310)5!5!!B QT@/  !onK: $?3?39/]]]q39993333333104ᒑ4>3".5}}RcKLbSaRQa>or5CHջ# #IB4rq/@ #??93103#!RR@.  ! onK:$?3?39/]]]q3999333333310#5>5475&54&'523"RcKJdS}}-pr5EF+# #+FD4rpaQRaX'9}N@ @# H?o?_ /]q33/+]39910"56323267#"&'&B7}6gIKb5~6eBxZC6m 7@9m%8u^D@( /_@H   }Y ?+/9/_^]+]q9933103!#"&546323^ZVS[]QT\^1%TVXRQYXI@) sY@sY?]2+?_^]2+9333310%$753.#"3267#3\ӲZH|>ytRd; A$-= RjP@*   xY sYvY?+?+9/3+39933333102&#"!!!!5>=#53546]sNTwgMR@YSۏN,rdq!'X@4"  "() P%o/]3]29933333333331047'76327'#"''7&732654&#"6[ji[55}_esT}6mPQoqONof_57Ynk\}}33{}]hMonNPnpn@?    yYp yYO  ??39/]3+3/_^]3+39333333310 !3#3#!5#535#53!H9<\Zݲ/(@ #??9/9/9333103#3#j,7W@. 2- -&22&895050 mY$mY?+?+9339333333331047&54632.#"#"'53254.'.7654&'y߶Rj{:QJ\y}>?˒QF%ZP}yNl~&4!^TT2"-/.I/EiP(iJO)9u'03"K@d19WE]-K (@ P   /]q3339933104632#"&%4632#"&K@BKLA@KQ}6^ZƬ֭+*/!K@- "#   0y h  ?3]]]29/]39333310'#"&546?4#"'>32%326=1+|Ju}cQBBcD. MYcn:@ujmm u= 2F&$SA$ R^ .@    /3/39933333310 % Rsr=wwwwX9?<@&eJ9/o/]q]]]]33/99310%#!5!9l=VdD&6e@: ' //78p#3+#?3?399//]3]39/3933333331032654&+###!24$32#"$732$54$#"fQYRZdVJ^^m+*լ֭PAIASysb^ZƬ֭+*.@@ H/+]q33310!5! \!@ ?33/39933104>32#"&732654&#"\\^\]]]ɿYBBZ[A@[q\^\\]ZǑ@Z\>?^\X9 F@&     eC9 /3/]]]]3333393333310!5!3!!#5!}}}{/J.@   ?3?3999333310!57>54&#"'632!yf90(Qc{^i`JdY2&(XuUu_;9%Z@4 &'{yh X# !?3?3]9/]]]]q39933333310#"'53254&+532654&#"'>32^h{XNp\SQ23/T9e>g7 nOyFZk5549&2&(/>L!@  _/]9910567!L?V4Ge42^9@   ]Y?+???39993333103265!#'##"&'!!X^~r1+*xX>h 1yyUU.,UJq'@ ?3/39/93310####"&563!>T\P3u)} @ }Y ?   /]+99104632#"&uZVS[]QT\TVXRQYX1@  v/?3]]9/39933210#"'53254'73ND[HNJXr>S=e\JH "@    ?3?9/993310#?'%3H0Nm-Jp_$*=9 '@ 0 ?3]2993310#"&5463232654&#"ᷟ#AHH??HHA\ŧŦdeeddccR^ ,@    /3/39933333310 ' 7' 7ss#;w\\w9;w\\w9.&{'; ?55.&{'t?5Z&u'; -?55=y^'N@-)"()/_ % ~Y%}Y%?+/+9/_^]]q9333103267#"&5467>=#"&54632Ylm9WYO`fbja_5(ZVS[]QT\^JbMNX?9J:*8EliFJ=;VTVXRQYXs&$CR@ %&+5+5s&$vR@ |%&+5+5s&$KVR@ %&+5+5`&$RVR@ %&+5+5V&$jVR@  %#&+55+55 !N@(#   "# iY??39/+299333332310!!!&54632&'4&#"26/jj+qm;%!6**7a*6Y;u\:Xl|$-33-]4%@U   iYF   !  L    I    iY jYiY?+?+3?9/+9/_^]_]+]]_]q_q]q+93333210)!!!!!!!!#%3z\`Nw&&z 7 %+5s&(CR&Ǵ %+5+5s&(v\R@ &k  %+5+5s&(KR@ & %+5+5V&(jR@ !&  %+55+55*s&,CR& %+5+5B.s&,vR@ &  %+5+5As&,K"R@ & %+5+59V&,j"R@ !&  %+55+55/u @W    iY*_oLL>  iY iY ?+?+9/_^]_]q_]_]q]_]_]qq]3+399333333103! )#%4&+3#3 /fe|bңRdT`&1RR@ & %+5+5ws&2CuR&%+5+5ws&2vFR@ &%+5+5ws&2KR@ #&#%+5+5w`&2RR@ &&%+5+5wV&2jR@ +&(%+55+55  ;@!   ?/_/]q9333333310 7   '՘-1-Ӗ-+ј-՘w"L@+  #$! iY !iY?+?+999333310!"''7&!27&#"4'3 ŋZZiQƒTX8Ti3 LhszAlmF}ht-u'^s&8C+R& %+5+5^s&8vR@ &t %+5+5^s&8KR@  & %+5+5^V&8jR@ (&% %+55+55s&<vR@ &n %+5+5 <@  iY  kY??9/+9/+99333310!#!!3232654&+6 Dd|54&#"!4$!2*@J@*5Bi3c<5@ RJ~bFFM>dt%&@aL:0*4([bzN"$2{)3<*HwQ@j17P.32!3273265"!.1HbŞYMcsBx-ĸOAq||ex#jeiuY TEBMe@A锂X'(W[zep|\s&Fz %%+5\b!&HC $ %+5\b!&Hvs ! %+5\b!&HK ") %+5\b&Hj !. %+55!&CO %+5!&vE y %+5!&K  %+5&j %+55\'w@A " ()]YaY_Y%_Y?+?339/+9/9+9/+9/93333310&'77#"54327&''4&#"326Y?endF@dzkyoxp{j;"CKhbzll&QR3% %+5\!&RC" %+5\!&Rv y %+5\!&RK  ' %+5\&RR * %+5\&Rj , %+55X9|@$   P `   @1 H /OoeJ9/o/]q]]]]33/]32/+]qr39333105!4632#"&4632#"&XJBBIJAAKJBCHJAAKdLKNIFRNKMQGFQN\#M@,  $%! ]Y !]Y?+?+999333310!"''7&!27&#"4'326~lCDt7:=+?zl &6zk1-eid04RlT^HQ<2!&XCx %+5!&Xv t %+5!&XK1 " %+5&Xj/' %+55!&\v= c %+5!A@" "#  ]Y ]Y ?+?+??999933310>32#"'#!!"324&2ih1qhkteQỦ>^;yxHN!R&\j ) %+55&$MXR@ %&+5+5V;&DM &%%+5}&$NVR@ %&+5+5V;+&DN )#%+5&$Q{!%+5VLu&DQ12%+5ws&&v R@ & %+5+5\!&FvV %+5ws&&KR@ $&r$ %+5+5\!&FK ?#%+5wf&&OR@ &n %+5+5\&FO; W%+5ws&&LR@ &t  %+5+5\!&FL P%+5us&'LhR&%+5+5\%&G8o \(( %+5/u\ 'k@:%%   )(  eY"]Y  ]Y?+??9/_^]+9/3+399933333310"5323&=!5!5!3##'#'26754&#"l ;2; hjunq{fqr$!e3ǡT}&(MR@ & %+5+5\b&HM   %+5}&(NR&  %+5+5\b+&HN  " %+5I&(O/5@ &  %+5+5\b&HO?  %+5&(Q5 %+5\(bs&HQ? '! %+5s&(LR@ &  %+5+5\b!&HL % %+5w's&*KR@ (&U!(%+5+5\q!&JK  -4&%+5w'}&*NR@ &[!%+5+5\q+&JN -'&%+5w'f&*OR@ "&f%+5+5\q&JOP "'+&%+5w;'&*9 R# %+5\q!&J: ,/&%+5fs&+KR& %+5+5&KK5#&# %+5+5@X    qY   iYF!L;  ?3?39/]]]]]_]q_q]q+_^]9/_]33+33993333333323103#!!!#535!!5!5!fʸ6C5w-ô_@3     eY ]Y   ?3?9/]+9/3+399333333310)4#"!#535!!!3632ϴsϜ1;fP/ǡS.`&,R"R@ & %+5+5&R %+5?&,M$R@ & %+5+5&M %+5}&,N$R@ &  %+5+5+&N  %+5B&,Q  %+5^&LQT 3 %+5Bf&,OTR&  %+5+5^@ ??9310)!1^BR &,-ߴ %+5)&LMJ %%+55hR s&-KR@  %&+5+5}!&7K %+5;P&.9%+5;&N9u %+5^<@      ?3?3939933333107! !!!ύ:EHƏ1FnZ^ۡR?s&/vR@ %&+5+5&Ovg@  % &+5+5;?&/9Jٴ %+5c;&O9%+5?&/8u # %+5?5&O8 %+5?&/O/p@0@pR %+]5&OO8 z%+5? @@!     @ iY?+?9/99933333103'7!7!Eq6uQ)oXĞX 3@     ??9/9933323107!'7!FtHq1+ph+p-s&1vDR@ &p%+5+5!&Qv r %+5;&19´%+5;s&Q9u۴ %+5s&1LR@ &%+5+5!&QLN  %+5'Q %+5R;@  kY"?+??399333310"'53267#!!35!rS]Irj {RYeN}P{wJs:@ ]Y]Y?+?+??99933310"'53254#"!33>32=kM;<{r)2tʼ۫^OUw&2MR@ &%+5+5\&RM  %+5w}&2NR@ &%+5+5\+&RN   %+5ws&2SBR@ (&m%%+55+55\!&RS{ [) %+55wP#@U  $%iYF!L I  iY iY  iY iY?+?+?+?+9/_^]_]+]]_]q_q]q+9333310)# !2!!!!!"3267.P&-S>=#d3Az&# ik \{s+2@I &00&34 /fY/i/ //// ,_Y )]Y `Y#]Y?3++?+?+9/_^]]]+_^]999333333310 '#"&5!2632!326732654&#"%"!.pGkdQfm{zkl{zl^| u-OM*.'(Esyo}Hs&5vR@ L %&+5+5!&Uv g %+5;H&59 %+5c;ws&U9 %+5Hs&5L3R@  %&+5+5S!&UL %+5^s&6vNR@ {(,%0&+5+5\!&Vv r'+%+5^s&6KR@ -5%5&+5+5\!&VK  ,3%+5^&6zb.(%+5\s&Vz-,&%+5^s&6LR@ 0(%-&+5+5\!&VL .'%+5);y&79)  %+5/;7L&W9%+5)ys&7LR@  & %+5+5/(&W8@@ Hz%+5+5)yE@#  iY  iY?+3?9/3+393333310)#53!!!3#sPsTb/7L\@.      eY `Y]Y?+?+333/9/3+3993333333310%27#"&=#535#5?3!!!!wPp4IX9I#fƔA>^`&8RR@ &# %+5+5&XR1 % %+5^&8MR@ & %+5+5&XM/ %+5^}&8NR@ & %+5+5+&XN1  %+5^&8PR@& %+55+]55&XPN %+55^s&8SR@ %&q" %+55+55!&XS x$ %+55^&8QH  %+5^&XQ#$%+5s&:KqR@ +&$+ %+5+5!&ZK ")%+5s&<KR@ &%+5+5!&\K $ %+5V&<jR@ & %+55+551qs&=vNR@ &i  %+5+57!&]v   %+51qf&=OR&  %+5+57&]O  %+51qs&=LR@   %&+5+57!&]L  %+5? !@  ]Y?+?93310"!4632&Pϼ͞xG\-\//L@&    `Y]Y]Y?+?+9/+339933333310#"'5325#5754632&#"3#鼰kM;;}–pHR?myqRR/F!-p@D/ ( "" ./@H@ HiY?o %*?3/]29/]q+++9/9333332310!!!&54632 &'5>7!4&#"3>/jl+pm1fVDpL.jVl6**7V&2U>J#:Wn-!EG*x t7-33-\3V; ",8D@U'?3-9",,93EF/<6 6B0@ H0 #gYO_  `Y)_Y?+?+_^]?9/]_]+9+2_^]2]]9333333105>7!'##"&546?54#"'6323265#"&546324&#"326.jV;M®evjeppqn6**710*6*x iD'aK 1QeXZzelnli-33--44%s&vRw@ %&+5+5V!&v _9>%+5ws&v1R@ +&n$( %+5+5\!&v{ m%) %+5^;&69+,%+5\;s&V9δ.+%+5! @ _/]399910&'#567!TMN˽CeH]SQ_p4F!@  _/]299910!.'5367Tlw˓RT!T2/{]SWY@G_/]3]9910!!Z+ *@ F V f  _/]33]/]39910#"&'3327 ܦ/UU+/6} /39310432#"SSGOTJ &@   _/]32993310#"&546324&#"326Jppqn6**600*6lnli-33--44 @  /?3933310327#"&54673-#73232673#".9 v)OMJ$9 t)OMJB56!' 46!'!! !@   _/]229105>7!3567!>o- J8-lU)56m+QD^@  _/]9910>7!#5 Nm6T 8@  ! P   /]q3339/9333104632#"&%432#"&'>7!#G:9JJ9:G#9JJ9  iY iY?+?+9/_^]_]q_]_]]q]++93310!5! ! 32654&#"3iQQeϿfzmm|B,P.3 @   ??393310!!!. 3 R <>)Jo01R? i@C   iYF!L I  iY iY ?+?+9/_^]_]+]]_]q_q]q+910!!!!!Rdw=Hw2=%@ iY?+?3993310)!!!=L3NyE@"   iY iY?+3?+399993333331035 5!!"' 63!N5J3#-  = )y7<\"+R@*'""+  ,- * *kY$$kY   ??99//+99+99933333331032654&+5#"$546$;5!32+#";)衏55)9V;m@@   kY ??339/+3393333310!#!# !;!3265!33"#L !7 F@$   !" iY  iY?3+3?+33933333310"!!&54$3 !!654&%Ąs<?yv}H]AƸ`H9V&,j"R@ !&  %+55+55V&<jR@ & %+55+55\^&~T1 430%+5N%^&T% [/,%+5^&Tw ` %+5^&T  %+5&UD'%+555\q *D@$" +,) ]Y%aY ]Y% ?3++?+?999333310%26754&#""323673327#"&'#ovko{kv2+ 'T [pv"nߏ 00TT^7ahv MZ)d@7"' '"*+"##"]Yi#"# # ## ^Y ]Y ?+?+?9/_^]]]+99333333102#"'!4$"32654&+532654&~0<|H5cnnй?4'|pnsmf\d^"@ ??3939310!47!3>7!8,V=B9=c-6VR>I,Yt\)C@"$*+!!' `Y']Y?+?+99393333310&54632.#"!"$5464&'326oэy\XIJC_i{xioz-B-76.6iF^Ҷ]:#~e}N%s&w@G# '(&&{Yk&Y&&&t&b&&<&& & &&  `Y ^Y ?+?+9/_^]_]]]q]qq+993333310#"!267! $54675&54632.#"3H gY瀐sX^wMqnAH}-)Mk 1э.&026B7\ 6@  !" _Y?+3/.39933333310!>54&'$4%#!5!EOOfH43V'IgA%]/ )M~ ߶JZ5 !}s/@     ]Y?+???9993310!4&#"!33>32V^r)3ryyy^QS\ n@B`YL~m$I*/  _Y  _Y?+?+9/_^]q_]+]]_]q_]+99333310! ! 267!"!. uk7iyln inxl>y^ @   ]Y ?+?99310327#"&5I#3^!"0@#$  aY aY?+?+?939103'.#"5632327#"&'&'##$\_24OWss3%L7!$r'n)r+ .!\ZJ Fhb lwC4L^ws^ @   ??299910!36! !9Esf4X^+\.^@0& # #))/0 ,bY / & _Y ?+3/9/_^]+9.39333333333104675&5467+5!#";#"!>54&'.\ۇC$K끓/aEOOf~6 4k% ҉u_R{{GU5!}f]/ )&FsR^=@    ^Y^Y?+?+33?993333310%27#"&5!!#57!#C?)66#Buf31ys-@   ]Y]Y?+?+?993310#"'#!32%"32654&ښqj+t#3f^)@    ]Y?+?39933310 !32654&'!2ir}r+3( `kַv\u #E@# ##  $% _Y `Y" /3+3?+??9333333104&#">$5474632^Z9@nxZJֺNOa a zy4 m F@#"!"aY aY?+?+??9999993332102!327#"&'!.#"56ZrP)J39F=14Un}4hF88;rm3q{%@5 Fu`F>FA@!  ^Y ?3+3??3?933333310>54!!$!PN#<& !3ŭ ;m{^'C@!! % () ! ^Y?3+3?39/993333310"&'##"47!3265!3265!z) .w0@%}c`SLLT^d}%@1iind.Ѥt'هs3 &j + %+55&j'(%+55F^&RTB k" %+5^&TN 6%+5m{^&T5 N0- %%+5V&(jR@ !&  %+55+55)H@&   iYiYkY?+?+3?9/+93333310"'32>=4&#!!!!!!2mtWcI62S_ZJ\&+D7YG^νTs&avR@ &k %+5+5w#r@I iYI*]L!Hn I iY iY?+?+9/_^]q_]+]+]]qq++93310"!!327# 4$32'&J y ɼjzMloWɿM(#jW70%<^6B,9V&,j"R@ !&  %+55+55hR-#m@A$%#iY0]s  iYiYiY?+?+?+9/_^]]_]_]_]q+9333103 )! #"'5326!32654&+s'i>_T@:35>7[ X^HeaW Hefc[@S  iY   iYF   !   L ;      iY?+??39/_^]_]]]]_]q_q]q+_^]+933333310)!!!!!3 32654&+i#65s'X^Hw=aefeY)>@  iY  iY ?+3?39/+9333310!4&#!!!!!!2FPZJYG^Ѻ`s&vR@ &X %+5+5N&6sR@ &%+5+5V= 0@  "iY?3+??3933310)!!!!!=TR65VL$ e@;  iY0]s  iY iY?+?+9/_^]]_]_]_]q+993333103 )!!32654&+z8VhO7HefeY%Ta V ?@     "iY iY ?+33?+?393333103!3!!!! q)Tl ];"COLTV^(@@"     ?33?339933333310 !!! !!!?!@<<B^&z@I!  '(kY1EV$ I*  $$kY$ kY ?+?+9/_^]_]+]]qq_q+993333310! '3 54&+532654&#"'6$32ȫ^nq{ԅ}`O-3ahXfKYwSM(@    ?3?2999933310!3!!4#! sZ>VJ&6R&%+5+5` 0@   ?3?3999333310)!!!`6 J<B=)@ iY iY?+?+?99310)! #"'5326!=>_T@:35>7[ eaW 0f+w2=n3w&)y7N)@   iY?+?3939310 #"'3267!367N;U˓~lZQf$Hi 5«S $Jb8{|\sV;V 2@   "iY?+3??3933310%3!!!!!=65`Lm+@ iY ??39/+9933310)#"&5!3267!ʚ]5buRw654&ɶ\jk!) 1@   iY?+3?33933310!!!!!!568JLV;@   " iY ?+33??339333310!!3!!!!!5"68@`Lu c@:  iY0   ]   s   iY iY?+?+9/_^]]_]_]_]q+99333210)!!3 32654&+uV{8/hPaefeY c@; iY0]s  iY?+??39/_^]]_]_]_]q+9333310)!3 32654&+!!p6d5NQC5beffXy X@4 iY0]s  iY?+?9/_^]]_]_]_]q+9933310)!3 32654&+V6z80hO7aefeYHs@I iYI*]L!Hn I  iY iY ?+?+9/_^]q_]+]+]]qq++933310"'63 !"'3 !5!.)c]bEcx8'gq}KMt@P       iYF   !   L ;      iYiY?+?+??9/_^]_]]]]_]q_q]q+_^]93333310! !!!!! 3 4&#"6"I<N+LwM>!3x G@%   iY  iY ?+?39/_^]+3993333210 !.54$)!#";| ʙx12юJ1VdapV;uD\$G@% ""%&_Y aY]Y?+?+9/_^]+99333310%>73>32! 2#"\%7my#~Q,5d16kY5$+PmRXox+#2Q)^ x@F  !"{Y9(M<   bY  bY ?+?+9/_^]_]]]q]q]+9933333310#!!24&+3264#!326qnw=ffaead9Zc^B;If8^@`Y?+?99310!!-^^o1^B@!   @`Y _Y ?+33?+29333310%#!!!36!3\WMw^`op$\bsH^;@      ?33?33993332310!! !!! !;dVVd;?77FN#s(w@G# ##')*'(('{Yk(Y(((t(b((<(( ( (( `Y  ^Y?+?+9/_^]_]]]q]qq+9933333102654&#"'>32# '532654&+5jzMPZw߉uV`v8=66&!-'9 "}efVE(.C>DA#^ (@       ?3?39993310!!47!o^FFw^#?&6  %+5^ .@  ?3?39933310! !!!}PE71^7^^)@ `Y aY?+?+?93210)! #"'5326! \|jD119M=Ny O!^*@    ?3?339993310)#&/!!>!!6++13 !%,q>l nD^#MGn^ N@/   `Y$ Hv$ I   ?3?39/]+]q]++99333310!!!!!1V^R3^\sR^%@ `Y?+?3993310!!!k^y^sS\sF/=^%@ `Y?+3?9310!!!5=^y^\\'L@'   !`Y `Y ?3+3?3+3??9333333310!$54%!4&'>>4&ŚXd/%d ^[od^ 3@    `Y?+3?3933310%3!!!!!N12^y{^+@   _Y  ??39/+99333103267!!#"&5XM1jU^g( 8.!^ 1@  `Y?+3?33933310)!!!!!!1w1w1^yyo^<@    `Y?+33?339333310%3!!!!!!!!1w1w1^yyf^ ^@8   bY$ Hf$ I  `Y bY?+?+9/_^]+_]]++993332103 )!54&+325gh^PyA:-^ ^@9  bY$ Hf$ I  bY?+??39/_^]+_]]++93333103 #!!4&+32!!ѓ11Xhg1^A:^^ S@2  bY$ Hf$ I bY?+?9/_^]+_]]++99333103 )!4&+321hg^A:Jsg@@  fY   ""#H "H9  M <     `Y`Y?+?+9/_^]_]]]q++q+933310"'532!5!&#"'>3 ҆ZwVK^EP?#-sh@?     `Y $ H f  $ I    ]Y]Y?+?+??9/_^]+_]]++93333310#"$'#!!36$3232654&#"1%bqobcpob13^R^ G@%     bY@ bY?+?39/_^]+3993333210).5463!!#;#"J-loϨnYKU-sbFOI\b&Hj . %+55&o@<$'( eY  ]Y    ]Y?+??9/_^]+9/3+3993333333310"'53254&#"!#535!!!3632=kM;<{^VsϜ1;fʼnn/ǡS!&v g %+5\sf@@fY""#H"H9M<   `Y`Y?+?+9/_^]_]]]q++q+93310 !2&#"!!3267!Xkis[ngOf#* JAz}˃}$,I\sVL&j %+55}M^h@> !bY$ Hf$ I `YbY aY?+?+?+9/_^]+_]]++99333210!# #"'5326!32!4&+32- \|jD119M=#bay OP`A:^o@B  bY `Y$ Hf$ I   bY?+??39/_^]+_]]+++93333331032)!!!!!4&+32^;1\1Dba3^RA:_@3     eY ]Y   ?3?9/]+9/3+399333333310)4#"!#535!!!3632ϴsϜ1;fP/ǡS!&v P %+5?&\6  %+5o^ 1@   `Y?2+?39333103!!!!!12x^yo}'@ iY?+?993310)!!! q6/@  `Y?+?_^]993310)!!!^1s&:CR&&"& %+5+5!&ZC $%+5s&:vR@ &&H# %+5+5!&Zvd d!%+5V&:joR3&0 %+55+55&Zj .%+55s&<C|R&j %+5+5!&\CY %+5R@ O/]399105!R\R@ O/]399105!R\R/399105!R\1N4@  H H/+3+]2323310!5!5!5!NRR1@  ?]9910'673'e5B#[q@ ?]9910#72~E(?$@@ H/+]]9910%#7!4|A$ @  ?]9910#&'7?%@;a Uw"@  ?3]2910673!%673!e5B#e5B#[q[qw "@   ?3]2910#7!#7!2~E2~E((? /@   @ H/+]33/]3910%#7!#7!4|A$4|A$  { >@    ??9/333993333333310%!5!%77777LB_qi@9      @   ??99//]]3339/3339333333333310%%!5'75!%%dL87K//K78L/-yxb) @    /]99104632#"&b욣ub9 #&@ $% }Y! ?33+3391074632#"&%4632#"&%4632#"&uZVS[\RT\GZWS[\RU\HZVS[\RT\TVXRO[YQTVXRO[YQTVXRO[Y?  "-7Bc@6.>83 )#) 3>CD +0@1;;;@5@&?333?3??99//3]333933333310 #3254#"#"&5!23254#"#"&5!23254#"#"&5!2+-2``2-YP,2``2,X;-2``2-YJ}{}}{}j}{} BR^@ //933310 Rs=wwR^@ //933310 ' 7s#;w\\w9u'H-!%+55w??3310 #+Jf H@0     P   0 @ p  ?3?]qr299333104&#"#3363 D<9ZHǢIL@`qTe/#'Y@0  yY wY  wY ?+?9/+9/_^]3+393333310!!!#53!!!!<ϕoRj%w@B  " &' ! yY !!!! !yYsYvY?+3?+9/3+3_^]2+39333333333102.#"!!!!!!5>7#535#53546]NEPLggFJdKR#VVqsJl'*jUss)z@B #''%*+"" #&#&xY## wYP     wYxY?+?+?9/_^]]+9/+333/933333331032654&+!#!! 27#"&5#5?3!!B~T5uNSaXHhumh#3>lgGD==DG>Zud7f@IItZOJĨǤdeeddcc82)f_+-)$H@! "  %& "    /3/399//3/999933333310%2673#"&=56746324#">32!3267.#"y1RQHbٓ2Xz#5Fi)|5Bu:''>{ ?555;''>u ?555Z''>< ?555C''>= ?555;b#I@)"" $% `Y   ]Y]Y?+?+9/_^]+933310#"&54632&#"63227.#"bܼӕiT6Vg N5FsI@Ϯ6)69Z644lp9 -@     iY?+?9999933107!!!'&9^/i(Z $ W4'7H%@ "iY?+?3993310!!! 7})7 @@"    iY iY"?+?+9999933105 5!! !)? H7Bo Xd9?4@"eJ9/o/]q]]]]399105!Xd%<@& @ H/+]q/99//39310##5!3Elq{7#)[@6" "'' *+"$ ?_/]q3333/333993333310#"'#"&5463263227.#""32654&7{;Os}XN&P28EEjWQPZ8DF͎İM]DCM<JXP )@  ?/9999333310 # 3 P=r=r!fg)&IL $%+55)&IOO%+5h3? @  _/]33/39910#"&'!32673Ysec ?gS[_}^ @  ]Y?+?93210"'53265!FuTFIMG1VT)^ ?9910>7!#^'PV1@^; /9910>7!#^'K[V1@N!@  _/]9910!5673'NX1@ J B@"  X  ?3?9/]33]39933333310##5!533!547}} 5ᗗAͤVblT9:@ p !?3?39/]3993333102#"'53254&#"'!!6d27WQ?8m%84 *?@+;J @ ?39?9310!5!TMJ)-5!-D@&%+ +./e(u(((  " !?3?29]9333333102#"&5467&54632654'">54&CLKB#GW:9;F=E2&1%  -, 48!59/33/33399/]]q3_]]/3/33/33/39/_qq3993333333333333333333333310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3#"&546323254#"%32+32654&+32654#"'53253T/0momImmmm0oowoooomm~smp.,;0m^{B.$*/;J1%Z^4+V}i0oo/mmmmmmoo;mmJoooo/yhIaCS1D D8QYb" "+%J fVr_cT*,@ % +,((""//99//339910 5467>54&#"63232654&#"TV,AgIOGRZ?>1HT;GFBIHCHEVW/2A1R~X8*P:/5K6DpJ;?HI>@IH}!&7L  %+5)3s@C ,311&(,452(/332`Y33 /^Y0Pp  //`Y/ #^Y ?+?+9/_^]+9/+999333310.#"! 4654&#"'632326='&$&5463 3cJ *0LZga\ơWߦlp|+-K5i+*V^X?GKO!t͊(@ kY?+??9993210>32&#"!!}>xhu_UB,(5C6PTB +'`/3^(X@-  #  )* !!  `Y &^Y?3+3?+339/99333333310"'##"547!57!!4'!326=!326FS R??3@>\gTLLTg\fѲɰssu&0vT@ &j %+5+5B!&Pv i%) %+5&$Xs %+55V;u&DX  )#%+55xG @XN 6@   O    /  @H /+]r3/q3993310#"&546324&#"326Nppqn6**600*6lnli-33--44X,(@ ^Y ]Y)]Y?+?+9/9+10.#"3>32#"&54>3&#"3265v?9%h2?JPNA5!# 5!3265^JF- k5jg¢!isP@+  P   ]Y?+??39/39/_^]q9333310!'##"&5!3265!>5!)0s1V^r1GN- lMVsyyutfW!C!vR@ _/]]29/310#'>54&#"5>32 K6*"AJi)Ϝ)G 3% " oR%}  /]310432#"٦TR疖GNs&(CzR& %+5+5s&CTR&v%+5+5\b!&HC $ %+5#!&C  %+5w=2P@(+(#+( 00(34))  kY-&&jY ?3+3?3+39/993333310"'>3 !"&'# !2.#"327!3254&'ZDl@K )tMMt) JBlD[&SU6Hl!-175!!.'!!):9{)TCGV({ų$Ջ+%;|dqe{{9^g@  eYe@ H o bY?+?339/_^]q33+]+393333103>75!!.'!7!u(}[4Yy*t^8/6<^hZ} mjj {'MB ? DNm"@i!"    #$ lY 0   iYF   !   L ;       "iY ?+?3?9/_^]_]]]]_]q_q]q3+_^]/_]q+33933333333310!67!!!!5!!.'!!P,36C9{)TCGV({3+%;|dqe{{9^"@!"    #$  @C H eY `Y $ H f  $ I     "bY ?+?3?9/_^]+_]]+3+/_^]+33+_^]933333333310!67!!!!5!!.'!7!#u 3Yz)u^54&+532654&#"'>7&'53>J0>]_\b)YhZ6ì{ԅϾSw`6NWe "')X)enahXfKYw6Lw(dN./#dH@V >6 "##G ,> &CC >IJ)A^Y)"GHHG{Y)HH HH HHI 32&#"!"327632.#"# 463 54&+5jzMPZ{]W9RVc;H3"6TJ~сolaKYUON0V4D|v8=66&!0l_)fL- o"a9 "}e'44*)3DAmuFw t@IiY*FVL!H I  iY  iY?+?+9/_^]q_]q++]]qq+99333310! ! 267! !.iQQeH7zmm|\s k@A{Y""#H"H9I]L   ]Y ]Y?+?+_^]9/_]_]]]q+++99333310!"&5!227!"!.>ndm1̍0ttpq @iY ?3?+?9210"!!67>32&B.@*9!+67\xVtF3GvsK'f @ ]Y ?3?+?9210!367>32&#"!?2 ,{3LkULH+' 3^Oo|Xj1,7s&}s'R)&$%+55+55!&~s% %+55w  .I@'!0 .'/0..%  iY iY%*]Y%#?+?+?+?393933310! ! 326&#"%!367!#"'5326?LECK JN  G'ANM7AQy"{xxvRpg[u cd7\ )s&R\ # 2%+5w91(X@, #'!! )*0jY %'0#' 'jY ?+99/+9993333310#"'$%>32>32$%#"'9#qv.$D=5H1}E0-E'ff)(ss$FC{&<2,B&%**JMKKM\+V@,$* !! ,-'*0$**^Y0 `Y0 ?3+3?3+393333310#"&'&547632>32>54&'#"&' H69G nj99+>SOPI>=6D1&5:;6'%#!RR">'+!3~/816Aw=1GY{@H#TKHO //3OK=Z[I9Y9i9WpWWW KWKW9233===9B  kY,&&jY ?3+3?3+3/33/_^]3399//^]]9333310"'>3 !"&'# !2.#"32673254&#"'.#"#54>3235>54.54632'ZDXcGRKNxRw>/-!pcenA6) 3;1bt6&-&]V:5:Z\R)@R@VMDAH#+HD5STDDPP`PpPPDPDP1*++55`5p5515;;; ;  `Y `Y&?2+3?3+3/_^]33/]]3399//]]q9333310 32.#"3273265#"'632!"'#"'.#"#54>3235>54.54632f|V?B%wlmv'A>V||z&g2.+ ":fT:pwN2B%,%NGNT)&< V$&-&^V ;4:Yw=B&i h >&@4 %+5+5&j h '%+5w#1@ # iYiY?+?+?9933310 4$32.#"!267!ZMe[Z:NjWg':\s1@ ]Y]Y?+?+?9933310&!2&#"327!Z!Xh~rw}* PB% hy ;@      ?99333333310%'%7%7%LG㴁FGJ{J;{Z}9IĤ{{<@   @ H  )  //]33]2+993310#"&543!632#6083m bm69+3G8u^s9H 2@  ` p  /o/]33/]3/399102>32#.#"+5Nwp:in? +.IJ%-&6ua1;47X@ _/]93104632.TNGN%-%D1IY:5 : VX"@ _/]9933105>54.546320E%-%NGNTbV ; 5:Y) (6DR_m@iP4H, B&:V^&,c4k no-&44)"00)d^Wkk`ZggS`IB;PPE>LL7E)`EE`)P     ?o/]2q2/]]3q29///333q2333q2333q29333333102#.#"#62#.#"#>2#.#"#>!2#.#"#>2#.#"#>!2#.#"#>2#.#"#6!2#.#"#>]qO?#&*-O;_;;;;6@3P3333.6-&..&-6  ?/9////////]]93310#67'>737&'.'5467'67".'7&'7 F$a5; Ia4#GA݁hBO݁E?軋Ek(8PC{Lh&ZC7#BO݁GA܂ Ia5; F$a5[8D.^3DuOW.FcB=FKV+!K@'   "#! " jY ?+???322/39993333310!3!!!!4#!#"&'!3267 sNZwYsce >VTLgS[_oN? G@%   !"  _Y ?+??322/3999333310!!!!47!#".'!3267o+މ)g  Yqgd^FFw^ὩJlN_[/@6       iYiY0]@ Is   iY ?+?9/_^]+_]_]_]q+9/3+_^]399333333310!!3 )#535!32654&++z8V6hO7!HefeY@U eYbYQaf$ I bY?+?9/_^]+_]]_q_]]qrr+9/+999933333331035!!!3 )#4&+321ymhg5?oA:`@4   iYP      iY ?+?9/_^]]+9999933333310'+!! 37'7654&+_]XsVr DLe)w>}p5mn5Zmhs(S@- !&&$!")*#$ ]Y`Y?+?+??99993333310"'#!33>32'";7'7654&p+6c^l4qhktje;JSSѠ{v!{Nl/P =@   iY  iY ?+?9/3+39333310!!!!#53PoʉTd^ =@   ^Y  ]Y ?+?9/3+39333310!!!!#53Lω^^yZ@5  jYP `    jY#iY?+??+9/_^]]+993333310"!!!632#"&'32654&m5Jk1nJ o ס/Ͱ ^J@'   aY ]Y aY?+?+?9/_^]+993333310%#"'3265!"!!!632zr-y1t}*.1JKD31^ VL@*    " jY?+??3?339933333310 !!! !!#!!?!@R=ը<<BLoX^P@+      _Y ?+?3?3399333333210!! !!#!! !;d VVd;?77F^&|ٴ5/!%+5N#s&|1δ71%+5V=@    "jY?+???399333310!!#!!!Gո6 J L<Bo5^B@!      _Y?+??39993333310! 3!#!!}PE)71^7^PP@(     ?3?39399999333333310!!?3! !#j|6zXdZcbGy^H@%     ?3?29999933333310!737! !#'!c@        `Y?+?3?939332310!! !!!;F7^7yV@M      iYF   !   L ;       "jY?+???39/_^]_]]]]_]q_q]q+_^]93333310!!!!!!!!f+6C5 Lw=o^f@<     `Y$ Hf$ I   _Y ?+??39/_^]+_]]++93333310!!!!!!!1V^R3^ |@J     iYF   !   L ;       iY ?+?3?9/_^]_]]]]_]q_q]q+_^]993333310!!!!!!!6C{Lw=^ `@9   `Y$ Hf$ I    `Y?+?3?9/_^]+_]]++993333310!!!!!!V^R3^ `@9 !"jYP`  jY #iY?+?3?+9/_^]]+93333310632#"&'32654&#"!!!!s"mP:b\#ס/ըL ^R@-  aY `Y aY?+?+?39/_^]+93333310%#"'32654&+!!!!72yr-y1sPD3y^w)4x@'//$2**2!$562,',lY'' '''@ H'' kY kY iYiY?+?+/++9/+_^]+9933333310327#"'# !2&# 327.546324#">bq.BLD>thE>8.N\Nȱ?Mǿp7>8&=Jpb"W}L}{jz18\s*3{@%00%2++2"%452. (._Y(( ((@ H(( _Y    ]Y ]Y?+?+/_^]+9/+_^]+9933333310327#"'#"32&#"327.546324&#"6VN*;@HTb*y0CX8ohol *,-ZLgv4V"74OM9H~W@w&&|9 j% %+5\s&F| Y$%+5)Vy 7@  " iYjY?+?+3?933310!!!!!!+sPs L/o=^ 8@   `Y _Y?+?+3933310!!!!!5=^fy<^&@  ??3/39932310!!367!NP $"NM^I<`F@#    iY  ??399/3+39993332310 !!!!!!5!1N??P\Z)^@@  `Y?3+3??399333332310!!!!5!!367!#NP $"NM^I<`VF@$      "jY?+???399993333310!!# ! ! !%ժ:V;5N5 L)+ o^F@$     " _Y ?+??3?99993333310 !!!!# !ZZ;#d)VHB@"  "iY  iY?+3?3+3?9333310!!!!!!!!;i6+NTL/o7^D@"    `Y `Y ?+3?3+393333310!!!!!!#51^lyymVFE@&   iY    "jY?+??39/_^]+9333310!!!#"&5!3267!+ʚ]5buRw6 L54&ɶ\jk!){o^>@   _Y _Y?+?39/+93333103267!!!!#"&5XM1jU^g( 8.mN@)    iY??39/]+9939933333310#"&5!367!!#q((5bmY6ʁnɶ\neH 3J5-{^H@$    _Y??39/+9939933333310#"&5!;367!!#F31}N^1iC}Zg)6f-@  iY ?3?9/+9933310!>32!4&#"!6[buOv3'Ǹjk *q^-@  _Y ?3?9/+9933310!4#"!!>321jWH^D8.`!(s@@% & &)*  iY%X "iYiY?+?+9/_^]_]]]]3+33/99333332310%2$7# #"&5473;! !"!4&bLn}?5`)%d%\[ ҕ ]DKBU6ztYHX8u|Gݳ`s%k@;##&' fY"i _Y `Y?+?+9/_^]]]3+_^]33/933323106$32!3267# ' 54733%"!.N!jbN)`%^| w+-'(`E75Nsyp|V$+~@G (#$))$,- iY(X $"%iY"iY"?3+?+?9/_^]_]]]]3+33/93333332310&#"&5473;! !32$7!"!4&?5`)%d%\[ ҼLnm~ (IztYHX8u|G]D@> duo`s 'v@A   %% ()   fY$ i    !_Y_Y?+3?+9/_^]]]3+_^]3/3/933332310.' 5473;6$32!3267!"!.ݵ)`!jb^| w(`E75N+-? +syp|B,&6uR& %+5+5?&6/ %+5F@#   ! lY jY ??3?+9/+39933333310"!!7!32#"&'32654&Ko6X/nJ@Pס/Ͱ ^F@#   ]Y aY?+??39/+39933333310%#"'32654&#"!!!2yr-y1t~2z1X'D3 ^ V;@"jYiY iY ?+?+?+?933310!!!! #"'5326!=N>_T@:35>7[  LcaW o^:@ _Y`Y aY ?+?+/?+99333210%!!!! #"'5326!+݉ \|jD119M=Ny Ofz@I   iYF!L;  jY??3?+9/_^]_]]]]_]q_q]q+_^]993333310%#"'3265!!!!!fKR~6C5Z/= ^\@7 `Y$ Hf$ I  aY ?+??39/_^]+_]]++993333310!!!!#"&'326711Lv@prlo3^R :V@O      iYF   !   L ;       "jY?+???39/_^]_]]]]_]q_q]q+_^]933333310!!!!!!!!fN6C5 Lw=o^j@>      `Y$ Hf$ I   _Y ?+??39/_^]+_]]++933333310!!!!!!!1+݉V^R3^mVE@&   iY    "jY?+??39/_^]+9333310)!3#"&5!3267!]5buRw6V+4&ɶ\jk!){o^P@-   @_Y/o   _Y ?+?39/_^]+93333103267!!!35#"&5XM1jU^g( op8.V!I@'   " jY?+??3?39933333310!#!!3!!!!46## ZoN {uX^TL1oL^@@! _Y?+?3?399333310%!!!#&/!!>!!+މ6++13 !%,q>l nD^#MGnB,&$6uR@ %&+5+5V\?&D6) .)#%+5V&$jVR@  %#&+55+55V;&Dj#5%+55%VuvA&(6R&  %+5+5\b?&H6  " %+5K@) iYzi  iY iY ?+?+9/_^]]]+99333310"6$3 ! 5!.267!3pZ+ ӕ [G SEnuH#X^sU@2 fY  `Y _Y ?+?+9/_^]_]]q+933310%267! #"=!.#"5>LYu To9-_iUqzp{ 𔂒&2,$V&jR@ . %1&+55+55X^&j. %+55V&jXR@ '&$ %+55+55&j $%+55^V&j-R@ <&'9!%+55+55N#&j ); %+559jI@& lY iY kY ?+?+99/+39333310 !!! '32654&+hP]h{Z\d O,5irf_9V^H@$  _Y ^Y ]Y ?+?+9/+39933333310 !5!#"'32654&+F\evbxP-3&MR&%+5+5#&Mu  %+5V&jR@ %&"%+55+55#&ju  %+55wV&2jR@ +&(%+55+55\&Rj , %+55w t@IiY*FVL!H I  iY  iY?+?+9/_^]q_]q++]]qq+99333310! ! 267! !.iQQeH7zmm|\s k@A{Y""#H"H9I]L   ]Y ]Y?+?+_^]9/_]_]]]q+++99333310!"&5!227!"!.>ndm1̍0ttpqwV&{jR@ .&+%+55+55\&|j ,%+55HV&j#R@ .&+ %+55+55J&j *%+55N&MFR@ &%+5+5&\M  %+5NV&jFR@ *&'%+55+55&\j ) %+55Ns&SR@ '&v$%+55+55!&\SR f& %+55mV&jVR@ )&& %+55+55{&j# % %+55VT /@ " iY jY?+?+?93310!!!!T+TLo^ 2@  `Y _Y?+?+9933310!!!!-^f^V&j5R@ -&*%+55+55-&j ) %+55/P&} H%++5)^o@=    Y   _Y  ]Y _Y ?+?+_^]+9/_^]3+39333333310"'5326=!#53!!!!3]G2033ω1L)3DQ@*        iY kY#?+?+??3999933333310"'532=# ! ! ! !RkM;;{:V;5N5BL)+ )^Q@*      _Y   _Y ?+??3+999933333310"'5326=# ! !!3]G2033{ZZ)3D;#dVg@<      iY)$IL> ?3?39/_]_]]+q3+39993333310!! !!!! !!q)V;5N'hN)j ^g@;     `Yo$ I ?3?39/+]]q3+39993333333103!!3#! !#fZZ.+dJ=\b 0@ iYiY?+?9/+99333104$!3!! #";\8{5VPh1JYefe\qG\&X@/ & &'(/ iY  ##iY?3+3?9/_^]+9/_^]93333104$!3!3265!#"&'#"&#"3265\* s5OVZN1l'+}H][Tb1BAfq-N=?Jil`fA;\ ,H@&* $$-. (]YaY!]Y?2++?+?99/93333310"323.5!326=!#"&''26754&#"^j 1PXWK-x>.Zofjqb(6&*fiKFfq=L7R!(T@- $)*/ lY&&!kY& iY?+?+9/+9/_^]93333103265!#"&54&+53 54&#"'6!2SUYO1ùXkqo$eYfq-jmNXdΐ9\s(\@5' )*'(('bY((P(`((( (( `Y  aY ?+?+9/_^]+9/93333102654&#"'>32326=!#"&54&+5erMOZxׄWK-8=66%".&9 'zfq̙efVsO@*  !lY  "kY jY ?+?+?9/+993333310!!!4&+53 54#"'6!2+ȶu*o$LjmѦdΐNo-s"]@2! #$!""!bY"" "" `Y _Y?+?+9/_^]+9933333102654&#"'>32!!!4&+5jzMPZwсoי8=66&!-'9 "}egFNI!:@ "#/  iY iY ?3+?+/_^]933103265!#"&5! #"'5326!OWZN2>_T@:35>7[ rwHCfq-=eaW ^5@  !`Y aYaY ?3++?+/9933210326=!#"&5# #"'5326!jPXWK- \|jD119M=/yJCfq O@R ? iYF!L;  iY ?+??39/_^]_]]]]_]q_q]q+_^]/_]933333103265!#"&'!!!!!=IUUI165}KFfq-=^d@;  `Y$ Hf$ I aY?+??39/_^]+_]]++/93333310!!326=!#"&'5!!2NQUI-k^RHCfqV3^wE@%iY  iY iY?+?+9/_^]+933310!! 4$3 .#"326=!5MkrhӚ5{gTk9*\sI@' Y ]Y ]Y?+?+9/_^]+99333310!! !2.#"!265!\E,\KH]*0V#'tc)b@@" / iY iY ?+?+3/_^]9333103265!#"&5!!!KVXL1sPs}KFfq-˾?/F^6@   `YaY?+?+3/933310!326=!#"&'!5=PXVL-^KFfqX(j@=&%% " ")*&kYV$ I*  iYiY?+?+9/_^]_]+]]+9933333104>32&#";#"32$7! $54675.v΅z릪 a̷`i[COwQKfXhaga1/OʒN%sb!@@#  "# iY iY iYkY#?+?+?+?+933310"'5326=!! #"'5326!!kM;<@:>_T@:35>7[ %VTLcaW J)^A@"   ! _Y _Y  `YaY?+?++?+99333210"'5326=!! #"'5326!3b[I4/33 \|jD119M=N)3Dy OR&$dD %+5VR;u&Dd#'%+5&$c#R@ %&+5+5V;&Dc  0#%+5&$t!R@ %&+55+55V&Dt)#%+55&$uR@ %&+55+55;&Du0*%+55J&$v!R@ '!%'&+55+55V&Dv<6%+55o 3{@  54%-@ H0!((@"H0@   iY ??39/+9_^]q]+q99/]339/+33993310!!!! &'#&'#567!".#"#>32326737jj{%!2ZSB0<,PG?,( } s`1QG=*) }r\D`|$GQJN`E;=!)/n~!,,syV;&Dw)#%+55Rs&$'KXRdD@& %%%+5+5+5VR; &D&Kd 15%(/%+5+5&$x)R@ %&+55+55V;&Dx1+%+55&$y'R@ %&+55+55V;&Dy1+%+55X&$z'R@ '!%$&+55+55V;&Dz<6%+55o 2@ 43,,,,$,@B H  / ''`p@   iY ??39/+9_^]]]q23/]33]]9/+33]]993310!!!! &' 3273#"&'%".#"#>32326737jj{%!jb -OG?,( } s`1QG=*) | r\D`|$;F/!)/n~!,,p|V;&D{/)%+55R}&$'NVRdD@& %%%+5+5+5VR;+&D'dN#'%2,%+5+5R&(d  %+5\Rbs&Hd %+5&(cR@ &$  %+5+5\b&Hc 8* %+5`&(RR@ & %+5+5\b&HR  , %+5&(tR@  %&+55+55\&Ht " %+55&(uR@ %&+55+55b&Hu )" %+55J&(vR@ %%%&+55+55\&Hv 5/ %+55o 1@ 32"+@ H.& & p@MH   iYF!L I iY iY?+?+9/_^]_]+]]_]q_q]q+_^]^]+]q99/^]339/+33933310)!!!!!#&'#567!".#"#>3232673J+ZSB0<,PG?,( } s`1QG=*) }r+GQJN`E;=!)/n~!,,sy\b&Hw #) %+55Rs&('KRd&@ %%+5+5+5\Tb!&H&Kd@ *. %!) %+5+5B&,cR@ &  %+5+5u<&c %+5BR&,d   %+5R&Ld %+5wR&2d %+5\Rs&Rd %+5w&2cR@ &$%+5+5\&Rc ( %+5w&2tR@ %&+55+55\ &Rt  %+55w&2uR@ #%#&+55+55&Ru'! %+55wJ&2vR@ /)%/&+55+55\&Rv3- %+55wo #;x@ =<-5$@ H$$8)00@#H_o  iY iY?+?+_^]q]+q99/]339/+33993310! ! 3 !"#&'#567!".#"#>3232673iQQeպs掍ZSB0<,PG?,( } s`1QG=*) }rzmm|YGQJN`E;=!)/n~!,,sy\&Rw !' %+55wRs&2'dKR@ #&%$,%+5+5+5\R!&R'dK  %(0 %+5+5ws&\vR@ V"%&&+5+5\!&]v} o#'%+5ws&\CdR@ !&%&&+5+5\!&]C'+%+5w&\cR@ 3+%"&+5+5\&]c )0#%+5w`&\RR@  ".%"&+5+5\&]R '3%+5wR&\d "%+5\R&]d #'%+5R^&8d  %+5R^&Xd  %+5^&8c^R@ &! %+5+5&Xc # %+5)s&^vR@ }!%%&+5+5s!&_v x# %+5)s&^CRz@ %%%&+5+5s!&_Cq#' %+5)&^cdR@ *%!&+5+5s&_c , %+5)`&^RR@ !-%!&+5+5s&_R3 #/ %+5R)&^d}! %+5Rs&_d#%+5R&<d  %+5^&\dV { %+5&<cR@  & %+5+5&\c % %+5`&<RR@  & %+5+5&\R ' %+5\ &B *+%+5! @  _  /]3210.'5!!.'5!F>"-!d)I-!d)17H892H8-9 !@@ H _/]399/+10#&'#567!'673#pcrapg;5YU5CK[eAN[nYu  %@@ H@  _  /]3999/+1067!#&'#7#&'53/pg<1~(arjiX@6SH,Ae`FwWpY- -@ O__/]399/]]29/310#'>54&#"5632#&'#567!!} 7B%+#%F^qȢpcrapg;5`r=t H)K[eAN1 %7@#"@ H@ H _/]39+2/+]3310#&'#567!".#"#>3232673厍ZSB0<,PG?,( } s`1QG=*) }rGQJN`E;=!)/n~!,,sy11@@H0p _  /]3]29/]+]10673#%#"&'332677F/\s àsXXr i`naNWS^L11@@H0p _  /]3]29/]+]10#&'53%#"&'33267je/F àsXXr Uz`i3WS^L1 C@+ @ H0@  p_/]3]22/]q+]29/310#'>54#"5632#"&'33267126k 3';5FVd àsXXr 4A)n )hCWS^L1 $7@#!@ H @ H   _  /]32+2/+]33103273#"&'%".#"#>3232673jb -OG?,( } s`1QG=*) | r;F/!)/n~!,,p| @   /?39333104&'3#"'5326NFOB#pJR<7#-4mB32%"32654&1 5ixkmplkp/&8RT@5H&@ iY iY ?+?+9310"'63 !"&'3254WId9_o`M:'gtp#(.w"G@'$  #$kYo iY iY ?+?+_^]+99333310"!27# 4$32>32&#".%o۴7_W.N98ddRMKjW rR':\!:@#"# ]Y ]Y]Y?+?+?+99333310 !254632&#".#"3267f ;+O>97eZH|>XKJ=- Ts%/2/$/u oF@%    iYiY?+?+39/_^]933310)#"#&5463! !#3 oe|b5A<øf`C/.55BH\b a@9  iY0   ]   s   iYiY?+?+9/_^]]_]_]_]q+99333310!!! $54$!3#";->=#qPhJ\dfc\q!E@$   "# `Y]Y]Y?+?+?+?999333310"323&=!5!#'#'26754&#"l ; hjumq{fqr2 (`쑥!\(C@"##)* &]Y _Y?+?+99993333310#"&'732654&'.5!2>54&#"mАy]XJI %wzgpzu-A-76.6iG]Ӷpkf{y p@E   iYF!L I  iY iY ?+?+9/_^]_]+]]_]q_q]q+933310!!5!!5!!yJyAJX%z@I$##   &'$kY1EV$ I*  kYkY?+?+9/_^]_]+]]qq_q+9933333104$32&#";#"!267! $54675$,΅zqj`LGYwYKc[`i2.O̖7V@2  iYY   I iY kY#?+?+9/_^]+^]]]+933210#"'53265!!!!鸲XL3>0<F=>3w&d@8(&$$ $'(kYo  &iY   iY "iY?+?+9/_^]+_^]+93333310!# !2>32&#"&#"327!DggW.N98diúad5 .%lb rDP13 8@"   !"nY#?+?39993332310!#"&5467!>74&'32?򍟆K< ?6 <%!% EFF橅fۄ05K(.?E-rL"E@$ #$]Y ]Y ?+?+???9933333103265!! &=4#"!!3632_de`1 wn1kһ~Z%Z% !@   iY ?+?93310327# I32&#"P6^qwEXA.4UBh^^~k8X#@@     ]Y?+?3?939933333107! !!4632&#"Ņ9XDϴTL98d`Tiìs B@#  `Y) ??9/]]]3+3933333103#!#53!馦Ϥ1\w!'w@F  ()0@ ##  aY aY?+?+?9399//_^]3399999333103''7.#"56327327#"&'&'#3H/24OW?3%L7!$r'n)r+ .!9=3 bm;3hb lwC4L =@  !"   iY?3+3??3399933310!3265!#'##"'##"&5!32655{s7+2rjq5|q/PJU[)/6@  kY #?+??399993333210)##"'53265!35!v JL3>0<{R}ͯ=>3vsww^#E@#  $% " "iY iY?+?3+3?939333310! ! 632!4&#"3 ! ]I;lgIm-K^|okz~*0\s%C@"  &'  # #]Y ]Y?+?3+3?93933310#"32632!4&#"32654&#"oV^r<-$doncdomd15/;yyyHv ^@4  !iYP iY?+3?9/_^]]+9/_^]9333310!#!#"#&5463! 32654&+5A<ø CgvC/.55B5pmmh+I@&$ )),- ]Y ]Y ']Y ?+?+?+?99993333103>32#"'#!4632&#""324& 2ihϳTL9801qhkte[QỦ>^;5>Nj!R3HV@+   iY iY??9/+39/_^]+9933333310!!3 !'32654&+6t*d^d~>1bihYV$<@ %&" iY"iY"?+?+999333310467>54&#"'632327!"$Vn/_[Se\}j6kjR9CG0CI-)gS3DJ/KT^\Ns"<@ #$  `Y _Y ?+?+999333310467>54&#"'632327#"&NVMEKRRއag,7A;:&)+.$XE&6.$fXONypX) E@&"!"YP  Y ]Y?+?+_^]+93323310"&5#"&5463 327";54&R7^J#4m+&6@42/7L R@)   !"@`Y ]Y _Y?+?+?+339933333310"'5326=#"&5#5?3!!327VM/38.X9I#f 9@     iY?+3?9/_^]93310)#"#&5463!!;ˉA<øDrC/*55B/7F@$   !]Y `Y ]Y?+?3+3?+9933333310%27#"&5#5754632&#"!!wPpreWKO>69I#3Xl!:9ZA>)y/@    iY kY #?+?+393310327# !!!<0>3_4sPs}?< i77!D@$   "# iYiY?+?3+3933333310%2654&'!!#"$547!!%ŁŪL\Ʒꕕ>^G(@iY??+?9933210)!>7>54&#"5632H9'7 ?+6&6"HbL':4&XeL0.#w.@    iY?+??99323102!!.#"56g@-N)41*($LkV-UN"#s 0@ !"  aY ]Y?+?+?93910367>32&#"!"'532?`.$"n1j,^&6K'لEO H!CC^_/u ]n ;V1q@W   iY]L*nL I iY  iY?+9?+99/_^]_]+]]]q]qqqq3+393310!!!3!!!5#wVRdd7^v@B  fY g<L   ^Y ^Y?+9?+99/_^]_]]3+3993333333310)5#5!7!5!3!!ס3BӮ #9jPD@$ lY iY kY ?+?+99/3+9333310#"3267! $54675!!{Хl[Okahmj6+OPX^S@+ _Y ^Y ]Y ?+?+99/_^]3+9933333310#"3267! 5475!!wȢd[P3-T"b-^#h@7  $%!Y   _Y $ ^Y`Y?+?+99/+39/_^]+9933333333104&+5!5!327#"&5%>4XNͼYq%?4(#\P$,. 875b@4   ! Y /   kYlY?+9?+9/_^]3+39333333310 !!5!5!>54&#"'>3235(jWMX^cPeAJWjBVVK0꺝9PE@# iYiY kY?+?+39/+9933333102!"&'32654)#!!PvJLd(' (7rlN#^M@( {Y  `Y ^Y?+?+39/_^]+9933333102!"&'53 54!##5!!DvAGZ)ì)&$2JL$H@## !%&!@!$!$`Y! _Y ?+?+339933333310#"&'532654&'.=#5?3!!+&6m_Yr@LSna,3kX188?6`&)977"3YRfs:@ aY]Y?+??/+999333310!33632">54&+k{i}xcoJ'ܢ|}@ #??93103#' fb@5   Y Y@#??99//]+33_^]]3+39333333310%!5!5!5!3!!!!#<<==Vu Hs&''=LR@& "%1*%+5+5+5 !&'']L@ "%8*%+5+5\!&G']L@ 2+%&%L"):%+5+5Rs&/- #%+5d&/M #%+55P&OMq P %+55Ro&1- \%+5`&1M \(%+55!&QMB M -%+55&$LVm@ %&+5+5V;!&DL +$%+5A&,L"m@ &  %+5+5!&L  %+5w&2Lm@ &%+5+5\!&RL # %+5^&8Lm@ & %+5+5!&XL3  %+5^)&8 LR@,& %+555+555&X L@  %+555^^&8R@1& %+555+555 &X@  %+555^`&8 KR@(&92 %+555+555&X K@ ;4 %+555^^&8R@ 1&. %+555+555 &X0 %+555X^sU@2 fY  `Y _Y ?+?+9/_^]_]]q+933310%267! #"=!.#"5>LYu To9-_iUqzp{ 𔂒&2,$)&$ LR@$%'&+555+555V;&D Lw'9%+555)&$ O@ %&+55+55V;&D Nw&%%+55%&MRy@ %&+5+5V&M=ʹ<;%+5w!@^!  "#? mY ! !! !!Y @ H iY  lY ?+_^]?+9/+_^]+_^]_^]q9/q3+_^]3993333333310!3## !2&#"!275!5!5!DggcX^.%lbZPZc\s -{@E ++$ $ ./ Y !`Y  @ H(]YbY?+?+?/+_^]_]+99/3+399933333310!"'53267!5!57##"53237!326=4&#"XZDHmM lv{}hn|klkBV-0q%}w's&*LR@  &E$%+5+5\q!&JL0(&%+5Ps&.L\R&Ŵ%+5+5&NLF{& %+5+5w&2Q` !%+5\s&RQ%  %+5w&2'Q`Mm@+&!%+*%+5+5+5\&R&M Q@ )$ % %+5+59jq&LP@ "%&+5+59V!&L#%+5 H&'= 1%+5 &'] 8%+5\&G] L" +%+5w's&*v3R@ #& %+5+5\q!&Jv; A(,&%+5@M  iYF!L;  kY ?+??39/_^]_]]]]_]q_q]q+_^]?933333103265!! &=!!!!!WffX5 65߄v}h=:@ jY#iY?+??/+999333310!33>32"654&1BmhcpMUgˉs&1C}R&%+5+5!&QC %+5s&$sR@ %%!&+55+55V;!&Ds .-,%+55}&$R@ %&+5+5V;+&Dq#)%+5Ps&(sR& %+55+55\b!&Hs*% %+55}&(\R&  %+5+5\b+&Hm " %+5ss&,sR@ 5%&+55+55 t!&s %+55}&,R&  %+5+5+&7 %+5ws&2sR)&$%+55+55\!&Rs(# %+55w}&2/R@ #&%+5+5\+&Ry  %+5qHs&5sRt@ $ %*&+55+55w!&Us %+55H}&5R@  %$&+5+5+&U  %+5^s&8sR&&! %+55+55!&Xs# %+55^}&8R@  & %+5+5+&X  %+5^1s'l@?  !()`YE#  ]Y%]Y?+?+9/_^]_]]]]]_]+99333310%4&+532654#"'6!2#"'326ȷJHZ Yi{di'!aз% ɢyP+5fs&+LR@ &  %+5+5&KL9{@ & %+5+5h3@     #iY?+???99933310!4&#"!33>32h}1Dfm\NEm"V@-  #$!!iY iY?3?+9/_^]+99933333310! 5467.=!! 5!! ! qgtrg5-)6A^_/;v<9J\'L@'# ## ()&&]Y  !]Y ?+?39/+99933333310!".5%.=!326=!324&#"olrof1VbcW2$v|v|H46Ԟ:r3īʟ#1q&=}H  %+57)^f&$OR@ %&+5+5V;&DO1#'%+5&(z  %+5\bs&Hz +*+ %+5w)&2 L/R@/&%+555+555\&R Ly@  %+555w)&2 M/R@ &+%+55+55\&R My #/ %+55wf&2OR@ &%+5+5\&RO? %+5w)&2 O/ %+55\&R Ny  %+55&<MR@  & %+5+5&\M  %+5N{T@.    @YO  Y ?+?_^]+9993333310!>32#"''67.52654#"1T,S/@*-1`D9 C#XJy=#rB-%R2r)s&/k@< !..#**#&!01# @  ]Y,YO 'Y?+_^]+?+??999933333104&#"!33>32>32#"''67.52654#"NMUtg)-nT,S/@*-1`D9 yy^NVC#XJy=#rB-%R2r/L&n@<! %%!!'( @  `Y #Y O    Y?+_^]+?+333/999333333310#5?3!!>32#"''67.52654#"X9T+S/@*-1`D9 yfXC#XJy=#rB-%R2r\T)6S@, ..& 44&78  *]Y1##]Y?3+3?3+3?993333310323&5!3632# '!""326=4&!"32654&\k 2 kwyapklpelynmgnlkp/5|aj8μ/d55\Ts)6S@+4' - 78  #11]Y**]Y?3+3?3+3?99333333310#"'#!47##"326!22654&#"!26=4&#"Th hx~apinoflolepnip/Υ4`^2355ff@5       " @  iY   ??39/3+3_^]9933333332310!!##!!7&'%3X{jǯ6jo!F>uN\ XLCYwf%g@8 !#$$#&'@#)H@iY"&@#  iY?+33_^]?3+3+933333310&'327#"'#7&54$327"&-.AdV"<۴SF2E7ADk3&cMK RYW Svq\V1"\@0$  #$ "  ]Y ]Y?+3?+3??9993333333310"'#&!23'3267f51Ǧ ,(Ĥ3ZLXKJ#ysdz-4/2/$?M1/? z@K   iY]L*nL I   iY ?+?9/_^]_]+]]]q]qqqq3+39933333103!3#!!#/6QyRdT)fU@-    !"@  iY?+333?_^]^]933333310!!73#!57#/s13ʋddB ^bDo\s5L@() /$$67/$''-`Y'_Y]Y?+?+?+99933333310327#".'./53254.'.54632.#"$TGF7!j/ba9'G@3UQ,lZy7ʿ\TLWz:L &,QA :taP(6`$-9&6\wWX$.I)<;5\x7^E@#bY^Y ]Y?+?+9?+99933331035!5!327#"&'.#7BV00ZGI<#n1<$eZ7vrqX gM-@   kY?+?9/39933310!>54&#"'632ϴsXm !a=|Unb}s-@   ]Y?+?9/39933310!>54&#"'>32ϳsXmdـǭ^D=}Unb7F(@ & ""&&)*! z*Z8  kYF$IL: iY1"%""" "iY?+_^]_]]_]?+9/_^]]q]]]]+]qq+_^]99/_^]33^]]]q39333333333310#53! #!32654&+3#32654!7{f{6~q{ʀz\ sNZTIZ\be ]@1    iY_   iY ?+?39/_^]]]33+339933333323103## =#53!!26=!^Ȯ5Fߘ!:mo3if@s     !"@iYF!L I  iY iY?+3?3+39/_^]_]+]]_]q_q]q3+3_^]^]9933333333310)#7#!733#3#!3#7#N''H=RaqR??^^wA\Vb$(,@[)+,(# %%' $#,+ -. +)'")) ")fY(""L"\""" "  _Y `Y?+?+_^]??9/_]]]3+_^]399999333333333310"'#.5323!3267"37&'7[AǨq*,Ǧr{D\"3^5K'TU?< ixg]@\Ns)I@&+' !*+  %]Y^Y]Y?+?+?+?9993333310"3237!327# 547#726754&#"j<934=>} 1tlo{k14PT%9BiLZnQT%HU@, iYiY?+?39/_^]3+3399333333310!#53! !'32654&+ʤ*d^11݁91bihXwsZ@2    Y  dY ???+9/_^]3+39933332102&#"3#!#5333>>)%5yϠ-4s ha9Ѽ^sX@0      qYI   ??399/]]33+339333233310!7!3#!#53'!7#bNkkhhP/j /^"o@;$ #$ Y:/ ""]Y?+?3939/_^]]33+33993333333323103!!!3##"'5326?#6?#oNnmG}iAOL7AQy"8 !# JJ# cd7Fg[eeRps"Y@/ #$  eY_Y_Y?+?+?9/_^]+_^]9993333103>32327#"&57>54#"o;P®e/vj^cI 1RdXZze\qs:@   ]Y^Y?+?+??999933310"3237!#'#'26=4&#"o; hj}jo}fq23!s>@    ^Y]Y?+?+??99993333102#"'#!336"3265k +kjv|cnsУ^?N)C@"' ' '*+  ]Y]Y$]Y?+?+?+?9999333102#"'##4632&#"36"32654&p3鳳TL9801 kpqhkt^opsЏ{5>[!?s&@  ]Y]Y?+?+9310"&'32654&#"'63 nIF_rxdW%./2BL\#s(R@+ &&)* @Y ]Y #_Y ?+?+9/_^]+9333310632#"''67&5!2.#""32654& v+!Di#ZFvB4hAG[@+&4yOSUD[|,L&nZ91,2\N+M@(-) " ,- &]Y]Y]Y?+?+?+?99933333310"323&5!327# 5467#'26754&#"o 2934=>}  hjumo}fqr23}bfo9BiL7f+!\N#)G@%+' *+]Y$]Y]Y?+?+?+?999333310"323&=4632&#"#'#'26754&#"o `L961=; hjumo}fqr23}b=>L!X^s\@5  fYL\ _Y`Y?+?+_^]9/_]]]+_^]933310"!."&'53267!5432Lcp mJ`e/|puwR)&.*X^s\s!(k@9 *& %% )*bY %& &   `Y"_Y?+?+99//3399999/_^]+9333310"&'!"5%.#"5>327327265e(uckX{%13-BKCm4o}=qLo ti&2,$5jjB5TyR\N%sN#sNs3@U5. ("..2(45 bY& 2332{Yk3Y333t3b33<33 3 33& `Y &,^Y&?+?+9/_^]_]]]q]qq+99/+999333333102654#"'>3273267#"'# '53 54&+5GLZrӈ4s3-+CCm~K:nu~SXl7:p&!-'TLkjB TN "}efVE*,DA\s(|@I #'##)* '(('{Yi(X(( (}(l((<(( ( ((`Y ^Y?+?+9/_^]_]_]]q]qq+99333333104$32# 2654&#"32654&+5\msy!zwbnys'1]x u]25F1?H?@5}q^=@   Y ]Y?+?9/3+3933322310"'53265#53!3#FuTFIMG1VT#@\N#&2S@,!40 ** 34 #]Y.]Y'^Y_Y?+?+?+?+99933333310!"'532=7##"323&=4632&#"26754&#"qkv`L961=xgo{kBV6 3}b=>'%\qs &F@%  $'(#!%!]Y!^Y_Y?+?+?+?999333310%26=4&#"!"'532=7##"3237!o{jo{ks kvۍ%BV6 3\PsU@.fY  ]Y  ]Y?+_^]?+9/_^]_]+99333310!! 4$32.#"!275#VkcZ>S?DyX)(. ,^ 8@"!"  eY?+?39993332310467!367!#"&%4&'320Qd?$ 5?Th"$% EFO{'$Acm,ED*rs-7D@# 9#20..0890' !'!]Y'5eY?+?3+399933323106?>32&#"#"&5467'.#"5>324'326P/-H>yK]$(&&E.(>3N+"&YW6z&GEF$"#"T3!32673#'!&Jt#1%+ n)k!49=N@)  Y    Y??9/3+3_^]+93333233103#!#"&54632!";54uH~i?21w#%b>Vqljz%"e#Ci !@  ]Y?+?93310327#"&5!934=>|1}9Ba@4  _Y ^Y ]Y ?+?+9??9/_^]3+9333333310!#"'32654&+5!!!E\ew1^bߗwP-3;^#=@# #$%    ]Y?3+3??3399933310!3265!#'##"'##"&5!3265R1QWuj1)0jX,l1QWpo^syyOTLWsyy;^$@@   $!! %& !]Y?3+3??339993333310%#"'##"&5!3265!3265!!47 hX,l1QWpo1QWuj1 LWsyy1syy=kBs-J@& && ./   *]Y#]Y?+?3+3?3?999333310)4&#"!33>323>32#"'532654&#"QWuj)-nY-nükM;<@:QWpoyy^MWNVVTyys6@ ]Y ]Y ?+?+??99933210)4&#"#"'5326533>32V^rr>=33:)3ryyyB9ۏQSs:@    ]Y]Y?+?+??99933310327#"&54&#"!33>32934=>|V^r)3r}9Byy^QS#^(@  ?3?39993310!!!.5#n^mB^G5\s\s#@K $%  Y:/ ^Y`Y !]Y ]Y?+?+?+?+9/_^]q_]]+_^]99933333310)5#"&5!25!!!!!32654&#"^b1m{zkl{zl';-8#H^Ds'L@( " ()     ]Y %^Y?3+3?+9/_^]9933310"&'##"54$324&#"326=!326z* RYU7TLLTXVhj +sŇs\'+^)@  dY  ???+99310"'3265!#'#33#71-4 9]s+)@    dY ???+9993310"'3265!#'#33#71-4 ]s+^8@  dY ]Y?+??+9933310%#"'3265!327#"&=47| ^` 9%9B;hws+@  dY  ???+99933102&#"!33>>)%5-4s J^sws2@dY ]Y?+??+99933102&#"327#"&533>>)%5934=>|-4s J9B^sw @  aY?+?993103!2&#"}vw#D=!1+w @  aY?+?993104#"'63 d=D#ww}!h5^I@%    bY   bY ?+?39/_^]+39933333104&+326#!!2!NWMͰXiӞJ;JN`^S)^I@%    bY  bY?+?39/_^]+39933333104&+326!#!!3NiXMW;N1\GOJ=)S⡱^`\s/L@)# )01)!!'`Y!_Y _Y?+?+?+993333310#"'327# 3254.'.54632.#"C:.704H^UQ,lZy7ʿ\TLWz:L::R(6`$-9&6\wWX$.I)<;5\x#(@ ]Y]Y?+?+932310#"'532654632&#"ѱr>=33:`L961=yB9;=>#P@)!  !`Y ]Y ]Y?+?+9/_^]3+393332233103##"'53265#534632&#"Ѡr>=33:`L961=B9yݻ=>s(@ ]Y]Y?+?+9323104&#"5632327#"&5=2;3Hd934=>r>=u9BF#$X@0&" %& ]YY  Y?+_^]?_^]3+3?+993332331034632&#"3##"&546326=#"`L961= (0189(=>Lly25?6&+BJ`h@B    `Y/o ]Y_0P0/]q]+?9/_^]q+3399333310"56323#5!5!4&PprX9Im#3fA>/7L@@  @  `Y]Y?+?+339933331027#"&5#5?3!!wPptX9I#4fA>B^b@3!   !  eY    ]Y?+??39/_^]33+33993333332310!'##"&=#53!!!3#%267!)1sȚ11kyZVNUdRR}yy3^F@$     `Y^Y?+?3+399933333310%2654&'5!#! 57#5!jmcdpiٚBFԀ As/@ ]Y ]Y?+??+9933310! !32654&#"56321omo852=To`;B^ @     ??393310 !&'#!'%^}bi|^^"@ ?33?3999103!!&'#!.'#!VuH>т .) *B^yu_#8G>^)@    ]Y ?+?393992310)&'#!>32&#"  AXD5DQy"JKwg[î cd7^"@  ??39932310!!!RXX^X7s^J@'    ^Y   ^Y_Y?+?+9?+99333310"&=!5!5!!327ŊB 3321LQB47N^ Z@.    !"Y ^Y    ^Y?3+92?+99/+933333310!'67!5!5!3>32#7254&#"h4J{]|q! 0F&}598ACoP#KE9V^V^#/~@G'-  "-01$eY  bY  O    ^Y*_Y?+3?+99/_^]+39/_^]+93333332310%4&+5!5!&'#"&546326"3267.!fFt=4L0"Uzxk;54&#"'632϶sXm sa>{Unb}1/@  ]Y?+?9/399333104>32&#"!.1yۍmWtϺh~o}bmV{>oU-@   ]Y?+?9/39933310#"&'732654&'!|nrmWt³1}q9CcmV|=V\s(@ ]Y]Y?+?+993310 !2.# !267hšZV^-HML(.3Tw&2y %+5^Ns$|@I !  %& $${Yi$X$$ $}$l$$<$$ $ $$`Y^Y?+?+9/_^]_]_]]q]qq+9933333310!"$54675&54$3 #"3 !"3u#;%on!`q1Ѵa ,ԡ6C?DZ`;9D3\&]@3!(  & '( fY    #]Y]Y]Y?+?+?+9/_^]_]+93333310.#"!275#5!! 4$3254632&#">S?DEIO>97e1 ,X)Zs^F\#j@;! $% cYY Y?+_^]?_^]3+3??+_^]_]99333332310!3##"&546;432#"26=#"1 8 SSK(0189(^lyGO25?6&+V^7@    ???39393333310! !7!!71)`B1y`=kQT%O@' eY  ]Y ?+?9/39/3+399333333310>54&#"'6323#!#53sXm ϸ>{Unb}a1M@& eY ]Y?+?9/39/3+3993333333104>32&#"3#!#535.1yۍmWtݺh~o}bmV{>ƊU\$'d@6"&&'()&^Y%%^Y  ]Y ]Y?+?+99??+9?+9933333310)'##"323&5!!!26754&#"; ho 2K  umo}fqrn23}bfJQ !Z\9-:@F"8 1-))-1 ;<,_Y 5]Y^Y.]Y &]Y ?+?+?+9?+99??9/+39333333333310 !#'##"323&5!!#"'32654&+26754&#"; ho 2F\ewoumo}fqr23}bfJbwP-3>!\N%25>@J5!8044 )>!88!)?@";Y" $33^Y>4""^Y"-]Y&]Y?+?+?+9?+999?9/+93333333333310"323&5!!3>32+'67!'#'26754&#"254&#"o 2TzazoϞ4; hjumo}fqrn! 0F&23}bfJCq}598A!ZtP#KE/9L 0^@2 !!/&&/-12& `Y  @ 0 0`Y #`Y?+?+33?+9933333310!"&5#5?3!632.#"#3!254.'.547!X]Sʿ\SK%]gz9$bk fX$.I%/'3[xRj_"&0+F{*/F%-^@1 /+++(  ./"]Y@**`Y&]Y]Y?+?+?+33?+993333333310#"'5326=#"&5#5?3!54632&#"27!hr>=439rXE`L961>PpIyB93f\=>/#wA>/L0;@G -&$+//$4::$<=4!11Y]Y*+@(+%.+.`Y+!]Y!7_Y?+?+?+33?+9/+999993333333310%267&5!2.#"632# '#"&5#5?3!!%"3265469'#[YZEwB~dmX%57cFF[-'\,"*&'#iQPf녴60(:91^)1i@7'%'/23(+!]Y+ ]Y+%%`Y%]Y?+?+33?+?+?3993333333310"'532654#"!!!#5754632&#"!3>32kM;<@:qϨϞ{N\NA:)1rʼVT۫yyRR/M} 6wX83L`V^r1}9Bi#dUP{yy-@     TW?3?39?99333310#4#"#33632fW O괆w?{3@     TW?3?3?3999333310#4&#"#4632&#"3632CJfW>A--N QY[wVsX`nnq!@  V TW?2??9993102&#"#33>4.r# ,peVFIS1ym#@  V TU?2??99310"'732653#'#2 /r# ,ypeJQ-8@  @P VU?3?/]39933310#"'732653327# 547{-L2 .rN*.0a EHpe\\-U_8@    VT?3?39/399933333104&+326!#!33REA@/a{6;:jAzF'd@ VT??99910#!33>7!3>73dXE&0Je kf !iF.!n'$#3s*@   @ P  V?2/]393910!367!#"'532?  3G2"<5)3_GKP )w  +)u '@   / O _  /]q33/31044#5266*nn*6-3hm3+)u '@ /O_ /]q33/3103"&5463"6*nn*6-3mi3`!)@   ?33/9/399333105654&#"'632;6UWDq+p0;7@Ch!q!+@ ?33/9/399333105&54632&#"󫚚qDWU6;h;ɀ@7;0p+,@[/10%5% V@Hx,@[/10-5,VxHV1H/10 #3vHVVV1H/10#31HvHV+)X!vX!C+JH+]5+dM@_P]q5^XC @ H@Pp]q+5^Xv @ H@Pp]q+5+J @/?99//]]10 !lJ+-J@ /9/]10 J+)F+)F+V+V+-+7/3105!+{{LF3@o  0/]]33/]310%327#"&'4,BKDmf(RNhgE5To13@@$  ! @P V?3/]99393333310746733673#"&%4'328.  *d~glw575K\^4If`RtcxybRd3`V@ T??9310#3jy$/@  %& W U?3?3999333310#"&'53254.'.54632.#"ZDBD!RLbHAt:kJni^-)G"+7pamzA#7.*)EZ+ /@     V T?3?3999333310 !! ! !T+3 fTD!*@ T??39/399333104632&#"#.DѧToE[ٓ\IPA[/A@ /3/99310!#!5!o @  //9/3993310!#!5!3oR$@ //9/]3993310!#!5!3o^ @  //9/3993310!#!5!3oV@ //399310)5!3@ //3993103!!$@ //9/]39933103!!#odiL& @ H@  @ P p  ]q+5P]w +H/+3?910#3dۢ%9+H/+?29103# +#d%=/@@H/+]?10%5% Ndۍ/@@H/+]?1075-N/d܎?XCf?S{f? +@?  O      /]qr]qr3210.'5!!.'5!Q-n?Y-;?;2)W@.rY R#H@ HP]++5uks@ `/22/]10!##Tmsmks@ `/33/]10#5#5kmsmk; /22/1033mTmk; /33/10!5353kmm)V@  /]33/]310!3!53nsuVR)V /]210!3!nsVR) /33310 5!!+Rdy{N!KUMVE@4=/O@$H@ H/++]qr^]_^]210!!VVw+N[OYjcPC!SN!Lf) ?10#f)d)?3210#!#H()ddj 5@O_o  @ H _/]33+/]3]210#"&'33267%4632#"&j àsXXq F;9JJ9;F+WS\N+?>>?32#.#"w ۨ .SX ٗ/6}=$=$= $=$_!C @ H@Pp]q+5_!v @ H@Pp]q+5LB H/+9/310353#5#ᇇᾅ{LB H/+9/310##33ᇇDžmj@ _/]210!#!ڈjgFP3 /33/10>5!dN-#w{  /33/310463"3"&}r*600qi~0**0~)V@ /]3/]33103!535DRb{{)V@ /]333/]105#5!#DV{{)- -@      /q33322/]/]q]1053533##5ד{{UtM@ Pp]59j  _Y?+/10"'532653XL12517?j  _Y?+/10"&533273321LB4rY~jz@  P  ]q55 @   H /+3]210#"&546324&#"326moqo5++5`+5h~hg~d*00*Z0U;9zC6Q,KfH+]5Bm//]2/310!#5!#ڈ6b}& @ H /]3/+33310#"'#"&53326533265wjo55ohwop3@bs3@}q|GGzsBEBENiL& @ H@  @ P p  ]q+5NgK$ @ H@  @ P p  ]q+5wGNn @ H  P p  ]q+5wGn @ H  P p  ]q+5cYR#H@ HP]++5UeM@_P]q5!H/310!5!BY1]aRV.HH@ H0p]q+++5 /210!! +hP/10 djoV ?/10 #ou}  /33/310#526544}q006*qj~0**0~Bm@ o/33/]310!3!53m&ʈB\5-@  0/33/]310!!35#fٴ-}$ @ H /]333+22104632632#4#"#4#"}wjo56nhwnq3@bs3?q|HHzsBEBE/3 (@ ? O o  / O _   @H /+]q1077''7V{yX{{Xy{VyX{{Xy{VyyV{}%@/O_/]q]10463"#52654.r<;%+%t<;%+%FK]g.5>G(L^h,8>EV#@@ H/?o/]33/+]310!5!5!5!TT;|!C!v:cRUp8?5mxU@ @H+]555B!@`p_/]33/]210!#5!#B|Y1]KH+]55B@ _/]22/]10!#5#|{w-8@! / /]2/]3//999910"''7&#"#>32732673?MIeJ$,*} ucGCEeF""+* }s){32326734632#"&4632#"&.TLD,*} x`3UIA +* }sF;9JJ9;FF;9JJ9;F#+#7>?>?3232673".#"#>3232673+ME=*& } p\0OD<)& } o]+ME=*& } o]0OD<)& } o!*.l!-+qz!*.k!-,rz?f  /33310%35 5#F /]9/3105#7#Bxx% !'/7AIS]gqy@w  &$622?2O2_2?O[ooVjv~~rzB88F< QeeL` .*0*@**$2jz<`**`32#.#"#"&54632 à sXYrTF;9JJ9;FWS^L+?>>?73#&'##567&'5 YRh,:,!hd/_'nO39CT2=323273#".Kh,J9666l")"?H )@X@P / _   /]33/]2]10 #&$# #6$dCQT[aHaj?+/22310!5 5!2ξы)F @ X [Y?3?3?9/3910'#"&546?4#"'>32%326=\u}bVBBcC. MYb5o{ujmn t= 2E&#RA% )R@@ H [Y?3?39/+310"!."&54632!327 ;LI$ȶ'^U~|:qyHNFP]Q]85o @/ Z X??]210432#"#3mommo^^\)T  [Y?3?310#"&5463232654&#"T#AHG@@GHAŨťeddedbb)T@  ZX Y?3??3910'##"&5332653 wKwQL5Z06|di~LA)% [Y?2?310 4632&#"327/un323>32#4#"doMFə sF=tGnPB5i~Z06f/7z5ke5@ Z X[?2??9102&#"#33>,)^mɘ %v]Uw>E)T@  ZX Y?3??3910'##"&5332653 wKwQL5Z06|di~LA5  ZX??391033673fэ5pKF;VA5} @  Z X?3?39910 373#'#䏏㚜X)o   ?9910>7!#) P/+E5DF>)= @  /]9910!5>73"P+E9AF>?sC\s&FO@ 0pA %+]5?s&COƴ %+5?sZ#/j@'--01_Y@) H*Y0@p  $Y ]Y ?+?+9/_^]q+++9333310>32# !2#"24&#""32654&/=fN%ȠahXk~:r!"h5GFABN\ǟ~iÐx~ h}A72&)-%'QT(@?Pִ%+5]q5V&QjR@ +&(%+55+55\'^)U@- ( " ( *+ _Y %bY _Y?+?+?+??999333333210"'5326=$47!654#"'632!327WM2070)NV+%1Dj HZ/+7?7VtS\ZXVpw9@  iY  iY ?+?9/+3933310!$! 32654&#"mMOgսj$$:#=ڪ\s5@   ]Y `Y?3+?+?933310!&5!232654&#"ʼϸm{zkl{zl1+,(0w2@   kY iY?+?9/+9933310";!&5!2.%glBdRɲ),Eg':\Z's%<@'  &'#  _Y ]Y?+/+99993333104$32&#"#"&'532654&'.\ֹZ#FkG?_c[bNg$PBES2 /!3>-4, [@2   iYY   IiY?+?9/_^]+^]]]+9/99333310)!!!!5#N^ N@(      `Y `Y?+?9/_^]+9/_^]99333310!!!!#5#?J/#b@6 !!!$%iY$IjH   lY lY?+?+9/_^]]]++993333333210!654&#"'632!327#"&547hŅ0 ,(3Ou-;+#13Ka!wV/#ySjV'p^m\ #&P@* %"'(%&@ _Y _Y ?+?+9/9933333310327#"&5475654&#"'632% 5923No!/43Ox"bbG8<4O])PB? #~UdD}FG@&        iY?+?99//_^]99333210!5'%&''%&#"'6! Zq(f\Ru OWH;9-%gS5PkOWfA@"      ??399//9993333210%4''%&''%&%!w8!5{:TeQVLǦbB{~coc'<~[)G@$$   *+  iY $%iY$#?+?3+3?339993333310%##"&'##"&5!3265!3265!)!2> s㈸5 5y5Ç5É}5^j[mB\B5^_;^)G@$% ! *+   ]Y %&`Y%?+?3+3?339993333310%##"'##"&5!3265!3265!)5!27 hX,l1QWpo1QWuj1\ LWsyy1syy\!@@ ! "# jYjY?+??9/+9993333310.#"32>5!!47##"432#>+aqyto?4 >ŀDk; {_¶3J/`\j\qs>@ ]Y ^Y?+??+?9993333310&#"3265!!47##"32"Rcof2 kBRo =k++9@  jY jY#???+9/+99333310"!!63 #"'3254.OB6j}2G!\-//e Z^7@  aY aY?+??9/+99333310%#"'32654&#"!!632Z唎r-y1u|201N`w3ê?^\#V)M@)% *+ ' iY'"iY'?+?+9/_^]99333310467>54&#".54$32327!"$VoQTOW!ρi3kjIHbVBTUS*&i'q1çG+:H0KT^\1s,O@+'!-.! * `Y*#_Y*?+?+9/_^]993333104>7>54&#"&546323267#"$1Mwb1[KH\+JL}]2`fez-qM:PeEQRRMQO\sγ|E=K\;,40&9 ]@/    kY iY?+99?+99999993333333107'&#"'632>32&#"! !9>Y,6BW]^<6XW`D--2A/qZ})Oa^R)E6H1^]@/    _Y `Y?+99?3+3999993333333107.#"'632>32&#"!!':&028G[^t25sX[G721C5{`B1;DE:VDw#T@. !! $% iY  iY iY ?+?+9/_^]+999333310"3>3 ! ! &2654&#"NJ,m`Iiƨż˳R\fXiT%|\s"`@6  #$9/ Y`   ]Y]Y?+?+_^]9/_]+_^]_]99333310 32.# !2654&#"5632*tXIQ`qZT3u/r+ ?$,'-TJBK)L#!O@+  "#    !iY???3+39/_^]393333310)#"#.546;!32#654&+;uA<5rccH-"_I) ;V5C233*7\sF}Mw{\sJsw&4@   ?3?39/999333310 #!!3!!4#/ } {vXDJslY^ -@   ?3??9/9993310)#!! !mJLs$Q@* " "" %& eY ]Y]Y?+?+?9/3+39333333210#"'#!!!5#5332%"32654&ښ\yyqj+t323g2.+ ?ni:pwN6) 4;2bt6&-&w4\qsT:^Zf^ 9@#    gY/ ? _ o  ??39/]+9910!!!! '`TaT)ma5 ^5Lg^@U   gYv   d   $H   gY  gYgY?+?+3?9/_^]+9/_^]+_]]q]+9333310)!#!!!!!!#VwHe/'b^Xu(28@7 %6) -- 59:) gY)@%H) )6fY"H@"H /_Y#`Y_Y3_Y?3++?3++9/_^]++_]q+_^]++999333333102>32327#"'#"=!.#"5>7>54#"27!%HdÞYMcsBxĸYq||czRiufixW TEBNdA@ꔂX,$W[zfo|H^&x@F " "'( gY wf$H &gYgY?+?+9/_^]+_]]q]q33+339333333103!23##!#!32654&#'32654&+d7Te^ef`\ag/U;kLOJF;FB9ys&@ gY gY ?+?+9310"!267#"32&HSPʹ 9+ 8NHd^(@    gY gY?+?+993310)! !#3 di7h`9^m!Fd^ r@B    gY~ m$H  gY gY ?+?+9/_^]+_]_]q]3+399333333103! )#%!#3#3 Fji7j$h`fmD^ a@;   gYvd$H gY gY?+?+9/_^]+_]]q]+933310)!!!!!Dl^{^Vo'q@C"  ()gYvd>N  %%gY% gY ?+?+9/_^]_]]]q]+993333310#"'532654&+532654&#"'>32ISm`hikiքXhq="&VRJODQ;DZ@9J^ >@#   cYP`p  ??]q+_^]_]933310#"54632!!ݦSS1ߕGO^ $@ gYP` ?/]+93210"'5326539Q>=6NE]hH^ 6@     ?3?3939333310)#37!Hh`5pEb^ u+{^ P@,     @ gY?+?9/_^]^]99933333103'737!5Xq\qT%iAww^4@   ?22?399933333310!##!3!#47# JJ s^\n^,@   ?3?39999333310)##!3&533 Tt^Hyo (@  gY gY?+?+993310! ! ! !  !"1,'}}Ho&@ gY gY ?+?+9310"'632#"'532654&Ey9PRE+N9 ɶ\D &@  Y Y/+/+9933102654&#" # 46$4ό?m{zkl{zl \-@  Y/+/9/933310! '>54! !.\>+K%2./% ĘZH|>_FK%?#Q@.!  $% Y Y/+/+9993333310% 47'76! '"654&27-dhe/3RlTcCR;1!zoDDr57.=zk3 5&6zlXws+2@(&0& /340fY"H@#H #]Y`Y )]Y ,_Y?+?+?3++9/_^]++_]q+999333333310 632!"&'#"=!5.#"5>4&#"326267!-pGdmZl{zkl{zkcv >tsNM &2,$vun}u5^"V@.  #$!!gYgY?+?39/_^]+99933333310#"&5467.=332=33254#"CT`dLIme.Wi-(tυ!\'s@  ]Y?+/39910!54&#"!5!2l{zl' 0\' @  ]Y?+/39910%267!#"!{xk87pᥡ7^<@   gY   gY?+?9/_^]+993331032654&++#!2Rlq\en%hoPWUSQo^^ I@&    gY    gY ?+?39/_^]+9993333310 !.5463!##";5E`hty^gdgsN$oBNKV^ ?@    gY  gY?+?39/+99933333103!"&5467!#";fb sgdym'RWM+^%@ gY?+3?9310!#!5!!T`^%@    ]Y?+?3993310 !3265!1irbe1`jN+3<@   Y Y  /]+9/+9933333107!2654&#!!#Nyy^QS+1V^r1)3rD+d3 ,@& *!&&!-.  @) H/@ H!+!"Y!*++*Y++/]+9/+99//+_^]3+]393333333310#"&54632#"&54632%#!!2654&#!!^IBBKLAAJIDBKLA71{| , +^^^"'7)7^"@    ?3?3399910).'!3673>3%$ '! 'V9*J^$5j{L~V^ ;@ gYgY?+9?+999333310)5!5!!%83D^X@1 gYo gY gY ?+?+99/_^]+393333310!5!#"'532654&+ZʘKT` ="&SYOKdRo%<@!&'# gY#gY#?+?+999333310467>54&#"'632327#"&dBKHAQG=priP(TT-rA:-3%19!!NRyY.,39$;BFEo!2@  "# gY?3+3?999310327#"&'#"'53267.54632Db-Q.1bfm^bb35D6bCwiӬkRXL Vxc MU~nęj^@gY?+?99310!#^b^%^ @    ??399910#!#.+  +"Z^*9^%@ gY?+?3993310!#!#!9Zb^^m?^@@   gY ??339/+3393333310+##"&53;332653?&&vyHwfZlo/^)@ gY gY?+?+?99310!#! #"'532>!/EJvA3-)).)E tD 6@   ?  NI??39/]3999333310'!#! '%N}Nyy}o~:dDg@=   A 3    Y H  ?N IN?3?33?9/]39/]]q]]q_q39333310!5!#!!!!!%!#DlXfp]߰ `@8 !"N:$HHXIN?3?29/]q+]q_q]39933333310!2#!32654&+32654#JVNdZϸs[SYaw]YyWvwcg6>;2sDF $d@:  %& N:$HHX$IN?3?39/]q+]q_q]333339333333103!23##!#!32654#'32654&+J:7ϸss]Y[SYawyY67]DF6>;2 @    I N?3?3993310)! !#3 N y`HHj N@.  A3 YHI N?3?39/]]q]]q_q3933310!!!!!!d}i߰m L@-   A3 YH I N?3?39/]]q]]q_q3933310!!5!5!5!!mk{ff߰q8@ ? JO?3?39/]399333310!#"4!2&#"3275#5'Lu~KD; =7  N@-  A3 YH IN?3?39/]]q]]q_q399333310#!#3!3 ZJsT9 0@   I N?33?3393333310!57'5!9偁{97{{7n9 !@ @ I?]293210"'5326533M8E&K@[\J 6@     IN?3?3939333310!#373^X!A/wZ@5@ IN?2?993103!4@   IN?22?399933333310 ##!3!#47#f 3 4 _'iB&@  IN?3?399993310!##!3&53BA kx@/Bb,@   I N?3?3999933331033!#47#!heqZ  @  JO?3?3993310#"5432! !"Z   VXm"A@   #$!! I O?3?39/399933333310#"&547.=332=33254#"@P`]LCߺB\*+gS'hjjjk*@  NI?3?9/399333103254&++#! JV\g`TϙKI<@     N I?3?39/q3999333331032654&+#!2!HpafoD55IEH=={UC'L @ NI?33?9310#!5!!)%F @ I O?3?3993310#"&533265kjodoDz}hovm%"@  I N?3?33999910#.'#3>73673#ۅ'  њ! ))Zգg)9ǖfZ+Z1"3@ ""#$ NLO?3?3?9/3999333310'##"&546?54#"'6323265,8|ZvcJXa^mM]iE2u}t"w9Fi;>ZVEd!?@! "# /KLO?3?3?9/]39993333103>32327# 7>54#"'-;xYvdJW__kN\hG0t}u#v9F=@XUFhb.@   K NLO?2?3??999933310"&54632373#'#'26=4&#"P+MM\LP\JQԽqbeshk~ltvb='28c@7 $6( ,,59: ((/(((6/""L33O?333?2239/]]33/]3999333333102>32327#"'#"&=!.#"5>7>54&#"27!d5Hrvyd{HS2}Xh^FR[Y6.KWqLGIS=u}u;^7F\.,g[e= =@2&UDLX ,@  !"  F NLO?3?2??999933102#"&'##336"32654&XFo,% MRWGKVFPPջ/5V5d6sghzmwrsqhb0@   FNLO?2?3??9999333310"&546323&=3#'#'26754&#"P߬+MMXNP\JQԽqp,es`o~lwshVJ@+   / LO?3?39/]]]]q399333310"!4&"&54632!3267CU7P+ʷi^OEn5OUMWqο׼fZf7b`6@  L O?3?39/]]3933310267!2#"&=!.#"56E[V)Ϻmav=OUNVмֻg[e=8X%$Y@4! %&$$$$$$$/$$$ L O?3?39/]]_]]]q3993333310#"327#"&54675&54632.#"3{sbßy`gF5sKRP_i00V;5tqM\$aj:""&.&H"Y@4 !#$!!"""""""/""" LO?3?39/]]_]]]q39933333102654&#"'632#"'53254+5NoxLZAšdVֻjV".%'1:p]'WFo1;Z\f^` &4@  $'($%K!LOM?3?3?3?99933331026754&#"#"'532=7##"&54632373WLQYLʱPULap~lut-; csֻqb{ .@ P`p K N??]q2933310#"&546323#{y<=@9yi7226h5@    FK N?3??93933310?3 !#3faX` ZwG323>32#4#"j{TOު &GA Q{YKǨvb<5q5<Ǩyq^0@KN LM?2?3??99933310"'532654#"#33>32yL9'/.,]Uު )K^ 9< wb<5{h}  @  LO?3?399331032654&#"#"&54632LNZYOO[XN1ҹо;svvsttttػF @   LO?2?3993310"&'532654&#"'632JSl54jETcfWKe=j! $tswq-3h5}#@     L?3/]]q39910#54&#"#54632}O[XNо5ttttغh}5 #@     O?3/]]q399102673#"&'3XNѸNRspոps^0@   KM LO?2?3??999933310"&'##33632"3254&REl2 ޵ QQLMVI-7p^dsּ}cn}lss1fb.@   KO?3?33393331027#"&5#5?33#?LWuzk{@P#wXGXy)@  KN O?3??3993310'##"&5332653 (M݃_Re<73'!w3 )^8_&+ZOHf{&8@!'( G$O?3?393393333310&54632&#"#"&54'326[UXf<1ef|4We\JPZ1her''F*%I1Ao܅L}YIU_h^ #8@##  $%L ML" O?333?3??933333104&#">.54674632F?*.a|TQU?8daNqp7D|NɬndeQUathˮ^8@ !KML M?3?2??999993321023327#"&/#'.#"56`e(66)!)AMbp#Lma8"0$QRuV/# heRg6$ `{ ,@ _o R P??]q2933310432#"#3{yy{1iif`w!@  R PS?2??9993102&#"#33>X0 !jyި! /wf`tGISyiP?5adi P?5   Q?55fqR?5^w2@  0   SQ?3?3]q9993310%#"'##4632%"32654&^kX ӿLJ7_NEE5tDռ.fs;n{{kh S?55~R?5hs$+p@@" )),- (fY(i( ((((%_Y`Y ]Y?3++?+?39/_^]]]+_^]999333310 '# !3265!>32!3267"!.1irbe1+G0ebP_qr`j=&,销*.('}oq{'2p@$"11134!o %@H%%  (]Y .]Y ?+?+??999/+3/_^]]q33993332103632#"'##&#"#>35!3273#"'"3265 kp3K q#1 Jqkt^oBЏ{nEo 5\'4~@,6!22 & ++56$$$$o$$ $!$ @H ' '/]Y(]Y?+?+??999/+3/_^]]q3393333333103273#'#'##"323&=&#"#>32526754&#"qK p"; hl Jqumq{fqroZ2 (`nnGˈ!u+o@>#- * ,-    ` p    *!&]Y!*`Y*?3+3?+?9/3/_^]]q339333333323310!3273#"'!&#"#>35#5754632&#"! Jq%-q#Ϟ{N\NA:yo:4ѓRR/M? 57+-$$ ?"-(-  * 1;;]Y?3+3?33?999/_^]3/3/]33/9993333333332310&'!.#!#46733>323>32673!54&#"%54&#"ϧC yk)-nY-nJuxyQWyfPqQWdcL&e|MWNV$h/# yyc .-yyus'@2)&  () &_o @ H  #]Y?+?3?99/+_^]3/^]33999993333332310673!/!#46733>3254&#"Q uxD |h)3rV^gsFc}1))a~QSǤXyyns(4v@C 2-%2256 o  "")]Y"0]Y?+?+??999/3/_^]]q3393333333210"'#3273#"'!5&#"#>333632"324&p JqL q#+kiqhktemgl!Rws%j@@ ""&' o  @P` #dY ???+99/_^]3/^]]q33933332102&#"3273#"'!&#"#>333>>)%5JqL q#-4s /mm^s}w!]@8!! "#   o    @P` aY?+?9/_^]3/^]]q339933332103&#"#>325!2&#"3273#"'Js}vw#D=Jqmu!m s1W@1.30%0 23---% -+#`Y _Y?+?+9/_^]/933332310#"&'532654.#"#47&54632.#"673zKUQZL Db35#5?3!!3273#"'wPprJs#X9JoI#3mfm8A>^ y@F !"i`    & _ ^Y^Y?+9?+99/_^]3]/^]]33]933333310)5&#"#>327!5!3273#"'!%9qYWB!MwL\ X75'l's &N@'$$& & '(#!^Y!_Y&!!]Y!?+?+9/+99993333310"324&%4$!2&#"3632#"'#!xio{klu %RBVˤ D@&  *YH KN?3?39/]]]]q399333310#!#3!3C3L^!\@/     "#   bY   ^Y_Y?+?+99/+9339333333310!#"$5467'5%!5!32654&#"LزIXHwpovpuk|$peRZ~~v/f!4@Q+# !%% .( &  56& %, *" *'/ 11]Y1 !@$!$`Y! ]Y?+3?3?3+3?+9?3_^]99333333333333310)4&#"!327#"'#7&5#5?3!!!733632!X]q7 PprQ6uGX9'1G fyy##3Xf\\pgq^ J@+   Y"H|I ??9/]]+]3+3933323103#!#53!ѠϠ1)^W@2    qYg"H<L ]Y?+?9/_^]_]+]3+3993333210327#"&=#53!3#I#3y@s&~@I  $'( eY#w  "HL \   '    ]Y]Y?+?+_^]??9/_]]+]33+33999333333210"'#!#53336323#%27!"!.pϠ+kұb`\ ichc a;upqpqD^W@.   Y O  ]Y?+?39/_^]33+339333333210#"$'#53!!!325!11h^k@@|z/^$a@3 " ! %&" eY `Y^Y?+?3+3999/33+339333333210#! =#5367#5!!&'5!#3267!/\\2x2,t4{i}  DŽWU[Q;{~) -L@)++./  _Y!]Y (]Y ?+?+??/+99333310"'5326=#"'##!3632"32654&\G2033p31 k}qhkt^op)3Dq{EV!\) -U@-$+$$./ _Y(]Y _Y !]Y?+?+?+?/+99933333310"'5326=#'##"323&5!326754&#"]G4/33R; ho 2f*umo}fqr)3D23}bf!))u#W@-%!  ! $%_Y]Y `Y _Y ?+?+33?+/+993333333310"'5326=##5754632&#"!!3F]G2033Ϟ{N\NA:f)3DyRR/M323>323]G4/33QWpoQWuj)-nY-nf)3Dyyyy^MWNV)s"H@&   #$ _Y]Y _Y ?+?+??/+99333310"'5326=#4&#"!33>323]G2033V^r)3rf)3Dyy^QSs ,N@***-.  _Y!]Y (]Y ??+?+?/+993333310"'5326=#"'#!33632"324&\G2033p+k}qhkte)3Ds;JV!Rq)ws?@!   _YdY  _Y ?+??+/+9933310"'5326=#33>32&#"3\G2033-4h>)%5f)3D^^s \)s3K@)!1''145'.0  _Y%`Y _Y ?+?+/+993333310"'5326=#"&'53254.'.54632.#"5]G4/33@KzKUQ,lZy7ʿ\TLWz:T)3Dy ! (6`$-9&6\wWX$.I)<;5\xSX鱣#'L@'# %()# bY##_Y]Y]Y?+?+/+9/+9933333210"'5326=!#"'532654632&#"!]G2034屭r>=33:`L961=)3DB9;=>H)^=@   _Y _Y?+?3/+993332310!367!3!"'5326=!?$ (?]G4.34^yl`}3D )^O@)       _Y  _Y ?+??3/+999933333310"'5326=# ! !!3T]G4.34>{ZZw)3D;#d7)^F@%  _Y  ^Y  ^Y ?+9?+9/+9333310"'5326=!5!5!!]G4/33B )3DQV)ju&0w@B%2+ 00 12'gY?O "_Y `Y_Y -_Y ?+?+?+_^]/+9/_^]_]+9933333310"&=#'##"&546?54#"'63233273265>;M®eg3302Gvj)aK 1QeB5XZze\)s*S@,,) "" +,_Y&]Y_Y ^Y ?+?+?+?/+9993333310"&=#'##"3237!332726=4&#"R; hof33/4G}jo}fq)23B5!\N#(5W@.73 ", &&"67']Y0]Y)]Y ]Y ?+?+?+?+999333333310"323&=4632&#"327# 5467#'26754&#"o `L961=934=>}  hjumo}fqr23}b=>9BiL7f+!\)s&m@>(  $$'(  # fY#i# ####_Y   _Y_Y?+?+_^]/+9/_^]]]+_^]9333310 32!3267327#"&="!./eb33/4IfUaanr*3*.VB5{qq{N)s2@Q14+! %% + 34 ""{YkYtb<  ._Y`Y'_Y?+?+/+9/_^]_]]]q]qq+993333333310"&=# $54675&54632.#";#"!267327=T`瀐sX^wMqn gY33/4I) k 1э.&026B7AH}-)PB5N)#s4@P"## 3-- & 56"{YkYtb<  *0_Y`Y*`Y*?+?+/+9/_^]_]]]q]qq+993333333310"&532654&+532654&#"'>32#"'327fV`vpjzMPZw߉up@3302G)(.C>DA8=66&!-'9 "}efVwB5X)s")l@> +'&*+''fY  _Y `Y#_Y?+?+/+9/_^]_]]q3+39333331023327#"&5#"=!.#"5>267!!*33/4Gg_iUYu TosB5R 𔂒&2,$Rqzp{)L@)  _Y cY_Y?+??+_^]_]/+993333310"&=#!3327432#"R1f43.4GڦSS)^B5VGO?)s"9@!   #$_Y ]Y]Y?+?+/+99333310"&532654&#"'63 !"'327qF_rxdW*&33/4G)/2BLsB5)#!9@#  "#_Y ]Y ^Y?+?+/+93323310"&532654632&#"327ۋ=33:`L961=3302G)B9d=>uB5)^"H@&!$  #$ _Y_Y ]Y ?+?+?3/+99333310"&=#'##"&5!3265!3327#R)1s1V^r1f43.4G)NUsyyB5N)#^&i@7" & ""'(&&fY _Y^Y `Y ?+?+9/+9/_^]+9993333333310%!5!#"'327#"&532654&+?òp@3302GgV`}kVwB5(.C>C8\2@   K NLO?3?2??99993333102#"'##33>"326549K !oRVDK׷qcd4?+whvs^@  LO?2?39310 4632&#"327ö~r>nDrg0h4-C^h(E@" &&)*!#   L# O?3?39/399999333310632#"''67&54632.#""32654&5[f{S i)H|p=0R-^V\F)a1=*3'Ti\it7"S0ILm4oL@("%R;'X@2" () pGG%O?3??39/]3999999333310&'77#"&546327&''4&#"326q*?FbPCugjȮɵ13RElSJUIOOTI?#{/3`kG_CuPcm1J\bc_eqH#\@5 """$%""#######/### LO?3?39/]]_]]]q399333333102654&#"'>32#"'53254+5?isHT{y=_O^S̯dR!/%%1"o^'UFn1;Z\B1@ N GK?333?3?993333310###5754632&#"3;rrhX5O&Vef7>~!^6b.@    KM?2?9/33393332210"'5325#5333#1T5/3gqqnnbww5ˉh{^b- &6@  $'(#!%K!LOM?3?3?3?99933331026754&#"#"'532=7##"&54632373SGKTHrIQLal{msr-; csһqbhR-@   M KO?3?3?99933331047##"&5332653%mQ}WOh<\;8>vj \@7 _o  / KN??9/]]]]q333]q29333332103###533'432#"?ooooqssqD5hhf? @    KO?3?99310327#"&5^\;JPd{X#F  $@  K N?33?33993310'5!!57x{{@ $$ +, K N'L"M?3?333?3?999333310#4#"#33>323>32#"'53254#"BuQIѠ "vI=!uNxN1))VuSFævb98q7:xwvpb";@ " #$ KMO?333??339993333310#"'##"&533265332653#47HG;"xJuSFsSG sq7:>vo>yjB(MbV,@ KNL M?3?3??99933210#4&#"#"'5326533>32V32V)#)%%\}f]ZiBgX]bѧ=ҧ/G_~  ^^~ / ~^b,8@!'-.'%LO M?3?3?399333310#"'327#"53254&'.54632.#"4"!%""*G?1s>jzW@9e5\3))4ih @ MG?3?3932310#"'532654632&#"b{tS(%)#(zzD1$("*f~,(s )+1bHm <@  !"K OM?2?3?3333/9333310"'5326=#"&5#5?33#327E,""##{tes=3*;Jfb #/ZsXG-+zp#^@5        /   KNO?3??39/]]]3333399333333310'##"&=#533!33#%27! %Hjj#ooc;6E''ى)u=@    KO?3?333999333333102654&'5!##"&547#5![nKKsҷՍsnJfiY|-beYifN @   K O?2?3993310"&5332653쯲IODCӴVdXXd^j'@ LK O?3??39933310#"&53326=4&#"5632jOWJL'#!+2SpsVdXXd(.~z @    N K??393310 #&'##$ۓ%]A>`JR 0@ KN?3?39999333310!5!5!!d>f}柉)Rb;5@    K NM?2?39?399333310"&=!5!5!!327XiPd>f%#$.bgtw}柉)/# R9VK@%   ! K N?3339?399/3933333310'67!5!5!3>32#7254#"( odDV@yWM]y1&4`'.&}|c]Ong=4=4-h6@  MK?39?9/3393333310!5!#"'532654&+kPȬDAktR (Ԟ8""[\ZX^F T@3Zy/ G O?3?39/]]]]]]qq399333310#"32267!"!.FPJIROG:I_!^ @ /29/310432#"&%432#"&#&'5!^9JJ9;F>>EK87!#59JJ9;F9JJ954.54>54#"'632Z!(!!(!#+#2,.C6h & $$ZN?  7=   u%@_ o   _/]3]210"#".=332676;Nwp:iu8 +.IK5&-&;w[1;46?@o/]210!7!#V/@o/]310!%7!/V#ŝ?@ _/]2310!'!L"V/@ _/]3310!'%!/V"L99+@_o/_/]2/]3310%7%'V1V}}9+@_o/_/]3/]3310'%7V1V1}}J  H/+2?9102&#"#3>h'%%>4:B LTwE#,TR @/?/_/]]10&'5673_x-uHmgiG:,g#@  0O_/]]2910#.'537673%#567&'5 NRh*=*#hd/_'nO=[T2:&A;%+55+55\&V D?9%+55^Rf&6'ORd /&15%(,%+5+5+5\R&V'Odm/3%&*%+5+5)yf&7OR@  %&+5+5/7 &WOb@  %+5]q5)Ry&7d  %+5/R7L&Wd} %+5)y&7M@Pp %+5]5/EL&WM@ Pp%+5]5)gy&7K1@  @ H @ P p  %+5]+q5/gL&WKz.@@ H@Pp3# %+5]+q5r^&8jz!@ P% %+55]55r^&Xj1z!@ P' %+55]55Y^&8R-H@ HP# %+5]++5Y^&XR/0H@ HP% %+5]++5g^&8K.@@ H@Pp %+5]+q5g^&XK1.@@ H@Pp" %+5]+q5^^&8 HR@ %&%1 %+55+55 &X H '3 %+55^7&8 ER!@& %+555+q555&X E %+5553`&9R/R@ &%+5+5&YR  %+5R3&9d  %+5R^&Yd   %+5f&:OR@ "%%&+5+5&ZO1 %+5R&:d^" %+5R^&Zd %+5Vf&;OqR& %+5+5 &[O   %+5VV&;j=R@ !& %+55+55 &[j   %+55f&<OFR@ & %+5+5&\O  %+51qs&=KR@ & %+5+57!&]K  %+51Rq&=d  %+57R^&]du   %+51q&=M@ P p    %+5]57^&]M@ P p    %+5]5&KM7@Pp %+5]5/7&Wj$ߴ( %+55&ZP "%+55&\P  %+55V;u&D U.*%+5?&AO{@ &  %+5+5#R@+## $%lYiYkY?+?+?9/+399933333103!2!"'32654!#57.#"?1(㴺ݜPNX"`%"թK0,mjE<\X&~s  +1%+5\X&~?Ӵ5+%+5\5&~/4+%+55\5&~/4:%+55\5&~` -4/%+55\5&~L 4>%+55\&~#CO%+55\&~CO%+55&$pt%+5?5&$ct%+5?5'$3@ I%+55?55'$3@ I%+55?55'$3@ K%+55?55'$3^ %+55?55{|'$!@)))?))22%+55]q55{|'$!@)))?))22%+55]q55N%X&N '-%+5N%X&31'%+5N%5&  0'%+55N%5&06%+55N%5&1 10*%+55N%5&  09%+55'(pt%+5?5'(ctv %+5?5+'() %+55?55+'()%+55?55'(ȴ%+55?55'(ȴ%+55?55X& ( %+5X&j %+55&B( %+555&B, %+555&s ( %+555&^ ( %+55&F -9 %+55&1-9 %+559'+pt%+5?59'+ctv %+5?5'+) %+55?55'+)%+55?55f'+ȴ%+55?55f'+ȴ%+55?55^|'+$@)))?))00%+55]q55^|'+$@)))?))00%+55]q55X&3  %+5X& %+55& ! %+555& %+55,5& 3! %+555& ! %+55&  %+55& %+55',-pt%+5?5',!ct %+5?59',^޴%+55?559',^޴##%+55?55N',s˴%+55?55b',̴%+55?55)|',N!@)))?))00%+55]q55)|',N!@)))?))00%+55]q55\X&Ru $  %+5\X&R?$! %+5\5&R+ -" %+55\5&R+ #1 %+55\5&RR '- %+55\5&R= #- %+55v'2pt@  %+5?5'2ct#%+5?5'2@ "%+55?55'2@ "%%%+55?55'2"%+55?55'2@ "`((%+55?55X&  %+5X&Pд %+55&D)%+555&D-%+555&u )%+555&` )%+55&H .:%+55&3.:%+55L'<Nct %+5?5'<%+55?55'<%+55?55|'<!@)))?))--%+55]q55m{X&o 2. %%+5m{X&92/ %%+5m{5&% ;0 %%+55m{5&1? %%+55m{5&` ;;, %%+55m{5&L 21; %%+55m{&) @L %%+55m{&@L %%+55'vpt$'' %+5?5'vct.ݴ(( %+5?59'v'-!! %+55?559'v'-00 %+55?55'v-w(( %+55?55'v-|77 %+55?55|'vs$@)))?))ʹ'' %+55]q55|'vs$@)))?))Ǵ"" %+55]q55\!&~!/+%+5\!&~Z J3/%+5N%!&ƴ+'%+5N%!&1 T/+%+5!&/ %+5!&h > %+5;!&  %+5!&ڴ %+5\!&R" %+5\!&R= =" %+5!&'%+5!&` 4%+5m{!& )(%+5m{!&7մ0/%+5\X&~'s9@ GA"% +1%+5+5\X&~'?9FB"%Ӵ5+%+5+5\5&~&/9NJ"%=+%+55+5\5&~&/9NJ"%4:%+55+5\5&~&`9@ OI"%%=.%+55+5\5&~&L9@ OI!%4>%+55+5\&~&#9^X"%CO%+55+5\&~&9^X"%CO%+55+5&$'pt %+-%+5+5?5'ct&$! %+-%+5+55?5 ''$3!@  %3&5%+5+55?55 ''$3!@  %3&5%+5+55?55 ''$3!@  %3&5%+5+55?55 ''$3!@  %3&5%+5+55?55 |''${,@###?##<,,$$%B5.D%+5+55]q55 |''${,@###?##<,,$$%B5.D%+5+55]q55X&'@ 1% %0 %+5+5X&'j޵1% % %+5+55&&B޶9- % %+55+55&&B޶9- %$ %+55+55&&s@ 9- %& %+55+55&&^@ 9- %' %+55+5&&F@ H< %-9 %+55+5&&1޶H< %-9 %+55+5 '+'pt%)+%+5+5?5 '+'ctv %)+%+5+5?5 _'+)'H# %1$ 3%+5+55?55 _'+)'H###%1$ 3%+5+55?55 6'+'#ȵ%1$ 3%+5+55?55 6'+'#ȵ%1$ 3%+5+55?55 .|'+'/@###?##00%@3 B%+5+55]q55 .|'+'/@###?##00%@3 B%+5+55]q55m{X&'oT@ D8%(.%+5+5m{X&'9TD8%2(%+5+5m{5&'%T@ L@%1( %%+55+5m{5&'TL@%17 %%+55+5m{5&'`T@ L@%C1, %%+55+5m{5&'LT@ L@%21; %%+55+5m{&')T@ [O%@L %%+55+5m{&'T[O%@L %%+55+5 'v'pt$'' %δ>1@%+5+5?5 %'ct'v@ 1%ʹ>1$@%+5+5?5 ''v'q@  %%%δF9,H%+5+5?55 ''v'q@  %%%δF9,H%+5+5?55 +''v@  %%%ʹF9,H%+5+5?55 @''v)@  %%%δF9,H%+5+5?55 |''vs*@###?##344%ʹUH;W%+5+5]q55 |''vs*@###?##944%ʹUH;W%+5+5]q55\+&~N)  1+%+5\&~M)  +,%+5\!&~&!9?;"%/3%+5+5\q&~972"%+5\!&~&Z9@ ?;"%J3/%+5+5\&~R/;%+5\&~&R9NI"%/;%+5+5}&$NVR@ %&+5+5&$MXR@ %&+5+5&$x@ I%+5?5&$$I%+5?5&$%+5X&@  @ _  /]]2931046325>5.A1AMBF5D5:WIqV1 = (@    Y ?+/_^]93310327#"=#+*"=_jg31 ޤW R /O@1 - !01,$$$@ H$)!$o _  /]3333/]3/+]33933104632#"&%4632#"&7".#"#>3232673F;9JJ9;FF;9JJ9;FG-OG?,( } u^1QG=*) }rF?>>?>? %+5+5&R3% %+5&&R3޵9- %% %+5+5'(Y %+5?5'(%+5?5='+Y %+5?51'+%+5?5 6&+ %+550@  _o_/]]29310#.'5!%46325>5&aV+$50@  _o_/]]2931067!#%46325>5&17 2@4AGu9@jB802Q>aV+$&R@7 $ '(#@ H  o / _   /]]22/3/+]3393104632565.%".#"#>3232673:3>?qq29-OG@+) } u_1QG>*) }r)1H3ZeP50!*.l!-+tw+&N  %+5&M  %+59&@ ' %+5559&@ ' %+555&R  %+5&@  %+555}&,N$R@ &  %+5+5?&,M$R@ & %+5+5', Y%+5?5',˴%+5?55.@   _o_/]]29310.54632#.'5! j@9vGA4@Q2G<5.@   _o_/]]29310.5463267!# j@9vGA4@:7 u;`$+V`>Q2壘;E&P@5 $ '(#@ H o   /_/]]22/3/+]339310.546327".#"#>323267392qq?>3:#-OG@+) } u_1QG>*) }r&05PeZ3H1B!*.l!-+tw+&N'%+5&M'%+59&+/%+5559&+/%+555yX&{  )% %+5yX&H)& %+5&R5&%+5&?7C%+555}&<NR@  & %+5+5&<MR@  & %+5+5;'<=Y%+5?5;'<=@ %+5?5}'3ct!v%+5?596@    P/]q223933310#.'53%432#"&%432#"&2u ?ʁ9JJ9:GR9JJ9@" #$ `p!?3?39/]39993333104632&#"3632#"&2654&#"3C<.6G~zT5B8;8EBpb"FA7B@+AT+9"6@  #$ !?3?39/]3999333310#"'53267##"&54632%"32654&I613G~z5B8;7FD3pb#GA7A?+CSZS1z S?55hSVx  S?55hU}z S?55'`wj /@     R P?3?3999333310%373 # #9|r bS`x S?55+d@P YZ Y YY?+?+9/+9/_^]_]]q]]+_^]33933333310#"32&#"!!!!!! 㲒ZvYg]V)CD9ߐ{J%+u@F& ( "##"  )( ,- ()#)%" "kY   %kY/3/]3+/3/3+9933333333310'67#7&'#7&5%7373&#9obBx!!NB%/- !KF!7W>=DdkH-9A Q<g# sPRf0Jh%K@&"&' kY  kY iY?+?+9/+999333310"33>32&#"67# 4$32.ӣѼ# #gQ.!ybŚ$dl}VJZ 'KzmZi6*BT(W@.! !!)*!%  ]Y?33/+3?33?99333333310)5#54&#"!33>323632!4&#"ϚfQWuj)-nasGKQWpohlyy^MWqR'yy#&*@m $# !)'') &% +, *pY % "pY//$'?3?39/]q]+9_^]]+9933333333333333103!333#3#!###535#3'#35#3&'#wBuuuuwww[/TdT ) )A;@F/* 5*$$* <=*5$--3_Y- iY   iY "_Y?+?+?39/_^]+9?+993333333331032654&+!!23!#"&'53254&'.54632.#"Hj]chD_gAE?FzfĮJBu32"32654& #!!32654&#"'>\yEjO,ѺFW &gKa@CR'zTO\Ksre)4n%ε cnANsZEN~^$$'3q",[@/ '#-.*   %lY  lY#?+?+?/9/999993333333210#"'532654&' #7.546324#">faxķHH0?2=6Kܨj=}S̷^\8,(.T{m鱴Ad\oYAMMOE4 <: {Y:!@Q   pY nY/_0`p  pY?+3?9/_^]q]q+99/3+39333333310!.+5!!3#!53267!z]H&{mqhVaNiA_W; dD"2Y@4# ++ 34       p  /'?3?39/]39/]9/3933333103254&+#!2#%4$32#"$732$54$#"吪SY/^^m+*լ֭ۢQIE^ZƬ֭+* +15@M ,1 .44&267& 11.0@#?O 54 ?3???99//]3]399/399333333333310'##3#"'532654&'.54632&#"/ #21#f=h86>A3[M-t3N%'ADuP78L+@ir1 %9L5`q1+!,^ =J=J^ <@!  `Y `Y?+?9/_^]_]+93310!!5!!5!1M^ :'&{u?5&t'!uB$?5?sCd^D 1@  p /3/]/93310#&'5673!9>HH>9)DH$HDV @   //293105673&'#DH$HDV:=HH=:#d^D 1@    p     /3//]93310&'3#67!59>HH>9#DH$HDV @    //29310%67#&'53+DH$HDV9>HH>9d^D?@%  p /3/3/]39333310#&'5673!&'3#679>HH>99>HH>9)DH$HDDH$HD(@   /2/29333105673&'67#&'5DH$HDDH$HD:=HH=:9>HH>9H0@   /2/2293333310!!5673&'67#&'5 DH$HDDH$HDhPX:=HH=:9>HH>9wu)}y`@ //3993103!!^j8^@   /3/29933104>32#4&#"rуwfŠdHP @@&  P_ / ? o  /]3/]3/]3993333105!5!5!d8d7 @  /3/29933103 %! {RVX9?@ /2/99310!!#X?#@  /3/99310#4632#"'&'&#"}?L3% &!" @/)3 )''#i@  //3993103#"&546323265#9P3## #>/'5)%37/333105! h//93103#בK7@ /2/93310!!#'k7n7@ /3/932105!# )@ //3933103!!n@ //3932105!3  @   ///39333103!!#knn@  ///39332105!3# K7@   /32/9323105!!# kn@   //339323105!3! n (@    ///332933323105!3!!# knn6@ _/]]3_]q/_]33333105!5! BZ@  /2/39933103#3#ّiK K >@!  _ /]]2_]q//_]39333310!!!!#'kkב"7 &@   /33/39933310!###ؑ7nn# B@#   _ /]]2_]q/3/_]399333310!!#!!#iro" :@ _/]]3_]q//_]393323105!5!5!# i)ג(7 "@ /32/399332105!### ݑؑn @@"  _  /]]3_]q/3/_]399332310#!5#!5!ӑtt(F(ޑ <@    _//]3/]]3_]q93333103!!!!k#ב $@   /3/3399333103!!33B%n#n @@"      _/2/]3/]]3_]q993333103!!3!!ّJ%i# 8@  _//]3/]]3_]q93323105!5!5!3 iג) "@ /3/339933210!5!333#ؑn >@!        _/3/]3/]]3_]q99332310!5!3!3!5!#LF B@#  _ ///]]3_]q/_]3933333103!!!!#kk#ב" *@    /3/3/3993333103!!#3#Bnn K L@(   _    /3/3/]]3_]q/_]39933333310#3!!#3!!jr " # >@!  _ ///]]3_]q/_]3933323105!5!5!3# iגK &@   /3/3/3993332105!3#3# 㑑iK#K J@'    _ /3/3/]]3_]q/_]399333323103#3!5!#!5!B㑑tK (ޑ B@#   _   /]]3_]q//_]32932333105!!#5! ki"h7 (@    /322/3993323105!!### ؑnn N@)    _ /]]3_]q/3/_]3339933233310#!5!3!!#!5jtrrBޑ"ؒ @@"  _  //]3/]]33_]q932333105!3!5! BZ# (@   /3/333993323105!333! ؑnn L@(         _  /3/]3/]]33_]q399332333103!!3!5!5!B#oV@-   _  ///]]33_]q3/_]3329333332333105!5!5!3!!!!# ikkג#ב">@      /3/3/333339933333323103!!###!5!33Bؑnnnn d@4  _  /3/3/]]33_]q3/_]333993333332333103!!#!5!3!!#3!5!Bؑtr#ޑ" //3310!!V//3310!!V//3310!!V //9210!!+ //9310!!+ *fw% #'+/37;?CGKOSW[_cgkosw{@"2Jjj#3Kk.FVznn/GW{o6Nff7Og*BZ~rr+C[s :Rbb ;Sc&>^vv'?_wwcsgokcgkk`dh_[WW\XTOSKKLPHC?GG@n?o*Z+[BrCs.^/_FvGwA#&.6>JFF'/7?KG"*2:NBB#+3;OCCG@ PQӸCи@ԯ?<;87{{|4xgkosw3ccdhlpt0`OSW[_/KKLPTX\,H7;?CG+3348<@D(0#'+/' $(,$ #  KH@Ԩx`H00H`x DAGO   L/333/3339//////////33333333333333333333333333333393333333333333333333333333333333333333332333103#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#%3#73#73#73#%3#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#3#3#3#3#3#3#3#3#3#3#3#fiiiiiihhffffffffffhhiiiiZiiiiiihhffffffffffhhiiiiZiiiiiihhffffffffhhiiiiffffffhhiiiiiifffffffhhiiiiiffffhhiiiiiifffffffhhiiiiffffffhhiiiiZiiiiiihhffffffiiiiffiiiiffiiiiffiiiiffiiiiiiffbbbbbbbbbbbc^^^^^^^^^^^````````````e^^^^^^^^^^^`aaaaaaaaaaad^^^^^^^^^^^`cccccccccccb\\\\\\\\\\\bccccccccccc^```````````bbbbbbbbbbb%```````````bc^``e^`ad^`cb\bc^`b%`C%IMQUY]aeimquy}  !%)-159=AEIMQAS@zKk h| OoLldi}SsPp`"eWwTt\&a# +P;;Xxؿ)H*99]'A!/L?? ,Q<%D.55 &I+648<@DHH0M@26:>BFFA "E/22@6<@ ؾ RS@  $(,0ֹ:>Gʽɹ59ȹ48+/D*.C%)$( !@ ?| {~ osw <knrv ;jj\`dh[_cg@OSW8KNRV7JJDHCGLP@4 kKKk  ׹;?HKO@ 3"&*./333/339//////////333333333333333333333333333333293333333333333333333333333333333333333332333310!35#35#35#35#35#353353353353353353353#3#3#3#3#3#335335335335#3'#3'#3'#335335335335#373533533535!355#%355##5##5##5#353353353355##5##5##5#35335335335#3'#3'#3'#3#3'#3'#3'#335335#3'#335335#3735355#5##5#353355##5#35335#3'#3#3'#3+jjjjjjjjjjjkjkjkjmkjkjjkkkkkkkkkkkkkkjkjkjmkkkjjjjjjkjkjkjmjjkjkjmkk?kkmkkmjkjkjkkjkjkjmkkmjkjkjkkjkjkjmkkkjjjjjjmmkkkkkkjkjjjjkkjkjjkkjjWjjjjkjjkjjjjkjjkjjjjkkjjjjj!c c"a c!b!`````````````b^^^\`h^^^^^^^^cccccccb^^^^^^^^aaaaaaaa^^^^^^^^^^ccccccccb\\\\\\\\cccccccc^````````bbbbbbb bbbbbbb^^^^cccb^^^^aaaa^^^^^ccccb\\\\cccc^````bbb bbb{Z//9910!!{!!@  /2/3993310!!!7L17}1mh{//9910!!hmh{@  /3/3993310!!!hKPb//3310!!L//9910! XVRZ//910 7L//9910 LRZ//9910Z79e-)@  ! /3/39933104>32#".732>54.#"wx{yy{xwV`bcb`c``dyyyyxyy{b``bcbbbV^R'/7?GOW_gow@X\PhhTl8xx<|(pp,t ``$dHH L@@D0044DLdt|l\ JrvNvvvBz~F~~~2jn6nnZ^^RVVVVV:>>> *..v~n^V>..>V^n~v&f0b@bb"0p&/&?&&&/]]/]9///////32]32]3232q32]32]3293333333333333333333333310#"5432'#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432%#"5432#"5432'#"5432377349947575#3773865567557R75577667377349944976#58856556\677675577667667+557775555885Z557w55763:C558337775555776557737+558#  //99102#"54>jmsoujlw)@  //9933103!32>54.#")wvuwwwvu}Bwwwutww)#'@ $%  //933103!4>32#".'32>54.#")R`babbab`Nwvuwwwvu}B`bb`c``cwwwutwwsbu &@   /33/]3993310#"&546324&#"326bcfdiFIKgFEgcIN_}khfJHfFffFHdhy $0:k@%+ 6+:;<51@) H168833@ H(.""O"_"3"3"  /  /]3/399//]333+3]+29333310#"'4! 4#"32#"&54632#"&54632327#"'ybbZٗ31Z-!!--!!-+!!//!!+LL=`bHgj34V// -- // --# #-S@4( $./)--!O_-&+++@ H++  /  /]/99//+]]]32]3910#"'4! 4&#"326%4&#"326327'#"'bc0!--!0.!//!.bb>KLHgj{ -- // -- //ۺ#Fs;)5p@=3$-%-"') 3 3 67"   $'0*()/3/39/3933333333333333310373#'#5&''7&'#5367'767"32654&BAe;-VL12WXByd+N P)opÉ;'--qt>}`+%* J-d}>^1N =LJŇP'$T@+  ""%& /@/3/]99//]333339333333310.54632!!#!5!"327654&qqwTVhL%Xw{TV;>wBh}VTylFFxUVy>=TVwR,8F@#0',!66,'9:,*3$-$*/3/399/99933333310&'&54763327632".'#"&54632"32654&+ !C=#"&5467>!aFXdP{XZ[Xe`Ga^%%`]ZRw;3C@&'. 45*   .#  //99//]99233]3910)7>5'#"&54672&'&5463267632#".'Fw^69Zs\=e% qtET'iCJt8v_=1op#8wL/yuzs3B'$'ykVb'NKuu2Qi}x6fZy@   /3/99910./.54632>32bZ[K6dV'!XaXoV{eAkswwucVB   //9910&'6J{FT+t}FiRmX;.@  //9/93333103#654&'#"&54632Ll^/9r@k99}M+/dyyw {7-NsB@#      /3/933/9333310#"54632#"&54632%%5_s{N/+ىs9:wJ6.'DeQovt5-Luguf7u@E    !       `//]q/9///99933333333333310##57573%377```J`X=@`^``F\^` Z ?v@A   hY hY   iY?+?9/_^]3+3_^]]2+_^]3933333323103#535#53!!!!!!6==Qǰqo@>   hY    hY`  ??9/]]]]3+3_^]]2+39333332333103#3#!#535#53!ѠϠ1㬛𬛬1?[@6      o    ? iY?+?9/_^]3/^]]q339933332103&#"#>3!3273#"'!L r#6Jo(mPPm~@I  qY $H    iY 0p o  iY ?+_^]?9/_]_]+9/_^]+3+399333333310!#!#53! 3#32654&+ʤ Dfwn{qlmhH W@."  !"iY  iY  kY #?+?+?9/_^]+99933333310327# ! !'32654&+<0>3_4*d^1R?< i9݁91bihXVV;!&+@O ""$%*+!!)%$,- ")+$%+  +gY+++  `Y _Y ?+?+_^]??9/]+93999?93333333333310#'##.546?'6;>54'7`;32&#"V?! ))p,c'9J'^h}j{v ]lV'*@ (!)   #iY?+?3?393310)&'!!>7!67>32&#"H 50 11+%$, $yJI$!*-(),63ݢ9B3aK0'\Js$+@%&  aY?3?3?+99310! # !!36?!36>32&#"7Vt C0'Pn%|UA#?D^XLUM4|̚ W`Ds$U@-&  %& " hY "rY ??+?9/+999999333310!#527.54632367! 654&#"VwX6zy+*'?VZDF'&S_-{stfdmrs}h:Y3Z,./1^@:  iYF!L;??9/]]]]]_]q_q]q+_^]993310!!!!16Cw{^D@* `Y$ Hv$ I??9/]+]q]++993310!!!V^R3^\u ;@   !_Y ]Y ?+3?+?93333104&#"> 474632^Z9@?owZJֺpNOa <*zw4!,@ _o/]/]99//993310'%'JII)IImmml-@ 0 _o0/]]3/]]10##"&54632PF;9JJ9;F[?==?>?>?>?>?32uxJhg#96+ )@ fv P  /]33]/].2103273#"%ϧW,'l3 $@ /3/99993310!#!5!3d+\+ $@ /3/99993310!#!5!3d+%F $@ /3/99993310!#!5!3n!ZZ @  /3/999310!#!5!3n!DN @ /3/999310!# 7 3g\=H`3R "@ /3/9/3993310!#!7!3bH3o`RN @  /3/99993310!#73^J^sN @  /3/99993310!#5 73gRۇjh0hF@  /3/999310!# 73qZyfkFq @  /3/99993310!#73q_ZGd)5 @ /3/999310!# 7 3RkGHyN!5 "@ /3/9993310!# 7 3Rk`/L-!P "@ /3/9/3993310!#!7!3ysdH^- @  /3/99993310!# 73`wcFFH@ /3/999310!#73vq=Df-q @  /3/99993310!#73qFsGVH!% @ /3/999310!# 7 3>{GHZ1#% #@ /3/9993310!# 7 533X3'5g% #@ /3/9993310!# 7 3D{rVJ1}q' "@ /3/9/3993310!#!7!3by R1X/@  /3/999310!# 73HՇH7%q @  /3/99993310!#573q>{G\1# @  /3/39999310!# 7 36}MH)R/) $@ /3/39993310!# 7 530XAuP/j5 $@ /3/39993310!# 7 3NN-qT+o5 $@ /3/39993310!# 7%3Nu7T+}/@  /3/399310)733+NT-)q@ /3/999310!#73qB=T-)P@  /3/999310!#!'!`sboL #@ /3/9993310!# ' 3\H6fy3L #@ /3/9993310!# ' 3VH)hy?L #@ /3/9993310!#5' 3THhy-^ @ /3/999310!#' 3P5fy=Lq@ //9999310!#'3q^hy"@ //9/3999310!#!5!3qT; &@ //9/399993310!#!5!3`/h{T &@ //9/399993310!#!5!35%"@ //9/3999310!#!5!3@P@ //999310!# 7 3i``ybVL "@ //9993310!# 7 3e`;H`FF $@ //9/39993310!#!7!3gb+\t]^L @ //9993310!#73^'ezL@  //9999310!# 73e\هseRdZLq @ //9993310!#73qe^=ddZ/@ //999310!# 7 3Hu7T`H?9 "@ //9993310!# 7 53Ru9HpHF? "@ //9993310!# 7 3Mw\sX=T9H? $@ //9/39993310!#!7!3lwo V=1?@  //9999310!# 73\w߇5:=?q @ //9993310!#73qXw1-B=+5 @ //3999310!# 7 3N}5H)1q5 #@ //39993310!# 7 53N}<Au1]j5 #@ //39993310!# 7 3NN-q -+o5 #@ //39993310!# 7%3Nu7 -5@  //3999310)7331N߇ -P5q@ //9999310!#73qN3 -yL@  /3/999310!#!'!nwJ{L #@ /3/9993310!# ' 3nH J{3L #@ /3/9993310!# ' 3nH)T?J{?L #@ /3/9993310!#5 ' 3nHu^J{-L @ /3/999310!# ' 3n5RjJ{=hq@ //9999310!#'3qo;L{L@ //9999310!#'3kNf{F $@ //9/39993310!#!'!3bj\L "@ //9993310!# ' 3\H5gyFL "@ //9993310!# ' 3VHiyPL@ //999310!#' 3ifrq\%Lq @ //9993310!#'53q^g"@ //9/3999310!#!5!3XV9ZB^ &@ //9/399993310!#!5!3cf)9% &@ //9/399993310!#!5!3b-%w"@ //9/3999310!#!5!3ZV@ //999310!# 73f^ q%^`V "@ //9993310!# 7 53o^h^xX "@ //9993310!# 7 3q\3Hmg1ZV $@ //9/39993310!#!7!3bL+Vm\V@ //9999310!#73'^{^\q @ //9993310!#73qu\/3g7 @ //3999310!# 7 3=q+\=LD7 #@ //39993310!# 7 53Po+^-L i7 #@ //39993310!# 7 3Po?J?LL)7 #@ //39993310!# 7 3PqX/XLBb7@  //3999310)7331PqLb7q@ //9999310!#73qPq?L{H@  /3/999310!##'!y\1F #@ /3/9993310!# ' 3{Ny1PF #@ /3/9993310!# ' 3{Hb1F #@ /3/9993310!#5 ' 3{HtD1\F @ /3/999310!# ' 3{HX1)Fq@ //9999310!#'3q{91@  //9999310!#' 3wbL^=5 $@ //9/39993310!##'!3ws;s=9 "@ //9993310!# ' 3uHXqHETJ9 "@ //9993310!#5 ' 3uHHG}9@ //999310!# ' 3uHHG9q @ //9993310!#'53qw>K@  //9999310!#' 3[VHZdbVs @ //9993310 '53#/[{b $@ //9/39993310!#!'!3[HVbk^ "@ //9993310!# ' 3[VHB1by@ //999310!#' 3_VHLdy}Lq @ //9993310!#'3q^d+"@ //9/3999310!#!5!3LR= &@ //9/399993310!#!5!3aNA%1o5 &@ //9/399993310!#!5!3qÇ"@ //9/3999310!5!3#d+VL @ //3999310!# 73e\-yfL #@ //39993310!# 753e\ }VyfL #@ //39993310!# 7 3e\j?yf)L #@ //39993310!# 7 3e\=J3yf6P@  //3999310)7!31i\y whPq@ //9999310!#73qi\;wh/@  /3/399310!##'!l-T5 $@ /3/39993310!#%' 3H{-T75 $@ /3/39993310!# ' 3HJ-T+ $@ /3/39993310!#5 ' 3H/P% @  /3/39999310!# ' 3}H)/R))q@ //3999310!#'3qÁ)-T/@  //3999310!#' 3TH7H' "@ //39/3993310!##'!3eyT19% #@ //39993310!# ' 3{Nq}1JV% #@ //39993310!#5 ' 33h5@% @ //3999310!# ' 3{H#1Z%q @  //399993310!#'53q{Šy1H@ //3999310!#'3vfD=- @  //399993310!#' 3mwbFFcP "@ //39/3993310!#!'!3sXdH^5 #@ //39993310!# ' 3kL!-L5 @ //3999310!# ' 3kL!Ny-q @  //399993310!#'3qs!HVF@  //3999310!#' 3RZZjfyN @  //399993310!#' 53LRHh0hjN @  //399993310!#'3}^s^JR "@ //39/3993310!#!'!3bqlV`sN @ //3999310!# ' 3\H3`Fq @  //399993310!#'3qZ)d @  //3999310!#!5!3D $@ //399993310!#!5!53'ه) $@ //399993310!#!5!3sD $@ //399993310!#!5!33\+Շ\\&~ ?@  .-%+555\&~ @@  .-%+555\&~ A@  .-%+555\&~ B@  FE%+555\?&~ W@  -5%+555\?&~ V@  .6%+555\?&~ U@  .6%+555\?&~ T@  .6%+555& ?@  %+555& @@  %+555& A@  %+555& B@ )( %+555?& W@  %+555?& V@  %+555?& U@  %+555?& T@  %+555& ?%+555& @@ %+555& A%+555& B1)%+555?& W@  %+555?& V@  (%+555?& U@ !%+555?& T@  7%+555& S@  %+5555& R@  %+5555& Q@ 4. %+5555& P@ 4. %+5555& S%+5555& R%+5555& Q<6%+5555& P<6%+5555Rh:@  iYiY"?+?+??999333310"'32654&#"!33>32iNPBfX}1DR>fmyR ^#U@/""$% ## ## iYiY?+?+?9/_^]9/^]993331032654&#"!33>32# =㍘}1D1a9fm!)&_@4&  &'( ^YE *    #^Y#^Y#?+?+9/_^]_]]+9933333103254&+532654&#"%4$32! $5ꑊVDszpZ wpe_]1ιѲF l@J    @ H(:O//@H@H/++]qr^]22^]/+33//^]29310!!%#.'5!%46325>5&FZ_V,#F  l@J   @ H ( :O//@H@H/++]qr^]22^]/+33//^]29310!!67!#%46325>5&FZh6 2@4AGy9@jhB802Q>_V,#Fl@J    @ H(:O//@H@H/++]qr^]22^]/+33//^]29310!!.54632#.'5!FZj?9zFA5@Q2G<F l@J   @ H(:O//@H@H/++]qr^]33^]/+33//^]29310.5463267!#!!sk@9zFA5@96 u;Za#,V^>Q2;E>! @   _  /]2104632#"&5>7!F;9JJ9;FEkH@Es?>>?>?<>> PieT6xT/@ /  yXh /]22]]q/]33310!!4632#"&%4632#"&ZF;9JJ9;FF;9JJ9;F?>>?<>>>?<>>d /=@* @H , $/$?$$))!)$_/]3]/]3]/+3104632#"&%4632#"&".#"#>3232673F;9JJ9;FF;9JJ9;FE+ME=*& } o]0OD<)& } oh?>>?<>>>?<>>!*.k!-,rzh?G,d@d  W@@+ H/? _  /]3]/]]3]/+]]q10567!".#"#>3232673fVLAHp+ME=*& } o]0OD<)& } orA1!*.k!-,rzT 3@!O y X h    /]33]]q/]]]10.'5!!!I/L}2Z2026KT 3@!O y X h    /]33]]q/]]]105>7!!!>sL1TZF/1;Ku &?@)`### #0### _  /]3333/]]]qr29104632#"&%4632#"&3673!.'F;9JJ9;FF;9JJ9;FCUNQ0<F?>>?>?>?>?3232673!!+ME=*& } o]0OD<)& } oZ!)/k!,,rzT-@hxO?O _  /]33/]q2]10!!4632#"&ZF;9JJ9;F׾?>>?>?>?>?>?>?>?>?>?Q2壘CBJ? &`@A !'("@ H //_/]r22/]q33/+293310"&'332673.54632#.'5!XXYZj@9wFA4@5y 113229`#,V_>Q2?@(RJ? %d@C $ $ &'$ @ H //_/]r22/]q33/+2933310"&'33267367!#%46325>5&XXYZ7 1@5AFw9@k1322XA502Q>`V,#9? $^@A# #%&#@ H //_/]r22/]q33/+29310 332673#.'5!%46325>5&#XXYZ5y 7A4AFw9@k 1313??@02Q>`V,##K@'iY iY ?+?+?3?9999333310) ! # #"'5326! !#>_T@:35>7[ ;5N5)†eaW +^G@%`Y aY?+?+?3?9999333210# #"'5326!! ! ! \|jD119M=Z};> Od`@5    iYP  iY?+?3?9/_^]]+9933333310!#!! ! !32654&+3N5afwwt+qlmhs$R@, ""%&   ]Y  ]Y?+?+???39933333310"'#!33632! !"324&p+kҘ)Z}%qhkte;JTh!R@c    iYF!L I    iY iY iY ?+?+3?9/_^]+99/_^]_]+]]_]q_q]q+9333333210!!!!!#!.54$!#";| x12юVdaps )0@Z!! %..% 12  -fY-i- ----$$bY`$$$$ * *_Y 'bY `Y?+?+?+_^]?9/_]+99/_^]]]+_^]99333333210"$'#!.5463!>32!3267;#"%"!.!-lo0P/ebPnYKUuanr`-sR25딂*.('FOI\{qq{`B@!    ?3?3993333333310)!!'77!'`6 ēsJД<ωپsC@"     ?3?3993333333310!!!'77!q_71{fFP䱌p7^0mX,d@< #-. jY jYP` + #+iY+!&iY!?+?+??9/_^]]+/+9333310632#"&'32654&#"!! #"'5326!k0nJ5J>_T@:35>7[ X' ס/ί caW  ^(T@. )*aY ''`Y'#aY aY ?+?+?+?9/_^]+99333321072#"'32654&+!# #"'5326!^Pzr-y1t~ \|jD119M=#}3y O$@j !"%& jY ! !iYF!!!!!!!L!;!!!! !!jYp/ #?3?3/9/]]q+9/_^]_]]]]_]q_q]q+_^]/+9333333310632#"&'32654&#"!!!!!!Hj0nJ5J6$6' ס/Ͱ w= ^"@N  #$`Y$ Hf$ I aY ! aY ?+?3?39/_^]+9/_^]+_]]++933333331072#"'32654&+!!!!!!Pzr-y1t}V11}33^RVh 6@  " iY  jY?+?+??933310!!!!!h LLTo^ 7@   `Y  _Y?+?+?933310%!!!!!ky^V>@!  iY "jY?+???9/+9333310!!4&#"!!>32buOv6[ Ljk *q3'ǸoD@#   ]Y_Y?+?+??993333310%!!4#"!!3632ϴr1f%Z <@%   K {  @ H  /_]+]q22939910#'##'##'5R8127117PXffffX bu@K    @  @ P      sY  ??+?9/////_^]^]9993310>5!!"'57557!%%;nL+??Jo {sDDDD`ooou=G@LG.L@X\9Bga@Ga5@X9G0@G@#G@ ^ ^ v H~ "   D *b ( \ > <N  4 Copyright 2012 Google Inc. All Rights Reserved.Noto Sans UIBoldMonotype Imaging - Noto Sans UI BoldNoto Sans UI BoldVersion 1.06NotoSansUI-BoldNoto is a trademark of Google Inc.Monotype Imaging Inc.Monotype Design TeamData hinted. Designed by Monotype design team.http://www.google.com/get/noto/http://www.monotype.com/studioThis Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.http://scripts.sil.org/OFLff  34566778:;YZ[\_`ders}~NOGHTU > ? W X o 45Z[  2`bddss!#$$%%&()+,-/1256689::BNHIJJKQRRSSTT Ncyrlgrek$latn4 kern8kern>kernDmarkJmarkPmarkfmkmk|mkmkmkmk        *:JZjz  &b ' *2 , A Q R _ ` c  fTg dF`bddss  %(3!-:/6G8:OBNRHT_%$=D]4KBj77NNSV\_27 BI^alq~!WYZkl""(UZW]f]kngqk{QQbhssvvl $****0*****************6<***BBHN*6**T**TT****Z**T`````````````ff*lT* &,28>DJPV\bhntzt, t"(.4:n@FLR:X^.tdjpv|PP4Pvv| @$*06<$BHLNTTTZZ`flrx~~~>DDDD,,,,4444tFLtttt &&,&28>D@2J2J2J2JPJD@D@D@V@\b:\bhnttTntz(Rt:,~^~ "(.4:@,2FL&,2RDX^RDdXRDX8>jpPv>D|np4nthnPvhnthnthntP .$*0J2J6<BHNTZ`flrx~ftt:t  ,D@,@,D@@,@, &,26t8B>t"DN"DJN"DPNV\bhn&t26&t26z26t\B|(@(t^t~JtD 8>PJ"(.4:,@FLRX@B^dtjpBt\Bv |n@n@@n$Rnn6@$:l` >D\$b*40P6>Rn<B$HNTZ`fl@rX4x~4 vtJB"HJNRv: &,28>DJPV\bhntzTtD,2D@^4Nv$bn,b$*,b0$ "(.4:@FL RX^djpvD|TtTjt~$D\D62fbfD@D@@2b :$*06H<TjB^"2H,tNBTZ`flrx~hntHJ, JZvR~,tB(vtv@:@X &,R:X:2t^R^R^8R>DJPbJVbt|2v\tbD$P|hntzP$$`~\~~~& >4>>>|   @"(.Jr*`64$:HJ@JFLLRRRRpX^F,d4jJRpv|P:RRRRp8$   :n:X@ jp v$~  HJNv  n $ * 0 6 <T|| Bv$ HJ"V@:n N:X Tdj Zv$ `P f  l Nv$ r x ~  <HJ<(v": $^ T   ^P   T J :n@ : F 4 F :XR :X  :X  :X !:X!^ !^ ~!j!!! !&@,2!,&,!2"!8,2!>&,!D$&,!2"8>!JP fb!P!V4\!\!b\!\!bhnP!hn!nhnPhn!P f!tz!z l!"!"! N!!f!!!!!t! !t!!!t!!! !!!!b! !! !! !,@F," T," T" \$""&26"*2""!,"(".!>"(".t"4B!"":"@"FHJ"L"FHJ"L""Rb"X"^6"d"jB"p"v"|"""hntPhnt""""t"":~XOfdtL~L bXT4X[S|FCD|AP /< lHQ:g:jIk0hvg\8]:h8h9@{h9Lzv HGm%Pl0R bbllAl//llLk@kkx@\]@]9z@@zG@ $k<Dng,lghb]N],X<b"H,8X6@6lfb64<TD@ 09[<9,<9|XnpSnl<0<{C9hD<9hlhA, <l< nl/0zz,`L,4< <,pH,H:> >QT0Lx<v<m,G,l,\hm@lm|@D$(D kPXb ] 94P[ l/ <zPv LP jGP:,)g@EFTh`W,h|,,R,v,lvx~4w_xhTth%!v]2&55 6,@ `@Fp@n<hV  .8p@Dw@N$[ {5IN@ISS@5 + 9&1,>,,<,,@ @(,(\" ,haIppC0G:h@ll|h,,q p:n>,Xpp/X3zdl~Tr0Ghh h$}|RBhhh@D@|jGhMVShhTf@VDxx]t% m\hD]hrllpvp@p0pppph0]p9DpHHHh|HH>>pXp0,0tRll[p{@0S09X,  p ,@H4HlQl::pppg%PG@ImdGdk# ak,,+ Pb`aHNTTHNTZ`flrx~XblbKK/k]TzGfXqGT  $=D]4;CHNOPQ?ARCCUGGVJKWMMYSSZWW[YY\de]xy_{{ab  &,28>DJPV\bhntz\\\zVVVVVVnnnnz\ pt<uu(.nVx> :`L8H-33^^^s^^d^^,^^<^$HL8^@^^ X^ $=D]4679;<*+>FI@^aDH "(.4:@"FFLRX"^"@djpv||^^^^^-7xVx4XLxdxyG,:jV*\#?p?^^^ns%Lp^^J^^f^@^ ;A$=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0   1      & , 2 8 > D J P V \ b h n t z  "(.4:@FLRLRX^LRdj@Fpv|@F            J J J J                   $"(.(.(.(.LRLRLRLRpv@Fpvpvpvpvpvpv@F@F@F@F      " " " "   *0 (. (. (.  (. (.6 26 26 2 , 2< >BF 8 >@F JLR JLR JLR D JLR JLRHNT.Z VLR \ bX^X^` nfR h nLR h nLR h nlr h nLR @F @F @Fx~ @F pv pv pv | | |        @F @F @F @F @F @F          LRLR zdj                           (. (. (. . . . . (. JLR D JLR pv pv v v v  v pv&&&& @F @F,,,,    28>DJPV\  "  bh>Dpv  ntz &6 2  D J \ bX^ @F      (.(."(.4:@FLRX^djpv|   JLR pv @F(.$ , 26 2 b^ pv pv.(.4:@6 2 @F     (. (. JLR JLR pv pv | | @F @Fz< >BF    (. pv   " h n $*06  (. P VLR<B |  "HN(.(.TZ`fLRlr@F@x@FLR~LRdjdjdj@F@FHNpv|||||LRLRLRLR@Fz$*$*$*" lrLRX^$*$* &,28>DJPHNV\bhnt@F@F4:dj@F|zLR~z4:X^LRdj@F| (.LR"LR@F    "          . .  (.  (. (. &:6 2< >BF 8 >@F< >BF 8 >@F 8 >@F D JLR JfR b^ \ bX^ \ bX^ h nLR` nfR h nLR h nLR zdj t zdj @F @F @F @F  v  v  v  v   | | | |          @F @F @F BF BF          @F 06  h nLR h n  i<iJJ(N``{{ddPP^^HHhh  33VVBBqqyy000{0000^0 00VV1100B0,00q0mm;;H0h0000ooh0h0h000ZZJJ//99HH  ooJJqq y0 y  ++ ' 'P00 ybbhh!!{{))33ffBB==//\\LL  \\mmJJ11DD`0d000y030V0hh0VV $=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0   1      & , & 2 8 > D J P V \ > b h n h t z h h h     & & & & & & D D D D \ , b b b b b b n t t t t h b b b n n n n h t t t t t h h h h      t        & & & 2 2 2 8 8 8 8 > > > D D D D D D P \ \ > > >  P P P \  b     b b b b b b b b b b b b t t t t t t t t   & & & & & & &           D D           \ \ \ > h " ( h . 4 n : ( h @ F J L R   X ^ d & j p v h 2 8 |   >  J \ ^ > t t  h          b  & D t h h  & & t     h   b b t t   & & 2 2 D D F   h   > b t & \      n  >   D J t   h 2 \ b h h h n $ h h t t *   0  h h 6 < B  H  N T Z ` ` f f F l r      n & x  6 ~  h             h h z h ` h    < h  F h h z  h  b h h t    n  h h h n h h h h h t t t t t z h            & & & & , h , h 2 2 2 2 8 8 8 8 8 > > > > D D D D D J J P P V V \ > > > b     , 2 b  J00`000{0d00000P0000^00H0h00 0300V0000000B0q000000y0000000V0100m0;000000o000Z00J0000/009000H0 0o00000J0q0 y0 00+000 '0000b0h000!00{0)0000030f0000B00=0000/00\000L00 0000\0m0J001000D00000V0 Z`"$&(*,./125678DFHJLNOQRUVWXL"LRX^djXpv|d H40NAbl4@D $=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0j1jpv| $*06*<$BHN*TZ`flrx~|****B$BBBBBB````xHxvvvv| | $$****066****$$$BBB**TTTZZ`````lx~ &lllx00<B`,,,,,,,,*28>DBBBBB>JBBBB>PV``````V`\xxxb&hpnv|tB z~T6*fHZx~ZH    *B` " (6 . 4 . 4z : @H$,,**BB**`` F$ L R X ^ d jBx p vZ | v p ,0 *x   fff0 f$ *   $ B    H * $ *f 0 6 6 < B H N`BBr Tr Z ` f : : l r x ~  0 N l r    <$H**TZ~  ~ * H     H   & , 2 8 > D J P V \ b  h n Vp t z  N| t 4 ,,  $$$$$ *66 8 8 <<$$  BBBBHH*  T TT Z    " ( . 4 : @ F``f L Rllrrx~ X ^ dZlx**Zf jCz >4p\HH IlzZLHH\HBHppp'?)pp84HnH6pplppfpbHpH ,H HtP63SH8O  nHuHHbHp pHNvH=HHHp/H&HHH=&HHN.MHHhHH@HH+Hn~HHDHHJHLH-H.cmHHIH%zHX~`D>FH@d ppppp ppplpbpp\p&pppjv4 6@EGIKMQSZ ]]4:6<BHNTZ`flT6rfx~VVVVqVVpVhVBVq V7VVVVV p`bss!#%% )+!/1$66'::(BN)HI6KQ8SS?`bss!#%% )+!/1$66'::(BN)HI6KQ8SS?@@  $$ *06**<BH****6NTTTZ`fl<Tr`*xf~`*N~x~<xOt`T~h~T<xhOhT|0d@ djdd  $$&(,- 25"89&JJ(RR)TT*dd  $$&(,- 25"89&JJ(RR)TT*+    $*06<0 6000BHN6*00HTZ666666* +~t`V4 $ BHfl )`dos""PEKNO<-2"FEKNOFtsdtdpqt2200 0"""<"^^4 ,TT,T 0 0 0 0 0 0"""""""4<zTT 0 0 0"",,"""^T4T4 0""z   " !"!<!B!DFHIKKNNPS UU$WW%Y\&^^*+78=DJNORST^_`adnostwxyz{|}~$(**,,..0022446;==??CEGGVV[bddffhimmooqvxy{|~~      !! PQ UV]]__ggimosuuwwy 1P\bhjlpq{AFJLLNNPPRRUUWWYY[[]^cceeggiikq~//$79:< DFGHJPQRSTUVX  !$&+-/13568 : CDFHJV_biyz{~ TUV]_gopz|  "#$%&'()*+,-./013579=?AGIKOQSUWY[]_acegikl n p ~-Z&*24789:<$&*,.02468:GfmqrsuxQ\^iy{}    FHJLNPRTVXZ\^`bdfhjlnp~7$&q~< &*-2479:<$&68:G \FHJLNPRTVXZ\lnp~4$79:;<=$&68:;=?C U "$&(*,.0lnp~%&*24G\FHJLNPRTVXZ\-<$C U "$&(*,.0N &*24789:<$&*,.02468:G \^FHJLNPRTVXZ\^`bdfhjlnp~!~~$;=;=?C~ ~U "$&(*,.0"$&*247DFGHJPQRSTUVXYZ[\] !$&+-/1357<>@CDFGHJ TUV\]_ "#$%&'()*+,-./013579=?AFGHIJKLNOPQRSTUVWXYZ[\]_acegikm~$C U "$&(*,.0"$&*24DFGHJPQRSTUVX !+-/135CDFGHJ TUV\]_ "#$%&'()*+,-./013579=?AFGHIJKLNOPQRSTUVWXYZ[\]_acegik"$&*24DFGHJPQRSTUVX] !+-/135<>@CDFGHJ TUV\]_ "#$%&'()*+,-./013579=?AFGHIJKLNOPQRSTUVWXYZ[\]_acegik%&*24G\FHJLNPRTVXZ\   YZ[\]7<>@ m  < << <4FGHRTH]3579=?AGIKOQSUWY[]U( (DFGHJRTDFH( (V]#%')+-/13579=?AGIKOQSUWY[] ( ("( (   ( ( F@F`F( (  fmqrsux QV_bdipqrtux Q%V_bfimsvyz{|}~ !qrxQV_bi -V_bfimsvyz{|}~ !Pfmfms~~V_bi~ ~ V_birx Q V_bfim    y~ y~ y~    y~ 6  ky}     ghijnoprtvz{|~   i{    #iy{  N22 giop{|22 /  ky}      oy  }  n  = opwy}  !~~~ ~ops7 opw}  % gp| g|       p'  j~       I(W(Y2Z2[2\22%('(72224(5(Z([(m2(I2W2Y2Z2[2\22%2'272224252Z2[2m22  y}  5  gjz|~      op ghinoprtvz{|   ! gp| %  y}        %nixz{  g|   )  y}      j~  }   ghinoprtvz{|     p*  y}        }  )  jy}~    @  osy}         o}    +  oy}   Lcyrlgrek"latn0aalt2ccmp8ccmpBccmpLligaVrtlm\rtlmbrtlmh 0@P`ph< T"*2:BJRZbjpx&.6<DLT\djrz^fnv~ (0 $*2:BJRX`hpx$,4<DLRZbj                           LMW`bss!#%% )+!/1$66'::(BN)HI6KQ8SS? (B~FPZdnx  (2<FP ` a ` a ` a !` "a #` $a %` &a '` (a )` *a 3` 4a 5` 6a +` ,a -` .a /` 0a 1` 2a 7` 8a 9` :a Y G k l m j n o '*,./0I 4L5OZIL[IO  ; < = > V 7LMWlinphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/NotoSansUI-BoldItalic.ttf000066400000000000000000010654401434616504300316100ustar00rootroot00000000000000GDEF4(GPOS .GSUBF #a `OS/2VB`cmapO'hcvt 09fpgms-p. gasp#glyf?_Lhead"[z6hheaT$hmtxTbj%locafB:0%maxp [x name|Dpost]f prepq6\_< 4( X  X o pRTi\2K33f  GOOG!X ^ J+)h3sD Jm\hmH)H^hBhhhhhhXhNh7hVHHhmhmhm\5{j5V5?5{55%5 5 5{5{5=)?'J^wh#3FoZ%ZZuZ%%`%`%`%=%%ZZ^%H^ofN}?'shhmJhhhshXhoHhm)mhm;\o=H1 a a ){V5V5V5V5j% 5{{{{{hd5FZZZZZZZZuZuZuZuZ`%`%`$`%H%ZZZZZhm+oooo's'sZZZ{Z{Z{Z{Zj5Zj%ZV5uZV5uZV5uZV5uZV5uZ{{{{5%5%`%`%`%``%%`5%%%5`%%5`%5`%%5y%% 5% 5% 5%4 5%{Z{Z{Z5{Z5^%5^5^%=)=)=)=)?H8?H^?Hoooooo'N}'sJJJ hZ)Zd+=)o7oyHo`BTo;oo5oVH"-""N>"o>G55V5J5{5 5 5+{55X?{Z'%`wZ;7'Z%d`%ff\Z^ZZ^wZjwwZ`wZwZV55{=)555555;V5) 5 55 55{55{?{5b5V555 5ZmZZuZ1oo%T%Z%Z=%'sZ?j=j^jd\mjh%uZ%Z`%`%`1%%'sj5%'N}'N}'N}'s)))3+sj9wswj  1 s H}hh9h){)wm a \ f L {hmdbLhmhmhmhmqH%H%o=`{/^{RVyHTT`ju^ 5=%ZhX%%-{%Z3jbV5 5uZo1fj=5%Zd5%5%1ww{Z3^f3^f {}Z{Z1Z1f{ZhH-q?oZo;))5j5s5 T5%Z)15%5Z5%{/J!5H%)5%B5%{Z{Z?h%fLu\bb5%;=J;=J5%F5%!5H%b5ZZ)ZV5uZHDu3HDu3)1q` 5o 5o{Z{Z{Z's's'sb5%5mj?=Z=ZB+5/%{ Zb^D'5ZZZZZZZZZZZZV5uZV5uZV5uZV5uZV5uZV5uZV5uZV5uZ`%`{Z{Z{Z{Z{Z{Z{Z-{%Z-{%Z-{%Z-{%Z-{%Zoo3j3j3j3j3j's's's6`FV`?HV%f5%!}}{Zj%Lf?ZVHD)?3{R%f5%`y 3%{j#Zf5=)X+H^fH^?owsJqq=d?fwH!!!fJ 5 5dZ55%5j55%Z`%{Zooooou3ZZ)Z{{5%{Z{Zq` 5 5dZ{5) 5%ZZV5uZV5uZ``%{Z{Z5^%5^%oo)m5%5ZNTJZV5uZ{Z{Z{Z{Z'sR%f#Z#Z{L%/?DV5uZ`{Z^^'sbZ%%ZZL3u33'1BZ`Z5^Ro%%``R)#`%=j=j=%#%\%ZZfFZ^^^^^H%%`#`#q`HHD3wN'Jfw``H){Z1Z%`%Z/uZ%ZLZ/^^^5%%%!%;;'!jwjsTjTjuuHHHH#oo#VjooTT"T7u)V})9XXXXXXXoLwjPobdHs##mEI\n-52qjh'mj}p_N`H3L;%7D!Bj )# BFN\ZHbZ5ZZ?5/?3T;=jZb5%V%''{ZD9DZ`{Z5{ 5Vd{T{Z'N}m3)R`9`-u9`J9H99V?\?\?3BbZ9LDjw7D7h{o9//99wBLy7)){Fhd1oZd;!5/%jy}\)3}V.).V3Aa1wZ%=w^RFHu^`%Z%H%`=%%^#f?ZZZuZ'13`%`+oH+%VfRjRj\\ ow #Fyyy; udud5%5%5%{Zj5Zj5Zj5Zj5Zj54V5uZV5uZVu*VuV5uZ?5%{5%5%5%5%F``%5%5%5%%5`%5`%5`G%` 5=% 5=% 5% 5% 5% 5{Z{Z{Z{Z555^%5^5^5^I=)=)=)=)=)?H^?H^?0H?Ho@:ooff'N}'N}??'sJJJ%H^N}'sZ )ZZZZZZZZmmVV''''''f=o==o=%%%%%%%%TfTooq]q]````````!p)o)RoRf|f|ZZZZZZpoowwwwwwww|ZZZZZZZZpooL|L|ZZ''%%``ZZwwZZZZZZZZZZmmVV f  Ho H Ho H 7] 7]ZZZZZZZZ bp b o ho h | |ZZZZZZZ)u)o;o% PXNP`5``."6` SwwwwwwYVJRZZZZZZPyPf)L!LhJhwN5HbZZ%;^jBF=%+?P) {{h7 k Hdhm#f{mmb))s+jVFR?;?;fBhf%`%5H^!5%'5%J{BfPw}Pm5#Z!fj3/JyH%/J -h{VVVVVNVRVNVNVFFV5V5VPV-VH-V%V%V%V'V/%VVV5V5V/)VPVLVLVLV^LVVVVVPVLVFVLVLLV/V9V?V?V??V5V5V5V5V55VLVLVLVLVLhVLVFVLVLVLLVVVVVVVVVXVVVV\V7V7V7V7V77VHVFVFVFVFFVVV9V9V99VVVVVLVVVVVLVLVLVLVPPV/V5V5VV)V/V'V%V%V%%VHV-VPV5V5-VFVNVNVRVNFVVVVZZZZZZZZ````````wwwwwwww````wwww5 5{3333wdwy333333335y5%R\5%5;%5;%hLhlhl# \`` ~ac67"#ou~  OP\_'=?EMWY[]}  " & / 0 4 : < > D ^ p y  !!!!"!&!.!N!T!^!!!"""""""")"+"H"a"e###!%%% %%%%%$%,%4%<%l%%%%%%%%%%%%%%%&<&@&B&`&c&f&k&o),m,w.!# bd78#$tz PQ]`>@ HPY[]_  & * 0 2 9 < > D ^ j t  !!!!"!&!.!M!S![!!!"""""""")"+"H"`"d### %%% %%%%%$%,%4%<%P%%%%%%%%%%%%%%%&:&@&B&`&c&e&j&o),`,q. ^IvedAeY @SQzKIzx?wZI we%" {ob`aa` kLP>:6&$&8F0(.@@<"$ IJ$%KLM`abVPQWXYRh X Y Z [ \ ]STUV     ;<=>r i,-03PQ45Z[@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! , `E% Fa#E#aH-, EhD-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,CRX!!!!!F#F`F# F`ab# # pE` PXaFY`h:Y-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -, %EPX ED!!EDY-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,F#F`F# F`ab# # pE` PXaYh:-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-, T#T[XCPCT[X!!!!H+YCPCT[XH+!!!!YY-, T#T[XCPCT[X!!!I+YCPCT[XI+!!!YY-, #KSKQZX#8!!Y-,%%Ij SX@`8!!Y-,%%Ij QX@a8!!Y-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZXB TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY&QX@cTX@C`BY@cTXC`BYYYYYYCTXBY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,CPCT[X!#0Y-,Y+-,-@ ;K[PZUZZ?ZOZZXUYPXUX@XXTUUPTUTVUWPVUVV VUPU_o߉ \PMU+M;M MkMMMMNUJPIUIII7IIIIKUGPFU`FpFF;FFKUOPNU0NNKULPKUKK?KKSPRU?RRPUQPPU@% F!3 U  p   {  U3U}vs@us2+ttttts]/3U3Utdtdtpi<ooi$4nTmmmm{mm mFjjjji]3UU3U?Tdh]6@ g;>F$gggg@ g'Fg g@gFggtg9fYfiff;eeeete d]7c]*Kb bbbaa ``+```` `&)F __@^.4F@^%F ^F ]?[]]]]]]K3U3UU3U0UdUoTS++KRK P[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYss++ss++++tstu^s+++^s+^sss^st++^ssussts+t+u++t^st++++tu+stuu+u+sts++++stu++s+++ssstu++++s+st++s++stu++st^s++^st+^ss++^ss++++s++s+s Pu^{VF3`5 ++T 66662r Zj@  6  X 0JJrV"(|P 2 pPD|:x x  !4!!"F"l##v#$x%%l&&&'<'((())*B**+t, ,->-./"/011z11223T34556J667L78J889909::6:X:z:;L<<<<<^<<<<= =.=>>:>\>~>>?<?@ @,@N@r@AABB&BFBfBBDD,DLDlDDDDEE8EFF.FNFnFFG\HH H@H`HHI>I`IIIIIJJ6JVJxJJJJKK@KXKhL:L\L|LLLMMM2MTMtMMMMNN<NTNtNNObP P.PPPrPPPPQQ(QNQfQ~QQQQRHRjRRRRRSSFSTT&TFT^TtTTTUXUVV(VJVjVVWXXXYY&YHYhYYYYZZZ@Z`ZvZZZ[:[[\\6\V\x\\\]]*]B]Z]|]]]^^&^F^h^^^_6_`bb&bHbjbbbc cXccd,ddeef*fffg"gHgnggghhh$hXhhhxhhiiiiijjjjjkJkZkjll*lmTmxmmmnn&nJoop<pqrxrssttttuuvXvw~wxhxybz z{,{P{r{{{{||}}}}}~2HXhx2T"2BRbrp.,<h 24 *:JZ | ~xN8Z~:J NJx.>N@4FV&d0H`xF~bVBBBBBBBBBBBBBBt<L6^>flö@ Ŋ(H&ǜ,ɚʆb̈.ϰ^n~P8Ԫ<`xՐT~ڐ`ۼ\ܠޘL. <(@X( jNZx 8Pt\>@NBtF"8Z~.LR(Jj4Vn<L\ ,Pr J      8  Z  Z jz R>RJ,Np0T"Fn(Lv4T|8h":Rj  : ^    !(!P!r!!!!"":"Z"r"""""# #@#b####$$ $8$P$h$$$$%%b%&D' (0()J*D+8+z+++,--.R./^/011"12.2344,55r6B6778989::;`;;;<=x> >?B?@@AXABJBCPCD^DEhFXGGGHVI(IJlK KL,LZLMMMVMMMMNN&N>NVNxNNNNOO@O`OOPPHPPPQ0Q@Q|QQRR>R`S~TTTTUU.UFUpUUUUV V"VDVdW WWWWXX<X^XXXXYY2YTYvYYYZZ&ZJZlZZZZ[[*[\\4\\]r^$^^^^__*_V_|___``6`Z`|`a8bbcdeRefggh\hiNijknk~l@m2mn6nopRpqZqrrsNstnu(uvwZwjxxxyzz{|},}~FB~8l|ZtH$h^6Bp*F~fJ&($ <@,b@& >r6(v08$l.^"4Fb~0fJ,h*n$N`p*rHrn 2Z2z <l*P6t Nx0BTx\R²(:nÀðn8DƄƸɊʈP@x͊ͮ<Ζ<f"ѪҬ JӒӮ 8\l"ؼ:b܈R߆.p(^ DN:JZjz0XhR<`:l$jH nJ(:^8l*~ v64: *   R  t     >ND@,| jxB\`  "#N$j%&'()n*H+D,L--.B/P/001(123v4D5667 78|9@9:;@;z?l@nAhBdBCDDExEFPFGGHJHHI"IJJLJK~KL L<LMNNNOPXQQrR RS2STTLTUUV2VWPWXYYPYYZZvZ[2[t[\\:\R\j\\\]$]F]h]]]^^^.^X^^^^__<_f___` `.`P`r````aa*aNafa~aaab&bRbxbbbbc cTclcccd d@djddddee*eLede|eeff8fhfffgg:ghgggghh4hVhnhhhi i@ibiiiiij$jHjrjjjjkkJk~kkll>lhlllmm8mhmmmmnn"nDn\ntnnnno oBodooooppPpzpppqqqqrr8rZr|rrrss*sPsvssstt8tZt|tttuu.uTuzuuuvv(vJvlvvvvwwDwjwwwxx(xLxpxxxyy$yHynyyyz z8zZz|zzz{{({L{r{{{||(|J|l||||}}>}d}}}}~~>~b~~~~>d>^~"BbFn:h&Z,V0^"Lv&R~2`2Xn@bz&6Hp@d~Bd<^xp<\|(Ln|Lv(Lp*LB\@(8l8Z"&nvf0 ,L2L$\(|6B,V`*d"d\ Z4Z~: ZZ°>ðĔ.Űlƚ$RؠBٌٸBn:݂~\4j| l|tNLXTb:tXvz&`XF.~`T.(r ` N,$fZH,tj  N   H   $ j   j   >  8l ZT..tPJ(|fL4&rjJ8 jd >  !4!!""f""#N##$0$$$% %F%l%%%&&(&L&p&&&''$'J'p'''((.(T(z((())<)d)* *0*+,-./0012234<456|7.7890:V;J<.=>??@A:ABChDETEFGfH(HIHIJHJKRLLL0LFL\LrL  ?2/310!!7!!IhyJh2@ `   }Y ??+9/_^]]q10#!4632#"&TkaDRnYIRZlLEWlK?32/10#!#%GJ)5=@ Y Y    ?3?399//33+3333+3310!!####7!7#7!333337# EtusqF!wuttENhhii3`%+,@= %;%K%-%%%% H%&&sY@H@+4+D+"++++@ H+  sYp@ H-,?99//+]33+39+_^]]]9+33+39+_^]]]910#7&'.546?3&'>54&' ',ђG#!jn?BH?N1#>EP J k B=|DA 3a H>.;=D54#"&54632?_8A^pzo{@9'H.7)H/lyj}``mJp]ana`d۔"D$0@\ZjI:+ $*$:$$ $$"& 6  %I:+ %% %+nY"iY??+?+9_^]_]]]]33]9]]]9]]]]]9/910)'#"&5467&54632>7!327>54&#"-HߝLǩ,I*,XF[GsjhqU<(BIbv÷a᥎9mǸP3kNAV;q;h<93]I.\% ?/10#%GJH  $??10! #JuS?  mj $??10!3jtZ!3B% ?10%% '-7uqZVR!+IQ\qm% -@ YW`/o/]]qq3]3+3310!5!3!!#nodq@ @ H/+]10%#6yvJxO)j@oYoO/]q+107!)5 5{9 }Y ?+1074632#"&kaDRnYIRsZlLEWlK ??10 !)JBV @ sY sY?+?+10#"&5$3 "32654&Vͥ#VP`7FR\=#tsuof !@  ??9/]9310)67'3ϴ#) i҅6=N\J*@  vY vY?+9?3+9310)7>54&#"'>32!>+ݴREBfR8TKOCUp_ġk P'_@<uY4I$IN^ ! I I "%%sY% sY ?3+?+39/++_^]]+]+qq+910#"'32654!#732654&#"'>32PT].IVN|ي}yuO 23q|nCJdQAP :@ HsY O H??3+9/]3+39+10#!!7!3!7>7#??07ê1: 8 .Q//i:#Rch3@sYvY sY ?3+?+9/+39102#"&'32654&#"'!!6oԏu=smfoh7XHθ.# c{^^!NX&*@ sY sY sY?3+?+9/9+104$32&#"3632#"&2654&#"Xz,}b3Tk>sĝc}CE32654&"654&{a\ttrdU]mILZ{OTb4zjȪ*wh-|[Q_kUIu]IEHDNVD&4@ sY  sY sY?+?+9/_^]9+10#"'3267##"&54632"32>54&Dpot>oAAe7KD7>54#"'6324632#"&tu|C2q\ױ9l^Hn_DQnXJR咹OTV5w D}Sq[?aV[kLEWlL\F8D/@ @@ C9  .!6(.%?3?399//33333]310#"&'##"&5463232>54&#"3267#"$5$! "327&ŀXq 4|P𔹎j :BtE uw>jRI1-H9$HئMFPC7\,A Dk(.Z8gYAJ @ iY ?3?39/+10!!!! /#J'u'64\D`^k5_@;oY)"I$ I I I  iYiY?+?+9/_^]+++]_]+_q_q+9102)32654+32654+ꦙdw5yyʴu na[Htg{7 @iY iY ?+3?+310"327#"$32.Ӏ\}ovjˢĴMM+-<;&5+ @ iYiY?+?+10#!! 2654&++=5'۔|sch'H5 I iY@&I' I I I iY iY?+?+9/_^]+++]++10)!!!!!f526C7#P5 @ iYiY?+?9/+10)!!!!f5.6O8'{m0@iY   iY iY?+?3+9/_^]+10!# $!2.#"327!3sJW񉑖L^B5.!& c(0ͱ+5 3@!iY I) I I I I ?3?39/++++++10)!!!!!oΆ)52yy1w= @ iY  iY?+3?+310)?'7!Ly$ɘ%%ɜRRRNRR  iY"?+?10"'53267!^]XLc{%14Rydq5 @  ?3?39910)!!7!u}m52iPF>D5iY?+?103!!5526J5@    ?33?399310 !!6#!# !!H%$I =85AqEJmX5@  ?3?39910)#!!367! 5eT#!R;֑Pߢ{ @ iY iY?+?+10# $32%"32654&\ysyypA' >Ǝ5 +@ iYP`iY??+9/_^]+10!#!!232654&+֜m5}aa &t]W{ @iYiY?+?+910!# $32%"32654&\ysyyp!bwH' >Ǝ55 iY@I I iY  ?3?+9/+++91032654&+ !!2!N_fJw5g-ruRRyŞ71)V#,@" iYiY?3+?3+999910#"'32654.'.54>32&#"݇p~2cp|ٴmSjBsyxZlUJ+A8JcpqcJZJ=[KP@ iY?+3?10)!!!;77@  iY?+?310!"&547!326791{ NHBiJ3q?3?310767!!!3<'3+N9pJ5@s3@  ?3?3399310!!3>7!367!!'474=-!Q q 5LJ5y145]3~Jq^Zy @  ?3?39910)!!!dt@@kX+?@ ??393310 !!!svVww8fPy/ $@iYiY?+9?+910)7!!!+& 5)T1$?3?310!!#3'}- ??10!Jw$?3?3103#7!!\#-)qT#5?9/39103##j߮oJyFH/310!7!{6!@ @/]q10#.'5!G$7$OEEZs '@  ]Y]Y?+?+99??10"&54632373#7#'2>54&#"a'9CENM?D{ICgTPG]tXX%h!&@ ]Y]Y?+???+9910"'##!3>32"32>54bR:J->)+NL>JxNJACzKubHؾuP`uZs@ ]Y ]Y?+3?3+10"&54$32.#"3267ו\6hBUM[QLE[H"߀`a/#OZ#'@ ]Y]Y?+?+99??10"&546323767!#7#72>54&#"Rw5L-G!FvOKAD{IeJZnUfWNuP`tZBs!i@@  bY?   cY   hY ?+_^]3?+_^]9/]_]_]r+93_^]10"32654"&54$32!#3267X-3g`WesaS_RU[i&0V%*@ cY ]Y]Y?+?3+?3+310"'53267#?>32&#"3#-hF=6=\̣)ðhPE@9F 1MPZőTT1PA>s,/@&]Y]Y ]Y??3+?+?+99103#"&'32>7##"&5463232>54&#"4h[h8HSZ@DtNJ?G}GL^)V_a^LֿbE_}P`yXX%m@ ]Y  ?3??+910)654#"!!3>32Ӊl\+bJ-'*A>dD3{1M]Ik%@  fY???+10)!4632#"R-_WILX\^WY>:Pc @fY]Y?+??+10"'5327!4632#"ZhF=5$.MX_WILX\)PWY>:Pc%@ ??399?10! !!!3XxJJ- % ^HJ9v,% ??10)!RJ-%s&'@"$$ !]Y?2+3?3393?1023>32!654#"!654#"!336L+Fhӊb^(`Ӊb\+b sptLhD3{/D3{1^%ms@ ]Y  ?3??+910)654#"!33632Ӊl\+b ΊD3{1^䧛IkZTs @ ]Y]Y?+?+104#"32>%#"&54$32%KwKKxC/z}{ہMhs!'@ ]Y]Y?+?+99??102#"&'#!336"32>54-Sx3 HV :JyMJACzKsJYJwP`uZs$'@  ]Y]Y?+?+99??10".54632373!>7#72>54&#"XMV:9/4:H=FuMKAD{II_gKY^LwP`tXX%s@  eY?+?9?102&#"!336J;%B-7t%j s  ^s";@!     cY cY?3+?3+9_^]]3310#"&'532654&'.54632&#"=kEPfJ^y`ɢcv9F@X{nq#ZA8+D4D\_T3+';-?^oL&@ cY@]Y?+3?3+310%27#"&547#?3!!Aao sĄ12s#5~2>TJo^@]Y  ?2??+910!3267!#7##"&547-l\+b- ͊^sD3{㥜]xf^  ??391067!!!@)C'--.R^b}^@  ?3?3?99310!!3>7!367!!47RZ3 DH Q&3 \@^с,=hX^ @  ?3?39910!!!!{Asb-}=!NVs^@  ]Y  ?2?+?310!3>7!!"'5326?f'8 0)GXZ9D0T6^i3U `e1^ $@]Y]Y?+9?+910)7!7!!#?s3+Sd'8@'?O_/_  $ ?3?39/]q33104#7267>;3#"&54?6-z=&T1ZQB-US$C:5'?SaAJqN,E6(y9DE/ #??/103#!&@ %&$?3?39/3310 3"+5>7>75&54?654T'-z>%-]XBo%II)8DETa䪌AI)on 3+E6(m'%}m@ -   Y H@% H@" Y/?/]]3+3_^]++_^]q2+3_^]10"56323267#"&'.V6}6e@jV@^03z=g;daYLB7m$=hîq`K`/+-*710-1)VDO]TK0hs#' %/321047'76327'#"''7&732654&#"6[ji[55}_esT}6mPQoqONof_57Ynk\}}33{}]hMonNPnpX K@-tY tY   ??39/]9]39/]3+3+310 !3#3#!7#737#73!`s7'%//%')hNݲ/@ #??99///3103#3#,7d@@- I-Y-i-*-:----3F3V3f3%35333 3 $$lY! lY ?3+?3+9_^]_]]]99]]]]910467.54632&#"#"'532654&'.>54\n*8ŬR?QQWzx&4̄c[B^ud3>Uf15UE"e9XT28*B&6cs#e:KiF9'C.9.Y43U-\/j3 *@0@ Pp/]q333/]104632#"%4632#"OH}HMOH}HMfNPlIWnNPlIWh%5G@0   o  `p  *"20??99//]]3310"327#"&54632&4$32#"$732$54$#"z]xAb:~~>l^Zm+)׭֭->}6^ZƬ֭)(m4 0@PTH [0N ?^]++9910"&54>32373#7#'2>54#"qgr\dAT' P1-N5XIe쌀ux52ZB\hQJkwoHZ @0 //]3]210%H9ϛgϛgm%?0@ Y`/o/]]qq+9/_^]10%#!5!%#l)jh-5;@ #'..*%%)5*))**)*)* 0??99//]]39/9104$32#"$732$54$#"%###323254&+^Zm+)׭֭^Vm륟XX`aX^ZƬ֭)( M#\pK< Y/+10!7!- X  ?32104>32#".732654&#"][]\\^]][ABZ[AA[q]\^\][Z^?[\>?^_m% i@H Y j Y *     YeFV`p_@/]]qq3]]]]]3+33_^]_]]]]/+10!5!3!!#5!noq;J+K@0   { O _ ;  wFV7 ?3]]]_]q9?33]]]]q9310!7%>54&#"'>32!x#o<3#VfdJn{,`[J[>8*(R9<~aFjef_\9-%@U F4"HX#h%!I}=()I !#I;-G !?33]_]_qqq?39/_]+q_q_q+q3]93]_]_qqq910#"&'532654&+732654&#"'632-fsֲK2}Lb4>"ZTa4-VfRVs!H=:%389((B\o!@ @/]q1067!#OO.VX8>^@ ]Y ??+??39103267!#7##"&'#!!?0Z-b- u3M IV-\T\P3)}jD1W@ H@ He @H   'fvW?3]]]q/9/]+9]+]+10#"'53254&'73VB?>f>O`'t~R"- H1J M@     v  @ H Xhx  @H  ??9/+]9]]3+]]]]qq103#67'ϺT@fmb-Q) *@PTH[  N ?^]2++210#"&54632"32654)apЭ@WP?TFuinozkZ7 @ 0  //]3]210 '7'77on)1N^g31N^g3aJ'&{; ?55a'&{tq?5&u)'H;Z -?55y^$N@3/o_o ""}Y"]Y /]3+?+9/_^]]q99103267#"&54>7>7#"&54632l~zE2q\9n^H`o^DQmYIS^USV7v D}RsY?aVs\kLFVlJs&$CR&+5 s&$vR&+5s&$KsR&+5`&$RR&+5V&$jwR "&+55$,@0"iY "?3?3339/r+10!!!&54632/#4&#"326?A'%qm646**710*6#gA\5Tl^k-33--33o_ iY @2I  ' I  I  I   iY iYiY?+?+3?9/+9/_^]+++]++10)!!!!!!!#9JVP6C7"OP\`X{7&&z35s&(CR &+55s&(v}R&+55s&(K9R&+55V&(j)R &+55s&,CR &+5s&,vR&+5s&,KrR&+5V&,jzR &+55%+ `@>iY.I&I7I' I I  iY iY?+?+9/_^]_]+++]q++3+310#!#73! 2654&+3#+=}7'۔|sK7GcTdh'5`&1RR&+5{s&2CwR&+5{s&2vR$&+5{s&2KR"&+5{`&2RR%&+5{V&2jR .&+55  6@  HH//]++]qr10 7   '՘-1-Ӗ-+ј-՘d& @ #iY#iY?+?+9910#"''7&5$327%"&'326|lyb\{iu[~rTE;^|qAGs LuY E+P%=s&8CDR&+5s&8vR&+5s&8KR&+5V&8jR '&+55?s&<v{R&+55 @ iY iY ??99//++10!#!!3232654+؇>522T3i{''u:9@05]Y0&  &)cY)]Y?+?3+9333?+102#"'532654&'.5467>54&#"#"'53276$NbI.,9kKֻne^5R^FZuBMOU`x+ЭZF=6%/rJ*55+SS=NB5(F>HtDTzB%N9>Igx=ƵZ!&DC"&+5Z!&Dvs)&+5Z!&DK'&+5Z&DR*&+5Z&Dj 3&+55Z&DP $&+55Zs,:C@/  BB>;>"bY?>>>> > >>;cY H@1 H H H 4]Y+'' 'hY -]Y ??+3+_^]3?+9++++??+9/_^]_]_]r+93_^]10"&'#7##"&54632373>32!#3267%2>54&#""32654m/ JX]~3 9,\4ohBLH}IBEDxFAqX-6<^\Iڻ_KYZ1>`g.(V{P`}T\saS_Zs&FzZB!&HC#&+5ZB!&Hv-*&+5ZM!&HK(&+5ZB&Hj 4&+55%C!&C&+5%P!&v@ &+5$M!&K &+5%$&j &+55H++@%cYcY?3?+9/999+10&'77#"&546323&''2>54&#"j&Vv^LQ@蕬Z aXnBqAMFIr?N&*?MkjΥ}^)odM[jkQ[%&QR- &+5ZT!&RC&+5Zh!&RvX"&+5ZU!&RK &+5Z&RR#&+5ZT&Rj ,&+55m%T@:// I&IP`p Y`/o/]]qq+3/_^]++3/]q105!4632#"&4632#"&mJBBIJAAKJBCHJAAKdLKNIFRNKMQGFQN+f$ @!]Y]Y?+?+9910#"''7&54$327%"&2>7T{fmwH`FTBBR{Hm#IwM 15lkM8Xnik6rPnDo!&XC&+5o!&Xv&+5o!&XK&+5o&Xj )&+55s!&\v&+5h#'@ ]Y]Y?+?+99??102#"'#!!36"32>54&-≲] H-B3GvICHG{FKs̓P`|XXs&\j )&+55&$MR&+5Z&DM#$&+5}&$N\R&+5Z+&DN#&+5&$Q7Zs&DQ{7s&&vR"&+5Z+!&Fv"&+5{7s&&KR &+5Z&!&FK &+5{7f&&OR#&+5Z&FO#&+5{7s&&LR$&+5Z]!&FL$&+55+s&'L\R&+5Z&G8'%+Zq+J@+%]Y bY 0     ]Y ???+99//_^]^]993+3+1023467!7!7!3##7##"&5462>54&#"TX}- )'#-#)MUQDtLAIGzGL5EPlǡTZKõNwlHXzoXX5&(MVR&+5ZB&HM %&+55}&(NR&+5Zc+&HN$&+55I&(O-5&+5ZB&HO+&+55&(QZ(Bs&HQ5s&(L-R&+5Z!&HL,&+5{ms&*KR$&+5!&JKF3&+5{m}&*NR &+5+&JN/&+5{mf&*OR'&+5&JO#6&+5{;m&*9!&J:w1&+55s&+KR&+5%&KKN &+55-M@. lY iY I) I I I I  ?3?39/++++++9/33+33103#!!!#737!!7!7!{)Ά))(2))1V%)%w-ô%` !;@  lY]Y  ! ?3??9/]+9/3+3910)654#"!#737!!!3>32}j]+V+#-!++&*>d PD3{oǡǤ}N\Of`&,RR&+5%|&R &+5&,MR&+5%&M&+5}&,N]R&+5%S+&N&+5&,Q&LQ\f&,OsR&+5%?^ ??10)!R-^R&,-%&LM`Rs&-KUR&+5D!&7K&+55;&.9%;&N9{%^@   ?3?39910! !!!3XxJ-#),^H^5s&/vR&+5%&Ov &+55;&/9B;&O9"5&/8!?5%N&O85&/Oup  >+5%&OO!8 >+5 (@  iY?+?993333103'7!7!7g:fݒ/kkV6wN˧h !@  ??993333107!'7!/@jc'Q5R @ oY"?+??3910"'53267#!!3767!xFZGo~ ^ 5J} .RVdR;P'PuLJ%ms $@ ]Y]Y?+???+910"'53267654#"!33632#hF=5=]l\+bъ)QYD3{1^䧛Ik{&2MR&+5ZT&RM&+5{}&2NR&+5ZW+&RN&+5{s&2SBR -&+55Z!&RS` +&+55{{!]iY@2I' I I I  iY  iY iYiY?+?+?+?+9/_^]+++]++10)# $32!!!!!27&#"F]W\E)5D7#P5XF>jys'  'Zs#09@P  88414bY?4444 44411 1cY  $]Y +]Y! hY?+_^]3?+?+99?+_^]9/]_]_]r+93_^]10"&'#"&54$32632!#3267"3254&%"32654t.?{݊h3f`We]hGpEIImJX-MDDMI[i&0.(y~[b `evaS_5s&5vR&+5%!&Uv&+55;&59;s&U9&5s&5LBR!&+5%,!&UL&+5)xs&6vhR,&+5!&Vv+&+5)ds&6KR*&+5!&VKt)&+5)V&6zds&Vz/)s&6LR.&+5"!&VL-&+5;&798;oL&W9s&7LR&+5^(&W8!&+5(@iY  iY ?+3?9/3+3103#!#73!!!7}}7L77RTboL 4@cY lY @]Y?+?39/3+3+310%27#"&54?#737#?3!!3#Aao !))Ą12))#5~B.TƔJ`&8RR&+5o&XR1 &+5&8MR&+5o&XM9&+5}&8NR&+5o+&XN&+5&8PR&+]55o&XP &+55s&8S?R &&+55oR!&XS (&+55&8Qo^&XQs&:KR%&+5}!&ZK#&+5?s&<KR&+5s!&\K&+5?V&<j#R &+55s&=vjR&+5 !&]v&+5f&=OR&+5&]O&+5s&=L R&+52!&]L&+5 @ ]Y ]Y?2+?3+10"'5327>32&#"HhF=6$*ïkPE@9EM1PA/@ cY ]Y]Y?+?3+9/3+310"'5327#?>32&#"3#hF=6$)ðhPE@9F 1MqPT1PA>b)&./L@.*iYO***$O_o@0p. $$1/?3?9/333]]q]9/]+10!!!&54632%>7!#4&#"326!&'=M+on$*V:Tנ7**600*7+5C pDJB0Bny/ 1p&-33--33oj5U9Z !4BC @O H @/o/ @HC0@ 0@ H.C1.3-"))<]Y)"5]Y"?+?+99???9/+_^]9/+^]q+10>7!##"&546324&#"326"&54632373#7#'2>54&#"'&V:T;rppn7**600*7Pa'9CENM?D{ICz* 1p&kmlk-33--44}gTPG]tXXos&vR&+5Z!&vL&+5ds&vR/&+5+f!&vH-&+5);V&69;s&V97`!@  @/]q3910#&'#5>7!`?ci?4Rh25sXP9B6Hy! @    /]q29103673!.'y?csuSh2!5s^Ji6H@ /]q310!!k-{++@  U e 6 F  /]q3]]]]210!"&54733267{7IQNc+~&A<6GB @p   /]q3104632#"B_WILX\dWY>:Pc  $@w  0@x0/]]]/310#"&546324&#"326 rnon7**600*7komk-33--44T@ ?3/9]]3103327#"&5467wH ,:TPfw_x`h2kXRM;W@;Vfv7GYiy8H    /]q99//3]]]qq3]]]qq10".#"#3232673b1K@;!1;1MB:"0B!'!835!' 37!@ @/]q321067!#%67!#ru1+`ru1+`5E5E5^/]1067!#5LJxƧFV9@$_o@ H  F 7  /33]]]]33/+]9/10>7!#4632#"%4632#"IZMD<=GJy'MD<=GJy*KNP93IWnNP93IW&$T ?55)}jD"/'(T ?55"7'+T ?55"',T ?55>&2wT $?55"v'<7T ?55>;'vT *?55Gx&U .&+555$5%5iY?+?10)!!b5.6b(5(=5+{r@NiY$,I$$%H "I.INI.I$H' I I I iYiY?+?+9/_^]+++]++_q+_q+++++10!7%# $3 %"32654&5D5\_"ńs}jf!V3 18Ŏ,5.R ?2?910#!!&5{V; /*TDDqOW5051h IiY@&I' I I I  iY iY ?+?+9/_^]+++]++10!!!!!8eb755{3w=H{25@ iY?+?310)!!!d350L53 $@iY iY?+9?+910#7 7!! !+116> T5)7?<{H 9@  oY oY _     ??99//]q3+33+3107.5%7!>54&'!-1wtY%%1dvpvo tב?  Ǧm ©py;!@iY ??339/3+310#!#"&547!;!3267!\_^'eop"f)omNϸYl!D1bR!(@ iY iY ?3+3?+9310"!!.54$32!!$4&hsuGJ;5kk{Q٭m7;o˅ECgbV&,jzR &+55?V&<j#R &+55Z^&~TD 7&+55'/^&T .&+55%m^&TB &+55`^&T &+55w&U 1&+555Zs!/-@)]Y dY"]Y??+3+?+9910>73327#"'##"&546322654&#"y9-]C=$?f-Q\Y}3MtC@AGyHA^SxA _F[N^ VP`xc\+H@)$%%$]Y%:%% I % %% ]Y ]Y?+?+9/_^]+_]]+9?102#"&'!>"32654&+732654&#๧}OLrY.Sfh6qic/3;n|Jw!*cx|#~\\|DK;^@  ??39/3910!7!3>7!}3'' 2KCbjP k3t 7'7@ "]Y"^Y  cY?3+?9/93++9310&54632&#"!"&542654'pcwCP.hef~M~554&'.Z}Nf2 'ֈV\ja1WcwM8Q ߶|Lk*2ObB[\wu-"4%FB%ms@ ]Y  ????+910!654#"!33632hl\+bъyD3{1^䧛IkdT@8cYo*I#IO2I%I cY cY?+?+9/++_^]]++]q+10"&54>32'267!"!654& ?3A^a$\901=@!($%$cY//bY%11]Y_o/]?+9?9/+9+10#"!>54&'.5%5.5467+7!#";PFFlxW-WcqO;Jk`k:51-jusfgsDU4[\qz.&3"F;^rPp* @xSPTTZTsR^^%@ cY]Y?+3??+3310%27#"&546!!#?!#ABd rο/l!7yF3yR(Hhs%@ ]Y ]Y?+??+9310#"'!>32"32>54&h_ H.֝NWj"71dFsMB|R:oXpXbZs !'@]Y!!]Y_ o  /]?+9?3+10!>54&'.54$32&#"Ndja1WcqO>G \~bTOuPj-2ObB[\qz.(7 GƔKH9pZ;^@ cY ]Y?+?+310#"&54$3!!%"3254'JWσ1A/%5iRt)Nm[;-xՄof^%^ @  cY]Y?+3?+310%27#"&547!?!!Aao s1s#5}2>RJw^@  ]Y?+?310"&547!3 ! +5-PƶFP`=<}aZs#$@]Y!cY???+?3+310.547>32>54#"iߥČnTDn/̯Ω`fQ^Y(={mxҼ &݃s5@ dYdY?+3??+39933?102!327#"&'!.#"'63|@LJ 01-:ab~)5 **"'4es1:;!}NMA'w @  ]Y ?3+3??3?10%6!!.547!!wĵ-cb+[W `-1ƪFP`=54&+!!!!32uNPDIL%?F7%7l94b&'LA=6^'BL5s&av}R&+5{7@ZiY$,I$$%H "I.INI.I$Ho' I I I iY  iY?+3?+39/_^]+++_]_]++_q+_q+++_q++10"!!!27#"$32.95\zhvH˿MM+18)8)V6,V&,jzR &+55R-?$0@$iY iYiYiY?+?+?+9/+1032)# #"'532>!32654&+DJOP?5/ATPiGLw_amu?'HyuJQ5)@ iY  iY?+??39/3+310)!!!!!3232654+@m52wy1wVI}my1Eynh#@ iY   iY ?+3?39/+104&+!!!!32!67JKِ7%7l9Td 'D7^>p^25s&vR&+5&6/R&+55V @  " iY ?3+??310!!!!!!j40\\LJV$5 &@ iY  iYiY?+?+9/+102)!! 2654+dN566A%lTyujw5%5a;V $@ "iY iY ?+33?+?31036!3!!!!5y\] NoLTV^5(!@  ?33?33933310 !!! !!!/ %H'ܗ TT-13)'f@   $oYoY-$H@ I" I I I   !$ oY ?3+?39/_^]+++++_q+9+3_^]10!"&'32654&+73 54&#"'>32ŏ͋N٪3_^m{mw $+cys]WHR{[GX5@    ?3?39910 !!7'!!r^d#5AJ؏LF5&6R&+55 @ ?3?39910)!!!`52X1T-@iY iY?+?+?10 #"'532>!!#OP?5/ATPiG]u?'J505+{25n53{7&7 @  iY?2+?393310"'32>7!67!|WQu0J?13.XN  '"AQ]:n'yI{Hsy;5V @ " iY?+3??310%!!!!!h\/540`L@m@ iY ??39/+310!#"&547!327!w p1l 21V9;%.$JJ5 @ iY?+3?33103!!!!!55:};}9LLJ5V!@  iY"??+3?3310%!!!!!!!\5:};}9`LL@' &@iY iY  iY?+?+9/+1032)!!32654+CI7dlHuj5"@ iY  iY?+??39/+10)!)!3232654+5:h52w%iEM6Kuj5R @iY iY?+?9/+10)!3232654+RI52wCJdlKuj@ZiY$,I$$%H "I.INI.I$Ho' I I I iY iY?+3?+39/_^]+++_]_]++_q+_q+++_q++10%267!7!75!"'63 #"&'<5A}PyS83ZE%.`5#C@+ iY I) I I I I  iYiY?+?+??9/++++++10#"47!!!!32%"32654&Ͱ52yPe kfwjkerD'2, 4;ɋ 5iY@I I iY  ?3?+9/+++910 !.54$)!#";akY3wm^l1DkJ1roO[ZsDm&=@"  cY  m   ]YdY?+?+9/99_^]]+3104$%>32#"&%2>54#"mEiS+B_BsGs8<i2"SYykZRsOZZs *A@&$$bY$&I$I$$bY!bY?+?+9/+++_^]9102#"&54$32654&#"32654&ǻu}YZu۔[YU\apU'W_BsnrTjYCh iuVDBAnFE05s&;@!"" "&$cY$cY?3+?3+9_^]]3310632327#"&54>7>54#"гBpRdA^rFoE\Y$yrX_{T;#$*dZ/']|S6)(*VKZ&*@ !]Y ]Y]Y?+?+39/9+10#"&546323754&#"'63 267.#"yݍ`ja?j!YGsLJѺ$ε)1BWݺXcZBsHs9i@B7bY7I%I"I+I%I I9']Y"-  ]Y2?3+3?3+3??9/+++_^]+++]3+310##"'732>54&#"56323!3632&#"327#"&57#!+9M:/5THHA-LLfd!d9$GA45}HA-LIi` ށ`a/ `a-=s$d@$# # $#bY$I$ I$I$@I $ $$ ]Y cY ?3+?3+999/_^]+++++_^]9102654&#"'632!"&'53 54+7όtDVEMH|eE-;D.4-"Vg%8(#Xuo^Xo?&X6&+5%^ @  ?3?39910! !!!X!u.o^!^s+@ ]Y dY ??+?+993310)&#"#"'53267>32Ӽ(CZJH#C[`Y<%/2O&fpWEu?gNio/u93^@    ?33?399310#&'!!>7!!R*?; B-'>Oh^#TG54&'ܢ`h+ZVV]R]Q[Od٦&/ g i jr`^[jo^ $@ ]Y]Y/?+?+?39103267!327!.'##"&5473l\+b->'3`85Ɖ^sD3{Au_;ȥD\^@ ]Y  ??39/+910326?!!67##"&54?!>>9b--@ 3^D2?@/U_Oj^%'@ % !!]Y ??3+3?3393103267!#7##"'##"&547!3267c]+c-+ Յ-b\/`^sF1{㦛Ik}sD3{jo^.,@. $**]Y]Y/?+?3+3?339103267!327!&'##"'##"&547!3267c]+c-=)1aa+ Յ-b\/`^sF1{Au,㦛Ik}sD3{\^&@bY cY bY ?+?+9/+10632#"&547!732654#"cX}X1LCVgFZ^:xeFn=BYI{ NjJ^ $@bY  bY ?+?39/+?10632# 47"32654!!5bbisܘz%9J>4IY-^:yhEWfp}C5=>]Iw%^h^@bY  bY ?+?9/+10632#"&547!"32654V|-FZNBVfyeAsw}g%>AZH{s@h bY/?,I""#H I%I"I,I%I I/o    ]Y ]Y?2+?3+99_^]q9/+++_]_]++_q++q+r+_^]102#"'53267!7!754&#"'6ב쿘h4xNq!+dWQ9]9Ps7'u{[c R%Ts G@- cY# I"I"H" I   ]Y]Y?+?+??9/_^]+++++10#"&=#!!36$32"32>54Tb0]9NDnD@nB53^R y}{^#W@! !bY!ͲI!DzI!@I!"H!!cY dY?+?+?9/+++++_^]910"#"'53267>7&5463!!";9R.Bo[<(%*5:O5[O]FM];Odt-"`NAWI7CZB&Hj 4&+55%`*+E@%&'&lY'')$ ]Y   -+)$]Y?+???9/_^]+99/3+310!!3>32#"'53267654#"!#737!{++&,>d)ɣhF=5=]j\,X+#-sǻfN\OfLQYD3{oǡJ!&v/&+5Zs}@TbY,I/""#H I%I"I ,I%I I   ]Y ]Y?2+?3+999/+++_^]_]++_]++q+q+10"&54$32.#"!!3267ؗ \6hB["o+LEZH"sr/#OsV%L%$&j &+55M{s(5@@&)bY &&]Y&dY 1bY ?+?+?+9/_^]q+10632#"&547'"#"&'5326?>32"32654?ln|Z-CYGDHge3;!%/3M#+Q̝ΝEZLCVgyeIk@[ gp\:[#=BYI{1{^$P@1bY cY# I"I"H" I  bY ?+??9/_^]+++++39/+10632#"&547!!!!!"32654?tf|c/\'Z-EZLCVgye=]3^R}[#=BYI{%`%!&v&+5s?&\6&+5j^"@ ]Y0???+?39103!3267!#7##"&547T1`Lۉl\+b-҉)=YsD3{㥜D\5@ iY?+?10!!!Bu56H%5@ ]Y?+?10!!!!I@f^1s&:C%R &+5}!&ZCs&+5s&:vR'&+5}!&Zv;%&+5V&:j}R 1&+55}&Zj /&+55?s&<CR &+5s!&\CM&+5)cY/]+107!)1T1)cY/]+107!)1+1)+w&I%Iܵ$IԲ"Iϲ!I@ IIβI@ I`p 0$'H ?9/+9/]+++]+++q+++10!7!7!7!`55 1AsZ ?10'673{g[gjR ?10#6JgYj'/?/]10%#6ygXj' ?10#'7 C hg{ s  ?3210'673!'6739gab%g[gj ?3210#6!#>JgYjg/}'5tLB@ /?/]3210%#6!#>ygXj d2y'9{B  @  ??9/33310%!7!%ɘ4'"CBa1)@    ??99//33333310%%!7?7!%%NC336X38D4/wv)  /3104632#"&욣9&'1b7@- >/?oO0]]q5+5s u '5@N9@"6L@3333<""E,O?3]2???333]22310"3254#"&54632%#"32>54#"&54632"3254#"&54632?_8A^pzo{@9'H.7)H/lyj}'I.7B^oxj|``mJp]ana`d۔"4p]a`dړ!% HZ@0//]]10H9ϛgZP@0//]]10 '7Pn)1N^g3'3 ??10 #?JX& @ 32&#"!!!!!!7>7#737#7s)îq`Hc^']%+71em%'VDM_qwE!n\w9%.P@-#&iY##% cY@o .iYcY?+3?+9/_^]3+3?9/+10%267#"&547#?33#!2!#!32654&+&@+h{>o{3.B.=Ro3`^D5t/Vjs$(ԿKP)&q@G tYtY/ @H ?@ H# sY vY?+3?+39/+_^]q39/+^]3+3+310"!!!!327# #7367#73632.a8\' +%Yg~B% }'WGdV}WXՕ"Qx{??C0.91{'31@8(%%%/0 8?3???99//310"&5432&#"3267 # #"&54632"32654ʨmb7U;2M+63!=@`?ʮ}̭+E(*&?T})#PA324&#"32>/5-0 'N']scoz$&+F-J*F.ND}Jysms-32!3267.#"1RRHbٓ2XzK#5Fh)|5Bua'&{>d ?555\&u')> 6?555f'&<> +?555''>f= ?555Ls$;@" dY cY   ]Y?+?39/9_^]++9910#"&54632'.#"63227.#"sۿҏ떉T odh'Q5FsIBϪ7CsZR2(6lpb @  iY?2?+9310!!7!&u`'#*L7W`7b@ "iY?+?310!!!%7}{7T $@iY iY"?+9?+9105 5!! !{? H7Bo md%?@Y`/o/]]qq+105!mdb9?3/9/310##5!3նFl{L#(M@0#0?##@00P`o/]]q]9999//10#"'#"&5463263227&#""32654&L{=Mq}XNK]:CEiWQPZ:CH͎İPZ凇N;Jqh  /?9910 # 3 h>s>s!fg%t&IL%&IO=?@  /]q3210#"&547!3267"Ԙ ck?LXbD^  ]Y?+?10"'53267!XhF=6=\-MPZ) ?1067!#S)@{9E{;   /]10>7!#{Q!}EV39>Mm! /10!5>73mWC(;;J/J- i@JFV4#YH ;  $HY i y  /?@ $H ??9/+]q9]+q333]]_]_qqq310##7!7!3!?w !yw: <ᗗ-JL^95YIIIҲI@Ia')  0!?3q?]q9/]]+++++102#"'532654#"'!!6qڳ]3|9Yc?FLw)+$k6#NFnC {Jm@  ??3_]q910!7!{j'/J)R5'",e@G} H) ((t((6(F(f('(((( !###?2]?3]9]]]]]3]]]+]]]93102#"&5467.54632654&"654&dYAOƫxm39=5,4<+##1@^*paXi $bIi^x&#W>n,]*2?+,82(8*)K"&T #'+/37;?CGS[kt|@T %11&2}kKKvVulE==F>c\tZZQ)AA*B 2Vl>\QBBQ\>lV2 -, 48!59/333/3339//////////333333333333333333310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3#"&546323254#"%32+32654&+32654#"'53253T/0momImmmm0oowoooomm~smp.,;0m^{B.$*/;J1%Z^4+V}i0oo/mmmmmmoo;mmJoooo/yhIaCS1@ D8QYb" "+%J fVr_cT*@(""//39////310 5467>54&#"63232654&#"TV'FdLOGRZ?>1HR=GFBIHCHEVW//=8NZ8*P:/5K6CpK;?HI>@IH!&7L&+5jRu 8B@%]Y **]Y6 11cY1']Y?+?+99//_^]3+3+10654&#" #"&547>54#"563232.546323|H?BA6 9&2tmkj#|z;|1Vb27u|XA]2:-jTJqS*f lp##H!@  oY?+??933107632&#" !!qCфSO8&;@uw8f{PN/^^+1@##   cY )]Y?2+3?+339/910"&547!?!##"&'4'!326?!32)YIi/lBpESdrӾiRXoaim]I0۬oU2s+5u&0vT&+5%!&Pv/&+5&$X^Zs&DXB; ??10#nWJ F@0[k0  &    @Wg0/I,I?++q]]]]_]]10#"&546324&#"326rppn7**600*7kmlk-33--44X,(@ ^Y ]Y)]Y?+?+9/9+10.#"3>32#"&54>3&#"3265v?9%h2?JPNA32&#"!7>32&#"3#!"'5327!!!4632#"-hF=6=\̣)ðhPE@9F )ðhOEA9E 1MhF=6$tM-`WILX\PZőTT1PA>T1PA>^WY>:Pc%;8<G@%;:* 66cY'  #]Y2]Y-?2+3?3+333?33+33??10"'53267#?>32&#"!7>32&#"3#!"'5327!!!-hF=6=\̣)ðhPE@9F )ðhOEA9E 1MhF=6$tMI-PZőTT1PA>T1PA>{#4@    iY@ H iY?+?++99_^]310# $3 67!%"32654&\2p / /ysyypA' J:Tͷ>ƎZ"$@ ]Y]Y?+?+93102>7!#"&54$4#"32>sDT' 0.KwKKxCs<3NMEz}{`@   iY ?+?39/310>7!!"&547!3267'LT/ 7!#7##"&5473l\+b-3D+/ :̮҉^sD3{p >aj㥜D\!C!vRERA@ (  H 8 H8)@ H/9/]+9]]++q10#7>54&#"5632NA 3JIezAZH{5j$R@2"iYiY I) I I I I iY?+3??9/+++++3+3?+310"!!3267#"7#!!3!2&75|JƆ52yR zhv˲#3 -M+ V;18a%s%t@H# ]Y I Y # I I,  I w f  I     ]Y$?2+??9/_^]+_]]_]+_q++3++3?3+9910"&547#!!36$32.#"!!3267b0]B#µ\5hBP'g0]Q={[& 1^RH#mb`a#/O !@ oY ?33?39/+310)#!!!#7&'/)uD15|wDw~{;^ )@bY H   ?33?9/9++310!#!#!&'9%Pa) ^ZZ^e5-@  iY  ?333?9/3/+33?310!#!#!!!!!!3&'D1/Z5)x^?u*wwwCDdڗc%H^0@  cY   ?333??9/+333910#!#!!!!!!!'Vhc\\jZt333^R1@iYmY ?22?9/3+3+99910#>77!!.+!#" !VDHyq !/.}ʃ7W(Fkى$D`T{O_3qC^T@0HB bYaY P`?22?9/]3+3+939_^]+10#>77!#.'!7!f=zPR%! \g/8źs}mhh3I@=885 %B@# lY iY  iY  ?333??+999/99/++310 !67!!!!7!!.+!#"7m2;52yG^]2 !./}˃ 9P)qCLo?&[xD`T{M]/%^"f @=H  B     " "bYaY Y_ o  P` ?333??9/]9/]++3+999_^]+10!67!!!!7!#.'!7!+2c\SP%#]g/8ĻLX)3^RFhh 3HA=88/Ki@:     +*+*oY!#++M= =7;;FA7@I4771oY7nY"?+?+3329/9_^]9/99+999_^]]]]10327632.#"&54>7>54&+73 54&#"'>7&'53>32&#"řÃ^)wGXW7zJ8`yB3_^mVn2fk/P`b:<*"&%_Qw ")J)vY!:TF[SHR{7Mhp>YK' CW!/)dL@a9)I))))++:+J++ )+=#LKLKbYBD BD0 LLN =0bY=Vf    bY ?3+_^]29/3^]9]/+9/999_^]]+999_^]_]]]]102654&#"'67&'53>32&#"32632.#"#"&5467>54&+7ՍmDVEMHu)Tk/Q_e7>(%#$N>ytpUG>EADBL4V]ߌs4XX-;D.4-"4P{p>ZI' 1>?f& 8tX -++! #;24:uw{ ]@>iY"III"H I) I I I  iY iY?+?+9/_^]+++++++_q++10# $32267!"!74&\4{1x=yA' Cɴ8۷3ZTs |@R  bY'#I""I I-,I%IIM)I  ]Y ]Y?+?+9/_^]+_]_]+++q+++_qq_q+_^]10#"&54$32267!"!54&TM|"{PK{$LMT|r[b{ndi@  iY?+??910>7>32&#"!!J\v_RF4-@Z3'31:"῕HZcIYff@  ]Y?+??91067>32&#"!! 32%"'632654&'ϣ BA32>l6<a\V+TTTe^8*MWe'e70#i%LL 5RN1ᇗ4 ?)&Zp@?74QQ:A'HAHoY$-AUN:NiY4:$#$ lY 0 @  p   @lY?+_^]q9/+_^]?3+3?39/+39/910#".#"#>3235>54&'.54632"'632#"&'#"$32'&#"32673265)3GzhX'+3>wux@H??>GV3X3i޴}>Vi:a4$DHcjW[)OmDvm"("3;&-&p \1* "-2Q)#i8NKVC(;,0pqXW2XZTR,@Ri@8DDP&/@bY//884@4;bY4@%H4@P &  ! cY cY?3+3?+39/93++_^]9/+9/10"'#"&54632&#"32673254&#"'632#".#"#>3235>54&'.5432'iɐfVvvw@I?}HVnnU>)z݌?Nl`)>"("4;&-&w\0* "^RA&i hZA&+5f&j h"&+5{7@iY iY  #??+?+310"327!&5$32.}nf\zhvH˦$& 18)8Zs@ ]Y ]Y??+?3+10!.54$32.#"327fk\6hBUMdi֭[H"߀-hy  ?/10%'%7%7%LG㴁FGJ{J;{Z}9IĤ{-{H@   W/]q2/]10#"&5463!632# "Y02H?Y30H=^;-54&'.5432IK}GV\0* "^R) (6DR_m@iP4H, B&:V^&,c4k no-&44)"00)d^Wkk`ZggS`IB;PPE>LL7E)`EE`)P     ?o/]2q2/]]3q29///333q2333q2333q29333333102#.#"#62#.#"#>2#.#"#>!2#.#"#>2#.#"#>!2#.#"#>2#.#"#6!2#.#"#>]qO?#&*-O;_;;;;6@3P3333.6-&..&-6  ?/9////////]]93310#67'>737&'.'5467'67".'7&'7 F$a5; Ia4#GA݁hBO݁E?軋Ek(8PC{Lh&ZC7#BO݁GA܂ Ia5; F$a5[8D.^3DuOW.FcB=FK5VH$2@ iY  "$_@!0?2]2??+99?10!3!!!#7'!#"&547!3267jA r1^d#"Ԙ dlLFTL؏L/[_jo?+-@+#@(0 @cY?+??392/10!7##"&547!3267!3!#"&547!3267Ɖ-l\+b-i2 ckȥD\sD3{?¤Xb5R9@ iYiY   iY?+?99//_^]3+3+10)#737!3#3232654+RI7 2!8!CJdl!Kujs%&6@bYbY & !bY ?+??9/+9/3+310!!632#"&547#737!"32654m/)eX})/-JFZLCVf5)xeFn N=BZH{5*@ iY iYP   ??9/]++910'+!!237'7654&+솁8B[cm5554#"'7RiW3> r: HV p;JW[SWHs^}ZmRF]JA׈uK_R $@iY  iY ?+?9/3+310!!!#73!!58}7.6RTd^ $@ ]Y  ]Y ?+?9/3+310!!!!#735I/1XZ1b^^5+@iY# iY iY??+9/+?3+10"!!!632#"'3254&VI6u5.6VPXsؾzȑ/ g13 ӥ% ^+@]Y]Y dY?3+??+9/+10%#"'32654&#"!!!632ՒR`}YW/#P5I53Pʸ+6ؼqz^V.@"  oY?+?3?33933?10!!! !!!3!;ܗk/ %Hя\13 TT-`os9?@!7bY91']Y"-  ]Y2?3+3?3+3?/?9/3+310##"'732>54&#"56323!3632&#"327!.=#!+9M:/5THHA-LLfd!d9$GA45}NERP\` ށ`a/ `a!o֯-=)&|s&|q5V"@  oY"??+??3910%!!#!!!#]52X`1T-%o^!@   dY/?+??3910!3!#!!X!`u.o^u!^5#@    ?3?9993910)#!!73!`{Vyo52KXNTTf/Z^#@  ?3?9993910)!737!!#'-oVDP"}#N/^d@( iY I ) I  I  I I   "oY?+???39/++++++10%!!!!!!!!\)52yy1`w=%o^E@*  cY# I"I"H" I   dY ?+??39/_^]+++++10!!3!!!!D]]Z-^`b^Ru3^5 =@' iY I ) I  I  I I   iY ?+?3?9/++++++10!!!!!!mΆ)52yyf5Lw=%!^ A@) cY# I"I"H" I    cY?+?3?9/_^]+++++10!!!!!!D]]Z1`b^R3^5-@iYiY iY #?3+?3?+9/+10632#"'3254&#"!!!!I_tۿ{ɑM2uE513 ԣ L% L^-@]YcY dY ?3+?3?+9/+10632#"'32654&#"!!!j3PʒVb|[V.#PӺ^+6ؼryy^{*4L@-+0"(0nY((`(p(( ((iY ""iY oY ?++9?3+9/_^]+910327#"'#"$32&#"&54632654#"2?Ld2q`{ZsTQUz|?q΃+Tm>E_m  ` $-xL}fޯbZs*6+H+@ H+1(1bYP(P(`(p((((H( H(@ I( ((]Y ""cY bY ?++9?3+9/_^]+++qr+9++10327#"'#"&54$32&#".54632>54&#"yZ">8FUk`}ؙ32327!&547654#"!654#"!336L+Di;=)1`@b^(`Ӊb\+bsnvJhAu1>A/D3{/D3{1^?<f^ @   ??33?31067!!!,,)Chi'05XR^PaL?5@! iY? O    ??39/]39+310 !!!!!!7!svV'5:958fPy^!@  cY  ??3+3?391067!!!!!7!!(4)C1781'-1wdR^9Vy"@"  oY?+??39?10!!!!!!t@@kX߲\+`o^#@   dY  ?3/?+9?10!!!!#!{Asb- ^}=!NuVVo&@ iY iY iY"??++?3+310!!!!!!!B\/750 LNT\o1^$.@!"!cY "]Y]Y/?+?+?39+3103267!327!.'##"&547#7!Xfa+b->'3`85ƉP11yXD9uAu_;ȡZyV$@"iY oY?+?39/+?10!#"&547!327!!!w p1l 2\1V9;%.$J@`o^+@ ]Y dY?+?9/+39/10!?##"&54?!326?!3!@ 3->>9b--`/X\OD2?@um@iY ??39/3+310)#"&547!367!7wfLFA p1l }EDR^21./9;%.$v H,^%@ ]Y ??39/+9991073>?!!67##7"&54?!>H1/E\#-? $>I6/3^D2^#ß/-D$ըO5@ iY ??39/9+10!632!654#"!j2w pm V5@.$Jq%!^@ ]Y  ?3?9/9+10)654&#"!!3632=>9b--?#F1?@^ZZD=;#-1@ ' oY''iY$iY?+?+39/3+310!2!#327# 7.54733"32654&Xv~jűJ5--׀0\Ed'BƳKM&VqubQ='/gɞvr;Ds#,M@,'bY ?'''' ' ''$cY"cY?+3?+9/_^]_]_]r3+310"&57$5473;632!#3267"32654:+)! <=ٱ3g`We6X-)dR@G*.[i&0VsaS_=V;&0C@#" $$*oY *'** iY 'iY ?+?+39/_^]3+39?10473;!2!#327!&57."32654&=J5-- ^~ v~jņV^B0\EqubQ='/,=ƳK= f2Vɞvr;Dos&/S@/%*bY ?**** * **#%%cY%'cY/?+?+39/_^]_]_]r3+3910.=$5473;632!#3267"32654\:+)! <=ٱ3g`WePX-o'ҡ)dR@G*.[i&0C+saS_,&6`R&+5?&6y>&+55(@iY# nY ??39/+3?3+10"!!!#"'325sSMm52XvbĨ{z!TR13 B% ^4@bYO dY ?3+??39/_^]3+10!#"'32654&#"!!X-]b{|oF;N-n^۷դ+6ȵt^V$@iY iY iY"??+?+?+10 !!!! #"'532>1OP?5/ATPiGTLu?'os!#@]YdYcY/?+?+?+10%3!#&#"#"'53267>32 (CZJH#C[`Y<%/2O&fpWEu?gNio/u95=@' iY I) I I I I   pY#?2+??39/++++++10"'3267!!!!!{}'k)52yy1=12=% ^A@)cY# I"I"H" I  dY ?3+??39/_^]+++++10!!#"'3267!!D]]Z-5ԛgrobx"Xb^R:<3^5V>@(iY I) I I I I " iY?+???39/++++++10!!!!!!!!=)52yy11w=TL%o ^C@)cY# I"I"H" I   cY?+??39/_^]+++++/10!!!!!!3!`b0]]Z-3^RVm"@iY "oY?+??39/+10!!!#"&547!327![єD p1l 2V;V9;%.$JJo^A@' ]Y*I1I" I  dY ?+/?39/+_^]+++910326?!!!37>7##"&54?!>>9b--T  <[3^D2?@o7-uBKO5VH'@  iY" ?3?3??+99310!#!!3!!!#67#j=+#5A %1+B mҗXETLou^%@    cY?+?3?3993/10!#&'!!67!3!#R*?; @I'>Oh^#TG,^&$6uR&+5Z?&D6%&+5V&$jqR "&+55Z&Dj 3&+55oZs5&(6'R&+5Z?&H6&&+5D";@"oY & I  iY iY?+3?+9/+_^]]+10"63 #"&54$!3654&267#"mS "k@N, NFĴ)%wnX- 嵱M63g`WegŝuaS^)[i&01%DV&j}R 5&+553&j 4&+55V&jqR $&+55&j L&+55)V&j-R :&+55&j 7&+550@iYlY oY?3+?9/+3+910!"'32654&+7!!KJ-{17Z+LO*9taY\-^3@aY  dY ]Y?+9?3+9/+310!7!#"'32654&+71V)śעFO|-u?͠P (6iu5&M R&+5o&XM3&+55V&jR "&+55o&Xj )&+55{V&2jR .&+55ZT&Rj ,&+55{{ZTs|{V&{jR 1&+55ZT&|j 1&+55V&jR /&+55&jO 0&+55&MmR&+5s&\M&+5V&j1R )&+55s&\j )&+55s&SR (&+55s!&\S (&+55mV&jR '&+55&j ,&+555V @"iYoY?+?+?103!!!!55.6\>`%o^ @ ]YdY?+?+/103!!3!%3I`^u5V&jRR )&+55jJ&j 3&+55&}@  0ִ>+]q5^7@ Y ]Y dY ]Y?+?+?+9/3+310"'5326?!#73!!!!!hH7K4Ʈ1#9;fqɋ}KHZ#1,@  +]Y $$]Y ?3+3?+99?/10%#"&5463234>!326?!#"&'%2>54&#")Ϻ努` S- @3KN51C-߽s,IwFCIExKLȾhK[2$8=cVTI|P`XXq,1@mY(**%oY* iY/?+?+39/+9103267!#"&54654&+732654&#"'6!2DntzBPT6c, ml/QOrs) n}t`w-&M-OUws?NiӐs+j@(bY`pIIϲ I@ I,"/ "cY " ]Y /?+?3+3_^]9/++++_]+9103267!#"&=4&+732654&#"'632A2IM=1C+Ͻ\}-pIQFJH{J65ZiŢ)OD:E6,.!Vj$ V%3@$"oY oY!oY?+?+39/+9?10!654+73 54&#"'>32!!DO4~^^m{mň \m?-HR{[GX` wO`os$^@bYIIϲ I@ IcY# dY?+?+39/++++_^]+910!7654&+732654&#"'6323!L7^^-}tDVEMH{`DH;;D.4-"Vj# 3%Xud&#@&iY&iY iY /?+3+?+103267!#"&547! #"'532><;ETT5b+OP?5/ATPiG<"3?fq-ñ>qu?'s/1@&$$"*]Y*"dY ]Y /?3++?+993310326?!#"&547&#"#"'53267>32+@3KN51C- b(CZJH#C[`Y<%/2O&fpWE T8=c¯6;?gNio/u9q25o=@'iY I) I I I IiY /?+??39/++++++103267!#"&5467!!!!<;ETT6c+!)52yy<"3?fq-ô-k=%^A@)cY# I"I"H" I  dY/?+??39/_^]+++++10!!326?!#"&546?!!D]]Z- C6CQ51C- b^RV4&68`w®8#3^{/@iY*I iY iY?+?3+9/+3+10!! $32.#"!267!#DseJ!50c6"ʷZs:@"YII ]Y ]Y?+?3+9/_^]+++10!!"&54$32&#"3267!7 82mDmh[iG2&Halkj9@ iYiY?+3/?+10!!3267!#"&54777<;ERT5b+<"3?fq-ñ>q^^@cY ]Y /?+?+310326?!#"&547!7!!b@3KN52D+T/1` T8=cñEt17D']@" oY-  $H @%I " I  I I   iYiY?2+?+39/_^]+++++_q+9102.#";#"3267!"$54675&54$#~Afjz3{ˉ{]O,>P/@^N[Sur\N5+VȲ H'/s +@ iY iYgY oY?+?+?+?+10!#"'5326?!! #"'5326+X'iH732'M'hH73232673767!#&'#%A6=)%" yQ>q,-#%5 A<>$^,x) /#yx1 '-<MRz<`) C@+vWg@ H@p/]q]2+3]]]9/]10 33273673#3єTVB"?eG}RYQ? 0C@+vWg @ H@p   /]q]2+3]]]9/]10&'53%#"&53327B~58"џTVBsbjiNYQ @"H"H@" H`p_o@ H V  @/H  f v W  P`0@p/]q]]qr23]]]q+r9/+]]9/++9]+]10#"&53327'#7>54#"5632;"џTVB5CD h 63-* *3232673#"&'.D"̛R_Xl#>uQ5T28%#' 5)>5%0?B8I{Lpm.(F /?310254&'3#"'5Jm1-\=:f5a9vr|`/ @  gYoY?2++10"'53267!hH732"32>54&bR:)#-##)&GK>JxNJACzKAǡǏTQDγ~zuP`uPKf#+g@?$#$#oY)$$$"I$$$$ I$ I$ I $ $$+iYiY?+?+399//_^]+++]_]+_q_q+9102)#"#&546332654+732654+觙dw4DS iu3yx n[F1JtgbZ5%#-@ ]Y cY]Y?+??+?+9910"'##!!3>32"32>54&bR:JX1,4HN>JxNJACzKA؎SBγ~zuP`uQJ}@ iY  iY?+?9/+10!"&547!3232654&+ 1v^]r+ë18KCuYO }^!!@]Y ]Y?+??+910#"&547!3>32"32>54^-M) 32&#"&Ӏ\k^(GD45-;KˢĴMM+>KgZ#%@ ]Y ]Y"]Y?+3?+?+10"&54$327>32&#"&#"3267ו(4*K=07aTnUM[QLE[ Ts}J߀`a/#O%+f %@ iYiY?+?+39/10#!#"#&5463! 2654&+ >4DS '۔|rc[F1h'H? &@iY iY iY ?+?+9/+104!3!!!"&#";?V'e?5]EiaY1JwfUWZ$.@cY ]Y]Y?+?+99?+?10"&5463234>7!7!#7#72>54&#"꒪X #1RG!FvOHFEyHSď JcWNuHSz'N@."o ]Yt`" "  cY?3+?99_^]]_]+99_^]910#"'732654.'&5!2">54&ї߹wCP1c<hggNmpf@915Q=buO,ՂU]` IiY@&I' I I I  iY iY ?+?+9/_^]+++]++10!!7!!7!!/P5D51yAJD)']@  oY-  $H @%I " I  I I   oYoY?2+?+39/_^]+++++_q+9102&# ;#"327# $54675.546$hu3띌d艘MQwYStu^Y^.(ͺr}h3&@ iY  iY oY#?+?+9/+10"'53267!!!!+\F160D O.6O8'+=>3;Ť{V(J@) oY  (iY   iY $iY?+?+9/_^]+_^]+_^]10!# $!2>32&#".#"327!3ZX(GD44-<L`X񉑖L^B5.!& >K/4ͱ+{:@! H   H kY#?+?39+399_^]+10!#"&5467!672654';@C2q[)f CB-15H@LJmYJ+wiWVRF5C%5)'@""]Y" ]Y ??+???+9103267!#"&54?654#"!!3632%Vd#}+8%j\(hJ-D!-z -mX<_F3y޸Ê1CFf oY?+?10%267#"&547!=?o1 5}4i+;H@b  iY iY 6'I&%I"$I.III/I#I7IL I I iY?+3?9/_^]++_]+_]_]++_q+++q+++33+_^]+3103'7!3#!?#sJ%%J7J%y$JZZRRRRZ5@iY?3?993?+10)!!?>32&#"u}m52jtvHCB/1KKPF>e1Z%#@ ]Y ??399?+10! !!>32&#"3XxJ$[>24-B J % ^Hð:;9v, !@cY/ ??9/]3+3103#!#73!1Ӆ1-\w7%@@ %dY@ dY ?+23993399?+?910''7&#"'6327327#"&/&'!/?,9$Wi)-f31 #Iusy 2 %599% id11sW I0}<5y%'@ ##iY ??3+3?339310!3267!#5##"'##"&547!321 f,/EiN ♵1 K/6.PJXX@;/80T3@ oY #??+?39910)##"'53267!367! (VL160D OeT#!R;֑Ϳ=>3ߢ%ms{{j3"0(@# #iY *iY?+?399+3?10# $32>32!654&#"%"32654&]Bpn ZRB|+ysyyoD' bWhQ˦3\3,&PV9*Y;ƎZs+,@]Y  "]Y (]Y?+?+993+?10#"&54$32>32!654#"4#"32>TsOk`TяKwKKxCMOKDXyD!H1Nz}{f,@iYiYP??99//]++310!#!#"#&5463!232654+֜l4DS |Ï [F1%uh#+,@]Y %]Y]Y?+?+99??+102#"'#!>32&#"36"32>54&-≲] Hk$[>24_,GvICHG{FKsfTP`|XX53-@ iYiY`??99//]++9/10!!32!'32654+L52+5列BNJdǜ61su)H**@%( iY iY ?3+?399+99104&#"'>323267#"&54>7>!SK;uV~kWBoO-^XQ[G|[4P9@"29.̥wI.7E0EL"<\ʰy;/8Hs$,@ cYcY?3+?3+9933104#"'632327#"&5467>}\EKĸEme+fc֪kg,HI3Zaa5&/-$`V.!?#/0p+'M@1 "_Y "0""""""@"P"" ""( `Y ]Y?+3?+9/_^]q+910"&547#"&54632327";654&h umLdv"431'&86yk|w9<V#4c-+%)T $^oL",@cY@]Y cY?+?+?3+310%27#"'532?#"&547#?3!!PV&QF-;n sĄ12s#fx~2>TJf-@ iY?+3?9/10)#"#&5463!!GP 7UH1^}".@ cY]Y]Y?+?3+?39+310%27#"&547#?>32&#"!!Aao s)jNEH;B 2s#5~2>Li!;54&'!!# 547!!qjJQ;b5hs7;}NChbWD@ iY?3??+10)!>7>54&#"5632#d m58)6&Pd~9QKQ7&VWr"" #yQ!@  iY?+??933102!!.#"5>PsXwVwwu#)&&(dz|Py-NU$ ss!#@!]Y dY?+?+??910?67>32&#"!"'5326?!)+HpOS+# 8W8=Z9D0T6'8 3 :{T)y \i `e1^i@l  iY  iY6'I&%I"$I.III/I#I7IL I I iY?+9?9/_^]++_]+_]_]++q_q+++_q_q+++3+_^]3+910!!!3!!!7#I5)7T5+&2Rdf^g@A  ]Y lY #I "II I*I#I I ]Y?+9?9/++++++_^]+3+_^]3+910)7#7!7!7!3!!#+s3+͙+#=G@)iYlY  oY ?+3?9/_^]q+2+910#"3267#"$54$%7!!lˇbk$)53qrYd..Qݺ ^<@ ]YcY  ]Y ?+3?9/_^]+9+910#"327#".54%7!!?jsrp@+o36gxX/#r҅#+^$Z@ # @& HcY  Y  ]Y?+9?39/9_^]+9+3+_^]9104&+7!7!327#"&5%>+?P4=+|iQYxţw96)y^ ,&PP#-?(DoB@$ lY  I   iYiY?+9?+39/_^]+3+310 !!7!7!654#"'>323%uC5)++뮗BfSE+{ ǢCUnaȯ^A<iYѲ I@ IiY oY ?3+?+39/+++102!"&'32654&+#!!dz+]q5f;@ lY lY#??99//3+33+39/10%!5!5!5!3!!!!#<<==V5 s&''=jLuR@ !!!>(&+5+55 !&'']jL@ !!!!>(&+5+5Z!&G']LP@ 1 11)*>7&+5+55R&/-%5&/M%%&OM`5R&1- 5&1M %d&QM&$L}m&+5Z!&DL+&+5&,Lxm&+5%!&L&+5{&2Lm&&+5Z!&RL$&+5&8Lm&+5o!&XL!&+5)&8 LR!@ >))&+]555+555o&X L#@ " >+&+555+555^&8R!@##( >11&+]555+555o &X@%"%* >3&+555+555`&8 KR!@ >))&+]555+555o&X K?@!" >+&+555+555^&8R!@##( >11&+]555+555o &X@%"%* >3&+555+5553s)&$ L}R!@>$$&+]555+555Z&D L@'', >5&+555+555)&$ ON@>&+55+55Z&D N+@!y!' >)&+55+55o&M5R&+5Z&M^G&+5{m$@i"" I" #"lYM#;###  Y"III ^IYIO_ I I  iY oY ?+?3+9/_^]++]++]_q++++_^]39/qq3+_^]+310!!3## $!2.#"!2?#7373L)9sJW/NP)q.!& c(0ͱTs%3w@G?-_Y &hY$@ H!4!-]Y!bY??+?+9/+99_^]+99/_^]r3+_^]_]]31033##"&'5327#7!76?##"&5463232>54&#" ?h[xJ! zZ@CuNJ?F|IL^)V]L'D9PE_NroHXqPO{ms&*LR(&+5!&JL7&+55s&.LR&+5%&NL^{&+5{&2Q'ZTs&RQ{&2'MRQ'&+5ZT&R&MQ&+5s&L R$&+5S!&L%&+55 &'=V5 1&']jZ&G]{ms&*v-R&&+5!&Jv55&+55>@(iY I) I I I I oY ??+??39/++++++103267!#"&54?!!!!!]j/;%>52yy1hh﷧B;=%; @pYiY#???+3/+10!33>32"6$54& Z\Tk/l PraXW93XZ5s&1C}R&+5%m!&QC&+5s&$sR !&+55Z!&Ds 2&+55}&$R&+5Z+&DH/&+55s&(sR &+55ZB!&Hs{ 3&+555}&(jR&+5ZF+&HD0&+5s&,sR &+55!&sw &+55}&,R&+5%)+&'&+5{s&2s3R -&+55ZT!&Rss +&+55{}&2R*&+5ZT+&R1(&+55s&5sR (&+55%!&Us% !&+555}&5{R%&+5%+&U&+5s&8s=R &&+55o!&Xs (&+55}&8R#&+5o+&XP%&+5)s(KcYֲ I@ I %##]Y#]Y?+3?+39/_^]+++9102654&+732654&#"'>32#"'Ǫ}1jcBRGx㈵{Lqf|~DE%#4-[ #zP,45s&+LR&+5%&KLL{$&+55@ iY  #????+910!654#"!!3>32T/-5q{F9)tد?[ZEN*1@ iY %iY/+?39/_^]+9310! $5%&5467!3 7!"32654&c^u*- ~t32"32>542654#"ɖt׋\ B/N2 54&Ztֈ[ LX, FHV7S{;ABEuQTtABvOBežͣ}kR6>1IB~vLiX`fE@@32nύ[K>_bs^h CU,6F7Ds @  ]Y??3+98310!>54&#"'>32%D[Km:bjcDMΆCUDJ3'@` lY''' 'oY)"IZIRI$ I I I  iY iY?+?+9/_^]+++_]+++_q_q+_^]999/_]3+_^]3102)#7332654+3#32654+ꦙdwJ)yy)u n\a[k\tg *@iYiY?+?39/33+33103##"&54?#73!!26?!q5#<6p1pp{ )'HB:8Ri5fa@ iYiY@'I' I I I  @iY?+??39/_^]+++]+3+39+310)#7#!733#3#!3#7#fcFF58++66t7_wǮm9^^wAZVB!&,W@0)+hY +!+bY%$?!! ! !!$cY "??+?39/_^]_]_r999+99+910.54$733267#'7%4'67#Ńp~xyGWe7yH8Wu1Duh)<V,ƜD jr&0Vh1"~R=1:*R$@ iY  iY"?+?9/3+310"'53267#73!3#^]XLc{o717y4Ry \-@fY bY ]Y?+?9/3+3?+10"'5327#73!3#4632#"ZhF=5$t+`.a+}MX_WILX\#@PWY>:Pc{"0,@oY#  *iY#iY?+?+99??+10"53237!327#"&547#26765#"#Nq?F,&>3!r0A`3#}w| \k(( ;7mWWZs"0,@]Y  *]Y#]Y?+?+99??+10".54632373327#"&54767#72>54&#"XMV:9 R81Kp ")H=FuMKAD{II_gKYB0 H|44^LwP`tXX>@ iY@I IiY?3?+9/+++33910!#73!2!'32654&+ww6gBN_fJ11Ş71ruRRs+@ bY  eY?+?9?9/3+3102&#"3#!#73336J;%B-7P3)``(b s ^k9^?:@ H  lY   ??39339/33+33+10!7!3#!#73'!7#TzVi+ww+e48}/s^ /@ bY]Y?3?+?39/33+33103!3!3#!"'5326?#>7#/i2'#Gi)pZ9D0T6V2- JJ `e1N3]ibws'7@ `YcY$$cY?+3?+3?9/_^]+103>32327#"&5467>54&#" Nk H rD9`^ZR1"lPd:be17rmZsD%hs '@   ]Y]Y?+?+99??102#"'##33>"32>54&-R: BL|JL?CzKCsؾУ^^aI[uXX%h#)-@]Y ]Y $]Y?+??+?+9910"'##>32&#"3>32"32>54bR:$[>24_,NL>JxNJACzKðfTbHؾuP`us@ ]Y ]Y?+3?3+102#"'732>54&#"5>ٖ򬷐\5hBUL[Q=z\HsG#~`a#/%+s *4@!aY ]Y %cY ?+2?+39/9+10">32#"''67&54$32."32654&v A^|iy_7K@+\6hi}z*Rb5554&#"Rw5L- R81Kp ")O@FuMKAD{IIeJZnUf0 H|44bHwP`tXXZ#).@]Y $]Y]Y?+?+99?+?10"&546323>32&#"#7#72>54&#"Rw5[z]>22eG!FvOKAD{IeJZtwHWNuP`t3s #R@3bY?   cY  hY ?3+_^]?+_^]9/]_]_]r+10";754&"&'53267#"$546328Gsz:[xLT[u4F=AOk}R&+.$xu3s3s&/@P.. H.'*  bY*bY0***1*A*****'' 'cY$    hY?+_^]3?+_^]9/]_]_]]r3+392/+39+31027327#"&'#"&54>%54&#"5>267V,.;>Nx~aw5e[Rgf4TCsƱ7h^P-Tq~gjBZh$22$R^EX'/ssjs.m@  .-.-bYbY.I. I.I.@I . .." &("(]Y%" cY ?3+?3+999/_^]++++3/++999102654&#"'63 7327#"&'!"&'53254+7tFM}H"K}+/8AMvPxBp_H-;D3/OV!i^P-TOSI7&%XuZs'@U '& & '&bY''''''#I'""I' I-'',I'%I'I'M'')I' ''cYcY?+?+9/_^]+_]_]+++q+++_qq_q+_^]910!2#"&2654&#"32654&+7Z]0{-slL?hQed_pc`/-E{} &@A19}ތhqMD7:{^$@ bY  ]Y?+?9/3+310"'5327#73!3#ZhF=5$t+`.a+}M#@#'55@"]Y /]Y(]Y]Y?2+?+?+99?+10"&'32767##"&546323>32&#"2>54&#"h[3 ,HSXy;\z[A50g4XFwIJ?G}GL)V3^LֿhCaru+ˉzP`yXXs,/@&]Y]Y ]Y??3+?+?+99103#"&'32>7##"&5463232>54&#"4h[h8HSZ@DtNJ?G}GL^)V_a^LֿbE_}P`yXXZmsA@&bYI   ]Y ]Y?+?3+9/_^]_]+_]+10!#"&54$32.#"32?#`w4ˠ_<|Pjcjm1L'yXLP&qԊcn5^!@  aY ?3?+99104>7!367!#"&%2654'5,Ra X%DB/q *56GBr:o}unc4jRLZH5 !Rs(2>@"..+.. .# #]Y#)aY?+?3+39_^]]9931067>32&#"#"&54>7'.#"56322654'VAK{JC4.LG2q"?Kh#@( (RENm]?b&0#kRTF /Pv}m32&#"3>32Ӊl\+b$[>24,? 2>dD3{1ð00gM]Ik%m#,+@ $$ ]Y$]Y]Y?+??+?+910"'53267654#"!>32&#"3>32#hF=5=]l\+b$[>24,? 2>d)QYD3{1ð00gM]Ik F@.fYbY"H I*I#I I ??9/+++++q3+3?+103#!#73!4632#"ߜ+dd+^-_WILX\)WY>:Pc`^^ @  ]Y ]Y?+3?+310'7!!?!!uVVtVV]@>jIY(8  e F V ' 7    ??9/3/3]]]]]]2]]]]]2/10"#>32!3273#"'!&H u-D& qӗk!m#R0@  _Y@_Y??9/3+3+9103#!#"&54632!";654&!'wu7>t-N2&1 &Vn_r%"eH3 "&]Y?+?10327#"&547!1R81!q-_- HJGl4%8@aY dY ]Y?+9?3+?9/+3?10!#"'32654&+7!!!=V)ÚդLKz-J-^?ќP +3jtj^j^('@%"]Y?3+3?3393?10%#"&'##"&547!3267!3267!!>7+m Յ-b\/`-c]+c-/4:vm㦛Ik}sD3{sF1{%s//@+-- *]Y%%]Y?2+3?+?393?1023>32#"'5327654#"!654#"!336L+Fh)ʣhF=6$b^(`Ӊb\+b sptLhD3{/D3{1^#ms $@]Y ]Y ??+??+910)654#"#"'5326733632Ӊl\+}'WK.80E  ΊD3{@;䧛Ik%ms#$@]Y]Y?+???+910327#"&547654#"!33632R71Ksl\+b Ί HFOD3{1^䧛Ik#4%9^ @  ?3?39910!!!79%]V"_^wG^ KZTs|Zs#k@C? Y .I %I III IcY  ]Y  ]YcY?+?+?+99?+9/+++++++_^]10)7#"&54$327!!!!!4#"32>NX [  2%+/C1HKwKLxB$NN9z}{Fs,+@&&]Y""]Y?3+3?+9/910"&'##"&54$32'2654&#"326?! sDp~c{ǰ{NZ)3 0emqaͶa횚sG65Z;^@  eY?+3??10"'3267!#7#?"B);v#j-  #@  eY?+3??10"'3267!#7#?"B);v#+  #;^@]Y eY?+3??+10"'3267!327#"&54>7#?"B);v#j- R71Ko,$ #B4H|=ss@   eY?+??9102&#"!336J;%B-7t%V s  Js"@ ]YeY?+?+?9102&#"327#"&547336J;%B-7t%zR81!q- s BHLYLw @  dY??3+103>32&#",̸h~BA8#%3Hw dY ??+10654&#"5>32H569HUa409 ,KG%{^Q  bY@III@ I   cY  ?3?+9/_^]+++_]++9102654&+#!!2!u_xLN;fXχmZK3B`^49%^O@ bY3II@I- I cY?+?39/_^]+++_]++910%2654+!#!!3#Yz;HT`-ZZHy)J^`s0F@( ++ &+(#(cY%# cYcY??+?+?3+9_^]]3310#"'327#"&54732654&'.54632&#"=_6J04H`} _PfJ^y`ɢcv9F@X{nqIu.0ZA8+D4D\_T3+';-?#5#@ ]Y ]Y?+?+102&#"#"'53267>]>22e'WK.80E %#w˹@;F#5#-@cY]Y ]Y?+?+9/3+310#73>32&#"3##"'532671h%]>22ed1'WK.80E 豩w)@;w @dY]Y?+3?+310327#"&547654#"5>32JR71KsP*6LP3 HE\i DA;5##@@'/O_Y  `Y ]Y?+?+?_^]3+_^]3103>32&#"3##"&546326?#"%%]>22e"1vo&: /7P~:f1RD Ue0f^"@ H??3933+10)!!DX69aXD^.@]Y]YcY?+?+9?+910327#"&5467!7!7!!I14H`}%#?s3+PIu3SNN^!C@%/ aY  ]Y !  ]Y?3+39?+99/+_^]10!'7!7!7!3>32#72654&#"=0?#?s3+gyvvGcrH\\,U3^TEmSp6f2-KE-^H-^ *Q@,! !aY  aY  &Y ]Y ?+9?3+99//_^]99+3+_^]1072654&+7!7!&'#"&546"327.n]#s-1V)̥7$Ē;FV6}s[hFblZfڝRo>T53`3BS/1@9+  ]Y??+39_^]_]]310!>54&#"'632/Ӡ_IdLD}IY`}b@  ]Y?2+?93102&#"!.54>lkyhӅ8E`jl5oQڑvy@  ]Y?2+?9310"&'732654&'!`t_ze-ƎZlLEWlKZZs1qs&@U $  $bY$$$$$'#I$""I$ I-$$,I$%I$I$M$$)I$ $$cYcY?+?+9/_^]+_]_]+++q+++_qq_q+_^]910!"&546?.54$32"3254&#";q퍄Zc suQ?odZe/-q pNVPA8=mv?32&#".#"32?#`w4E@*K=07a _<|Pjcjm1L'yXL Vs&qԊcn%^$>@'fY `Y  /O_Y?]3+_^]3??+?+1046;!3##"&4632#"2?#").",ĝv_WILX\IV /:Pci=:0"h^@ ??399?103! !7!!6?#PyI-Ӡ $ f\H\Ao+%7^]Y?+?103!!%-1^Z#-.@]Y ']Y ]Y?+?+99?+?10".54632367!2&#"!>7#72>54&#"XMSy6E.[A50g/4:H=FuMKAD{II_gJZrXuZ^LwP`tXX//D@& bY 9+  ]Y ??+39_^]_]]39/3+3103#!#73>54&#"'632/+;=+:_IdL D}IY`}bD@&bY  9 +    ]Y?2+?9_^]_]]39/3+3102&#"3#!#737.54>lkyh9+<>+8E`jl5ƊQڑvZ(+C@$*]Y))]Y #]Y]Y?+?+99??+9?+910"&546323767!!!!7#72>54&#"Rw5L-^+2G!FvOKAD{Iߏ)eJZnUfJSWNuP`tZZ1>V@.-)(aY((!dY++hY/ 9]Y2]Y?+?+99??+9?3+9/+3?10"&546323767!!#"'32654&+7!#7#72>54&#"Rw5L-^V)ÚդLK|.G!FvOKAD{IeJZnUfJ?ќP +3iuWNuP`tZN#)69Cd@8@@/@ @aY8%%]Y %77]Y' 1]YC**]Y"?+3?+99??+9?3+99/+_^]10"&546323767!!3>32+'7!7#72>54&#"2654&#"Rw5L-^+gyvvGcr=0?G!FvOKAD{Iߏ-\\,U3eJZnUfJSp6f^TEmWNuP`tVt2-KE^L1H@')## #1cY  ,,cY 1cY?+?3+3?3+99_^]]3310#! 547#?3!632&#"2654&'.547#3 sĄ1OEȢbv;D:]}mcT@htesXq2>T_T4*%:0@/0)?;@`47J^%.5@"]Y]Y **cY @ &]Y?+?3+3?+?+10%#"&547#?3!7!2&#"#"'5326727!k sĄ1/I'[A5/e'WK160D y]ns1~2>TgZwϹ@;\#wJ^L4>e@9(0-0cY75!5aYI)  +@*-!]Y!9cY]Y ?3+?+?+?39/_^]_]+99+99+310%27&54$32.#">32# '#"&547#?3!!%"32654&{\6gBMVaH{iyiiU sĄ12s]*Rb4N(2[H"pqA0rhYCZ~2>TJnZA6#.%<C@$144 ]Y4 /cY"/&+]Y(&]Y??3+3?3+?3+3?+910"'5327654#"!!!"'53267#?>32&#"!3632hF=6$m]+bӽMhF=6=\̣)ðhPE@9F Ќ)F1{1yPZőTT1PA>䨚Lh%}&A@#%$  cY"cY?3+?3+399_^]]33??1032654&'.54632&#"#"&'!!PfFayaɢbv;D:]zpޟaJ-5jA8+A7B[_T4*%:0>`+1H%= (@ ]Y ]Y?+9?+9?10!!!! =+1J--^SaV 7@   ?33?399//39933399310 #333  #333<<߼'%P'%P%J@/ cYP@_O  cY?3?+99//_^]q]q+310!#!#!#!#}l!!w'$@  ]Y  dY ?+???+9104&#"5>323267!!>7##"&54?6((+6LP35l\,b-059- 09 @ED3{򴪦[nw2+@(++]Y+"]Y" dY ?+??+?+9104&#"5>323267!327#"&54##"&54?6((+6LP35l\,b- P71KmZBb- 09 @ED3{@(&H{r SW[n@  W  T?3??3910#654#"#33632Zf PDo"J+,ekl4$\_n8P $@ Wi8HX T?3?3]]?3910#654#"#>32&#"3632Zf PDo"JwL("+E)ekl4$\G2n8PX @Yo V?2?]2]107"'5326734632#")O4,*.E:@HAqBEs=B`zABZ733673#47,,% 8:r ~qjF_#nTCs^@ T V?2?3?31033>73#"'5326?+ $C,(0F^#{N&g?C RA%jRj sZ jq8/1032654&'7#"'y*:* `iu'00&2z]mjq8/10#".54632{*;* ]lt00&2x^n!@ H  ?3393+/107654&#"'632-0*0R,51&.>ow"f!@ H ?3393+/107&54632.#"ZrL(K3;JZR-hF@C89@@[/10%5% $A@Hx@[/10-5B$xHVH/10 #3JwGVVVH/103#/wGHV)!v!CVJo)jdM^C"?5T^vq"?5J //10 !wJ-J/10 vJ"PF7eFuV<"?33103!737+T+b{{Vf"?23107#7!#++V{{-P A@+   )`/]q3/]33]]]q22/]1073733##7u{{P7@ `/]q3107!u{{L'3  /3210%327#"&'-,.;>Nx~a^Nh^P-Tq~3 ?/1077''7 i`Fak^CX{{Xy{VyyV{@  V?3?3991074?33673#"&72654'm;A7?#|xUe',2zTP`L^ZJj~d%M@=>;w%1\ T??10#3f}y#"#@   W U?33?33993310#"&'532654&'.54632&#"ٷQv5wy=L6I\FvJe[&9*G[SC1)"0)4cFE?"#*&/m) @  TV?3?3991037!# !V ^5\^@  T?22?93102'&#"#.546wMVg(UMObplyedc&60dPO)>l@ /3/99310!#!5!o @  //9/3993310!#!5!3oR$@ //9/]3993310!#!5!3o^ @  //9/3993310!#!5!3oV@ //399310)5!3@ //3993103!!$@ //9/]39933103!!#odh8L 0>+]5-dwj R?39/310#3RdŚv%9P?29/3103# bŚu=/ @?2/]393310%7% //!1dۓ}/ @/]2?39331077-)!f/dܔ}bC Cfj?Sf?GSf@dXR) "?5s!Z ?55s@ `/22/]10!##Tmsms@ `/33/]10#5#5msm#w; /22/1033#mTm#w; /33/10!5353wmmVN@ @0"?]210!3!53NnsuVRVN0"?]10!3!NnsVRB 4@ 0@0@0 ?22/]3]22/]q310 5!!Rdy{m!K6EM{/210!!<+I+N\OKjRcPm!Sn!L-F) ?10#FLL)d7)?3210 #!#!LI%KK)dd` O@*   @ H @/' H /23+]q9/]/+]9/10# 33267%4632#"&`"͘ǓYqLA2CID6?+R]MGJ13BR45+1@  Z j 9 I  /]qr22]]]]10!2#4&#"7RIQNcR~&A<6G*t"  "t"_[C"?52__vO"?5BB<@" ) `pH/+]2/]]2]q3/10373#7#Tᾅ{B%>@# ) `pH/+]3/]]3]q3/10##33 TDžqbj 0/10!#!uX>jg; ?10!7>/ |D'`a_ sj@ ?39/]310#".54632{*;* `iu..%/v[khV/"?33103!7379+T+b{{VZ"?23107#7!#-+V{{-F A@+   )`/]q3/]33]]]q22/]1073733##7{{tM H>++5j   ?2/]10"'532673TWA+819 78':<')j ?2/]10"&5463327/x;3P,4G)n* Fr~jtz~>+55mb <@Xh) @H Wg&?3qqq9/+3qqq10#"&546324&#"326bonpo5,+5`,5g~hg~e*00*Z0j;9}RzLpQK H>++5B@ 0 `p/]310!#7!#J171b%@  0    /]3223/910#"&'#"5473326733267Ir:KNEao[3L`\3M}Ek=$#G( TBEVBEiLz H>++5gKi H >++5GNin@ H>++5Gn@ H>++5YRv@ H>++5eM He>++5DH/310!7!'j:_R$N:/210!!;+K+hP/10 djoB /?10 #BAu@ ?39/]31032654&'7#"'*;* bfv..%/uZlB@ @0 `p/]210!3!73)J//B\`-P/39/310!!37#hX)-!@0 /]3229210>32632#654&#"#654&#"ln%BpTa n"83Lc#83NnHHTT% !5BE"5BE3 ?/1077''7i`Fak`DX{{Xy{VyyV{0@0/104>3"#72654.TPR;N++55RP?]210!#7#{P3{3)-3@wx  /]99//3]]3]]10"''7&#"#3273267337LJ\J",/s9:AA\C% (3u"w/}8x:9%s7q/CL{F".=@%   /&&/3/]99//3]3]2/310".#"#>32326734632#"&4632#"&N*MFA+2r~Y.RG<+4uE~LA2CID6?oMA1CID6?#+#:9#+#5>wGJ13BR4GK23BQ3;N/@Xx$)))w))))$$- wx  @H -- /q2/+99//3]]3]]99//3]]3]]10".#"#>3232673".#"#>3232673V+E<6'.s~Z+G=5)0u +F<6#-s~Z+G=5(1t !(0p{!(0p{! 8q{!)0p|?  /329910%377#'3?9/33107#7#5yy3% !'/7AIS]gqy@w  &$622?2O2_2?O[ooVjv~~rzB88F< QeeL` .*0*@**$2jz<`**`3 #4#"4632#"&"͘9YpJLA2CID6?]MBHJ13CQ37H    /]/1077''7VhdaegVid`dVfd`diVgead)  ?2210.'5673x/x/qKI3gh E7/gR  ?3210#567&'5dt)iSRjiiH34f!) @  ?33/329/310>73#&'##567&'5s"UXh.9-ig2X)iS3BDM-@+5@ ?39/9310"#>3232673#".+Gs/ٔ]ZHp,ݖwL;6 86#)#H %@IY8@P /3]2]]]10 #&!"#/k3omHefj+/9910!5 5! Ծы )@ X Z [Y?2?399??310"&54>32373#7#'2654&#"/\iWXz2#T+Ci.(Ae)xxhZC\h-9{n))"@  [ 0 Y?3?9/310"32654"&54>32+3267:a bjbsn~־A<.\E@rweG>2Om\@GB) [Y?22?3310"&54>32&#"3267F~[mje;M?Qij0Q,`)}z-$|x1)"@X [Y?2?]399?10"&54>3234>73#7#72>54&#"7\iVY1J$ )̒ &^ +H2-)Bc)ywނ,<`;/\08NH3;zn5@   [ X?3?3]910#654#"#33632/V F:]=Ͻ##Tp[Y5,M~ѪEjn]885F& @!Z## X[?223?3393?1023632#654#"#654#"#33>YWY WV =:^>V =9_= 5io\Jb,M%%MNAF5{ Z X[?2?9?102&#"#336=!))GnD [ no)FN5 Z X?3?310673#3/XS:uA5 @  XZ?3?39910 373#'#GٚNVp]8?5\=  /]9/10!5>73RC.A;J@sCZs$@V#i#X#M#)#9## # #,'I#&I4!IIIkI  ]Y ]Y?+3?3+9/_^]+]]+++++q3^]]]]]]q10"&54$32.#"32674632#"ו\6hBUM[QLE_WILX\[H"߀`a/#O)WY>:Pcs%@V$i$X$M$)$9$$ $ $,'I#&I4!IIIkI  ]Y ]Y?+3?3+9/_^]+]]+++++q3^]]]]]]q102#"'732>54&#"5>4632#"ٖ򬷐\5hBUL[Q=z\H_WILX\sG#~`a#/%+WY>:Pc1sb(4s@ "cY p@2 I /6 /aY// ))/) )aY ]Y ?+?+_^]9/+_^]99/_^]+]+910>32#"&54$32#"2654&#""32654&%W$IZǔ~ιOq^H c9~*^1IS:HN\б5m߳QeG8syE79,"'m'Q%T ?55HV&Qj#R %&+55Z+^,.@#cY+ cY 'bY'?2+?+99??+10#654&#"'632!327#"'5326?&547)3+'++Hj$J y6&P'VA)929 !OϹDACZLAy:<=Ӕ0@iY' I/ I7 IiY??+9/+++3+10!&54$32%"32654&79ĹX꓏ӹ6Cr}ZTs@ ]Y ]Y?3+??+10!.54$324#"32>TfktяKwKKxC&/˖Mz}{4@ nY I*   iY ??+39/_^]_]++10"!3!.546$32&}[o:F^o54&#"'632!327#"&5467L#%(.-^q#"L >.6h-*w"H%yk1fB(A1 sd:k>3#%4@ "! !cYcY?+?+99999910327#"&5477654#"'632%?-,13NlH9)V39..-SsLZ"#*}kY%PY22#xiZH}b0OXj@? jYjY  @  W g 8     iY??3+9_^]_]]]]]3^]]+333+310!67'%754''%&#"'63 z5E7@sj7_25PkX@TZX@3 `Y`YP` @ [ k      gY??+9_^]]]3_]+333+31067'%&''%&% {q_sa{Qqe'3TF/*@+,iY+# $  iY ?3+3?3399?+10%##"'##"&547!3267!3267!#!!27 HhM Sq3u%1 s02'X76[abn>;BE.\B5<7^l^.*@()cY("]Y?3+3?3399?+10%#"&'##"&547!3267!3267!)7! 767+m Յ-b\/`-c]+c-9A1=#vm㦛Ik}sD3{sF1{>qu4@iY iY@ I  ??9/++9?+310&#"3 !!67##"&54632R@7IvEiB/e! αʐsh`Y/J'hǹ+1Zs#@]Y ]Y ???+9?+10&#"32!!67##"&54632HmKa_-T:L_?Hq kE]Oۺr5"@iY iY #?3+??9/+10632#"'32654&#"!oo\sƭzmeP=8%i13V% ^"@]Y dY ?3+??9/+10632#"'32>54&#"!?Z?M›Rfw_LWU,*^^V+6sρ;^%`,>@%O_ **(#iY#iY?+?99+999_^]104&#"&54>323267!"&5467>-M:Uayᖹae9^XX;?E'7\ɧI-DYs+:@! H))) )!cY!cY?+?39+9_^]9+104&#"&54632327#"&547>QATj\ŀj8iUEzZ5IZ~c6(;GLѨ{ĕO7OeDY[ZVٳ [4D]6@iY oY?3+3?9993+9910.#"'632>32&#"!7 !=),6/`^Xt(FS\PN$.'LC'H#\?1WM)P`ZV)=[^,@cY cY?3+3?99+9910.#"'632>32&#"!7 !9"+1'B`Pj%FzSY:C&,KC"%j;4@?H7!X{&*@!iY iY iY ?+?+39/9+10 3632# 54$3 .2654&#"{ۈv|, hWҍ͊lˇk'-%kqkZs&7!Y!@ I!! ]Y ]Y?+?3+9/++910"$54$32&#"32654&#"7>32?$Z\jYjgZsHE)YI194J+^#%@cY" cY ??+99??+10#654&#"'632!327#"&547)3+'++Hj&J y-+?[OϹDAC}_LAys!/V@   @( H   H   )]Y"]Y?+?+9?99_^]+_]+33]102#"'#!654&'.546"32>54&vX_'8fp{7{9RORs©X!'=Dg_A28+:vwifZsFM{{Zss5h{7&5@  ?3?39/33310#!!3!!6#15ic HFJxY1^ $@  ???9/399310)#!!!F۸զXV^'JLdhs%H@,]Y bY@ 'H@H ]Y_?]+?9/++3+3?+910#"'!!!7#73>32"32>54&h_N)s)s.֝NWj"71dFsMB|uuoXpXb{7%o@J#}Y 4#I*I4I"I5I, * I" I iY iY ?+3?+39/++_^]_]+++]+q+qr+10"327#"$32.4632#"&Ӏ\}ovjkaDRnYIRˢĴMM+-<;&ZlLEWlK%o@J#}Y 4#I*I4I"I5I, * I" I iY iY ?+3?+39/++_^]_]+++]+q+qr+10%2654&#"63 #"&'74632#"&~jwF/kaDRnYIR;NND/9(8ZlLEWlKTR5@"V7GZ9I(o /]2]]]]9/3]]10#".#"#>323)3HyhY&+4>vvw@"("4;&-&{4ZsT:}^Z^^#@ aY ?3?39/+_^]10!#!#=m?l ^G121^O@2aY aY I I %I "I I    aYaY?+??+399//+++++++10)!#!!!!!#7=)3f+>a<^13s+9Bs@F>!bY0>>>>> >>:cY 3]Y ,]Y *&& &hY?+_^]3??+?+9??+9/_^]_]_]r+102733>32#"&'##7#"&54$!374&#"56"32>54&267#"Dm/ IZ]2 9-YM63phBJaH}JBFDxFAX-s7<^ZL׿KXZ3;'ae/'V~P`}T\FuaS^^$N@/aY #II"H I$aYaY?+?+9/++_^]++33+331023##!#7332654&+732654+bO'+vg'\%VdAD%qW_q^}{wK6GcZO}w!-^ v@OaY''I"&I/!2I,III7I1I%I"I I aY aY?+?+9/+++++++++_^]_]]++3+310)#73!2254&+3#_k+ib'pcV9)5;>}w9^ ?@( aYII%I"I IaY aY?+?+9/++++++10)!!!!!d)3f);^do%P@0#aYaYkII"H   # aY ?3+?39/_^]+++q+9+910#"'532654&+73 54&#"'>32dj}r{r~h%`FHRprj }d=J\ZIB6A\G4J?^@  fYp??]+10!!#"&54632-`VIM]W^WY>;T^^  aY/+?10"'532673VAJ=>J\(Yc!9R^ @  ?3?39910!##37!j^Vrt8A3b^u^ T@1          aY?+?99_^]]]]3399]]]]33103'737!9N+NqTkP?'hXq99f^@   ?3?3?33310 !#67####!?qCP))o--JLf^9^@  ?3?39910)##!37>3q  zT^=@<V+o @ aY aY?+?+10#"&54$32%"32>54&+ל\ZWd[ZUaVsskzqo @aY aY ?+3?+310%2>54&#"5632#"&'7%f]fqhՙfIX6o~9<%(+\%^ 3@ gY gY?9/+9/+993310254.#""$54632)w퀸 PTHxMsэ 9\H^%@   gY?9/+99//3104632'>54.#"#&\ZG#߀``/#Pg?ٖ򬷐\6gBVMZRKE^$%'@"! gY!gY& '%?9/9/++9910%"$547'7632'4.'6%3'5mkM7Xohj5mޒPwvC@% wjmwH^FTAOzH$LzJf 13s!.7j@?3bY033333 33/cY "]Y )]Y cY?+_^]3?+?+99?+9/_^]_]_]r+102632#"'#"&54$!374&#"5>2>54&#"267#"`t0޻܊gM63g`WegFqEIHmJxX-sKG)[i&01%ny[a`euaS^B^)*@ aY$aY?+?39/+9310!"&5%&54?332?3"32654&KGl #X^0iersk-yRẦk@.*?M}j}oM[v|IYbTs@  ]Y?+?9/310!654#"!32T _#5KFFKſEL^&@aY  aY ?+?39/+910#";3!"&54673B>FN7h[~s?@y(Rj^@ aY?+3?10!#!7!!,)w^@  ]Y?+?310"&547!3267!+- _q-=ǷJL^>-j7\-@gY  gY ?3+339/3+31073254&'%'#"'7D2{1_wlIk-l[,b-NdDN\"-B@ #))) gY / gY ?3+339/3+399//3310%3254&'%'#"'.54632"&54632F1{1^ykDMP84HXoMP84HX-l[,b-PcMHNV'^/1V%^@  ?3?3399310#33?33>3!476#E5gx^-C{)o]-4U}^ $@aYaY?+9?+910)7!7!!Li)f^0@aY_Y aY?3+?9/+3+910#"'532654&+7!7!們v0=ubbs!+=/__LH5o),@ !aY aY ?3+?3+9933104&#"'>323267#"&54>7>V>95iIBiQBmO'GB={mE{6\E'P-/ -Zc7,2;'6;-E^d.$+8o""@ aY?3+3?93310327#"&'#"&'73267&54>32R.M%C@:`mD{\-U'24*ceqzLYE"]x}X?gtp}/}^aY?+?10!#!!`'^^ ?2?310#!#&5}3g+:^`9Z/T^@ aY?+?310!#!#!h7b^9^)^#@aY ??339/33+310###"&5473;332673DH MR SU Ձ pqTZRFF7!#/=xqt93&&?UTm@|[czL #@?  8  IN?3?3]9/]3]10'!#!.'=}9c}}7R@1 F  ?   #I IH   INN?3??99//]++]_q_]3_q3]3310!7!#!!!!!%3#3Ŗ#'/X%7qXF߰?<@&II &I"I IMNI?2?39/_]+++q++3910! #!32654+ 32654&#CbwhRVsb}X\fT7`iNX`oNg:CdsPD4A#D@* II &I"I IM#IN?3?39/_]+++q++33333103! 3##!#!32654&#'32654+LJVCcL%|&s^f7`iTRT}X\g\E5FPD8=:CdIN?3?310)!22654&+;{ofqj 3@G  #I IHI N?3?39/]++]_q3q10!!!!!!O&/X%7p߰ 3@G #I IH NI?3?39/]++]_q3q10!!7!7!7!!r8$Y/'Rf߰w;@$IIl{ JtO?3_]]?33]]9/_]++310!#"&54$32&#"32?#oʘPsjn^_n9>-7л+C=xyqw /@+I%I_L IN?3?39/_]]++3]10#!#3!3^y^TTJsd @ I N?33?3310!?'7!;)po{97{{7n9 @ v NI??2_]]10"'53273?C>E.###+ @  IN?3?39910##37!YKkmd1Zb IN?2?103!߲'@   IN N??3?399310 !#6?####!}5g!u+.d)-lFZ)#@ IN?3?39910###33>3Fi' rJ/@ NN II??3??3103 3!#67#!hFai2n !@ {  JtO?3_]]?3]]10#"&54632%"32>54&ˆsSP\TTMW?ε.ށfi{܈^q'=@$ { I#t##O?3_]]?39/_]3_]]9310!"&5467&54673326?3"3254&˅ԅyghk*8{hIƿx%>w$PNh\mqѣkdOS '@w  xIN??3]9/]3]10+#3232654&+8p`9L:Eqj-fHB%#PJ'X@K  @ I k  ֲ I ٲ I @ I O$L?3?39/+++_]]+933]9?103>323267#"&5467>54&#" /uVZj i6n;1pPj[1%Bah5Ar^3L!Eri HB#%OK@N K LO?2?3]99??10"&54>32373#7#'2>54&#"cv]e8' _-/[54/Imsdes`c2?~y5+6?r@L)';!!!!!!!I!I-!!, I !!!7ON 1O,,,K''' *L?222]?3_]]?39_]??39/]+]++]q39102733>32#"&'##7#"&546;754&#"56"32654&267#"-Kd 8ce@&(ta DG=<ч{ՃB9jUB;B @ LFNO?2]???39910"'##33>32"32654N7'+9V5et`)3S63-Imqc5t[H/yYS8CՃy!#@NF LO?2?3]99??10"&54>3234>3#7#72>54&#"etab9R% : *j 1R74.Gp22? 2He3@XP8CԆ<=!D@$I I~ H{L  O?3]3?3_]+]9/]++3]10"32654"&54>32+3267=lkxm{#JB2\Uz5nPC:Aqod?J&;!M@2,I$II/ I+ I' I O L?33]?39/+++_]++_]+3910267#"2#"&546;754&#"56;jg}mŀy#GB9\Q~=kTC<@od?J%<q'i@F && In&&I&I&3 I&. I&) I&& o   L  O?3_]3?33]]9/+++++_q+_q3_]9910#"3267#"&54675.54632.#";gQa>;Rt0pot8;p@3b>3232>54&#"X$Lv=5 @W  `pNK??]2]103##"54632bӦѱC32d7T0iQe޺c~]S󢉔@  OL?2_]2?33]102#"'732654&#"56gx{f?%F/Yv>95Z0j͔1BC75  L?3210#654#"#>32dB`֩41=(hܤ5@?   O?3]q2102673#"&5473%?\Ӧ R{hԢ02/3y^ @ KM OL?2?399??102#"&'##336"32654gt`d;R# 3 b'3S63-Im˖4?tY^tYU8AՃ{b!@ KO?3?3]3]31027#"&547#?33#0@JnibPh]##NP%X\;wf93@ O N K?2??3910332673#7##"&547La LBhCѤhdc<2"TAxgDP%@ N K?3333?9/3331053254&'%5'#"'.$V uj9D/` KBgFХf_i%"@"N$ KN?333?3393?10"'##"&54733267332673#7#Tfab\_ F@iD^ DBiCѤfxg4I<2"TC<.&TA K N?3?3107673#3\^(Uk3!@ LO?333?93310327#"&'#"'73267.54632?2.0+NDe0LuKH'!-/(:%0(nb'"32654+732654&אtUfYh3[3#\a:{^B3|B''@ "O  G?33]?3939310&54632&#"#"&5462654'!kYgi5Z;B^ @ OLLM??3?3?33310.5467>32>54#"GwQ[hL|Dgk|A@^PlwQch Č{{h @ MKL?2??3399331023327#"&/#'.#"'6UXt2%*=JXX$%@an?/" clpg5, ` @ oRP??]210#3'4632#"ӦѰC32"32>54ȢsB0ϛ ^l-j7L[i&0.(saS_h)6@\TdtE&6 [k{J)9 / /##*]Y#1]Y?+???+9999//_^]^]3^]]]]3/3/3]]]]10"'##&#"#>327!3273#"'3>32"32>54bR:H v-#D& r$K|NDHvLJACzKjE mNV?˶~|qP`uZ,9@Z(TdtE&6$ [k{J)9 $$/$ /$$* 4]Y-]Y?+?+99?99//_^]^]3^]]]]3/33]]]]?10"&54632376?&#"#>327!3273#"'#7#72>54&#"Sx3H "s-#E& rG!FvOKAF{GVKYLVjE mXWNzrM\z%4@S'$'cYt***e*6*F*V*'**00 0@,,[ k { J  ) 9    $ ]Y]Y?+?3+?39/3_^]_]]]3/^]2/2]]]]+310"'53267&#"#>327#?>32&#"3#3273#"'-hF=6=\tH v-)ðhPE@9F 1?E& rmMPZ)jёTT1PA>lws,6>l@"'"bY/-87'  bY%@&H%%%'''!(++!4<<?223?3393?9/3/]9/]++9+91023>32>73!&'!.'!#73367654#" 654#"L+Fh3H N>DKX LSU(dhaDZ gbQ)b@ssptLhGU?% )j9 #D3{74i1{rJs *d@9)]Y bY !$bY ?3?99//]3/]+992/_^]+99?+910>73!/!#733632>54#"B(1+tHNbh&0CZ Ί NQl :8s--35>1䧛#U203{hs*7w@L& TdtE&6[k{J)9 #### (2]Y+]Y?+?+99?99//3/3_^]_]]]3]]]]3/?102#"&'#3273#"'!7&#"#>32336"32>54-Sx3E&s)H v :JyMJACzKsJYGv mgjwP`uRs$m@G t e 6 F V '   @[k{J)9  #eY?+?9/93/3_^]_]]]^]2]]]]2/?102&#"3273#"'!&#"#>32336J;%B-7r&D& rDXH#uj s mmFLw#l@Fte6FV'  @[k{J)9  !dY ??3+9/3/3_^]_]]]^]2]]]]2/1032673#"'!&#"#>327>32&#"$3 rCXH#u,̸h~BA8# 49mu%s.R@- # #  cY(.[ T.. .. cY?3+?399//_^]]]]33+993310#"&'532654.#"#67&54632&#"2673=kEPf>1W*4ɢcv9F@X[4);4q#ZA8*=Vl:0_T3+';-/339BuoL,x@McYt"""e"6"F"V"'""' '$$[ k { J  ) 9    @]Y?+3?39/3_^]_]]]3/^]2/2]]]]+310%27#"&54?&#"#>327#?3!!3273#"'Aao !H#u'Ą12> D&F#5~2>mTmTJ^ @]Yp@XH  @H  te6FV'o @[k{J)9  ]Y?+9?9/3_^]_]]]^]2]]]]99//+]+]+910)7'#"#!27!7!32673#"'!# 7ABG=s3+=:,C>^-A3#3>?s-:@! ]Y']Y 0 ]Y?3+?_^]+9/99+10#6$32&#"3>32#"&'#"32>54&2f`iLQ]<eHvIK?F{IK)y+V`gsO`JٽI[uP`xXX; M@1.I.II I< KN?3?39/_]_]++_]++_q_q3]10#!#3!3BB<6;3w^"8@ ]Y  bY cY?+?9/+93+9910%!"&54>7'7%!7!%"32>54&c 3-'d|k+yT^MwBUTں peRe#O_X]S[f8Z@0!%6,%1]Y% 7 78cY@8]Y?3+?3+399?399?+??910%27#"'#7&547#?3!!!733>32!654#"!AaoO4! sĄ12mh-Z;>dӉl\,bӣ#5T\\}M]NfD3{1 {^ ,@bY*I#I I ??9/+++3+3103#!#73!ߜ+dd+^-)^3@ bY *I #I  I ]Y?+?9/+++3+310%27#"&5467#73!3#B_o+`-^+#5}=@ǝHs$,f@>  aY(II I*I#I    ]Y %]Y?+?+99??9/_^]+++++33+_^]331023##"&'#!#73336267!"!754-g+\9Sx3 H͙+^ KAy%JHs&bs=JYls-P`ul%^C@(bY *I#I I ]Y?+?39/+_^]++33+3310"&547#73!!!3#'267!++^-^^-^+@[n wǷ@-@@u, H^'c@<cY$    bY I I I *I #I    ]Y?+?39/_^]+++++33+_^]33+33310##"$57#7367#7!!&'7!#3267!Z b+tN1!2C{)O11V)#hvׇ2ǓHyW]ev;toy%)h -1@  !]Y (]Y cY/+?+???+910"'532?"'##!3>32"32>54;[<+7_4R:J->)+NL\PA' JxNJACzK)wsubHؾi͵VuP`uZ)#07@ +]Y$]Y cY cY/+?+?+?+99?10"'532?#7##"&546323767!32>54&#"\<-5_TG]Rw5L-mL'FvOKAD{I)wWNeJZnUfuP`t%-I@'"'cY$""/+bY.@ cY ]Y]Y?2+?3+?3+3+9/3+10"'53267#?>32&#"3#!#"'532?!-hF=6=\̣)ðhPE@9F 1܇R'\<-6^%#MPZőTT1PA>ws-;L@) cY =bY=@,)")5]Y)".]Y"]Y??3+?+?+99+9/3+103!#"'532?!#"&'32>7##"&5463232>54&#"R'\<-6]$4h[h8HSZ@DtNJ?G}GL^wF)V_a^LֿbE_}P`yXX%))@    cY cY/+?+???910"'532?#!!3!3\<-6^9xJJ- % XL')wHJ9v,y)@ cY cY/+?+?10%3#"'5326?#!mL'[<+728 J-;<%)s38@/11$. cYcY))]Y?2+3/+?+?393?1023>323#"'532?#654#"!654#"!336L+FhVmL'\<-5_b^(`Ӊb\+b sptLhbwD3{/D3{1^%)ms$-@]YcY cY/+?+???+910%3#"'5326?#654#"!33632mL'[<+728 l\+b Ί;<D3{1^䧛Ikhs -6@ cY .(]Y!]Y?+?+9??9/+102#"'532?"&'#!336"32>54-\PA'[<+7_4Sx3 HV :JyMJACzKsk͵wsJYJwP`u)s+@ cYcYeY?+/+?+9?102&#"3#"'5326?#336J;%B-7t%;mL'[<+728  s ;<^)s/;@, ))$$!!cY cY cY/+?3+?3+9933910"'532?#"&'532654&'.54632&#"^[<+7](*kEPfJ^y`ɢcv9F@X{np/()ws#ZA8+D4D\_T3+';-?`h߶##%3@ cY &bY&@]Y]Y?+?++9/+102&#"!#"'532?!#"'53267>]>22eR'\<-6^%#'WK.80E %#wDw@;Ff)^!@cY cY /+?+?391067!3#"'532?!!@)CL'\<-6^'--.Rw^b)^(@    cY cY/+?+??3910"'532?#!!!3\[<+7]9}Asb-K()wV=!N)^-@ ]Y   ]Y cY/+?+9?+910"'532?!7!7!![<+7_#?s3+N')wSZ)s#17@  +]Y $]Y cYcY/+?+?+?+99?10"&5467#7##"&5463237333272>54&#"x$Ra'9oLP,4GENM?D{IC)n,gTP FG]tXXZ)sZ#+93@!]Y!]Y) 3]Y,]Y?+?+99?+?+10".54632367!2&#"327#"&54767#72>54&#"XMSy6E.[>20g R81Kp ")H=FuMKAD{II_gJZrXw0 H|44^LwP`tXXZ)Bs'0c@< cY    //+(+bY?+++++(( (cY hY?+_^]?+_^]9/_]r+93_^]/+10"&54$32!#3267327#"&5467"326543g`We[P.2Gex "X-U[i&0X Fn+saS_')/s5k@!4  4bY4I4 I4I4@!I 4 44( +.(.cY*(]YcY/+?+?3+999/_^]+++++_^]910#"3267327#"&54?#"&5%5.54632.#";xZV\^\P-4Gey $,HPV\\HahadMD8=)1LFm,.}5qGV.$@=40)s2j@$cY$12 1 21bY2I2 I2I2@I 2 22 * ,,]Y cY ?3+?+999/_^]+++++_^]9/+102654&#"'632!"'327#"&5473 54+7όtDVEMH|dTP.2Gex _-;D.4-"Vg%8 Fn.,Xu3)s*3c@267#"`P-4Gey pIűM63g`Weg;X-s&"% Fm,.ף)[i&01%RuaS^%)#@fY cYcY/+?+??+10"&5467#!33274632#"x$-mLP,4G_WILX\)n,^ F;WY>:Pc)s)$@ ]Y]Y%cY/+?+3?+10"&54732>54&#"5>32#"'327x ^3@UL[Q=z\Hiٖ,.P.2G)n,.$(~`a#/%+uF+)5## @]Y]YcY/+?+?+10"&5473267>32&#"3273x R160D %]>22e&P.2G)n.,@;mw s Fo)^'+@  ]Y cY#cY/+?+?+?3910"&5467#7##"&547!3267!3327x$T ͊-l\+b-lLP.2G)n,㥜]x\sD3{ F)R^%@@!bY]Y]Y!cY/+?+9?+9/9+910"&5473 54&+7%!7!!"'327x _1t%\'3y+#FlP.2G)n.,V814 { F@ K N OL?2?399??102#"'##336"32654etc`:) ^+5U43-Imŗqct_S1?Ӂ{X LO?22?3310"&54>32.#"3267ey}d?&F.Yv?85Y0h4ˆBC 7jH(!@ L# O?3?339/9310"632#"''67&54>32."32654&oSxdoUgMT? AXCex~d@%FIXT[8E%vN`NLm;7e42_>T4N>.$!);@# $9I +OG?33?39/]99]]]93310&'77#"&54>323.''2654&#"VFR]E39+ė[fx?,=NDd25Kc8D'+5ZiIKiϙshVFr\gPq2Ct7?DXF(@  KXhxG\?2?3]?33]310"'53267#?>32&#"3#wQ(3+A sz^H:3)(0#5X9>f9<|#:-+AbX@   KM?2?9/33310"'5327#7333#dK--"\PlkACjhVbww5ˉhz^d@ O M K?2??3910332673#767##"&547J_ L>fD2-_vda@/#T=Guwf;Wj O@4Xo +I'I#II I/& I KN??9/+]+++++333]2]103###733'4632#"jkDEjk?ϮB;f;?iD5;>T6BX KO?3?1027#"&54631>Jlha nmN%W[K91j @  NK?32?3310'7!!?`h^fG54&'qԒCJ?<AJX+&"W8\e''00V '<9^^!f]!%F# KN?3?310#3#&=-&^r'f\)y5 @ KN?39?3910!7!7!!#z-}㢋)yb5"@KNrM?3_]?39?3910327#"&5467!7!7!!3 &*IVb #z-5-3 WQ6e}㢋)y9E@+  KvWg N?333]]]9?399/]3]10'67#7!7!3>32#7254#"L^#zFUPR0DO1%4G{1* }㢋)pK&WFuoB"d;hR!@ MK?39?339/3910!7!#"'532654&+75#FuĒn36p]U\ k6%r_GQ I@06I-I#I\6 I1 I- IGO?2?39/+++_]_]+_]++q310"&54632'267#"3654Ĕvȃ>[&>]%_pT!xLy^@ o /329/]10#&'7374632#"4632#"a;IErDGuJDrDGuw{HKdDO@HLeDO@ o /329/]10>73#%4632#"4632#"I]ID:9DGu`JDrDGu3*KHK5/DO@HLeDO +,V@ $)y)++ Hq@ H,@ H/+33?9/+]+39q333310"54>54.54>54#"'632!!)!!)!#+#1-.D5i ' $$ZiN?  7=   )5@"Y8Ho U 6 F '  /3]]]]]2/3]]10"#"&5332>;Exqp?w(-%Tph)5&-&87")"u@oVG/3]]]210!7!#Vd@ oVG/3]]]210!%7!V#ŝu@YHo/3]2]]10!'!L"Vd@YHo/3]2]]10!'%!V#L9jC@*GWHXIY8oFV7/2]]]2]]99//3]3]10%7%'V1V}}jC@*HXGWIY8oFV7/3]]]2]]99//3]3]10'%7V1V1}}'@   ?9/29102&#"#36%"3MZ=B ^XablHR  /2210&'56731_x-uHmgiG:,g@   ?33/329/310#.'537673%#567&'5"UXi(>, id/_)iSBDM-7++5%h&EM""Ht"">++5{7s&&'z3vR3&+5Z+!&F'zv3&+55+f&'OR&+5Z&GO-&+55R+&'dZR&Gd5+&'M.Ht>++5Z&GM%%Ht%%>++55;+&'9Z;&G9h5g+&'K>+54g&GK///>+55^&( I)R &+55ZB &H I .&+555^&( JFR &+55Zw &H J /&+55g&(K>+5*gBs&HK--- >+5Y&(R >+5YBs&HR--- >+55}&('zNR &+5Zc+&H'zN6&+55f&)O1R&+5%&IO{(&+5{m&*MR!&+5&JM0&+55f&+OR&+5%mf&KO}R#&+55R&+d+%Rm&Kd5V&+jR &+55%X&Kj\T ,&+55&+zm&Kz5G&+N*n>+5%Gm&KNn(((>+5FY&,R >+5Y&LR""">+5^&,dR@ ((&+]555%\ &  &+5555s&.vR&+5%&Nv{&+55R&.d%R&Nd5&.MQ Ht >++5%&NM*Ht>++55R&/dR&Od5R&/'MYmd&+5RnP&O'MUd&+55&/MHt>++5G&OMHt>++5g&/K>+5g&OK>+55f&0OfR&+5%&POj0&+55R&0d%Rs&Pd5f&1OR&+5%m&QO/ &+55R&1dX%Rms&Qd5&1MHt>++5%ms&QMHt>++55g&1K>+5gms&QK""">+5{^&2 HR@-- -0--&+]55Z &R H +&+55{7&2 FRBB@ HBB&+]+555Zi&R F @&+555{^&2 IR( H(&++55ZT &R I &&+55{^&2 JR) H)&++55Zi &R J '&+555s&3vR&+5!&Svs*&+55f&3ONR&+5h&SO)+&+55f&5O`R &+5%&UO&+55R&5dRs&Ud5R&5'MFRd&+5R&U&Md&+55&5MWHt>++5Is&UMHt >++5)Vf&6OR-&+5&VO,&+5)RV&6dRs&VdD)s&6 ChR 7&+55I!&V C 6&+55)m7&6 DR99&+]55&V D} 8&+55)RVf&6'dOR8&+5R&V'dDO7&+5f&7OR&+5^o &WOR """>+5R&7dj^RoL&Wd90&7MHt>++5oL&WMvHt>++5g&7K>+5goL&WKO###>+5r&8j++55or^&Xjz H>++55Y&8RI >+5@Y^&XR*** >+5g&8KM>+5:g^&XK!!! >+5^&8 HR& H&&++55o &X H! (&+557&8 ER H&++555o&X E &+555q`&9RDR&+5f&YR&+5Rq&9dfR^&Ydof&:OR(&+5}&ZO&&+5R&:d}R^&Zdwyf&;O\R&+5&[O&+5yV&;jRR &+55&[j &+55?f&<O#R&+5s&\O &+5s&=K!R&+5!&]K&+5R&=dR^&]d9&=M Ht >++5^&]Mr Ht >++5%m&KM Ht>++5^&WjW@ !!!>+55}&ZP  &+55s&\P &+55Z&D/&+5 &AO{&+5)y#6@oYiYlY??9/+9+93?3+103!2!"'32654&+5&#")>/ %댑ӅBQ{+8|(&*K--ykThZX&~6&+5ZX&~5&+5Z5&~ 6&+55Z5&~ 4&+55Z5&~+ 6&+55Z5&~+ 4&+55Z&~ M&+55Z&~ L&+55&$Gt?5&$+55V\|'$@ A>+55'/X&/-&+5'/X& ,&+5'/5& -&+55'/5& +&+55'`5& -&+55'M5& +&+55fV'(Gt ?5V'(+55]{|'+@  A >+55`X&;&+5`X&&+5`5& &+55`5& &+55`I5& &+55`I5& &+55`& .&+55`& -&+55p',Qt ?5',+55||',V@  A >+55ZTX&R\ &+5ZTX&R%&+5ZT5&R  &+55ZT5&R &+55Z{5&R#  &+55Zf5&R &+55pH'2Qt?5H'2+55Z5X&D,&+5Z5X&+&+5Z55& ,&+55Z55& *&+55Z55& ,&+55Z55& *&+55Z5& C&+55Z5& B&+55pr'vQt"?5r'v+55||'vb A"">+55Z!&~1&+5Z!&~+8&+5'/!&Ē(&+5'/!&/&+5%m!&&+5%m!&+&+5`!&&+5`!&&+5ZT!&R&+5ZT!&R "&+5w!&&+5w!&#&+5Z5!&'&+5Z5!&.&+5ZX&~'X6&+5ZX&~'X5&+5Z5&~&X 6&+55Z5&~&X 4&+55Z5&~&+X 6&+55Z5&~&+X 4&+55Z&~&X M&+55Z&~&X L&+55j&$'Gt?5j&$'+55V;|'$'@ A>+55mX&'{&+5mX&'1&+5m5&& &+55m5&& &+555&&9 &+55}5&&% &+55&& 4&+55&& 3&+55f'+'Gt+ ?5'+'+55] |'+'H@  A >+55Z5X&'D=,&+5Z5X&'=+&+5Z55&'= ,&+55Z55&'= *&+55Z55&'= ,&+55Z55&'= *&+55Z5&'= C&+55Z5&'= B&+55p 'v'Qts"?5 'v'+55| |'vb' A"">+55Z+&~N2&+5Z&~M#3&+5Z!&~&XA&+5Zs&~XZ!&~&+X7&+5Z&~R9&+5Z&~&RX9&+5}&$N\R&+5&$MR&+5&$K?5&$ ?5j&$FX /3102#767.5468Gq.6BXM>{V K 8-6Ed"@ @ aY aY ?++_^]10327#"&5467>'";^\o$j7 aR O=;R!+@d"@%UF7teFV7 w  x* /3]]q2]233]3]]]]]]]qqqqq3/9/]10"#>323273#".4632#"%4632#"!7 X/QG=?#U-NF=ID:9DGuJDrDGuV4,p$`|w$HL6/DOdHLeDOm!&'(&+5ms&m!&'+.&+5%&R+ &+5&&R+ &+5Z'(2?5PP'(?5b'+2?5PX'+?55&+q5@ @0/]102#767.546#.'73^>=s-7?͇+C 5M7!#^>=s-7?>"?5M323273#".2#767.546%2 Z0RF<B {\-NF>G6<{j&0=N3%m~!Xry!i<0_rP1+#+7`S+&N&+5`&M&+5.t9& -&+555"9& -&+5556|&R&+5`i& ;&+555}&,N]R&+5&,MR&+5{'2,S',5@ @0/]10.54632#.'73F>4cpXH-3+C `&VdTUa1%h R8X5@ @0/]10.54632>7!#F>4cpXH-3>"?`&VdTUa1%h yD;:B(Q@9HXG W   @H@HP"o"""/]q2/++33]q]2]q210"#>323273#"..54632%2 Z0RF<B {\-NF>:6*XhR?*07BN3%m~!Xry!PWEFY) /4 w+&N&+5w&M'&+5w9& 0&+555w9& 0&+555hX&f%&+5hX&$&+5w&R!&+5w& >&+555?}&<NR &+5?&<MBR &+5'Y<DY'<D'37!#fM  u&XL\X /310.54632>4gr[K0:RCe&Vk[^o9+:H+//10#+V!@   //310#'7'77'+V5555X7777  //339210'7##!'77RJ75}o}7L)  //339210##'7!)T77L}57}JV?;/210!!y/:;/'__    ?3210#&'7!#'7 *  C hg{z_hg{  /3/10#!+ToPN) /3/105!#NTP@  //]210###!V @   /33//]3310###!!!VtP @    /3/]2210!!5!###tVPNP @   ?3322/10#!#!#%GJGq  +a@B))   ))))##?### ##)0@Pp/]3]]/]]]]3]]99//3]]3]]107432#"&432#"&432#"&432#"&Pa`QQ]Pa`QQ]Q``QQ]Pa`QQ]oMRQOOMSPPPMSQOOMRQOO!@  /33/]23103#5!#3###VPPN @  /3/]2210!!!## P?55%Sx<  Q?55;UzO Q?55`9j @  PR?3?39910%373#'#PVzy}SxO Q?55^@T Y"III I   YI I Y Y ?3+?+_^]9/+++_^]9/^]++]++3+_^]310#"&54$32&#"!!!!!! Í_aePF11$71Ѹ9D7㹶{B (.I@&,+$! &&vY@/' !!vY?329+399/932+391073'67#7&'#7.54$?3#&K==I:G`djB<{/RP.|}3> BݓkP V8CF)A@"  sY  ##vY&#vY?3+?3+99/_^]+910"33632&#"67#"$32.?x7B f##9 (NI2)b}T~lwb˨íu"q*aU3M%.;8)%T+/@')@&!!]Y?3+3?339?1023632!654#"!7#7654#"!336LC͞15ӊb^(`b\+b soLhD3{/\nD3{1^#'+g@<'+ * jY"!jY$/o/  ?3?99//]q]333+333333+333939103#3#!###737#73!333'#!#33'#7#Dp!pq!qj0jkq!qp omE1mՁ ZlD3 '# ۔+%5!*8\@25+""+iY""-..*iY.7-    cYcY?3+?3+99_^]]33?3?+9/+910#"'532654&'.54632&#"32654&+ !!2!ǺuHZ6YkNӱb|g3>BAsV''yR_"v59뎅dqAZB7)?;GZ_T3++H&E`oQLyʘ>1H#'+.14w@D.//1-jY($ 4+'#jY2 /o/ ?33?99//]q]3333+333393333+3333393103333333#3#!#!#737#37#37#37##!7#37hc!e!}5!!yV3X6}L+/D-&½bA@' lY ?o/?3?9/]]]q33+339103!37!!#!!#N{1 mh+klσ}3}}PFmP@ iY??+3107!7'%7'%!!!7Da3A'D!V77)C{y5kypxtx)8h@BaY?O (( (p(( ((''*oY'62oY #aY?+??+3?+99//_^]^]+9310#"&'#!>32"32654& #!!32654&#"'>\yEjO,ѺFW &gKa@CR'zTO\Ksre)4n%ε cnANsZEN~^$$'3!,:@**%nY nY#?+/??+99339/9910#"'532654'#7546324&#">hg@A*323#y!X3^XQb!N RSK;uV~k` 2R3JEL"<\ʰT@U?8\9@"29.̥SK/E {\7.@iY@iY ?33+?3+9910&54$?3&'67#!MvayNPXˬ % IOOK-=> ݂.l@ElY  jY  @P `p jY mY?+_^]+?99//_^]^]q3+_^]3+310&+7!!3#!7327!7P+^ ] ".+/}#Z՗G~o'  h"2O@1   p      '/0??99//]q]q339/3]q103254&+#!2#%4$32#"$732$54$#" SY/^Zm+*լ֭ۢQIE^ZƬ֭+*7 (.2O@+2   #..!!%%%%%&1,& ?33?3?99//]]39/3339933?10%#"'53254&'&54632&#"'##3/5 #X6\6{9-ux9fV*,:6GMP_Ry?r1; H~g{3+!$Uʗ@3!#HJ^ @cYcY?+?9/+10!!7!!7!q-/:h1^ k'&{u?5&t')u'?5sCd^D 1@  p /3/]/93310#&'5673!9>HH>9)DH$HDV @   //293105673&'#DH$HDV:=HH=:#d^D 1@    p     /3//]93310&'3#67!59>HH>9#DH$HDV @    //29310%67#&'53+DH$HDV9>HH>9d^D?@%  p /3/3/]39333310#&'5673!&'3#679>HH>99>HH>9)DH$HDDH$HD(@   /2/29333105673&'67#&'5DH$HDDH$HD:=HH=:9>HH>9H0@   /2/2293333310!!5673&'67#&'5 DH$HDDH$HDhPX:=HH=:9>HH>9)}jD`@ //3993103!!^j8^@   /3/29933104>32#4&#"rуwfŠdHP @@&  P_ / ? o  /]3/]3/]3993333105!5!5!d8d7 @  /3/29933103 %! {RVm%?(@Y`/o/]]qq+9/10!!#m#?#@  /3/99310#4632#"'&'&#"}?L3% &!" @/)3 )''#i@  //3993103#"&546323265#9P3## #>/'5)%37/333105! h//93103#בK7@ /2/93310!!#'k7n7@ /3/932105!# )@ //3933103!!n@ //3932105!3  @   ///39333103!!#knn@  ///39332105!3# K7@   /32/9323105!!# kn@   //339323105!3! n (@    ///332933323105!3!!# knn6@ _/]]3_]q/_]33333105!5! BZ@  /2/39933103#3#ّiK K >@!  _ /]]2_]q//_]39333310!!!!#'kkב"7 &@   /33/39933310!###ؑ7nn# B@#   _ /]]2_]q/3/_]399333310!!#!!#iro" :@ _/]]3_]q//_]393323105!5!5!# i)ג(7 "@ /32/399332105!### ݑؑn @@"  _  /]]3_]q/3/_]399332310#!5#!5!ӑtt(F(ޑ <@    _//]3/]]3_]q93333103!!!!k#ב $@   /3/3399333103!!33B%n#n @@"      _/2/]3/]]3_]q993333103!!3!!ّJ%i# 8@  _//]3/]]3_]q93323105!5!5!3 iג) "@ /3/339933210!5!333#ؑn >@!        _/3/]3/]]3_]q99332310!5!3!3!5!#LF B@#  _ ///]]3_]q/_]3933333103!!!!#kk#ב" *@    /3/3/3993333103!!#3#Bnn K L@(   _    /3/3/]]3_]q/_]39933333310#3!!#3!!jr " # >@!  _ ///]]3_]q/_]3933323105!5!5!3# iגK &@   /3/3/3993332105!3#3# 㑑iK#K J@'    _ /3/3/]]3_]q/_]399333323103#3!5!#!5!B㑑tK (ޑ B@#   _   /]]3_]q//_]32932333105!!#5! ki"h7 (@    /322/3993323105!!### ؑnn N@)    _ /]]3_]q/3/_]3339933233310#!5!3!!#!5jtrrBޑ"ؒ @@"  _  //]3/]]33_]q932333105!3!5! BZ# (@   /3/333993323105!333! ؑnn L@(         _  /3/]3/]]33_]q399332333103!!3!5!5!B#oV@-   _  ///]]33_]q3/_]3329333332333105!5!5!3!!!!# ikkג#ב">@      /3/3/333339933333323103!!###!5!33Bؑnnnn d@4  _  /3/3/]]33_]q3/_]333993333332333103!!#!5!3!!#3!5!Bؑtr#ޑ" //3310!!V//3310!!V//3310!!V //9210!!+ //9310!!+ *fw% #'+/37;?CGKOSW[_cgkosw{@"2Jjj#3Kk.FVznn/GW{o6Nff7Og*BZ~rr+C[s :Rbb ;Sc&>^vv'?_wwcsgokcgkk`dh_[WW\XTOSKKLPHC?GG@n?o*Z+[BrCs.^/_FvGwA#&.6>JFF'/7?KG"*2:NBB#+3;OCCG@ PQӸCи@ԯ?<;87{{|4xgkosw3ccdhlpt0`OSW[_/KKLPTX\,H7;?CG+3348<@D(0#'+/' $(,$ #  KH@Ԩx`H00H`x DAGO   L/333/3339//////////33333333333333333333333333333393333333333333333333333333333333333333332333103#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#%3#73#73#73#%3#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#3#3#3#3#3#3#3#3#3#3#3#fiiiiiihhffffffffffhhiiiiZiiiiiihhffffffffffhhiiiiZiiiiiihhffffffffhhiiiiffffffhhiiiiiifffffffhhiiiiiffffhhiiiiiifffffffhhiiiiffffffhhiiiiZiiiiiihhffffffiiiiffiiiiffiiiiffiiiiffiiiiiiffbbbbbbbbbbbc^^^^^^^^^^^````````````e^^^^^^^^^^^`aaaaaaaaaaad^^^^^^^^^^^`cccccccccccb\\\\\\\\\\\bccccccccccc^```````````bbbbbbbbbbb%```````````bc^``e^`ad^`cb\bc^`b%`C%IMQUY]aeimquy}  !%)-159=AEIMQAS@zKk h| OoLldi}SsPp`"eWwTt\&a# +P;;Xxؿ)H*99]'A!/L?? ,Q<%D.55 &I+648<@DHH0M@26:>BFFA "E/22@6<@ ؾ RS@  $(,0ֹ:>Gʽɹ59ȹ48+/D*.C%)$( !@ ?| {~ osw <knrv ;jj\`dh[_cg@OSW8KNRV7JJDHCGLP@4 kKKk  ׹;?HKO@ 3"&*./333/339//////////333333333333333333333333333333293333333333333333333333333333333333333332333310!35#35#35#35#35#353353353353353353353#3#3#3#3#3#335335335335#3'#3'#3'#335335335335#373533533535!355#%355##5##5##5#353353353355##5##5##5#35335335335#3'#3'#3'#3#3'#3'#3'#335335#3'#335335#3735355#5##5#353355##5#35335#3'#3#3'#3+jjjjjjjjjjjkjkjkjmkjkjjkkkkkkkkkkkkkkjkjkjmkkkjjjjjjkjkjkjmjjkjkjmkk?kkmkkmjkjkjkkjkjkjmkkmjkjkjkkjkjkjmkkkjjjjjjmmkkkkkkjkjjjjkkjkjjkkjjWjjjjkjjkjjjjkjjkjjjjkkjjjjj!c c"a c!b!`````````````b^^^\`h^^^^^^^^cccccccb^^^^^^^^aaaaaaaa^^^^^^^^^^ccccccccb\\\\\\\\cccccccc^````````bbbbbbb bbbbbbb^^^^cccb^^^^aaaa^^^^^ccccb\\\\cccc^````bbb bbb{Z//9910!!{!!@  /2/3993310!!!7L17}1mh{//9910!!hmh{@  /3/3993310!!!hKPb//3310!!L//9910! XVRZ//910 7L//9910 LRZ//9910Z79e-)@  ! /3/39933104>32#".732>54.#"wx{yy{xwV`bcb`c``dyyyyxyy{b``bcbbbV^R'/7?GOW_gow@X\PhhTl8xx<|(pp,t ``$dHH L@@D0044DLdt|l\ JrvNvvvBz~F~~~2jn6nnZ^^RVVVVV:>>> *..v~n^V>..>V^n~v&f0b@bb"0p&/&?&&&/]]/]9///////32]32]3232q32]32]3293333333333333333333333310#"5432'#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432%#"5432#"5432'#"5432377349947575#3773865567557R75577667377349944976#58856556\677675577667667+557775555885Z557w55763:C558337775555776557737+558#  //99102#"54>jmsoujlw)@  //9933103!32>54.#")wvuwwwvu}Bwwwutww)#'@ $%  //933103!4>32#".'32>54.#")R`babbab`Nwvuwwwvu}B`bb`c``cwwwutwwsbu &@   /33/]3993310#"&546324&#"326bcfdiFIKgFEgcIN_}khfJHfFffFHdhy $0:k@%+ 6+:;<51@) H168833@ H(.""O"_"3"3"  /  /]3/399//]333+3]+29333310#"'4! 4#"32#"&54632#"&54632327#"'ybbZٗ31Z-!!--!!-+!!//!!+LL=`bHgj34V// -- // --# #-S@4( $./)--!O_-&+++@ H++  /  /]/99//+]]]32]3910#"'4! 4&#"326%4&#"326327'#"'bc0!--!0.!//!.bb>KLHgj{ -- // -- //ۺ#Fs;)5p@=3$-%-"') 3 3 67"   $'0*()/3/39/3933333333333333310373#'#5&''7&'#5367'767"32654&BAe;-VL12WXByd+N P)opÉ;'--qt>}`+%* J-d}>^1N =LJŇP'$T@+  ""%& /@/3/]99//]333339333333310.54632!!#!5!"327654&qqwTVhL%Xw{TV;>wBh}VTylFFxUVy>=TVwR,8F@#0',!66,'9:,*3$-$*/3/399/99933333310&'&54763327632".'#"&54632"32654&+ !C=#"&5467>!aFXdP{XZ[Xe`Ga^%%`]ZRw;3C@&'. 45*   .#  //99//]99233]3910)7>5'#"&54672&'&5463267632#".'Fw^69Zs\=e% qtET'iCJt8v_=1op#8wL/yuzs3B'$'ykVb'NKuu2Qi}x6fZy@   /3/99910./.54632>32bZ[K6dV'!XaXoV{eAkswwucVB   //9910&'6J{FT+t}FiRmX;.@  //9/93333103#654&'#"&54632Ll^/9r@k99}M+/dyyw {7-NsB@#      /3/933/9333310#"54632#"&54632%%5_s{N/+ىs9:wJ6.'DeQovt5-Luguf7u@E    !       `//]q/9///99933333333333310##57573%377```J`X=@`^``F\^` Z :@kY kY  iY??+9/39/3+3+3107#73!!!!!!!#7!%l2m3%1%)6a%wǰC@' `Y `Y?      ??9/]39/]]q3+3+3103#!#737#73!3!%hh%!%w-w%7𬛬1Ϭb@>Y8H  o   @&  iY?+?9/3/3_^]_]]]^]2]]]]2/103&#"#>32!3273#"'!5D" t|2E& qC6kPTm5Q@1 lY  iY 0P`@  iY ??+9/_^]q+39/3+310!#!#73!23#32654&+֜mϽ)Mb)}aa n֘{t]W$.@iY$iY oY #?+?+?9/+910327#"&547!2!'32654&+ݏT;3!l5 OgBN_fJ1\R ~261Ş71ruRRV$4@ ]Y   ]Y "??+????9+9910#&5463233#7##72>=''#"퇐`FzCM} D{Ijhg)J t^V4@]Y cY "??33+33??33+910&547#?3333#67#y sĄ1j 21;YaOV*2>TJf /j;5V%o&@ ]YcY/?+???+910%3!#654#"!!3>32V‰l\+bJ-'*A>dD3{1M]Ik5V"@   iY"??+??39103!#!!7!\G}m52i LPF>D%o$@  cY?/?+9??10!3!#!!3XVLxJJ- % ^yHJ9v,V '@ iY  iY"??+9?+910!!7!!!;]R& 5)TVo^ &@ ]Y  ]Y/?+9?+910!!7!7!!V#?s3+oS{#'@  iYiY?+?+99??10"5$3237!#'#267>5#"Po>FKt+6 xtz!]jJSL%k](fs@ dY ??9?+103!367>32&#"'- ;AgYS:%3X/^b;mx fcV3'!@$iY   ?3?3993?+10)47#!!3>7!37>32&#"`4=-!Q q Y$CnRR7$0F0B^Z145]3~U ]p}s&"@"dY   ?3??993>+10!47#!!3>7!36?>32&#"Z Z3 DH ABbQmCW90N.@^с,=hĎկ VaDs%P@/D     aY   `Yo  ??3?9/]+99_^]+99_^]_]10%367!!#727&54632">54&+H)DoTj%E:n}9#%';R n8kl:r9+6Qa3&5#@ iY??9/+10!!!!52yw#j^!@cY" I??9/+]3+10!!!!7}b0]3^RZs @cY ]Y?+3?+?10 $547>32'>54#"ԭvTDn/̯κ[fQ^Y(H|juҼ݃!/10'%'JII)IImmml /9/310##"&54632PF;9JJ9;F[?==?>+5%}s @ }Y}Y?+9/+104632#"&4632#"&%m`DQmYJRo^DQlZIS \kLFVlL\kMEUmJ/\@YY/]+9/+107!7!/0--{ ?9/10#!T/ ?9/10#/R@ X@<7@H  & 6   `po 0/3]q]q3]]]]3/+9/]1067!#4632#"%4632#"}TqE/bJDrDGuID:9DHt^4FHLeDOdHL6/DOF Z@> H@H  & 6   `po 0/3]q]q3]]]]3/+9/+10#.'5!4632#"%4632#"eg! Y$JDrDGuID:9DHtbz9mHLeDOdHL6/DO-7 $@iY iY"?+9?+9107! !7! 3cX+5#2%7hhH@ I8/2]]106$%"hmefyH@ I8/2]]10 #&%5/k3tH{ @ H8 /22]+310.#"#>32ͣHHs-ݒqY(;6" @ H7  /33]+31032673#"&'JHp/ݔm[%86! $@ /3/99993310!#!5!3d+\+ $@ /3/99993310!#!5!3d+%F $@ /3/99993310!#!5!3n!ZZ @  /3/999310!#!5!3n!DN @ /3/999310!# 7 3g\=H`3R "@ /3/9/3993310!#!7!3bH3o`RN @  /3/99993310!#73^J^sN @  /3/99993310!#5 73gRۇjh0hF@  /3/999310!# 73qZyfkFq @  /3/99993310!#73q_ZGd)5 @ /3/999310!# 7 3RkGHyN!5 "@ /3/9993310!# 7 3Rk`/L-!P "@ /3/9/3993310!#!7!3ysdH^- @  /3/99993310!# 73`wcFFH@ /3/999310!#73vq=Df-q @  /3/99993310!#73qFsGVH!% @ /3/999310!# 7 3>{GHZ1#% #@ /3/9993310!# 7 533X3'5g% #@ /3/9993310!# 7 3D{rVJ1}q' "@ /3/9/3993310!#!7!3by R1X/@  /3/999310!# 73HՇH7%q @  /3/99993310!#573q>{G\1# @  /3/39999310!# 7 36}MH)R/) $@ /3/39993310!# 7 530XAuP/j5 $@ /3/39993310!# 7 3NN-qT+o5 $@ /3/39993310!# 7%3Nu7T+}/@  /3/399310)733+NT-)q@ /3/999310!#73qB=T-)P@  /3/999310!#!'!`sboL #@ /3/9993310!# ' 3\H6fy3L #@ /3/9993310!# ' 3VH)hy?L #@ /3/9993310!#5' 3THhy-^ @ /3/999310!#' 3P5fy=Lq@ //9999310!#'3q^hy"@ //9/3999310!#!5!3qT; &@ //9/399993310!#!5!3`/h{T &@ //9/399993310!#!5!35%"@ //9/3999310!#!5!3@P@ //999310!# 7 3i``ybVL "@ //9993310!# 7 3e`;H`FF $@ //9/39993310!#!7!3gb+\t]^L @ //9993310!#73^'ezL@  //9999310!# 73e\هseRdZLq @ //9993310!#73qe^=ddZ/@ //999310!# 7 3Hu7T`H?9 "@ //9993310!# 7 53Ru9HpHF? "@ //9993310!# 7 3Mw\sX=T9H? $@ //9/39993310!#!7!3lwo V=1?@  //9999310!# 73\w߇5:=?q @ //9993310!#73qXw1-B=+5 @ //3999310!# 7 3N}5H)1q5 #@ //39993310!# 7 53N}<Au1]j5 #@ //39993310!# 7 3NN-q -+o5 #@ //39993310!# 7%3Nu7 -5@  //3999310)7331N߇ -P5q@ //9999310!#73qN3 -yL@  /3/999310!#!'!nwJ{L #@ /3/9993310!# ' 3nH J{3L #@ /3/9993310!# ' 3nH)T?J{?L #@ /3/9993310!#5 ' 3nHu^J{-L @ /3/999310!# ' 3n5RjJ{=hq@ //9999310!#'3qo;L{L@ //9999310!#'3kNf{F $@ //9/39993310!#!'!3bj\L "@ //9993310!# ' 3\H5gyFL "@ //9993310!# ' 3VHiyPL@ //999310!#' 3ifrq\%Lq @ //9993310!#'53q^g"@ //9/3999310!#!5!3XV9ZB^ &@ //9/399993310!#!5!3cf)9% &@ //9/399993310!#!5!3b-%w"@ //9/3999310!#!5!3ZV@ //999310!# 73f^ q%^`V "@ //9993310!# 7 53o^h^xX "@ //9993310!# 7 3q\3Hmg1ZV $@ //9/39993310!#!7!3bL+Vm\V@ //9999310!#73'^{^\q @ //9993310!#73qu\/3g7 @ //3999310!# 7 3=q+\=LD7 #@ //39993310!# 7 53Po+^-L i7 #@ //39993310!# 7 3Po?J?LL)7 #@ //39993310!# 7 3PqX/XLBb7@  //3999310)7331PqLb7q@ //9999310!#73qPq?L{H@  /3/999310!##'!y\1F #@ /3/9993310!# ' 3{Ny1PF #@ /3/9993310!# ' 3{Hb1F #@ /3/9993310!#5 ' 3{HtD1\F @ /3/999310!# ' 3{HX1)Fq@ //9999310!#'3q{91@  //9999310!#' 3wbL^=5 $@ //9/39993310!##'!3ws;s=9 "@ //9993310!# ' 3uHXqHETJ9 "@ //9993310!#5 ' 3uHHG}9@ //999310!# ' 3uHHG9q @ //9993310!#'53qw>K@  //9999310!#' 3[VHZdbVs @ //9993310 '53#/[{b $@ //9/39993310!#!'!3[HVbk^ "@ //9993310!# ' 3[VHB1by@ //999310!#' 3_VHLdy}Lq @ //9993310!#'3q^d+"@ //9/3999310!#!5!3LR= &@ //9/399993310!#!5!3aNA%1o5 &@ //9/399993310!#!5!3qÇ"@ //9/3999310!5!3#d+VL @ //3999310!# 73e\-yfL #@ //39993310!# 753e\ }VyfL #@ //39993310!# 7 3e\j?yf)L #@ //39993310!# 7 3e\=J3yf6P@  //3999310)7!31i\y whPq@ //9999310!#73qi\;wh/@  /3/399310!##'!l-T5 $@ /3/39993310!#%' 3H{-T75 $@ /3/39993310!# ' 3HJ-T+ $@ /3/39993310!#5 ' 3H/P% @  /3/39999310!# ' 3}H)/R))q@ //3999310!#'3qÁ)-T/@  //3999310!#' 3TH7H' "@ //39/3993310!##'!3eyT19% #@ //39993310!# ' 3{Nq}1JV% #@ //39993310!#5 ' 33h5@% @ //3999310!# ' 3{H#1Z%q @  //399993310!#'53q{Šy1H@ //3999310!#'3vfD=- @  //399993310!#' 3mwbFFcP "@ //39/3993310!#!'!3sXdH^5 #@ //39993310!# ' 3kL!-L5 @ //3999310!# ' 3kL!Ny-q @  //399993310!#'3qs!HVF@  //3999310!#' 3RZZjfyN @  //399993310!#' 53LRHh0hjN @  //399993310!#'3}^s^JR "@ //39/3993310!#!'!3bqlV`sN @ //3999310!# ' 3\H3`Fq @  //399993310!#'3qZ)d @  //3999310!#!5!3D $@ //399993310!#!5!53'ه) $@ //399993310!#!5!3sD $@ //399993310!#!5!33\+Շ\Z&~ ? 3&+555Z&~ @ 3&+555Z&~ A K&+555Z&~ B M&+555ZS&~ W 3&+555ZS&~ V 3&+555ZS&~ U 3&+555ZS&~ T 3&+555`(& ? &+555`~& @ &+555`(& A ,&+555`|& B .&+555`:S& W &+555`|S& V &+555`:S& U &+555`gS& T &+555w!& ? &+555w!& @ &+555w!& A /&+555w!& B 1&+555wS& W &+555wS& V &+555wS& U &+555wS& T &+555`]& S'&+5555`]& R'&+5555`~& Q9&+5555`~& P#&+5555w& S*&+5555w& R*&+5555w& Q<&+5555w& P&&+55555R!$@ iYiY"?+???+910"'53267654#"!!3>32_[UOc{-5q{4RyHF9)tد?[{5R $8@   iY iY ??+?+99//_^]9104#"!!3>32!"$547!3 6`6ϡq{Z9-  APtد?[XL@>=1yO{.7@"!"!]Y" I" I"")]Y]Y?+?+9/+++910#"&5467>3232654&+732654&#"`k5PVftg/37!#'u>=s-7?>"?hXM5cpXH-3b'u-'VdUUa1%i nY@9 @ H@14H@@@H@ HVfvG@&%(HD%5vGg/3]]]qqqq+rr++qr++]/3322910.54632>7!#!!>4cpXH-3>"?'u-'VdUUa1%i xD;:B>w! H@. @ @!H @H 6' O _ o  /]q]9/3]]]]/++2104632#"67!#wHA:;BGuNCEB`EK41BQWL.s }@@DH:)2I3I,I$ I@p  /]q]2++++]q2]]]]]9+/39/]q104632#"'3673!&'HAuBGurzzS:yVEJdBQ3|#fQio @ ?O I@ I6'D 5 &    @ I 3 I 2 I ; I @H7gw/]q3]]q+qq++++]qqqq22]]_]++3/3_]2910!!4632#"%4632#"'tROG{HL}{OG{IK}yHKdDOdHKdDOs *@e @#0"@08k{ZI /?OO_o@#&H@"   &/33]]2]]2+]qr22]]]]]q3/9/104632#"%4632#""#>323273#".OG{HL}{OG{HL}&4 ~V/K@7?!}S-G=6RHKdDOdHKdDO5%rz#[}o"G,=@w UH@+ H@x w  /]q33]]]2]]2+]+10"#>323273#".767!#&3 ~U/K@8?!9,H=6D9,b15%rz#["۝c-@Z  S@&$4    @H  g w  /3]]q+qqq_]]]]10#.'5!!!D&,a''u=?qdw S@&  $ 4      @H  g w  /3]]q+qqq_]]]]10767!#!!90:+t&t>H,OKy "@Z  H  E6% `  0  vg5& /33]]]]]]]3]]]]qr29/9/333]]]]3]+104632#"%4632#"3673!&'yNG=>HK}{NG=>HK}E`QIPd/HL50DOdHL50DOC2ei.N|m h@ O  @ IF'7'7gwI@I/I-IG /33]3+++]+2]qq/+3/]9104632#"%4632#"!!OG=>HL}{OG=>HL}'t/HL50DOdHL50DO ]@Dx  F'7@PO_o>/w/33]]]]]q2qq]2]]210!!"#>323273#".'u'3 ~V/K@7?!9-G>5׾5%rz#["Z =@ F ' 7 ' 7  @I _ o  G/3]]+2]qq/33104632#"!!IB89r}'u/HL6/ ) 5@! I ( 8 h x  &IO  x/3]]]+2]qq/33104632#"!!sJBps}'tHKd  (@%'(/(?(((@&O  &@L I"/((@H(((pfvWoo  VG&/33]]]]3]]q2]]q]29/]+q/+3]]3/]3]]9/9/]3104632#"%4632#" 33273673#NG{HK}{NG{HK}ѓTVB"3qG}HKdDPeHKdDP!RYQ3 0)@@&%%%O  &@M I#VG&/@HpfvWo o    (/3]]q2]]q]29/]+q3]]]]3/+3]]3/]3]]9/9/310.'53%#"&533274632#"%4632#"h2f8"џTVBNG{HK}{NG{HK}.w0jhNYQHKdDPeHKdDP  @ O @: IFVv7  / ?  @H  gwOo /3]q2]]+q3]]3/+33]3/]339/]]]310!!4632#"%4632#"673#?'u%NG{HK}{NG{HK}RG]"I}HKdDPeHKdDPBK&0  y@ O @: IFVv7 /?@HgwOo /3]q2]]+q3]]3/+333/]339/310!!4632#"%4632#".'53?'u%NG{HK}{NG{HK} 2f<HKdDPeHKdDP/.w0cp? &r@N%@H @ H  @ H &?&&&o&&@H& /&  f G W  /3]]qq]q2+]q9/+/+9/+310#"&533267%.5463267!#"țWVQm$>5cpXH-3@,%@7-,8&VdTUa0&h y?B? %%@[ H%E!U!!&!6!!! @ H "@ H"?o@H /&  f G W  /3]]qq]q2+]q9/+/+9/]]]2+10#"&533267%.54632#&'73"țWVQm$>5enWH-4d7-,8&VdTUa0&h f? $l@I# @ H @ H$?$$$o$$@H$ /&  f G W  /3]]qq]q2+]q9/+/+9/310#"&5332672#767&546>7!#"țWVQm$>=se@>n|7-,8M=rd?͇>2 7-,8M!!1u@OP?5/ATPiGkXu?'+ s$9@"!]Y dY ?+?+9933??3910"#"'53267>32!!!&+CZJH#C[`Y<%/2O&fpgwrb-}߃$}?gNio/u9NV=:5;9@  iY P `  iY  ??3?+9/_^]99+310 ! !!#!!232654+=X ӧVm5Rd8J+ts&4@   !]Y ]Y?+???+93??102!!#"&'#!336"32>54-b-FSx3 HV :JyMJACzKs}JYJwP`uk@iY  iYiYI I@%I' I I I    iY ?+??99//_^]+++]+++++9+310!!!!!#!.54$)#";D8#P5wuakY3m^l1DkroO[s1:C@A AbYAͲIADzIA@YIA"HAAD """995222 2cY5&bY?5555 5 55;cY dY /++ +cY?+_^]3?+?+?9/_^]_]_]r9++_^]93_]9/+++++_^]910"&57#"#"'53267>7&5463!632!#3267"32654";}>9R.Bo[<(%*5:O5t3f`We]5Y.#O]FM];Odt-"`NA?T[i&0.(saS_WI7C5@     ?3?39107!'!!!'7XnZ52-rad1T\d%!^@     ?3?39107!'!!!'7TuXhRRu.ocyiV!^h,5@iY++iY+ %iY  iY #?3+??+?+9/+10632#"'3254&#"!! #"'532>! PXvؾ{ȑI6uOP?5/ATPiG]13 ӥ u?' ^s5B@$.",,*2]Y]Y2%*dY% dY ?3+??+?9/++993310632#"&'32654&#"!&#"#"'53267>32^3PʒA'b{YW/#PӼ(CZJH#C[`Y<%/2O&fpWEo6ؼqzu?gNio/u95#H@.iY iY I ) I  I  I I  " iY #?3+?3?399//+++++++10632#"'3254&#"!!!!!!PXvؾ}ʑI6uӆ)52yy113ץ w=% `^#L@0]Y cY # I "I "H " I    " dY ?3+?3?399//_^]++++++10632#"&'32654&#"!!!!!!`3PʒA'b{YW-%P`b0]]Z-o6ؼqz3^R5V @ "iY oY ?+?+??10!!!!!!3350\L@`%os&@ ]YdY/?+???+910%!!654#"!33632^l\+b ΊPuD3{1^䧛Ik}5V#@ iY oY"??+??9/+10%!!654#"!!632\m 52w =`.$JqV5@%o&@ dY]Y???+/?+9104#"!!3>323!!6=l\+bJ-'*A>dP^{1M]Ik}uD G@3  i y X  0/   /   p   /]q33]q]]]q210#'##'##'7sEEFEF'XggggXDC@(    sY ??+?9/////9910%>7!!"&'??!%%8M;%%'N:o%n% j1111^ffdg`G@LG.L@l\$BG/@l$G0@G@#G@ ^ ^ v V 0  ** DT * ( \ >F <  4VCopyright 2012 Google Inc. All Rights Reserved.Noto Sans UIBold ItalicMonotype Imaging - Noto Sans UI Bold ItalicNoto Sans UI Bold ItalicVersion 1.06NotoSansUI-BoldItalicNoto is a trademark of Google Inc.Monotype Imaging Inc.Monotype Design TeamData hinted. Designed by Monotype design team.http://www.google.com/get/noto/http://www.monotype.com/studioThis Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.http://scripts.sil.org/OFLff  34566778:;YZ[\_`ders}~NOGHTU > ? W X o 45Z[  `bddss!#$$%%&()+,-/1256689::BNHIJJKQRRSSTT Ncyrlgrek$latn4 kern8kern>kernDmarkJmarkPmarkfmkmk|mkmkmkmk        *:JZjz  * + . 0 E V W d e i  lm dF`bddss  %(3!-:/6G8:OBNRHT_%$=D]4KBj77NNSV\_27 BI^alq~!WYZkl""(UZW]f]kngqk{QQbhssvvl   $*06<BHNTZ```flrxxxxxx~xxx~~~~xxx  xf f~~x~~JPV\bhntzP "(.4:@FbL4:RXb^dPjptv| $*06<BHNTZ`fZ`lrx~`fTT &,28>DJbVJbVJbVPbVPbVVbV\bhntntttz.:@:@:@:@:@4:@|||| ****ZfZ"fZ(f."f4:@FLTRXRXRXR^XFTFTFTdTj2j2pvV|JVJP    bb**P *P*$B*0B6$B<BBHNTHNTTZZf`f"flZrfx~`f$~rlx ~P fP`P fP `P `(. T"(&,2(. :T8>D"(JPV:@\Xb@hXn@tzt^Xbr^ jjdPj pt pbFLT"(T.FT4:@FTL >TvR,Xb^d jpv2|pDDD   j2lZlJP 4L$v|*TJP0 6<BVHNTZ`flrx~fZ`4:X@v|*T2: N2j2pt&,24t8>DJ2PPV\bhnxtz |>J$x$VTNVPl~~f ""P4:@(.4:@FL2bXbRdXj^dDjpv|,pt\hV,X28DPb $*|06<BHNTZ`flrx~VZf@RFTh:B:B~4V\ &,28>DJPVh\Bbhntz(.FTVVZfZ"fb@ b@RX^^ F*TF*Tv *|.T"tB(.4:@FLR8XJxV^dj:@Rpv2| p^8 h   \bhv| $b * 0P 6 <Z`l B H N TXt^r,2 Z `  f l r x ~X  `       lBB    * H:TH:PZ`f !!!!!! !&!,!2`!8!>!D!J!P!P!VPP!\L!b!h!n!t!z!!!:!!!!!!!!r!r!!!!!!! !l!l!!!!l!,*T"\" !\"Z! "X" 8""8"(". ,". ,"4":"4"@"F"L"R"X x"^4:@"d"j"p"v"|!"`"Z`l"""""4":"4@"""""""""""""""""### #L##:" #$h#*h#0#6#<#6#<P0#B<#HTr!#N8D#TB#Z#`#f#l#r#x#~###f##!#"" P####B##rx#`##H#6# x###!d#&#8####  6$$$"$Z$$ $&r$,$2$8$>$D$JbhP\b$P$V$\\b$b$h$V$n$t$z$#$$$$T$` 6T$` 6$$$<$$B$$T$HN$$T$HN$HN$z$Z`$$$%~% t%%T%% t%"%T%(%.`%4%:%@%F%L`%R%X`%^%d!n%j%p!n%v(. T"(%|6"(%%"(%%%tL%%tL2b^%Xb%%Xb%%Pj%dP%%tj%%%j%%%D%%&t&& pt&r&pt&r&$pt&*r&0v|&6*&<v|&B*&Hv|&N*&T&Z%&`&fX&l &r&x&~&&&&2&&D&8X&8&H%&&& &&2&N&&<&&&& `f FbLXb&$LB& ,''8''YmEj 9$vB~V`\2( (dttL2dt~~~L~{L{S~+ SFt<qqpp \F\m2mBB%t%%Labhb\Sah hkhk0@l0xh$F0/W& H9 r'Ll0:sqqll-lPlljLka@aatxA@};@;l 0O@tOk@W@Wk@a$,B<n,l{;{~,8{l;,<;2 a,,l<aT@400, $20,2 XInnL<h<Fh<0h<pOOh-k,<<77n0/<,/@,/,DK, <<,GB,H`>8h>HT0k,kLx`<',~j,jla,,\h\l'@rl'BR@Ga%G4/4 LaLP+4;4+ ~;P;+~ H0P.444W%4 <WP4k94 kP4B b,P^^\,76@&c4h ,:x,DJ,JiX>X`. 8w,,[h[Nh  e(99<<S$ ,C@ `W@p}@ohxLpU@=@h+,,((,$,I} %@,%9S@"n CXH\j1j,`g,.\,,W,,@p,,'EP,h442V0Ehh..,,sS,p$ >gg+;ppUUbzda&rKDf:[Mx)0ehhXh h^|Ox3h;hKGssh9Rhn9>opapp ~f,0ak4lklK}4LpptF@t~4FhF4tF44040Xpp,54O449p9pb@,p4444@G4LGl4Hl 44ppMp/ppCp 44b44lal@4'rdLdaR,p*,( Pb`aHHNTHNTZ`flrx~Z<l}/lkqTzGfPoTD $=D]4;CHNOPQ?ARCCUGGVJKWMMYSSZWW[YY\de]xy_{{ab & ,28>DJPV\bhntz\bbb\\\\\\\tttt\b uLHuTlLZtxJbN^8z-_^^(Ls4^^b^^"^^P^BHLLL^b^J^^^ X^ $=D]4679;<*+>FI@^aDH "(.4:.@(FLLRX^Fd.(jpv|dddddxVx4* xdxxyG,NtHb^\#M^^`n8s%(Lv^^J^^f^@^ ;A$=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0   1      & , 2 8 > D J P V \ b h n t z &  " (.4: @FLRLRX^LRdj@Fpv |@F &            J J J J                   $"(.(.(.(.LRLRLRLR*0@Fpvpvpvpvpvpv@F@F@F@F      " " " "     (. (. (.  (. (.6 2 6 2 6 2 , 2 < >BF 8 >@F JLR JLR JLR D JLR JLRHNT Z VLR \ bX^X^` nfR h nLR h nLR h nlr h nx~ @F @F @F @F pv pv pv | | |     & & & @F @F @F @F @F @F          LRLR zdj                           (. (. (.  .  .  . &. (. JLR D JLR pv pv ,v ,v ,v 2v pv8888 @F @F>>>>    & DJPV  "  \bDJ   hntz &6 2  D J D J \ bX^LR @F   &   "(.4:@FLRX^dj pv   JLR pv @F(.$ , 2 6 2 | b^ pv pv@FLR6 2  @F     (. (. JLR JLR pv pv | | @F @Ftz< >BF     (. pv   " h n & $  (. P VLR*0 | 6< "BH  NT(.Z`flrx~LR  @F@F@FLRLRdjdjdj@F@Fpv||||| pvpvLRLR&,LR@F28>D  JP V\bhLRX^nt z28  4:dj@F| LR28 X^LRdj@F|  &   (.flrxLR"LR@F""    "           &. &.  (.  (. (. &(:6 2 < >BF 8 >@F< >BF 8 >@F 8 >@F D JLR JfR| b^ \ bX^ \ bX^ h nLR` nfR h nLR h nLR zdj t zdj @F @F @F @F 2v 2v 2v 2v. .  | | | |      &4 & & & @F @F @F BF BF:    @ &@ &    @F4FL RX^ h nLR h n  djpv<J-J(NjjVV??%%    ==''JJuu``==^^HHNN''0)0)0V00j0 0000FF000,0%0`0yy550=0?0'0J0hh 0--%%33h0uhu0h0-030!!LLHHRR##XXwwqqHH 0   ddjj00q0``  ))mmRRffLL11BB^^))\\ffqq33uu%%LL//!!HH000H000Nh'h0PP $=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0   1      & , & 2 8 > D J P V \ b h n b t z b h b     & & & & & & > > > > V P b b b b b b n t t t t h b b b n n n n b b t t t t t b b b b      b        & & & 2 2 2 8 8 8 8 > > > > > > J V V \ \ \   J J J V  b    " b b b b b b b b b b b b t t t t t t t t   & & & & & & &           > >  "  "  "  "  " V V V h & ( h . D n 4 ( b b : @ D F    L R & X ^ d h 2 8 j p v |   \      h      b X  b  & > t b b  & &      b   b b t t   & & 2 2 > > @   b   \ b t & V    ^ ^ n    >  t   b 2 V  b h h n  b b $ t * 0 6 < B b b H N T Z  ` f l r x ~      \        &  B H   b            2 2 h b z h  b  N  T h   h b  h  b b b t 0 6  n  h h h n b b b b b t t t t t z b            & & & & , h , h 2 2 2 2 8 8 8 8 8 > > > > > D D J J P P V \ \ \ b   b   , 2 b   J0000j0V0?000000%0 0 0000=000'000J0000u000`00=00^00H00N0'00)0F0000y0005000h0-0%03000!0L0H00R0000#00X00000w0q0H00 0 0d000j0`0 00)0m0000R00f000000L00010B000^000)00\00f0000q00300000u0%0L0/000!00000H0000P00 Z`"$&(*,./125678DFHJLNOQRUVWXL"LRX^djpv|dS t1Ab4XD $=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0j1jpv| $*0$6<B$HNTZ`fljjjjjjrx|~$$$$ffjjvvvv| | $$$$*$$$$Z$$HHHNNZflllZZZf**6jjjjjjjjjjjjj$ &,2,8,>DDJfffPV\pbvh|n tzlH$T<hNlN<     " ( .j$ 4r : @ F @ L R   X<jj ^ ^$$$$ d j p v |j f N jvh p ^* p$fh h TTTh * P d  $      $ * 0 0x< 6 < B HT N T T Z ` fP ll~ r x ~    * x     6<$$HNl   $ &< , 2 8 > D 2< > J P V \ b b h 2 2 n t z > np  |   F L  $ 66  <<$   H " (HH " (N . 4 : @ F L R X ^ dT j pZZ``fll v | NZf$Nh z SX4pE\ HHHclLHHHH$OppHp p'?pp HbpHpplppXpp0HHpXH0HR,HHtP!3HvH5H*^Hm Uk%HMHI HHppp ANHHHp/H&H9H&~H HN8aHHRHHE+H~HHDHHJ>G8-HRc7 EHHH%H4HXLpH@< Hpppp pppplbp4\ HRpv4 6@EGIKMQSZ ]]4:6<BHNTZ`flT6rfx~jdjpjJjj\jhjOjqjjjbjjj p`bss!#%% )+!/1$66'::(BN)HI6KQ8SS?`bss!#%% )+!/1$66'::(BN)HI6KQ8SS?@ &,28,>DDDDDDDDDDDDDJJP    VV\@``flrxx~  &  ,28    >>DJPJJJJ~((lOFt(`(`F2(t`xT<22hTPxh hhZxPhhT(nxhhhd|nxx0PTPxdxxdFdZdP@Z@2dFF|d@x@ djdd  $$&(,- 25"89&JJ(RR)TT*dd  $$&(,- 25"89&JJ(RR)TT*+    $*06<B HNNNTZ`f6*BBlrx666666* + $*06<BHNTZ`~t`tV a`24` 4 26fl$$*D)`dos""PEKNO<-2"FEKNOFtsdtdpqtod    xzz    d<< n t t *0b ndvvdddvv n *****ddddddvvvvvvvv6vv d d dH*v*v*v*v*vdHd*bbb d*f t6f f!^!hf!~ !!h! t!d!!"".d"d!"v"""""""6"#<$&&#<#<#<''())$)')'**&$'+|'))#<#<**d,Vdd,V,\,-8,-8-R,,\dd-|-|,,d-8d-|-|-8,\-R$. << t< t t. d..d*-R/6-|&(/(/1212),V1<*,'-R'-R-R-R*,*,*-R&)d/6-|*,1345 56),V56',\',\',\)d1.65 74,&$d7n9p7n9p56)d)d95 :<d:<d':^,\)d)d)d(d(ddd*,*,'),V-8*,*,*,*,'-R'-R'-R$.1356'6:;:;)d:;:;DFHIKKNNPS UU$WW%Y\&^^*+78=DJNORST^_`adnostwxyz{|}~$(**,,..0022446;==??CEGGVV[bddffhimmooqvxy{|~~      !! PQ UV]]__ggimosuuwy{!0O[abfhjnouxAFJLLNNPPRRUUWWYY[[]^cceeggiikq~// $79:< DFGHJPQRSTUVX  !$&+-/13568 : CDFHJV_biyz{~ TUV]_ghnoprvz|  !"#$%&'()*+,-./013579;=?AGIKOQSUWY[]_acegikl n p ~-Z&*24789:<$&*,.02468:GfmqrsuxQ\^iy{}    FHJLNPRTVXZ\^`bdfhjlnp~7$&q~< &*-2479:<$&68:G \FHJLNPRTVXZ\lnp~4$79:;<=$&68:;=?C U "$&(*,.0lnp~%&*24G\FHJLNPRTVXZ\-<$C U "$&(*,.0N &*24789:<$&*,.02468:G \^FHJLNPRTVXZ\^`bdfhjlnp~!~~$;=;=?C~ ~U "$&(*,.0"$&*247DFGHJPQRSTUVXYZ\] !$&+-/1357<>@CDFGHJ TUV\]_ !"#$%&'()*+,-./013579;=?AFGHIJKLNOPQRSTUVWXYZ[\]_acegikm~$C U "$&(*,.0"$&*24DFGHJPQRSTUVX !+-/135CDFGHJ TUV\]_ !"#$%&'()*+,-./013579;=?AFGHIJKLNOPQRSTUVWXYZ[\]_acegik"$&*24DFGHJPQRSTUVX] !+-/135<>@CDFGHJ TUV\]_ !"#$%&'()*+,-./013579;=?AFGHIJKLNOPQRSTUVWXYZ[\]_acegik%&*24G\FHJLNPRTVXZ\   YZ\]7<>@ m  < << <5FGHRTH]3579;=?AGIKOQSUWY[]W( (DFGHJRTDFH( (V]!#%')+-/13579;=?AGIKOQSUWY[] ( ("( (   ( ( F@F`F( (  fmqrsux QV_bdipqrtux Q%V_bfimsvyz{|}~ !qrxQV_bi -V_bfimsvyz{|}~ !Pfmfms~~V_bi~ ~ V_birx Q V_bfim    y~ y~ y~    y~ 4  ky}     ghijnoprtvxz{|~   i{    #iy{  N22 giop{|22 -  ky}      oy  }    < opwy}   ~~~ ~ops6 opw}  g|          p!  j~  % gp|  I(W(Y2Z2\22%('(72224(5(Z([(m2(I2W2Y2Z2\22%2'272224252Z2[2m22  y}  b  ghjnrvz|~      op ghinoprtvxz{|   % gj2p|~222 #  y}      j~  !i{  gt2|   '  y} t  t }   ghinoprtvz{|     p(  y}    }  '  jy}~    :  osy}         o}    )  oy}   Lcyrlgrek"latn0aalt2ccmp8ccmpBccmpLligaVrtlm\rtlmbrtlmh 0@P`ph< T"*2:BJRZbjpx&.6<DLT\djrz^fnv~ (0 $*2:BJRX`hpx$,4<DLRZbj                           LMW`bss!#%% )+!/1$66'::(BN)HI6KQ8SS? (B~FPZdnx  (2<FP ` a ` a ` a !` "a #` $a %` &a '` (a )` *a 3` 4a 5` 6a +` ,a -` .a /` 0a 1` 2a 7` 8a 9` :a Y G k l m j n o '*,./0I 4L5OZIL[IO  ; < = > V 7LMWlinphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/NotoSansUI-Italic.ttf000066400000000000000000010401501434616504300307760ustar00rootroot00000000000000GDEF3d(GPOS`$GSUBF #7 `OS/2R`cmapO'hcvt }%&8lfpgms-p. gasp#tglyf1e_H.head[z6hhea=]T$hmtx%loca솘9%maxp x name4post]fT prepPx6j\I_< 4(   !%  o pRTh\Kt33f  GOOGX J )#+=hFdbBRRRbhh} 5 )huh%hh-h hLhhh\h` ) hwh}hwqmTVTTTmqV/TTRRTT 'jj7?RRmhT)Do9b9bbb3997799bb/9u Zqbu?hhshhhh{h7oVh}5%mh}\uo= R~K3[qTTTTVDRhujjjjTbbbbbbbbbbbb9999X9bbbbbh};qqqq??bbbbbbbVTbVDbTbTbTbTbTbm3m3m3m3qV9qT99999!9/T77T7TT7T`7R9R9R9/UR9bbbbT/9T/T/9 'u  'u  'u  'u KZ)jqjqjqjqjqjqu????Dhbbu; 'u ooHoD%ymoNoh B BYW-BDT}hTTT?qVTRR#qTT/7b=9}hbRsH=b9`b}h7bRbNbbfNobb}hbbTdT '/)=TdTqTVTT TTFNTTT1RqVqTT7^R#RR VTuTTb\dTb3bbqq9Z99b9b9?`bqyq%qT/J9Bb93bu 99+999?qT9uuu?555)1d{d{d{{hB') #LVL) h)h7h=7)o7w}^U]Zs h}db#hsh}h}h}ZXVy%TTd{5!qjR9b sh++bq(TTbqhuZT9FT9T^Tm9Nb`` =bb!bhbswPsy))TquT/}TJ T9NR;9R9T7N{T9 T9T9bb9bj7BFZ+m#y#R9!{J!{JFT19H`qT9T9#R9bbbTbJR;JR;FN;yTqTqbbb???#yT9VT57\bbb;T9bNVZu=-VbbbbbbbbbbbbTbTbTbTbTbTbTbTb9bbbbbbbbbbbbjqjqqqqqq??? L RB9\V9bVDbboXJRND=mj9}hT77;9 `b#T 'u/X/ZRZo;??;ND==j!!!) TT1bTT!7RR9b9bjqjqjqjqjq;bbbm 3m3T7bb;y TT1bm3/T R9bbTbTb9bbT/9T/9jqjqNyqV9TbZP?bTbbbbb?V93b3b?u u}JjDT/b//m?hqb99bb%;h;=mb33Xb9\q99}hZ%7qq9/99b}b\`b/N/// -RP9P9uqRf-y\???)Tbm=`b9R9b?!?+bHbbZZ3Zo77#7++sd{{d{d551''H H HHBN551 uoB})XXXXXXXoq)y{#MJ;o' }}}#}#iPM>Bu*qhV=K~?S[;PL`= 0|QDXJ%E!3m!P/!N!h\!fb Rd=P!`bbbTo5ZVbT+;;5 bB7-bbTRjubuF+b;5{9#9J99b ;5Bmb9Jmww7J7{})J;;9 yXD+HH';qFH{yHH-yy??=9yH53}^?(*3^??S9b /uXJ} 9bX3799/u ;bbbbb=;9/q%D 3jj99 VoHFyyHy;hZTmum T9T9T9bVTbVTbVTbVTbVTbTbTbIU;&TbTm3qV9qV9qV9qqV9A9T7T7T7TTTRM R9R9R9R9R9R9bbbbTTT/9T/T/T/[ 'u  'u  'u  'u  'u ZZWjqjWjqjqjqjbjbuu77????9Zu?bDoBbbbbbbbbLXLXLXLXLXLX======v~~99999999v~~ff}h}h}h}h}h}h}h}Uw~wu~u~ffbbbbbbb~bm~yR~LB)3^fbbbbbbbb~~w~wffbb==99}h}hbbbbbbbbbbbbXXXXXX        ZvZ `~ ` `~ ` `f `fbbbbbbbb ~ ~ ! ~ f fbbbbbbbPoNo   9 ii}h}h}Q}Q};}bT}i;7-{bbbbbqsFVXL!Lhyh4N%%?5Ld2hhuh9hm5?X1hhHm hDjRjR>  dh#f{mmb))s+jVFR?;?;fBhfT#{T9T;7?buTy9ob!j3/{yS VjH hVVVVVNVRVNVNVFFV5V5VPV-VH-V%V%V%V'V/%VVV5V5V/)VPVLVLVLV^LVVVVVPVLVFVLVLLV/V9V?V?V??V5V5V5V5V55VLVLVLVLVLhVLVFVLVLVLLVVVVVVVVVXVVVV\V7V7V7V7V77VHVFVFVFVFFVVV9V9V99VVVVVLVVVVVLVLVLVLVPPV/V5V5VV)V/V'V%V%V%%VHV-VPV5V5-VFVNVNVRVNFVVVVbbbbbbbb}h}h}h}h}h}h}h}h}h}h}h}hTR3 3 3 3 db3)3)3)3)3)3)3)3)1T}ZT9DR}9yT9=R9hu"hsh}# \`` ~ac67"#ou~  OP\_'=?EMWY[]}  " & / 0 4 : < > D ^ p y  !!!!"!&!.!N!T!^!!!"""""""")"+"H"a"e###!%%% %%%%%$%,%4%<%l%%%%%%%%%%%%%%%&<&@&B&`&c&f&k&o),m,w.!# bd78#$tz PQ]`>@ HPY[]_  & * 0 2 9 < > D ^ j t  !!!!"!&!.!M!S![!!!"""""""")"+"H"`"d### %%% %%%%%$%,%4%<%P%%%%%%%%%%%%%%%&:&@&B&`&c&e&j&o),`,q. ^IvedAeY @SQzKIzx?wZI we%" {ob`aa` kLP>:6&$&8F0(.@@<"$ IJ$%KLM`abVPQWXYRh X Y Z [ \ ]STUV     ;<=>r i,-03PQ45Z[@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! , `E% Fa#E#aH-, EhD-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,CRX!!!!!F#F`F# F`ab# # pE` PXaFY`h:Y-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -, %EPX ED!!EDY-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,F#F`F# F`ab# # pE` PXaYh:-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-, T#T[XCPCT[X!!!!H+YCPCT[XH+!!!!YY-, T#T[XCPCT[X!!!I+YCPCT[XI+!!!YY-, #KSKQZX#8!!Y-,%%Ij SX@`8!!Y-,%%Ij QX@a8!!Y-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZXB TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY&QX@cTX@C`BY@cTXC`BYYYYYYCTXBY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,CPCT[X!#0Y-,Y+-,-@;k K~5t~~[PZU?ZoZZZOZZZXUYPXUX@XXTUUPTUTVUWPVUVV VUPU_o߉ PMU+M;M MkMMMMNUJPIUIII7IIIIUGPFU`FpFF;FFUOPNU0NNULPUKSPRU?RRPUQPPU!3 U  p   {  U3UX}us0tttt tFs]@-3U3U l04Fl] im>m]-Z&&H&H&&&###3UU3U?b ;aa]9`]0_]5^^$^;^]0]]]]dU3UU3UOUdUoTS++KRK P[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYss++^stu++++st+^s+++s^s^st+++sstt+++++++++tu+s+++ssstu+++s++++s++stu++st^s++^st+^ss++^ss++++s++st+u+st PuH{jF3`5 {n{ 6666zd `T~<v . l > F N LFJ*x(nb88L6X B~r z6`  j !:!""#F#$>$%&%l%&@&'''(h(()F)*+++,-@.<././x/001 1x1223h334B445P5|5566667 7D788889949V9x99:t::::;;B;<<<==:=\=>>>??<?^?@@@@AA(AJAlAABpBBBBCC2CDDDDE E@EFF&FFFhFFFFFGG<G^GGGGGH HHII@I`IIIIJJ&JHJhJJJJKK&KHKjKLLLMM&MHMjMMMMMN N.NPNhN~NNOO0OHOjOOOP<PPPQQQ>Q^QvQRRRRSS8SZTU U,ULUdU|UUUVV$VFV^VvVVVVWW(WX0XRXrXXXXY$YFYjYYYYZZ"ZDZhZZZZ[[2[\4] ^@^b^^^^^_D__`:``aPab@bc&cJc\ccccdd:d`dpddddddeeeff&f6fffggdgtgh,h<hibiiiij j,jPkklmmnrnooopppqqrFrshstTtuPuvtww8wZwzwwwxxxy<yLy\yyz6z{:{\{~{{|V|f|v|}}~B~~ tVfv.^:j&F$D ,<L"<^<\&6ZjN6V,l:\~n<(| @N8XD<jt Fhl,t:xb&t*Bjr.@Rd0Rtp$4Ġ:lȮXhx@r$<T$^ҰӄPԚHՐ׈v4rܪrޒ F^v>.*XJ\ vP` df60F<BP`*J*Np<^n~ .Nr j"NV@Ph   X &  t  ^n :b$Rv"X| 0Z8`Jn4Ld@h0Rr:Rj Bb0Hj>  !H""T"""#$f$v%%&&&'8''(n()*P**+,,-<-.@./F/00122*2:234D556$6678(899~:::;<<<=F=>?L?@J@AxAAB@BPBBBCC.CFC^CvCCCCDD6DVDxDDDE>EdEEFF&F6FvFFFGG8H(IFIhIIIIIJ&JNJpJJJJJKKLTLvLLLMM"MFMjMMMMNN:N^NNNNOO0OROvOOOOPPPQLQ\R RSRSbSSSSTT(TVTzTTTUU(UHUVWpX\YJYZ[n[\D] ]^ ^x_d```ab"bcd8de>efrg$g4gh|hijfkkklllmnoopqqr\rsbttttu|vvVwwwxy$yzz${{{| |z|}^}~2~~x@>\\\X ^BRD8.8N xTdtLz 6b&8rJHn:Z T\J|P"4DVhzN@0b^ 0Tfvp Bn*RtlJ\n4Ft2,`2rV`pr*L**x<ÖzbrŲJ\l $ʆ ˀz͂8ВX~ӂ4Ո4ך؈.&6FVfvR(:JZjz߈j>T:T<4 hBV\:VTpzB"pF\rDf(l@\4 B      "   V r   LjbZH0zzT*RZtL$ p!F"(#*#$.%"%&'P(*()d)*+X+h,H-./001:123N34$45b5r56V6f67P778^89>99:^:;x;P>?2?@H@A A^AB8BCTCDZDEF F\FFGG`GGH,HHHI I"ITIIIIJJ4JLJ~JJJKK@KdKKKLL4L\LLLLMM<M\M~MMMMNN.NDNlNNNOO:O\O~OOOPP*PBPlPPPQ,Q^QQQQQRR.RFRxRRSS<S`SSSST"TFThTTTTUU(U@UjUUUVV:VRVjVVVWW.WNWpWWWWX,X^XXXY Y4YfYYYZZ6ZXZxZZZZ[[[>[`[[[[\ \,\D\\\\\]]>]b]]^T^v^^^^_ _B_d____``@`l````aa@abaaaabbBbdbbbbcc0cRcvcccd d2d\ddddee2eVezeeef f2fXf|fffgg6gXgzgggh h0hVh|hhhii&iHijiiiij&jHjjjjjjkk>kbkkkkllHlrllllmm4mVmxmmmmnn:ndnnno o6o`ooop p:phpppq$qNqxqqqr rJrvrrrs,sZssstt>thtttuuBunuuuv&vXvvvvw w2wRwzwwwxxxdxvxxyXyyyyzz,zNzrzz{{n||*|L|r|||}}$}H}l}~8~~8\~(LpBd2b b&PzJJ2jL@ *vJ0nz@8t$H,6FXfhXH dp6Z0phLl.\FH2zèJzĨVǠLZʄǹF.ϚRx\x0l@׀\غٮrVܖ&^ݦFrަ0߆Vx2h,pVB*t `B.f b<.^RF nhJHlf<2zlZB. bR8(|\L,~ll8:f  `   :   0 x   Z   B  .>d"Hn,Rx0X HPZlb.`. B !"V##$P$%&P'z'(L))*+^+,B,-T-.h.~.....  /2/310!!7!!IhyJh)3"@   }Y ??+9/_^]10 #4632#"&3yTF.:QD58K[96F`:L ?3210#!#sH%sH=;M@- Y Y? O ?O    ?3?399//]]33+3333+3310!!#!#!7!!7!3!3!!! ` {}x c +z}'{'\TTLNNLF3%+,U@. %&&sY@H@+  sY@H.,?99//3+3+399+33+39910#7&'5.546?3&'>54&'11|JU`'$|Cx~\shy@M lm<'14t @A T8 wY6QJqZp4f  *9=@&7`7777o77%00????99//]]10"32654#"&54632%#"32654#"&54632Af?g>fC?fWp{grvz >@d?d?dE=fYq{fsv{F{ugla2|JFzt}fld-B)(4K@(((% ) )0iY%iY??+?+9_^]33999/10!#'#"&5467&546326733267>54&#"qy܂˲e׳MwLetp:gVf߆d=QOfobRf̟SqZh<`rJh{CS`CM[8HYvix ?10#sHR  $??103#RsWD b $??10#3q`'}Vf ??]10%% '%7Xy{o}29q)06}m} '@ ~Y /_/]q3+310!5!3!!#}}{?/?/]10%#67b\o&5=w@ `Y/_/]+107!5%$Ѧ)+ }Y ?+1074632#"&)UI-7RF37NL^76H`8 ??10 #պ-JuF @ sY sY?+?+10#"5$3 "3265F qnvalnr]Ц9% @   ??9/9310!#67'3T-)8QT˝֊:2q#9,@  sYuY?+9?3+39310)7>54&#"'632!w!~;teOXVKQ`q=B{jʼ-3(>@!# & sYsY $& sY ?3+3?39/_^]+9+310#"'532654&+732654&#"'6323ȳ~ԭU^nc^MT zrO/7XjA6 J +@uY _??39/]33+310##!733!6#'HHv9nJV^LL6^.$LJ3@sYuY sY ?3+?+9/+39102#"'532654&#"'!!6XՓȆԑ.UgL#{aұyOf| ;X^((@!sY sY sY?+?+9/9+104$32&#"3>32#"&2>54#"{oM%JbC;j咹[P@x^.w"M]ŬRjn7aq]@ uY??+9103!7!9!\X&29@--sY-- sY 'sY?+?+9/+_^]99102#"&5467.54>32>54&">54&ŞzmxܺZTp oZHf nJXoͮMJtzfĪM@dr_7wh{CyJcjM}68dXf`-'(@!sY sY sY?+?+9/9+10 #"'532##"&54632%"32>54&-ojj@ 3d铴yjiZXrWݎ"-%HWƽLݬyWy)f @ }Y }Y ?+?+1074632#"&4632#"&)UI-7RF37TI-8QG37NL^76H`8N^48H`7f@ }Y /?/]?+10%#734632#"&3btV`TI-8QG37N^48H`7w$@ @p/_/]q10%5 ef}7@&~Y~Y @`p//]qq+9/+105!5!}d1w$@ @p/_/]q10 5weDnfX&7@ p $$}Y$ ]Y?3+?+9/_^]]9910>7>54&#"'>324632#"&jpxR.c^NDAZ[PSJ-8RF37XZYa>Yc4 07z{^kdL^76H`8mF7B-@8>  8  ,4&0,%??99//32910#"'##"&54>3232>54$#"!267#"$5$!2"327&p{isCX`!bI|P _dˤ F\ZVD9Rޤ$r>qz.(Z9 bnr7@ mY ?3?39/+10!#3# &5/I! L9Jj2uM^T!:@ !!iYImYmY?+?+9/_^]++9102)32654&+!2654&+۫ry-53| n{ahqx @mY mY ?+3?+310"327#"$32&fΖI)ջ<=+RHT @ mYmY?+?+10)! 2$54&+y5Z *ooITo 7@  mY-@ImY mY?+?+9/+_^]+10)!!!!!95#a q18To @ mYmY?+?9/+10!#!!!! 5!m "P(@mY mY mY?+?3+9/+10!# $32.#"327!/՜yjiuhFMiǴo^5*!# (."2ݽ%Vy (mY@Im ?3?39/_]++10!#!#3!3D}5Vj  @ lY  lY?+3?+310)?'7!d)'ff'd){B mY"?+?10"'73273i2EL43R{H}T5 @  ?3?39910!##373{51}:?}T\mY?+?1033!T51#R@    ?33?399310 !#7####!и=&?35 JdVR@  ??910!###33>73}<55 ,H]>% @ mY mY?+?+10# $32%"32654&P􆾤U* ͑hT @ iYmY??+9/+10!##!232654&+Ńy5Jۈ9޴zl @mYmY?+?+910## $32%"32654&P􆾤뉷OJ* ͑׿dT(@ mY mY  ?3?+9/+91032654&+ #! #ʂ5BukEg{\''$,@"  mYmY?3+?3+999910!"'532654&'.54>32.#"̈crz߈ǨF@INf4AVLtRYr}jV$,sNfLWez@ mY?+3?10!#!7!!n# n@  mY?+?310#"&547332678 + d6) \'i-<Vh o\T]tJ @  ?3?39910!## 33%Sy/H@ ??393310 3#3!pwi% $@mYmY?+9?+910)7!7!!ysz#aq$?3?310!!#3yu|!-Z ??10#٤Jms$?3?3103#7!!u; wӓT''?9/99103#TVq 3'f!DH Y/+10!7!q-9!@ /]q10#.'53nB)fB7bf^ '@  ]Y]Y?+?+99??10"&54632373#7#'2654&#"]* A^rlYebZIJcb\ߕ!byon9=#&@ ]Y]Y?+???+9910"&'##33>32"32654'_& CJN2fY_okfbbbZY|cò۽hu#b^@ ]Y ]Y?+3?3+10"&54$32&#"3267֓}3shndrI?P54}(Ab!'@  ]Y]Y?+?+99??10"&546323673#7#'2654&#"V K`!]phecc´dżcfwhlrb^ "*@]Y ]Y ]Y ?+3?+9/+10"32654&"&54632!#3267}g)Hغԓ"y~MQcto5D#¼VÇyL~,(.!*@ _Y ]Y]Y?+?3+?3+310"'53267#?>32&#"3#bF=>4FV.a_/J?XX(py.KDbʣ'duh3h^ *0@&%""]Y"]Y ]Y ?3+?+?+99?10%2654#""'5326?##"&54632373ZseabŅ?K 3[]W C/!ީޞF&0EujIJew9/@ ]Y  ?3??+910!#654#"#33>32W| dJX $ YeY.wݚ'm>Auj8p9)@  bY??+103#34632#"&괅C6^F/+7J9J\ q{ рCHNwiRw d Dcu^sX\d^1>JsՏ^)vߙ'JZR39/^@ ]Y  ?3?9?+10!#654&#"#33>32FLV~ d Ck}Fc*?Kvߙ'JVV3Nfb!X @ ]Y ]Y?+?+104&#"3267#"&54632hxgj\{rd[ڔsP=\!%@ ]Y]Y?+?+??99102#"'##33>"32654Y  dP_]skfbb\ȯü1&6vihu#bf^"%@ ]Y]Y?+?+?99?10"&54632373#67#'2654&#"]( Cc#[!Zvi`agIJfb\i{uj gwݤ9o^@   `Y?+??9102&#"#33>F1'81Zuj I^h^ u} J`R- L^$,@" ]Y ]Y?3+?3+999910#"'532654&'.54632.#"˴AMz~Esk̩:5YXiIlqU-=E(0_M7OAH^L+SC7MSi'}j72xw@RTQNQ)0AqfJ@]Y ?2??+910332>73#7##"&547W d ZcJNZ0vvi@pbJ  ?3?310%673#3qq9C= i\J\'uJ@  ?333?3?310#33>33673#'53P-  OD#4i{JPEÕ6;-RJ @  ?3?3991033##B85jV?J!@ ]Y ?2?+?3931033>73#"'5326?bG h?GZO??ERv6GJK^B9ZgbJ @_Y_Y?+3?+310)7!7!!))c {H3&@   $ ?3?39/33104#7267>;"3# 54?6"vD!%f]Gi-HR1oWhAGWy{(9HA402YK ??/103#&0@ ?O_/_$%?2?39/]q3310 3"+5267>75&54?654-1!wC#qfG~k/R`2YUVhHV?wy*9H8&41sL1X+@ ~Y  @~Y/+3_^]2+10"56323267#"&'.\62cFxQMV.73dF{MMUE4l"!B7n !!^)@  }Y 0/]?+9/_^]]103##"&54632w QE37TG/6eF`64L^8+A@sY  sY _o @ H   ??99//+]3+99+310#7.546?3&#"327F+!%\3qjngrr{"͜> &3}E>?@# tY ?uYsY?2+?+99/_^]3+3102&#"!!!!76?#73>LFj}Dm-ZQ#C5-H%VNts‡f*.чV'%%/]321047'76327'#"''7&732654&#"D`gppebDD``utc^DegfdqdbDD`lktc`BD``uceg{A@%tY tY ?O  ??39/]939/3+3+310 33!!!#!7!7!733H!%:8!ʰ@ ??99///3103#3#7+72@12##_Y(*7,  _Y ?3+?39+91047&54632&#"#"'532654&'.>54&uұ7uavLxqwnq͵v8KQvuVNhaM[hiIF>OC1F5<[`7Ju? 0XR,J8:SxGC]@*zD>]  0@  0!HH/]++q32/104632#"&%4632#"&:2R9-'1p<1*(=,%1b3BR1H,*4A-%4E,h%5M@4 o     `p  *"20??99//]q]q3310"327#"&54632&4$32#"$732$54$#"z]xAb:~~>l^Zm+)׭֭->}6^ZƬ֭)(*@ 0 [ 0[?+?+9910"&54>32373#7#'2>54&#"\PdT`r'%\`3e54&#"'632!V;9_d?lhJp⃀B3>P`gs^azu9#Q@4'+ _ !+0 !?3q_qrr?qr39/_]qrq910#"'532654+732654&#"'632GLl}rhc^_^sC9f^9ru7dE8HYMqNG;:DcXq! @    /]q10>73#3-/Iq7E95sJ@ ]Y ??+??391032>73#7##"'##33T~#i ^^q1)BPuۜze^6NX'@0@0/3?39//]10####"&563!ww>T1y3G\}dR+@ r i    0?/9/]]]9/10#"'53254&'73ס7% *=Mcr;cw jm%/ u&J ??9/93103#7'l:Xk9LE` @ [ ?3?+310#"&54>32'"32654%QftTdwatIB]p|owoTR\o{ @   /]]29/10 '7%'7{wX'}la}XE6A kLfb6j~^'j&{;w ?55K'7&{t?5]10[&u'; +?55qX&H@,  $$}Y$ ]YO_O0/]]q3+?+9/_^]]99103267#"&5467>7#"&54632gs1ZD)c^=`Bú|RUI-7TC29Z'HO\:Xc'-f{v[kiNO]66K]5s&$CR&+5as&$vR&+5Fs&$KFR&+53&$RdR&+58)&$jFR #&+55$6@0"@mYO " " ?3?3339/]r+10#!#&54632&54&#"326A77X}`d{2 L@30@:63@/Jd9y6~atr͆7M^5<<55<<D@ mYmY mY - @I  mY?+??99//+_^]+++310)!#!!!!!#`J c o/wTT98V&&z%Tos&(CR &+5Tos&(v}R&+5Tos&(KDR&+5To)&(j?R  &+55 s&,CR &+5xs&,vR&+5Us&,KUR&+5K)&,jYR  &+55D K@1mYo  mY mY?+?+9/_^]q3+310)#73! 2$54&+!!y"Z *kB#fooI R3&1RR&+5s&2CjR&+5s&2vR%&+5s&2KR"&+53&2RR%&+5)&2jR 0&+55-{ z@  x  @BH    wH/_/]q2232]]]+q332]]]+q332310 7   'h>Ag@ef?i@gf@gu( @"%mY%mY?+?+9910#"''7&5$327"&4'326ÇtiPվw02'\/\U^^k^?5XTdmTFfs&8CVR&+5s&8v!R &+5s&8KR&+5)&8jR +&+55s&<vXR&+5TV #@ mY mY0   ??99//q++10!##33232654&+VD55ڊ;೩{kh=9@27]Y2(  (+]Y+]Y?+?3+9333?+102#"'532654&'.54>7>54&#"#"'53267>Tr>l\gjݻ_+Fs8NWH&@\M?"hXw(G64;@U+OW.PB7AI[G#7q^=X>Co=6WHB4>C&@I}]lbf!&DC"&+5bf!&DvH*&+5bf!&DK'&+5bf&DR*&+5bf&Dj 5&+55bf&DP $&+55bb^*8BK@,32!#3267%2654&#""32654&l*xh^{P Cs7hz){;lo\sZLbaIq/DMN~aůfɾLVxL~$0K&evpoHsl9Db^&Fz}b!&HC$&+5b!&Hv5,&+5b!&HK)&+5b&Hj 7&+559!&Ci&+59!&v2 &+59!&K &+59&j &+55Xs .(@@(]Y!]Y??+9/9+10&'7%#"&54632754&''2>54&#"?Qa|DBVN҆g$EJ<8gd}ojTq596{SEmq|;XN[nmrps|z~9X&QR!$&+5b!!&RC&+5b!!&Rv;%&+5b!!&RK"&+5b=&RR%&+5b!&Rj 0&+55}p@"%H@"HIII@#I ~Y /_/]q+3/_^]+++++3/]+105!432#"&432#"&}q5=>40Aq5=>40A{=>=>9{=>=>9;R' @%]Y]Y?+?+9910#"''7&546327%"&2654'ebmoFfdms>li`9d^ 5?}ThPA|QacW&s-H%+qf!&XC&+5qf!&Xvj"&+5qf!&XK &+5qf&Xj -&+55?!&\v"&+5=%'@  ]Y]Y?+?+99??102#"&'##733>"32654]+ d$N(#d Zvjgbb\Ʊ`\iQ&Tvzejs#?&\j -&+55?&$MfR&+5bfj&DM#$&+5Z>&$N7R&+5bf&DN#&+5D&$Q-bDf^&DQs&&v3R!&+5b!&FvB"&+5s&&KR&+5b!&FK&+55&&OR &+5b&FO!&+5s&&LR#&+5b!&FL$&+5Ts&'L^R&+5b&G8Db5*H@+]Y _Y    %]Y ?]+?9/_^]3+3?99?+1023767!7!733##7##"&5462654&#"w]* !s$'d\\skdda^_[W5+zeIJa#msٝTo&(MZR&+5bj&HM&&+5To>&(N;R&+5b&HN%&+5To&(OX5&+5b&HO+&+5TDo&(QFbc^&HQ #<## >+5Tss&(L/R&+5b !&HL.&+5Ps&*KR%&+53h!&JK1&+5P>&*NR!&+53h&JN-&+5P5&*OR'&+53h&JO3&+5;P&*9+3h!&J:u/&+5Vys&+KR&+59?&KK?"&+5T4@ iY mY  ?3?39/+9/33+33103##!##7373!737!HṐ}5560}0V-9+C@' _Y]Y  ?3??]+9/_^]3+3910!#654#"##7373!!3632T ^ ')ZX ˂V/v7Շᐍ:n3&,RvR&+59(&R &+5M&,MtR&+59j&M&+5j>&,NGR&+59&N&+5D &,QD)&LQ/ 5&,OR&+59J ??103#3J{&,-97&LM{*s&-K*R&+5!&7K&+5T;5&.97;3&N9J73J@  ?3?399103 ##33^)%Q=!J)usJKFoTds&/vR&+57G&Ov &+5T;\&/9F;7&O9T0&/8 ?57&O8LT&/Ohe >+57&OO| >+5\ ,@  mY?+?99333333103'737!Rlj@ͦ?`/#>wugs5X ;@#    yFV ??99]]]33333]3107#'73w9՗}p>ѬmEp=HDq Rs&1v1R&+59/!&Qv\$&+5R;&199;/^&Q9XRs&1LR&+59C!&QL&&+5U'QR}"@ mY"?2+???39910"'73267##33>3j2EOf- $5,&}tmWV7J9/^#$@ ]Y]Y?+???+910"'5327654&#"#33>32B=>8|*FLV~ d Ck}F%;c*?Kvߙ'JVV3DpĮ&2MR&+5b!j&RM&+5>&2NR&+5b!&RN&+5s&2S)R /&+55b!&RS /&+551!K@ mY-@I  mY  mY mYmY?+?+?+?+9/+_^]+10)# $32!!!!!27&#"VRNPՖU!b !n1G6No* 8tb^ -7F@&1]Y11.]Y  !]Y (]Y]Y?+3?+?+99?+9/+10 '#"&5463 >32!#3267"324&%"32654&Zؓ[Jy+z~8qi^yjty+@NjwyL~8Kג;zyp0CTs&5vR&+59!&Uv&+5T;&59;o^&U9Ts&5L5R &+59!&ULz&+5'0s&6vmR.&+5 !&Vv.&+5''s&6KR+&+5 a!&VKa+&+5''&6zH L^&Vz''s&6LR0&+5 !&VLM0&+5;&79K;D&W9s&7LR&+5Z}&W8;#&+5(@iY  mY ?+3?9/3+310!!#!7!!7!! ߍ!in# n3bᢢ)D"4@_Y  _Y@]Y?+?39/3+3+310%27#"&5467#737#?3!!3#>Si' 25j75 2xw PQN#0A3&8RR &+5qh&XR1"&+5&8MR&+5qfj&XM/&+5>&8NR&+5qf&XN&+5&8PR H&++55qf&XP &+55s&8SR *&+55q!&XSF ,&+55D&8QNqDfJ&XQs&:KoR$&+5u!&ZK$&+5s&<KR&+5?!&\Kv&+5)&<jR &+55s&=v\R&+5!&]v&+55&=ONR&+5&]O&+5s&=L!R&+5!&]Lz&+5m@ ]Y ]Y?+?+10"'5327>32&#"E:<5-)5k#0J=YZ(«etͽ\ /@ _Y ]Y]Y?+?3+9/3+310"'53267#?>32&#"3#G:<7>]ɽ %'j//J=YZ(svID«du;u%,-N@.)mY))# &&#@ H@H@0p#@H##-?3?9/+q++3999/+10#!#&54632%673#4&#"326! Z5+zcf{[2??30A;63?,2!tA9 4X^ysQ2&6<<65<<\dx!'bf 3A[@60-2,!(/@0@ 0@H@ H((;]Y(!4]Y!?+?+++_^]^]99??10673##"&546324&#"326"&54632373#7#'2654&#"gz4E|zcfy~ab{lA03>892?E]* A^rlYebZ_4(ctsbavre6;;63>AIJcb\ߕ!byons&vXR&+5bb!&vL&+5us&vR2&+5;R!&v;1&+5';'&69 ;L^&V9! @  /]q99/10#&'#5673q8iqw1"`*0qIQ\BD!@    /]q29/103673#&/l@b~xuB:"[+!2pK=Z_Bj@ 0/]q10!!'j# >@%0 @0 /@ w  0/]q]]]q2/10!"&=73327#?skIZ!zk*5lF#+#:9#*#3?{! @    /]q3210>73#%>73#1$-F^Z/,1G^5<923F?2hs @ @ H  /9/+10>73#h)mFqML9TF ?@?  0!HH/]++q3339/]q10673#4632#"&%4632#"&g23EQ=.R=*%1=.Q;+%1{G? 4AR4E,*4AR2G, &$T ?55G\}dB &(yT ?55BH '+T ?55Y ',T ?55W &2PT &?55B '<T ?55T &vhT +?55h&U .&+555$T%TomY?+?10!#!! 5!(To(=Vy+M@1mY&"I"!I I'I: 6 ImYmY?+?+9/+_^]_]+++++10!7%# $32%"32654&! uQ򈾤7TY+̒ؿf ,T5. ?2?310#3#&'u·lJ`LmR0R1o 7@ mY-@I mY mY ?+?+9/+_^]+10!!!!!7!}M0#f#!L 2T{@ mY?+?310!#!#!F}5T3 $@mY mY?+9?+910#7 7!! !b#i#^17<!3@!iY!mY !!! ??99//]3+33+3107.54$?3>54&'#)17$%1R ʼ؝| ֊ؓ;P#@mY ??339/33+310###"&5473;332673j$_^TZ*fi5B[rTjF}\  %@mY  mY ?3+393?+10"!7!&4$3 !!7654&b~ap ^ȷGk!+ڸpFrxK)&,jYR  &+55)&<jR &+55bs&~T9&+5=s&T1&+59/s&T$&+5hs&T&+5P&U 0&+555b^!/-@)]Y ]Y"]Y??+3+?+9910673327#"5##"&546322654&#"40[ H"#M]WT:^piZe`Zu3;82#' pZǯbȼ߳bypo*1@#$$#]Y$$ ]Y ]Y?+?+9/+9?102#"&'#>"3265!#732654&IJ{WN}R&~xs&3GB!Hm&2<4f(.bqRJ  ?/3?310#633673*K D\7>JCLHs+Z@6## & &9&&&&  9   ]Y ]Y?3+?+9_^]_]9]39]9310.54632.#"#".542654&'N`PʦH^54&'.b1`!Rxmb/fTDv{2Q vgs5.IZ=Ww:,>0BY9/^@ ]Y ????+910#654&#"#33>32FLV~ d Ck}Fc*?Kvߙ'JVV3Nfb7 D@*]Y I"HI ]Y ]Y?+?+9/_^]++_]++10"&53 '2!"!65K몁MYlHmG B|hJ@ ]Y?+3?10%27#"&54736W!i&}zu/g H0s73J!'@]Y ]Y ?+?+??9/310'.#"'632327#"&'&5# I@<*DIu =!"3A]X )<; 3m[ l flkHsJwbJ  ?2?310363#b+2IMJKxHRb239@11_Y11(5*'(']Y( 33]Y/?+9?+39/+910#"#>54.'.54675&5467+7!#"!39Mz[~_a6K_rF F[~׵1EY:!m'^Wl[j3$H`32%"32654&3XC &G3ou+P/Dh^o;CCqH49bV^ ,@]Y  ]Y /?+9?3+9310#>54&'.54$32&#"^bQ[]]Fh3snkkIw37yXHRT?2<(@ИH54SbJ@ ]Y ]Y?+?+310#"&54$3!!%"3254'ђ6(vuFXٖ,` {NJ@  ]Y]Y?+?+310%27#"&547!?!!:Wn#s!t2xw9JPn30APJ@  ]Y?+?310"&5473 323pHHra̴GGtkb \#$@  ]Y ]Y???+?3+310.547>32>54#"sdřs~)blHMEkw +m ptZN/@ ]Y]Y?+??+9933?1023327#"&'#.#"'6eo'D 8:$.H?kt-?b< 3.0 >NsNYG k|FO>w @  ]Y ?3+3??3?10%>3#.54733beflmux-Yr. $ǫPhN{ bJ'$@%  ]Y"?2+3?39/910"&547332332654'3#"&'}:A>A!3 4&+ivrL343Qh^i|L3`W 7qjT)@ mY  mY?+??39/3+310)!#3!33 3 4&+Ӑ5-lGh{ThN7qh#@ mY   mY ?+3?39/+104&#!#!7!!!2#6ej#"lV \\9WO!m%hGTT7s&vR&+5Tf&6jR&+5T{ @  " mY ?3+??3103!3!#!^PT\J$Vo &@ mY  mYmY?+?+9/+10 )!! 4&+P5#`$k|sL8P7qjT%ToaT $@ "mY mY ?+33?+?310'3!3#!# !9hlITuPR(\/ JSlTo(-@  y ?33?339]3333310 333 ###?`ufqHH#'''3'I@*iY "II  "%%iY% iY ?3+?+39/_^]++_q+910#"'532654!#732654&#"'>323DzWg»zgJkᅯ$pO15򕢎eawHDT@    ?3?39910 3#67##35ˬ4^5>J=mTf&6R&+5T7 @ ?3?39910!##33-F5-+:%;@mY mY?+?+?10 #"'532>!#얓vrL343Qh^ɸ3`W JR0Vy+2T{nT3&7T @  mY?+?393910"&'532673673(cPVe`͸3D5^ 'uVZ֫s;R^ @ " mY?+3??310%#!3!3wR/5f-@ mY ??39/+10!#"&54733273?;mjRb۔TXH\X+MNPJR @ mY?+3?331033!3!3R5JR@ " mY?+33??3310%#!3!3!3NuP5 &@mY mY  mY?+?+9/+103 )!7!3 4&+T#j|L7qjT` "@ mY  mY?+??39/+10!#3)33 3 4&++55/j|+P7olT @mY mY?+?9/+10)33 3 4&+5-k|P7ols9@ mYI mY mY?+3?+39/_^]++10%2!7!74&#"'632#"'5y.!\aG/ƽÚvX'JJO8'T#3@mY mY  mY?+??9/+?+9910#"47!#3!!2%"32654&5:Co瀭߃R, CDV&[ΐּi (@mYmY ?3?+9/+910 #&54$)##";X5͹^gJ^xbf^Dd(=@!     "]Y ]Y ]Y?+?+9/9+9_^]3104>$7>32#"&2>54&#"d0VqiiL`꛼[WUWXHvES9+TsZ^îŝVz|qxcQb\)z@R##]Y#/I#.I##)%I## I##2I#.I#)I#"I## I#7 I#2 I##]Y ]Y?+?+9/+++_^]++++_]+q+q+++9102#"&54$32654&#"32654&Žm`jȍݐჃ{&ߧa\osZaW;1iWOM촦g_GM3q\$*@]Y ""]Y?3+?399+9910>32327#"&54>7>54#"xLa/`gOa0,"|lE>3227.#"=籬Uoh-2G/#~TZ]f/Ӽ@ -$*qvwb^H^<r@H:_Y"%I,I'I#IO I 4 I/ I<(]Y#/  ]Y4?3+3?3+3??9/++_^]+_]+++q+3+310##"'732654&#"5632333>32&#"327#"&547## 5G4/3cge`5>W7df!{P=!+?ccd]8:S8f'"%s 8V^'Y@6&''&]Y' +I''' I'"I'I ''' ]Y ]Y ?+3?3+9/_^]+++]_q++910 54&#"'>32#"&'532654&+7TVHP5[d}T=JRdk#=H##+){o1$!+-q\VLqfJXqy&X63&+59DJ @  ?3?399103 ##3bnrqJ#JT+@ ]Y `Y ??+?+993310!#&#"#"'532>7>323%)L^OU[nc2");XQE0Tpxv;W FˉʅA#9\J@   ??3?399310#&'#3673#fjn"Yj'jHJA8M9J .@]Y]"I I   ?3?39/+]+_q+10!3#!#^bfhJ7Jb!XR9/^Q=\Sb^F9^P?J\b#+@ #]Y ]Y ?3+3?3+3??10#.54$73>54&'{ʟ߷ddǚ^B}n~cdzrZźˮ$˸, @3猊 J[qfJ"%@ "]Y_Y??+?+?391032>73327#.5##"&547͔W d&(3sR>9؂JNZ0vF3#+ocaۏ@p=J@ ]Y  ??39/+910326?3#>7##"&54?''FO/.1&Dj6J-IIsoZb6qJ*%@* %%]Y ??3+3?33991032>73#7##"&'##"&547332>7JSv d Z\q{ ҀCHNwkJP^*wݚzerDpPh(>IrՐqJ6.@"$6 *1$1]Y$_Y/?+?3+3?33991032>733267#.'##"&'##"&547332>7J=HSv d)#(uP>;q{ ҀCHNukJNZ,EJwݚU$&( ocaہr?uNd*>IuԎTLJ&@]Y ]Y ]Y ?+?+9/+10632#"&547!732654&#"fXfĆb!tqhiTuJ#o^MsɚtaXFsJ"@]Y ]Y ?+?3?9/+10632#"&547"32654&#3gWǮVpmi:J#ˢYgcj^ZqdQMH\]@<]Y +I"!I"I 9 I5 I ]Y ]Y?+3?3+9/++_^]]+]]+q++102#"&'53267!7!54&#"'>эE*s)|ws}5>\;:*9X"I@+]Y ]Y"I I  ]Y?+??9/+_^]+]+?+9910#"&57##336$32%"32654&헷h^AweVrk]ToVJ7L~ tJ$.@""]Y""]Y ]Y?+?+?9/+910"#"'5326767.5463!#";?7M<):N`C2"4F.Hs_`]nvѴL!FUx\, F`*]g[fb&Hj 7&+559'*+S@0$ ]Y    -+&'&_Y''''$)$]Y?+??9/_^]3+3?9/_^]+910!!3632#"'5327654#"##7373Z % ݁%G84B|*V~ \'\Nݏ>jﰞnvߙPՇ43!&v .&+5b^]@<]Y +I"!I"I 9 I5 I ]Y ]Y?+3?3+9/++_^]]+]]+q++10"&54$32&#"!!3267֗}3sh{/#6p9s\[54 @|*A L^V9)L9&j &+55'MT)5I@*!$!']Y*]Y '`Y 0]Y ?+?+?9/_^]++993310632#"&547&#"#"'532>7>32"32654&}UfĆ` :LJSKtd4",;XQE,MgpwrLYpqhmo^Rn=^ FˀȉH%n8taXF9 J"I@+]Y]Y"I I]Y?+??9/+_^]+]+39/+10632#"&547!#3!3"32654&݀[ŮJf`bCVuqhmɢ\iJ7r6rcXF9+9D!&vD&+5?&\6&+5qfJ.@  ]Y@ H??9/+?+?39103#32>73#7##"&547)]RHW d ZcFZuNZ0vvi@pT@ mY?+?103!#@d5+19@ _Y?+?10!#!3NjʵF=J=s&:CR&+5u!&ZC#&+5s&:vR'&+5u!&Zv'&+5)&:joR 2&+55u&Zj 2&+55s&<CR &+5?!&\C&+55y@ `Y/_/]+107!5%9"Ӧ5jy@ `Y/_/]+107!5%"Ӧ5jy1OڲI޲III@ H/ ?]29/+_]++++310!7!!7!-R-cn{ ?10'63Y={@ o/?]q10#7 (XE[*3/?/]10%#7+btV ?10#&'7>{  ?3210'63!'63P8Y=*{ ?3210#7!#67 (XE=W4[*tD= @  /?/]3210%#7!#7+btV<Z?< @ 0 ??9/3210%#73%Ǥ%%%ՐF! ] B1@  0    ??99//]323210%%#773%% F'ׅ&8g'7ׅE' y$! z !  /3104632#"&tonuwlnuzzz)F&'C@8 >?OO__@]]qr5+5 *9FUI@,A%0:7S7`7777o77L00????399//]]32210"32654#"&54632%#"32654#"&54632%"32654#"&54632Af?g>fC?fWp{grvz >@d?d?dE=fYq{fsv{@f?06?dB=gVq}hrvzF{ugla2|JFzt}fld- |xRRglb2 LVo7@  /]]10VX٨}BX5o@  /]]10 '7yY'}{XM{6I)' ??10 #¢>J!@   [ ??3+3910654#"#3632;VLL=khGWNA)T~QQ:Z)<@!tYuYo uY ??+99//_^]+3+310!!##73!!!!97#n#)$U@0 tY#$#tY $$$$ $$ uY sY?3+?+99/39/_^]3+3+3107!2.#"!!!!!!7>7#737#7h2Me[FF4C/-l#m[M#Cl{!ye-) .Ӊw*7$-?@"%"]Y _Y@%%$-]Y]Y?+?+?99//3+3+10%27#"&547#?33#32!##32654&+32&#"3267 ##"&54>32%"32>54x`uO['KDi0I]GuThvWk};V/ul"i! kJzyn"TMo$/@ #Yi 0??9/9]]10%273#"&=56746324#"6}iPpNrxεP{A>wөy&ꐟNgx!UgV7Z *E@&"kY (kY iY  ?3?993?+9/_^]+9/+10!##3737!#"&54>324#"32646Z;h!ZeyWet}Un=>XmǴN\1%Jrfvn|KSw,@ 0?233339/333310##5!###33#7#}@zell%4ET)/yv}H/@ /?0?22?9939/]10"&54>32!3267.#"1RPGbٓ2Xz#5Fi)|5Bu^'J&{> ?555UJ&u'>D 4?555]R'&<>L ,?555Z'F'>= ?555sJ(*@ "]Y ]Y]Y?+?+39/9+10#"&5463275!"5>322>7.#"J겮阻\>87A_vNi`a;6("("&ҍTpzuw @  mY?2?+93103!7!&:cpNmoP@ mY?+?310!#!^^T $@mY mY?+9?+9105 5!! !qC;q+r} @~Y /_/]q+105!}b?3/9/310##5!3' gP".S@5#0`p`p@)0@@//]]]q]q99//9910#"'#"&54632>32267.#""32654&P~|=VW>:\yAk52jGJ`^Ak83nEHb_̓fqmpdqYa^\kQSenYa^]jSNj#!@sY sY?+?+9/10"#"'532654632&R1I*Z^U.7ups{1#/q@G*$~Y** * *@'~YP`@ ~Y  @~Y/_/]+3_^]2+_^]q+3_^]2+10"56323267#"&'."56323267#"&'.\62cFxQMV.8~5dF{MMU146bFxQD_.73dF{MD]E4l"!B7n !!A7m"B7n !}@O~YO_o@.1H@+H@H  ~Y p  @ `    $)H @ H /  /]++]qr_q33+39/+++_^]_qr3+3310'7#5!!5!3!!g={Hj}7ߔ;} @:+[kH $Td@&H     ~Y/+/3_^]q3]q3+]q]q3]3+]q]q3]1035!55 }d䓓g} @    $ T d    @FH +[kH~Y/+/3_^]q3]q3+]q]q3]3+]q]q3]1035! 5}ddDlgYf  ?3?310 # 3 f73#`;d.:5:; /10>73#%R=bV=G3:1! /10#5>731!]=d4;9=XJ ;@% 0 /?_  ??39/]q3_q29_]10##7!733!7ρ))y}GPkAH;4]9-@  0 !?3q?9/]q2102#"'532654&#"'!!6qVqw\tign[GG>7{?/mq_R4FhWBI/ J3@ 0 ??q910!7!w%-Jyb9".I@1)))')7)W) )))) !###?q?q9/]]]qq3q99102#"&547.54632654&">54&o~m^I?u=/+^VO?HUEDI)7XF?rWRx /dBuufT.U=g#Y<9@PA2VF5(B#O6.6T #'+/37;?CGS[kt|)@cddz0<@1=A TN HX#4,lvvk7/`pgz8; $(D%)E zp/k,XN cuu{llZljlllk\}}VKKvkZQDkTkdkk k0ktQk0\@\p\\\/\O\\\ B*A)>F=E2&1%  -, 48!59/33/33399/]]q3_]]/3/33/33/39/_qq3993333333333333333333333310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3#"&546323254#"%32+32654&+32654#"'53253T/0momImmmm0oowoooomm~smp.,=.m^{B.$*/;J1%Z^4+V}i0oo/mmmmmmoo;mmJoooo/yhIaCS1BD5QYb" "+%J fVr_cT*,@ % +,((""//99//339910 5467>54&#"63232654&#"TV,AgIOGRZ?>1HT;GFBIHCHEVW/2A1R~X8*P:/5K6DpJ;?HI>@IH!&7L&+5{5 <D@'_Y //_Y:@ 55]Y5+]Y?+?+99//_^]3+3+10365#" #"&5467>54&#"76323267,546323%S_#(.5 UGNV!RMk$ȠwtJTp+kŒH?&J""YH;n56g8ILJ@$!@ iY?+??933106>32.#"#3!T}fC-A&,U}jnw٠{9Q%jJ+1@!!   ]Y (]Y?2+3?+339/910"&547!?!##"'4'!3273326Ëpig!{Ԋ/kDI>=%A891?buqdavw`5<<55<<f,(@ ^Y ]Y(]Y?+?+9/9+10.#"3>32#"&54!&#"32>5i%$#[7? :j瓸ɶWTpa2aQ;vk]L M]Ŭ۟'|+BJ.|.jD;?JN@*CHbYC@>=* 88_Y' #]Y 3]Y.?2+3?333+3?33+33??+10"'53267#?>32&#"!7>32&#"3##"'53267!#34632#"&bF=>4GW.a_/J?XX-d_2J=XY(K6:9EV'(뵅C5]F/(8vs.KDbʣ'duhjǤ'fshwr.J9J\:M2R;?E@$>=* 88_Y' #]Y 3]Y.?2+3?333+3?33+33??10"'53267#?>32&#"!7>32&#"3##"'53267!#3bF=>4GW.a_/J?XX-d_2J=XY(K6:9EV'(Jvs.KDbʣ'duhjǤ'fshwr.%,@ mY@ H mY?+?++99310# $32>73"32654&PՕBNU"+􆾤U* pi|;vhbT$&@  !]Y]Y?+?+993102>73#"&5464&#"326nN[!!ڔ{xgj\{rd[X!6RhPms@  mY ?+?39/31063#"&54733267+) #㭉8-"}Rmrk=q!)@!  !]Y??+?39/391032>73>73#7##"&547͔W dQj"寴 ZcJNZ,v}!vi:p(x!C!vRc) @H 0/9/]+9/10#7>54&#"5632papZ^=3!@,KpzS_k30(hV{}  H /+3104632#"&E40,E0(89J4(;L0Tos&(CR &+5Ts&CjR&+5b!&HCY$&+5qf!&XC&+5m91@(,,!!mY3((mY?3+3?3+39/910"'632 #"&'#"54$32.#"326?332654&6^4Jrt"7p״kV2Y6y~zl5-[U՗k'+X߷kВ\TWYZ (eƬU7SWјhJ @ ?2?339]3?310333673##섵E ;5,-oo4K۶3J{Sh\ht5V}3@mY  mY mY?+?9/+9/3+310)!7!73!!3232654&+}!/36m!4/sڋ5 obZ'$-@]Y]Y @ ]Y ?+?39/++310!632#"&547#7373!"32654&Hjα`/-j|ehwi̞dZǚn8^ZqdTJTF%J@#!!mY!mY@I  mY?+3??9/_^]+3+3?+310"!!327#"47!#3!6$32&Ag! ^_ɐ564.͚L):U<"&GJV!RH9^%j@C]Y  +I"!I"I 9 I5 I]Y $ ]Y?+3???3+9/++_^]]+]]+q+3+310"&547##336$32&#"!!3267h^:Ð4sh{/#7rNz<~25J7543y(A{ !@ mY ?33?39/+310!###3##7'&'^uwVkpX0VLVkJ #@_Y   ?33?9/9+310#####&'MYdoX@xJJ/|Tf-@   mY  ?333?9/3+333310!####!#3!3!'.'jqwy5i HVVVhLL^\UG9VJ,@ ]Y   ?333??9/9+3310######3!3# XVflV!h`7 ⼲`0J7}H 3@   mYlY ?22?9/3+3+93910#>77!#.+##" !RJti%+ez= Uc i{EşL鋋N9ɋhDg=^J5@ ]Y^Y ?22?9/3+3+93910#>77!#.##" !o8c[rq0.S\on\r6!+jkf< `iiRiQ Mk)T'D@$"lY mY mY ! ?333??+939/93/++310 !>7!#3!7!#.+##""B+5%,ex< VahzD^Gt*Vߋ N9ɇlDc99PJ"D@$ " " "]Y ^Y ]Y  ?333??9/+3+3+93910!67!#3!7!#.##" !;-5h`rq2-SZqj[l;-jW+J7`iiRlN Ln)V3K@(>>%>>7;;FAA/A A7+*+*iY++++@#I+I +++ I5772iY7mY# #mY ?+?+?+339/_^]++_qq+9_^]29/9]10327632&#"#"&54>7>54!#732654&#"'67.'53>32&#"3DzΩi9mmGBX&cnSgXZ»zgJ/T|OG_m8-%%AOt$37T %wf]xGFza򕠐eawt!M{V_tIs4hwRK@ :/]Y:!KJKJ]YK˲+IK@!*IKK IK"IKI KKK ))B_Y)@ H @ H  ]Y ?+33+29/9+?+9/_^]+++]+++9/+10 54#"'67&'53>32&#"32632&#"&54>7>54+7x6z*c}JGDHI+/'%%H9[c}YbgpR)tXL1X-][|CoK=v]:ϖ!F>LT^WB#u;N{]owVjX+L 'l`N`: 0L>Puw L@1mY)"I$!I I I I7 I mY mY?+?+9/+++_^]++++10# $3227!"!654&P35X5IU* 8;2&b!X W@9]Y +I"!I"I 9 I5 I ]Y ]Y?+?+9/++_^]]+]]+q++10#"&54632267!"!74&!ܔs&{v(+rP>@F}m@  iY?+?3?102&#"#3767>>;25&32&#"#3qNGLn4%+A'y=˘Mo AWJ[gms&}s%R *&+55`-!&~s{ (&+55 &2\bX&R\1.2@-@mY%(@#((mY ?3+3?3+310#"&'&54$7>32%"&'6326'J6;>ʣ)I9;>2Avm1[1@ -{t<3I6*':2C80`.,1i%N/+/324&'#"&'6326FkȈE338uˇD629DE*O#6 L|FNK-N#3 w ;0;2߲)9-93!Ӝ\C"݃kF%,&m!TUr@ P `  @* H @H   1SؑkAɁ9r$*$@L,0 ID+X߼eԋ^*4Z (eI>9NИfb-@Qg@=EDDOO55_;o;;;@ H;@00888/88 @! `Y' @]Y?3+3?3+3_^]]23+]29/]10"'#"&54632&#"3267324#"'>32#"'.#"#>3237654'.54632bmےlaBHGYPij/aUazLX+-? {}EX)aj>mjnA~u'&3-;D??[;/}!5V[ -"9r|u$*$Kt L)3$(Fm&i hdX@ C$C@>F&+5+5h&j h@ %%>$&+5+5@mY mY  ??+?+310"327#&5$32&RHgd̘I)ֻh 'RHb^@ ]Y `Y??+?3+10#.54$32&#"327=eҘ}3shodc^Ѿ^54/s //10%'%7%7%VH㴁EHJ{J;{Z}9IĤ{PN 0@0/10#"&543!632Y*,Z+-_2 y^(#{sf@ 00/9/102>32#754&#"+7Qys>ao4(+oRd$+$^V"$"%+%yj3 @/104632.yF?+-"+vyx;B*"1-J t3 /105654'.54632wys'$2.;DMsL(2  $&D) (6DR_m@iP4H, B&:V^&,c4k no-&44)"00)d^Wkk`ZggS`IB;PPE>LL7E)`EE`)P     ?o/]2q2/]]3q29///333q2333q2333q29333333102#.#"#62#.#"#>2#.#"#>!2#.#"#>2#.#"#>!2#.#"#>2#.#"#6!2#.#"#>]qO?#&*-O;_;;;;6@3P3333.6-&..&-6  ?/9////////]]93310#67'>737&'.'5467'67".'7&'7 F$a5; Ia4#GA݁hBO݁E?軋Ek(8PC{Lh&ZC7#BO݁GA܂ Ia5; F$a5[8D.^3DuOW.FcB=FKTf".@ mY  ""?22]2??+99?103333##67##! 54733267>4^= nqm=f%"Wcq{-0@-%* @ ]Y]Y?+?+?3922/10!7##"&547332>733#!"&54733267 VfEJW dȬ= ppti~@pNZ0GFvN}~z%"[_T3@mY  mY mY?+?9/+9/3+310)#7373!!3 32654&+#'&)#:1s؉bRob}!?@#_Y ]Y   ]Y ?+?9/_^]+9/3+310!!63 # 47#7373"32654& wjX5lywi!ݩ=Xfj32'"327'7654'], dP_\IL:ZvncHFUa^^9&6viȯ\B ۲kr=S#Jf $@mY  mY ?+?9/3+310!!##73!!D# "#{ mJ .@ _Y 3 I . I  ]Y ?+?9/++3+310!!!##73m!jGfggJTo*@mY mY mY??+9/+/3+10"#!!632#"'532654&\%5!mxsH3`R!bCL9m^ĭ3##틞1Jl.@"  mY?+?3?33933?10!## 333 3# `uftO''HH#^<t@I:_Y"%I,I'I#IO I 4 I/ I<3(]Y#/  ]Y4?3+3?3+3?/?9/++_^]+_]+++q+3+310##"'732654&#"5632333>32&#"327#.547## 5G4/3cge`5>W7df!{P=!+?ccd]FKpLf'"%s i ؿ5D3&|mDV^&|R7"@  mY"??+??3910%3###33ǬuRFF5-+:%95J!@   ]Y/?+??39103 3###3bsPRroJk}#JR7)@    ?3?393939910!###3733)R}{5Hyk}`:CZ9TH-@   ?3?93939103#373%3##'owF#VͶ'JHHsDdZqT5/@mY @H ?3?9939/+3+310!###73733#3{#)&!N{b73)@ _Y ??9/3+3?39910373!!33 ###ŕ))@/1)%Q^)us @  mY?3?+99?10!##!7!3C#-+:%NJ @  ]Y ??3?+99103 ##!7! us!oJ#Tw5 mY @I    "mY?+???39/_^]++10%3##!#3!3dtO}5Vj9J:@# ]Y"I I  ]Y /?+??39/+_^]+]+10!33##!#^bȗrRfhJ7N}JT 4 mY @I    mY ?+?3?9/_^]++10 #!#3!!V쵐}5!Vj9J :@# ]Y"I I   ]Y?+?3?9/+_^]+]+10!!!#!#^b!ǶfhJ7ɚPJTT,@mYmY mY /3+?3?+9/+10632#"'532654&#"#!#!@K ˜s}y{\%5/59 J -@]Y ]Y  `Y ?3+?3?+9/+10632#"&'53254&#"#!#jfL?́D^+*Z>zrL3bƵJ Ĭ#$싞3PJ{-9N@- H ..4+4iY++ ++mY#mY mY ?++?3+9/_^]+999+10327#"&'#"$32&#";7&54632654&#"{*BEI@ZR09[5Wy}<]_$TvȀ!59=oD-k2* ,/)ϾBZQfbbm^+5a@ H ,,@ H,1)1]Y)@))]Y ]Y @ H !]Y?+++?+9/8+99+9+10327#"'#"&54632&#"327&54>32>54#"m|&.5>7Vb\dْeR%HJo^r&H[pr\nRIaZPG Emz;vuzD&&|7bD^&F|} @ "mYmY?+?+3?10!!7!!3#7n# nrO9^81@4412&1_Y ++]Y?2+3/?+?3?931023632327#.547654#"#654#"#33> q{ рT $//pP@EZNwiRw d Dcu^s8nv+$ (o[F0>],sՏ^)vߙ'JZR3<bJ @  ??33?310%673#3}-1fgA k!N媴j'@ mY    ??39/]33+310 3!!#!7!73! ##GJ!i#X)J!@ _Y  ??3+3?3310%673!!##733}-1IJA k!eJ峫"@"  mY?+??39?10!# 333# %S׮tQy/HHJ#@   ]Y  ?3/?+9?10333###춨B9sTE5jk}VB'@ " mY  mY?+3?+3??10%#!!7!!!3sR/n!"leZJ'/@$%$]Y%]Y_Y ?/?+?+?9+31032>73327#.'##"&547!7!BuW}!e "-3pT?>Xdn `&wߚ/! *ocave3y-$@"mY mY?+?39/+?10!#"&547332733#?٨uqR^ӔwRTX4TI&GHP=J+@ ]Y ]Y?+?9/+39/10!67##"&54?3326?33#A&Dj6''FO/ȗrR7yZbNm,IIsN}-@mY ??39/3+310!##"&547333673bB> yw UaJGUT7'0>1.(JGX 7=J%@ ]Y ??39/+999103673#>7##7"&547>?;T-/+h~738JV-Hnuᇉ+FdR@ mY ??39/+103632#654&#"#|ޡ ww S]ZZ2=/.*GI!/99J@ ]Y  ?3?9/9+10!#654#"#33>32=.P \[#V/mJ~}=jW:n{'15@ 'mY+'!!''mY(mY?+?+39/3/3+3106$32!#3267#"47.54733"3 $54&3/K2h^{x u1N?;r[y5-;0"=2&);@h`^HH"P앧X`J^%/7@]Y) &]Y$ ]Y?+3?+9/3/3+310"&547.54673;632+3267"32654&ry+M:"|zMNg-R8g^&R=BK}v`L/%Oݿun9@{+5;@#"))mY/ $!!mY! ,mY ?+?+339/3/3+3?10473;6$32!#3267#&547."3 $54&{1N3/dD^r]JN u?;r[yb^HH"P-f;0"=-&u#;@h'앧X`J^(29@]Y, %''!]Y')]Y/?+?3+39/3/3+310.547.54673;632+3267"32654&Kry+M:"|zMNHg-Rw#ϝ8g^&R=BK}v`L/%D Fun9@ ,f&6fR&+5&6P?&+5T7(@mY mY ??39/+3?3+10"#33#"'532654&Db[}5Zęo}wwq:X/59 5J)@^Y `Y ?3+??39/+3103#"'53254&#"#3b{`ebɊxSF^oJ޴ɪ3=9J;$@mY mY mY"??+?+?+10 3##! #"'532>;vrL343Qh^3`W T 4@   ]Y`Y]Y/?+?+?+993310%3##&#"#"'532>7>32Tɰ%)L^OU[nc2");XQE0Tpxv};W FˉʅA#Tw3 mY@I    mY/2+??39/_^]++10"'5326!#3!3Ӕx~6s}5D15#Vj9 J:@#]Y"I I `Y ?3+??39/+_^]+]+10!3#"'53267!#^b<{b]|}-ZhJ71?JTw5mY@I  " mY?+???39/_^]++10!!#3!33#}5Vj9J<@#]Y"I I  ]Y?+??39/+_^]+]+/10!!#3!33#fh^bȬJ7N}-"@mY "mY?+??39/+10!#3#"&547332673JTr\٨ yw R^ce#X0>1.(HI)'JfJ(@ ]Y  ]Y ?+/?39/+910326?3##3>7##"&547>/Qq4с8JY,mo3DdR'@ mY" ?3?3??+99310!##!3!3##7#'5I?G_VdHX9\J&@  ]Y?+???3993/10!#&'#36733#Ѻfjn"Yˬj'jHJA8MN{ ,f&$6R&+5b&D6J#&+58)&$jFR #&+55bf&Dj 5&+55bb^Tf&(6mR&+5b=&H6%&+5R#5@ mYmY mY?3+?9/_^]++9910"5>32#"&54$!36=4&27# ywhj"Eow+^2&I*'dXa;\"9@ _Y   _Y_Y?+3?+9/_^]+3102#"&54$!374&#"5>267#"ϖO2!zKPd:h. I\ݾvP.&."wp5FR)&jR 8&+55;&j 7&+55)&j=R &&+55&j- Q&+553)&jR <&+55r&j <&+55^0@mYlYiY ?3+?9/+3+910#"&'532654&+7!7!-Վb=O^ϐ"#5v/"-9ot٤yJ=@!^Y   ]Y ]Y?+9?3+9/_^]+310!7!#"'532654&+7!ƌʀΩL ٮFXұ{T&MR&+5qfj&XM-&+5T)&jR $&+55qf&Xj -&+55)&2jR 0&+55b!&Rj 0&+55{b!X|)&{jR 4&+55b!&|j 3&+55s)&jR 1&+55H&jU 1&+55T&M^R&+5?j&\M&+5T)&jBR *&+55?&\j -&+55Ts&SR )&+55?8!&\S ,&+55-)&jR *&+55=&j /&+55To @"mYmY?+?+?103!!3#T5!uR9mJ @ ]Y]Y?+?+/103!!3#9J!jsPJ}T`)&j+R *&+55&j 4&+55of&}D>+5omJA@% _Y 3 I . I ]Y ]Y ]Y#?+?+?+9/++3+310"'5326?##73!!!!3mE*/5*8 ggJ!jGH@wo=8Ԅwo)@    mY iY#?+?+??3910"'532?## 333E*04R`%Sٚ@touy/HȂyoJ)@    ]Y ]Y#?+?+??3910"'5326?##333dD*04*8 RB8@vo=8V5jiԃx-@  mY ?3?9/3+393910##!7!33!%" #Ly^/J=@" _Y 1 I. I?3?9/++]3+393910#73333!##X϶Byⷺj/ V\ @ mYmY?+?9/+104$!33!"&#"!3\=ۨjJbGbH&<@! mY'# #mY ?3+3?/_^]q9/+910#"'#"&54!33327#"3267He)̨JBsU$TL.fqgblPºQRj &KQꦰZgtgbN$1>@$ P ,]Y !]Y!%]Y?+3+?+99?/_^]10%#"&54632367332673#"&'2654&#"!]q\, KPScpBB*ŷ^qkddaxoóc\`}bD/Q_w7}jRlrݟT,E@'iY(**%iY* mY@/]qr?+?+39/_^]+91032673# 4654&+732654&#"'6327ǫnMUbl_d-Ŵ ǺxgK${w`Yk˱=#U6q`eau{^*=@#']Y !!]Y! ]Y /]q?+?+39/+91032673# 74&+732654&#"'632hmBB*ȸWhoP\GJ7Y]fT9ԽVTF^Zcm_g*ƶvrL343Qh^i?pȹG13`W T.@@%$'$!*]Y*!`Y ]Y /]q?+?+?+99331032>73#"&547&#"#"'532>7>32}CY9.*H,̴u%)L^OU[nc2");XQE0Tpxv #2iήE?;W FˉʅA#V;T9@mY@I mY@    /]qr?+??39/_^]++1032673#"&54?!#3!{en^h*ƶ4}5B1qȹFUVj9uJF@-]Y"I I ]Y   /]q?+??39/+_^]+]+10!332673#"&54?!#^b_j#?H+ʹhJ7=Dm7̰AGPJV(@mY mY mY?+?3+9/+10!#"$32.#"3267!+++jFjT.&V/%bd^0@]Y ]Y ]Y?+?3+9/_^]+10!#"&54$32&#"3267!q;+֌DpHc;PN+@ mY   mY?+3/_^]q?+107!!32673#"&547#"lcm ^d-DzB1o̵8_bVBJ*@]Y ]Y /]q?+?+31032>73#"&547!7!!BW9?H+ʹp t#/ep7ʲDFHu)I@*"  iY  "I I  iYiY?2+?+39/_^]++_q+9102.#";#"3267#"&54675.54>{U^QS ܗnRk{wDJA6ytvs7%QĮ"o|e=\o;!+@!mY!mYmY iY#?+?+?+?+10 3#"'5326?#! #"'532>;BugI%,8(9 vrL343Qh^Ȇu=83`W oT)<@ !$!']Y'`Y]Y ]Y#?+?+?+?+993310%3#"'532?#&#"#"'532>7>32R@whF*13T%)L^OU[nc2");XQE0TpxvԄwu;W FˉʅA#&$dbf^&Dd#&$cR&+5bf&Dc&&+5k&$tR&+]55b+&Dt .&+554&$uR&+]55bf&Du .&+551&$vR H&++55b&Dv ,&+55b&$wR2 H2&++55b&Dw D&+55Fs&$'dKFR&+5bf!&D'dK.&+5Z&$xR H H&+++55bf&Dx !&+55\&$yR H H&+++55bf&Dy ,&+55ZX&$zR H H&+++55bf&Dz $&+55b&${R H H&+++55b&D{ $&+55Z>&$'N7Rd&+5bf&D&Nd#&+5To&(db^&Hd{To&(cR&+5b&Hc(&+5T3&(RTR&+5b&HR,&+5Te&(tR&+]55b&Ht 0&+55To&(uR&+]55b&Hu 0&+55T1&(vR&+]55b&Hv .&+55Tb&(wR/ H/&++55bd&Hw F&+55Tos&('dKDR&+5b!&H'd{K0&+5/&,cR&+59&c &+5 &,d)&LdR&2d7b!X&Rd&2cR!&+5b!&Rc!&+5&2twR))&+]55b &Rt )&+55&2usR))&+]55b!&Ru )&+551&2vsR''&+]55b&Rv '&+55b&2wsR? H?&++55bp&Rw ?&+55s&2'd7KR)&+5b!!&R'dK)&+5s&\vR/&+5bT!&]v;-&+5s&\CjR'&+5bT!&]C&&+5&\cR*&+5bT&]c*&+53&\RR/&+5bT&]R-&+5&\d7bT&]d&8dqfJ&Xd&8cjR&+5qf&Xc&+5s&^vR'&+5q!&_vV*&+5s&^C=R&+5q!&_C#&+5&^cqR"&+5q&_c'&+53&^RR'&+5q&_R1*&+5&^dq&_d&<dR?J&\d&<cR&+5?&\c1&+53&<RR&+5?&\R"&+5 5&B!@  /]q2210.'533.'53=.c6''f=<6Jy @ /399/10&'#7673'5673ZaVVW/)1,D7oi=k5:G,u+LB @ /399/10&'#7673%&/3ZaVVW/TZ5B7oi=k5:Ljc) !A@ H  H @ H0@ H /939/+9/+9++103#&'#76%7>54&#"5632W/EZaVDE-'*DS[SI5:7oi=` &%R=:AE)%]@< #   ####/]q3]q2/]99//]3]]3]]910"&'.#"#632326733#&'#76*I#">*3g8*G"#?)2f;NW3 E[`zV3-3,4;/@7ok; h *@ @ /2]22//9/310"&'33273673#zo VN7r!aO+APّwB;}Vw-|& h8@!@ H/@0 /]]22/+/9/310&'737#"&'3327)x@6A!yo VN7sZl]B;} h Q @@% H0 /@  0/]]22/q]9/+9/9/10#"&'3327'#7>54&#"5632!{o VN7THTDC,&%#=N`zB;}AG)b %(R: "X@6  @ H/? /@  0/]q]]22/]+99//33/3/10#"&'3327%"#63232673#"&'&!{o XN6+3h9?Q#@%1i?*L%H~xA8y-3-&:+RD 0 //102654'3#"'7B:=-{(zB/&HBdX%v6qzq o  iY/2+10"'532673FE*7'/6=Dwo4Atu&7zBBD&Wz9=*O@. _Y    ]Y   %]Y?+???_^]+999/_^]3+310"&'###7373!!3>32"32654'_& C''o$-fY_okfbbbZׇ |cò۽hu#R#,B@$$#$#iY$I$$$,mYmY?+?+399//_^]++9102)#"#&5463!2654&+732654&+ۮp|-% '!{ p%1曐qx}ahVo9%-@]Y ]Y ]Y?+??+?+9910"&'##!!3>32"32654'_& CJ!.2fY_okfbbbZY|cò۽hu#J @ mY mY ??+9/+1032654!#332#"&547Dyx73?^XHP?<1"!@ ]Y]Y?+??+910"&54733>3232654&#"״P1 bTheTZJy/ǷGrGyf½)sqys @mY mY ?+3?+310%2$54&#"5>32#"'7\ XiϕJpԺ;!@QH$&@iY mYmY?+3?++102&#".#"327#"$327>E*7(.5:GQft]y4A'-ջ<=+Dwrb#%@ ]Y]Y]Y?+?+3?+102&#"&#"3267#"&54$3276?D+4*11 TvundrI?֓=:/-5@{<}(AP D%@ mYmY?+?+39/10)#"#&5463! 2$54&+{% *®op%1Mb &@mY mY mY ?+?+9/+104$!3!7!!"&#"!3bE6a#!jȢJb#.@]Y ]Y]Y?+?+99?+?10"&546323>!7!#7#'2654&#"V 32">54&}`P˦Ha9f]3bszcV߂XMQr9)_K(A32.#";# 327#"&546?.xߐ|wT]` +״hs)e:T|B0lolyfVï"=o&@mY mY mY?+?+9/+10"&'53267!!!!L"B/*>HX!m "$ NZX쪘5(1@iY (mY mY $mY?+?+9/++10!# $327>32&#"&#"327!/՜yjiޏzeD*7'.5;˸Ǵo^5*!# %Pur4AZݽ%'@  mY?+?39910%#"&546736732654'?2\ozt÷j:dC/6G8̐|df ~]XLi1H9,'@& **]Y*! ]Y ??+???+91032673#"&547654#"#33>32; u#3ѭ @Rx dJX $ Y]~D8p5'bЛ(>3R5uߚ'm>Axgh iY?+?10%27#"&54736W!i&}zu/gEH0s F@*  lYmYI II I lY?+3?9/++++3+3+310)?#73'7!3#p#hi#nd)'ff')T7@iY ?3?993?+10 ##37>32&#"Z{5FoA1/. %D:3}:I? );73@  ]Y?+??399102&#"33 ##>b#C+?Wd%#)%Q" w-p)us77 "@_Y/ I ??9/+3+3103###733\+Շ!#$a@ 8@*H6FV   &$#]Y ]Y ?+?+??9_^]39/]+3999910''7&#"'6327327#"&'&5#%$Y<*DIq %u =!"3A]Y )<;3A{EX Uf@}Bl emkH''@! '!mY!??3+3?3393103267332673#7##"'##"&547+ ZX-ķ WY2˅88,Za62UbtJ̭=@ ;@ mY ??+?39910!###"&'5326733>73}<+!t"B/)?IX ,&{( PXXH]>%9/^{-,@ mY  'mY?+?399+33?10#"$3 >32#654#""32654&qLRgٷ 4򆻣遳T+  V`E<65}fӾb^-2@]Y  #]Y *]Y?+?+99?+3?10#"&54632>32#654#"4&#"326ϔbAP wViXj\nk`YPKL8EI<6cSLu #$@mYiY  ??99//++310!###"#&5463!232654&+#Ńy# ۈ9%1޴zl=!.,@]Y )]Y"]Y?+?+99??+102#"&'##>32&#"3>"32654]+ du y#C+?W/d Zvjgbb\Ʊ`\iQ&웄 {bzejs#T3b.@ mYmY?O  ??99//]++91032654&+ #33 #ʂT5++ukEqg{\'3(*@#&mYmY?3+?399+99104&#"'632327#"&54>7>svg~8\˄8u'D]mv<;oVdLRrCJN\G_nLKMw\F7,>Rfj^&*@$"]Y]Y?3+?399+99104&#"'6323267#"&54>7>VTc/K|`4^fEaU|y2F>GFL~\~Z3%5H3ER&.GY\)"6@p/')(=@# # ]Y#P#`#p# #@]Y]Y?+3?+_^]+91027#"&547#"&54632";654&R6Wg+~~7l|kxB+-1%A '}s>\qdsyh0"L,s1&&%0(,ZD#,@_Y@]Y ]Y?+?+?3+310%27#"'5326?.547#?3!!LSXwiC+7'.0+z~}j72dxr0E|q@RTQNQ)0A@ mY?+3?9/10!##"#&5463!!?MN !oDK%1Z#(@ _Y]Y]Y?+?+?3+310%27#"&547#?>32&#"!!>Si'}%JB?Gz%2xw@RTQPQ)0A@ mYmY?+3?+10!7!!327#"&547Jn# nb0*Q(m J"%a wm)5L"&@ mYmY?+?3+39310%2654'7!!#"&547!7!ϓ|ap>!cfɫ NË'p`ljr@ mY ?3??+10#367>54#"5>323z!Ş^L` 7?\0.O"p|8jmHYD^ !@   mY?+??93310"56323#&"!2EQ[yqw1QWi%H?T^")@ "]Y ]Y ??+?+?3931033>7>32&#"#"'5326?bG hB]J?,)#ZO??ERv6GJK^B9yM ,C쪊gb?@$ iY II  mYmY?+?+9/_^]++3+310!!7!3!!!7!Xoz#ac!sBФ!JI@*  _Y _Y"I_Y?+3?9/_^]_]+]3+3+310!!7!3!!!7#) )1Bɏ{w^D3@lY iYmY?+9?+39/+3107!!# 327#"&54$%#E#cFױ0%rsfSƶ J=@!_Y  ]Y]Y?+9?+39/_^]+3107!!#"3267#"&546$7IqQpǃ}Ͷ~!3L⾡ J$R@0]Y    _Y   ]Y?3+?9/_^]9^]+39_^]+91027#"&5467>54&+7!7!㤖ʟg TxaWF@|p~$|o_]}N +53(4#E@'mYlY 1 I- I iY?+39/++_^]3+3+910)7!7!>54&#"'6323!!fy!ZP`PM\VѢƦ1k`N`:E{ݷ`)@mYiYiY ?3+?9/++31032!"'532654&+#7!HZ&Q_ѕy#C#uϼO.2~"J)@]Y]Y ]Y ?3+?9/++31032#"'532654&+#7!5!kT晴GVwSY9D&6@# #  ]Y _Y ?3+3?3+99_^]104?#?3!!#"&'532654&'.! 'j7)" t]\.DUI<)5QN&,BZ' XbR5N1+Z5\$@`Y]Y???+?+910#33>3276$54&#"PIb}"WMw*6jwŧir`??9/10#3`h';@iY iY ??99//3+33+39/10!3!!!!#!5!5!<<<ğ<7#)3T s&''=VLwR@ !!!>((&+5+5T !&'']VL@ !!!>((&+5+5b_!&G']L@ ///'(>66&+5+5T{&/-T&/M77&OMR{&1-R&1M9&QM&$LFm&+5bf!&DL,&+5&,LMm&+59!&L&+5&2Lm'&+5b9!&RL'&+5&8Lm"&+5qf!&XL$&+5 &8 LZ&@, >/ H/&++555+555qf&X L 1&+555R&8Z&@  1 >4 H4&++555+555qf&X 6&+555&8 KN@&&7 >?555+555qp?&X K <&+555R&8Z&@( >+ H+&++555+555qf&X -&+555;\}&$ LDR&@>' H'&++555+555bf&D L 9&+555&$ Oo@,>&+55+55bf&D N -&+55&MR&+5bbj&MVF&+5P&n@F"#"iY#&mY#- I#) I"IIY I 2 I- I## mY mY ?+?3+99//++_^]+]++]+++3+310!3## $32.#"32?!7!7!/C:yjiuhFMiǴo!ϒ*!# (."2ݽ%3h^$1@U +]Y" ]Y? O _   ; I 7 I 2 I . I ) I %]Y @H]Y?2+?+_^]+9/+++++_^]q+33?+99_^]?10"'53267!7!?##"&546323733#2654#"}Ņ?Kez%A9[]W CAK\uf_YF&0/7Ouj_¾XvަocPs&*LR*&+53h!&JL6&+5T5s&.LjR&+57F&NL{&+5D&2QJbD!X&RQD&2'MRQJ&+5bD!j&R&MQ&+5^s&LR'&+5y!&L]%&+5T &'=VT&']Vb%&G]Ps&*v1R(&+53h!&Jv?4&+5T4@ mYmY@Im??39/_]++??+10#3!332673#"&5475: HJYf$%f}=Vj=%UKbV98' -@  mY mY?+?9+93??102#33>$54&#" :6d d ff]l̛+ֱX<*(rldmzRs&1CfR&+59/!&QC&+5s&$sDR  &+55bf!&Ds 2&+554>&$sR&+5bf&D!-&+5Tos&(s5R &+55b!&Hs 4&+55To>&(bR&+5b&H/&+5s&,sXR &+55j!&s &+55:>&,yR&+59&&+5s&2sR -&+55b!!&Rs -&+55>&2R(&+5b!&R (&+5Ts&5s1R &&+559o!&Us $&+55T>&5TR!&+59o&U&+5s&8sR (&+55qf!&Xs *&+55>&8R#&+5qf&X3%&+53ys^&3@&%&%_Y&& ]Y ]Y ?+3?3+9/+9102654&#"'>32#"'532654&+7Vxb}5[]ɬuyũcӠ\}B+!*(HV˪Vys&+LR&+59}&KL9{'&+5TV@ mY ???+9?10"#33>32#65445! Fи1-yHZdO@bEZ3+(@ mY&mY?+?39/+991026?3!"&547&54?3"32654&쎽!%'=itη %% {NæT7uƳ3\085/mn۰Ѳ}P"1(@ --]Y&]Y?+?39/+99102673#"&54>7&547332>54&#"}"78"sQeUs 77]vps]rtYb2ᐡ&*|ټ} R4>J7\T|tyBj.@  mY  mY iY#?+?+9?+910"'5326?!7!7!!ZC+7'.6z#aqFyj4AwsjJ5&$OR&+5bf&DO3)&+5To&(z{b^&Hz&2 Lm@ 44&+]555b!&R L 4&+555&2 Mm H &++55b+&R M  &+555&2OR$&+5b!&RO%$&+5&2 O (&+55b:&R N (&+55&<MR &+5?j&\M&+5+@ _Y @_Y?+2+99?10632#"&''6732654&#"N*8ixVw09nZy 7@7&73~cyG>Ak;tcB5-13%f9-^)6=@ $$]Y$ 0_Y0 @*_Y?+2+99???+910632#"&''67654&#"#33>322654&#".5gzWv09nYyRFLV~ d Ck}F2E7%73 -~czG>Ak;vc*?Kvߙ'JVV3Nf<;-13*06D';@_Y@ !_Y! @_Y?+2+99?3+310632#"&''67#?3!!2654&#"N3;g{Vq-;oQtj752E7%83-}dxG>32%2654&#""32654OqdV KN+d\ucp]ccM]rq`bb|r[3dżrf{ yfıȕk}Fl{&b^$1>/@,22]Y%99]Y?2+3??39+3102>32#"&'##>7##"&546"326542654&#"Hue\* aT-fZ}vbo^`i]sncda^|k`5ô^^Os,z|cİ`ȕj}%k}ݟfs9@   mY ?3?39/+33999910####373#&5%3/@^l䌵 Lhya9rLjMlM^f&>@$ mY@ mY ?3+?3399+339910&'327#"'#7.5$327"&@-%I"4BUk`VwchfT?-b4 v<= G Vj1Vu$@@!! !]Y  ]Y0?]3+?99399+3999?10"'#&54$323&'3267#"`V q*,53$(^2KI?u lcLqP' (AL7Ù?\ $@ iY  mY ?+?9/3+31033!!!!#^Bm1#9}f#@  mY ??3+3399103###!7!77#>N :n#=Ùf^?V ^ L^79@53(#!.(.]Y*( ]Y]Y??3+?+?3+999910+327#"&'.'.'532654&'.54632.#"  DB54&#"'632aydCոtB}\f{dsϮJ?^'@  ]Y??+39_^]3103>54&#"'>32=|]WDGTu''T֊f+080̭v )i@?mYII2 I- I*! ! iY!I!!!)mYmY?+?+9/_^]++99/++_^]++3+3102)#73!!!2654&+732654&+۫ry-J! ) | n^H›qx{ahD"*@mYmY?+?39/33+33103##"&54?#733!26?!{!/8 }}}+-b MvAA׭՜$n$tiTfoM@ mYmY-@I @mY?3+3?39/+_^]3+3+310)#7#!733#3#!3!3#9mBA5))P#sǵp`1ᡚ^^8 VN&+0I@'.0]Y 0"0]Y%*)"" ]Y?+??39/999+99+9910&5463233267#"'"3274'67#bX&"sΨ;dMQcTwXJf*FA \`VoVD!/,(.!-=sVy+9;#{P$@ mY  mY"?+?9/3+310"'7327#7333#i2EL4"#R{qm{}'/@ ]Y ! bY ]Y?+?+9/3+310"'5327#7333#4632#"&I6:=}+^^%OC6\F/*6CfL9J\F1'81j;lm^ I^h^ `R-m1@  mY   ??39339/33+3310!33###733!ϲy#pw"qD%#7?J#9@ ]Y $#]Y?3?+?3939/33+3310#733!33##"'5326?>7#s6/Xm\ZO??ERv6Gw8BH Nkkgb%Fq+\(/@ "^Y ]Y]Y?+?+39/9+?103>323267# 47"7>54& Hg˦ZZOG7 } ӳbH[UW*MQ0 Z3>}b {xHRbf^D9=^ '@  ]Y]Y?+?+99??102#"&'##336"32654&]( A\xkYgaZ^ȯc[Jߕcxon9=,-@ ]Y ]Y ']Y?+??+?+9910"&'##>32&#"3>32"32654'_& C"v#C+?W0fY_okfbbbZ {T`|cò۽hu#?^@ ]Y ]Y?+3?3+102#"'732654&#"56Ԗ{3wdmgrI?^53 }(A^+4@ ]Y  ]Y @&]Y?+2+9?3+10"''67&54$32&#">32"32654&l;6n=Z!}3shmhMaxRAAaG[t@jPj?t~FrP54HMoMG+3J=29b#0,@]Y ! +]Y$]Y?+?+99??+10"&546323673327#"&54>7#'2654&#"V KZB/6dps ,&`!]phecc´dżrfYA"b!so:Arwhlrbj+.@]Y &]Y]Y?+?+99?+?10"&5463236?>32&#"#7#'2654&#"V !x#B*@W`!]phecc´dżZV uwhlr%^ !(@]Y ]Y ]Y ?3+?+9/+10";74&"&'53 #"$54632Nf?z\LGQY&ʠύ]GY_G#&+%3^;\;h\%.P@+,,)&&_Y))_Y `Y  #_Y?+3?9/_^]33/+3+3+9310273267#"&'#"&54$%4&#"5>267 ./)B&9tuem 䇙Db{KPd:z#I\ð+zWIN۠vI~.&."8h5F=\V^^3o@B 3232]Y`Y3 +I333 I3"I3I 333$ $+]Y'$ ]Y ?+3?3+9/_^]+++]_q+3/+3+99910 54&#"'>3273267#"'#"&'532654&+7TVHP5[d} /.*A'9vs%8T=JRdk#=H##+)[W!{VJNӂ&1$!+-q\VLb1^)O@.]YO ""]Y"]Y?+?+9/_^]]_]_]_]q+910"32654&+73 54&'2#"&54>gr7zzT=+\Z~|UNlq`XFAK~j3хeJ$@ ]Y  ]Y?+?9/3+310"'5327#7333#I6:=}+^^%Cf3j*75@%]Y!]Y  2]Y +]Y?+?+99?+?3+10%#"&5463236?>32&#"#"'5326?2654&#"`_V !x#B*@W/Ņ?K3]pheccwh´dżZV uF&0EJlr3h^ *0@&%""]Y"]Y ]Y ?3+?+?+99?10%2654#""'5326?##"&54632373ZseabŅ?K 3[]W C/!ީޞF&0EujIJewb X(@]Y ]Y ]Y?+?3+9/+10"&54>32&#"327#7!P됦;y# wtp[?yS{fJFJ;-# 9J"@  ]Y?+?39910%#"&54>733>32654'3y[t+N^R ;>@,5?@ sx_;y{%urd[CdZQ4%#\7^ 0$@.]Y")]Y?+?399+310%2654'#"&5467&#"563236?>32&#"*2"q"DnYlTs*16^-b(2B1X15' !#-I89_~W#!tzkYQv{5 bYGOO7) /qfJ@]Y ?2??+910332>73#67##"&547W dc / ZcJNZ0v32&#"3>32W| d"v#C+?W'9 YeY.wݚ' {uj8p9/,*@#(]Y#]Y ]Y ??+?+9?+104#"#>32&#"3>32#"'53276yW| d"v#C+?W'9 Ye%B=>8|*7wݚ' {uj8pĮ;Y) 1@bY ]Y I I ??9/++3+3+103###7334632#"&wjm^C6^F/+79J\)@]Y@]Y   ??9/3+3+10"&5463233##";654&j}o:5wǁ%%J@#`n]f{)1`*?$".7 ]Y?+?1027#"&5473-6Wg+~~`}s7grK2s7D@%^Y  ]Y ]Y?+9?3+9/_^]+3??10!#"'532654&+7!#3ČʀΩLȵLJ ڭFXұ{NqJqJ,%@, '']Y ??3+3?33991032>73#67##"&'##"&547332>7JQy!db / Z\q{ ҀCHNwkJP^*yIrՐ9^4-@.0"-]Y'']Y?2+3?+?399?1023632#"'5327654&#"#654#"#33> q{ р$B=@7{*CHNwiRw d Dcu^sX\ī;^1>JsՏ^)vߙ'JZR3//^%$@ ]Y ]Y??+??+910!#654&#"#"&'5326733>32FLV~ !u#C->*8 Ck}Fc*?KvߙZ 6A#VV3Nf9/^'$@ ]Y ]Y?+???+910327#"&547654&#"#33>32\ZB/6drsFLV~ d Ck}Fb!umB=;c*?Kvߙ'JVV3NfA9J @  ?3?39910##367Z JsaJ1{b!X|byX%_@9]Y"II I]Y  ]Y "]Y]Y?+?+?+99?+9/+++_^]_]+10)7#"&546327!!!!!4&#"326_xڔd?!%Jslj\{re[brPӕ(w\^+-@!((]Y(! ! ]Y!?+33?+9/910%2654&#"32673#"'##"&54$32DŽɶ\w:7 NwЊ 1jiݱĜ׏{ 68QKͪ[]°`ĂbJ@  `Y?+?9?10"'73273#7#5F1'54&j I^i `R-V@  `Y?+?9?10"'73273#7#5F1'54&ʹ I^i `R-J @]Y `Y?+?9?+10"'73273327#"&54?67#5F1'54&jZB/6dps  - I^i #<'b!soD0<`R-o^@   `Y?+??9102&#"#33>F1'81ZuӴR I^h^ u}6`R- o^'@!`Y]Y`Y?+?++?9102&#"327#"&54733>F1'81ZuZA/6cqu I^h^ u}yA$b!um9@`R--^ @  ]Y??3+103>32.#"-%TO)A3(!R^@  ]Y ??+310654#"5632R hDTVa 2"mpE.9)J (@ ]Y]Y ?3?+9/+910!#!2#4&+326J\ȾwafPLJ%3#DMn9J&@  ]Y  ]Y ?+?39/+9103#!334+!26jZihÅ`NyJ3foYJLۏwL^34@1/$*$*]Y&$ ]Y]Y?+?+?3+999910#"'327#"54632654&'.54632.#"YW'a91Q- RAMz~Esk̩:5YXiIlqU-=) d%Eu(0_M7OAH^L+SC7M32&#"G:<51,H=@0EW)!Ǥvs߿1@]Y]Y ]Y ?3+?3+9/3+3103##"'5327#73>32&#"y)G:<51t,H=@0EWi'Ǥvs^(@ ]Y]Y?+?+993310327#"&547654#"5632ZB/6drsZ@16dqtb!umD;<(c!roC32&#"267#"!(yd,H=@0EW@EYygǤvs`\isRuX(@]Y@ _Y?2+3?+310"5>323#7!7!654&97Z g%}j7:{uAPRNVL/89gD"@ _Y@]Y?+?3+31027#"&547#?3!!/=Uj*j76 |tDN+QNQ)5<J"3@]Y ]Y ?3?9/33+33?+910#733!33##7##"&546267!_^\\o Zc5r=NNvi0hηjZ4RJ&@  ]Y ]Y?+?3+393103254&'7!##"&57#7!vYL?Dk"?]٣U3d^ @]Y]Y?+?+?104#"5632!"&547332676d4=K>{ ?gT&@ To|s4/CPaP<寳%.PJ ?3?310#3#.5Bq9R>iJbA>HJ@ ??3?399310%?3#47##'57##33P-  .5#>`zgA s{bL;-Js#@ ]Y ?3?3+?39310!#.5##>32&#"PH a?YQ=?DSv5HJWnca}fJ@ ??393310!#33^}nJ*@_Y_Y]Y?+?+3?+310327#"5467!7!7!!a91Q- #)c 9d%F{HELJ!@@$_Y   _Y !  _Y?3+33?+39/_^]+10!'7!7!7!3>32#72654&#"44y?)cprrFaz!XZ,3bIOeAs{Hϟu6sXC2+TkyJ\J!+O@-&]Y! ^Y    "]Y ]Y?+9?3?+99//_^]9+3+10!7!&'#"&54632654&+727&#"!N=:1)E}§ӗLjWae ۮ}KaRR4?G|CH{lB7;@@ ]Y??+39/3103>54&#"'>32mWADCMdBYr6.5>Qw@]Y ??3+9/310!.54$32.#"'~bMZ5tK} Oޖ6A+7s%@  ]Y ?3+?9/310#"'732654%w~ƇZw|}kOޖvbt)^@]Y ]Y?+3?3+10"&54632&#"3267aB53yVބpvHv5u94-*A '[@=%}Y/O*I"I.I%I?   I mY mY?+?+9/+_^]]]++]++q+10# $32%"32654&4632#"&P􆾤UI-7RF37U* ͑hL^76H`8b\= ^(z@R ]Y /I .I  )%I  I  2I .I )I "I  I 7 I 2 I ""]Y"]Y?+?+9/+++_^]++++_]+q+q+++910%2>5!";#""&54675&54632bp6쁐|:/\RPjeZNOk^AJo4b+2@"']Y"]Y+`Y ]Y?+?+39/+?+10&# 3267#7!#"&54>327>32&#"~zOut@[:?wWgOXW1zgC+4*11 7{FtTC -%&zhws5@9J'#/@ ]Y bY ]Y?]3+3?+?+1046;33##"&4632#"&267#"A!/}uC6\F/*6D5</UWJy9J\32&#"#67#'2654&#"]( !x#B*@Wc /[!Zvi`agIJfb\ZV u54&#"'>323#H!8mWADCMd#!FPBYr6.5>Q2@]Y ]Y ??3+9/39/3+310!#737.54$32.#"3#'H!#~bMZ5tK} 3!FPOޖ6A+7sb&)?@"(_Y''_Y  !]Y]Y?+?+99??+3?+310"&546323673!!!7#'2654&#"V Kcb `!]phecc-´dżrf6χwhlrBFb+.;_@5.-^Y-- --!!&]Y#!]Y6]Y/]Y??+?+99??+9?3+9/_^]+310!#7##"&546323673!#"'532654&+72654&#"9 ɔ`_V KcČʀΩLo]pheccNwh´dżrf6 ڭFXұ{lrbLu'47Ai@:! #@>_Y #A6#_Y#55_Y % /]Y(]Y?+?+99??+3?+3339/_^]+210"&546323673!3>32+'7!7#'2654&#"2654&#"V KcbprrFaz90x?9`!]phecc-XZ+!1^N´dżrf6ϟu6sXW]AswhlrBFC2-MrZqD"4H@' 4' )']Y .._Y 4]Y?+?3+3?3+99_^]99]10#!"&547#?3!632.#"254&'.547!3 yj7EB:5YXiIlqU-PEsk/{2;={uAP@QNL+SC7M32&#"27!ZG:<51 Y }j7'+H=@01)KHU2u xw@RTQNjħ߿k%Q)0AZ D3>\@3&.+._Y644]Y )@(+]Y9]Y ]Y ?3+?+?+?39/_^]99+99+310%27&54$32&#"632#"'#"&547#?3!!%"32654&}3shmgwɲij}j72UxcG[t@}BGP54 p[8xw@RTQNQ)0A17J=29@C@$488 ]Y8#2_Y%2).]Y+)]Y??3+3?3+?3+3?+910"'5327654&#"###"'53267#?>32&#"!3>32B=@7z,FLV{ e(F=>4FV.a_/J?XX a^$;n?Kxݙ'py.KDbʣ'duh|cJjī7)5@(' ""]Y%"]Y?3+?3+39999??10%32654&'.54632.#"#"&'#3lkz~Esk̩96YYgJjpW-ːaL91_M7OAH^L+TB8N:CS_?/7R7 $@ _Y _Y?+3?+3?10!!!3 b rLJχH# I@)   ?33?399//]]99322399310#333 #333-7 +-7 +PP7*@ `Y `Y?3?+99//+310!#!#!#!#3eB5A63eB5B#5#5+}^'(@  ]Y ]Y?+3???+9104#"563232>73#67##"&5476Z@16dqtLV~!eb, ZdIhc!roC<_+wTviFjX<+}^4,@,']Y,! ]Y]Y?+?+3?9?+10%#"&547654#"563232>73327#"&54?67 ZdIZ@16dqtLV~!e[A/6cpt (viFjX<(c!roC<_+w%Fb!rp@4R@  W  T?3??3910#654#"#3>32=q V_$LFAq@gx 01^R-A2nm07@  W T?3?33?3910#654#"#>32&#"632DlVa#Lq\@+7!7pgy<%^tlR~euml.Q!# !@h@V?22?]]107"'532734632#"&4=;0G­;2-+<-&6!Z~`0=0&/<,=@ V TW?22??9102&#"#3>86 ;&Z P 0p @KHs@ T VU?22?9?10"'732673#786 @![P -ks uDO@  VU?22?9?3310"'732673327#"54?B0 >!\P 3059O< cs w^,A}U!% @   V U?3?39/39103#!332654&+T>KI|L^EB7iFHK><3%@  VU?3?33993105#3>3673#@#' B}[3H[a?5@ U V?2?33?393103>3#"'5326?˰9  *Gj>=?45M*3`*21?J 0  //10"&5463"3Mex++5NeM He>++5_.C H >++5_7vt H >++5J ?/10 !wJ-J ?10 vJ+\1?\V@ p/]33103!7373+T+b{{V@ p/]23107#7!#++V{{- A@+   )`/]q3/]33]]]q22/]1073733##7{{ 7@ `/]q310!!R{\# /233103267#"&'' /.)B&:vtem szVJNuL3 ?/1077''7i`Fak^CX{{Xy{VyyV{#@  V?3?39910#"&54673673254''vaOcHXL +-ݼ.ASp`LBm6Q\Hwj4-WI+# T??10#3/}y#W U?33?3310#"'532654&'.54632.#"ît74PQ+TeS3.j?9B+PkTwz;%-71'309lHqB$7, -0++5y9\H{ X?39/310#3XdŚw%9#?29/3103# cęu=/ @?2/]393310%7% X// 1dۓ}/ @/]2?39331077-  g/dܔ}MCCjJ?BSf;?s-f'}R >+5'Z ?55s@ `/22/]10!##Tmsms@ `/33/]10#5#5msm#w; /22/1033#mTm#w; /33/10!5353wmmVc@@0 p/]]210!3!53cnsuVRVc@0 p/]]10!3!cnsVR5 4@ 0@0@0 ?22/]3]22/]q310 5!!Rdy{>!K>ijMP+}/210!!NM$O  jcP>6!S?!LB+?3210#!#GHHHOO <@$  @ H @  '  g w  /23]]q9//+]9/10!"&=73327%4632#"&?uj$A6\E-*7{jO8K\;L2u !@ x  /]q22]]10!2#4&#"?sjI[#zl-++5_Kv H >++5BB<@" ) `pH/+]2/]]2]q3/10373#7#Tᾅ{B%>@# ) `pH/+]3/]]3]q3/10##33 TDžqbj 0/10!#!uX>jgqh ?10376&"5̴>+5hV//33103!7379+T+b{{VZ/23107#7!#-+V{{-F A@+   )`/]q3/]33]]]q22/]1073733##7{{V7 0/107!V'{{V ?2/10"'532673^H3&45: BB|693x=V  ?2/10"&5463327epH=E4.5e[Q @Kzeje>+55~0:PYW >+55;9&?zD.Qp` H>++5B@ 0 `p/]310!#7!#J171b%@  0    /]3223/910#"&'#"5473326733267Ir:KNEao[3L`\3M}Ek=$#G( TBEVBESiL H>++5gK H >++5[N H>++5; H >++5R >+5PeM He>++5 H/310!7!IŃnuR>`/210!!`L/310!7!!Iw++ /3210 L9}1 /?10 #1Dy5̴>+5B@ @0 `p/]210!3!73)J//B\`-P/39/310!!37#hX)-!@0 /]3229210>32632#654&#"#654&#"ln%BpTa n"83Lc#83NnHHTT% !5BE"5BE3 ?1077''7i`Fak`DX{{Xy{VyyV{0@ 0 /10"#72654'&546?I@G#!g7.>8Yrh4#IE&Ts=@0@ H@0/+]10!7!7!7!RR8!C !v0RnW93?5|xU=@ `  /]]210327#"&54?9 %-DGU@D wJH}0/310!#7!#@J1/6Q`t H>++55R@ 0P?]10!#7#{P3{D--3@wx  /]99//3]]3]]10"''7&#"#32732673=7LI]J ,1j7:AB\D'*5lF/}8x:9%s7q3?XBd +?@&!&&&&!!))/))/3/]2/399//3]3]104632#"&4632#"&".#"#32326737=0R9.&2k=/R9-&2)JFC!+2k9,OG=+4mH4@Q1H,&3AR2F,#+#:9#+#5>JZ*w@S!!!!!!!'''$_o   /99//3]]3]]2/]99//3]]3]]10"#63232673#"&'&"#63232673#"'&'+3h9?Q#@'1i?*L%H+3i9:U#@'3h?>^H7-3-*6+.2-*63+?  /329910%377#'3?9/33107#7#5yy3% !'/7AIS]gqy@w  &$622?2O2_2?O[ooVjv~~rzB88F< QeeL` .*0*@**$2jz<`**`32#4&#"#"54632mujI[%C4\E-*7|i73#&'##567&'5s"UXh.9-ig2X)iS3BDM-@+5@  /99//331023273#".#"#>qߞ;n3kj8n&>?}'/'3 @ P /3]2]10 #&!"#6$a)oPxx3ȹt|?/9910!5 5!)!@  X Z[0Y????99//10"&54>3273#7'2>54&#"ZnZ`AOswdF5iA608b9)qt}1)PVXdofY6Ca`tP)} @ [0Y?3?9/10"&54>32#327"2654&R[adkHAMzAc8cy%)wt}\Qv{CF9wTkXE<%5@ @ HZX??+10#3'4632#"&Nu1,#&6"#+5%5#*2%/) [ 0Y??104&#"3267#"&54>32 B9ZvC>ZpXk|Zf~BPJOzn~ws!)@ 0Y X Z?2??910332673#7#"&546{[ HOz @y_rTdX8K!V^jYU/N)@ [0Y?3?310"&54632&#"327VϤfZ1Y6d~GBMbZ){.p'GN/u/) @X   [0Y??]99?10"&5432?3#7'2654#"9^i5Y 0y,l SmVy)yj2-BEA^37qބݒr!5@   0[ X?3?]910#654#"#3632FZ HN~>Ȏ5XwUe58Nwl[[-h5d% @"Z$ !X0[?22?3399?102>32#654&#"#654#"#361#+t@U[ VZ %#As?Z FGp =yXy:?\U2>j8"(2NXe\5s@ Z X0[?2?9?102&#"#36(2)%K|?xS u qZg!)Ff5 ZX?3?310>73#3) ; ɝ[){rV5 @  XZ?3?39910373#'#dᚖb¢Ln39^3?5= /10#5>73*S32#"&532#"&254&#""32654&f*Ne¿?NJR [W]I)y>`fIqS]71o6CejXRbJ #(J?-9P'QT  ?55)&QjR *&+55b^/=@ )_Y) %]Y @ H. H. ]Y ??+?99++?+?+103654&#"'6323327#"'5326?.547SJB4*$+;\;0{=<-+TyiD+7(20 +Vhlk/ZTNZb"NTmws7>BX'@mY mY?+?9/_^]3+10.54$32#"32654&1Ͱ8ҕx5fvz$^+򩑬b!X@]Y]Y?3+??+10#.546324&#"326!fk۹xgj\{rd[ "%՜Ps'@iY  mY ?3+?9/_^]+10&54$32&#";#PҘJ)`3ARH񑡳5b%^&'@# #]Y ]Y /+?399+9910#"'532654&'.54$32.#"(TijѸfIUSgwAmwwFک3lLlk>O8#1t[WG02%'Y]EN, To '@  mYmY?+?9/+9/10!#!!!#! 5!m X8ZJ )@   ]Y ]Y??+9/+9/10#!!!##PZ^?6RD)o&3@mYI  iY  mY ?+?+9/_^]++10!>54&#"'632!327#"&5467%)&*-EJm{#T')#7);Rcr 25S) !k^2h-3]&gW0geZ(4@#%$#$]Y]Y?+?+999999104#"'632%327#"&54675>B1/+IUop@N$:05-8Sn!D;R-5>N;eZ0d}Gse\((0x`9n{Bk]\@    '  @H '@6H?O_  @  T       mY??3+9_^]]]_]]3^]q2+]3233+]310!'%54''%&#"'>328Pa#ZP^NepVJjg`jF7QVHR@1  8 @ ?     `Y??+9_^]]3]233]21067'%4&''%.'51.a/Hcq|B/*Orf0-@"(* 0mY**mY%*?3+3+?3399103267332673#!7!2>7##"'##"&547+ ZX-ķ WY,0#_[~S#@o88,Za62Ubt^͢APA̭=@ )J3)@!3 '.!.]Y]Y!?3++3?33991032>73#!7!26767##"&'##"&547332>7JQy!d$}Ԯ̠%&q{ ҀCHNwkJP^*yV|l߁rDpPh(>IrՐ# +@  mY mY ???+39/+910&#"32673#67##"&54$32Z\leml-[˷i! MsÑ ^/~luJ'h`g˶+3b\ #@]Y ]Y  ???+9?+10"3273#67##"&54632&jVd-ks LX#O))ǣևv'lsl Tq#@ mY mY??9/9+/3+10!#3632#"'532654&#" 5̹δdr~qSN115ߩCN; J$@]Y `Y ?3+??9/9+10>32#"'53265#"#3f@LTWtrn^%1DNC5=/ J5F-.@&&( mY  mY ?+?399+99910"&546323267#"&54>7>54&uϩTYf8yp]]bϩz=s)-#.?Ęxv<3HjQ^g<5`{}B6JgNVm^,.@'!]Y '']Y?+?399+99910&546323267#"&54>7>54&#"ĥOws`kcmkj0kQl98X޷jZ_ƅfq*40+lAQeNNoz6)8@mY  iY ?3+3?9993+9310'.#"'632>32&#"!7 !m<2%)?K32;"@.p+=)#)2QAW#-h1jH?2Z +6{{n++ 32#"54$3 &254&#"XXӅw(H3 )VT«ZPb%vޘb1^#5]Y@I ]Y ]Y?+?3+9/++310"&54$32&#"32654&#"5632(3z\NzE;:O_VVuBV%@  mY ??3+39/3?102#654+##"#&546;3`u7-oƻu NLJqb^B@9\T!"8l7#@ ]Y?3+3??9/10!!##"#&546;3VȷyEPbJLKD'-w^%2]Y@ H$ H$ ]Y ??+?99++?+103654&#"'6323327#"&547SJB4*$+;\;0{=<-+?Jxlk/ZTNZb"NTB-7\#10@ ]Y']Y .]Y ??+?+9/+910654&#"&54632#"&'#3232654&#">O^Ȋe> 'UU{#mIuRv^tg?!ƭ:8K)RYb+ET3.R5b^F'M{b^H\TV=&R@  ?3?39/33310 ##333#6#!5 PиMJcNJ@  ??333?9/310#33#67V%P调#j46%sjj3\&F@)]Y _Y /!@H ]Y?+?9/+_^]3+3?+910#"&'!!#7#73>32%"32654&3XC j#%3ou+P/Dh^o;C5H49s#a@@!}Y/O*I"I.I%I?   I mY mY ?+3?+39/+_^]]]++]++q+10"327#"$32&4632#"&fΖIUI-7RF37)ջ<=+RHL^76H`8s$a@@"}Y/O*I"I.I%I?   I mY mY ?+3?+39/+_^]]]++]++q+10%2$54&#"5>32#"'74632#"&\ XiϕJUI-7RF37pԺ;!@QH%L^76H`8uy%@ 0  /]]9/]10#".#"#>323yXq]*km>mp|N#*#uu%,%4bf^T:uJZ`N@]Y8  ?3?3]9/+10!!#3 .'1z$ !JNTF XJS@3]Y ]Y   "I I I    ]Y]Y?+??+399//+++_^]_]++10)!#!!!!!3#qFX?uJdJJϑL++^+9C_@;!>]Yo!!!!!!! !! 3]Y:]Y,&&]Y *?22+3???+3+99/_^]q+102733>32#"&'##7#"&54$!374&#"56"32654&267#"l*yh^{Q) Ds5ozN1){KP\sXN`bJ5q0D^MN~aŰ^`GZxK~1#Lړbyqosl9DJ$K@* _Y O_Y$]Y?+?+9/_^]_]_]_]33+3310#73!23##!32654&#'32654+`)n9#N}]`vkwdžv;5cxhFAV`bX @]Y ]Y ?+3?+310"3267#"&54$32&wp|D~Jݣx@Ő3NJF;'J @ ]Y]Y?+?+10)!24&+32>q=umtJ̜|5'J g@C]Y*%I? 3I.I*Io7 I3 I]Y]Y?+?+9/++_^]+++]_q_q+3+310)#73!24&+!!32>qcihe?wEDmtՓ̜|9J C@) ]Y"II I]Y ]Y?+?+9/+++_^]_]+10)!!!!!lKAfJJϑuX&b@>_Y +I"!I"I 9 I5 I !$$_Y$ _Y ?3+?+39/++_^]]+]]+q++910#"'532654+732654&#"'>32uT]tJCQDFoHUiZf%~TC*0vgla>C(Ay:AjJ@ bY ??+103#4632#"&!KE30-E0(8J9J4(;M1#J  ]Y/+?10"'532673TU4I:6T"UF6գ95J @  ?3?39910!##3>3^^Zh39VJ6J ,@   ]Y?+?99333333103'737!;J;:{d;BZ!qVJ'du͖9HJ@   ?33?399310!#33#7sw?踑@jJ%t9yJ@  ?3?39910!###3>3 @+`sJ bZZ @ ]Y ]Y?+?+10#"&54$32%"32>54&Z VrhzsoewƮB^wX @]Y ]Y ?+3?+310%2654&#"5632#"'7wp|D~Jݢ賮y@3IF DuH  @aYaYO?99//]++10%2654&$#""$&54632qĿO1ujj^{sd[ݑuH!@aY @ H  ?99//+9+104632'654&$#"#&P63}CB3֓1qjnerqH()3@'$ aY   *)$aYO/]+?9/_^]+9910%"$&547'7632'4.#"6%327@}TgQB}R+Y$r-J"+LecloFcdlp i`6d^ 7;\".8\@83]Yo /]Y #]Y )]Y!]Y?+3?+?+99?+9/_^]q+102>32# '#"&54$!374&#"56254&#"267#" -@ńٓ[JyL-+z}MQyjuty+?\mlatkwyK~/%L):|yp0CBJ*(@((]Y"]Y?+?39/+991026?3#"&5%&54?332654&#"H]t+BP|ٗdkUhZpiV"}X}d_G}, 'eXgU]m'!X@   @]Y?+_^]210#654&#"#6632! xg!⊿EDLRs߽b'@   ]Y?+_^]210%2673#"&5473 {@QJN9J @ ]Y ]Y?+?9/+10+#!232654+XX)Je)`J}}uJ (@]Y]Y ?3?+9/+910 #&5463!##";3\mu{FG"JJ&@ _Y  ]Y ?+?39/+910#";3!"&54673x}d[{l\ͤƦsdJN$EwJ@ ]Y?+3?10!#!7!!(woJ@  ]Y?+?310!"&54733267oQn tcrJ3o1PE/'Yc73H8@ aY aY@ H  ?99//+3+33+3310753254.'%5'#"'7n xݙ'Jvj~Jj<ٴV{ e ZcJ3H$/0M@* aY%% %% aY@ H  20?99//+3+399//_^]2+3310%532654.'%5'#"'2#"&5462#"&546{k"?Kxݙ'J|cJj4@Q1H,*4@Q4E,ٴFLV}e a]R=0R9.&2=0Q=+%17X()_@;& "#"aYaY $# ## #0#`###### #@) aY /3+3?_^]q29/3+33+3310#"'%53254.'%53254.'%5'rT`dY7sԐY/wߘ'Jo| рOuiRve {J  ?2?3103673#{N (6iJwEeaJ@  ?3?3399310!#&5#367373Ӱ+03U+ c^Fk:J{o1!ߑ]J $@]Y]Y?+9?+910)7!7!!Vy;{J;@]Y  ]Y _Y?+9?3+9/_^]+310!7!#"'532654+7j}I\q)VX%*@$"]Y ]Y?3+?399+99104&#"'63232>7#"&54>7>QAO4Ɣ[(XV2[WP?hN6AFFvD35?2>K$E}O^=1adZ @  ]Y ?3+3?910327#"&'#"'73267&54632WU1H4X^|@|[W,7+1XޱAw9gz_h]λܬM;J]Y?+?10!#EȹJLJ/J  ?3?310!#&5#3/P $;eK\koJ;fJ@ ]Y?+?310!#!#!+ȹCJJ9JJ!@]Y??339/3+310!#.54733>73!hD NL be}NNVND/5q2&XPu|qi;J@]Y _Y??+?+10!#! #"'5326!V\tkc;42,>fuVX@0 IN?3?39/]10!#3#.'ˢ)3"W!HDy0@     IN0N???299//]]10!!#!!!!!#@=dEz#^13A/@ I0 N?]?]9/]]q9102#!32654+32654&+tcBSطHcr}ov^Ru{ddQV]Ob]IN?$<@$I I I$$I0N?]?]9/+++33]221023##!#7332654&+732654+Z?طbZov^Rat}u{jL?Tсb]INyZR3@ I0N??]10)32254&+3~dYو ,@  I I II 0N??9/+++]10!!!!!!=dL ,@I I IN 0I??9/+++]10!!7!!7!!{If;P%d#@ J0O??3]9/]10!#"&54$32&#"327#llF^֍7cndqLM< ˶-?A{ (@0I I I IN?3?39/+++10#!#3!3bRbעXX/Zq @ I N?33?3310!?'7!{k{jVXZF @ 0 NI??2]10"'53273HQ,A3r"ѢVT @  IN?3?39910##373\TפecPDAj@jV@@ I0N?]?103!נ}X#@   I  N?33?399]]3103#7##s\Ӣ'X^ZPf @   IN?3?399]]10##3>3ɢ٠")a4oY @ IN?3?399]]10 3#7#3Xלw5נy5 @  J0O?]?]10#"&54632%"3254&~蚹̂훳}viu)ڠӺ)Pဌ#݀F ,!@!I''O?]?39/931026?3#"&5467.54673"32654&Yv+ANН+5 O,y]]}cXkfB#|Vŧ|&VE$'BErM_tPb @ 0IN??]9/]10!##3232654&+9IR BSWXqjNF!@ 0 I N?3?]9/91032654&+ #32#ZxRbRfV휨hcGB)fHJ@ 0IN??]210#!7!! q@  I0O?]?310#"&54733267(ѭ UWlzdALX>=ISx~-#@   IN?3?3399]]3105#3673>3#\'7()9#j[n9Q@NXRli=jQ^m%(@  -  L0ON???39/]9105#"&546?654#"'632%26?y7cEfxc kM'yy hNu No6T<&o] >bAtJpa&'oce3LJ,.%(@K  "  O0L??39/]9?10>32327#"&547"7>54&0gHfxc kM'yy h7Nu No6T7+o^ >bAuIoa*$oce4LK,.@ N K L0O??99??10"&54>3273#7'2>54&#"oydmzC'.tFmB>6ewn`e2?{lT>M꧃&2:@@$.35 -ONK'"0%L?222???399/]210273>32#"'#7#"&546;754&#"56"3265427Cu/hH]n^hqC#r Xs\k VEkrzm?nA4/_F?,VHg7>dXBNbVQJE9{qQ?LॏJA)#@  LFN0O????9910"'#3632"32>54&-zB)&, OdqaAvG>A?h>5bV5p}j[?Ng^GN@N F L0O??99??10"&54>32763#7'2654&#"ixaowF =dXf;<@l@jP/eq{?Njd^@ L0O?3?9/10"&54>32+327"$54& hrrqRJgz6s?p)dX-KO=!t_ +?@ O0L?3?9/102#"&546;74&#"5>267grrqNNgz6s@o)dX-HQ= ov] ,!!3@    -  L 0 O?3?39/]_]_]910#"327#"&547&54632&#";d`_cir}Xf5_XDQ6fC=m^70]ep;u540#-!)@  !!-!!! O0 L??39/]9910254#"'632#"'532654&+7b-_C-yuod{{TZLQ[iIuAfT,3gz3CA<0:}h &!@"K!LO0 M?3??99?102>54&#""'5326?#"&54>3273DrB=@]B|z09Pg%nyludk|C' #o\?TE8&Q`s{l^#w  @ INK??]+]103##"54632^50V;(&2() @ F NK??399?103##37߿ѬP:_1iJR95Lv7# @ K" N0L?22?3399?102632#654#"#654#"#36*nae ^b NRE` IQ"Dcf`=>A;'R=Xdrh @ LKN0M????910"'53267654&#"#363254.&,5u++YF ``orvh2?#K'-iwea,Ouk L 0O??104&#"3267#"&54>32G>fMEbbueuJWԚQUЖ|@ O0L?3?3102#"'732654&#"56ᐓvW%ORmNGYn^'%̖NZ3/5 @0L?210#654&#"#>32G>Q|՗%1.5JWxѠ5 @0O?2102673#"&5473Qyқ M%{؞>*/;QUyh@ K M O0L??99??102#"'#3>"32654&exaj=;7k?vGAB]5l>mC8ncAJ?N`@ 0@K0O??321027#"&546#?33#,ET.deZu \i'R+#v PX#H=y{- @ 0O N K?2??910332673#7#"&547Rc T[ J ,pR^q/8$TLs8Gda88#@ N 0 K?323?9/321032654&'%5'#"'%P&.h4Bea1JAdb++YF*mP`o\Z$ @!N" K0O?32?3399?10"'#"&54733267332673#7^(naf ^c NRE` JQ"C5jg`B83>$RN3:"X>g=6 KN?3?310673#34 bb1^r<j3@ L0 O?32?910327#"&'#"'73267&54632/:#361B[)Hh@B35! G=afy᮰`p:GP1w/Ays}h(0@M!"I"I ""-""" O0G??9/]++9?102#"&'#>"32654&+732654&ǀݘ?a/O%E[1h%_(^o_d59ixCu?7>{b t_P`}ob>Gh@ KNM??33?310#673>3aw5  6ժi[huR\r$&!@"IYO F?3?9]3310467&54632.#"#"&2654'ׯny{=UW%;6 '\ʱ=]nVS.XtfuXp.5'"6 n`Ӟ#vNCRZhJ#@ L0OM??32?10.5467>32>54#"DzzadYB?V{pou?\Pu?L(-h8hg\mSin pdBKh%@M MK0L???9933?1023327#"&/#&#"'6ZRR - 4<5TT ) 3 2Qgd23 q^ktB9X m`wh@ RP??]10#3'4632#"&32T=&%1(9-1'[E6e^R=(USGR?5*aSMR?54W  Q?553SCR?5o#@S0O    Q?]]q?910#"'#>32%"32>54_qrS3%RX5UaP<尲iL`yL~,(.!šun5D9=,9K@'  "&&-]Y&4]Y?+???+9999//_^]33/3/310"&'##&#"#327332673#"'3>32"32654'_& C+4j9/)4lF2fY_okfbbbZC4v4C Y|cò۽hu#b*7I@&&"""" (2]Y+]Y?+?+99?99//_^]3/333/?10"&546323>7&#"#327332673#"'#7#'2654&#"V "+3k9/)4mF`!]phecc´dżIC4v4C Rwhlr5G@$*0 00--6$'$'_Y$ ]Y]Y?+?3+?3+39/99//3310"'53267&#"#32#?>32&#"3#32673#"'bF=>4FV +4j9<.a_/J?XXR)4lFv(pyB5KDbʣ'duh~4C ͽ=^*6?@J$7_Y# 9+7 --_Y!!!!@ 77&3==]Y?2+3?399399//_^]33/]+3992/_^]2+399?1023632673#&'#$'##6733>7654&#"654#" q{ р &L'lKVV_enmM"j;^ Dcu#CHEs~#a^s.OV#f "?&U%ZR3 ^1>J]) &7^#0p@B( (_Y  $_Y$$  @   $ $ .]Y ?3??+99//_^]3/]3+333/_^]+3910>73#&'&'##6733>32>54&#"/6l#}dXV[GlmU j<^ Ck}FhiF+FLe% 66v{f'& S"VV31j&!' 2?K =\*7M@)%$"" "" '2]Y+]Y?+?+99?99//_^]33/3/33?102#"'#32673#"'#7&#"#3233>"32654Y  &)4mF!5+3k9_]skfbb\ȯü14C C4vihu#o^&a@@ H ]Y  @]Y @ H   `Y?+??9/_^]3/++_^]+3/+102&#"32673#"'#&#"#3233>F1'81,)4lFOg +4j:g I^h^ 4C B5 `R-^#f@ ]Y@ H     @@" H ]Y  ? O   ]Y??3+9/_^]+3/+_^]]2/++103&#"#327>32.#"32673#"'-d #5j7#%TO)A3(7)4lFN2E !4C ^04@/'&' !!]Y ]Y?3+?3+99399310#"'532654.#"#>7&54632.#"32673˴AMz~aS_"kmN̩:5YXiTO0:mjH=E(0_MD^Lsr6IL+SCSi' . +3k9;j7R)4mF2xwKB5QN~4C OR!0AJZ@5_Y_Y  @ _Y o   _Y?+3?9/_^]+3/_^]2/++310)7&#"#32!7!32673#"'!)N%;j;?J)-9lHAJ { 3D%< 2E)^ +2@]Y&((" "]Y" ]Y ?3+?+99//99+10"326542.#"3>32#"&'##>dZsbdbǃ?K"3[]\* C0ߒݤ#lE&0|Eujǰa^ +@0 - LN?3?39/]_]]10#!#3!3yHG?b@R)#J"3@ _Y ]Y  _Y ?+9?+9/+9104>7'5!7!#"&"32654&7Ỏ1o_}w~X ?Īvwрsuf=Z@14 88]Y8,03- ,11.!)&)_Y$#&@]Y ?3?3+?3+3?3999>+910!#654#"#327#"'#7&547#?3!!333>321W| d&>Si'Z<}j7}Ti YeY.wݚ' 3N@RTQN?1t~/uj8pJ '@]Y I I ??9/++3+3103###733wjm^ )J.@  ]Y I I]Y?+?9/++3+310%27#"&5467#7333#6W!i&}$]^(zu!`C8s\%,G@(!_Y) I I- ]Y &]Y?+?+99??9/++33+331023##"'###7333>267!"!74cX2Y  dӋ^_o-k$[9\ȯ,,༉1&vi%˲LThuF\J 4@_Y I I!]Y?+?39/++33+33103#!"&5467#733!26?!o_Qn _`^Jr tJCoByC3wo/'YcJ )G@'%_Y I I*]Y!]Y?+?3+3939/++33+33103##"&547#73>7#7!!4&'7!267! r_)нdf%udY+P?o" w2(]8ElΫ99j=$1<@$!  %]Y ,]Y ]Y 0/]]+?+???+910"'5326?#"&'##33>32"32654%F5&45: 8?_& CJN2fYvi7|F_okfbbj69bZY|còhx]hu#bj"/C@']Y]Y  *]Y#]Y?+?+99?/_^]]+?+10"&5463236733#"'5326?#7#'2654&#"V K`@|lF5&54: J`!]phecc´dżcf΁y69whlr0M@,-_Y1@"(]Y""" " _Y ]Y]Y?+?3+?3+3/_^]]++10"'53267#?>32&#"3#!#"&'5326?!bF=>4FV.a_/J?XXV{k$E&44: 71(py.KDbʣ'duh;hy 693^^.:R@/+_Y<@!&]Y!!! ! 5]Y/]Y]Y?2+?+?+99?/_^]]++10"'5326?##"&54632373!#"'5326?!2654#"}Ņ?K 3[]W CVzlF5&45: 8$/ZseaF&0EujIJehy69m!ީޞ7j35@]Y]Y 0?/]]+?+9??1033#"&'5326?##3 3^)o@|l#E&44: 'QJX_.J) сy 69usfFj7%@]Y ]Y 0/]]+?+?10%3#"'5326?#3 Z?|lF5&45: eLтx699j^:F@'4(36 ]Y]Y "--]Y?2+3/_^]]+?+99?3?10236323#"&'5326?#654&#"#654#"#33> q{ рoT?|l#F&44: `CHNwiRw d Dcu^sX\΂x 69^1>JsՏ^)vߙ'JZR39j/^)7@ $$]Y$]Y ]Y 0/]]+?+???+910%3#"&'5326?#654&#"#33>32X?}l#E&44: bFLV~ d Ck}F΁y 69c*?Kvߙ'JVV3Nf=\"/A@&*]Y ]Y     #]Y?+/_^]]+?+9??102#"'5326?#"'##33>"32654rk7|lF5&45: 8AY  dP_]skfbb\ȯmx691&6vihu#jo^ 9@  ]Y]Y `Y?+/_^]]+?+9?102&#"3#"'5326?#33>F1'81ZuLZ?|lF5&45: e I^h^ u}΂x69J`R- jL^2G@'00.#)#)]Y%#]Y ]Y 0/]]+?3+?3+99999910#"'5326?#"'532654&'.54632.#"-|lF5&54: 2?AMz~Esk̩:5YXiIlqU-=Wׁy69 E(0_M7OAH^L+SC7M32&#"%V{kF5&45: 71)G:<51,H=@0EWhy69!ǤvsbjJ)@_Y ]Y  0  /]]+?3+?310%6733#"&'5326?!3qq9C=|l#F&45: = i\EՂx 69J\'jJ4@]Y ]Y  0  ?3/]]+?+9?10333#"'5326?##B8f?|lF5&45: 5ji΂x69VjJ3@_Y  _Y ]Y 0/]]+?+3?+310"&'5326?!7!7!!$E&44: )c 9|j 69{H݂xbjf^#1C@' ]Y ]Y  " +]Y$]Y?+?+99?/_^]]+?+10"&546323733327#"&5467#7#'2654&#"]* A\<F4.8Iep$C^rlYebZIJcb\L @e\&ߕ!byonbjf^bj-:3@#)]Y#5]Y.]Y]Y?+?+?+99?+10327#"&54>7##"&5463236?>32&#"2654&#"NZB/6dps ,&`_V !x#B*@W{]pheccb!so:Arwh´dżZV s]AUlrbj^&09@!*]Y**']Y]Y"]Y 0/]]+?+?+9/+10"&5467#"&54632!#3267327"32654&ep"0Bԓ"y~MQIE4.8g)Hje\ ¼VÇyL~,( @_to5D=j\6h@A#55]Y5 +I555 I5"I5I 555))/]Y+)]Y]Y 0/]]+?+?3+9/_^]+++]_q++910#"3267327#"&5467#"&54675.54632.#";uliLeGE5.8Iep"KJØLMᴼB+Kwg[}jaPV)1 @e\$sKJ'_TDKjV^5m@D$]Y$$$ $4554]Y5 +I555 I5"I5I 555 -]Y ]Y ?+3?+9/_^]+++]_q++9/_^]]+10 54&#"'>32#"'327#"&54732654&+7TVHP5[d}`bF4.8Iep NJRdk#=H##+){o1 @e\$,\+-q\VL;j5\+5S@-0_Y00,_Y ]Y )%_Y?+3/_^]]+?+9/99+3/31023327#"&547#"&54$!3654&#"5>267#"F4.8Iep sDO2!zKPd:h. I\ݾ9@- @e\$,!v4.&."wp5F9j),@bY]Y ]Y  0  /]]+?+?+10%3327#"&5467#34632#"& X9E5.8Iep#e괅C6^F/+7 @e\#J9J\32&#"327epD,0ER,H=@0EW&|F4.8je\#7l}LǤvs@qjfJ)6@!$$]Y$ ]Y]Y 0 ?2/]]+?+?+910332>733327#"&5467#7##"&547W d^9F4.8Iep$J ZcJNZ0vL@e\vi@pjJ'L@+]Y]Y_Y#]Y 0/]]+?+9?+9/_^]+310"&54632654&+7!7!#"'327jdpR|HE5.8jf[$uVrQH/ @@ K N O0L??99??102#"'#3>"32654iz_l|@).wGmBE6b}bVd2@{lX>I䩅B@ L0O?3?310"&5432&#"327tY%OSmNGYn^ '%˗NZ3/jB&#@ L"0O??39/910"''67.5432&#"632"32654|O.i/< tS%UMl\}]j@TV9&?>9G0:VJW'%ʖ\`Net \2 ?)@ $ 0OG??9/9910&'77#"&54632.''2>54&#"d/1P`/=->ÌĢFf!.3ACa7OBcvHL("hB+gb]?r噀<(B~Die/OGE[xFZ X?@  KG0???3210"'53267#?>32&#"3#s81*,,7}|$X&?0.<4XIJJ1Fz sDLAy-X+@   K0??9/3210"'53267#7333#R<-.+":^de==hh\2X?By#y>hh@ 0O M K?2??910332673#67#"&547Rc T[ JA'5sF^q/8$TLEkD=da889 -@ 0  /  NK?]?9/]321033###74632#"&!==hhHJd51T9)&2#yVy):H+;(% K0O??1027#"&5473,?yg2?);hZ% @#M  K0O?32?3399?10#"'#"&54733267332673#>=`(naf ^c NRE` JR E*.)g`B83>$RN3:"X>¯ V @ LK0 N????910654&#"#"'532673632c++Z `g]1+%#+ ``o\K'-;}by5<biwea,OAV"@ LKN0????91027#"547654&#"#3632=+.8En++YF ``on t)HH"'-iwea,Z9 C @  KN?3?39910##37VTnٞ :@&0"III - LO?3?3]9/]]+++10#"&54>32267!4&#"!bueu}IoO(H?Fn!Z|wbQUsHYc[`B'@L MG0 O?32??9/?2210#$473>54'5?D?urqq36 -#' )Tof 3,@"..+'%L M0O?]?3?3993310#"'327#"54732654&'.54632.#"Ǡ55 *,D 95z.KI2GaE~u1+d84>4>`Srw 8:w 0((*2!1)8bCex={%,,!1#6kX G??10"'5327>32&#"J927Q kD1*383Xyt NN}f` @0@K O 0M???321027#"'532?&547#?33#2AB#0/+!3Mt \i'N )#Ϧ{F} -?qH=y0& ! .@O 0 -NK?3?9/]3322?3910#733!33##7#"&546267!^^>>+@;dcI ,pR^qNy%uus8Gda-zsu$T@   K0O??32931032654&'7!##"&5467#7!SGkx86ZX׶HRXgjS_Hmo{\}饊iE{o( K0O??310!"&5463327e9ۄgcF;%e{h'3#9:!@ KO0L???104&#"5632!"&5473326?6#))8?%DɒVTHFTc&  }(y*DnLAFpw(F NK?3?310#3##/b1>`Vy '@K0N?]q9?]q910!7!7!!D^^woyV,@K N0??]q9?]q910327#"5467!7!7!!q7(,5D D^/ 9t=d^wo-y1X!'@  K! 0N?329?]99/10'7#7!7!3>32#72654&#"0o)DLNOS2IW369&#`32'267!"!654|Vua}xVo-%YOLh`{g`\R\h %m @ /329/]104632#"&%4632#"&#.'73=.R;,%154.54>54#"'632)!(!!(!#+#2,.D5h & $$ZUN?  7=   TH-@0  @ H @0/]+]2/]10"#"&547332>;%Qys>`q4(+oRT$+$_V"##%+%@/3]210!7!w%Fjm@/3]210!%7!E%ٚu@/3]210!'!IEߙm@/3]210!'%!EJNw%@/3/2]22/310%7%'#E)FÛw/3/310'%71E)FH   ?9/2102&#"#363' BU{Zi>B l j[{`j \R  /2210.'5673F,z1qKG6 gi D7/g@   ?33/329/310#.'537673%#567&'5#YXi(>*#hg2X)iSC7M-7++59=&EM$ He$$>++5s&&'z%v3R2&+5b!&F'z}vB3&+5T5&'OR&+5b&GO*&+5T&'db&GdT&'ME He>++5b&GM# He##>++5T;&'9b;&G9RTg&'K  H>++5bg&GK, H,,>++5To^&( IR &+55b &H I 0&+55To^&( JR &+55b &H J /&+55Igo&(K H>++5Ug^&HK- H-- >++5;o&(R>+5&^&HR.. >+5To>&('N;Rz{&+5b&H'zN7&+5To5&)OfR&+5^&IO{(&+5P&*M R"&+53hj&JM.&+5Vy5&+OR&+59/5&KOR$&+5Vy&+d9/&KdVy)&+jR  &+559B+&KjPT 0&+55y&+zo/&KzRVy&+N<>+59/&KN>+5A &,R>+5)&LR>+5f^&,?f) H)&++5559& !&+555T5s&.vR&+57Y&Nv{&+5T5&.d73&NdT5&.MI He>++573&NM He>++5T\&/d7&OdRT\&/'dMWm&+5&O'dRMC&+5T\&/M He>++5R7&OM He>++5Mg\&/K H>++5 g7&OK H>++5R5&0OyR&+59&PO4&+5R&0d9^&PdR5&1OR&+59/&QOJ#&+5R&1d)9/^&QdR&1M He>++59/^&QM He>++5Rg&1Ke H>++59g/^&QK$ H$$>++5^&2 HR H &++55b< &R H  &+55&2 FR7 H7&++555b<&R F 7&+555^&2 IR )&+55b! &R I )&+55^&2 JR (&+55b! &R J (&+55Ts&3v}R&+5=!&SvD+&+5T5&3OyR&+5=&SOL*&+5T5&5OR&+59o&UO&+5T&5do^&UdZT&5'dMsR$&+5j&U'dZM"&+5T&5MI He >++5[o^&UM He >++5''5&6O1R-&+5 L&VO-&+5''&6dm L^&Vd'rs&6 C7R 9&+55 }!&V CB 9&+55'J&6 DR; H;&++55 &V DR ;&+55''5&6'O1Rdm-&+5 L&V'Od5&7OFR&+5Z&WOF ###>+5&7dVZD&WdW&7M He>++5D&WMO He>++5g&7K H>++5gD&WK$ H$$>++5&8jiy>+55qfJ&Xjy>+55&8Rp"">+5WfJ&XR $$>+5g&8KW H >++5qgfJ&XK" H"">++5^&8 HR H&++55qf &X H &+55&8 ER &+555qf&X E &+555'3&9RTR&+5b&YR&+5'&9dbJ&Yd!5&:OR&&+5u&ZO&&+5&:duJ&Zd'5&;O?R&+5&[O&+5)&;jR  &+55&[j  &+555&<OR&+5?&\O!&+5s&=K/R&+5!&]K&+5&=dJ&]d&=M He >++5J&]M He >++59/&KM  He>++5Z&Wj@ ###>+55u&ZPb !&+55?&\PG &+55bfJ&DN'&+5m^&AO{ &+5B#4@iY@## mY ??+9/293?3+10 .# #32#"'532654&+sveRȴ81%wDTΝB9VOMNśO.2qzb1&~s9&+5b1&~39&+5b1&~ A&+55b1&~ 9&+55b1&~ 9&+55b1&~ 9&+55b&~ O&+55b&~ N&+55&$k?5&$v?5X'$ ?55X'$ &?55X'$ !?55X'$ #?55Xp'$@ A))>+55Xp'$ A>+55=1&1&+5=1&1&+5=1& 9&+55=1& 1&+55=1& 1&+55=1& 1&+55v'(W?5'([?5~'( ?55'( #?55~'( ?55'(  ?559/1&b$&+59/1&-$&+59/1& ,&+559/1& $&+559/1& $&+559/1& $&+559& :&+559& 9&+55v'+W?5'+[?5~!'+ ?55!'+ #?55~!'+ ?55!'+  ?55f!p'+ A >+55f!p'+ A >+55h31&&+5h1&&+5h1& #&+55h[1&~ &+55h1& &+55h1& &+55h]& 1&+55U*& 0&+55~',_?5',]?5~', ?55', #?55~%', ?55%', ?5fp', A >+55fp', A >+55b!1&RJ%&+5b!1&R)%&+5b!1&R -&+55b!1&R %&+55bA1&R %&+55b!1&R %&+55~#'2_?5#'2g+?5~-'2 !?559'2 3?55~'2 .?55 '2 +?55P1&R&+5P1&&+5P1& %&+55P1& &+55P1& &+55P1& &+55& 3&+55[& 2&+55'</]?5'<  ?55'<! ?55fp'<L A >+55b1&1&+5b1&1&+5b1& 9&+55b1&Z 1&+55b1& 1&+55b1& 1&+55b& G&+55b&f F&+55~A'v_$?5A'vg0?5~K'v %?55X'v 7?55~+'v 3?55+'v 6?55fp'v? A!!>+55fp'v? A!!>+55b&~į1&+5b&~7&+5=&])&+5=&ϟ/&+59/&&+59/& "&+5h&&+5hn&&+5b!&Rī&+5b!&R#&+5P&ĭ&+5P&&+5b&d)&+5b&/&+5b=1&~'s$J9&+5b=1&~'3$J9&+5b=1&~&$J A&+55b=1&~&$J 9&+55b=1&~&$J 9&+55b=1&~&$J 9&+55b=&~&$J O&+55b=&~&$J N&+55&$'k?5&$'v?5X`'$'L ?55X`'$'L &?55X`'$'L !?55X`'$'L #?55X`p'$'L A>+55X`p'$'L A>+55 /1&'b$$&+5 /1&'-$$&+5 /1&&$ ,&+55 /1&&$ $&+55 /1&&$ $&+55 /1&&$ $&+55 &&$ :&+55 &&$ 9&+55v'+'W?5'+'[?5~'+' ?55'+' #?55~'+' ?55'+'  ?55fp'+' A >+55fp'+' A >+55b=1&'$1&+5b=1&'$1&+5b=1&'$ 9&+55b=1&&Z$ 1&+55b=1&'$ 1&+55b=1&'$ 1&+55b=&'$ G&+55b=&&f$ F&+55~'v'_$?5'v'g0?5~ 'v' %?55 'v' 7?55~ 'v'w 3?55 'v'w 6?55f /p'v?' A!!>+55f /p'v?' A!!>+55b&~N2&+5bj&~M#3&+5b=&~&į$J1&+5b=^&~$Jb=&~&$J6&+5b&~R9&+5b=&~&R$J8&+5Z>&$N7R&+5?&$MfR&+5&$[?5&$&?5&$%1 @ /1046327>7.X60-:q?>%5.8C8jK 6,P=]$BY%?5N7Ru +5@ /$)/3333/]99//3310"#63232673#"&'&4632#"&%4632#"&}+3h9;U#@&1i?*L%H;1R9-'1p=0R>+%1V-3-(8+3AR2F,*4@R4D, /&&$&+5 /^&$ /&& $!&+59X&R!$&+5 X&'$R!6&+5'(?5i&(?5'+?5i&+}?5&+5q1 /10#&'73%46327>7.RQ*:60-9q?>%5ٙ.8C8jK 6,51 /1046327>7.673#60-9q?>%5+H?V.8C8jK 6,Zi$'@  /2/]99//3310"#63232673#"&'&4632767.+3h9;U#@%2i?*L%H/0*.=pp%1V-3-&:+%/71YjD 8+h&N&+5hj&M&+5Q9& &&+555Q9& /&+555;$&R&+5b7& ;&+555j>&,NGR&+5M&,MtR&+5',?5i',?51 /10.54632#&'73F30-PZF=%1RQ*45 *K _NTb*ҙ1 /10.54632673#F30-PZF=%10B?V45 *K _NTb*ki%'@ #/2/]99//3310"#63232673#"&'&&54632+3h9;U#@%2i?*L%H<3:*B9#)V-3-&:+(/ DAN*P&N&+5Pj&M&+5P9& (&+555P9& 1&+55531&h)&+531&)&+5P&R&+5l& =&+555>&<NR &+5&<MR &+5'<)0?5'<% ?5-'3[#?599  !@fW/33]]39/104632#"&%4632#"&#.'73=.Q;+%1:0*(;+%1@Q%F94AR2G,*3B-%2G,B?X)99!@ f W  /33]]39/10673#'4632#"&%4632#"&`Xc5T=.Q;+%1:0*(;+%1:~|9G4AR2G,*3B-%2G,{N /10#.'73JV-? U5b=&&d$)&+5b=J&$b=&'$.&+5b&R1&+5b=&'$RB&+51'20"?5s&2P?5T'v0'?5V&vj$?5&v /10673#+H5VZXL%1  /10.54632%F3/-QYF=%145 *K _NTb*+//10#+V!@   //310#'7'77'+V5555X7777  //339210'7##!'77RJ75}o}7L)  //339210##'7!)T77L}57}y5'/3107!y4'_ _  ?3210#&'7!#&'7>> /3/10#!+ToPN) /3/105!#NTP@  //]210###!V @   /33//]3310###!!!VtP @    /3/]2210!!5!###tVPN @   ?3322/10#!#!#sH%sH&sHq #/U@9!}Y}Y ''-}Y{'''T'd''+';'' ' }Y 0 @  ?]+?_^]]]]+99//++1074632#"&4632#"&4632#"&4632#"&@?=BD;=B@?=BD;=BA>=BD;=B@?=BD;=BoBIHCCIJyBIHCBIJDHHDBIJyBIHCCIJ!@  /33/]23103#5!#3###VPPN @  /3/]2210!!!##54#"&54632@i:4<>gAίv~dmNr[`w􉦨#7$!@ 0 !??9/]910>32#"&54632&#"2654#"Je8`ssؔG>;Hp#`N`u-R5A+1}kŒ2y}^.LFGS="%@   0!?3?9/]910#"'53267#"&54>32%"32654&՘F;=S}L_qP`wN`uMg@͓m{jbZ}^}nRHPIU> Q?55Ua4 Q?555UaI Q?55PR @  QR?3?39910%33# #j˸zջw{2Uag Q?55}#F@%!lY$lY lY "lY?+?3+9/+9/_^]3+310)#"&54>32&#";!!!!!bGqy/ghG|Z/ZcX<NFgϽc8:P^oru"*0>@!&-.# #iY iY   !?3/399//92+3+39107&54$?373273&'67##7&'#"&{KD28FR5.F&2uHLFA^+%6M"9O(% 2B "HI*qo@ I$9@"iY mY mY ?3+?3+99/+9102&#" 27#"$32&#"3363.'0$N9HѿJ{v <=&  RH]ɞ9T//@)+@(""]Y?3+3?339?1023632#654&#"##654#"#33> 4 Q_CHNwiHBRw d Dcu^1X\d^1>JsՏR_6vߙ'JZR3#(-e@8),,lY $&&#lY //@H ?3?99//+]q33+33339333+33339107#733333#3#####7%#3!3'#3'67##kCjk#nFon}>y@P!$R{sm;5h9P@*57+&$0+0iY-+iY mY mY  ?3?+9/+9?3+?3+99991032654&+ #! ##"'532654&'.54632&#"}[iq5 thǰr7Agj@NfJ;knGQ=KY@!rnE0w\᝴E(0_M=Y9L[G>TB=T8BN^#'+05;q@>166::4lY($ ,. .+'#lY //@H ?33?399//+]q333+3333393333+333393107#73333333#3#####7!37#37#37#3667#>7#taì˰]q;ĺup:j>32%"32654&2+332$54$#"'>ye|5eK%əSj(ocJT4!VJ>Lol)1$r% htjXrHRz@&"%-h",1@*%lY lY?+?3??+999/310#"'532654&'#.54324#">V=JBCYa`wblZM[~jR!en-km~tǫ}H#)t@E(jY@H )) iY))) )p) )) #iYjY ?/]33+33?+39/_^]q+9/_^]+33+3310#3#+##737#73! 367!!&+267!{ zIud'=HL&! R'>ل1q%pFo#on#Sc$VK@\P!&7@##mY"mY @ mY  ?33+3?39/++3107&54?3.'!+27!ԹPFE[uyj Âz~do^'II J25*!Ø$+% O@,lY lY ? ?2?99//]qq3+333933+3310##737#7!3!#3##! !'!3&uZ)D>9%HN\僨#݃h涭m0i@BlY0-lY*O  #%% mY% mY ?+3?+399//_^]q]3+33+310!327#"&547#73>7!7!654&#"'6323#-!*&u-`GrBvg~8)cf7!ÜH@(lYlY  lY_  ??9/]]+3+339/3+310!3##73 7!7!.+7!{/@$/d?8H IH3rL {bكknK| h"2K@1       p  '/0??99//]]39/103254&+#!2#%4$32#"$732$54$#" SY/^Zm+*լ֭ۢQIE^ZƬ֭+*D 04;@4($! !!!3 ?3?33?99//]]39/333?10'##3'&5#"'73254&'&54632.#"#1n^!f{P'f20Fz.r()"\&9D2?SG}H@36Per)y_,"Bwfum+- 0&YJ/J @]Y]Y?+?9/+103!7!!7!{LVJ_j'V&{u?5>9'&tu?$?5?^Cd^D 1@  p /3/]/93310#&'5673!9>HH>9)DH$HDV @   //293105673&'#DH$HDV:=HH=:#d^D 1@    p     /3//]93310&'3#67!59>HH>9#DH$HDV @    //29310%67#&'53+DH$HDV9>HH>9d^D?@%  p /3/3/]39333310#&'5673!&'3#679>HH>99>HH>9)DH$HDDH$HD(@   /2/29333105673&'67#&'5DH$HDDH$HD:=HH=:9>HH>9H0@   /2/2293333310!!5673&'67#&'5 DH$HDDH$HDhPX:=HH=:9>HH>9G\}d`@ //3993103!!^j8^@   /3/29933104>32#4&#"rуwfŠdHP @@&  P_ / ? o  /]3/]3/]3993333105!5!5!d8d7 @  /3/29933103 %! {RV-*@~Y /_/]q+9/10!!##@  /3/99310#4632#"'&'&#"}?L3% &!" @/)3 )''#i@  //3993103#"&546323265#9P3## #>/'5)%37/333105! h//93103#בK7@ /2/93310!!#'k7n7@ /3/932105!# )@ //3933103!!n@ //3932105!3  @   ///39333103!!#knn@  ///39332105!3# K7@   /32/9323105!!# kn@   //339323105!3! n (@    ///332933323105!3!!# knn6@ _/]]3_]q/_]33333105!5! BZ@  /2/39933103#3#ّiK K >@!  _ /]]2_]q//_]39333310!!!!#'kkב"7 &@   /33/39933310!###ؑ7nn# B@#   _ /]]2_]q/3/_]399333310!!#!!#iro" :@ _/]]3_]q//_]393323105!5!5!# i)ג(7 "@ /32/399332105!### ݑؑn @@"  _  /]]3_]q/3/_]399332310#!5#!5!ӑtt(F(ޑ <@    _//]3/]]3_]q93333103!!!!k#ב $@   /3/3399333103!!33B%n#n @@"      _/2/]3/]]3_]q993333103!!3!!ّJ%i# 8@  _//]3/]]3_]q93323105!5!5!3 iג) "@ /3/339933210!5!333#ؑn >@!        _/3/]3/]]3_]q99332310!5!3!3!5!#LF B@#  _ ///]]3_]q/_]3933333103!!!!#kk#ב" *@    /3/3/3993333103!!#3#Bnn K L@(   _    /3/3/]]3_]q/_]39933333310#3!!#3!!jr " # >@!  _ ///]]3_]q/_]3933323105!5!5!3# iגK &@   /3/3/3993332105!3#3# 㑑iK#K J@'    _ /3/3/]]3_]q/_]399333323103#3!5!#!5!B㑑tK (ޑ B@#   _   /]]3_]q//_]32932333105!!#5! ki"h7 (@    /322/3993323105!!### ؑnn N@)    _ /]]3_]q/3/_]3339933233310#!5!3!!#!5jtrrBޑ"ؒ @@"  _  //]3/]]33_]q932333105!3!5! BZ# (@   /3/333993323105!333! ؑnn L@(         _  /3/]3/]]33_]q399332333103!!3!5!5!B#oV@-   _  ///]]33_]q3/_]3329333332333105!5!5!3!!!!# ikkג#ב">@      /3/3/333339933333323103!!###!5!33Bؑnnnn d@4  _  /3/3/]]33_]q3/_]333993333332333103!!#!5!3!!#3!5!Bؑtr#ޑ" //3310!!V//3310!!V//3310!!V //9210!!+ //9310!!+ *fw% #'+/37;?CGKOSW[_cgkosw{@"2Jjj#3Kk.FVznn/GW{o6Nff7Og*BZ~rr+C[s :Rbb ;Sc&>^vv'?_wwcsgokcgkk`dh_[WW\XTOSKKLPHC?GG@n?o*Z+[BrCs.^/_FvGwA#&.6>JFF'/7?KG"*2:NBB#+3;OCCG@ PQӸCи@ԯ?<;87{{|4xgkosw3ccdhlpt0`OSW[_/KKLPTX\,H7;?CG+3348<@D(0#'+/' $(,$ #  KH@Ԩx`H00H`x DAGO   L/333/3339//////////33333333333333333333333333333393333333333333333333333333333333333333332333103#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#%3#73#73#73#%3#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#3#3#3#3#3#3#3#3#3#3#3#fiiiiiihhffffffffffhhiiiiZiiiiiihhffffffffffhhiiiiZiiiiiihhffffffffhhiiiiffffffhhiiiiiifffffffhhiiiiiffffhhiiiiiifffffffhhiiiiffffffhhiiiiZiiiiiihhffffffiiiiffiiiiffiiiiffiiiiffiiiiiiffbbbbbbbbbbbc^^^^^^^^^^^````````````e^^^^^^^^^^^`aaaaaaaaaaad^^^^^^^^^^^`cccccccccccb\\\\\\\\\\\bccccccccccc^```````````bbbbbbbbbbb%```````````bc^``e^`ad^`cb\bc^`b%`C%IMQUY]aeimquy}  !%)-159=AEIMQAS@zKk h| OoLldi}SsPp`"eWwTt\&a# +P;;Xxؿ)H*99]'A!/L?? ,Q<%D.55 &I+648<@DHH0M@26:>BFFA "E/22@6<@ ؾ RS@  $(,0ֹ:>Gʽɹ59ȹ48+/D*.C%)$( !@ ?| {~ osw <knrv ;jj\`dh[_cg@OSW8KNRV7JJDHCGLP@4 kKKk  ׹;?HKO@ 3"&*./333/339//////////333333333333333333333333333333293333333333333333333333333333333333333332333310!35#35#35#35#35#353353353353353353353#3#3#3#3#3#335335335335#3'#3'#3'#335335335335#373533533535!355#%355##5##5##5#353353353355##5##5##5#35335335335#3'#3'#3'#3#3'#3'#3'#335335#3'#335335#3735355#5##5#353355##5#35335#3'#3#3'#3+jjjjjjjjjjjkjkjkjmkjkjjkkkkkkkkkkkkkkjkjkjmkkkjjjjjjkjkjkjmjjkjkjmkk?kkmkkmjkjkjkkjkjkjmkkmjkjkjkkjkjkjmkkkjjjjjjmmkkkkkkjkjjjjkkjkjjkkjjWjjjjkjjkjjjjkjjkjjjjkkjjjjj!c c"a c!b!`````````````b^^^\`h^^^^^^^^cccccccb^^^^^^^^aaaaaaaa^^^^^^^^^^ccccccccb\\\\\\\\cccccccc^````````bbbbbbb bbbbbbb^^^^cccb^^^^aaaa^^^^^ccccb\\\\cccc^````bbb bbb{Z//9910!!{!!@  /2/3993310!!!7L17}1mh{//9910!!hmh{@  /3/3993310!!!hKPb//3310!!L//9910! XVRZ//910 7L//9910 LRZ//9910Z79e-)@  ! /3/39933104>32#".732>54.#"wx{yy{xwV`bcb`c``dyyyyxyy{b``bcbbbV^R'/7?GOW_gow@X\PhhTl8xx<|(pp,t ``$dHH L@@D0044DLdt|l\ JrvNvvvBz~F~~~2jn6nnZ^^RVVVVV:>>> *..v~n^V>..>V^n~v&f0b@bb"0p&/&?&&&/]]/]9///////32]32]3232q32]32]3293333333333333333333333310#"5432'#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432%#"5432#"5432'#"5432377349947575#3773865567557R75577667377349944976#58856556\677675577667667+557775555885Z557w55763:C558337775555776557737+558#  //99102#"54>jmsoujlw)@  //9933103!32>54.#")wvuwwwvu}Bwwwutww)#'@ $%  //933103!4>32#".'32>54.#")R`babbab`Nwvuwwwvu}B`bb`c``cwwwutwwsbu &@   /33/]3993310#"&546324&#"326bcfdiFIKgFEgcIN_}khfJHfFffFHdhy $0:k@%+ 6+:;<51@) H168833@ H(.""O"_"3"3"  /  /]3/399//]333+3]+29333310#"'4! 4#"32#"&54632#"&54632327#"'ybbZٗ31Z-!!--!!-+!!//!!+LL=`bHgj34V// -- // --# #-S@4( $./)--!O_-&+++@ H++  /  /]/99//+]]]32]3910#"'4! 4&#"326%4&#"326327'#"'bc0!--!0.!//!.bb>KLHgj{ -- // -- //ۺ#Fs;)5p@=3$-%-"') 3 3 67"   $'0*()/3/39/3933333333333333310373#'#5&''7&'#5367'767"32654&BAe;-VL12WXByd+N P)opÉ;'--qt>}`+%* J-d}>^1N =LJŇP'$T@+  ""%& /@/3/]99//]333339333333310.54632!!#!5!"327654&qqwTVhL%Xw{TV;>wBh}VTylFFxUVy>=TVwR,8F@#0',!66,'9:,*3$-$*/3/399/99933333310&'&54763327632".'#"&54632"32654&+ !C=#"&5467>!aFXdP{XZ[Xe`Ga^%%`]ZRw;3C@&'. 45*   .#  //99//]99233]3910)7>5'#"&54672&'&5463267632#".'Fw^69Zs\=e% qtET'iCJt8v_=1op#8wL/yuzs3B'$'ykVb'NKuu2Qi}x6fZy@   /3/99910./.54632>32bZ[K6dV'!XaXoV{eAkswwucVB   //9910&'6J{FT+t}FiRmX;.@  //9/93333103#654&'#"&54632Ll^/9r@k99}M+/dyyw {7-NsB@#      /3/933/9333310#"54632#"&54632%%5_s{N/+ىs9:wJ6.'DeQovt5-Luguf7u@E    !       `//]q/9///99933333333333310##57573%377```J`X=@`^``F\^` Z \:@iY iY  mY??+9/39/3+3+3107#733!!!!!!#7'pr?'@=1#`X꒶ݤǑJI@/]Y ]Y  /  /O??9/]39/]3+3+3103###737#7333#wlm&{{?\Z@8lY  @  lY    mY?+?9/_^]+3/_^]]]2/]+103&#"#>32332673#"'!T!.jsZ%,/l5!%^1# .Il3 A6HT9@ mY iY mY ??+9/+39/3+310!###73!2!!32654&+ŃyŢ!NJi!-ۈ9nӴzl##.@mY#mY mY ?+?+?9/+910327#"&547! #'32654&+6(/*Ou|XBϠʂ\%+-' rl&DZg{\ukVy#+>@#*+ ]Y  ]Y +]Y?+?+9+99???10#'#&546323373#7#&#"2654'̐㇍L7 A$eb^r)h\c#+rb9!Z5V8@ _Y@]Y ?3+?3+39999?10%27#"'#&547#?333#>Si'W8ڍ}j7EP4C@RTQN'}Tw9N&@ ]Y]Y/?+???+910%3##654#"#33>32sTW| dJX $ Ye}Y.wݚ'm>Auj8pT5"@   mY"??+??3910%3###373ϑrRA{51}:?}73$@  ]Y?/?+9??1033###3 3^)ݖsTBQJX_.J)%}usfF (@ "mYmY?+9?+9?10#7!7!!#z#aquT}J #@ _Y_Y?+3?+3/10#7!7!!#)coT{H}"&@mYmY?+???+9910"3267654&'2373#7##"5$j0:J˒X̓I+׿ m4Jvi. bP^@  ]Y?+?3?102&#"#3367>K,% 6>A 5L3f^4;J{0<bH)!@ iY &?3?3993?+10#33733767>32&#"#&5&SP> d6) ;'3/wVG #($-jXVh o\إ\XiX  .%?huf^'"@]Y #?3?993??+10#33>3367>32&#"#&573P-  OD#=\*kIL, G-{JPEÕ6x-^L oR&iHh$.@ _Y  _Y ??3?9/+9+910%>3##527&54632">54&GL8he#jy) 34 HV!0rDtcz4TT\1JvG 0T@ mY??9/+10!#3!5BV9bJ@ ]Y??9/+10!#3!BhbJ7b \! @]Y ]Y?+3?3?+10"&547>32'>54#"Ps~nv)zkBPEk󛈗s h|!/10'%'JII)IImmml!` /9/1034632#"&+?,+?:0,?!?#;77;6=8{ /2104632#"&ij>-+?:0->ii<67;6=8@ /3]2/]310!!4632#"&h?>,38:1-=ok;7>46=8j ?2105!53ll3; ?310 !#!b;{ / ?310 !3! }@ ' OG??3]9/10#34632#"&y`HSJ-8RF37XL^66H`7}@)   GN??3]]9/103#4632#"&y`ӶUI-7RF37PdL^66H`7{V9@ (  ??3]9/]103#4632#"&;yfӼUI-7RE38DL^76H`8  ?33210##7##1m21lGmTyT /23210!7373373y1j11lGmSSKz >+5Vg'-o?5HuV@ ~Y~Y/+9/+10!77!i:3 ?9/10 #3y5) ?10#3w}\5'7@@H  0/3]]2/329/+10673#4632#"&%4632#"&wv[%Rq;2R;,&2q<0*(=+%1刋0;2CR2G,*2C-%4E,  :@ H 0/322/]]/329/+104632#"&%4632#"&7#&'53;2R;,&2q<0*(=+%1n|S'^%2CR2G,*2C-%4E,ԁsh $@mY mY?+9?+9107! !7! #]#Crlq3 /2106$!"xyős}J3 /210 #&!a+kO3ɸ  /22310.#"#>32[k8o'ƌiY'&  /33/3103273!"&'\k;objX'' $@ /3/99993310!#!5!3d+\+ $@ /3/99993310!#!5!3d+%F $@ /3/99993310!#!5!3n!ZZ @  /3/999310!#!5!3n!DN @ /3/999310!# 7 3g\=H`3R "@ /3/9/3993310!#!7!3bH3o`RN @  /3/99993310!#73^J^sN @  /3/99993310!#5 73gRۇjh0hF@  /3/999310!# 73qZyfkFq @  /3/99993310!#73q_ZGd)5 @ /3/999310!# 7 3RkGHyN!5 "@ /3/9993310!# 7 3Rk`/L-!P "@ /3/9/3993310!#!7!3ysdH^- @  /3/99993310!# 73`wcFFH@ /3/999310!#73vq=Df-q @  /3/99993310!#73qFsGVH!% @ /3/999310!# 7 3>{GHZ1#% #@ /3/9993310!# 7 533X3'5g% #@ /3/9993310!# 7 3D{rVJ1}q' "@ /3/9/3993310!#!7!3by R1X/@  /3/999310!# 73HՇH7%q @  /3/99993310!#573q>{G\1# @  /3/39999310!# 7 36}MH)R/) $@ /3/39993310!# 7 530XAuP/j5 $@ /3/39993310!# 7 3NN-qT+o5 $@ /3/39993310!# 7%3Nu7T+}/@  /3/399310)733+NT-)q@ /3/999310!#73qB=T-)P@  /3/999310!#!'!`sboL #@ /3/9993310!# ' 3\H6fy3L #@ /3/9993310!# ' 3VH)hy?L #@ /3/9993310!#5' 3THhy-^ @ /3/999310!#' 3P5fy=Lq@ //9999310!#'3q^hy"@ //9/3999310!#!5!3qT; &@ //9/399993310!#!5!3`/h{T &@ //9/399993310!#!5!35%"@ //9/3999310!#!5!3@P@ //999310!# 7 3i``ybVL "@ //9993310!# 7 3e`;H`FF $@ //9/39993310!#!7!3gb+\t]^L @ //9993310!#73^'ezL@  //9999310!# 73e\هseRdZLq @ //9993310!#73qe^=ddZ/@ //999310!# 7 3Hu7T`H?9 "@ //9993310!# 7 53Ru9HpHF? "@ //9993310!# 7 3Mw\sX=T9H? $@ //9/39993310!#!7!3lwo V=1?@  //9999310!# 73\w߇5:=?q @ //9993310!#73qXw1-B=+5 @ //3999310!# 7 3N}5H)1q5 #@ //39993310!# 7 53N}<Au1]j5 #@ //39993310!# 7 3NN-q -+o5 #@ //39993310!# 7%3Nu7 -5@  //3999310)7331N߇ -P5q@ //9999310!#73qN3 -yL@  /3/999310!#!'!nwJ{L #@ /3/9993310!# ' 3nH J{3L #@ /3/9993310!# ' 3nH)T?J{?L #@ /3/9993310!#5 ' 3nHu^J{-L @ /3/999310!# ' 3n5RjJ{=hq@ //9999310!#'3qo;L{L@ //9999310!#'3kNf{F $@ //9/39993310!#!'!3bj\L "@ //9993310!# ' 3\H5gyFL "@ //9993310!# ' 3VHiyPL@ //999310!#' 3ifrq\%Lq @ //9993310!#'53q^g"@ //9/3999310!#!5!3XV9ZB^ &@ //9/399993310!#!5!3cf)9% &@ //9/399993310!#!5!3b-%w"@ //9/3999310!#!5!3ZV@ //999310!# 73f^ q%^`V "@ //9993310!# 7 53o^h^xX "@ //9993310!# 7 3q\3Hmg1ZV $@ //9/39993310!#!7!3bL+Vm\V@ //9999310!#73'^{^\q @ //9993310!#73qu\/3g7 @ //3999310!# 7 3=q+\=LD7 #@ //39993310!# 7 53Po+^-L i7 #@ //39993310!# 7 3Po?J?LL)7 #@ //39993310!# 7 3PqX/XLBb7@  //3999310)7331PqLb7q@ //9999310!#73qPq?L{H@  /3/999310!##'!y\1F #@ /3/9993310!# ' 3{Ny1PF #@ /3/9993310!# ' 3{Hb1F #@ /3/9993310!#5 ' 3{HtD1\F @ /3/999310!# ' 3{HX1)Fq@ //9999310!#'3q{91@  //9999310!#' 3wbL^=5 $@ //9/39993310!##'!3ws;s=9 "@ //9993310!# ' 3uHXqHETJ9 "@ //9993310!#5 ' 3uHHG}9@ //999310!# ' 3uHHG9q @ //9993310!#'53qw>K@  //9999310!#' 3[VHZdbVs @ //9993310 '53#/[{b $@ //9/39993310!#!'!3[HVbk^ "@ //9993310!# ' 3[VHB1by@ //999310!#' 3_VHLdy}Lq @ //9993310!#'3q^d+"@ //9/3999310!#!5!3LR= &@ //9/399993310!#!5!3aNA%1o5 &@ //9/399993310!#!5!3qÇ"@ //9/3999310!5!3#d+VL @ //3999310!# 73e\-yfL #@ //39993310!# 753e\ }VyfL #@ //39993310!# 7 3e\j?yf)L #@ //39993310!# 7 3e\=J3yf6P@  //3999310)7!31i\y whPq@ //9999310!#73qi\;wh/@  /3/399310!##'!l-T5 $@ /3/39993310!#%' 3H{-T75 $@ /3/39993310!# ' 3HJ-T+ $@ /3/39993310!#5 ' 3H/P% @  /3/39999310!# ' 3}H)/R))q@ //3999310!#'3qÁ)-T/@  //3999310!#' 3TH7H' "@ //39/3993310!##'!3eyT19% #@ //39993310!# ' 3{Nq}1JV% #@ //39993310!#5 ' 33h5@% @ //3999310!# ' 3{H#1Z%q @  //399993310!#'53q{Šy1H@ //3999310!#'3vfD=- @  //399993310!#' 3mwbFFcP "@ //39/3993310!#!'!3sXdH^5 #@ //39993310!# ' 3kL!-L5 @ //3999310!# ' 3kL!Ny-q @  //399993310!#'3qs!HVF@  //3999310!#' 3RZZjfyN @  //399993310!#' 53LRHh0hjN @  //399993310!#'3}^s^JR "@ //39/3993310!#!'!3bqlV`sN @ //3999310!# ' 3\H3`Fq @  //399993310!#'3qZ)d @  //3999310!#!5!3D $@ //399993310!#!5!53'ه) $@ //399993310!#!5!3sD $@ //399993310!#!5!33\+Շ\b&~ ? K&+555b&~ @ 3&+555b&~ A 3&+555b&~ B 3&+555b;&~ W 2&+555b;&~ V 2&+555b;&~ U 2&+555b;&~ T 2&+555h& ?n -&+555h$& @n &+555h& An &+555h& Bn &+555h;& WQ &+555h";& VQ &+555h;& UQ &+555h;& TQ &+555P& ? /&+555V& @ &+555P& A &+555P& B &+555P;& Wy &+555P;& Vy &+555P;& Uy &+555P;& Ty &+555h& SY*&+5555h& RY*&+5555h& QY6&+5555h& PY>&+5555P& S,&+5555P& R,&+5555P& Q8&+5555P& P@&+5555T}V -@  mY mY"?+?+??9/39/10"#33>32#"'732765445! F&׳j2EO/-yHZPO@R} F(0@   mY#mY?+?+?9/9/9/3104#"#33>32#"&547332676yԖ F^:(\?)yC_BNNHRtLq,&@ ]Y $]Y$]Y$?+?+9/+103265!#732654&#"2#"&547>=vrA!Gkau$iŲ~!`7}arv?&|װ P%@ @ H/3]3/+]10#.'73%46327>7&!!BR(E960-:t?>ZV'H8.8C8hL 6 #@ @ H/3]2/+]10!!46327>7&673#)'ٔ60-9t?>Z+H?VZ.8C8hL 6Zi P!@ @ H/3]2/+]10!!.54632#&'73)'#F3/-QYF=%1RQ*Z54 *L _NTb*ҙ !@ @ H/3]2/+]10!!.54632673#)'LF3/-QYF=%1+H?VZ54 *L _NTb*Zi;! @ @/9/3/104632#"&>73#<0R9-&23-(Hqb4AR1H,D7E34H E@)IY8o@p  /]q]2]2]]9]/39/104632#"&3673#&'`<0R9-&2m[Qlu+?(X4AR1H, Jq`[&anT@ /3]2/]33310!!4632#"&%4632#"&'H<0R9-&2q:2*(=+&0`4AR1H,*3B-%4E,o ,-@"(0@(($/99//3]2233104632#"&%4632#"&"#6323273#".<1R9.&2q<1*(=,%1'2i=/G<8 K4iR-D;9X4AR1H,*4A-%4E,);%en|$bG,b@o -@ 0@  /99//333/]q10"#6323273#".7>73#'2i=/G<8 K4iR-D;96i-"Vr;);%en|$?|E,<# @   /3]3/]10#.'53!!n6s&H(q':CDDs9  @   /3]3/]10673#!!bdh-Br'u:-X?%S@5@p` p    6'#/3]]]22/]]q]]q29]/9/103673#&/4632#"&%4632#"&lD^z|tz"[+0;2R9-'2q;2*(=,%1?6mM,_B2CR1H,*2C-%4E,9(@ @0 /3]]2/910!!4632#"&%4632#"&'1:2R9-'1p;2*(=,%11DR1H,*2C-%4E,u3@0/?   @ H /+99//33]]10"#6323273#".!!+2i=/G<8 K4hR-D<9y;13%em}$u#@0_o /3/]]10!!4632#"&'{E4^F/*8:I\%1+H?Vu822870: )L `MTb*Zi); &5@ #@    w  /3]]]]22/]10!"&'33267.54632#&'73Ao NYU^F3/-QYF=%1RZ!u8228745 )L `MTb*Ҫ); $5@ @$$$   w  /3]]]]22/]10!"&'3326746327>7.673#Ao NYU^70-9q?>%5(J?Vu8228%.8C7kL 6,Si); $5@ @   w  /3]]]]22/]10!"&'3326?#&'73%46327>7.Ao NYU^;RQ*970-9q?>%5u82283.8C7kL 6,+@mY mY ?3?+?+9?10!##! #"'532>!3%TvrL343Qh^y 3`W /HhT$9@"! ]Y `Y?+?+9933??3910"#"'532>7>323##&L^OU[nc2");XQE0TpxARB8ۮ;W FˉʅAnV5T-@ iY  mY ??3?+9/99+3103 #!##!232654&+y)y5Jۈ9*(HŊ޴zlT\'3@   "]Y  ]Y?+??+93??31023 ##"'##33>"32654F#AY  dP_]skfbb\ȯ㼉1&6vihu#K@ mY mYmY-@I mY?+??99//+_^]++9+310)##&54$)!!!!#";5#` q1^g8wx^3;E^@3?)]Y "??""<]Y"99]Y994]Y]Y1-]Y?+3??+?+9/+9?+9/99+10"'##"#"'5326767.5463!632!#3267";%"32654&La]7M<):N`C2"4F.Hs_`w#z~LR`nvѴLg)I{!FUx\, F`*]i}yL~,(,#g[fun5DT7@     ?3?391073' ##3'7wF5ouX%+:xY9DJ@ ?3?3910##3'773Ӯnrqybo}bٮ#JKP/+4@mY**mY* %mY  mY /3+??+?+9/+10632#"'532654&#"#! #"'532>!@I ˜s}yـ\%vrL343Qh^/53`W  T5B@$/ "- -*3]Y]Y3%*`Y% `Y ?3+??+?9/++993310632#"&'53254&#"#&#"#"'532>7>32L?́D^+*Z>zrL3b%)M^RQTqe2"):VOJI_kcvjĬ#$싞3>Z Bֲb/##?@ mY mY @I   " mY /3+?3?399//_^]+++10632#"'532654&#"#!#3!3#@K ˜s}y{\%}6/5Vj9 J$E@*]Y!]Y!!"I!! I!!# `Y ?3+?3?399//+_^]+]++10632#"&'53254&#"#!#3!3L?́D],*[>|oL3cg9h^cjĬ##펛3J7T{ @" mY  mY?+?+??10%##!#!vQ}59F^&@ ]Y]Y/?+???+910%##654&#"#33>32FsRFLV~ d Ck}Fo}c*?Kvߙ'JVV3NfR%@ mY  mY" ????+9/+104&#"#36323##6S]Z5|ޡ TwRw GI!/9Z@/u/.9F&@ ]Y]Y/?+???+910%##654#"#33>32FsRW| dJX $ Yep}Y.wݚ'm>Auj+e@    /322/10#'##'##'7v!H!G#+ ggggTU@6_o   sY??+?9/////_^]]9910%%2>73!"&'??3 2#1-A~32!Ps FFEFtu=G@"G.;@s{1#G/@}G0@G@#G@ ^ ^  v L &  "  D. *r ( \ >  <^  40Copyright 2012 Google Inc. All Rights Reserved.Noto Sans UIItalicMonotype Imaging - Noto Sans UI ItalicNoto Sans UI ItalicVersion 1.06NotoSansUI-ItalicNoto is a trademark of Google Inc.Monotype Imaging Inc.Monotype Design TeamData hinted. Designed by Monotype design team.http://www.google.com/get/noto/http://www.monotype.com/studioThis Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.http://scripts.sil.org/OFLff  34566778:;YZ[\_`ders}~NOGHTU > ? W X o 45Z[  `bddss!#$$%%&()+,-/1256689::BNHIJJKQRRSSTT Ncyrlgrek$latn4 kern8kern>kernDmarkJmarkPmarkfmkmk|mkmkmkmk        *:JZjz  & ' *h ,J A Rt S `( ` d  g\h dF`bddss  %(3!-:/6G8:OBNRHT_%$=D]4KBj77NNSV\_27 BI^alq~!WYZkl""(UZW]f]kngqk{QQbhssvvl $*06<<BBHNTTTTZTTTTTTTTTTTTTTTTT`fTTTll rxTx`TT~TT*TTTTTTTT &,28>DJPJPV\bhntz,J nt,"(.4:@:FLRX^@djpv| ^p@ <$*06*<BH0N$TZ`fl$Tr$Tx~v&,PPPP88>Dntntntnt:P@@@ &@,@28@v>DpvJJJPV\VbVbhbntz    , X zzz&d&@@\2|\\P\ >Dn 8>D PPP"(.4P&\:@F\D@L\RX@V\^d@ntjphntv|8,X@ $*06<B  H NTZ`flr0Jx$~ 0<64r B~0 B0ntXntntXt ntXn&N,2.48>lD:J&PV:\LRbLRhnLRhtzJ.4>l2.4>l.4>l:&x0tNj @J"( P.PP4:@FzLRX^djpv n|Nj:$&xx:&x BXj&,24j8DXj|8>D :L  rj$*0f6<BHNT@Zlr4`flPrf*x&2X~j@<4@zB8z P00"$FLR~n|  &,28>DJPV\bhntz&@z t&~rp  "(@n.P4n.P:@FLRnX^djpv|b :n@@:(PPPP&|  Dr :Dl0$t*06t<0BHNTZ`6tflrxt~F~@Jn, 0xB &, 286~r>DJPVJ\Dntbhntzp0|JD$0$xPX@&jX&jp|$|||~  p@$&N$6< HZ"(.4(.44PP:@&vFDLxRXJX$~$~b^djX@@6pvpv|pvBNjPrr*xrZ~n~n~~>J0zXpn|@6$0>  T     $ * 0 60 < B H N T ZN `N `j f$~0 l<B<~n r~ x ~p (  Nj   p@P   f  $~, l~, $ $ ~n ""  , $ X~  N n !d!j!d!!!!!! >!& 8>!,d8>!,d8>!2!88>!>!DJJ!J!P!V\:!\@nt!bhn!hnthn!nPhn|z!t6!z!,"!@,!!,!!!!!!!8!!!!!!!!F!F!L!!! !!!~0J!,$~!J!,$~!V!<!6"4" <"""""r"(4!"."!J"4":0 "@B"F B"L  B"Rn"XNn"^N n"^N "d,""j&Z,"p&"v"|.4">l(."`>"":@"&r!J:@"&r":"j&x"dLR""FL"~n"FL"~n"! ""l""x"@"X"TP!J"z" &<"#D:~:OE~< LfdtL~~~(<\L  X44XG|FDD|G P <ll:g+:}j0II0vhv\Xh8DDhx8hQh,lh|lLAvNQ hbl0~Rllplp4lHLl@bllx@\D@D@@@@v@v @$lll,nn4X, :,: l D,Xl<nH8>l\lfv,<T@l@,@lT,G<<,|pnl<<Dh<|h<,~<<n4`,40< <,pX,,H@,> ^> 0 v vLxu<<,l,llIv0,\hP<%@ @l@|T@@$(  lP D ^NP @\ <P  vLPv jPXlvbHh`h|,[|,l vx0,hTtN!!5b&5D n,N@0@Fp<f 8p$,@ ,X@,*,@J{55II,*@<g q&r0@X,l?l,,@D\,j,@l  (,b@(,"\"n,,hMpjplfllhhlXlh%\ p\ThnZ%Xpphkzd~r,TG0,o,$ms| <T,h@@jVhhhnJDddVt]ItE R#Nrlpp@ppp;I|pp~pp|HHHH&vTlDD>>DpplHRlapp@NXplhpl,1pvX@@4vl,ThTppllKlbl@%ddK 0 Pb`aHHNTHNTZ`flrx~vl0l\ll: zb+D,,,Xh $=D]4;CHNOPQ?ARCCUGGVJKWMMYSSZWW[YY\de]xy_{{ab &,28>>>DJPV\bDhnntzhJDDDDDDD\\\\nnnnnDJJn`~u~uj5&F~B~x`~JH-)pH@JpJT JJJJJfJ<J6hH* ^J,JrJJ X^ $=D]4679;<*+>FI@^aDH "(.4:@"(FFLRX(^("djp"v|v^^^^^7xx!wJxdxyX8$*8J\#apMJJ@R%FJJJ`J@J ;A$=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0   1      & , 2 8 > D J P V \ b h n t z  "(.4:@FLFLRXFL^djpv|            P P P P                    "(.4.4.4.4FLFLFLFL$*jpjpjpjpjpjp       "( "( "( "(    .4 .4 .4  .4 .40 80 80 8 2 86 D< > D PFL PFL PFL J PFL PFLBHNTZ \FL RXRX` hfL b hFL b hFL b hlr b hFL  z  x~ z  jp jp jp v| v| v|                    FLFL t^d                    .4 .4 .4 4 4 4 4 .4 PFL J PFL jp jp p p p p jp           &&&&    ,2 8>   "(  DJPV\b  hntz & ,0 8  J P RXFL z    \b   tz"(.4: @FLRX^djpv|T   PFL jp  2 8 0 8 X jp jp FLRX^0 8      .4 .4 PFL PFL jp jp v| v|  tz b h6 D<     .4 jp    $*$*  "( b h 06<B  .4 V \FL v|   "(HNTZ`flrx~FLFL^d^d^djpv|v|v|v|:@:@ & &,2,2FL,2jp 8>DJPVPVPV\b hntzFLRXPVPV.4$*:@^dv|:@ FLjp"RXFL^dv|(.  .4`flrFL"(,24:@    "(          4 4  .4  .4 .4F ,L@0 86 D< > D6 D< > D > D J PFL PfL X RX RX b hFL` hfL b hFL b hFL t^d n t^d  z  z  z  p p p pR R  v| v| v| v|      X       < <^    d d    Xjp v4:8> b hFL b h  |<JJ(JVVmmqq//  jjjj77??//uu00000V000j00m0q00!,!/000``//0 000?0DDhh0h0h0h000\\ooJJNN}}  ``##//XXRR;;==jj 0 11!0;0 //  33}}hhmmXXZZ}}``NNPP--??TT``RR++HH33ooJJXX;;00000j070hhD0oo $=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0   1      & , 2 8 > D J P V \ b h n h t z h h h h h h      > > > > V & b b b b b b n t t t t h h h h h h b b b n n n n h h t t t t t h h h h h h             h  h  h  h , , , 2 2 2 2 8 8 8 > h > h > h > h > h > h J V V \ \ \  J J J V  b  b   b b b b b b b b b b b b t t t t t t t t    b  b  b  b  b > h > h           V V V 8 h  & h " , n ( . h 4 : @ D F L  R X  h ^ d j h , 2 p v | 8  4   \  @    &         b  > h  2 h      h    h b b t t   , , > h > h @  h  h   \ b t V     n  8   > t   h , V  b b h n  h h   $ * 0 6 < h h B H h h h L N T Z ` f h h l r x ~  z z      h          <    h       R     t   h h z h h z h  B  L h  h h  h h  b b h t * 0  n  h  h h h n h h h h h t t t t t z h h h h h h          h  h  h  h & h & h , , , , 2 2 2 2 2 8 8 8 8 > h > h > h > h > h D D J J P P V \ \ \ h b  "   & , b   8J0000V000m0q00/0000000 00j0j00700?0000000000/0u00000000000!0`0/000D0h0000\0000o0J0N00}000 0`0#0/0X0R00;00=00j0 00100000000/0 000000300}00h0000m0X00Z000000}00`0N0P000-0?00T0`00R0+0H0030o00000J00X0;000o0 Z`"$&(*,./125678DFHJLNOQRUVWXL"LRX^djXpvj|Xd Bv&le_^\Z! ^ $=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0j1jpv| $*06<jjjjjjBH|NTZ<<jj`fvvvv||lrx~ ***00< < jjjjjjjjjjjjj <<<&,2p8>v|DJPV\b6hntz0<0jBT    jj   "j ( .< 4 : @ F Fjv L R X ^pj d j< p v | | | **J J >$ 6          4    | $ *J 0 6 <$  | B H N T Z ` f l r x*0 ~   @    p   HZ|     &   , , 2  8 > D J P V P V \ b h n t r  z z  * ** 0 , 6 < 0<  | .& /,2x` fDX2HH(H\sxHAHHpppph7pp8HHPbppppp pHHXHJHlN6 H ):X_HH}H`DHp! xxHJ3D|0DpDpp X:pph@|phpl\Hp^& 6@EGIKMQSZ ]]4:6<BHNTZ`flr6x~BVV>V-VnxlpVnVVV=VXV'VV-VV p`bss!#%% )+!/1$66'::(BN)HI6KQ8SS?`bss!#%% )+!/1$66'::(BN)HI6KQ8SS?@             &&,22288>@BBHNTZZ`flrx~~~~xr~xP O80t`t`TLh@!,Xhh<,(,,(dthFhF@<<<0x(F(dP<d(@,< djdd  $$&(,- 25"89&JJ(RR)TT*dd  $$&(,- 25"89&JJ(RR)TT*+    $*06<0 6000BHN6*00HTZ666666* + $*00~t`VV4\4\H 6fl$$*D)`dos""PEKNO<-2"FEKNOFtsdtdpqtod    xzz    dPP    J   H:TfTx:::TTv       J::::::TTTTTTTTTT : : :  T T T T T:   :  v v HHH :  $$$Bl l $n%8 %J%h ff%f%f &&X&^ &h&n&' &'P&h &h((&X&&n)&h'P'P (( *h**%+F*h+F *+` +F**+r+r %+x*+r+r+x+F+`&* PPP+ :+,&%(,0,n+r&X&,&,.. . ( &n,0&n,0+`+`( ( (,0&X'P*,n+r( /:0<0^011 *h11&h+F&h+F&h+F'P*/:*262p3 &X&*333311'P*'P*+F3%%&h+F4F4'P*'P*'P*& & %%( ( &h+F *h5>( ( ( ( &n+`&n+`&n+`&*/:0<11&h133'P*335T56L6~'P* : : : : : : : : : : : : T T T T T T T T T T +++++,&,&,&,&,& v$ $)./ 247>DFHIKKNNPS UU$WW%Y\&^^*+78=DJNORST^_`adnostwxyz{|}~$(**,,..0022446;==??CEGGVV[_abddffhimmooqvxy{|~~      !! PQ UV]]__ggimosuuwwy0O[]^_egimnx   AFJLLNNPPRRUUWWYY[[]^cceeggiikq~//k  6 I 45Z[ij{~   ghiknoprtv{|   qrxQfms*9:<68:V_bfimsvyz{~ !lnp$C U "$&(*,.0$~~$;C}~ ~U "$&(*,.0$C U "$&(*,.0  }  p"$8=DJPQSUX *+,-./012345;=?CDF TUV^_ "#$%&'()*+,-./01^_`abcdefghijkt"$8=DJPQSUVX !*+,-./012345;=?CDFJ TUV^_ "#$%&'()*+,-./01^_`abcdefghijk= opwy}  z  ky  < YZ[\7 jy}~    m     J< FGHRTV!HJ ]3579=?AGIKOQSUWY[]=F2G2H2R2T2222222222222222222222222222H22222]22222232527292=2?2A2G2I2K2O2Q2S2U2W2Y2[2]2  y}  1 &*247$&G| \xFHJLNPRTVXZ\~  ( ( F@F`F( (O$&*247;$&CG} U\x "$&(*,.0FHJLNPRTVXZ\~5 YZ[\]7<>@ y}    mt gijkoptz{|~    t2  =F2G2H2R2T2222222222222222222222222222H2((((]2(((((32527292=2?2A2G2I2K2O2Q2S2U2W2Y2[2]2"$8=DFGHJPQRSTUVX !*+,-./012345;=?CDFHJ TUV]^_x "#$%&'()*+,-./013579=?AGIKOQSUWY[]^_`abcdefghijkfmV_bdipqrtux Q~~V_bi~ ~ V_birx Q V_bfim _&*247$&Gfmqrsux|Q\kxy}    FHJLNPRTVXZ\~  $&*247 89:<=DIJPQSUX $ & *+,-./01234568:;=?CDFGV_biy~45TUVZ[\^_iopx{ "#$%&'()*+,-./01FHJLNPRTVXZ\^_`abcdefghijklnp~ 29:<68:V_bfimsvyz{~ !Plnp y~ y~  py~ k     !~~~ ~op  oyU22222222222222222222 h2kn2opr2sv2222222222222222222 p'  jz~  7 opw}  qx   j~      < << <-Z   ( (8=J*,.024;=?( (^^`bdfhj   p22  j~  pI]<>@ 45Z[hiknoprtv{    p op@  oy}    }  %  y}    )  y} t  t  ( ("( (   j~  }  )  jy}~    *  y}    V_bi  o}  j~  +  oy}   y~0 &*-247$&G \xFHJLNPRTVXZ\~  fmqrsux Q-< Lcyrlgrek"latn0aalt2ccmp8ccmpBccmpLligaVrtlm\rtlmbrtlmh 0@P`ph< T"*2:BJRZbjpx&.6<DLT\djrz^fnv~ (0 $*2:BJRX`hpx$,4<DLRZbj                           LMW`bss!#%% )+!/1$66'::(BN)HI6KQ8SS? (B~FPZdnx  (2<FP ` a ` a ` a !` "a #` $a %` &a '` (a )` *a 3` 4a 5` 6a +` ,a -` .a /` 0a 1` 2a 7` 8a 9` :a Y G k l m j n o '*,./0I 4L5OZIL[IO  ; < = > V 7LMWlinphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/NotoSansUI-Regular.ttf000066400000000000000000011253401434616504300311770ustar00rootroot00000000000000GDEF4((GPOSGSUBF # `OS/2SC`cmapO'hcvt 7fpgm6  . gasp#glyfL^dhead[x6hhea OT$hmtx=x%loca"8%maxp x name۴}postif prepf5\)_< 4( b   o pRT`\K33f GOOG@X J 'D+3dmfRf=hTf%TR%db\+qZdf%%?fsfy1w3}s'}R/`1B?}?}dhsqN3N}^qqqqF{qqNh!J;%P 9h Bf'Byhy1dBRfRdm{f1#=q%LBPRGR.R y3 }ssss<R<=?}?}?}?}?}?} }^}^}^}^}^}^^qqqqqqqqqqqfq}^}^}^}q}q}q}qq=qsqsqsqsqsq}q}q}q}q,R3RR/`FF11\111?}q?}q?}qm}oNNbN~dhhdhhdhhdhhs!s!s!qJNPNPNP}^ ^?}qdhh+m#%31)sN?{RBqH?}JsohomBN<qXq % oXqqFV`oqFqqqhBsqBss1%}dhR</`}31 sN%%B?}}sohLT#=j-}^uw'qD%%;qq)o;%/?%9;y!qwqh;B}qJqJqJRRRJff?fT{T jdD{R{P y-m\D5/)o)/#BNfRER#RGRfb) Hfd%w `fffjo{=93Vy%TTf B{}^ zsF}q=dM1s%q%'f`wo= =omB}q!! }q}Fq{o'%{qhu))HF/=/y5D;^ND\uR/F% B;}#q}qs) j%)7R-7R-R{ LV R}^}^ ^sqyhyhNDH%%%%?}qB}qB}q=;=w9=/yf%;%q71o=HyNHLNV}#q?)mX}^}^}^}%}^}^}^}^}^}^}^}^sqsqsqsqsN?sqsqsqR{R?}q?}q?}q?}T?}q?}q?}qF}qF}qF}qF}qF}q=d=d=d=d=dqjjfqqqf-s!?}q={hqos{yo'}yFFV=B}}q{d`\Jy!!sBLVNPHqF9bHDJ!!!' V q`B!D%}^?}qh}^}^ ^}q}qF?}q?}qH V q}qJ}^}^sqsq1?}q?}qN#NNq^wqNP}^sq?}q?}q?}q?}q qq}q1shP}3sq/`#}qN}qD?bqqhhhXD3DqoqqJ B{{{qqBsoNNNNN%yyh-!=JPVPhh5hq?}\q=F{qhh5qqBq!!!L11'sfff5+5+PP!H3H3H'H++!!5+5+oBj+DXXXXXXXJhh%}}}})))V dfdf33LL-))) ~9Y}5Yj5}/}W~BW BF%T=TT'w=TTBBTTT )))DqD%?fo3}q}qqFsb+f={dsq5^d`31)T!}qs)3qqB}q;}B?}??}qqJ7hJ-yJV3+y-H33jhuyy7171+NLNN)VDd !5m?hqqodqy;TDbqmq'h%yZyh'bh}h}bXHfRhFhh\1X-;fbhh-;bhhqq{NF%V! q=oFR{NRhd;%P}^qqqXD1hDDhNZhHD/f'H!N!Nhbfb\1'D-RR^R-h}BBZT333}qqqqqqsqsqsqsqsq'}qZ>)FFF1111B{B{?}q?}q?}q?}qNNNNdhhdhhdhhdhhdhhs!s!s!s!qJqJ;%;%NPNPNP!J}^qqqqqqqqXXXXXX%VL##8qqqqqqBsBsBsBsBsBsBsBsqqXXDqqBsBsqqqqqqqq d X   w w BsBsBsBsBsBsBsBs {      w wqqqqqqq!?h,+hBsBsBsBsBs N!LRh N%')#yZ}hhL'}bJds{\'1}d}"d =R.R1-F y%dj#f{mmb))s+jVFR?;?;fBhf11}^!  7yNP#}%oDq!'''%f'' +VVVVVNVRVNVNVFFV5V5VPV-VH-V%V%V%V'V/%VVV5V5V/)VPVLVLVLV^LVVVVVPVLVFVLVLLV/V9V?V?V??V5V5V5V5V55VLVLVLVLVLhVLVFVLVLVLLVVVVVVVVVXVVVV\V7V7V7V7V77VHVFVFVFVFFVVV9V9V99VVVVVLVVVVVLVLVLVLVPPV/V5V5VV)V/V'V%V%V%%VHV-VPV5V5-VFVNVNVRVNFVVVVqqqqqqqq 3j3j3j3jd3^3^3j3j3^3^3^3^ysD- !;%#)u`f# \`` ~ac67"#ou~  OP\_'=?EMWY[]}  " & / 0 4 : < > D ^ p y  !!!!"!&!.!N!T!^!!!"""""""")"+"H"a"e###!%%% %%%%%$%,%4%<%l%%%%%%%%%%%%%%%&<&@&B&`&c&f&k&o),m,w.!# bd78#$tz PQ]`>@ HPY[]_  & * 0 2 9 < > D ^ j t  !!!!"!&!.!M!S![!!!"""""""")"+"H"`"d### %%% %%%%%$%,%4%<%P%%%%%%%%%%%%%%%&:&@&B&`&c&e&j&o),`,q. ^IvedAeY @SQzKIzx?wZI we%" {ob`aa` kLP>:6&$&8F0(.@@<"$ IJ$%KLM`abVPQWXYRh X Y Z [ \ ]STUV     ;<=>r i,-03PQ45Z[@G[ZYXUTSRQPONMLKJIHGFEDCBA@?>=<;:9876510/.-,('&%$#"! , `E% Fa#E#aH-, EhD-,E#F` a F`&#HH-,E#F#a ` &a a&#HH-,E#F`@a f`&#HH-,E#F#a@` &a@a&#HH-, <<-, E# D# ZQX# D#Y QX# MD#Y &QX# D#Y!!-, EhD ` EFvhE`D-, C#Ce -, C#C -,(#p(>(#p(E: -, E%EadPQXED!!Y-,I#D-, EC`D-,CCe -, i@a ,b`+ d#da\XaY-,E+)#D)z-,Ee,#DE+#D-,KRXED!!Y-,KQXED!!Y-,%# `#-,%# a#-,%-,CRX!!!!!F#F`F# F`ab# # pE` PXaFY`h:Y-, E%FRKQ[X%F ha%%?#!8!Y-, E%FPX%F ha%%?#!8!Y-,CC -, %EPX ED!!EDY-,!QX d#d b@/+Y`-,!QX d#dUb/+Y`-, d#d@b`#!-,KSX%Id#Ei@ab aj#D#!# 9/Y-,KSX %Idi &%Id#ab aj#D&#D#D& 9# 9//Y-,E#E`#E`#E`#vhb -,H+-, ETX@D E@aD!!Y-,E0/E#Ea``iD-,KQX/#p#B!!Y-,KQX %EiSXD!!Y!!Y-,EC`c`iD-,/ED-,E# E`D-,E#E`D-,K#QX34 34YDD-,CX&EXdf`d `f X!@YaY#XeY)#D#)!!!!!Y-,CTXKS#KQZX8!!Y!!!!Y-,CX%Ed `f X!@Ya#XeY)#D%% XY%% F%#B<%%%% F%`#B< XY%%)) EeD%%)%% XY%%CH%%%%`CH!Y!!!!!!!-,% F%#B%%EH!!!!-,% %%CH!!!-,E# E P X#e#Y#h @PX!@Y#XeY`D-,KS#KQZX E`D!!Y-,KTX E`D!!Y-,KS#KQZX8!!Y-,!KTX8!!Y-,CTXF+!!!!Y-,CTXG+!!!Y-, T#T[XCPCT[X!!!!H+YCPCT[XH+!!!!YY-, T#T[XCPCT[X!!!I+YCPCT[XI+!!!YY-, #KSKQZX#8!!Y-,%%Ij SX@`8!!Y-,%%Ij QX@a8!!Y-, #Id#SX<!Y-,KRX}zY-,KKTB-,B#Q@SZXB TXC`BY$QX @TXC`B$TX C`BKKRXC`BY@TXC`BY@cTXC`BY@cTXC`BY&QX@cTX@C`BY@cTXC`BYYYYYYCTXBY-,Eh#KQX# E d@PX|Yh`YD-,%%#>#> #eB #B#?#? #eB#B-,CPCT[X!# Y-,Y+-,-@ !3 U     U3U?[PZU?ZOZZXUYPXU0X@XPXXWPVU VVVTUUPTUpTT0T@TTTT0MMNUGdFU?FFFKUJPIUIKUOPNU3NNKULPKUKK?KKSPRU;RRPUQPPU7$~adX}wsvsAus2ts2sss3U3Umili&ki=jiHiZ&&H&H&&&###3@mUU3U?d]4xcb]#a]3`]*_]*^]3]]]]dU3U3UOUdUoTS++KRK P[%S@QZUZ[XYBK2SX`YKdSX@YKSXBYss++^stu+++st+++++s+st+++ssttt+++++++stu++++s+s++s+st++s++++s++sstt++st+s++s+st++ssst+^ PuHjJ3  ` @5 }jHHHHnP8z0  v  "  <`pB:N*|(bX H(n* !!"d"#$$$%%%&b&'X(())*B*++,,,--./ / /|0*012x234D5B56h6678,899:B:;;r;<">??>?n?@xA6A\AAABBLBzBBCCD D8DhDDE<F"FRFFFGGHtHHHI I2IZJJJJKK@KhKKKLLMM<MbMMNLO OFOjOOOPPPQQ@QfQQQRR4RXRRRRS.STSdTXTTTUU4UZUUUUV(VLVzVVVWW>WnWXVY"YPYvYYZZ(ZNZrZZZ[[L[|[[\>\l\\\]]6]\]^^h^^^__6_Z_```aa8a^aabcccddDdtdddee@eheeeff,fZf~ggghhFhjhhhi iRixiiijjJjnjjjk"kJkxkklmoo>odoooppbppq^qr*rrsrstXttttu$uPuuuuuv6vFvVvfvvwBwRwbwwwxVxfxxy>yNy^z@zPz{{||(|L|p||}~^~nbl|NxrN"Fl<j 0brJ JZ Htb6HX 6\(~HX*Z@r$:<L*h4Z <d*RF R| D"r N|NN ĂlvT|ǢPȔ*ɬPʖnnnnnnnnnnnnnn2 "КhіѼ "ҢVӔԊRְ֞<vצ"ټچX2ނN4. *p(NZ\$tlP"B2Z*lBh"  " H       " *.TdVlTP` 0`2Bv * R !!!" "F"x""""##L#v##$$*$\$$$%2%%%&&'h(()n))*+,v-:-./V/01r223>3445H56 646h6667(7R77899F9|99::<:v:;<<>>4>j>??@>@r@@@A AHApAABB*B`BBBD4D\DDEE&EVE|EEFF(FPFxFFFG(GVGzGGHH*HXH|HHHIILIrIIIJRJKTLLMbMNOfOPP&PNQ>R>RNSST8TUtVV$VWfXXYlY|ZNZ[\l]]t^^_F_`aDaaabcd2eefVfgghrhiijk,kl@lmn.nojp>qqr2rszsstftvttu&uLutuuuvv@vfvvvww>wbwwxx,xlxxyyyz zHzrzz{||}}F}v}}~ ~@~p~~~ 8\ ,V 8^Fp"Rx*:*Xn F| 8`xH2 |lv(86dH,~NBRbj@Ph\l t(FT\lX&D<.j```8@(ÆÜĊŤfv>Ǹn0̀rVИьJӚԶrnR׬:Pf,Br۠ 2b܈ܚܬ&8J\n݀ݢެh<rBx*Zj*hT0Z,>P*f8f0pHTzBTfxFx0Vz@h L p$^pl8L~P$^|R|>lFB*P^p 6hxV 2  n 6   b2r tF:d"2BRbrj*:l&6Fvd < !!">"#0#$$%%&&'(()H)*Z*+6+,--.,.//0R01&1p112\23<345V56*67778@889d9:.:;N;>?P@X@AzBBC`DDE.EFBFG2GGH HI*IJJJKzL$LM>MNNOFObO~OOPFPdPQnRSTVW XHY0ZZ[\]^4_`2`a*b$bcdefPgXhhriVjjkl`mmnPnopqr|stzujv vwHwxyDyz{`{p{|b|r|}}~:~<v,PBLH z(z@P P4L|*b*Rz8t`> N,T<z<fJfJx Fl>zJ&Tz*Rx4jBj>l V J FLp6\ :b6lBp0V~@h(\ 2X~ P Fl.TP"Hp:j6j 2Z,`$Lt8h 2X"JxDx>d Fj:t X JÐdĴ:pŦLƂƸ<ǂTȢ(dɠVʔXˠ0v@f͞ XΆξBϰ(8"X~Ѳ :jҚrԨHn՘.^b:`؊ش(RـٸHۈ6pܖ.^݊ݲlގ `ߊߤ,V&&>NBz\b:X 8XB6Jp:^BL\lzH8p(hDPz:b P  H  ,     L t<j&(!""Z"""#*#Z##$6&&','(:)d*N+`,&--.z./2/0X1<123B446667889 9:<:;^< <=B=>V>??~?@ @P@@ATAAB(BZBCCBCpDDDEEJEFFpFGG^GGHFHHI(IxIJJlJJKFKKL@LLM"MxMN$NfNNODOOP<P~PQQrQR R`RRSDSST.TTUUfUV V`VVW<WWX.XXYYVYYZPZZ[2[[\$\r\]]l]]^F^^_F__``h`aabaab@bbc6c~cddddeeLeef>ffg,ggh hbhhiPiij8jjk(k|kllVllmLmmn.n~noofoopHppqqDqpqqqr rLrvrrrssHsrssst tLtxtttu(uTuuuvv6vdvwxLyyzn{{|4|}:~~*vBHf:JV.,:&  "8Nd @  /2/3993310!!7!!IhyJh9@  P`  }Y ??+9/_^]]993310#34632#"&Nu3A>>AC<=BBGI@@LJ@  ?32993310#!#H)q)9(q)3{@C    !?O   ?3?39/333339/]33333933333333333310!!#!#!5!!5!3!3!!!?TTPNA+RR'RR%@}TTHNNH &-s@? '% *!!./$**%  vY +vY@//]]33+3/_^]33+393339333333310#5.'5.546753&'4&'6˼rDKcͪʭ8J^sVj^b##3AG= Z6`{dHU( IZ&uZdB  ,0G@% '!-'/12* * * $0/$?3?3??99//33933331032#"#"&546323265#"#"&54632 #GNNGɝGNQMNGɞ՝+LJڧH"Jm 3^@5%"+&,)&. 45%.+1& ((- lY1 iY1?+?+?9/9999993333310>54&#"27%467.54632673#'#"&GP|ffQYiVaMA$Ƴ]3E+rAOE~QL\^S\qhWek;m\lݬfZH?9310#H)q)R) @   $??99331073#&R1 ή26= @  $??993310#654'31:1T{E@*      ?  /?]9/]39933310% '%7)󰮞+wo`f`Dof) 4@  /_/]333933310!!#!5!3hhZT@/?/]9910%#67{7yA%RBu(@/_/]]]399105!Rՠ @  }Y ?+991074632#"&A<=DD=;BoBGGBAKJ??9910 #!Jd/ (@  sY sY?+?+993310#"3232#"/ꑞr~r!74" $@    ??9/9993310!#47'3װ fy^tUcy3b)4@ sYuY?+9?+933310)5>54&#"'632!)9p7xZe`FQt32򝒯t[]crg}akZZ`"-.2!iv5F{GQ+j @@   uY ??9/33+3993333310##!533!47#jԯD} 7JLL0̉h=@sYuY sY?+?+9/+993333102!"'53265!"'!!63JcvZ7#sO,4(9Xq3$R@, !!%&  vY  _  vYsY?+?+9/_^]+999333310!2&#"3632#"&2654&#"qtALe nזzWWPqZ̬%ڲHDddZ/'@uY?+9?9310!!5!Xd-"/L@'-&&- -01 ** #vY vY ?+?+93999333333102#"$54%.54632654&'">54&H/xn2v6fps˺nKVz̽Nqtw`C>]pc?`L/9Ydof)%P@+"  &' vYPsYvY?+?+9/_^]+999333310!"'532>7##"&5432"32>54.)`tDMgo pՖyZTOFЪ &I|Edef (@ }Y }Y ?+?+99331074632#"&4632#"&A<=DD=;B?>?BD=;BoBGGBAKJBIHC@KJ?f/@   }Y ?+/_^]910%#674632#"&h4|A ?>D=;Bh2\BIBIJf+(@0p/_/]3/]/]10%5 +;fs6@  O//]3]]]29933105!5!sVThf+*@0p/_/]3/]/]10 5f;BnfXD'D@&"()P` %%}Y% ~Y?+?+9/_^]]933105467>54&#"'632432#"&L`mDxP_?վ(QycA:CD98G7wP\PP5cj(.eJrfeUo["ӉEDCIBwJ6@K@%*$/7<</AB3>399 , 3',%?3?39/3339/39933333310#"&'##"&54>3232654$#"!27# $!232&#"YiUt 'dkȀECYn>/lQ INیcOS_˲u,ҭXTd߶5 ,@   iY ?3?9/+99210!!#3 '\;:ZF!;DhxcE!b@5 "#kY:kYkY?+?+9/_^]_]]+9933333310! #!!2654&+!2654&#&' Hnxj}}&@ iY iY ?+?+9310"327# 4$32&9]p?J):"hTVPZ(@   kYkY?+?+993310)! !#3 ZuiU|R U@1   iY: iY iY?+?+9/_^]^]]]+933310)!!!!!1Ty8 F@+  iYI?_o iY?+?9/_^]]+93310!#!!!!1R};=@ iY iY iY?+?+9/+933310!# 4$32&# !27!BtWF 3%&cVVT#% E@'  iY8i0 ?3?39/]q]]q+99333310!#!#3!3%VjRb 9@   nY nY?+3?+393333310)57'5!bj')jj)n'`{u @  iY"?+?93210"'532653^:GMdd{yra 8@     ?3?393993333310!##373~ /F@iY?+?9931033!Ǹ{0@   ?33?3999333310!##!3!#47#LRJN,@ ?3?39999333310!###33&53N - QGs'B9} (@  iY iY?+?+993310! ! 32#"_G>b|nheq,&%)o 2@  kYkY?+?9/+9933310!##! 32654&+o꨸%Ķ!}@@!    iY iY?+3?+_^]933310! ! 32#"^3_G>b|FJheq,&%)A@    kY   iY ?+?39/+399333331032654&+#! #۲ѸEeq\h%:@  &' iY iY?+?+999333310# '532654.'.54$32&#"]`<ˮ۸99LE(.~nI^R4JɟRNpeH_N232%26=T#R|lwWD7S` igINA}w4 ,2}cjrV\{!<@  "#  ]Y]Y?+?+??9999333102#"&'##336"32654&o7 oǦ\POxqqq^(@  aYaY?+?+993310"32&#"327f P37br#+!4@;q= @@!  !" ]Y]Y?+?+??999333310"323.53#'#'26=4&#"3w  sƤ( .y쓧!q^K@(    ^Y]YaY?+?+9/_^]_]+99333310"32!3267"!.Xj[/+9m-' ߦ;@  ]Y ^Y?3+3?+?9333310!##5754632&#"!il0]F[XBT>?%xGq=^ (I@&" (()*]Y]Y %]Y ?+?+?+?9993333310%26=4&#"7##"32373#"'53265Poy +u) 2FVL4@     ]Y?+?3?999333310!4&#"#33>32w 1q8Z@PZ5s)@  cY???]]+9310!#34632#"&b=-*??*-=J)<66<;88s 6@ cY`  ]Y?+?_^]]+93210"'5326534632#"&-^@ECNI%=-*??*-=UW_<66<;8838@     ?3??939333310?3 ##3`=F_D}5NTs+mmӲb@ ??9310!#3b^"B@!"#$  ]Y?3+3?33?999333310!#"#4&#"#33>3 3>32#ߙmt /jN 5tÂJPZX`5L^2@      ]Y?+?3?99933310!4&#"#33>32w 3oJQY5qh^ (@  ]Y ]Y?+?+993310#"&53232654&# h| ã'ҋ +{^!@@! "#  ]Y]Y?+?+??999933310"'##33>32"32654&w @n)N==6ZP۸#q=^ >@  ! ]Y ]Y ?+?+??999933310%26754&#""32373#47#Rou s%Օ,  /n</^,@   bY?+??99933102&#"#33>I8=:WT?^ `gJmphy^#:@  $% ^Y^Y?+?+999333310#"'532654&'.54632&#"yـ|w~;=ptd>/EXXJAZ:73`E D_Jy` !'3J"@  !  ?3?3399910!&'##3376733673/2* Ӻhm Ľ  @jMbJkZW>Zk#OMILJ%J .@      ?3?399933310 3 3 # #1bJJ,@  ]Y?+?3?999331033>73#"'5326?K @'EKJ2FVx&9J_%=o _cPsJ 8@ dYdY?+9?+993310)5!5!!sNTwG9^@8  x//$?3?39/]q]]]]]39933333310>5463.54 v׿tpslvn΁y]`/^_' |jdX-hY@ ??93103#閖B\@7  x//$?3?39/]q]]]]]39933333310#565475&54&'53zvmπzVgϚ)' '+dYiXfL+X*@@H ?o/]33+229910"56323267#"&'.P56dFwQI^.66fH~HI^C6l"@9n! ^8@ _o   }Y ?+/9/_^]]9933103##"&54632w3A>>AD;;DHCFEDBIHU@"  tY tY p  @ H    ??99//+]3+3+93333310%#5&5%53&#"3267qv5eY?9>55#BHZ@0   wY sYuY?+?+9/_^]3+39933333102&#"!!!!56=#5346@uz_AKTNy`-/׈/y'[@1   ""() P%/]3]29933333333333331047'76327'#"''7&732654&#"HddyhcHH`gzbbHooonulaGGaotc`EG`lwoprsq@C     wYo wY  ??39/]3+3/_^]]q3+3933333310 33!!!#!5!5!533Hnd5˰6i(@ ??9/9/9333103#3#閖y.:U@- 5/"/)55);<8,383 lY  &mY ?+?+93393333333310467.54632.#"#"'532654&'.7654&'VLJR\k7aJsmt҈WMjŏ6yAR)W$(pT{*';=8T7Cl\PC'-JG=O=I3KF>LoQm:c1 q 0@   0  /]q]3339933104632#"&%4632#"&18('::'(88&'::'&8s5//552255//5522dD&6L@.'// 78p    #3+#?3?39/]39/]3933310"327#"&54632&4$32#"$732$54$#"}wu_x}6^ZƬ֭+*Bw!=@  "# @?3]29/399333310'#"&546?54&#"&'632%32=^aqsNDdjzNnbobVcdggj-=<5>&b/9Rs 3@     0/]q3/39333310 % RX])JKJKf)*@/_/]33/99310#!5)RBudD&6i@= ' //78p#3+#?3?399//]3]39/q3933333331032654&+###!24$32#"$732$54$#"fQYRZdVJ^^m+*լ֭PAIASysb^ZƬ֭+*.@+;k{/_]]q33310!5! {V @  ?329933104632#"&732654&#"{{sOQnnQPrOqrNPqpf) D@(     /_/]q333/393333310!!#!5!35!hh!Z71Ju,@   ?39?399333310!57>54&#"'632!sGK>>d5H4SJn{qEAB0(^qo.OQ\#9!K@+  "# _ !?3?39/]]qq399333310#"'53254+532654&#"'>32wtuug_MBh{JJQ9,:FqNA;BN^7.y! "@  o  _  /]]]9910>73#&u(,?w0N@1NJ9@     ]Y ?+???39999333331032653#'##"'##3b mݒZ <\J6qd'@ ?3/39/93310####"&563!dvw>T1Z3H^ @  }Y /+99104632#"&A<>CD=;BAJK@@KJ1@   @ H/?39/+399333310#"'532654'73A%$HKMXw5cl p '3Ym&LJ @   ?2?9/9933103#47'V.G5Cs&]dB '@ @ ?3]2993310#"&5463232654&#"íXfdZZddZmywwyyttPs 3@     0  /]q3/39333310 ' 7' 7]hZeK_^JdeK_^JdG'&{;) ?55.'L&{t^?5 -'';su ?553w`^%W@7  &'_o ##}Y# ~YO_O/]q+?+9/_^]]93310327#"&54>7>=#"&54632ZJdFw?(Rxg=>?I46I5uTonT`mXbIqfgZoX!/GBIBBs&$CR@ %&+5+5s&$vR@ N%&+5+5s&$K'R@ %&+5+53&$R R@ #%&+5+5)&$j9R@  %#&+55+55$V@-    %&  o "iY " ?3?9/+_^]23399333333210#!#.54632'4&#"326m<3554&# #4632-OA@udŸn@DQpviDHJAp>I"(BL\dE(.GhGK}W?i57\3NVT^!&DC*&%+5^!&Dv1 +/*%+5^!&DK3-%+5^&DR/;%+5^&Dj&8%+55^&DP,&%+55^^(3:y@A )/88/;<#& 0`Y77dY7777& 4 ^Y !aY!&&,^Y&?+3+?3+39/_^]_]++99933333310%754&#"'>32>32!!267# '#"&7326="!4&^qt8Gg+6pB5X^Xf}RƈhVw }1NEzvT(6S]U]o!+' |k\Vcj*q^&FzL 1 %+5q!&HC!% %+5q!&HvP [%! %+5q!&HK )# %+5q&Hj . %+55p!&CQ %+5C!&v$ m %+5c!&K  %+5*&j %+55qh'l@< ""()     ^Y %]Y?+?339/+9/9_^]99333333310#"54327&''7&'774&#"326h^ ;MVbIfN˗3wֱp:3{IKnuu菦L&QR  * %+5qh!&RC!%+5qh!&RvZ >!%+5qh!&RK %%+5qh&RR !-%+5qh&Rj*%+55f+ Q@7  0 @ p  ?/_/]33/]32/]q3933105!432#"&432#"&fspB.0CspB.0C{{B;;{{B;;qh"T@/  #$  ]Y  ]Y?+?+99933333310#"''7&327&# 4'326hqRv\ uPy]1Il/=El'CsP +JsN`r4`d/D!&XC %+5D!&Xvu S %+5D!&XK" %+5D&Xj!' %+55!&\v ]! %+5{!A@" "# ]Y ]Y ?+?+??999933310>32#"'##3%"3 4&dCjx >%[Mҟ(56FN'&\j* %+55&$MBR@%&+]5+5^j&DM)(%+5>&$N/R@ %&+5+5^&DN)1%+5=&$Q%+5^=\&DQ 23%+5}s&&vR@ !&!%+5+5q!&FvH   %+5}s&&KR@ %&v%%+5+5q!&FK I$ %+5}7&&OR@ !&u%+5+5q&FON U %+5}s&&LR@ "&v$%+5+5q!&FL Q# %+5Zs&'LXR&%+5+5q&G8 \!!%+5=Zq(|@H&& )*_Y#]Y  ]Y?+?_^]+??9/_^]^]3+399933333310"323.=!5!533##'#'26=4&#"3w  Ls£& *n(})#&(MR@& %+5+]5qj&HM  %+5>&(N R@  &  %+5+5q&HN ' %+5&(Of5@ &  %+5+5q&HOR " %+5=&(Qm %+5qZ^&HQb&!%+5s&(L R@ &  %+5+5q!&HL (# %+5};s&*KR@ )&z)#%+5+5q=!&JK 60%+5};>&*NR@ &y'%+5+5q=&JN ,4%+5};7&*OXR@ %&"%+5+5q=&JOZ  )/%+5};;&*9% ;%!%+5q=!&J: -.2%+5%s&+KR& %+5+5L&KK!@ $&$ %+5+5m@<   lY` iY0 ?3?39/]q+9/_^]33+3393333333321053!533##!##55!ǸǸmV/Ls@A      _Y    ]Y   ?3?9/]]+9/_^]^]3+399933333310!4&#"##5353!!3>32wN 5lՇX@UU^3&,RR@  &! %+5+5&R  %+5,&,MR@& %+5+]5<j&M %+5>&,NR@  & %+5+5G&N %+5R=b&,Q  %+53=&LQ  %+5Rb7&,OPR@ &  %+5+5bJ@ ??9310!#3bJR{+&,- % %+5&LM +%+55`{us&-KR@  %&+5+5]!&7K@  %&+5+5;&.9%+5;3&N91 %+53J5@   ?3?39393333103 ##379a TJoJ㋉fs&/vnR@  %&+5+5A&Ov"@ k % &+5+5;&/93ô %+5\;b&O9 %+5&/8%@   %+5?5&O8; %+5&/Oi S %+5&OOL8 %+5 H@(     @ iY?+?9/_^]993333103'73%!eGI9zgX># 3@     ??9/9933323107#'73TLϴiIf[yDHBys"Ns&1vR@ &N %+5+5L!&Qv} Q %+5;N&19 %+5;L^&Q9Xʹ %+5Ns&1LR@ &  %+5+5L!&QL# ! %+5'Qv %+5{N;@  iY"?+?3?3999333310"'53267##33&53b:GUfm {unmVN^<@ ]Y]Y?+?+??9933310"'53254&#"#33>32'V;<>w} 4nȏkJRX}&2MR@&%+5+]5qhj&RM%+5}>&2NR&#%+5+5qh&RN #%+5}s&2SR@ +&G!'%+55+55qh!&RS\ >!'%+55}n@@  !iY:  iY iY  iY iY?+?+?+?+9/_^]^]]]+9333310)# !2!!!!!"327&f`YAjZ%L5uVUig8#\!o'\*1m@;%//%23 .^Y.... +( (]Y aY"]Y?3++?3+39/_^]_]+99933333310 '#"32>32!!26732654&#"%"!4&ހ?ш><~'DZh],={ qr4 +romtm-(;՟s&5v{R& %+5+5/!&Uv D %+5;&59 %+5b;/^&U9  %+5s&5LR &" %+5+5~2!&ULz %+5hs&6vNR@ q/+%/&+5+5hy!&Vv [-)%+5hs&6KR@ 3-%3&+5+5hy!&VK 1+%+5h&6z+ܴ,&%+5hy^&VzԴ*$%+5hs&6LR@ 2-%0&+5+5hy!&VL 0+%+5;\&79 %+5!;F&W9  %+5\s&7LR@ &%+5+5!&W8s  %+5\G@&   kY  iY?+3?9/_^]3+3933310!5!!!!#!59H70и1ᤤf!Fd@5   _Y `    dY]Y?+?+333/9/_^]3+39333333310%27# 5#535#5?3!!!!!U@k9ċHk=+U ^VHjk3&8RsR@ &'%+5+5D&XR * %+5&8MR@&%+5+]5Dj&XM  %+5>&8NR&%+5+5D&XN  %+5&8PR @ H&%+55++55D&XP'  %+55s&8SR@ %&H!%+55+55D!&XSq L$ %+55=&8Q# %+5=iJ&XQ!" %+5Vs&:KVR&&& %+5+53!&ZK -' %+5s&<KR@ &%+5+5!&\K % %+5)&<jR@ & %+55+55NDs&=vBR@ I%&+5+5Ps!&]v U%+5ND7&=O=R@ %&+5+5Ps&]O %+5NDs&=LR@ %&+5+5Ps!&]L%+5 !@  ]Y?+?93310"#!2&^Wkdh/Zuvb'D@$   ! dY ]Y]Y?+?+9/3+3933310"'53265#5754632&#"!!JICF;\Lע]u-f9^N`sT>+!dy>".y@G0 ) ## /0!@ H!!&` ,iY ,@,P,, @ H ?3/+]9]39/+9/_^]39/+9333332310#!#&54632&'>73#4&#"326q6-dxgg/(9_?y@31A;73@A^!w6bwx6=kh\C'**7;;76=;^%1=G@W&28,## ,2>CIHBBB@ HB@>P>>5;//////)) ) ) `Y ^Y^Y?+?+?9/+9_^]]q32]+]933333310!'##"&5%754&#"'>32%26=#"&546324&#"3265>7!T#R|lwWD7S` i}fgyxhe~qA12A;83?.j gINA}w4 ,2}cjrV\7evvcavva6==66==]*x iDs&vTR&]%+5+5^!&v lD@%+5}s&vR@ -&L-) %+5+5qh!&vX <,( %+5h;&69Ӵ/+%+5h;y^&V9޴-)%+5! *@  o  _  /]]33/]99910>73#&'#}g4ZX{+e58! *@   o  _  /]]3/]2999103673#&'{ri~a3<!Js~?`f+j @/_/]29910!!+`j ,@  /_/]]22/]39910"&'332673X nTsebq يG;?Cs $@   0  /]q]399104632#"&=-*??*-=s<66<;88m/ 0@     _/]]3q/2993310#"&546324&#"326/}fgxxge~qA12A;83?evudbuva6==66==#=u @   //]3993310327#"54731+,7E:ӠFF.. swBm0@   `/]]r22]229910".#"#>3232673+ROI"12h ta-UNH 01g t%+%;73#%>73##h'!Bgi/j!Bf.P87A887q @    _  /]]9910>73#5 i8lKI?X =@"     0/]q]3339/93310673#'432#"&%432#"&8$q=V_&88&)6^%92,*4;Kd/55225d/5-:2&$T%+5?5H^yu&(}T%+5?5'+T%+5?5P',T%+5?5&2DT@ =%+5?5'< T ޴%+5?58&vBT@ #9%% %+5?5&U@ #)%+555$%@iY?+?99310!#)}((ND=%+{Z@7  iYJzIo  iY iY?+?+9/_^]q]]qq+93310!!%! ! 32#"k_G=ez7Enies-%%'Rb,. @    ?3?99210 #&'#I"R Jυcd{0N1H' L@.   iY:  iY iY ?+?+9/_^]^]]]+910!!!!!5Rs!L }2%@ iY?+?3993310!#!#!%Io3J^ A@"    iY iY?+3?+39993331035 5!!' %!J-f`Nd!\7<h"+e@7' +,-"$$kY* *kYP`??9/]+339/_^]3+39333333310332+#5#"$54>;32654&+";۶D'+A˶87˴M׽Ѳ;mD@#    kYo??339/]+3393333310!##"$&53!333 3+-/!\y|NE@$    !iY  iY?3+399?+933333310"!5!&5! !!5654!ld:>bk+tX4^sG<|)&,j R@ !&  %+55+55)&<jR@ & %+55+55qq&~T 561%+5Xq&T `/+%+5Lq&T? H %+5q&T%+5y&U;@ .%+555q^ ,C@"# ))-.* ]Y ]Y& ?3+3?+?999333310%26=4&# "323673327#"&'#Rۍ{y6 )1# A"WZ< XЕ* +TT\8EfV?4 VQWP(T@,&& &!)*!""!]Y"" ]Y ]Y ?+?+?9/+99333333102#"&'#46 3 4&+532654&ȻoJGhPmXз3ǻ!$4%/-y J!@ ??393910#4733>73!<-C%A Aٻj-7["{>]0(MHuof*E@# %+," ]Y(]Y?+?+99993333310.54632.#"#"$544&'326sǩhNeWR`mլ]wªOb.@80LAEk[uҳw|I-֡X^%e@9## &'%%]YE%%%%%%% !]Y ]Y ?+?+9/_^]^]]]+993333310# 3267#"&54675.54632.#"!וʔUdqcjoWDcJ9Y]'/Kc& ]%),qq$6@  %&" ]Y?+3/.39933333310#654.'.54>7#5!1nY.Ty9T;{Re(-&#g`vG"qL^3@      ]Y?+???999333104&#"#33>32v 3oJQYIqR g@@]YIYiX  ]Y  ]Y?+?+9/_^]]]q]]]+99333310#"322!"! R  s nzl((  J@ ]Y ?+?993103267#"&5ZJT,bp6Jcb 3JV!"1@#$  ]Y ]Y?+?+?93993210#'.#"5632327#"&'&'# 7"1C1>5DC^|[8b/$%2CJZ U!P5YB! <%96 J[S~NJw J@  ??2992103363# #"J%ac[Moq0d@3(+ % %++12_Y ".( ]Y ?+/.39/_^]+993333333333104675&5467+5!#";#"#>54&'.oٌg>6}2\R{7Bw* >s/ N]piNa;#!nYJ26;"qh^RJ7@     ]Y]Y?+?+33?93310%27#"!##57!#5%1V/ߓXLNHJf^3@   ]Y]Y?+?+?99333310#"'##32%"32654&fy t%\D  Йdqq^.@   ! aY?+/.399333310#>54.'.532&#"+9{4G/qZR99lN"o\E4&-((9"4qJ 5@    ]Y ]Y?+?+399333310#"5)!3265'#"f{Tť?!>J,@]Y ^Y ?+?+39310!327#"&5!57ThF'q0ٖJuPHyJ)@   ]Y?+?39933310"&3!2654&'3u-%' Xћ|q\^"F@$   #$]Y  ]Y?3+3?+??933333310$746324&#">ыYO^܈sxeGO!*`u{#b &)rr\P 7@!"^Y ]Y?+??+999321023327#"&'#.#"56:P?-:R)QB,0A>s<ӨE4(5P-YtTkQv}lHR\ A@! ]Y ?3+3??3?933333310>54&'3#$3h$@dsȃ&  !ysJ'C@!  ()& ]Y#?2+3?39/993333310"4733265332654'3#"'#;>B;uj_fe]hz;BB7зE A( Ԏ|6ʀǝٸ&j  "%+55y&j7'%+55qhq&RT# !3 32654&+/8USmE@4=:Q8GHy!NbWul!z\b@7   iY :     kY?+??39/_^]^]]]3+3933333310)!#3!33 32654&+\sw!PbVj!zT>@    iY iY?+3?39/+9333310!2#4&#!#!5!!۸w {l#s&vR@ &%+5+5b&6FR@ &" %+5+5 0@  "iY?3+??3933310)#!3!34@۶$ W@1  iY :    iY kY?+?+9/_^]^]]]+99333310)!!3 32654&+TdTἩ8t%a Z ?@  " jY  iY?+33?+?39333310#!#36!3!!Zq%Hlh(=@!    ?33?33993333310 333 ###N//Jű<<<NF']@3#  ()kY: %%kY% kY ?+?+9/_^]_]+993333310! '532654&+532654&#"'6!2'cbןrmX`O/1{j{6G}`(@    ?3?2999933310333#47##ɬ  $ͬ U*Jh)?`b&6R@ & %+5+5 0@   ?3?3999333310!##33k<:)@ iY kY?+?+?99310!#! #"'5326!1?^J;4=O]m7 V{0%+}2no3}&\7)@   iY?+?3939310"'5326733?3%qV[fk=^-S)d?/9R+꺪Phs; 2@   "iY?+3??3933310%3#!3!3۶+@ iY ??39/+9933310!##"&5332673ϸh޸|_X5'Gvu6 1@  iY?+3?33933310)3!3!3DHJ;@   " iY?+33??33933331033!3!33#Ǹ9;! U@1  iY :    iY kY?+?+9/_^]^]]]+933310#!!5!3 32654&+!I  #z U@1 iY:  kY?+??39/_^]^]]]+9333310#!33 32654&+#3}P 粦"xV J@* iY:  kY?+?9/_^]^]]]+9933310#!3! ! 4&+ f'{=Y@5 iY:0@ iY iY ?+?+9/_^]q^]]]+933310"'632!"&'53 !5!&$ۧL9qeVc 9)NVn##_@7     iY :     iYiY?+?+??9/_^]^]]]+93333310! !#3!! 32#" ^P2XqnPV5Lr,&%)-V G@%   kY!  kY ?+?39/_^]+3993333310 #.54$)##"!3ݷq^2ΞJ^^\Du\"L@(!#$ ]Y? aY]Y?+?+9/_^]+9993331076%3>32#" !"uw荐 :m)Io#o4-/& Qaf}jG8J 1@   ]Y?+3?33933310%!3!3!3۴/ٵJNJ;@  "  ]Y ?+33?33?9333310%!33#!3!3۲FٵL{JN%#J i@@ ]YE `p ]Y ^Y?+?+9/_^]q_^]]_]_]+933310! )!5!4&#!! 7/A:y/ ZPJ i@@  ]YE `p ^Y?+??39/_^]q_^]]_]_]+9333310!2#!34&#!! #3b7 w JYSJTJ ^@9  ]YE `p^Y?+?9/_^]q_^]]_]_]+9933310! #!34&#!!26bF>|6ɤJXT\;^]@9  ]Y i 8 X o       aYaY?+?+9/_^]q]q]]+933310"'53 !5!.#"'>3 ^zPi3@L ;>g6"?^m@A     ]Y E      ]Y]Y?+?+??9/_^]_^]]_]_]+93333310#"'!#3!>3232654&#"?鴴򎝝' J7!J =@     ]Y ]Y?+?39/+39933333103#.5463!#!)!"9~η sw wPjZq&Hj . %+55N'@J%()!_Y!! ]Y!!! ! !!]Y?+??9/_^]]+9/_^]^]3+39933333310"'53254&#"##5353!!3>321P97:w}v 1sʐBׅ=[N\L!&v K %+5q^_@: ]Yi8Xo  aYaY?+?+9/_^]q]q]]+933310"32&#"!!3267{T;5u ]>x!(!4%;hy^VsL,&j %+55sMPJs@F ]YE `p^Y]Y dY ?+?+?+9/_^]q_^]]_]_]+9333103 )! #"'532!4&+326F``vCk%P}ㄉcYQ\Jq@A    ]YE   ^Y?+??39/_^]_^]]_]_]+93333331032)!#3!3 54&#J\ }J;J7ZPLs@A      _Y    ]Y   ?3?9/]]+9/_^]^]3+399933333310!4&#"##5353!!3>32wN 5lՇX@UU^#!&v? '%+5&\6 # %+5RJ 0@   "]Y ?3+?3?933310)3!3!#-<JN'@ iY?+?9933103!#fm-/P'@ dY?+?993310!#!3PBJ?Vs&:CR&" %+5+53!&ZCu%) %+5Vs&:vR@ "&I" %+5+53!&Zv! M)% %+5V)&:jdR.&+ %+55+553&Zj 2 %+55s&<CR &%+5+5!&\Ca! %+5Ru(@/_/]]]399105!R\ՠRu(@/_/]]]399105!R\ՠRu(@/_/]]]399105!R\ՠ9NK@.  `H/+3]/]3]333210!5!5!5!NRR9N ?9910'673% b8B%Z yN?9910#7?4|F ?u@/?/]9910%#7f0C$P ?9910#&'7%B-m^e@   ?32910'673!'673j.C$ f6C$j`Sr@   ?32910#7!#67=1A#;1B d4]'@  /?/]33/3910%#67!#67B0A%;0>h&h 9@    ??9/333933333310%#53%31J13__{i@8         ??99//]3339/3339333333333310%%#553%%?_21X++X12_+{$|d   /99104632#"&tonuwlnuzzz "&@ #$ }Y  ?33+3391074632#"&%432#"&%4632#"&A<=DD=;B}$?399?3??99//339993333331032#"#"&546323265#"#"&54632 # 32#"#"&54632GNNGɝGNQMNGɞ՝+GOOGɛLJڧH"JKHH Rs+$@ 0/]q/993310 RX)JKPs)$@ 0/]q/993310 ' 7)ZeK_^Jdh']!%+55y??3310 #}Jm<@"    0 `   @   ?3?]q2399333104&#"#3363 LLNoZ|f IUEe|XeP\#a@7  wYtYI?_o   uY ?+?9/_^]]+9/3+393333310!!##53!!!!6ʲ@ 'DJ!@R  "#wY   wY/?O sYuY?+9?+9/_^]q33+3/_^]3+39333333333102&#"!!!!!!56#535#535ƥ@ca?MPEq-+ )i@7 !%  %*+  _Y!kY)kY^Y?+?+?9/+9/+333/933333310%267#"&5#5?33#!##! 32654&+f%QCpv}?i5?5µT} RIՉVLR!/y&@$ '( /wY   wY/O_ /_ ""tY"tY?+?+9/_^]qr33+_^]3/_^]q]q+_^]3393333333310 !!!!327#"#53'57#5332& KL#Ǩ.((͢L+y8>,BA **N_T .M@*'",  ,/0)    % ?3?3??99//3]39333310 # #"&5463232654#"%"&54632&#"327՞+XVSYg[#SOgXiJswwsٲ%k#ko$F@!#  %&#     /3/399//3999993333310%273#"&=56746324#"6}iPpNrxεP{A>wөy&ꐟNgx!UgV'+u@A "+"( ,- %mYmY ((()lY(?+33?39/_^]+9/_^]+99933333310!###33&53#"&5463232654&#"5!^"PZZNNZYQ`mL3quuqrmm#B@$    ?33/3993333310##5!###33#7#s!T{Ǻcnn+T+/NvfH4@  ! /?/3/299//]39310"&54>32!3267.#"y1RQHbٓ2Xz#5Fi)|5BuE'd&{>j ?555#''>u ?555G'&< >} -?555f'N'>u=- ?555b9'A@"%%() !dY ]Y]Y?+?+9/+9333310#"&546327!"5>3227.#"9챰Y.:;=C#}Pcaa74QEL*(!:Vl{t~)} '@    iY?+?999331073!!&)͸'3+CqEoȂ%%@ iY?+?3993310!#!m`^H @@"    iY iY?+?+9999933105 5!! !HrH9q+r f+"@/_/]399105!f% @  //99//39310##5!3s% ^w-+J@% #)) ,-# &  @  /]]3333/3339993333310#"'#"&54632632267.#""32654&-|}y|?l41kEM_^?k51lDL`_φ԰ӮWa^ZiQQelY_^ZhPNj @  ?2?393102&#"#"'5325S.;8O==>崹l`/#/X@9(01'/$ *** ***/ /]3/]3/]q3/]33993310"56323267#"&'."56323267#"&'.N69lDvSI_/5}9iCoXK\067j?lbAa5<|3hEvOYW@9n#B9m%A7m)F3n !%f)G@,  O //]33]99/]]393310%7!5!!5!!!!!iTy3l}ߒ=f+ A@&  0p/_/3/]3/]/]933310%5 5!+;;fmf+ I@,  0p/_/3/]qr3/]/]933310 55!f;BofXj= '@   ?/999333103 # jN>N^5!4&IL '%+55#&IO %+5 &@  _/]]22/]39910"&'332673H [qgc ْhRXbbJ @  ]Y?+?93210"'532653-^@ECNIUW{ @  `  ?]9910>73#' V1bB35Ho;u @  `  /]]9910>73#o6a3bT3B?<{! "@ o_/]]]9910#5>734c2d;:8=J @@     ??9/]33399933333310##5!533!547}p}ZkCͿkd=7B@'  o !?3?39/]]3993333102#"&'532654&#"&'!!6NJ)86\mlc6K"!As{"&QYOUp 9J @ ?39?9310!5!\;]J{d39"-M@*+& &++ ./ (((((( # !?3?29/]q3999333333102#"&5467.54632654&/">54&h~JTGF=E2&1%  -, 48!59/33/33399/]]q3_]]/3/33/33/39/_qq3993333333333333333333333310!#%5!#533!5353!5!!5!5!#3#35!#35!35!#35#3#3#"&546323254#"%32+32654&+32654#"'53253T/0momImmmm0oowoooomm~smp.,=.m^{B.$*/;J1%Z^4+V}i0oo/mmmmmmoo;mmJoooo/yhIaCS1BD5QYb" "+%J fVr_cT*,@ % +,((""//99//339910 5467>54&#"63232654&#"TV,AgIOGRZ?>1HT;GFBIHCHEVW/2A1R~X8*P:/5K6DpJ;?HI>@IHc!&7L  %+5N *4g@7 2"*((+" 56+/)%**)dY**%^Y%%/]Y%^Y?+?+9/+9/+99933333310!"&547654#"'6323 4',546323%.#"jD,0'^an+`NY_F94t=ooR++wwCb;,ǑދkWH(@  kY?+??999321076>32&#" #3?E9L]@:$#-E}##۟"l2Ka/J(V@+ &   &&)* ]Y#]Y?2+3?+339/99333333310"&54!57!##"'#326=332654'7p˾EG{lt\hj[tijNH븸έ}{u&0vT@ &N %+5+5!&Pv ^,("%+5&$X; %+55^\&DX ,&%+55zG @s7 4@   o  @H @ H /++q3]2993310#"&546324&#"3267}hgxzecrB13@:93@cxudduud8;;86==f,(@ ^Y ]Y(]Y?+?+9/9+10.#"3>32#"&54!&#"32>5i%$#[7? :j瓸ɶWTpa2aQ;vk]L M]Ŭ۟'|+BJ.|.j&I'IL-,=%h=%+5+55&I'IO-,1%h1%+5+5}q ?@"!"  iY iY?+?+93?99333310! ! >5332#"aEED?h]~qjh|'+'$*q+#R@-%$%   !]Y ]Y?+?+93/_^]^]99333310#"&532>5332654&#"h| lB2; ~jEÞ'ҋ +IDCej'8@    iY ?+?39/3?99333310>53! 533265RM!ԹŴ  JľZ@2 P     ]Y?+??39/39/_^]^]q99333331032653>53#'##"&5Xw}PI  2tH?:y OVM!C!vRw-@  /_/]qq]29/310#'>54#"563 w oJX5-%L׌&m .0Rj 1} @ P  /]3104632#"&1?,+?;/0;<77<5=<s&(CR &´ %+5+5`s&CfR& %+5+5q!&HC´!% %+5u!&C %+52P@(+("+( 00(34))iY-%%iY ?3+3?3+39/993333310"'632!"&'## 32.#"3267332<_,I~tKIs|J,^<˺>|1ḅ%+Ta2222uSxT+'%=LE,!-J"+@#$  ???33939210#3367673363#&-"u6XdjKa پ}ioJlV@%acYOPKr@@ kYiY: kY?+?9/_^]^]]]+9/_^]3+3933333310!53!!3 )!32654&+:b5gӬ`5gu'z@H  ]YE `p   ]Y ^Y ?+?3+39/_^]q_^]]_]_]+93333310!!! #!#5353!2654&#Z5)J˥\YYQ%!c@: "#iY: iY iY?+?+??9/_^]^]]]3+3933310"!!327# !#3!%2.G ]q V o2ڵJi):"mQV/N^3%^!@L  "# ]Y E       aY aY?+?+??9/_^]q_^]]_]_]3+3933333310"'!#3!6$32&#"!!3267 紴 ᣄ5r Y<=J7;4%w T@.       kY8  i    ??339/q]]q+39_^]93332310!####3 !'&'ꐨbcIP: .\\JBƖd'J v@M     ]Y    M ] }   / ?    ??339/]qr_]q]_]_]q+39933310 #####!&'#Ӹlqƹc  JJ+g#Dog@9     iY8i0  ?3?39/]q]]q+299333333310####!#3!3#!'&'kal>J<AVVVjJJĘ\^1J@O     ]YM]}/? ?3?39/]qr_]q]_]_]q3+339933333310 #####!#3!#!'&`Ѷlk͸߰cs($8 :#JJ5ySY"g@< ! " #$ lY: &""jY?+99?339/_^]^]]]3+3993310#.+##"#>75 !7_sf]W BcRQa?"ć.brgKo#Mia+J)]o?śM J #@O!"##$%`Y#o-=]o   " "]Y ?+99?339/_^]q_]q]_]_]33+39333310#.+##"#>75!SlH/!7OF BO7#6NqYJi2Ni>LUDBXPb9 bi$'z@I&"!'%'"# ()!kY!iY'!:! !!!!!$#&$$&jY$?+9?39/_^]^]]]33++3933333310#.+##"#767!#3!5!N`sd.$BeV~s+'[Q#\n$oM;n](Lb?;%V㇦FJ%(@T&'#"((#$)*"`Y"]Y(""E"" """"""%  $'%%']Y%?+9?39/_^]_^]]_]_]33++39333333310#.+##"#67!#3!5!;SkH/!8QC DQ8"-%ϰ+Ji1Nh>LTDBVPt(J7`i=JBK@U( >6ABB;,;0>!E!0*LM0*..833/3 3*AkY:*H*$lY;*iY#?+?3+.39/_^]_]q+9_^]22/993333333333310327632&#"#"&5467>5!#532654&#"'67&'53>32&#"TX`xxADFBlmhź=ןmcZ3\]A70',o0Ŀ`v652'3 {j{@)D 7//7D>)GH 3443]Y4i484X4o4444 44>,&]Y&D>BB@ H>>9^Y>?++322/9/+.39/_^]q]q]]+9933333333333102&#"32632.##"&5467>54&+53 54#"'67&'53>:*+/e-z҂usMWnK{)Z+rjw7?vkVHYPu M<k8%d.<2* ){uz][`WF6R2pUmu} Y@4iYzIo  iY  iY?+?+9/_^]q]]q+99333310! ! 2!"!._G>b^ D nheqM qh^ m@C]Yo-=]o  ]Y  ]Y?+?+9/_^]q_]q]_]_]+99333310#"&532267!"!h|  -'ҋ +TFHR"@ kY?+??93310"#367>32&=N4MF!E;TnY*W8+hXBLT"@ dY ??+?93310!3367>32&#"B !6op&.).9r)5_k RVRs&}sR!&%%+55+55L!&~so& %+55}  .H@' '!.'/0.  iY iY%*]Y%?+?+?+?3?9933310! ! 32#"%33673#"'5326?ZE,'EmD N۾+ENJ7BUw*9rljs*(''GxQs \fq^&R\w "1%+5}-(Q@* & " )*" $& &jY iY?+992?+3993293333310#"'$%632>32654'#"'w|-|v .)úI6i$¹nqu+oo(95+ll,u*0&V*+((XV(q+M@( )#!! ,-&#))]Y ]Y ?33+33?33+33933333310#"&'&547>32>326'#"&' @89? >96B V <5f=46= w'&5.-8$(!$6**8&;*"J<\U>*!"+{DEW@RIMC6FM*%M; 6XYIUHU@5 HIUIU9     @9@iY'93iY-3?3+3?3+3/_^]33/^]3399//++]93333310#".#"#54632326732#"&'632!"&'# 32.#"5>54.54632Wxc*j|m:qwN#^;>YΣ&5-9D#*#tnn%-%J?@IJ'!-Z>Vb//0.vUvV>Z-ZPuJ2&'Fo *?N@JCF@F(,"F6OP HLHL@C HCLCL 2+,,55 555552:::::: :@ H: aY% aY?3+3?3+3/+_^]]q33/]3399//++9+93333310"'#"32&#"!2673 #"'>32#".#"#5463235654'&54327a/pS?x*;[Erm)9rGt}'G[;){?RWxb*56zp:pwM}ww11b9DE %' + 4d%/T4 $*$7>nk$,$QtH(:,NF &i hf>&@4 %+5+5-&j h  0$%+5{/@   iY jY ?+?+?93310"327# 4$32&HoGGK'jnXVPq^1@  aYaY?+?+?9933310&32&#"3267#sM=5dBY)*4dhu7@     ?9933333310'%7%7%{D!Ey!FC7C@ubw=E¦uu0@   @ H0 / _  /]2+993310#"&5463!>32#*05)*6,03-,6-2455+//158*8@   @ H /_/]33/+]3/3991027632#54#"+5xQmzj+dxVh;:kn!d$+$9@  /9333104632&F9/3$w9H)':$L:9@  /9333105654.54632w$4.9D:L%9')H) (6DR_m@iP4H, B&:V^&,c4k no-&44)"00)d^Wkk`ZggS`IB;PPE>LL7E)`EE`)P     ?o/]2q2/]]3q29///333q2333q2333q29333333102#.#"#62#.#"#>2#.#"#>!2#.#"#>2#.#"#>!2#.#"#>2#.#"#6!2#.#"#>]qO?#&*-O;_;;;;6@3P3333.6-&..&-6  ?/9////////]]93310#67'>737&'.'5467'67".'7&'7 F$a5; Ia4#GA݁hBO݁E?軋Ek(8PC{Lh&ZC7#BO݁GA܂ Ia5; F$a5[8D.^3DuOW.FcB=FK%b"]@3    #$ " " iY?+???399_^]22/^]39333333103333##47##"&'332673Ǭ  $ɔӞ I ]nic ەQB+lN]]1b@7      ! "_  ]Y ?+???99/_^]]33/]3?933333331033##?#%"&'332673X Lݼ} \qic J_N{hJgS]]/z@G iY iY: kY?+?9/_^]^]]]+9/_^]]3+3933333310353!!3 )#32654&+/PLmPѽ`^>{T@V     _Y]Yo-=]0o   ^Y ?+?9/_^]qqr_]q]_]_]+9/3+3933333310!!! #!#5353!2654&#b/4'!ɤ[ZYQ}Y@-    kY  kY ?+?9/+999999333333310'+#! 327'7654&+}riuib_8fqv8XVF{^(]@4 $!&&!")*!$# ]Y]Y?+?+??99933333310"'##33>32'"327'7654&w @npjDr,&wp}] =6YQT۸# Tg/ E@$   iY    iY ?+?9/_^]3+39333310!!!##53qZ}NJ G@&   dY    ]Y ?+?9/_^]3+39333310!!!##53!NX#݋L@(  iY   iY&iY?+??+9/_^]+993333310"#!!63 !"&'53 4$7^ZRf_x>XUF{w  1 JJ@'  aY]YaY?+?+?9/_^]+993333310"'53 4&#"#!!63 Njn~ N< R<  <=/J L@*    " iY?+??3?339933333310 333 3####N//°fű<<<=JK@*     " ]Y ?+?3?33?99333331033 3#### 3;gȮb%%;Ja{--5N=F&|^3- %+5D=^&| /)%+5=E@$       "iY?+???39393333310%3###373p~ /FVJ?@!    "  ]Y?+??3?993333103 3###37+wXJ_}-JP@(     ?3?39399999333333310#3733 ##}}}Z^}k+^FHJI@&     ?3?39999933333310&'#3733##N%s7N)JyJl3/X@,  iY  ?3?3939/+9999333333333103533#3 #&'##/Z݃{Ǯ^f1P@)  _Y??9/3+3?393339333333310353!!37673 ###w@,^D}\[.s+km D@#     iY?+?3?9393333310!3 ##! X+%J 6@      ]Y?+?3?993310!3 ##!%+J-Z@4    iY :      "iY?+???39/_^]^]]]+93333310%3##!#3!3%Vj Jj@>   " ]YE   ]Y ?+??39/_^]_^]]_]_]+?93333310!33##!#bTJ7N{Ju U@1   iY :      iY ?+?3?9/_^]^]]]+9333310!#!#3!!uVjJ c@;   ]YE    ]Y?+?3?9/_^]_^]]_]_]+9333310!!!#!#bTJ7ɔJJ) R@, !" iY   iY&jY?+?3?+9/_^]+93333310!#!#!63 #"&'532654&#"ݶXL1POF~+}a כ1 JR@-  aY ]YaY?+?+?39/_^]+93333310%#"'5324&#"#!#!632ÄcjoG8yM:;<=3NJ' })5m@;30$3**3!$673- ' '-kY'' '' iY iYiY?+?+/+9/_^]+999933333310327#"'# !2.# 327&54324&#">rBUN=8]fH;\1f2?2*P^ǰúdXYdZNapVd$Vy#  _RAq^ 4y@(3+%.+#%56#! .((dY(((@ H(( 0^Y ]Y!]Y?+?+/+9/+_^]+9999933333310>54#""'#"&532&#"327&54632327C:BQEH^|hvz_M'FA>i[2>B3,\/*kxfO(/ ۬~=}=&&|' P#%+5q=^&F| R" %+5\ 4@    "jY iY ?+?+3?93310!5!!3##7J7)J 4@    "]Y ]Y ?+?+3?93310!5!!3##y{<J&@  ??3/39932310#33>73bRO =RN_9@ZB@"   iY   ??399/3+393332310 3!!#!5!53D}-ӹ/9\1J<@ _Y?3+3??3993332310#!5!33673 !bTN}G8O@'    iY??39/+339/9/933333310 333673##u/}t}sGzqZ:JT@)JQ@(   ]Y  ??39/+339/9/9333333103673##5#"&5Lyp~ny Jn)TW-@   iY ?3?9/+99333103$32#4&#"#Ǹ߹|f\-vv"2;NJ-@ ]Y ?3?9/+9933310!4#"#3>32_hcl5@+JD;f7P%s@@#$$&'##iY#z#I### ## iY iY?+?+9/_^]]]q3+33/_^]933333310473;! !3267# "&"!7o")H(4_qݏ`M:-CeGOh2 )"aKv-\$f@7  "" %&!!dY !!!!]YaY?+?+9/_^]_]3+33/_^]9333310"'$5473;>32!!267"!4&PhPdd[ 1G4%Eem *' ݟ7P!(@G % !&&!)*%%iY %z%I%%% %%!""iYkY?+3?+?9/_^]]]q3+33/_^]9333333310$"&5473;! !3267#"!4&o"P'5_P` ](vwM:-Ce?Wn2 B-\ 'r@>   %% () "$$dY $$$$!]Y]Y?+3?+9/_^]_]3+33/_^]?93333310&'$5473;>32!!267#"!4&hPddH[J 1G4%Eem *"DRb,b&6R@ %&+5+5&6 %+5'F@$    kY iY& ??3?+9/+393333310"#3737 #"&'53254$d_IcS~J{q<Rכ1 5JD@# ]Y aY?+??39/+393333310%#"'532654&#"#335oˈc/lDQ_5;<%HJ;@"iYiY kY ?+?+?+?933310%3##! #"'5326!Ǔӟ1?^J;4=O]m7 VJ;@ "]Y]Y ^Y ?+?+?+?933310%3##! #"'532!𼃶}`v="l#{a %U@0   iY:  iY&??3?+9/_^]^]]]+993333310%!"&'53 !#3!3%T}L{1Vj jJa@9   ]YE    aY?+??39/_^]_^]]_]_]+993333310"'53265!#3!3ц_ni}tR :;J7^@6    iY :      "iY?+???39/_^]^]]]+933333310%3##!#3!3%ɖӠVj'Jn@@    " ]YE   ]Y ?+??39/_^]_^]]_]_]+?933333310!33##!#bT~J7N{J;@   iY  "iY?+??39/+9333310!##3#"&5332673ϲh޸|_%5'Gvu69J=@   "]Y  ]Y ?+?39/+?933331032673##3#"&5L\enlJp7>QG8BE@$  "iY?+??3?39933333310!##!3!3##47#Lǔբ]wRJB@"     " ]Y ?+33?3?99333310%6733###.'#3.較?4%mtN{;^-wJC]Rb,b&$6=R@ %&+5+5^&D6)1%+5)&$j?R@  %#&+55+55^&Dj&8%+55^^b&(6R & %+5+5q&H6 ' %+5yjE@% iY iY iY ?+?+9/_^]+99333310"5>3 ! 5!27!}ڀLr/+T,"pqyvFbh^Hyj)&jsR@ - %0&+55+55h&Hj.%+55)&jR@ $%'&+55+55&j %%+55NF)&jR@ =&(: %+55+55D&j$6%+55H;H@&kYiY kY?+?+99/+3933310 ! '53265!#5!5!dbA]B O00ФJK@'  ^Y]Y ]Y?+?+99/+39333310#"'532654&+5!5! ̢xFɐ|HV}`&MR @& %+5+]5uj&M7  %+5`)&jR'&$ %+55+55u&jB  %+55})&2jR@ -&*%+55+55qh&Rj *%+55}{qh^|})&{jR@ /&,%+55+55qh&|j+%+55=)&jR1&. %+55+55;&jSŴ+%+55&M/R@& %+5+]5j&\M  %+5)&j9R@ ,&) %+55+55&\j * %+55s&SR@ *&U & %+55+55!&\S M!' %+55)&jhR@ )&& %+55+559&j % %+55 /@   "iY iY ?+?+?93310!!3##GqJJ /@   "]Y ]Y ?+?+?93310!!3##J{)&jR-&*%+55+55&j) %+55/j&} %+5jNJa@5    dY   ]Y dY aY#?+?+?+9/_^]3+3933333310"'532=##53!!!!3.9b\+jkJ1bZ_@8     iY9h0 ?3?39/]q]]q3+3339333310!3 3!!# #!}3wfgu9}wV`?L{%J]@9   dY/? ?3?39/]q]]33+339333310!3 3!!# #!s c`{b1J; :@ iYkY?+?9/_^]+9933310!33! $#";FaٽjJyq=G}"Z@1 ""#$piY# kY?2+3?9/_^]+/_^]99333310"&54$!3332653#"'#"!265L("fsֹbp"ϻzj{m̧tio ,O@)*$$-. (]Y!!]Y ?3+3?+?99/_^]93333310%2653#"&'#"323/3!26754&# ri.S}i< iꈁ3WknV)  0MUNT#XH*\@2" +, p  kY+ kY%iY%?+?+9/_^]+9/_^]9333310#532654&#"'>3232653#"&'.ǿғca\abhztmj{9B{JNĥ3Ӑ}x)̓N^%R@+ !!&' ]Y&]Y]Y?+?+9/+9/_^]9333310%23# .+53 54#"'>32Jײ~s!NO;Rnjv3IdY$"'%9}dH$W@/ %&kY " ""kY" iY ?+?+?9/_^]+9933333103##4&+532654&#"/>32 ¸ߠgk-/ebj{8C?T4lpݴ6wm"̓s);J@@"]Y ]Y ^Y?+?+?+/_^]93310323#"&5! #"'532!fsϴ`v="l#|59a fe@:  p iY :     iY?+??39/_^]^]]]+/_^]93333310"&5!#3!3326539Ƕlonm-Vjσux)Jq@B   ]YE ]Y?+??39/_^]_^]]_]_]+/_^]93333310!3323#"&=!#b>hsϲ´J79}3nJ}=@ iY iY iY?+?+9/+933310!! 4$32.# 3265!b@Ty]FYa XaU0*'/q^E@% ]Y ]Y ]Y?+?+9/_^]+933310!! !2&#"3265!B"ݪ=ƽHF),PJ֜@@"   p  jYiY?+?+3/_^]9333105!!3253#"&5>5spsu))J>@   ]Y ]Y?+?+3/_^]933310"&5!5!!32653jjshk;̓}z9smd']@3%$$ ! !()%kY: kYkY?+?+9/_^]_]+9933333104$32.#";#"327! $54675. pbgjǶǯϺ\EKB5yl{\MǖX^j@@#   iY  iYkYkY#?+?+?+?+933310"'532=#! #"'5326!3;?.8b1?^J;4=O]m7 jkVjJ@@#   dY  ]YdYaY#?+?+?+?+933310"'532=#! #"'532!33232673#&'#57673\;:ZF!p$GC@(*] dL%IE>(* \ edbfo\yb6o60w4;DhxcE$.2js$/1jsBbSQ%+55s&$'dK/R@ &&(%'!%+5+5+5^!&D'dK&,%ߴ?9%+5+5&$xR@!% &+q55+55^&Dx19%+55&$yR@ % &+q55+55^&Dy08%+55X&$zR@!)% &+]q55+55^&Dz9A%+55^ %2@ 43/( (((,& &&@< H& 0 "  iY ?3?9/+9/_^]3^]/^]]q33]+q22/]]39310!!#3 '".#"#>3232673 332673\;:ZF!p$GC@(*] dL%IE>(* \ elOkbXm ;DhxcE$.2hq$/1grE<@A^ &D{@H%+55N&$'N1bd@&"(%%+5+5+5^&D&Nd4:%)1%+5+5&(d  %+5q^&Hd " %+5&(cR@&-  %+5+]5q&Hc @) %+53&(RR@  &! %+5+5q&HR  %1 %+5z&(tR@ % &+q55+55ql&Ht  " %+55N&(uR@  % &+q55+55?&Hu@ " %"&+55+55AJ&(vR@ % &+q55+55q+&Hv " %+55b #2@) 43%'**.:.. 0*@** *   H @2 H   iY: iY iY?+?+9/_^]^]]]+/3/++_^]33^]r]99933310)!!!!!".#"#>3232673#&'#576731Ty$GC@(*] dL%IE>(* \ edbfo\yb6o60w48$.2js$/1jsBbSQy@* @?136:*::6 6@.2H6@ H6 )! H@ H,$ iY iY?+?+/3/++_^]33++^]]99993310! ! 32#"".#"#>3232673#&'#57673_G>b|g$GC@(*] dL%IE>(* \ edbfo\yb6o60w4nheq,&%)$.2js$/1jsBbSQ73'673#dpcrae3w0GPI6Q{gK[eA:Eg[plb 3@ @ H   _/]]33/]99/]+10#&'#57673%#&'53farjid5q3>byV9FAe`F=JY[suXj d@!@ HD`pH@ H   _/]]33/]99/++qr^]+^]29/310#&'#5>73#'>54#"5632dpcrae3w0GT ;>c/8K[eA:EgxfO %>Tf&K@1 @H @ H  @ H 5"" """_/]]33/]]]9]+2/++3310".#"#>3232673#&'#57673-$GC@(*] dL%IE>(* \ edbfo\yb6o60w43$.2js$/1jsBbSQCBq5@"$4  /  _/]]33/]3_]]10#&'53 332673bwV.O9lQi`Zm ^qeh G>ADqf@$H@ H */H$H@ H/_/]]33/]33/+++r]r29/++310#'>54#"5632 3326731}T 9>a%$>lQi`Zm yc)\ #=P G>ADf $E@. @H @ H  /?!/_/]]33/]3]]2/++3310".#"#>3232673 332673-$GC@(*] dL%IE>(* \ elOkbXm 3$.2hq$/1grE<@A-=q@   //39933104'3#"'5326݋id@7#5%3gw[js /j @  kY#/?+99310"'53253;?.8bjk3\&7zF %+5!F&Wz %+5{)t@B'  ' '*+ _Y ]Y  $]Y?+?_^]+??9/_^]^]3+3999933333102#"&'###5353!!36"32654&o7K oǦ\POxׇ=qqh$-t@> && ** ./ %%kY: $ $kY &kY?+?+39/_^]_]]+99/9333333310"#.5463! #!!2654&+!2654&#5} &'g=1B} /nxj}{#E@#! ! !$%  ]Y]Y]Y?+?+?+?99993333102#"&'##!!36"32654&o7^V oǦ\POxqq J@*  iY :     kY?+?9/_^]^]]]+993331032654&+3! # qż  { u9@  ]Y ]Y?+?+?999333310"3363232654&#" o⧓(qq3н?&@ iY iY?+?+9310 #"&'532#"'6D[qWJv!:<5PV}$G@'& !!%&kY/ iY iY ?+?+_^]+99333310"327# 4$3254632&#".9]p?|tn~==19`JD):"hTch1q^!:@# "# aY aYaY?+?+?+99333310"3254632&#"&#"327f 8Mm~54&#"vĬhNLhR`mլ{ªZMc.@+=LAEk[u ҳ~E-֡{ S@0   iY:  iY iY ?+?+9/_^]^]]]+9333107!!5!!5!!{yT1Ϣ ȢJyjof']@3%$$ ! !()%kY: kYkY?+?+9/_^]_]+9933333104632.#";#"327! $54675.؄dXkjƻ߸ݿ`MK}I4}h{kZϜQ@1   iYI  ? _ o    iY iY?+?+9/_^]]+933210"'53265!!!!\B1*3H91RSUX}'Z@2) %%'()kY/  'iY iY #iY?+?+9/+_^]+93333310!# 4$3254632&#"&# !27!BtW钆n~;@1:`F 3%&cV##clT#B@"!    !  iY?+?399333332310%#"&5436734&'32DEjnR#"''E݆MyvUhs6AM-?:$E@$"  %&]Y ]Y ?+?+???993333310%2653#"&54&#"#33>32ykt 2c}bA8Z@UU@ iY ?+?993103267#"&5oJS,^p8cb tFoX@,  iY nY nY?+3?+39/3+3993333333310)57#53'5!3#b𬸸j' )jj)'<@     kY?+?3?9399333310 #&&'#3676>32&#"V=p9B,O;iT>(*0"<-=QӈHL2MA(63A@"      aY?+?3?93933333103?3 ##32&#"`=F_D}CB/;/2NTs+mm6A D@&  _Y/ ??9/]]3+3933333103###533b\+ՇV!&d@(# '(@ H""  ]Y ]Y?+?+?9399+9933333210#''7.#"56327327#"&'&'# /'D;>5DCm3&n/$%2CJZ U!P5A=.( VcDB96 J[S~u$>@# #%& $  iY ?3+3??339993333103265332653#'##"&'##"&5qvw 3w- :~tJWedhbj N6@   iY?+??39999333321033&53###"'53265-  B1*3H9Gs'B9JQ%SUL^}{}'E@#"""() % %iY iY?+?3+3?939333310! ! >32#4&#"32#"O6#9{¸vl'mqieXc>?,&%)q^%G@$!!&'  $ $]Y ]Y?+?3+3?939333310#"&532>32##"32654&# 5vف5W>G'Ӌ ,KHIZF@#  kYkY?+3?9/+9/933331032654&+7 !###"#.5463#Ķ$騹5} ׍Rg=1B}{ -J@(%  ++./ aY!]Y(]Y?+?+?+?999333102&#"3>32#"'##"32654&CB/;/2 @nw 裑6A=N)ZPҟ)N==#3N@&   iY  kY  ??9/+39/+993333331032654&+#33 #۲Ѹ+~Eqep\`%;@ &' iY#iY#?+?+93993333104>7>54&#"'632327#"$`R}7:ϟ>E{cr?4M_GepNRʫK:R^Cn~a"-\m^#=@ $% ]Y"^Y"?+?+99993333104>7>54&#"'632327# \>ftpg>;~ls0J/LnX4EO=>GFJLjU<)@?-PRXEJ^p"O@.$ #$]Y  ]Y ]Y?+?+3/_^]q+93323310"&546323267#"&5";54&{zJS'em4d,$eO2Vxmnvoda 51RH?!FP@(     dY ]Y aY?+?+?+333/9933333310"'532=# #5?3!!3267C2%$-AKJ2FJ5FKR'[^ 69  ’N_IoWNDW@0   lYiY  iY?+9?+99/_^]3+393310!!5!3!!!5!y5 lBФ%PsJl@=   ^Y5EedY  dY?+9?+99/_^]^]]3+393333310!!5!3!!!5#uIT =w{H;H@& kYiY kY?+?+99/+3933310! '532654&+5!5!T|dbZrǃO00qdF@% kYiY kY?+?+99/+39333105!!#"3267#".54>7]dc\ڈ{ގ%c00,#ỏuFJI@& ^Y]Y ]Y?+?+99/+393333105!!#"327!"547}Fx̷ǃ}VH9J$f@7""%& ]Y    ^Y]Y]Y?+?+99/+3/_^]+933333331027#"&54632654&+5!5!#"zйƾxv'lgmJ54&#"'6323!!!57!81zl]K`+34}9g`wZZYgrDCyǬV[!C1 }H;C@# kY iY kY ?+?+39/+933331032! '532654&+#5!Vdbun̈O00"DJC@" ]Y]Y ]Y?+?+39/+99333331032#"'532654&+#5!/򄷽ᄋGVmlmjJXF!P@(  "#@  dY^Y?+?+3399933333105#5?3!!#"'532654&'.Gk=*6؀|caIVHEA?jEXXJ)%+5+5q_!&G']L{@72&'%X#*9%+5+5{&/-1 %+5&/M1 !%+55&OM W %+55{&1- c#%+5&1M c/%+55e&QM S 0%+55&$L/m@ %&+5+5^!&DL2-%+5&,Lm@ & %+5+5c!&L  %+5}&2Lm"&$%+5+5qh!&RL $%+5&8Lm@ &%+5+5D!&XL ! %+5&8 LR@!&-,%+555+555D&X Lu@ 0/ %+555J&8R&@ !0!@!!&$.%+555+q555D&Xs@ '1 %+555^&8 KR&@ !0!@!!&:3%+555+q555D &X Ko=6 %+555J&8R&@ !0!@!!&.%+555+q555D&Xu1 %+555h^W@5    ^Y    / ?   aY]Y?+?+9/_^]q^]+9333102#"=!.#"5>267!Xj[^ m-' !&$ LR@ %&+555+555^&D LJ8&%+555&$ O %+55^&D NJ54%+55&MR U@%&+]5+5^j&M״>= %+5}#l@= !# $% lY #iY/  iY iY ?+?+9/_^]+9/3+3933333310!3## 4$32&# !275!5!5!BtWF -͒%&cVVT#q^".@K!-&" "!/0   ^Y  / !  *]Y #]Y@H@ H]Y?+?+++?+?9/_^]3+39993333333310%7##"323733#!"'5327!5!7%26=4&#"oy uaSlū" *($FVf=!h};s&*LR@ &&z(#%+5+5q=!&JL 50%+5s&.LLR&ʹ%+5+53&NL{@  %&+5+5}=&2Q} )"%+5q=h^&RQ "%+5}=&2'MRQ}%@&)&"%%+5+5+]5q=hj&R&MQ&!%%+5+5H;s&LR@ '"%%&+5+5!&Lr&!%+5 &'= 4%+5 J&'] >%+5q_&G] X#*,%+5};s&*vdR@ %&%!%+5+5q=!&JvJ D2.%+5\@4 iY:  iY?+??39/_^]^]]]+?93333310#3!332653#"&5^^`[aȳVj]fg^V;@  jY iY?+???+9993333102#33>$4&#"K[ LJX(qjˊ Ns&1CfR& %+5+5L!&QC %+5s&$sR@ %&+55+55^!&Ds4&%+55>&$R@ %&+5+5^&DT1)%+5s&(sR& %+55+55q!&Hs+% %+55>&(bR@ & %+5+5q&HV ' %+5ds&,sR& %+55+551!&sV %+55>&,\R@ & %+5+5E&%+5}s&2sqR#&'!%+55+55qh!&Rs'!%+55}>&2!R@ #&#%+5+5qh&Rm #%+5s&5sRz@ $ %!&+55+55#/!&UsH! %+55>&5}R@ ! %!&+5+5/&U  %+5s&8sXR&!%+55+55D!&Xs$ %+55>&8R@ &%+5+5D&Xy   %+5NF^(L@)##' )*'(('^Y((( ]Y  ]Y?+?+9/_^]+99333102654&#"'>32#"'532654&+5=~V=Z_򂂥[aΡӏqq(+ŭ},*͖|L+/%s&+LR@ & %+5+5L&KLN{@ /# %!&+5+533@  iY ?+???99933310 #33>3 #4&#\ B}-5]tdǶq'Ew%L@' ""&'$$kYiY?+?39/+9993333331026=3!"$&5%&533265! 엞1縠༹èc0ћyA]_*Jqh(L@'  ##)* &&]Y ]Y?+?39/+999333333102653#"5%.5332654&#"my^i i\yᓞ'3ٞKb'NjD&=}  %+5PjsJ7&$OR@ %&+5+5^&DO5&,%+5&(z{   %+5q^&Hzo " %+5}&2 L!m@'&32%+555+555qh&R Lm@ 32%+555}&2 Mm @ !!!&!-%+55+]55qh&R Mj!-%+55}7&2OR@ !&%+5+5qh&ROb%+5}&2 O'&%+55qh&R Nm '&%+55&<MR@  & %+5+]5j&\M  %+5P@)  dY  dY ?+?_^]+9999993333103632#"&''672654&#"?M|^&'JV7<3?6>?mrG<=o=m=-561^",o@; ++&&-.+ )  )dY ]Y#dY?+?+??_^]+99999933333310632#"''674&#"#33>322654&#"L?L|S(JVw 3oă4:>6>?mr9s=mJQY55561F$m@8 ##%&  #! !dY    @  dYdY?+?+33_^]+999999333333310#5?3!!632#"&''672654&#"Hk=?L|^&(KT84:>6>?VHmrG<9s=j55561qV'3T@,1&& +  +45  #..]Y((]Y?2+3?3+3?9933333310"323.533632# % 4&#"!2654&#"Vw  orr-" .9qqмVqV^(4S@+! , '2256 )]Y/$$]Y?3+3?3+3?993333333102#"'##47##"3 > 3265!"32654&qv  kr:kӑᐟ^ҡ#Ki^pq* %|ZϽ߿fd@7  /     iY  ?3?9/3+3_^]q993333333210#!###37&'3щӎ;>V(!l\N_;Tq^xcE }f&d@8 "$%$'($ '@")H@ HiY iY?+?+++9999933333310&'327#"'#7&4$327"&y'=@JI#zI[]p_b5C?NJ(p :"RbT SJqV"\@0 #$  aY aY?+?+?/999993333333310&'327#"'#&;#"զ9+7?#9Hr]] L2@;PP+ g =@    lY  iY ?+?9/3+3933331033!!!!#\ɳ9}f\D@%      iY?3+33?_^]93333103###!5!7#)-`Ϗ^7Y-^P#C^/h^2Q@*#0)034!,)0!&]Y!^Y ]Y ?+?+?+999933333103267#"&'&'&'532654&'.54632&#"%"'YF`E,|w~;=ptd>6b "XXJAZ:54&#"'>32׊J;DKj7sy8,6=Ѷ\3^-@   ]Y ?+?9/3933103>54#"'632FACȹ'750r˼V )@M %!%%*+iY  !!kY8!!i!!! !!)kYkY?+?+9/_^]]]q+99/_^]3+3933333333310#53! #!!!!2654&#%!2654&+Ǩ&/'^ ˆ}nxjO@'     iYiY?+?39/33+339933333333103#! =#533!26=!ܤ !͠AA·ӳif@W    !@iY: iY iY ?3+3?3+39/_^]^]]]3+3_^]93333333333103#3!!!#7#!73!3#et)''GXrF^8^8 qV#'+@O (*+'"&# $$#"+*,-*(&! '(!!(^Y!!!! ]YaY?+?+?9/_^]_]+999999/93333333333310&323!3267#"'"3&';q}R{>YXj[mkZ-kbZT`VL99m-' Nu6ʸO``{?@   iY iY"?+?9/3+3933333210"'53265#5333#^:GMdd{yr{m!Y@0  "#cY`   ^Y " ]Y?+?9/3+3_^]]+933333210"'53265#5333#4632#"&-^@ECNI%=-*??*-=UWC`_<66<;88}7-I@&)/# --./!iYiY+&iY+?+?+?+?9999333310"32>54.47##"$5!2373327# ʞ[\" 9奻G#; :G2*/C+\<[6]`pVancWQBq^ (K@'$* (()*]Y]Y&!]Y&?+?+?+?9993333310%26754&#"47##"32373327#"R su-8@&*e%՘n<,  /#pU!VK@%   kYiY?+?39/3+3399333333310##53! #%32654&+۲\\eq\/^R@,  ^Y bY?+??9/_^]3+3993333310!##5333>32&#"3d?eI8=:ump L@'       iY  ??399/33+339993332310!33###533!RƏ{y{'/1)J!U@, #  "#! ^Y ]Y?+?39/33+33?9933333310#533!33##"'5326?6!͹‰uEKJ2FVx&9Xo?= Nkk _cj6I\D}H ?55q=^<@     ]Y]Y?+?+??9999333310"32373#'#'26=4&#"3wsƤ( .!{^@J ?55{*E@$ ((+, aY]Y%]Y?+?+?+?9993331032&#"3632#"&'##"32654&EB/;/2 oo7ꦐ6ArqqPOxǼDf^(@ aYaY?+?+993310732654&#"'>32#"'V7271^r@";b^']@1 %% ()]Y  aY "]Y?+?+9/_^]+99993333310"''67&532&# 632"32654&}+ +@N T28bŽ~wRWiQbQa?~n+"4`iNs^M<09q',M@(.*#  -.']Y ]Y]Y?+?+?+?99933333310327# 5467##"323.5326=4&#"=1DI,/m  vw  j[!Vw*  .ym!q)K@'+'  *+ aY$]Y]Y?+?+?+?999333331032&#"#'##"323.526=4&#"H?/;/2sw  Ǥ6A( .y#!h^GH@h^W@5    ^Y    / ?   aY]Y?+?+9/_^]q^]+9333102#"=!.#"5>267!Xj[^ m-' !h^#+e@4-)((,-!aY ()) aY $]Y ?+?+99//3399999/+93333310#"#"=%.#"5>3273272>5s{KXj[m ,#>.RT9\F N ȅ-' Ŵ/zUK7NYX^D^D3^0{@E2%  ++/%12aY# /00/]YE0000000# ]Y #(]Y#?+?+9/_^]^]]]+99/+9333333310 54#"'6327327#"'#"'532654&+57O_?ԑ*o">.OW'1}?L/}v򄷽(L[U{UK7"._$$gGV^\^[qq^#e@9! !$% ]YE     ]Y]Y?+?+9/_^]^]]]+993333310"3 54&+53 54%2# (V98u и^[9$g((J?@ ^Y ]Y ?+?9/3+3933333210333##"'53265#^@ECNIC`UWo%2U@-4%0 )  34 aY-]Y &]Y #]Y#?+?+?+?+999333333310326=7##"323&=32&#"#"'26=4&#"Šoy F?/;cV+) 2u@wF%+q=^Jq^=@ ^Y aY aY?+?+9/+933310%#"!2&# 327#5!xj#{Bi9+"#+JH`-J$G@#&   %& " "]Y?+?399999333332310%#"&546733>34&'326mQ4gj9L`<*  $+B)#$(--EooOv.!^n22:<2;42^ 1K@&-3  23 ##/]Y*]Y?+?3+3999993332310%4'326#"&5467&#"563236?>32&#"RLL--ll8S&.%.:4G'. !D'I2;-%."XgkR1+)kg~~iHo5 )7I!32#4&#"#CB/;/2 1qʲw6AZ@PZ5L(D@$((")* aY ]Y %aY ?+?+?+?993333104&#"#32&#"3>32#"'53265wCB/;/2 1qCB*@/26AZ@PZh6A X@1  cY`  ^Yx ??9/]]3+3_^]]+933333103###5334632#"&b=-*??*-=)<66<;88JJFJ 9@   nY nY?+3?+393333310)57'5!Fj#-%kk%# T@1   ???9/]3/]339999933310"'#&#"#>32332673#+11i sb#+01f u b;3 3>32#"'5325#ߙmt /jN 5tCB*A`ÂJPZX`hwL^<@    ]Y]Y?+?+??999333210!"'5326533>32#4&#"b]2/;H7 3oIJw!YlݖQY55^ >@"  !"]Y ]Y ?+?+??9993333103267#"&54&#"#33>32L5H?O+w 3oēj[TJQY`J,@   ?3?39999333310##3.5`8 JsoJ'{qh^|q^#{@F $%  ]YE]Y]Y !]Y ]Y?+?+?+?+9/_^]^]]]+99933333310)5#"&5325!!!!!32654&#"/ŕ| 'o +qӕs^%B@!   &'# ##]Y#  ]Y?3+3?+9/99333104#"326533267#"'##"5! vi_fe]hzϸE AgKBh| 󀊬5Xo\JUNJ ?5(@  bY?+??9993310"'732>53#'#I8A6WU> `gkrJ7@  bY]Y?+?+?9933310327# 5467##"'732>533AL)/n >dI8A6WUlY!Vkr `gJ/^*@   bY?+??99933102&#"#33>I8=:WT?^ `g6mp/^3@ bY]Y?+?+?99331033>32&#"327#"&5?eI8=:WTHT=@DRJmp `g#da!^@ aY?+?9931034632.#"R^N8HG!Zc%^@ aY ?+?993104&#"'632jGH8N^RcZ! XJ A@   ]Y ]Y?+?39/+3993333310!#!2#4&#!! m΂~9vt LJx 3U[XJGoJ@ ?55hy^/M@'# )01,)!&]Y!^Y ]Y?+?+?+999933333310#"'327#"&532654&'.54632&#"y^FHS=@DQ|w~;=ptd>/ da!kXXJAZ:H7!Yl\V!YkLZ@/  ^Y]Y ]Y?+?+9/_^]_]3+393333323103#!"'53265#53!2.#"b]2/;H7\4>H7n!Yl<V!YkL^(@ ]Y]Y?+?+9323104&#"56323267# 87../V}}6I>4\dS!PlY!VL!P@+#  "# ]Y]Y  ]Y?+?_^]3+3?+99333233103!2.#"3##"&5463267#"\4>H7B5ZV!Yk9k{Upcb-Zf@?    dY/?O]YO_0?]q]q+9/_^]+3399333310"5>3 3#5!5!4&OKi0BHj>ZVH{\i!F@@  @  dY]Y?+?+339933331027# #5?3!!`9f5Hk=[ TVVH_fJh@6    ! ^Y  ]Y?+??39/_^]_]33+3399933333333310!33##'##"&=#53267!X7 1wɎwJNNOVы7=JF@$     !  ]Y]Y?+?3+399933333310#".5467!5!32654'5!mjoh뚘P2-H^/@ aY]Y?+?+?933104&#"5632#"&33265=P:;F;ߴlQoJ @     ??393310 #&'##qF)J\`{LJ3J"@  ?33?3999103673#.'## '##-("-- ,} &1JeiiJi(W&AJ+@    ]Y ?+?393993210!#&'##>32&#"HDCW?5D^t"9fhjϲ fXJ"@  ??39932310!#3 3Bsw{P\JG@& dYdY ]Y?+?+9?+9933310)5!5!!3267#"&5NT5HCN+}wGj[ PNBJ]@4   dY   dY   dY?3+39?+9_^]+933310!'7!5!5!3>32!"3254&),1NYviO7e6,ogxvtS[| uddBoZ:FZr6,fj90>K3-@   ]Y ?+?9/3933103>54#"'632G?CǺ66.s̻Ww5OGh@3-@  ]Y?+?9/393310#"'73254&'ОC>FɴmVr.69q^(@  aYaY?+?+993310"!2&#"327fP37br!4@;}&2y@?!o!!!! %+5]5XJ\\^"e@9   #$]YE]Y ]Y?+?+9/_^]^]]]+993333310 !3# !2654&' !"&54675&54697:V)uǚЗحg%9q%M@)' !!#&'$#^Y$$ aY aY aY?+?+?+9/+93333310%#"!254632&#"&# 327#5!xj#lVn~;?/;`Bi9+"#+bH`-jJ=#]@5 $%!cY` ]Y ]Y?+?_^]3+3?_^]]+9333231033##"&546;267#"4632#"&zXh9-Ny=-*??*-=JmzVocb<66<;88J6@     ???3939333310# 373#7O4$|} i8ulNVJ@]Y?+?993103!!XJLq',J@'.* #  -. ]Y']Y ]Y?+?+?+?9999333310!2&#"#467##"323.526=4&#"m/,ID1  vw  ǤV![iMw*  .y#!3R@+]Y  ]Y?+?9/39/_^]3+393333310!#53>54#"'6323#G?C߿P86.s̻W5PR@+ ]Y ]Y?+?9/39/_^]3+39333331035&54632.#"3###ݯӜD>FţYs.67Pq^%(g@8# '' ()*'dY&&dY   ]Y]Y?+?+?99?+9?+9933333310"323.53!!!'#'26=4&#"3w  TNsƤ{L( .y6ȋ!=Eq-:@F- 8(1+%1;<%"^Y-++]Y+)"5]Y".]Y ]Y?+?+?+???+99/+3999333333333310#"'532654&+5!#'##"323.53!26=4&#" ̢ysw  ɐ|HV}N( .y6!qN-%25>@R950 )#44%5<<5 )?@ 6dY 49 dY%3##3dY#!-]Y&]Y?+?+?99?+9?+9_^]+93333333310%3>32!#'67!'##"323.53!26=4&#""3254& Wwi(!9sw  {L8e6,}XT^9P)( .y6!=EZam1!F/a@2 ,,*!!*(01 @$!(]Y + +dY /]Y?+?+33?+99933333310#! #5?3!632&#"2654&'.547!3Hk%TX>otdN{t~;!`L/TjVHJFG>H7U>[!Yl SVHV!Yk+_f!F/:@O+%%#3 88 #%;<3 0# 0]Y  ((.aY(dY5  ]Y ?3+3?+333/?+9/_^]]+9999933333333310>32# '# #5?3!!3267&532&# "32654&_]ǡycHk=RCKm- T28bk>eRWiQ!OKF;sZISVHXm;Rp+"40G{M<09?1g@7%'/23'+!]Y+ ]Y+%%dY%]Y?+?+33?+?+?399333333310"'53254&#"#!##5754632&#"!3>32W;>=v}il0]F[X 6jȏkBT>?%xGVT&J@&  '(# ]Y ^Y?+?+??9993333310#"'#33 54&'.54632&#"ꬴ@w~;=ptd>/VBlAZ:32Ϣti&LR<032#4#"#2H;+3(Hti=G<(9FR!\ 5@ @Po V?]2/]393332107"'5326534632#"&TIDI22*<+,65-+3;>>/]~ 0o lE@gU1y?!@    V TU?2??9993310"'732653#';>B*]~ 0ty leW1!8@    @ P  VU?3?/]399333103267#"547#"'732653?'?/]:kM;>B*]~+B? !ABhT l J@)       /  V T?3]?39/]]]339933333103#!334+326Z\yRG{Xv@{@'"@ VT?3?3399910&'#3673?3D uv^Ff@3@II_7Y1g'RT!\/@  :  @ P  V?2/]9]39310373#"'532?$66nQ4I(q3&-_EZwdN NP + J -@ / /O_/]q33/]2102#52654&#+hxzf2A;8Juderf;66:+ J -@ / /O_/]q22/]310"&5463"3 fzxh2A;8teavg:66;'!*@ @H /29/+3933105654&#"'632RH/l*-n8;J"jDoMk!8!GH@3RI/10%5% ~R3P3RI/10-53~P3'^?/103# '3P^_@/10#33P@+}!v!C+_}H H++]5ydM@ p]5^^!J!@??99//]]10 !lJ!-J@ ?9/]10 J+ \+ \VV-7/210!!R{\7 @  _/]22/310327#"'9">/QT%p}KZ%zUK7N3#Z >@%  !" @P@P V?3/]q399933310#"&54673>34&'326.4oXXn.4½! ż  X?VspY=_7R=)T--T&'P@ T??9310#3Pjy!&@  "#WU?3?399333310#"'53254&'.54632&#"ᵩnxMo_/;eJKBpsw9Nh*;)0@Q;jy=:,&%428s+d 4@     V T?3?39993333310 33 # #`7/hX+D"@  T??39933310.54632.#"dB8z1Exa'-g*@ /3/99310!#!5!o @  //9/3993310!#!5!3oR$@ //9/]3993310!#!5!3o^ @  //9/3993310!#!5!3oV@ //399310)5!3@ //3993103!!$@ //9/]39933103!!#odiL  H H #?++]5N\@@H@H@ H+++55 H/+3?910#3dۢ%9H/+?29103# #d%=hj/@@H/+]?10%5% jNdۍhj/@@H/+]?1075-hN/d܎D /]10#.'53wCh-D476>? /33/]310673#%>73#S\!ByP&h FyZj;30G68?  /33/]310#.'53#.'53yA &c&y?#j!?39P.06M(}@     /]3210".#"#>3232673(OLG-2fqZ)QME,-h n#+#5>|#)#3>|hs@ `/22/]10!##Tmsmhs@ `/33/]10#5#5hmsmh; /22/1033mTmh; /33/10!5353hmm)V@  /]33/]310!3!53nsuVR)V /210!3!nsVR) /33310 5!!+Rdy{[!K2jMV+@@H@ H/++]210!!VV?NjO  jwc Po!S[!LR`?]10#RO @ `?3]210#!#ROO= %@ /_/]]22/]3210"&'3326734632#"& nTtda p =-08:.-=يF<>Du<7>56=8= #@/  _  /]]33/]2102#.#"#> n Uqg`p 슉I8@Ad ?10'63 4|B%f ?10#67 4|B%d ?10#'73%B|4 f ?10#67 4|B%3^ @ ` /]]10#.'53yK%v3^<89<3^ @ `   /]]10>73#5m!,?wwD?@1LB H/+9/310353#5#ᇇᾅ{LB H/+9/310##33ᇇDžmj@ _/]210!#!ڈjg-q /33/10>53ps"3s  /33/]210"&5463"3sgyxh2A;83vcavf;66:)V@ /]3/]33103!535DRb{{)V@ /]333/]105#5!#DV{{)- -@      /q33322/]/]q]1053533##5ד{{2eM@ p]5V kY/+/10"'53253><.8bk7jV kY//+10"3327b8.<-k ej@/  ? P  ]q55 0PW 2H ?+55~;99zY=Q6`RH H++]5Bm//]2/310!#5!#ڈ6b}& @ H /]3/+33310#"'#"&53326533265wjo55ohwop3@bs3@}q|GGzsBEBEi[L  H H #?++]5g[K  H H #?++]5?N H+]q5= H+]q5xR@ / H+q52eM@ p]55H/310!5!jŃY9]N@   /33/]210".#"#>3232673&HEA,* h eU(KE?**g d%+%;w++/10 L9}j ?/10 #py3s   /22/]3102#52654&#mhxxh2A;8udbug:66;Bm@ o/33/]310!3!53m&ʈB\5-@  0/33/]310!!35#fٴ-}$ @ H /]333+22104632632#4#"#4#"}wjo56nhwnq3@bs3?q|HHzsBEBE/3 "@o   / O _  /]q1077''7V{yX{{Xy{VyX{{Xy{VyyV{}@O_/]10463"#52654.r<;%+%t<;%+%FK]g.5>G(L^h,8>EW[@/?o]55S!C4K!v,xR~n93?5>xU@ @H+555=B@ `p/33/]210!#5!#B|W9[ `H H++]55B@ _/]22/]10!#5#|{w-<@$ / /]]2/]3//999910"''7&#"#>327326739[L\J-11i sa:LE\C0$01g v/}8x;32326734632#"&4632#"&+SOI"11i sa-UNH 01g q8(.2:&(88&.2:&&8%+%;3232673".#"#>3232673'MIF&*h hU*PIC+&f eW'MIF&*h dY*PIC+&f f$,8pz%6/pz$,8k~$6.n{?f  /33310%35 5#F /]9/3105#7#Bxx% !'/7AIS]gqy@w  &$622?2O2_2?O[ooVjv~~rzB88F< QeeL` .*0*@**$2jz<`**`j=-08:.-=n n Uqbd p L<6=56=8I8=D=7  /1077''7VidaegVidaeVfd`diVgeadT @ 0O_/]]10&'5673_x,uHmghG:,gT @ 0O _  /]]10#567&'5g4V)iSRj K.iH34f#@   0O_/]]2910>73#&'##567&'5 YRh,:,!hd/_'nO39CT2=@@% HO/O_ /]q333/]/+q10#7#73Fi%Fi%T) @ P`/22/]310 $'3!2$73qgUJOar)ƻztT3;@/_]5Bh@/_/]310!5!4בBi=P+5T-@ /`    /]]r33/]21023273#".#"#svhuuh.&/&}&/&}T3 /@"P` ? O o  / _   /]q33/]210 #&!"#6$b/UOa3ƻztT?/22310!5 5!+)%#&@ ) 9  X[Y?3?3?9/q3910'#"&54%754&#"'632%26=5fBoyV`?92g0+wRdQlc<5eA0na K<$h?ny3cZV/:?0.)3/@ ;/@ H[ Y?2?39/]+q]q310"&54632!3267"!.%14dQ8nfDQ 4G)ŨN!}RWMJZ5R@ ZX??]210#3'4632#"&H1# 00 #15+''+((()J  [Y?3?310#"&5463232654&#"J]WX\po)/@ Z XY?3??391032653#'#"&5^Y} &k<NlubVc2=w)  [Y?2?310"&54632&#"327=lX)i4XVYjN)+q#pw/{+)=(@/  @H X[Y?2?3?+q9910"&54632&53#''26=4&#"uOz $f.aOU]RP)e <Ce3>sknqo5/@  @H X[?3?3+9104#"#3>32^Y#o95iz*68wG 5"@ZX [?333?33?99104#"#4#"#3>32>32fvZLyTO} )_9=#vBzz5idkxd?2y<=xG)5@ Z X[?2??9102&#"#3>w)9AKj'Z t]TH)/@ Z XY?3??391032653#'#"&5^Y} &k<NlubVc2=w5X ZX?2?3103673N 5sWA\<V5T @  Z X?3?39910 373#'#T򤤢N)o@  `?]9910673#)VD;8we*)=@  `/]]9910#5673,XywC;f=Df^Cq^&FOb!@0!P!!!!!!%+5]q5Df^&COd$@0!P!!!!!!%+5]q5?ffJ(3`@5+&&1 145#aY .]Y  )]Y ]Y ?+?+9/_^]+_^]+93333310>32#"4632#"2654&#""32654&R/3}v~^ft1 £_+E[fdsGYo~zyuȋ}T{D}RF $.?3;D'TQ@ &%+5?5)&QjR@ +&( %+55+55o\^)\@0  " ( *+ ]Y%]YaY?+?+?+??99933333333210"'532=$47#654&#"'63 3327/D@3+9VT5qD^!<@!  "# ]YaY?+/+999933331032654&'.5!2&#"#"'sX\gnUe>zɪrNPB7: PNû} V@1    iYI  ? _ o    iY?+?9/_^]]+9/99333310#!#!!}XjJ X@2    /    ]Y?O ]Y?+?9/_^]+9/_^]99333310#!!!#!d6RD)#E@$ !!$%iY kY kY?+?+9/+9333333210!654&#"'632!327#"&547v4*0$/CP%9{5-9.(@C'1*.qf>eweI"<=~8uOBq\S3H@&     iY?+?9/9/993333210!4''&''&#"'>3 }{O5Rl\?V[~D\y/!R;V J64D9,SfQ@-   / ??39/]]9/9993333210%4''%&''%&$/#-*M+/wȫ:L VL](?~[fu.G@$'# /0 ! iY '(iY'#?+?3+3?339993333310%##"&'##"&533265332653#!5!26546 3w- :~vw zWedhbj tc䢓~*B)J,O@($ ) ,,) -. $%]Y$  ]Y ?3+3?33/+99//93333310%# '##"&533265332653#!5!26=472hN 5tߘntFSWX`=^=:BFs@@   iY iY?+??9/+9993333310&#"32653#47##"5432;cnͺ =؂x/J/`\k1q?`<@   !  ]Y]Y?+?+??99333310"32653#47##"32&!y} 2qY#/1ո8TFQY(& 9@ iYiY???+9/+99333310!#3>32!"&'53 #"G]UF{w^L>=g1s6E #J7@ aY aY ?+??9/+99333310>32#"'532654&#"#3b<~Sljn~d}.2<=b#J`-T@+# ++ #./'#+  iY  iY ?+?+9/_^]9993333310".546323267#"$54>7>54&H{{ϟ>acR}7)crL@mBɬK:R^Cn~<9dcr?4M_Ggn1^*V@-*((""+, " %]Y]Y?+?+9/_^]9993333310&54632327#"$54>7>54&#"=ڻۼƄ?YYʋswfps+bU;cmQ^.-nZ32&#"! !)U/0##!/D?X~G8JZ<@D0!"2B.1'qCcoY.E]dDo!!3^"G@&  " #$ ]Y  dY ?+99?3+3999333107! &#"!5'.#"'>327>32dw.7"_d$2% -1+F`)^\&`G8CZ (2{{f3# =:9<}!R@* "# iY iY iY ?+?+9/_^]+9399333310"3>3 ! ! & !"/6Tۘ'LoC'JB)XNdZuVPb˶qb^#?@   $%]Y ]Y]Y?+?+9/+99333310"!2.# 32654!"5>32m0ı6ILL3-i~&;;!Pӌv.(#/xj)J C@"  !" kY ???3+39/3933333102#>54&+##"#&546;3)3 >324&#"7>267! pv7Gg,6o X^Qj Wąa^Hv ~E{uT(6S]U] o ,%"gV\bjס5J#p@<   $% dY 5 #^Y^Y?+?+9/_^]^]]33+33933333333310#53!23##!3254#'32654&+PVծThyhvLJlBGrKUPEyX&@ ]Y ]Y ?+?+9310"327#"32&rJ\&E1'-HBJJ(@   ^Y^Y?+?+993310)! 4&+3 Ji#ž/JJJJ ~@R ]Y8i/o?O  ^Y ^Y?+?+9/_^]qr]]q3+393333310)#53! 4&+!!3 Jrr] &“t/ՓBJ Q@.   ^Y ]Y ]Y?+?+9/_^]^]]+933310)!!!!!Bn$AJϑV{X$c@:  %&^YXho ""dY"dY?+?+9/_^]q]]+99333310#"'532654!#532654&#"'632dudχOP²k\LaTBe4ΜA%/d\aSHR(=u{dJ'@  cY??]+93103##"&54632=-*??*-=J<77<;88^J "@  ]Y?/]+93210"'53253+FAF=6ՙ J 6@    ?3?3939333310!##3673 X* {?LXJ 5#+FJ L@,    @  ]Y?+?9/_^]993333103'737!=HJ`#wNH q{͖#J0@     ?33?3999333310!#33#47CBRZjJVNBJ,@   ?3?39999333310!##3&53B DWF;J iHkyZ (@  ]Y ^Y?+?+993310#"3232654&#"')  'HX&@  ]Y]Y?+?+9310"'732654&#"5>32ЎCprDaGB23)! &@ bY bY/+/+993310% 546$3 "3265oЊ -)~=?3+@   bY/+/993333104! '654&#"#&3#, 3?<O:8et%"Q@-   #$ bY bY /+/+99933333310% 547'76! '"6527qDsP -JsN`s3af/%sRw\vOyZ=1>Gm?}/Ekj#\+2t@C&0& /340^Y /?#]YaY),,]Y ?3+3?3++9/_^]^]+99933333310 >32#"&'#"=!!"5>4&#"326267!!?Јz>< \cPے| \psqpns m} ,%#ԟuJ#L@'  $% ""^Y^Y?+?39/+9993333331026=3#"&547&=3!2654! 3gnHSϘm~qhd+SݰΪRTƔgrzmy'Z @   ]Y ?+299104&#"#32۪' 'y' @   ^Y?+29910#"33265ᆲ') J :@  ^Y^Y?+?9/_^]+9933310+#! 32654&+i9X|w`J`i`^1J G@$   ^Y  ^Y ?+?39/_^]+9993333310 #.5463!##";9shHz~F*pQ]1JG@$ ^Y  ^Y ?+?39/_^]+9993333310#";3!"&54673Ӌ~zhs\Rp*E+J%@ ]Y?+3?9310!#!5!!7`J%@  ^Y?+?3993310#"&533265綂J==}~N`6@  bY  bY /+3/+993333310!2654&#!5!#!NJQY5w 3oN` ,j@8  $$**-. !'' bY   bY /+3/_^]+99//3_^]393333333310!2654&#!5!#!2#"&5462#"&546JRX56//642246//6422y 4m8('::'(88&'::'&8NB"v@D" "#$ bY "bY" "bY/?o /]]q+/_^]+9/+939933333333310!2654&#!5! 54&#!5!#!N‚JPZX`53lsᙌ /iN 4tJ @    ??2999103673## kJGdFVY)J"@   ?3?3399910!#&'#3673673Ӯվ ŰU4-`=Jh@YKVlG]@`VyJ 8@ ]Y]Y?+9?+993310)5!5!!y3By;yDJK@'  ^Y dY ]Y ?+?+99/+39333310!5!#"'532654&+5j 򄷽!ƟGVgdfZdFX#=@ $% ]Y!^Y!?+?+99993333104>7>54&#"'6323267#"&d?Z]]b7̅grqDV-JnW30RF?OBDu;8W@KS,&A!Z!.@  "# ]Y?3+3?99310327#"&'#"'53267.54632D,7?2U[X\YS6J9{J|sȢB\evsvs]ddHJ@]Y?+?99310!#HJLJJ @   ?3?99910!#&'#3$  j@TZLJJ%@ ]Y?+?3993310!#!#!bJJJmJ@@    ^Y ??339/+3393333310!##".53!3 3+ ij˔Dezquq|eJ)@ ]Y dY?+?+?99310!#! #"'532>7!2Gvi>=@**30IlwQ)@ 0IN?3?9/]39910!#3 &'%{ym  j114E S@/       N IN?3?33?9/39/]]q39333310!!#!!!!!!#pu012 O@   @I I N?3?29/]]+3993333310!2#!32654&+3254#5TPůpamw~Rr9RFPJ?`#c@   $%  @I#IN?3?39/]]+33333933333333310#53!23##!3254#'32654&+5PơMů箦pamw~aADhFPJ? @   IN?3?3993310)!24&+3 Luイ >@#  I N?3?39/]]q3933310!!!!!!^Jco <@"    I N?3?39/]]q3933310!!5!!5!!odJ`=Fq.@  JO?3?39/3933310!#"5432&#"327#\%;Eo5AAʹ >@"   IN?3?39/]]q399333310#!#3!3Ө!ߨ3\T 0@   I N?33?3393333310!57'5!kwwxx```D !@ @` I?]293210"'532653'=@G4<=JF% 8@     IN?3?3999333310##3>3R#k`Hy/+B@ IN?2?993103!0@    I N?33?3999333310 #3 3#47h+1\AmQF ,@    IN?3?39999333310##3&53blZo= ,@    I N?3?2999933331033#47#ӅT'EqB  @  JO?3?3993310#"543232654&#"Bߡm$A@ ! !!%& ##IO?3?39/39993333331026=3#"&547.=33254#"_hBNZiùEFfZj_](#jM&XahT 0@  NI?3?9/]39933310+#! 32654&+T`!Rr|n{VeXX8@    N I?3?39/3999333331032654&+#!2#Lpgnq}-ǼWWYK)hL='H @ NI?33?9310#!5!! !o @ I O?3?3993310#"&533265ըt~sȳs}u%^"@   IN?3?3399910#&'#3673?3Hؠ겞  R-&,"lTB^6bJ(Z#A@#! $%   NLO?3?3?9/]q399333310'#"&54%754&#"'632%26=j.q[{oIC7t61`m\yoBV4.zm VB)uH}qi^3BG62#=@ !!$% / KOL?3??9/]]399933310>323267#"&5"7>54&3sTzoIB8r81s`mZzpBT7+|m UB(wG|oj^4AG64h30@    K NLO?2?3??99933310"&5463273#''26=4&#"`)l326324&#"7>27!l]38HH&uJEvAAvI`+`y>3YeVpi}L !At *w8?X  RJxp65j]3AVb^0@   F NLO?3?2??9999333102#"'#3>"324&T}/f6f]_hZͿsg5U<;:vxh34@     FNLO?2?3??999333310"&54632&53#''2=4&#"Ni) 6k0`hVa\л=8MHi@55}hB@&   / LO?2?39/]]]]q39933310"&54632!3267"!.qgE{HuN\ [OзݿXpu 3aXTeb:@     ,   LO?3?29/]q3993333102#"&=!.#"5627!¢rgtpWиۼVqt=6bX`X ]@9 !"       /   L O?3?39/]]]]]]qq399333310#"327#"&547.54632&#"39{¶ulBUIQ>tkqnH7zi1W?ar%{8[bH]@9  ! /  LO?3?39/]]]]]]qq399333310254#"'632#"'53254+5;ǚ;g?9l{b[{qGJP%50=@ KN L?333?33?9993333104#"#4#"#3>32632\eU^Y .lAAVxpZxrrG8h=2@KN LM?2?3??9933310"'53254#"#3632^9@A(Lkb blhgNwpjwmhP  @  LO?3?3993310#"&54632324&#"PǰŰchcjef7պֶ}F @  LO?3?3993310632#"'732#"VV}c1*c%bs0ӿ/ 5h5P @   L?3299104&#"#4632he˨Ư5ضhP5 @   O?329910323#"&5ͨƱ5Թh^0@    K MLO?3?2??9999333102#"&'#3>"32654&Mj(1n2`i[]]ξ=8HNNh=8~~1#`.@    KO?3?399333310267#"&5#5?33#MT&|umq3m6%z zxN3{c>;5)@    K NO?3??39993331032653#'#"&51HLka -yDTPzq9DXf*@    /33/399333310!2654&#!5!#!XSPyoq9D HKkb -yD9@  !KN O?333??339993333103265332653#'#"'#"&5/dW_Y .lAC)KwpwqG6DC @  KN?2?39910 3673=կ@>n]O!(@"#L O?333?99310327#".'#"'532>7&54632uYMJT2,$E;HI'9nOF%1)&.5utECd w80B?y,$sh$?@" ""%& MG O?3?2?9/399333333102#"'#46"3254&+532654&sjhixk^P\fc򒅷2/ꔝ5>3bcXTNNh@   KM??399910#4673673'!Ŭ-&!%h:`qrFHlVfN)8@ $*+! F'O?3?393393333310467.54632&#"#"&%4&'326fSK~K^Hq50Coǰ@VLx|mZdkt,3hEbl#,}M-"*D:QrXn&eYirhh <@  !"LML O?333?3??933333310.54746324&#">״9B˄rЯJ9Tdsh@ϯϽVF^"ҫ}qhH /@!"KMK M?3?2??999331023327#"&/#.#"56-<.%P˰0$$0/>Yj,TLs)"/:ZtG0 yRq`F!37y`By,@  RP??]2933310#3'4632#"&76($77$(61--1---`yo%@  R PS?2??99933102&#"#3>2?31Vu/co efcKU5c P?5ac P?5  Q?55cR?5No8@  0    SQ?3?3]q99333310%#"'#4632%"32654&NnS`[HpeZY/8dսS;hr Q?55HeR?5^%,g@8# ** -.)^Y)))) &]YaY^Y?+?+?+?39/_^]_]+999333310"&'#"&5332653632!3267"!.oH4約}uXmX /^j_c=}sm-&!ߦ{,9@K 77*:;$!&)// @&/ ""-]Y4]Y?+?+??999/_^]3^]q]232999993333210"'3632#"&'##&#"#>325332673"32654& oo7!++ i eU)+f f qqPOx ;32533267326=4&#"hsw  #++ i eU*+f fX( .y;32#5754632&#"!!32673 ++ h dUil0]F[X**f h L;?%xG;<~^+4=@V'?5  ,7#++. >? 7,#.&55/555@*.@   1;;]Y?3+3?33?9/_^]333]2299933333333332310!"%#$'##>733>3 3>32>735#"%54&#"#-/i`] /jN 5t2+ g aaFBߙICmt+B* .2`xǖPZX`Ӟ 5,azZP):-[^ +~@H- ! %,-?!!!!@ H! !%%@ )]Y?+?3?99/_^]333/+]q33339933333332310!&'&'##>733>32>7354&#"a]Z ic\ 3o0.g]`w)) cpxQYϦ:5&d &+ {^,9@, 1$ 77*:;$&)/ @& @ H -]Y4]Y?+?+??999/+33_^]]22999993333210"'#5&#"#>3233>32#"'#32673"32654&#++ i eU @nw *+f f ;3233>32&#"32673++ i eU $?eI8=:WT*+f fj  ;3254632.#"32673#"' ++ i eUR^N8HG!*+f fT;3#5?3!!32673#[Q#^i6,* h eU"Hk=$**g dr_f S;32!5!32673AeT0-++ h dUCo*&**g d)w;323##"'###267!"!.@nb\ w VZP)N==ȵɴFJc@5     ^Y5^Y?+?39/_^]^]]33+339933333333103##"&=#533!26=!璒JCwsC9~ww}J!*|@G(!'((&+,'^Y5 ]Y "]Y ?+?+99/_^]^]]33+33933333333103##".=#5367!5!!.'5!26=!/o[]o)L|[zJӚ}z y"slͿ j{ -P@,++./ !]Y (]Y aY#?+?+?+??99333310"'532=#"&'##33632"32654&.8cmil0]F[X[jkT>?%xGo ^)6l@:#4-) )78  ^Y 1]Y *]YaY#!&]Y!?+?+?+?+?9/+9993333333310%7##"32373!#"'532=!#"'53265%26=4&#"oy 3 3>323?;?.8bjߙmt /jN 5tZjkÂJPZX`j^ N@)   !"  ]Y ]Y aY#?+?+?+??993333310"'532=#4&#"#33>32332#"'532="32654&w @nI8=:WTZ/TkEXXJAZ:H7k!Yl\V!YkjJ?@    ^YaY#?+?+?399333231033>73!#"'532=!E D.8b1bekJPjsJE@%  dY  dY aY#?+?+9?+9933310#"'532=!5!5!!s32332726=wus;#R|lwWD7S`Z65.0*ijgINA}w4 ,2H;cjrV\qj`^*W@.,( ! +, %]Y ]Y]Y]Y#?+?+?+?+?99933333310"323733327#"5#'#'26=4&#"3w[551-*YJsƤ( .LH;!q 5W@.++7 /%33/674 !aY]Y ]Y -(]Y-?+?+?+?+999333333310%26=4&#""323.=32&#"327#"5467#Psw  H?/;/2-8@&*e  v!ҕ*  .y6AapU!Vwqj^%Y@1'  ##&'" ^Y""""]YaY]Y#?+?+?+9/_^]_]+9333310"32!3267327#"5"!.Xj651-*YZ/+9m-H;ߦXj\^0z@D2( .." "1200]YE0000000%%,]Y%]Y]Y#?+?+?+9/_^]^]]]+993333333310# 3267327#"5#"&54675.54632.#"!וʔUd651-*YZsqcjoWDcJ9Y]'/H;c& ]%),Dj^.w@C#-# ))#/0-..-]YE....... ]Y &]Y!]Y!#?+?+?+9/_^]^]]]+99333333310 54#"'632#"'327#"32654&+57O_?}vu`651-*Y緽(L8$g}H;VV^\^[hju^!(o@B *&%)*&&^Y /?OaY"]Y ]Y #?+?+?+9/_^]q^]3+39333331023327#"#"=!.#"5>267!65.0*YXj[^9H;5 m-' !jG@(  cY` ]Y ]Y #?+?+?_^]]+933310%3327#"5#34632#"&bZ651-*Ym=-*??*-=H;J)<66<;88Djf^"6@ ""#$aYaY ]Y #?+?+?+933310732654&#"'>32#"'327#"V7271^H*64/0*Z@"sH;jL :@" !"]Y^Y ]Y #?+?+?+93323310327#"3265!2.#"b~~651-*Y19H7\4>H7BmH;YlV!YkjfJ!I@&#   "#! ]Y]Y]Y#?+?+?+?3993333310326533327#"5#'##"&5Xw}Z641-*YL 1wJ="32654&\(l=h_`i[[[мuih8=t~~h @  LO?2?3993310"&54632&#"327ױ·6($77$(6nlrTLYS2?:1--1-..h @   FM?3?93310327#"&57X%P?F|ruwyzzh3 @   F M?3?93210#"'532653szF?N&/)szzy<;@ KN?3?993103!!Zh'D@"  ()  KN $ LM?3?333?3?9993333310#"'53254#"#4#"#3>326326:>/eU^Y .lAAV7AqxpZxrrG8h <@ !" KM O?333??339993333103265332653#47#"'#"&5/dW_Y .lACXwpw:E>G6h;2@    KNLM?3?3??999333210#"'532653>3 #4#"7B4< & &Rjc\{4CXt=Dyh4@  KNL M?3?3??9993333103267#"54#"#3632;);C"̖jc e^?8 { Byrp}L ,@    KN?3?39999333310##3&5Lh)9`f'_@3    / K NO?3??39/]]33333993333333310!33##'#"&=#53267!1`mm -yDdd:faH'}q9DQ}'qlGTPDs:@     ! KO?3?39933333310#"&5467#5!32654&'5!KQǰRJjIUhcbk]Em5-Y³q7t mozzqet9 @   K O?3?39933102653#"&53\WW'ixNRxi9%@ KLO?3?3?933104&#"5632#"&533265",43B;ufPbaRB.v|ͰRvkhy @    N K??399910 #&'#--WU\PAR .@ KN?39?3999333310!5!5!!4v`'{lRhH9@ KN M?3?39?3993333310!5!5!!3267#"&5D4v&A 4Ec^`'{lC4{x|R55e@;    /  K N?3339?399/]q3]]933333310'?#5!5!3>32#7"32654&6x4vREZMc8?8E5,1*$`'{l\XCb!-h:@ MK?39?39/3393333310#"'532654&+5!5!ٴp54.54>54#"'632Z!(!!(!#+#2,.C6h & $$ZN?  7=   q@   /]3]210"#"&=332>;dNwp:n|k*cxWT%-%nmu#+#7@/_/]210!7!%Ej/@/_/]310!%7!/E%ٚ7 /2310!'!JEߙ/ /3310!'%!/EJNB/@ fviy_/]2/]33]]10%7%'E(EÛB/@ fviy_/]3/]33]]10'%7E(EHZ  H/+2/102&#"#36d'%&JCyiABn Y[PZTR @ /?/]10&'5673_x-uHmgiG:,g#@  0O_/]]2910#.'537673%#567&'5 NRh*=*#hd/_'nO=[T2:&('N Rz{@ &% %+5+5+5q&H&Nzo@)/*%' %+5+57&)O\R@ & %+5+5`&IO{@  %&+5+5};&*MR@&w%+5+]5q=j&JM ,+%+5%7&+OR@ &  %+5+5L7&KOR@ 8 % &+5+5%&+dZ   %+5L&Kd %+5%)&+jR@ !&  %+55+55L+&KjdT@ 8) %,&+55+55Z%&+z= %+5>L&Kz! %& %+5%&+N"   @ H  %+5+]q5L&KN$ H" %+5+]q5&,R%@ @H@ H! %+5++q5&LR'@ @H H% %+5++q5)wJ&,JR&@ 0@&( %+555+q555D&@  %+555s&.vR@ &%+5+53&Nv{{@ k %&+5+5&.dδ %+53&NdѴ %+5&.Mq@  p  %+5]53&NM@  p %+5]5&/d %+5s&Odm  %+5&/'MRd)@  &@ % %+5+5+]5:&O'Mdm'@_o@H&%%+5+5++q5&/M@  p %+5]58&OM@ p%+5]5g&/K! H# %+5?+]5gc&OK@ H# %+5?+]5{7&0OR@ & %+5+5&PO#)"%+5{&0d %+5^&Pd!#)"%+5N7&1OR& %+5+5L&QO  %+5N&1do  %+5L^&Qd %+5N&1M@  p %+5]5L^&QM@  p %+5]5gN&1K!!@ H!#! %+5?+]5gL^&QK!"" H"#" %+5?+]5}^&2 HR"H&!-%+55++55qh &R Hj!-%+55}&2 FR$0@H0&9E%+555++555qh&R Fj9E%+555}^&2 IR)@P` @H&%+55++qr55qh &R Ij%+55}^&2 JR)@P` @H&%+55++qr55qh &R Jj%+55os&3vXR@ &%+5+5{!&Sv =+' %+5o7&3ObR&Ѵ%+5+5{&SO"( %+57&5OsR& %+5+5/&UO %+5&5d %+5/^&Udm  %+5&5'M!Rd+@& % %+5+5+]5/j&U&Mdm % %+5+5&5MJ@  pԴ %+5]5/^&UM@ p %+5]5h7&6O=R@ /&&,%+5+5hy&VO$*%+5h&6d&,%+5hy^&Vd9$*%+5hs&6 CVR@ ;&N&7%+55+55hy!&V C "$5%+55h&6 DFR@ <&>9%+55+55hy&V D  <7%+55h7&6'O=Rd/&@ 28%&,%+5+5+5hy&V'Od9@ 06%$*%+5+5\7&7O/R@ &%+5+5!&WO+@0 O   %+5q5\&7d %+5!F&Wd/  %+5\&7M@ p %+5]5!F&WM]@ p %+5]5g\&7K@ H#%+5?+]5!g F&WKQ!$$ H$# %+5?+]5&8j#@/?P$%+55]q55DJ&Xj&@/?P' %+55]q55&8Rs'@ /@ H'%+5+qq5DJ&XR)@ /!!!!! H!* %+5+qq5g&8K@ H#%+5?+]5gDJ&XK!"" H"#" %+5?+]5^&8 HR @H&'%+55++55D &X Hy * %+55&8 ER1@!P-`- ----@H-&-,%+555++]qr555D&X Ew@ 0/ %+5553&9RR@  & %+5+5&YR # %+5&9d  %+5J&Ydm  %+5V7&:OR@ "& %+5+53&ZO  & %+5V&:d  %+53J&Zd  & %+57&;ONR@ & %+5+5%&[O   %+5)&;jR!& %+55+55%&[j   %+557&<O9R& %+5+5&\O %+5NDs&=KR@ & %+5+5Ps!&]K  %+5ND&=d   %+5PsJ&]dF   %+5ND&=M@ p    %+5]5PsJ&]M@ p    %+5]5L&KM@  p %+5]5!&Wjߴ) %+553&ZP & %+55&\P  %+55^J&D ,)%+5`&AO{@ &  %+5+5d"L@( " #$"kY  iY kY?+?+?9/+393333310 .#"#4! #"'53265!#?&w&~lU\9POSŴNO.2 q1&~s  -3%+5q1&~Hߴ6-%+5q1&~5-%+55q1&~۴>-%+55q1&~ "-@%+55q1&~ 6@%+55q&~ 6B%+55q&~6B%+55&$v%+5?5&$Њt%+5?5'$%%+55?55'$"~%%+55?55'$!%%+55?55'$!%%+55?55|'$"@o##%+55]55|'$"@o##%+55]55X1&) 30,%+5X1& /,%+5X1& *8-%+55X1&  7-%+55X1& W09%+55X1& B/9%+55'(p %+5?5'(c %+5?5'(Ŵ #%+55?55'(  #%+55?55'( #%+55?55'(ݴ#%+55?55L1& 9 %+5L1&  %+5L1&'' %+55L1&B& %+55L1&7 9( %+55L1&% '( %+55L&B $* %+55L&B $* %+55'+p %+5?5'+c %+5?5'+Ŵ #%+55?55'+  #%+55?55'+ٴ #%+55?55'+Ҵ#%+55?55|'+"@ o   !!%+55]55|'+@ o    !!%+55]551& %+51&%+51& %+551& !%+5581& U#%+551& %#%+55& (-%+55&0(%+55R',p%+5?5E',c %+5?5d',#%+55?55Z',  #%+55?55',5ܴ #%+55?55',5մ#%+55?55|',=@ o   !!%+55]55|',=@ o   !!%+55]55qh1&Rh"%+5qh1&R\!%+5qh1&R*%+55qh1&R3)%+55qh1&R )"+%+55qh1&R !+%+558&2up%+5?5J'2c$д%+5?5}'2#%+55?55s'2,%+55?556'2st--%+55?556'2s$t--%+55?55y1&{%+5y1&d%+5y1&#'%+55y1&;&%+55y1& (%+55y1& (%+55y&3 *%+55y&*%+55'<5c %+5?5'<?~%+55?55'<T۴%+55?55|'<@ o   %+55]55s1& 2. %+5s1& 1. %+5s1&:/ %+55s1& 9/ %+55s1& )2; %+55s1& 1; %+55s& 1= %+55s& 1= %+55k&vup#&& %+5?5'vc, %+5?5'v+'' %+55?55'v4'' %+55?55s'v}#d55 %+55?55}'v,Z55 %+55?55g|'vq"@ o   == %+55]55g|'vq"@ o   88 %+55]55q&~0-%+5q&~ 41%+5X&)&%+5X& 6-*%+5L&+Ҵ %+5L&H D %+5D&Ӵ%+5& /%+5qh&R˴%+5qh&R% 2%+5y&%+5y&- %+5s&Ҵ,/ %+5s& -/, %+5q=1&~'s @ I<% -3%+5+5q=1&~'H I<%6-%+5+5q=1&~& QD%5-%+55+5q=1&~& QD%۴>-%+55+5q=1&~&  @ QD%"-@%+55+5q=1&~& @ QD%6@%+55+5q=&~& @ _R%6B%+55+5q=&~& ^Q%6B%+55+5&&$@ ,.%%+55+5?5&Њ&$ @ ,.% %+55+5?5''$! @ 4%6%%+55+5?55''$#4%6%%+55+5?55c''$!@ 4%6%%+55+5?55c''$! @ 4%6%%+55+5?55k|''$+@ o@ B3,D%@**%+55+5]55k|''$+@ o@ A2+C%@))%+55+5]55L1&'@1$ %@ %+5+5L1&'@1$ %  %+5+5L1&&'@9, % %+55+5L1&&B@ 9, %& %+55+5L1&&7@9, %@( %+55+5L1&&%@9, %'( %+55+5L&&B@G: %$* %+55+5L&&B@F9 %$* %+55+5 @'+'p@ *,%j%+5+5?5 3'+'c@ *,%g %+5+5?5 q'+'!@ 2#4%z %+55+5?55 g'+'!@  2#4%z %+55+5?55 R'+'!@ 2#4%%+55+5?55 R'+'!@ 2#4%%+55+5?55 y|'+'*@ o   @1B%!!%+55+5]55 e|'+''@ o   ?0A% !!%+55+5]55s=1&'@ D7%(. %+5+5s=1&'D7%1( %+5+5s=1&'@ L?%0( %+55+5s=1&'L?%9( %+55+5s=1&'@ L?%0(; %+55+5s=1&'@ L?%1; %+55+5s=&'ZM%1= %+55+5s=&'YL%1= %+55+5 V&vu'p#ߵ=0@%&& %+5+5?5 s'v'c,ߵ>/@% %+5+5?5 'v'#+F7H%'' %+55+5?55 'v'#4F7H% %+55+5?55 ^'v}'##߶F7H%d55 %+55+5?55 i'v'!,@ F7H%%33 %+55+5?55 R|'vq'-@  o   ߶TEV%== %+55+5]55 R|'vq'-@  o   ߶SDU%88 %+55+5]55q&~N08%+5qj&~M-.%+5q=&~& B5%0-%+5+5q=^&~ :-%+5q=&~& @ B5%41%+5+5q&~R6B%+5q=&~&R RE%6B%+5+5>&$N/R@ %&+5+5&$MBR@%&+]5+5&$%+5?5&$Y(%+5?5&$%+5s1:@   0@ "H _  /]+]q293104632565"&=-27jxy-=4.D5mz K U.= -@  `Y `    /]/]+93310327#"&=$,6GJXo-) w^eYs?5R#/]@;*$$ 01  @ H  /@H'-! !!!/]q]3333/+]q2/+]3393310".#"#>32326734632#"&%4632#"&+SNI"21^ ja-UNG /2\ m8('::'(88&'::'&8$6.l}$6.qx6..651156..6511L&&+* %Ѵ %+5+5L^& " %+5L&&H@* %D %+5+5L&R * %+5L&&R@:- %* %+5+5'(:y%+5?5'(%+5?5'+:y %+5?5'+%+5?5&+  %+5?b14@ o_/]]]29310#&'53%4632565"&bVED=-27jxy-=٥j4.D5mz K U.h14@    o_/]]]293104632565"&673#w=-27jxy-=DP{V4.D5mz K U.j$F@.## %&  @ H   / _   /]2/+]339310".#"#>32326734632565"+SNI"21^ ja-UNG /2\ m64iqqyj$6.l}$6.qxs*.j]] D6&N %+5j&M %+59&@ *%+5559&@ *%+555&R %%+5&@ %%+555>&,NR@  & %+5+5,&,MR@& %+5+]5/',:s%+5?5T',%+5?5+D14@  o_/]]]29310#&'53%#.54632DVC>>-ywj71->٩2.U K zm5D.h14@   o_/]]]29310#.54632673#;=-yxj72-=:DP{V2.U K zm5D.j#F@.  $%  @ H  "/_/]2/+]339310".#"#>3232673#.5432+SNI"21^ ja-UNG /2\ mjyrohj$6.l}$6.qxsV6D ^\jy&N+ %+5yj&M-%+5y9&%/%+555y9&%/%+555f1& '# %+5f1&\&# %+5y&R*%+5y&*%+555>&<NR@  & %+5+5&<MR@  & %+5+]5'<):i%+5?5'<% ɴ %+5?5'3c]%+5?59;@!   ! o /]q]]22393310#.'53%4632#"&%432#"&V?q@H6(&88&(6_%92,*5N7{@6./55225d/5-:29;@!   ! o /]q]]22393310673#'4632#"&%432#"&:#q=V6(&88&(6_%92,*5 ;KX6./55225d/5-:2$@ o_/]]]9910#&'53VCD٩js=&'=0%Ѵ+( %+5+5s=J&5(%+5s=&'@ =0%-/, %+5+5s&R 1= %+5s=&'RM@%1= %+5+5R'2:%+5?5&2D@ -%+5?5'v:%ܴ %+5?5:&vD@ "'$$ %+5?5N&vB/ 1%+5 $@ o_/]]]9910673#>CV1:@   0   @ "H _/]+]q29310#.54632q>-yxi71->2.U K zm5D.+//10#+V!@   //310#'7'77'+V5555X7777  //339210'7##!'77RJ75}o}7L)  //339210##'7!)T77L}57}RB'/399105!R `'_ _@   ?32910#&'7##&'7`$C-j$C6e ^mrY /3/10#!+ToPN) /3/105!#NTP@  //]210###!V @   /33//]3310###!!!VtP @    /3/]2210!!5!###tVPN6& q #/@2 $*01* H}Y '%@? H!}YP!`!`!!!!O! !! '*---- H'-}Y'%@ H }Y ?++_^]_]?++_^]_]9/^]]q++_^]_]9/++_^]_]99331074632#"&4632#"&4632#"&4632#"&@?=BD;=B@?=BD;=BA>=BD;=B@?=BD;=BoBIHCCIJyBIHCBIJDHHDBIJyBIHCCIJ!@  /33/]23103#5!#3###VPPN @  /3/]2210!!!##32#"&2654&#")J17L oU{FN`USOpiy+;^\^NXUeX`RN\V9`mZUn Q?55hUp Q?55hUPp Q?55'`#b 4@     R P?3?39993333310%33 # #Fﻶź{ybUp Q?55JF@R  !mY*:  mY mY mY?+?+9/_^]+9/_^]]q^]qq+33933333310)#"5432&#";!!!!!F'ɟ~BofzVq@:ϲdh"*0@I .&+ +&- (!!)(- 12-( kY .##kY"O_/3/]q3+33/3+33/39333333333333310&'27##7&'#&4?37327"& )FAG;%DžQj%'YG/<'#H.%-'DX)Zx -9! %~S4R,́9sL&M@(% " "'(% #kYkY"  kY?+3?+9/+99333102&#">7# !2.#"36;>B+]~J_Vb@٦J;b~ _ l #l\V13qT'_@4 &&() &$ $]Y@'''$?3/?33/]+3?993333333310632##"##54&#"#33>325R^ߙmt /jUT!5uJPZ!%*/@g- )$*("(&$ #  !,++,#$01" -mY ( %mY/*/ ?3?39/]q+9_^]+9933333333333333333105#533333#3#####5%#3!3'#3''# 4񢠠)== VZ VG{e{߿ X <t@=/  )6)"")=>%")96,3^Y, kY kY ^Y?+?+?39/+3?+99933333333310#!2#'32654&+#"'53254&'.54632.#"X+nk!s|^59Gol^.W;B9e9GNIqj^.\Џ2q\~A%1=R@7#iXP^aLTe{Vb\\<;wr "X-I"H wcVXL@*    mY/   ?3?39/]3+3393333310!!####53333V)s53T}'oq@@       @ iY?+3?99//_^]99933333331077#'%5'%!5!!IIǸJ:J:7H9ťlϦmFPṃl1y9_@6#0 0*+(7:;))(lY( 3kY(+kY(lY?+?+?+?9/_^]+9/93310#"&'##>32%"32654&2+332$54$#"'>ye|5eH%əSj(ocJT4!VJ>Lol)^~% htjXrHRz@&"%-!"+Z@/ '#,-*   %lY  lY ?+?+??39/2999333333310#"'532654 #&546324#"6vlcHRRH=P~&PUqu;.}j!YWf-9xR_!&,@*#",   # -.nY" P +nY/   ,kY? O _      &kY?+?9/_^]]q+9/_^]33+33_^]qr_]22+339333333333310#3#+##535#53! 34'!!6%!&+267!7r?U-'J׏hy*V%J'Eo#on#))=VAJ}\;!i@9   "#iY @ H  iY kY?+33?+333/+9/+93333333105$%53&'!67R-FBvS?"OGRO3C + "{@C    #$ mY  mY  ?3?9/33+33_^]22+339933333333210!!3##!##537!5!3!'!73'&'8=˜ªî<4;-I"P僨#υOP0@O.&//, ..,12mY 0/0/mY00?0O0o00000) iY)#iY)?+?+9/_^]3+3_^]2+393333333310>7!5!654&#"'6323#!!327#"$547#59z6:kZ3?E{c/L-9_epNRʫXBV?9^n~a"-ܿFA}\N@*   iY @ H  iY?3+3?33/++39333333105$%53&'67BԛJܤU@MG LFf56x@B   mY mY  mY?+3?9/_^]]+99/3+3933333333310!.+5!!!!#53267!-P}b}- sZrgN1{Zdu"` dD"2]@7# ++ 34       p  /'?3?39/]39/]9/q3933333103254&+#!2#%4$32#"$732$54$#"吪SY/^^m+*լ֭ۢQIE^ZƬ֭+*  26u@E&  5- 3 5 7865 E   p 0 - * #P#@###?33/]q399?33/]39/]]]3??93333310'##3/#"&'53254.'.54632.#"#HIEF>t%'3??i\w;y+#&i-968\qZݕ"@3CF[i}N"!#$_P[hm)+-&aJ=J .@  ]Y]Y?+?9/+933103!5!!5!nCJ_.'`&{u?51-'&tu$?5FX&@]Y ]Y ?+?+9310"'632#"&'532654&pE'bDrBH1d^D 1@  p /3/]/93310#&'5673!9>HH>9)DH$HDV @   //293105673&'#DH$HDV:=HH=:#d^D 1@    p     /3//]93310&'3#67!59>HH>9#DH$HDV @    //29310%67#&'53+DH$HDV9>HH>9d^D?@%  p /3/3/]39333310#&'5673!&'3#679>HH>99>HH>9)DH$HDDH$HD(@   /2/29333105673&'67#&'5DH$HDDH$HD:=HH=:9>HH>9H0@   /2/2293333310!!5673&'67#&'5 DH$HDDH$HDhPX:=HH=:9>HH>9yH^y`@ //3993103!!^j8^@   /3/29933104>32#4&#"rуwfŠdHP @@&  P_ / ? o  /]3/]3/]3993333105!5!5!d8d7 @  /3/29933103 %! {RVj-Gn@#@  /3/99310#4632#"'&'&#"}?L3% &!" @/)3 )''#i@  //3993103#"&546323265#9P3## #>/'5)%37/333105! h//93103#בK7@ /2/93310!!#'k7n7@ /3/932105!# )@ //3933103!!n@ //3932105!3  @   ///39333103!!#knn@  ///39332105!3# K7@   /32/9323105!!# kn@   //339323105!3! n (@    ///332933323105!3!!# knn6@ _/]]3_]q/_]33333105!5! BZ@  /2/39933103#3#ّiK K >@!  _ /]]2_]q//_]39333310!!!!#'kkב"7 &@   /33/39933310!###ؑ7nn# B@#   _ /]]2_]q/3/_]399333310!!#!!#iro" :@ _/]]3_]q//_]393323105!5!5!# i)ג(7 "@ /32/399332105!### ݑؑn @@"  _  /]]3_]q/3/_]399332310#!5#!5!ӑtt(F(ޑ <@    _//]3/]]3_]q93333103!!!!k#ב $@   /3/3399333103!!33B%n#n @@"      _/2/]3/]]3_]q993333103!!3!!ّJ%i# 8@  _//]3/]]3_]q93323105!5!5!3 iג) "@ /3/339933210!5!333#ؑn >@!        _/3/]3/]]3_]q99332310!5!3!3!5!#LF B@#  _ ///]]3_]q/_]3933333103!!!!#kk#ב" *@    /3/3/3993333103!!#3#Bnn K L@(   _    /3/3/]]3_]q/_]39933333310#3!!#3!!jr " # >@!  _ ///]]3_]q/_]3933323105!5!5!3# iגK &@   /3/3/3993332105!3#3# 㑑iK#K J@'    _ /3/3/]]3_]q/_]399333323103#3!5!#!5!B㑑tK (ޑ B@#   _   /]]3_]q//_]32932333105!!#5! ki"h7 (@    /322/3993323105!!### ؑnn N@)    _ /]]3_]q/3/_]3339933233310#!5!3!!#!5jtrrBޑ"ؒ @@"  _  //]3/]]33_]q932333105!3!5! BZ# (@   /3/333993323105!333! ؑnn L@(         _  /3/]3/]]33_]q399332333103!!3!5!5!B#oV@-   _  ///]]33_]q3/_]3329333332333105!5!5!3!!!!# ikkג#ב">@      /3/3/333339933333323103!!###!5!33Bؑnnnn d@4  _  /3/3/]]33_]q3/_]333993333332333103!!#!5!3!!#3!5!Bؑtr#ޑ" //3310!!V//3310!!V//3310!!V //9210!!+ //9310!!+ *fw% #'+/37;?CGKOSW[_cgkosw{@"2Jjj#3Kk.FVznn/GW{o6Nff7Og*BZ~rr+C[s :Rbb ;Sc&>^vv'?_wwcsgokcgkk`dh_[WW\XTOSKKLPHC?GG@n?o*Z+[BrCs.^/_FvGwA#&.6>JFF'/7?KG"*2:NBB#+3;OCCG@ PQӸCи@ԯ?<;87{{|4xgkosw3ccdhlpt0`OSW[_/KKLPTX\,H7;?CG+3348<@D(0#'+/' $(,$ #  KH@Ԩx`H00H`x DAGO   L/333/3339//////////33333333333333333333333333333393333333333333333333333333333333333333332333103#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#%3#73#73#73#%3#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#'3#'3#'3#'3#'3#3#73#73#73#73#73#3#73#73#73#73#73#3#73#73#73#73#73#3#3#3#3#3#3#3#3#3#3#3#3#fiiiiiihhffffffffffhhiiiiZiiiiiihhffffffffffhhiiiiZiiiiiihhffffffffhhiiiiffffffhhiiiiiifffffffhhiiiiiffffhhiiiiiifffffffhhiiiiffffffhhiiiiZiiiiiihhffffffiiiiffiiiiffiiiiffiiiiffiiiiiiffbbbbbbbbbbbc^^^^^^^^^^^````````````e^^^^^^^^^^^`aaaaaaaaaaad^^^^^^^^^^^`cccccccccccb\\\\\\\\\\\bccccccccccc^```````````bbbbbbbbbbb%```````````bc^``e^`ad^`cb\bc^`b%`C%IMQUY]aeimquy}  !%)-159=AEIMQAS@zKk h| OoLldi}SsPp`"eWwTt\&a# +P;;Xxؿ)H*99]'A!/L?? ,Q<%D.55 &I+648<@DHH0M@26:>BFFA "E/22@6<@ ؾ RS@  $(,0ֹ:>Gʽɹ59ȹ48+/D*.C%)$( !@ ?| {~ osw <knrv ;jj\`dh[_cg@OSW8KNRV7JJDHCGLP@4 kKKk  ׹;?HKO@ 3"&*./333/339//////////333333333333333333333333333333293333333333333333333333333333333333333332333310!35#35#35#35#35#353353353353353353353#3#3#3#3#3#335335335335#3'#3'#3'#335335335335#373533533535!355#%355##5##5##5#353353353355##5##5##5#35335335335#3'#3'#3'#3#3'#3'#3'#335335#3'#335335#3735355#5##5#353355##5#35335#3'#3#3'#3+jjjjjjjjjjjkjkjkjmkjkjjkkkkkkkkkkkkkkjkjkjmkkkjjjjjjkjkjkjmjjkjkjmkk?kkmkkmjkjkjkkjkjkjmkkmjkjkjkkjkjkjmkkkjjjjjjmmkkkkkkjkjjjjkkjkjjkkjjWjjjjkjjkjjjjkjjkjjjjkkjjjjj!c c"a c!b!`````````````b^^^\`h^^^^^^^^cccccccb^^^^^^^^aaaaaaaa^^^^^^^^^^ccccccccb\\\\\\\\cccccccc^````````bbbbbbb bbbbbbb^^^^cccb^^^^aaaa^^^^^ccccb\\\\cccc^````bbb bbb{Z//9910!!{!!@  /2/3993310!!!7L17}1mh{//9910!!hmh{@  /3/3993310!!!hKPb//3310!!L//9910! XVRZ//910 7L//9910 LRZ//9910Z79e-)@  ! /3/39933104>32#".732>54.#"wx{yy{xwV`bcb`c``dyyyyxyy{b``bcbbbV^R'/7?GOW_gow@X\PhhTl8xx<|(pp,t ``$dHH L@@D0044DLdt|l\ JrvNvvvBz~F~~~2jn6nnZ^^RVVVVV:>>> *..v~n^V>..>V^n~v&f0b@bb"0p&/&?&&&/]]/]9///////32]32]3232q32]32]3293333333333333333333333310#"5432'#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432#"5432%#"5432#"5432'#"5432377349947575#3773865567557R75577667377349944976#58856556\677675577667667+557775555885Z557w55763:C558337775555776557737+558#  //99102#"54>jmsoujlw)@  //9933103!32>54.#")wvuwwwvu}Bwwwutww)#'@ $%  //933103!4>32#".'32>54.#")R`babbab`Nwvuwwwvu}B`bb`c``cwwwutwwsbu &@   /33/]3993310#"&546324&#"326bcfdiFIKgFEgcIN_}khfJHfFffFHdhy $0:k@%+ 6+:;<51@) H168833@ H(.""O"_"3"3"  /  /]3/399//]333+3]+29333310#"'4! 4#"32#"&54632#"&54632327#"'ybbZٗ31Z-!!--!!-+!!//!!+LL=`bHgj34V// -- // --# #-S@4( $./)--!O_-&+++@ H++  /  /]/99//+]]]32]3910#"'4! 4&#"326%4&#"326327'#"'bc0!--!0.!//!.bb>KLHgj{ -- // -- //ۺ#Fs;)5p@=3$-%-"') 3 3 67"   $'0*()/3/39/3933333333333333310373#'#5&''7&'#5367'767"32654&BAe;-VL12WXByd+N P)opÉ;'--qt>}`+%* J-d}>^1N =LJŇP'$T@+  ""%& /@/3/]99//]333339333333310.54632!!#!5!"327654&qqwTVhL%Xw{TV;>wBh}VTylFFxUVy>=TVwR,8F@#0',!66,'9:,*3$-$*/3/399/99933333310&'&54763327632".'#"&54632"32654&+ !C=#"&5467>!aFXdP{XZ[Xe`Ga^%%`]ZRw;3C@&'. 45*   .#  //99//]99233]3910)7>5'#"&54672&'&5463267632#".'Fw^69Zs\=e% qtET'iCJt8v_=1op#8wL/yuzs3B'$'ykVb'NKuu2Qi}x6fZy@   /3/99910./.54632>32bZ[K6dV'!XaXoV{eAkswwucVB   //9910&'6J{FT+t}FiRmX;.@  //9/93333103#654&'#"&54632Ll^/9r@k99}M+/dyyw {7-NsB@#      /3/933/9333310#"54632#"&54632%%5_s{N/+ىs9:wJ6.'DeQovt5-Luguf7u@E    !       `//]q/9///99933333333333310##57573%377```J`X=@`^``F\^` Z l@:   lY lY ? iY?+?9/_^]3+3_^]2+39333333331035#533!!!!!!#\\ɳX꒶ݤ_@1   ^Y   ^Y??9/]3+3_^]2+39333333333103###535#5333#b?Y@0   / @ iY?+?9/33_^]]2299999333210"'!!&#"#>32332673V,* h dU#**g dNN ;323#>=4'7T#R|klfWD7S`&"d_Mq^`DVgIjt++B4 ,2)%mck,fcQ;!VF j@7"   !"    dY]Y?+?+3/33?/99933333333310%267#"'#&5#5?333###^i6_A6Hk%,Q NQVHu;yE@$  " ]Y]Y?+?+???993333310%##4&#"#33>32w 1qʘ}8Z@PZE@$    " iY?+???39393333310!#373 3#~ ᜱ/Fk`J@'  " ]Y?+????93933333310!#33?3 3#Z}=F_DfmmӲNTs+#}ND C@"    "iYiY?+9?+9?933331035!5!!#NyPsJ =@   "dYdY?+9?+9?9331035!5!!#PNTwG}}Z":@   #$iYiY?+?+??9999333310"32>54.'2373#'##"$5ʞ[\ƙ;  9奻G+\<[ncJ`pVa1^"@  ]Y ??+?933102&#"#3367>C2%#0j  4Z^ 69Jy"QOH8>]I#*@ $%  kY?+?3?399310!#&'#367367>32&#"ǻ? 6}.865ra:$#%' KsHrgZ ,'{^(*@$ )*   !&]Y!?+?3?399310!#&'##337673367>32&#"Ӽ2* Ӻhm Ľ  ;g`RC2%LjMbJkZW>Zk#OMIGTR oh $^@2"&   %&$!  ]Y  $!dY$??+?9/_^]+99993333310654&#"#527&546323673#2('3>Oxk)3rqwdKT'WO@+99TZpit4{T;@" iY8i0??9/]q]]q+993310!#3!sVyJK@- ]YM]}??9/]_]q]_]_]q+993310!#3!y鴴J7q\^;@  ]Y aY?+3?+?9333310 746324&#">ыYO^xeGO?*`u{#^Qrr!,@ _o/]/]99//993310'%'JII)IImmml!h`-@! / O _  @/O_/]]/]31034632#"&ӕ+?,+?:0,?!?#;77;6=8{%@ / O _  /O/]/]3104632#"&5ij?,+?:0,?ii;77;6=8#@  / O _  _/]3/]310!!4632#"&@?,+?:0,?ok;77;6=8_/]22105!53llT;@?39310 !#!b;{ T@?39310 !3! &@  * /33/9]993310#34632#"&Nu3A>>AB==BX7BGI@?LJ$@ % /29]9933103##"&54632u3C<?BD=;B;BHHB@KJBIHC@KJfu-"@ /3]2993310!55!-9?9910#3Nu35?9910#3Nu35- ;@)@P``p0 _/]3333/]qr10#"&54632#"&54632'673#3..2:&)88'.2:&'8 09y3046.52255/6.522dY ;@)@P``p0 _/]3333/]qr10#"&54632#"&546327#&'533..2:&)88'.2:&'8y903046.52255/6.522gYd+G* @ 3 @  _   /]33/10#"#6$!3 N`" zt3 @  _/]22/103 #&$+ #bO 3Ľtz 9@'    /_/]q33/].]].]]10.#"#!2Oh. $@ /3/99993310!#!5!3d+\+ $@ /3/99993310!#!5!3d+%F $@ /3/99993310!#!5!3n!ZZ @  /3/999310!#!5!3n!DN @ /3/999310!# 7 3g\=H`3R "@ /3/9/3993310!#!7!3bH3o`RN @  /3/99993310!#73^J^sN @  /3/99993310!#5 73gRۇjh0hF@  /3/999310!# 73qZyfkFq @  /3/99993310!#73q_ZGd)5 @ /3/999310!# 7 3RkGHyN!5 "@ /3/9993310!# 7 3Rk`/L-!P "@ /3/9/3993310!#!7!3ysdH^- @  /3/99993310!# 73`wcFFH@ /3/999310!#73vq=Df-q @  /3/99993310!#73qFsGVH!% @ /3/999310!# 7 3>{GHZ1#% #@ /3/9993310!# 7 533X3'5g% #@ /3/9993310!# 7 3D{rVJ1}q' "@ /3/9/3993310!#!7!3by R1X/@  /3/999310!# 73HՇH7%q @  /3/99993310!#573q>{G\1# @  /3/39999310!# 7 36}MH)R/) $@ /3/39993310!# 7 530XAuP/j5 $@ /3/39993310!# 7 3NN-qT+o5 $@ /3/39993310!# 7%3Nu7T+}/@  /3/399310)733+NT-)q@ /3/999310!#73qB=T-)P@  /3/999310!#!'!`sboL #@ /3/9993310!# ' 3\H6fy3L #@ /3/9993310!# ' 3VH)hy?L #@ /3/9993310!#5' 3THhy-^ @ /3/999310!#' 3P5fy=Lq@ //9999310!#'3q^hy"@ //9/3999310!#!5!3qT; &@ //9/399993310!#!5!3`/h{T &@ //9/399993310!#!5!35%"@ //9/3999310!#!5!3@P@ //999310!# 7 3i``ybVL "@ //9993310!# 7 3e`;H`FF $@ //9/39993310!#!7!3gb+\t]^L @ //9993310!#73^'ezL@  //9999310!# 73e\هseRdZLq @ //9993310!#73qe^=ddZ/@ //999310!# 7 3Hu7T`H?9 "@ //9993310!# 7 53Ru9HpHF? "@ //9993310!# 7 3Mw\sX=T9H? $@ //9/39993310!#!7!3lwo V=1?@  //9999310!# 73\w߇5:=?q @ //9993310!#73qXw1-B=+5 @ //3999310!# 7 3N}5H)1q5 #@ //39993310!# 7 53N}<Au1]j5 #@ //39993310!# 7 3NN-q -+o5 #@ //39993310!# 7%3Nu7 -5@  //3999310)7331N߇ -P5q@ //9999310!#73qN3 -yL@  /3/999310!#!'!nwJ{L #@ /3/9993310!# ' 3nH J{3L #@ /3/9993310!# ' 3nH)T?J{?L #@ /3/9993310!#5 ' 3nHu^J{-L @ /3/999310!# ' 3n5RjJ{=hq@ //9999310!#'3qo;L{L@ //9999310!#'3kNf{F $@ //9/39993310!#!'!3bj\L "@ //9993310!# ' 3\H5gyFL "@ //9993310!# ' 3VHiyPL@ //999310!#' 3ifrq\%Lq @ //9993310!#'53q^g"@ //9/3999310!#!5!3XV9ZB^ &@ //9/399993310!#!5!3cf)9% &@ //9/399993310!#!5!3b-%w"@ //9/3999310!#!5!3ZV@ //999310!# 73f^ q%^`V "@ //9993310!# 7 53o^h^xX "@ //9993310!# 7 3q\3Hmg1ZV $@ //9/39993310!#!7!3bL+Vm\V@ //9999310!#73'^{^\q @ //9993310!#73qu\/3g7 @ //3999310!# 7 3=q+\=LD7 #@ //39993310!# 7 53Po+^-L i7 #@ //39993310!# 7 3Po?J?LL)7 #@ //39993310!# 7 3PqX/XLBb7@  //3999310)7331PqLb7q@ //9999310!#73qPq?L{H@  /3/999310!##'!y\1F #@ /3/9993310!# ' 3{Ny1PF #@ /3/9993310!# ' 3{Hb1F #@ /3/9993310!#5 ' 3{HtD1\F @ /3/999310!# ' 3{HX1)Fq@ //9999310!#'3q{91@  //9999310!#' 3wbL^=5 $@ //9/39993310!##'!3ws;s=9 "@ //9993310!# ' 3uHXqHETJ9 "@ //9993310!#5 ' 3uHHG}9@ //999310!# ' 3uHHG9q @ //9993310!#'53qw>K@  //9999310!#' 3[VHZdbVs @ //9993310 '53#/[{b $@ //9/39993310!#!'!3[HVbk^ "@ //9993310!# ' 3[VHB1by@ //999310!#' 3_VHLdy}Lq @ //9993310!#'3q^d+"@ //9/3999310!#!5!3LR= &@ //9/399993310!#!5!3aNA%1o5 &@ //9/399993310!#!5!3qÇ"@ //9/3999310!5!3#d+VL @ //3999310!# 73e\-yfL #@ //39993310!# 753e\ }VyfL #@ //39993310!# 7 3e\j?yf)L #@ //39993310!# 7 3e\=J3yf6P@  //3999310)7!31i\y whPq@ //9999310!#73qi\;wh/@  /3/399310!##'!l-T5 $@ /3/39993310!#%' 3H{-T75 $@ /3/39993310!# ' 3HJ-T+ $@ /3/39993310!#5 ' 3H/P% @  /3/39999310!# ' 3}H)/R))q@ //3999310!#'3qÁ)-T/@  //3999310!#' 3TH7H' "@ //39/3993310!##'!3eyT19% #@ //39993310!# ' 3{Nq}1JV% #@ //39993310!#5 ' 33h5@% @ //3999310!# ' 3{H#1Z%q @  //399993310!#'53q{Šy1H@ //3999310!#'3vfD=- @  //399993310!#' 3mwbFFcP "@ //39/3993310!#!'!3sXdH^5 #@ //39993310!# ' 3kL!-L5 @ //3999310!# ' 3kL!Ny-q @  //399993310!#'3qs!HVF@  //3999310!#' 3RZZjfyN @  //399993310!#' 53LRHh0hjN @  //399993310!#'3}^s^JR "@ //39/3993310!#!'!3bqlV`sN @ //3999310!# ' 3\H3`Fq @  //399993310!#'3qZ)d @  //3999310!#!5!3D $@ //399993310!#!5!53'ه) $@ //399993310!#!5!3sD $@ //399993310!#!5!33\+Շ\q&~ ?HG%+555q&~ @0/%+555q&~ A0/%+555q&~ B0/%+555q;&~ W3-%+555q;&~ V3-%+555q;&~ U3-%+555q;&~ T3-%+555& ?x@ +*%+555& @x@ %+555& Ax@ %+555& Bx@ %+555;& Wx@ %+555;& Vx@ %+555;& Ux@ %+555;& Tx@ %+555y& ?0/%+555y& @-%+555y& A%+555y& B-%+555y;& W%+555y;& V%+555y;& U%+555y;& T%+555& Sx@ +*%+5555& Rx@ +*%+5555& Qx@ "%+5555& Px@ "%+5555y& S0/%+5555y& R0/%+5555y& Q3-%+5555y& P3-%+5555{ <@  iYiY"?+?+??9933310%#"'532654&#"#33>32 Ͼb:GUfo 6s un;\u{N;@  iY"?+?3?3999333310"'53267##33&53b:GUfm {unmV"U@- #$/?iY iY?+?+?9/9/_^]93993333310!"=332654&#"#33>32㹶 5uĵɾռ[v&W@/&  &'( ]Y   #]Y#]Y#?+?+9/_^]+993333310!2654&+532654&# '4632#"$5bPmXϴȻӝy з3ǻjL@2 @ H/_ /]33/+33/]/29310#&'53%46325>5"&!!VCC=-27jx8A-=4_4.D5mz L0+/jO@3    @ H/_ /]22/+33/]/293310!!46325>5"&673#j_F>-17ix8A->:"CVZ4.D5mz L0+/j L@2 @ H/_ /]22/+33/]/29310!!%#&'53%#.54632j_:VCC=-A8xj72-=Zݩ2/+0L zm5D.jO@3   @ H/_ /]22/+33/]/293310!!#.54632673#j_ >-A8xi71->9:"CVZ2/+0L zm5D.h! #@ o@_/]]q]2104632#"&>73#8(.2:&(8+r%)Ews5/6.522L7I=6Z 3@   p   @_/]]q33]]/39/]3104632#"&3673#&'`8(/1:&(8{ri~a3<h6/8-422Js~?`f1 '@    /]q33/]333104632#"&%4632#"&!!8('::'(88&'::'&8q`h6//642246//6422o /M@5vO _ o   , O$$$$( H$)!$@_/]]q3/+]3/]]33]3104632#"&%4632#"&".#"#>32326738('::'(88&'::'&8:+SOI"11_ o\-UNH /2] nh6//642246//6422$6.pz%6/rx?G,d@X  @?$@IH $ 4        :` p O  ( H  @_/]]q2/+]q^]3/_^]]]+qr^]10".#"#>32326737673#'MIF&*h hU*PIC+&f e1n#7w$,8pz%6/pz5;~EO1  +@  0 /]q22/]]10!!%#&'53`y71Yd1  +@  0   /]q22/]]10!!>73#`s= aw[#.KZ (n@$%O_ @0,/H@# #H %%% %%%%0%%% _/]3333/]qr/++]qr]9910#"&54632#"&546323>73#&/3..2:&)88'.2:&'8{5s38q6B(,@3046.52255/6.522#N78M#@KJ@/ @` _/]3333/]310#"&54632#"&54632!!3..2:&)88'.2:&'8`3046.52255/6.522HXA@-  ( H  @_/]]q2/+]3/]]310".#"#>3232673!!'MIF&*h hU*PIC+&f e`$,8pz%6/pzב/ /@   _ o  @   _  /]]q33/]3104632#"&!!h=-08:.-=`L<6=56=8)/ #@_ o  / ?   /]33/]3104632#"&!!h=-08:.-=`<6=56=8^ $,m@F ) ,-.',,,$0@5 ,"?O/?@H _  /]3333/+]q33/]]]q39/]933104632#"&%4632#"&#"&'3327673#{8(/1:&(88&/1:&&8q Vg R-[qe76/8-42246/8-422xz96ol`wX^ $,m@F  %(-.)&&&$0@5 ,"?O/?@H _  /]3333/+]q33/]]]q39/]933104632#"&%4632#"&#"&'3327#&'53{8(/1:&(88&/1:&&8q Vg neq[3L76/8-42246/8-422xz96oXwm_j #a@A  #$%_#o######//?@H _  /]3333/+]q33/]]933104632#"&%4632#"&!!7673#{8(/1:&(88&/1:&&8p_R-[qe76/8-42246/8-422jl`wXj #a@A  $% _o//?@H _  /]3333/+]q33/]]933104632#"&%4632#"&!!%#&'53{8(/1:&(88&/1:&&8p_eq[3L76/8-42246/8-422jXwm_^; %M@/"%&'  %@ H%% @ H _/]33/+3]+293310#"&'33267#.54632673#q Pn]]>-A8xi71->9:"CVv~x5/22)1/+0L zn4D-^; %J@.  &'#@ H @ H _/]33/+3]+29310#"&'3326?#&'53%#.54632q Pn]]DVCC=-A8xj72,>v~x5/2231/+0L zn4D.^; %M@/"%&'  %@ H%% @ H _/]33/+3]+293310#"&'3326746325>5"&673#q Pn]]P>-17ix8A->:"CVv~x5/22)5-D4nz L0+/^; %J@.##&'#@ H @ H _/]33/+3]+29310#"&'3326?#&'53%46325>5"&q Pn]]RVCC>,27jx8A-=v~x5/2234.D4nz L0+/F@%iY kY ?+?+?3?999933210!# # ! #"'5326! 3}w9NQnEB4=;Q>T4fi<{6wm"=HTJE@%]Y ^Y?+?+?3?99993310 # #"'532! 3 # #_v="l#1c bJjO@*   kY  kY?+?3?9/+9933333103 #!##! 32654&+o&-Mը%ĶH!f^'W@0 %%()  ]Y"]Y?+?+???39933333310"'##33>323 #"32654&w @n٣)N==6ZP۸#-|@F   iY:    kY  kY iY ?+?+3?9/+39/_^]^]]]+9333333310!!!!!!#.54$)#"!3Ty ݷq8^2Ξӝ!^'.@H  +# ,,#/0  +^Y++++ "]Y"" (]Y %]YaY?+?+?+?9/+39/_^]_]+9933333333103#.5463!632!3267#"$'!!3!"%"!.9~ηvXmXo swӁ/ wwm-&!PjZq=@     ?3?39933333310!##3'773'kjgi<m˰m#J;@     ?3?39933333310##3'773ɢZV-J!XZ,T@. #-.iY + iY +iY+!&kY!?+?+??+9/_^]+933331063 #"&'532654&#"#! #"'532>!}L1PMJ~+z8USmE@4=:Q8GH כ1Wul PJ$V@1 %&aY ##]Y# dY aY?+?+?+?9/_^]+933331063 #"'53 4&#"#! #"'532!R;Ќjl N;`vCk%Nq <=/cq$u@C !"%&iY !iY!:! !!!!! iY #?3?3?+99//_^]^]]]+_^]+933333331063 #"&'532654&#"#!#3!3%L1PMJ~+z כ1Vj J @P !"aY ]YE  aY?+?3?399//_^]_^]]_]_]+_^]+933333331063 #"'53 4&#"#!#3!3R;  Ќjn~ M<q <=/J7 6@  " iY  iY?+?+??933310%##!#!%IJ 6@  " aY  ]Y?+?+??933310%##!#!ȴ{PJN>@!  iY "iY?+???9/+9333310%##4&#"#3$32|f-vv"2;\VG@%  " ]Y]Y?+?+???9/93333310%##4&#"#33>32w 1qʘ{8Z@PZ (@     @ H /+]22939910#'##'##'5R!11!11!P!ffff!)Vq@H   _o   sY  ??+?9/////_^]]999331023#"'575573%%ZZtD۰Zj&(LJJJp͑u=G@ G.@`/#G/@f)G0@G@#G@ ^ ^ v > ^   D *2 (\ \ > < Z 4Copyright 2012 Google Inc. All Rights Reserved.Noto Sans UIRegularMonotype Imaging - Noto Sans UIVersion 1.06NotoSansUINoto is a trademark of Google Inc.Monotype Imaging Inc.Monotype Design TeamData hinted. Designed by Monotype design team.http://www.google.com/get/noto/http://www.monotype.com/studioThis Font Software is licensed under the SIL Open Font License, Version 1.1. This Font Software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the SIL Open Font License for the specific language, permissions and limitations governing your use of this Font Software.http://scripts.sil.org/OFLff  34566778:;YZ[\_`ders}~NOGHTU > ? W X o 45Z[  `bddss!#$$%%&()+,-/1256689::BNHIJJKQRRSSTT Ncyrlgrek$latn4 kern8kern>kernDmarkJmarkPmarkfmkmk|mkmkmkmk        *:JZjz  %~ & )` +B @" P Q ^ _V b  de dF`bddss  %(3!-:/6G8:OBNRHT_%$=D]4KBj77NNSV\_27 BI^alq~!WYZkl""(UZW]f]kngqk{QQbhssvvl $****0*****************6<***BBHN*N6**T**TT****Z**T`````````````ff*lT* &,28>DJPV\bhntzbn,bn "(h.4:@FLRnX^d4jpv|JJ"J<jp. $  *0:6<<<BBHNTZ`ffflr>r>rx>~x>,,,,f""""n:4:nnnn &.,2,2,2,282&.&.&.>.DJ(DJPV\nBVbnhRntz@bnF,FlRnt "(.,24:&,2v|@>FL>RF~>F8XJ^~>djVpv|V"hnbhJ^bhnbhnbhnJ 22$*06<BHNTZ<`nfnnlrxb(~nN,.,.,..,.,$n*n  n6&  ,6  268>DJ$J$PV$n\*dbhzntzRn.RnnRnnl2n>8J2 ,"(.4.b*:@nFL*nR*X^h.d.jp.vV|@ Vj|xdj.(T0>V\p"J<@ Nd.L""$*06<xBXHNTbn2* 0,6Z@0`flrx~R( &<,nr2>8,,>&.Dn"JPV\bhn4tz,t\D,n, l tx, <\nBbnf"lr(>.~>.4:bn@FLR>X^<Rdjpv.|v..|2\Fpv0nBnRn8n,n* $ *06<B6Hbhn>N~0,TpBZ`Bf,lrxZ~D|4v@b,n*n..F4@L(Zn4@4@4@N>>RndjZ4RnnJJ &,22t8>:D JPZ.BVBZ.*N\N\bh$n"~<*<*ntz<b*.2B $pr0,T0,0`T0`T<BXdx4n"4,@JnRZ(<BXdj\ "(.4:@pFLpR8X8XvhFL.X^d^N~0,6Rdhj4pv|djdj2.vhFLX^4J^N~4~0,Rn4FRn~~nJZN, (h.  ( "   " FL@ $FL * $FL * 0FL 6 <FL~ BRn BRn H N^ T Z ` f l,2|&, rv| l,2&, xv|&, rv|8 ~J\ "V  V  bhJ h bhJbh BJ z Dtz D   @&X N N  N n ~b *~ n r n  *l~!!!!>!! !&!! !&!,! !2,",!8,!8!>R!D!JPV$!P!Vl!\!bl!\!bnR*!h !nr!t !z0,! !z0,!v|!x>!!$!!*!n!!x!Z!B bhnJbhn!ZD!Rn!!:~:OfdtL~L  XX44[[|FDD| P  < ll::gg:jII00vhv\h8Dh8lh,0hlLvv H%%l0~R XXl[[l l  llLX0@0:Dx@\D@D@@v@l@lv @ $DD,<Dn ,:,XNNN,llX,X<bbHH8X\lflfb,b4<DTDl@[0[[<<|Xn,pnl<0<DhD<h l, <l< nl l`L,4< <p,,H,:> >:0v,vvLx<v<%,,DlI,I0\h%@l%@|@D$(D DPXX [ 4P[ l  <lPv vLPD jP:vH,h0`,h|[,0,lvxw,hTbth!!&&5 6,@ `@FFp<n< V 8 .8pDN${ {55IIN&S@ + 94,,,<,l,@ l(,N@("\",aIppCCD@ll|hq p:dnnXpp/XXzd~TG0G,,$|,,T@@@|jhVThTV]]It  \\,rlblpvp@p0pppp]h]pDpHHH|h|HHlvTl>>pXpblHRRl[p0@0"Xl,  p@D4:l,:,:pppglg%Il@IJ%dd## Pb`aHNTTHNTZ`flrx~Xl0ll *l \+D,l XN,0 $=D]4;CHNOPQ?ARCCUGGVJKWMMYSSZWW[YY\de]xy_{{ab  &,28>DJDPV\bhntVzVVtPPPPPPhhhhzzzzV `~du~uTWS&n~B~x>~H-J:J@J\J:JJJJJfJ<J6;JJ  *^JTJrJbJ X^ $=D]4679;<*+>FI@^aDH "(.4:@"FFLRX"^"@djpv||^^^vvv^^vv-x~x!qL{wJxdxyX*\\#9p8J^J\J^@R%mF@JJJJ`J ;A$=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0   1      & , 2 8 > D J P V \ b h n t z    "(.4:@FLRLRX^LRdj@F pv|@F            J J J J                   $"(.(.(.(.LRLRLRLR @F @F@F@F@F      " " " "   *0 (. (. (.  (. (.6 26 26 2 , 2< >BF 8 >@F JLR JLR JLR D JLR JLRHNTZ` VLR \ bX^X^f nlR h nLR h nLR h nrx h nLR @F @F @F~ @F    pv pv pv | | | |      @F @F @F @F @F @F           LRLR zdj                           (. (. (. . . . . (. JLR D JLR                @F @F&,2&,2&,2&,2,2       8>HN  DJ  PV\b  hntz &6 2 D J \ bX^LR @F |       "(.4:@FLRX^djpv|   JLR  @F(.$ , 26 2 b^   :@FLR6 2 @F     (. (. JLR JLR   pv pv @F @Ftz< >BF 8 >    (.   " h n  |  $*  (. P VLR06 pv  "<B(.(.HNTZ LR`f@F@F@FLRlrx~djdjdj@F@F  $pvpvpvpvpvlrlr|LRLRLRLR@F   `fLRX^ &,28>DJPVPV\bhnDJtz4:dj@Fpvlr| LRlr 4:X^LRdj@Fpv| (.LR"LR@F    "          . .  (.  (. (. &:6 2< >BF 8 >@F< >BF 8 >@F 8 >@F D JLR JlR b^ \ bX^ \ bX^ h nLRf nlR h nLR h nLR zdj t zdj @F @F @F @F           pv pv pv pv | | | | |        @F @F @F BF BF          @F   h nLR h n  i<iJJ(N33ss''//11BB??ddqq}}FF{{NNJJ;;0 0 0s0000?000  000,/0100mm0d0q00B0FF==;;}h}0h0h0F0=0dd{{yyVV==BB{{yy V0 V  ``!!DD%%0F00 VJJ^^  }}##??BByyhh{{BBLL%%dd1130'00000Jhh0 $=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0   1      & , & 2 8 > D J P V \ b h n h t z h , h h     & & & & & & > > > > V , b b b b b b n t t t t , , , , , , , h b b b n n n n h t t t t t h h h h             & , & , & , 2 2 2 8 8 8 8 > > > > > > J V V \ \ \  \ J J J V  b     b b b b b b b b b b b b t t t t t t t t   & , & , & , & , & , & , & ,           > >  "  "  "  "  " V V V h h h ( . 4 h h : @ F L R X   ^ d j p v | h 2 8 L   j D V ^ \           j     b  & , > t h h  & , & ,      h   b b t t   & , & , 2 2 > > F  h  , \ b t & , V      n    >  t   h 2 V b h h h n  h h t t $ *  D h h 0 6 < B H N T , j Z 6 6 ` ` , f l ^   r r r x &  D 0 ~  h r r              h h z h 6 h  P  6 h  , h h z  h  b h h t    n  h h h n h h h h h t t t t t z h            & , & , & , & , , h , h 2 2 2 2 8 8 8 8 8 > > > > > D D J J P P V \ \ \ b      , 2 b Z J003000s0'0000/0010B00?000d000q0000}0000000F0{0N0000J0;000 0 0000000m000F00=0;0d000{00000y00V0=0B000{0y000000 V0 00`0!0D0%0000J0^0 00000}00#0?000000000B00y000h000{000B0000L0000%000d001000 Z`"$&(*,./125678DFHJLNOQRUVWXL"LRX^djpv|jppdppX ,D  5D\N $=D]4KBj77NNSV\_q~]""eU&QQ'bh(ss/vv0j1jpv| $*06<BHNTZ`fljjjjjjrx|~NNNNf6fjjvvvv||$*`BBBHHNNNNNZflllZZZf$$0jNjjjjjjjjjjjj &,282828>NNNNNN>NDfffJppv|PV\bphnt*z6BHflB6jN  *     " ( . 4jjNN : @ F\ L Rj X ^f d jH p vjv |  p $ f  BB$      x6       N $ * 0 6 < B H N N$ T Z< ` f l r x ~ * <  F 06BHl   6  6 $   p    x | & , 2 8   > > D J  P V** \ b b h n J t00 z  66  nB BB H 2  J  T ZZ``fl h HZf xz%{N4RjHLbH$b=&Db@HE4HPHpppp(pbp\H`HupNpDpp\pDpH>2(bRHf  \Hd0L#3DH Hh]1 RHHH~_ pv 0H3HHH HHH=llHDHH\HhHH`u=HHHH)BHHjL H3tHH H/|@HHJ3DHpppbrhhppppJ 0lppR\/4p:bDXE 6@EGIKMQSZ ]]4:6<BHNTZ`flT6rfx~VVV V=VVpLV,bVV= VVDVbVVsV p`bss!#%% )+!/1$66'::(BN)HI6KQ8SS?`bss!#%% )+!/1$66'::(BN)HI6KQ8SS?@@ $***06<B***6HN**TZHTT`Tff*fffllfffff<<x:Ot`Txx@::@:0@TD d, djdd  $$&(,- 25"89&JJ(RR)TT*dd  $$&(,- 25"89&JJ(RR)TT*+    $*06<0 6000BHN6*00HTZ666666* +~t`V4\4H $ BHfl )`dos""PEKNO<-2"FEKNOFtsdtdpqt2200 0"""<"^^4 ,TT,T 0 0 0 0 0 0"""""""4<zTT 0 0 0"",,"""^T4T4 0""z   " !"!<!B!DFHIKKNNPS UU$WW%Y\&^^*+78=DJNORST^_`adnostwxyz{|}~$(**,,..0022446;==??CEGGVV[bddffhimmooqvxy{|~~      !! PQ UV]]__ggimosuuwwy 1P\bhjlpq{AFJLLNNPPRRUUWWYY[[]^cceeggiikq~//$79:< DFGHJPQRSTUVX  !$&+-/13568 : CDFHJV_biyz{~ TUV]_gopz|  "#$%&'()*+,-./013579=?AGIKOQSUWY[]_acegikl n p ~-Z&*24789:<$&*,.02468:GfmqrsuxQ\^iy{}    FHJLNPRTVXZ\^`bdfhjlnp~7$&q~< &*-2479:<$&68:G \FHJLNPRTVXZ\lnp~4$79:;<=$&68:;=?C U "$&(*,.0lnp~%&*24G\FHJLNPRTVXZ\-<$C U "$&(*,.0N &*24789:<$&*,.02468:G \^FHJLNPRTVXZ\^`bdfhjlnp~!~~$;=;=?C~ ~U "$&(*,.0"$&*247DFGHJPQRSTUVXYZ[\] !$&+-/1357<>@CDFGHJ TUV\]_ "#$%&'()*+,-./013579=?AFGHIJKLNOPQRSTUVWXYZ[\]_acegikm~$C U "$&(*,.0"$&*24DFGHJPQRSTUVX !+-/135CDFGHJ TUV\]_ "#$%&'()*+,-./013579=?AFGHIJKLNOPQRSTUVWXYZ[\]_acegik"$&*24DFGHJPQRSTUVX] !+-/135<>@CDFGHJ TUV\]_ "#$%&'()*+,-./013579=?AFGHIJKLNOPQRSTUVWXYZ[\]_acegik%&*24G\FHJLNPRTVXZ\   YZ[\]7<>@ m  < << <4FGHRTH]3579=?AGIKOQSUWY[]U( (DFGHJRTDFH( (V]#%')+-/13579=?AGIKOQSUWY[] ( ("( (   ( ( F@F`F( (  fmqrsux QV_bdipqrtux Q%V_bfimsvyz{|}~ !qrxQV_bi -V_bfimsvyz{|}~ !Pfmfms~~V_bi~ ~ V_birx Q V_bfim    y~ y~ y~    y~ 6  ky}     ghijnoprtvz{|~   i{    #iy{  N22 giop{|22 /  ky}      oy  }  n  = opwy}  !~~~ ~ops7 opw}  % gp| g|       p'  j~       I(W(Y2Z2[2\22%('(72224(5(Z([(m2(I2W2Y2Z2[2\22%2'272224252Z2[2m22  y}  5  gjz|~      op ghinoprtvz{|   ! gp| %  y}        %nixz{  g|   )  y}      j~  }   ghinoprtvz{|     p*  y}        }  )  jy}~    @  osy}         o}    +  oy}   Lcyrlgrek"latn0aalt2ccmp8ccmpBccmpLligaVrtlm\rtlmbrtlmh 0@P`ph< T"*2:BJRZbjpx&.6<DLT\djrz^fnv~ (0 $*2:BJRX`hpx$,4<DLRZbj                           LMW`bss!#%% )+!/1$66'::(BN)HI6KQ8SS? (B~FPZdnx  (2<FP ` a ` a ` a !` "a #` $a %` &a '` (a )` *a 3` 4a 5` 6a +` ,a -` .a /` 0a 1` 2a 7` 8a 9` :a Y G k l m j n o '*,./0I 4L5OZIL[IO  ; < = > V 7LMWlinphone-desktop-5.0.2/linphone-app/assets/fonts/NotoSans-hinted/README000066400000000000000000000007121434616504300257310ustar00rootroot00000000000000This package is part of the noto project. Visit google.com/get/noto for more information. Built on 2017-04-03 from the following noto repository: ----- Repo: noto-fonts Tag: v2017-03-06-phase3-initial Date: 2017-03-06 15:25:38 GMT Commit:60aa0da2ee84b11e78725b4577edc2e80b009d56 Initial phase 3. This is the first release of noto-fonts that includes phase 3 fonts in the hinted/unhinted subdirectories. The new fonts are Armenian, Cherokee, and Hebrew. linphone-desktop-5.0.2/linphone-app/assets/icon.ico000066400000000000000000001123051434616504300223310ustar00rootroot00000000000000hvn 6@@((L2 .~( vv^__^`aabb dghn r'w(w67=>CDIKSWZhowxy{^]^88  )555522*0'(0+$$5555 &255550$ .555555- 435555, "!88(,^^`a d eikmnpq'w(w)x+y,z.{/|1}^^^;>AHKNSWXeg^\_BB/@@@@,4)<@@@@@@@@<@8&#"'9@@/( ,.)11+;@@@@: %@@@@@@$ 5@@@@@@@@@@4@@@@@@@@@@!@@@@@@@@@!57!6@@@@4 AB(0@^__^`abc d d eiknpq$u%u'w(w*y+y.{3~4]^^9>FGHKMOQYdmoquw|[_QQ$0890!-@OOOOOOCLKIOOOOOOOOO/AO;++?@BCDEFGHIJMNPSVX[\]^dhnoqrsuvz{|]^]^_[,///,/,/" @`x{`E Q{jXSRcIMM=u %\'j zvqhuz SjM'1SrQxJKzYp5=xX 8H =_h<T@HX $8EE8'HfrJ4"M7M7SAp Y1YX0MS XL`dRhjHYXC@UQ^ XACfYep$`8r48\&]rM= "4A80,,,/,,,,(@^__^^^^^_^^^^`aabbc d d e f fgghiijkklmmnoppqr'w(w+y,z.{0|2~3~45^^^^^_^]^^]^^6789:<=>@ACDFGHIJKLMNOPQRSTUXZ[\]^_`abcdfgjklmnpsuvwxyz{|}~^^]^^_^]^`_]\`]_b]`Z`m@f>: :> 5858 >?6: *FS[]UF*%ZˬU")o%u{ KPݕ.S][FP"@,.㖚gFqU u @SX%˳qngn{ԨuS,@aHC "aH גFX]fgUSKS.XPPafk"Kkdٿ)aJ*KF2Xk{ofK%Kٺa)ޡ2%{qqa C H΂KuمP٨aoFِ)da u2.@Xu@gFfUCH]N|P +qa@Fa%F nd]FuqSqu S{%gX״g%]w."KgqqgN% 8:>> 5555 >8 8>PNG  IHDR\rf^IDATx{tTD#7QA;-*tAkPVҊ U^VT^*oeq"Y^ЀaA%  >30f}9{o0 -!]tBt#HqA›p) #n8CȰn8#V/ݔ0hTFpǹaa 7܆>AFOi%ݛM~}P 9t]DGgo-m%"Q⡊=(KuHxݴv="Ej|]DY O%")G~"n v"bEzlW!"([D\/"I ?":#Xq@a )Dq@a )Dq@a )Dq@a )Dq@a )Dq@a )Dq@a 2pf?H}-ʳ]Qt$!9 SYzIPZNJ(= ~K0~ɷˇ -v綇..n -ש|ǎ@>طͰ{ |#^h8!EDEfPَu"|Jۮfh vЮ;n } 'llH#?~Ņxs>ԩ&:u]MaaZ 1kcra.ZeBAкc~SmYçOJzA?`֗4{Ϳ{ز V,Tg Q/Cэ|^>1vh[Qǎ9 7Mu!FL̀'Gw&vnO'PVjgIAxqOO$7'47cŻlW쏰rϾ)`"|]4i>koB"n~4KNˁoL`KjD{p(=+[Z+fJ̀_.;o{`Rە8D]\˞Eؓ]9 >~CvN:ĽoxibByofp{*:un޸ 3c$ە7>~vP6gq)+n{=G?3 =[}{j7$mW UMv O$v z Jpgrpr;r^]4UѦs0)hX9\'ӵ:C@ ðj*$u{)@:Fnp_B`N ~UHwVԓ[ڮ@)9P`rOmW!ԮܵPkL~gUO8~/?#_]/)*++5K]I{+\7v 0TǠU@49vi_֙Jny N?vi/ڮ7`ƕ>ͼ )C]DۨRiz+fjXSZͪ~ï~յ:*"5Uo9cw2˱j6Gv/k+;S5.eDNv5nͻr5Ӡ#KÉbFX1V6M@Z{#3m xcR;5\]E~n9 |X5~{HN߭'[=vRذ?[ng)l)kSǟcbWY~Jٮi )DXnYA#}A7IAy?텏^/ik.rgxlx .7J+pAO 3Ĭzd[q!o]4soz?Ԋ XL.܋pY/FRV E6aW_gߩ=)]BY& Zm~NNpb(>h,/>z5;9TKt!"S8L R`ÔKخ@bIzg=hs#.ci[w\Flx ~8 Y2mW#~Q/ís`nWFTgW6"ZeC'nsdR7j͒ᶿ°/5ڟh3`FU, c&4W>탻Nn"Xdh/ +fð;C_C#,3( )|嘅1F^ݒC[įɾ|xbgz9=+:l cBA` \6gAk7OzWo`'fhp4|wg&8I07@fOβc<`̱E{|߄uI*Ӝ)|9{bc×m#)*z9l9v5l~ t&$Bzw .YV̄clW xf[`3Z77_~PX<2h Z}vߨh+ uP@emFƂR8Pv wE{O^ƅERl-6{)\{pNȝC! O#B!(.c&ߟrτSYڨ+7h _Q|Wr()KcjSXk wi㯆:ޅ7&ٮBi߿lWK ,~3J|.6/bؓufz6%a5Ib[Bbk8HP=[}@mW\ RǮ<:$v4o ~j _Qצwaư&?X7+@C|:!ivݴC% zxv'mW XZgbUaZ \m˷]4ԩͼh]yHO8Ė~&MWX%$v {ۮ:@8‚sP Ŏ+n]u pZ&v!AqW*m9$Xpf_Rx)xZMQ\h"pۯ*@$3WхC QV _{7__\k*Q0ZR3`t󁓚fC f@#+sޝ3+Bm`uGەK^z;MO7Hr+7> g9>K͖Ud9 ҲR='' UؔAݛ|*XHVINH4x&5=H])X fiTn1,}2 s&$ՠͪEz /)Ņj.|νpJHc'5Es#Ld)pč3!X9;K_:O2׭5PĂ6&zg†eΌ6tm} XgRĚ6pAO8d2ܥ%~__~&~STh15Lh.r %$Bz2hZ_ŲR ޯaܹk(ʄ.⫠!X^ʢNpv[hqL3_3E}̓#Pi(aХNq,Uv9+ @"S8L 0"S8L 0"S8L 0"S8L 0"S8L 0"S8L 0"S8L 0" m!"V Z]MB]XbM <6+v5 o2@`ik1=U;!HiخXDbI6]W#BYN3Wڮ^D}es_8%!Z"j ?i:9G5NzoGkWg `8!^ΰDFC!,̯Sg0tB"CZq)cW,",6**FЖ n# /V i&@e!D_B  4A'݁G.+? ՅbIENDB`linphone-desktop-5.0.2/linphone-app/assets/icons/000077500000000000000000000000001434616504300220165ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/genicons.sh000077500000000000000000000025561434616504300241720ustar00rootroot00000000000000#!/usr/bin/env bash ## ## Copyright (c) 2010-2020 Belledonne Communications SARL. ## ## This file is part of linphone-desktop ## (see https://www.linphone.org). ## ## 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 . ## for i in 16 22 24 32 64 128 256 do mkdir -p hicolor/${i}x${i}/apps inkscape -z -e hicolor/${i}x${i}/apps/icon.png -w $i -h $i ../images/linphone_logo.svg done convert hicolor/16x16/apps/icon.png hicolor/22x22/apps/icon.png hicolor/24x24/apps/icon.png hicolor/32x32/apps/icon.png hicolor/64x64/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png -colors 256 ../icon.ico png2icns ../../cmake_builder/linphone_package/macos/linphone.icns hicolor/16x16/apps/icon.png hicolor/32x32/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png linphone-desktop-5.0.2/linphone-app/assets/icons/genicons_1.0.sh000077500000000000000000000026131434616504300245420ustar00rootroot00000000000000#!/usr/bin/env bash ## ## Copyright (c) 2010-2020 Belledonne Communications SARL. ## ## This file is part of linphone-desktop ## (see https://www.linphone.org). ## ## 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 . ## for i in 16 22 24 32 64 128 256 do mkdir -p hicolor/${i}x${i}/apps inkscape -z --export-type=png --export-file=hicolor/${i}x${i}/apps/icon.png -w $i -h $i ../images/linphone_logo.svg done convert hicolor/16x16/apps/icon.png hicolor/22x22/apps/icon.png hicolor/24x24/apps/icon.png hicolor/32x32/apps/icon.png hicolor/64x64/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png -colors 256 ../icon.ico png2icns ../../cmake_builder/linphone_package/macos/linphone.icns hicolor/16x16/apps/icon.png hicolor/32x32/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png linphone-desktop-5.0.2/linphone-app/assets/icons/genicons_1.1.sh000077500000000000000000000026171434616504300245470ustar00rootroot00000000000000#!/usr/bin/env bash ## ## Copyright (c) 2010-2020 Belledonne Communications SARL. ## ## This file is part of linphone-desktop ## (see https://www.linphone.org). ## ## 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 . ## for i in 16 22 24 32 64 128 256 do mkdir -p hicolor/${i}x${i}/apps inkscape -z --export-type=png --export-filename=hicolor/${i}x${i}/apps/icon.png -w $i -h $i ../images/linphone_logo.svg done convert hicolor/16x16/apps/icon.png hicolor/22x22/apps/icon.png hicolor/24x24/apps/icon.png hicolor/32x32/apps/icon.png hicolor/64x64/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png -colors 256 ../icon.ico png2icns ../../cmake_builder/linphone_package/macos/linphone.icns hicolor/16x16/apps/icon.png hicolor/32x32/apps/icon.png hicolor/128x128/apps/icon.png hicolor/256x256/apps/icon.png linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/000077500000000000000000000000001434616504300234555ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/128x128/000077500000000000000000000000001434616504300244125ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/128x128/apps/000077500000000000000000000000001434616504300253555ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/128x128/apps/icon.png000066400000000000000000000056761434616504300270310ustar00rootroot00000000000000PNG  IHDR>a pHYs'tEXtSoftwarewww.inkscape.org< KIDATxyt?HC !R5"Ɏ@Zmp驀 ( .PhEeQ݄E@DI2G7/s?{ޛ&{wע1c3!X$b4B*A(YXB+Εv2uc,6O$ ǀ%kyxiK>Kx'#֛-Q Nq wY)/chC!+U"|{~!K1_];:p㷘{H, ]}Dk28a"iJ!|99CUH1* ja~^&ڇ2!>m]<118xcc q<118'T`ZH_=_dshM@Sԋ?™{BٙpkzY| +~o xֽ ?uCAp]*Zu˘rG8l!c9?6c #td8SB v9:a jNk-W-5kBq`忞{΄W> &B5U8Ct 0@ב*m1q0H[D4_V,cgCJ\`(m[ݠXU MvT%>Jp, }AJ{Ijw mj1@h]i T h5 pMOS`vei7@$$ a0x;,\T#L[ -Aq8qY.32`hiu&ɭ?I"ɰ?ipL#;帼'/V́GC|++y|NY0Arոqpin9ع -U~{ ރ#{IH,y$sT k{Ic_+r7agOq&q(,Ww?. n`'0^m|o[ /f_3.q7?V>l8NVwk+5[=/Eq(&0uG1Gh+6F1 ";nۖV;E>@M'aԤ_>:Xp^FRKU v0mh'EiB.ø3tꇚӬL2@Y_1$?2ZF#Bf2~\fg|oPj ܍@8o/N :oNrJuu0`2L~8O:nzWL"qysh8.4h,uƧH]~4Se'LNF-T < [/mlX$"i;{pĕR#O'%fk:W{͐ '!}-/L~rHw[ZjmMj˒9 lb\EJ[,=kas0C0bs;OP wWZUaYJf;2֠1t&OC|!tZW; $}U ۠y^Un'A#+.Ga2vCL: ~ p4hRc6L/s7E'/aϗr_;8z7ʗ2޿c48~4iSpCp;8%˸? ygq^.uŠ)FCk%5etq,+TAg_%f i.=aU8V uOj<%d).KuJKבe)3BNm%#4L\ 2ҥciѱn)UۈNwjp@3¢u \naz?&oVϒvؽtuWPV7_;!ujH`uhzIŹTXϐmasp?sL[5$8 22\`NGru*؅0Y((AAp$ XBQ3aWUgA`c寐(5@$Q>NIU8_pWΆ#w@\FUjJҤJvuwhQMT'mR{&ʦ1qAVhV;~5(oLrփwNgK}R#xK8! xcc q<118xcc q<pa! |@.{i>@'Ѡ!6[ujtY#am-ɢ|V 瀧 kyg. i|<p|c^ P[ȇ@kcP{VE4\QlyBV lu\!ФO&Jd,Jat7@c `$Y +Jk0r @4_Plb>',|18YcIENDB`linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/16x16/000077500000000000000000000000001434616504300242425ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/16x16/apps/000077500000000000000000000000001434616504300252055ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/16x16/apps/icon.png000066400000000000000000000007041434616504300266440ustar00rootroot00000000000000PNG  IHDRa pHYsvvN{&tEXtSoftwarewww.inkscape.org<QIDAT8ѿKqנkPA n(p 9?@!] \\\TppD%DO=ɻoYb/ޯ ƐUkB! ږ], m=T(~ae"/Rm '7x?@y;;;h/m'͓io3Ҁ6x()HR-\ nF3)vL1QTޏɿayipC-@?nYJ[_n73| 6MzQV#d QWd(ԇVkvzc D9'2U>yL)Cv=Zr&΋6w޷(3<S3B=)8* z>5ߊ fM'h@`pw/22 ?sx.cq-}ҧ_]b6mO֟&(X8PwŠ;u-;Vm;鎡ǔfraN'RQ]ޞ޾Cxr㜾|qqRޡw%8 2R 'P*ޏ1c.o ^BV[p6@"H-W)IENDB`linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/24x24/000077500000000000000000000000001434616504300242405ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/24x24/apps/000077500000000000000000000000001434616504300252035ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/24x24/apps/icon.png000066400000000000000000000011451434616504300266420ustar00rootroot00000000000000PNG  IHDRw= pHYs-ItEXtSoftwarewww.inkscape.org<IDATHKHQX-$B,˴"0RZT$AK p/-jQA2ZEԲV=\TX %25eѢ0gſGqnXzJ)ŬABnDXzb|P;[BHgwkVFظʭ`lϽtEFN*K7JO-ZIk71~|bnFEïyFbNfr((-)0Sl4_ `:5t]lh 5XQ-dӁ0S>oS N> Zo6=\jjJ"KFW2t*+H䱴B"sҚuGA^NeDQb.c5H8Fk2{KN}_욗 mc/qM'G0ckQ, _ )o- WKKÂ)f&[zvdg_ 8rnFu~3K:5A )9,ev98˭HIXA|#_㖯 g䲥.OAk4qgGrs``x>s|;!-#z0as*67vgW.>/Gusv?Q>^1\9e'[nĜ{P XMשּׂɓf6;w$2 MB# ڷE]ƚ@.+Qy0i(sPh~RT[0k$ ہg57I|PnO<8f^.|;v )Hl;MpwkYpt8$ X~v )/ᘮ=w `ܳHI%GRbnqfX0Og yN.߇ϞBJep(3֡0id;Vps k?]_p8s `s:%(2[AfN!rpKmzv#:uQ![wg2cBO]Yra϶SH,r u登$1] 49vPQTd;Ē+^ _u eB7ͧhwqt p;`f[ʟ ]-kn  D%~/^;lu*xcc1$8e< ~?x:v\_^ s>$am=~n̄W/s3fv԰]L"M0z^ P׫|f,hQt&L| z[k5ᎉPz*z$~< ̾{DﵫeSF\$87adFt^Rs"<|}K#X :R_7q?Cn92h|ӣa8sf3SUw~BD!N)HfgC %=&͊>{<j= ?2-I nH~J:z d'y0M9_TiUp\2P@J:n;M? X)i x q'@nŻMO׾=vs}=S2ܴ `/S(ؽk$N+\5 ~lG0w `}נ5?N2ԥJ=%w˧E_{ש\+/ÍcO8|^e˙G.ٱF?Sȹ svԷaݰgIJ6 $M{ZG7Wpo gJgYm.",٤xp+%_wpq ̑'sKkN] Æn~sUfmKV)]B5s|5uLG<͠3V=h `OIe/1D_VS;A.ؕ;rvw sMrao=V v"TTU:RmI$-=SU|l\Cn$MC_@ANBi BIP-a~ T}x2X5v]4sTc]C NB^$RN**> J%VS x8g2zڐL[dsvHzpbQ]]r oc#ͭUEo9~1 ̄aOlvɦ~s8oTbͱF~tR$ PT5Gf9$Nh1غΜef_D&(t3T"S8L 0T"S8L 0T"S8L 0T"S8L 0T"S8L 0T"S8L 0T"S8L 0".!DĊ`"bŖ>m+6FXh;X <)Dy`"b|>CD,abb`,"~Ͽh(",Ϭ?6H`c8QL"0~=xZ Ogx7d_}7 H 6m{~cf@\Ί=xcp`D$/{o< vc\#HL$ z+g$|O%F.l.Bt>Y4rJa+?`L@9Q'"2 YBwIO<2JMDbep7hyT`oÈt߀HH!E][{{М7݀D|H|W.Q>B6=Vb 7D `O~/'@&P (Dk9"م-Dƒh,^RkIENDB`linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/32x32/000077500000000000000000000000001434616504300242365ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/32x32/apps/000077500000000000000000000000001434616504300252015ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/32x32/apps/icon.png000066400000000000000000000014371434616504300266440ustar00rootroot00000000000000PNG  IHDR szz pHYsy(qtEXtSoftwarewww.inkscape.org<IDATX͗[Ha3^j3Jc dٓaPГAQZ&!PAQQo$b*Vf]qs!]Į9|AD!4.V^e d Z} c 9vcXxSs<Jp$ u "RP%La^I Zh1(z!< bLWtP8ꛃh[#8S4 ?zUMpJ]pgK@z!N` A*pۡ5TdAV&MOr>sS뒾c=9> `jx55 *o F.1N_IJ,{ӷ ~1肱n{!B07@f)DKdFKS-PT(i-{>/t7!1f4\ -p# ~>ރC@8cqԜS r!EOmNX nyư9o[axɧ+MW( bbk`C a"fx/*gXvIXַx"Mh|K]Z ]T @2%x=@64?WIENDB`linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/64x64/000077500000000000000000000000001434616504300242505ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/64x64/apps/000077500000000000000000000000001434616504300252135ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/icons/hicolor/64x64/apps/icon.png000066400000000000000000000030171434616504300266520ustar00rootroot00000000000000PNG  IHDR@@iq pHYs\rtEXtSoftwarewww.inkscape.org<IDATxklU--х"wD PDGC`J5D(F 6x+WAJ VHP BK㴴Ŷ=gO2?̾;sYj8L.<,/rt D`,[`Fdn\78{& ðb7iYtІ<C#MnqT(ہnZm@76?OvGT(/Х|p{(Ȼ W"@$J P<޷0 CѦ!4NaB Ц] sU<fC׷,8wWd>*J`FpE{՜IKA-Gg6ibbn(p{˲Ɋ$Uj`6YiQṵI>rx0=)xɴ0Ieu&y~9$]$pvo*Iƾtp[@e0eeM4E9pf7 (i"b yOf2[>fQrK"_ FnS3_(#b}wicɄ߶ANX7s"|!&,₷ٟ gڨk O^@+7i[В@K:?&/a%mH\>Kp'ns_+Sk,}ĥR^a0j6§gYWl"%=2ds ^Rۇ8 za]`ѱKfñ$J]g9̅>SVKBQ0\2Ð}J7 0hFV-+4FQ\"VʋLttU,6j*5T)8tyNbS0[@UXbe&C'Q{6&1L[Nê8wVF3w l K'ƿ^,u (F q (SEԥ N*6͆pLk28SL~:E.7Kj,+8iPUѰ^"sahK~XSljQ`'r4E[tMk Ƥ^l,?#Nv4HB_a0QB"SL,Dt;!)1ֱ<]SpE'1'j~>/|k0IENDB`linphone-desktop-5.0.2/linphone-app/assets/images/000077500000000000000000000000001434616504300221505ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/images/add_custom.svg000066400000000000000000000025301434616504300250130ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/add_participant_custom.svg000066400000000000000000000030061434616504300274100ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/admin_selected_custom.svg000066400000000000000000000024661434616504300272330ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/attachment_custom.svg000066400000000000000000000033151434616504300264150ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/auto_answer_custom.svg000066400000000000000000000045161434616504300266200ustar00rootroot00000000000000 image/svg+xml auto_answer_active auto_answer_active Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/back_custom.svg000066400000000000000000000024331434616504300251650ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/burger_menu_custom.svg000066400000000000000000000046321434616504300266020ustar00rootroot00000000000000 image/svg+xml burger_menu_over_dark burger_menu_over_dark burger_menu_over_dark Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/calendar_custom.svg000066400000000000000000000030531434616504300260350ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/calendar_participants_custom.svg000066400000000000000000000043471434616504300306250ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_accept_custom.svg000066400000000000000000000027101434616504300265150ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_chat_secure_custom.svg000066400000000000000000000045041434616504300275460ustar00rootroot00000000000000 image/svg+xml chat_secure@x2 chat_secure@x2 Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/call_chat_unsecure_custom.svg000066400000000000000000000046541434616504300301170ustar00rootroot00000000000000 image/svg+xml chat_unsecure@x2 chat_unsecure@x2 Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/call_custom.svg000066400000000000000000000027101434616504300251760ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_history_custom.svg000066400000000000000000000037111434616504300267610ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_menu_custom.svg000066400000000000000000000030671434616504300262300ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_quality_0_custom.svg000066400000000000000000000031411434616504300271640ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_quality_1_custom.svg000066400000000000000000000033601434616504300271700ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_quality_2_custom.svg000066400000000000000000000036011434616504300271670ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_quality_3_custom.svg000066400000000000000000000040221434616504300271660ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_quality_4_custom.svg000066400000000000000000000042431434616504300271740ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/call_sign_connected.svg000066400000000000000000000020461434616504300266500ustar00rootroot00000000000000 call_in_sign Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/call_sign_ended.svg000066400000000000000000000020221434616504300257570ustar00rootroot00000000000000 call_in_sign Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/call_sign_incoming.svg000066400000000000000000000017771434616504300265230ustar00rootroot00000000000000 call_incoming_sign Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/call_sign_outgoing.svg000066400000000000000000000017701434616504300265440ustar00rootroot00000000000000 call_outgoing_sign Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/call_sign_paused.svg000066400000000000000000000013541434616504300261700ustar00rootroot00000000000000 call_pausing_sign Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/camera_off_custom.svg000066400000000000000000000025551434616504300263540ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/camera_on_custom.svg000066400000000000000000000024461434616504300262150ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/camera_preview_custom.svg000066400000000000000000000027351434616504300272630ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/cancel_custom.svg000066400000000000000000000024361434616504300255150ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_amount.svg000066400000000000000000000022761434616504300252020ustar00rootroot00000000000000 statut_avatar_busy_l Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/chat_audio_pause_custom.svg000066400000000000000000000023331434616504300275610ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_audio_play_custom.svg000066400000000000000000000025751434616504300274210ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_audio_preview_pause_custom.svg000066400000000000000000000032771434616504300313320ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_audio_preview_play_custom.svg000066400000000000000000000025621434616504300311560ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_audio_preview_stop_custom.svg000066400000000000000000000030321434616504300311670ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_audio_soundwave_custom.svg000066400000000000000000000040451434616504300304610ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_count.svg000066400000000000000000000022751434616504300250260ustar00rootroot00000000000000 chat_count Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/chat_custom.svg000066400000000000000000000031341434616504300252030ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_delivered.svg000066400000000000000000000041021434616504300256300ustar00rootroot00000000000000 imdn_read imdn_read linphone-desktop-5.0.2/linphone-app/assets/images/chat_error.svg000066400000000000000000000047641434616504300250340ustar00rootroot00000000000000 image/svg+xml chat_error chat_error Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/chat_is_composing_0.svg000066400000000000000000000024221434616504300266000ustar00rootroot00000000000000 chat_in_writing_0 Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/chat_is_composing_1.svg000066400000000000000000000024221434616504300266010ustar00rootroot00000000000000 chat_in_writing_1 Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/chat_is_composing_2.svg000066400000000000000000000024221434616504300266020ustar00rootroot00000000000000 chat_in_writing_2 Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/chat_is_composing_3.svg000066400000000000000000000024221434616504300266030ustar00rootroot00000000000000 chat_in_writing_3 Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/chat_menu_custom.svg000066400000000000000000000053651434616504300262370ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_micro_custom.svg000066400000000000000000000034641434616504300264020ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/chat_read.svg000066400000000000000000000040501434616504300246020ustar00rootroot00000000000000 imdn_read imdn_read linphone-desktop-5.0.2/linphone-app/assets/images/chat_room_custom.svg000066400000000000000000000066361434616504300262510ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/close_custom.svg000066400000000000000000000024361434616504300253750ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/collapsed_custom.svg000066400000000000000000000042221434616504300262310ustar00rootroot00000000000000 image/svg+xml drop_down_list drop_down_list Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/conference_audio_only_custom.svg000066400000000000000000000037721434616504300306250ustar00rootroot00000000000000 image/svg+xml linphone-desktop-5.0.2/linphone-app/assets/images/conference_custom.svg000066400000000000000000000120421434616504300263710ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/conference_layout_active_speaker_custom.svg000066400000000000000000000054111434616504300330350ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/conference_layout_grid_custom.svg000066400000000000000000000051331434616504300307760ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/conference_merge_custom.svg000066400000000000000000000214001434616504300275460ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/contact_add_custom.svg000066400000000000000000000033041434616504300265260ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/contact_card_photo_custom.svg000066400000000000000000000066441434616504300301320ustar00rootroot00000000000000 contact_card_photo_over Created with Sketch. contact_card_photo_over linphone-desktop-5.0.2/linphone-app/assets/images/contact_custom.svg000066400000000000000000000073011434616504300257170ustar00rootroot00000000000000 contact_selected Created with Sketch. contact_selected linphone-desktop-5.0.2/linphone-app/assets/images/contact_delete_custom.svg000066400000000000000000000026061434616504300272440ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/contact_edit_custom.svg000066400000000000000000000044661434616504300267350ustar00rootroot00000000000000 image/svg+xml settings_edit_default settings_edit_default Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/contact_view_custom.svg000066400000000000000000000033141434616504300267510ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/current_account_status_busy.svg000066400000000000000000000027701434616504300305420ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/current_account_status_dnd.svg000066400000000000000000000027671434616504300303330ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/current_account_status_offline.svg000066400000000000000000000027731434616504300312050ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/current_account_status_online.svg000066400000000000000000000040271434616504300310410ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/declined_incoming_call_custom.svg000066400000000000000000000024361434616504300307150ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/declined_outgoing_call_custom.svg000066400000000000000000000024361434616504300307450ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/delete_custom.svg000066400000000000000000000026061434616504300255310ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/dialpad_custom.svg000066400000000000000000000045221434616504300256640ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/download_custom.svg000066400000000000000000000076301434616504300261000ustar00rootroot00000000000000 dowanload_icon Created with Sketch. dowanload_icon linphone-desktop-5.0.2/linphone-app/assets/images/drop_down_custom.svg000066400000000000000000000042221434616504300262560ustar00rootroot00000000000000 image/svg+xml drop_down_list drop_down_list Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/edit_custom.svg000066400000000000000000000044661434616504300252220ustar00rootroot00000000000000 image/svg+xml settings_edit_default settings_edit_default Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/ended_call_custom.svg000066400000000000000000000030171434616504300263360ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/expanded_custom.svg000066400000000000000000000042301434616504300260520ustar00rootroot00000000000000 image/svg+xml drop_down_list drop_down_list Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/file_custom.svg000066400000000000000000000056301434616504300252060ustar00rootroot00000000000000 image/svg+xml settings_file_1 settings_file_1 Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/file_extension_custom.svg000066400000000000000000000044161434616504300273030ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/file_sign.svg000066400000000000000000000033401434616504300246300ustar00rootroot00000000000000 download_ended Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/file_unknown_custom.svg000066400000000000000000000026241434616504300267650ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/filter_custom.svg000066400000000000000000000012571434616504300255550ustar00rootroot00000000000000 filter Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/filter_params_custom.svg000066400000000000000000000072461434616504300271240ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/folder_custom.svg000066400000000000000000000046651434616504300255510ustar00rootroot00000000000000 image/svg+xml settings_folder_1 settings_folder_1 Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/fullscreen_custom.svg000066400000000000000000000025671434616504300264370ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/generic_error.svg000066400000000000000000000044551434616504300255260ustar00rootroot00000000000000 image/svg+xml chat_error chat_error Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/group_chat_custom.svg000066400000000000000000000120361434616504300264200ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/hangup_custom.svg000066400000000000000000000030031434616504300255410ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/history_custom.svg000066400000000000000000000024321434616504300257650ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/home_account_assistant.svg000066400000000000000000000113451434616504300274320ustar00rootroot00000000000000 image/svg+xml home_account_assistant home_account_assistant Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/home_custom.svg000066400000000000000000000036001434616504300252120ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/home_invite_friends.svg000066400000000000000000000130141434616504300267100ustar00rootroot00000000000000 image/svg+xml home_invite_friends home_invite_friends Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/home_use_linphone.svg000066400000000000000000000105131434616504300263710ustar00rootroot00000000000000 image/svg+xml home_use_linphone home_use_linphone Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/ics_edit_custom.svg000066400000000000000000000037671434616504300260630ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/incoming_call_custom.svg000066400000000000000000000043621434616504300270660ustar00rootroot00000000000000 image/svg+xml call_outgoing call_outgoing Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/led_green.svg000066400000000000000000000027461434616504300246260ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/led_orange.svg000066400000000000000000000027511434616504300247750ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/led_red.svg000066400000000000000000000027431434616504300242750ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/led_white.svg000066400000000000000000000027501434616504300246410ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/linphone_logo.svg000066400000000000000000000050051434616504300255250ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/meetings_custom.svg000066400000000000000000000054051434616504300261020ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/menu_copy_text_custom.svg000066400000000000000000000035051434616504300273300ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/menu_devices_custom.svg000066400000000000000000000041561434616504300267370ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/menu_ephemeral_custom.svg000066400000000000000000000050131434616504300272500ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/menu_forward_custom.svg000066400000000000000000000065331434616504300267620ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/menu_imdn_info_custom.svg000066400000000000000000000037701434616504300272600ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/menu_info_custom.svg000066400000000000000000000037701434616504300262510ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/menu_reply_custom.svg000066400000000000000000000065251434616504300264520ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/menu_vdots_custom.svg000066400000000000000000000034361434616504300264540ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/message_sign.svg000066400000000000000000000033071434616504300253400ustar00rootroot00000000000000 call_sms_sign Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/micro_off_custom.svg000066400000000000000000000032131434616504300262250ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/micro_on_custom.svg000066400000000000000000000031411434616504300260670ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/missed_incoming_call_custom.svg000066400000000000000000000024361434616504300304320ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/missed_outgoing_call_custom.svg000066400000000000000000000024361434616504300304620ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/move_to_bottom_custom.svg000066400000000000000000000051311434616504300273170ustar00rootroot00000000000000 image/svg+xml drop_down_list drop_down_list Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/new_call_custom.svg000066400000000000000000000027721434616504300260570ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/new_chat_group_custom.svg000066400000000000000000000140661434616504300272760ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/options_custom.svg000066400000000000000000000041641434616504300257630ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/outgoing_call_custom.svg000066400000000000000000000043601434616504300271140ustar00rootroot00000000000000 image/svg+xml call_outgoing call_outgoing Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/panel_arrow_custom.svg000066400000000000000000000026751434616504300266060ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/panel_hidden_hovered.svg000066400000000000000000000044721434616504300270260ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/panel_hidden_normal.svg000066400000000000000000000044721434616504300266620ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/panel_hidden_pressed.svg000066400000000000000000000044721434616504300270370ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/panel_shown_hovered.svg000066400000000000000000000032651434616504300267300ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/panel_shown_normal.svg000066400000000000000000000032651434616504300265640ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/panel_shown_pressed.svg000066400000000000000000000032651434616504300267410ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/participants_custom.svg000066400000000000000000000111671434616504300267720ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/pause_custom.svg000066400000000000000000000023151434616504300254010ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/play_custom.svg000066400000000000000000000023051434616504300252300ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/record_custom.svg000066400000000000000000000052641434616504300255500ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/recording_sign.svg000066400000000000000000000040621434616504300256670ustar00rootroot00000000000000 image/svg+xml linphone-desktop-5.0.2/linphone-app/assets/images/remove_participant_custom.svg000066400000000000000000000024361434616504300301630ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/schedule_custom.svg000066400000000000000000000027141434616504300260630ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/screen_sharing_custom.svg000066400000000000000000000031471434616504300272620ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/screenshot_custom.svg000066400000000000000000000030321434616504300264360ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/search_custom.svg000066400000000000000000000064361434616504300255410ustar00rootroot00000000000000 search Created with Sketch. search linphone-desktop-5.0.2/linphone-app/assets/images/secure_level_1.svg000066400000000000000000000045431434616504300255740ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/secure_level_2.svg000066400000000000000000000311461434616504300255740ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/secure_level_unsafe.svg000066400000000000000000000224751434616504300267210ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/secure_off.svg000066400000000000000000000051031434616504300250100ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/secure_on.svg000066400000000000000000000035101434616504300246520ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/secure_pq_zrtp.svg000066400000000000000000000104211434616504300257340ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/send_custom.svg000066400000000000000000000033261434616504300252200ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/settings_advanced_custom.svg000066400000000000000000000041251434616504300277520ustar00rootroot00000000000000 settings_advanced_selected Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/settings_audio_custom.svg000066400000000000000000000044731434616504300273140ustar00rootroot00000000000000 image/svg+xml settings_audio_selected settings_audio_selected Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/settings_call_custom.svg000066400000000000000000000020231434616504300271130ustar00rootroot00000000000000 settings_call_selected Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/settings_network_custom.svg000066400000000000000000000133161434616504300277000ustar00rootroot00000000000000 image/svg+xml settings_network_selected settings_network_selected Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/settings_sip_accounts_custom.svg000066400000000000000000000052621434616504300307020ustar00rootroot00000000000000 image/svg+xml settings_account_selected settings_account_selected Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/settings_video_custom.svg000066400000000000000000000042531434616504300273150ustar00rootroot00000000000000 image/svg+xml settings_video_selected settings_video_selected Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/snapshot_sign.svg000066400000000000000000000067211434616504300255560ustar00rootroot00000000000000 image/svg+xml linphone-desktop-5.0.2/linphone-app/assets/images/speaker_off_custom.svg000066400000000000000000000026511434616504300265530ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/speaker_on_custom.svg000066400000000000000000000025721434616504300264170ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/stop_fullscreen_custom.svg000066400000000000000000000026011434616504300274710ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/tel_keypad_voicemail_custom.svg000066400000000000000000000044171434616504300304420ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/timer_custom.svg000066400000000000000000000050131434616504300254020ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/tooltip_arrow_bottom_custom.svg000066400000000000000000000036301434616504300305550ustar00rootroot00000000000000 image/svg+xml tooltip tooltip Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/tooltip_arrow_left_custom.svg000066400000000000000000000035371434616504300302110ustar00rootroot00000000000000 image/svg+xml tooltip tooltip Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/tooltip_arrow_right_custom.svg000066400000000000000000000035761434616504300303770ustar00rootroot00000000000000 image/svg+xml tooltip tooltip Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/tooltip_arrow_top_custom.svg000066400000000000000000000035661434616504300300630ustar00rootroot00000000000000 image/svg+xml tooltip tooltip Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/transfer_custom.svg000066400000000000000000000024771434616504300261210ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/update_sign.svg000066400000000000000000000111071434616504300251730ustar00rootroot00000000000000 image/svg+xml download_ended Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/images/video_call_accept_custom.svg000066400000000000000000000024351434616504300277070ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/video_call_custom.svg000066400000000000000000000024351434616504300263700ustar00rootroot00000000000000 linphone-desktop-5.0.2/linphone-app/assets/images/warning.svg000066400000000000000000000047641434616504300243510ustar00rootroot00000000000000 image/svg+xml chat_error chat_error Created with Sketch. linphone-desktop-5.0.2/linphone-app/assets/languages/000077500000000000000000000000001434616504300226515ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/assets/languages/CMakeLists.txt000066400000000000000000000062111434616504300254110ustar00rootroot00000000000000# ============================================================================== # assets/languages/CMakeLists.txt # ============================================================================== # This line prevent `.ts` files deletion. # See: https://bugreports.qt.io/browse/QTBUG-31860 # # On October 17, 2016, this issue was marked `invalid` but it's a # bullshit. It's not tolerated to remove source files. set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM true) # Build languages resource file. set(TS_FILES) set(QM_FILES) set(QM_FILES_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") set(I18N_CONTENT "\n\n \n") foreach (lang ${LANGUAGES}) # Note: the below `languages/` path is not the same as the `${LANGUAGES_DIRECTORY}` value. # It's the symbolic path used by the linphone binary in the qrc model. # This path is used in `app.cpp`. set(I18N_CONTENT "${I18N_CONTENT} ${lang}.qm\n") list(APPEND TS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${lang}.ts") list(APPEND TARGET_TS_FILES "${CMAKE_CURRENT_BINARY_DIR}/${lang}.ts") list(APPEND QM_FILES "${CMAKE_CURRENT_BINARY_DIR}/${lang}.qm") endforeach() set(I18N_CONTENT "${I18N_CONTENT} \n\n") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${I18N_FILENAME}" "${I18N_CONTENT}") #Files or directories to inspect for translations references set(TRANSLATION_SOURCES) list(APPEND TRANSLATION_SOURCES "${PROJECT_SOURCE_DIR}/src") list(APPEND TRANSLATION_SOURCES "${PROJECT_SOURCE_DIR}/ui") if (WIN32) foreach (lang ${LANGUAGES}) file(READ "${CMAKE_CURRENT_SOURCE_DIR}/${lang}.ts" content) set(cleanedContent) string(REPLACE "\r" "" cleanedContent "${content}") file(WRITE "${CMAKE_CURRENT_SOURCE_DIR}/${lang}.ts" "${cleanedContent}") endforeach() endif() set(LUPDATE_OPTIONS "-no-obsolete") set(LRELEASE_OPTIONS "") #Clean existing generated file to force re-creation file(REMOVE ${QM_FILES}) file(REMOVE ${TARGET_TS_FILES}) #file(REMOVE ${QRC_BIG_RESOURCES}) #Remove .o of resources before build. It is need to update resources from changes add_custom_command(OUTPUT ${TARGET_TS_FILES} COMMAND ${Qt5_LUPDATE_EXECUTABLE} ARGS ${LUPDATE_OPTIONS} ${TRANSLATION_SOURCES} -ts ${TS_FILES} COMMAND ${CMAKE_COMMAND} -E copy ${TS_FILES} ${CMAKE_CURRENT_BINARY_DIR} VERBATIM WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Updating translation source files (ts)..." ) add_custom_command(OUTPUT ${QM_FILES} COMMAND ${Qt5_LRELEASE_EXECUTABLE} ARGS ${TARGET_TS_FILES} ${LRELEASE_OPTIONS} DEPENDS ${TARGET_TS_FILES} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} VERBATIM COMMENT "Creating compiled translation files (qm)..." ) #The dependency on custom_command Output is important add_custom_target(update_translations DEPENDS ${QM_FILES} ) # Remove `*.qm` when `clean` is called. set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${QM_FILES}") if (WIN32) # Workaround: Create empty files for some cmake versions. Otherwise, the qm rules can't be used. foreach (qm ${QM_FILES}) if (NOT EXISTS "${qm}") file(GENERATE OUTPUT "${qm}" CONTENT "") endif() endforeach() endif() linphone-desktop-5.0.2/linphone-app/assets/languages/da.ts000066400000000000000000005117101434616504300236120ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name Hjælp os at oversætte %1 ActivateAppSipAccountWithEmail activateAppSipAccount AKTIVER DIN %1 KONTO confirmAction AKTIVER activationSteps For at aktivere din konto, følg vejledningen sendt til %1 og klik hernede. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount AKTIVER DIN %1 KONTO confirmAction AKTIVER activationSteps Vi har sendt en SMS med valideringskoden til %1. For at kunne fuldføre valideringen, indtast den 4-cifrede kode hernede. App commandLineOptionVerbose log til stdout nogle debug informationer under kørslen commandLineOptionConfig vælg %1 konfigurationsfilen der skal bruges applicationDescription En gratis SIP video telefon. commandLineOptionIconified start i systemets bakke, vis ikke hovedvinduet commandLineOptionConfigArg fil commandLineOptionHelp vis denne hjælp commandLineOptionVersion vis app versionen commandLineOptionCliHelp vis hjælpemenu for at bruge %1 med CLI commandLineDescription send en ordre til applikationen mod en kommandolinje restore Gendan quit Luk settings Indstillinger about Om commandLineOptionFetchConfig Angiv %1 konfigurationsfilen der skal hentes. Den vil blive flettet med den nuværende konfiguration. commandLineOptionFetchConfigArg URL, sti eller fil commandLineOptionCall ring op commandLineOptionCallArg SIP adresse checkForUpdates Tjek for opdateringer AssistantAbstractView back TILBAGE AssistantHome useAppSipAccount BRUG EN %1 KONTO useOtherSipAccount BRUG EN SIP KONTO fetchRemoteConfiguration HENT FJERN KONFIGURATION homeTitle VELKOMMEN homeDescription Assistenten vil hjælpe dig med konfigurationen af din sip konto. createAppSipAccount OPRET EN %1 KONTO homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. Jeg accepterer %1s %2betingelser for brug%3 og %4fortrolighedspolitik%5 AssistantModel loginWithUsernameFailed Fejl ved log ind. Tjek dit brugernavn eller kodeord. usernameStatusTooShort For kort! (%1 tegn min.) usernameStatusTooLong For lang! (%1 tegn maks.) usernameStatusInvalidCharacters Ugyldig tegn. (regex: `%1`) usernameStatusInvalid Forkert brugernavn. passwordStatusTooShort For kort! (%1 tegn min.) passwordStatusTooLong For lang! (%1 tegn maks.) passwordStatusInvalidCharacters Ugyldig tegn. (regex: `%1`) passwordStatusMissingCharacters Manglende tegn: `%1`. requestFailed Umuligt at sende anmodningen. emailStatusMalformed Ugyldig e-mailadresse. emailStatusMalformedInvalidCharacters Ugyldig e-mailadresse eller tegn. cannotSendSms Serverfejl: umuligt at sende sms. accountAlreadyExists Kontoen findes allerede. smsActivationFailed Fejlet SMS aktivering! emailActivationFailed Tjek at du har valideret din konto eller prøv igen. phoneNumberStatusInvalid Ugyldig telefonnummer! phoneNumberStatusTooShort For kort! phoneNumberStatusTooLong For lang! phoneNumberStatusInvalidCountryCode Forkert landekode! loginWithPhoneNumberFailed Fejl ved log ind. Tjek dit telefonnummer. unableToAddAccount Umuligt at oprette kontoen. AuthenticationRequest cancel ANNULLER confirm LOG IND identityLabel Identitet passwordLabel Kodeord authenticationRequestDescription Fejl ved log ind. Tjek dit kodeord. userIdLabel Bruger ID (valgfrit) realmLabel Realm CallModel callStatsCodec Codec callStatsUploadBandwidth Båndbredde for upload callStatsDownloadBandwidth Båndbredde for download callStatsEstimatedDownloadBandwidth Estimeret båndbredde for download callStatsIceState ICE status callStatsIpFamily IP familie callStatsSenderLossRate Senders tab hastighed callStatsReceiverLossRate Modtagers tab hastighed callStatsJitterBuffer Jitterbuffer callStatsSentVideoDefinition Sendt video opløsning callStatsReceivedVideoDefinition Modtaget video opløsning iceStateNotActivated Deaktiveret iceStateFailed Fejlet iceStateInProgress I gang iceStateReflexiveConnection Refleksiv forbindelse iceStateHostConnection Host forbindelse iceStateRelayConnection Relay forbindelse iceStateInvalid Ugyldig callErrorDeclined Fjern part afviste opkaldet. callErrorNotFound Fjern part blev ikke fundet. callErrorBusy Fjern part er optaget. callErrorNotAcceptable Fjern part can ikke modtage opkaldet. callStatsReceivedFramerate Modtaget hastighed callStatsSentFramerate Sendt hastighed callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel ANNULLER callSipAddressDescription Start et nyt opkald. CallStatistics audioStatsLabel Audio videoStatsLabel Video mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section CallTransfer cancel ANNULLER callTransferDescription Vil du overføre dette opkald? Calls acceptAudioCall BESVAR AUDIO OPKALD acceptVideoCall BESVAR VIDEO OPKALD terminateCall LÆG RØR PÅ resumeCall FORTSÆT OPKALDET transferCall OVERFØR OPKALDET callPause PAUSE OPKALDET attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CallsWindow callsTitle Opkald acceptClosingDescription Er du sikker på at du vil afbryde alle opkald? Chat newMessagePlaceholder Indtast din besked noFileTransferUrl Fejl ved fil overførsel. Server url ikke konfigureret. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 indtaster… %1 indtaster… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. Kopieret til udklipsholder selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. Udvalg kopieret til udklipsholder forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. Vælg videresendelse af besked conferencesCopiedICS Samtales ICS er kopieret ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. Organisator icsDescription 'Description' : Title for the meeting description. Beskrivelse icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. Samtalesadresse icsJoinButton 'Join' : Action button to join the meeting. Deltag deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. Vil du slette dette møde? cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. Beskrivelse icsJoinButton 'Join' : Action button to join the meeting. Deltag icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. Invitation til møde icsParticipants '%1 participant' : number(=%1) of participant. %1 deltager %1 deltagere icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Sendt til %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Modtaget %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Læst %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 har ikke modtaget beskeden %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message Fejl under afsendelse til %1 %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) Annuller fileTransferDownload 'Download' : Message link to download a file Hent ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. Videresendt ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Kopier alt menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Kopier menuPlayMe Afspil mig! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Afsendelses status menuDelete 'Delete' : Item menu to delete a message Slet menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Skjul afsendelses status menuForward 'Forward' : Forward a message from menu Videresend menuReply 'Reply' : Reply to a message from menu Besvar ChatNoticeModel nMinute %1 minut %1 minutter nHour %1 time %1 timer nDay %1 dag %1 dage nWeek %1 uge %1 uger ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Besvar ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Besvar til %1 Cli appCliDescription Styr %1 applikationen ved hjælp af kommandolinje. uriCommandLineSyntax cliCommandLineSyntax commandsName kommandoliste: showFunctionDescription vis applikationens hovedvinduet. callFunctionDescription Start et opkald til sip-adresse. initiateConferenceFunctionDescription Start en konference. joinConferenceFunctionDescription Deltag i konferencen startet af sip-adressen som navnet viser. Hvis du er forbundet til en proxy, se "deltag konferencen som". joinConferenceAsFunctionDescription Deltag i konferencen startet af sip-adressen som har gæstens sip-adresse. Hvis du ikke er forbundet til en proxy, se "deltag konferencen". byeFunctionDescription Afslut opkald, alle eller det nuværende. CodecsViewer codecMime Navn codecEncoderDescription Beskrivelse codecEncoderClockRate Frekvens (Hz) codecBitrate Hastighed (Kbit/s) codecRecvFmtp Parametre codecStatus Status Conference conferenceTitle KONFERENCE ConferenceControls conference KONFERENCE ConferenceManager conferenceManagerDescription Vælg deltagere til din konference. cancel ANNULLER confirm START Conferences conferencesTitle 'Meetings' : Conference list title. Konferencer conferencesEndedFilter 'Finished' : Filter meetings on end status. Afsluttet conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. Konference URL er kopieret conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel ANNULLER confirm BEKRÆFT ContactEdit removeContactDescription Er du sikker på at du vil fjerne denne kontakt fra din telefonbog? sipAccounts SIP KONTI address ADRESSE emails E-MAILADRESSER webSites WEBSIDER avatarChooserTitle Vælg din avatar companies FIRMAER save GEM cancel ANNULLER sipAccountsPlaceholder SIP konto companiesPlaceholder Firma emailsPlaceholder E-mailadresse webSitesPlaceholder Webside street Vej postalCode Postnummer country Land locality Sted abortEditDescriptionText Er du sikker på at du vil annullere ændringen? tooltipShowConversation Vis samtale missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Contacts searchContactPlaceholder Søg kontakt selectAllContacts Alle selectConnectedContacts Forbundet addContact OPRET KONTAKT removeContactDescription Er du sikker på at du vil fjerne denne kontakt fra din telefonbog? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Conversation displayCallsAndMessages ALLE displayCalls OPKALD displayMessages BESKEDER removeAllEntriesDescription Er du sikker på at du vil rydde op historikken? tooltipContactEdit Rediger kontakt tooltipContactAdd Tilføj kontakt cleanHistory Slet historik adminStatus 'Admin' : Admin(istrator) One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list conversationMenuDelete 'Delete history' : Item menu to delete the chat's history conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription Hvordan vil du oprette din konto? createAppSipAccountTitle OPRET EN %1 KONTO withPhoneNumber MED ET TELEFONNUMMER withEmailAddress MED EN E-MAILADRESSE CreateAppSipAccountWithEmail createAppSipAccountTitle OPRET EN %1 KONTO confirmAction OPRET usernameLabel Brugernavn emailLabel E-mailadresse passwordLabel Kodeord passwordConfirmationLabel Bekræft kodeordet passwordConfirmationError Kodeordet du har indtastet matcher ikke. quitWarning Din konto er blevet oprettet men endnu ikke valideret. Er du sikker på at du vil lukke denne dialogboks? displayNameLabel Navn til visning (valgfrit) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle OPRET EN %1 KONTO countryLabel Land phoneNumberLabel Telefonnummer usernameLabel Brugernavn displayNameLabel Navn til visning (valgfrit) confirmAction OPRET quitWarning Din konto er blevet oprettet men endnu ikke valideret. Hvis du lukker denne dialogboks skal du indtaste og validere din konto indenfor 24 timer. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Vælg en eller flere filer dropYourAttachment Slip din vedhæftet fil attachmentTooltip Send en fil EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation startButton 'start' : button text to start ephemeral mode ephemeralTitle "Ephemeral messages" : Popup title for ephemerals ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Warning about not being in conference based chat room. disabled 'Disabled' nMinute '%1 minute' %1 minut %1 minutter nHour '%1 hour' %1 time %1 timer nDay '%1 day' %1 dag %1 dage nWeek '%1 week' %1 uge %1 uger Event incomingCall Indgående opkald outgoingCall Udgående opkald declinedIncomingCall Afvist indgående opkald declinedOutgoingCall Afvist udgående opkald endedCall Afsluttet opkald missedIncomingCall Tabt indgående opkald missedOutgoingCall Tabt udgående opkald FetchRemoteConfiguration confirmAction HENT fetchRemoteConfigurationTitle HENT FJERN KONFIGURATION urlLabel URL remoteProvisioningError Umuligt at gemme denne fjern konfigurations uri. remoteProvisioningUpdateDescription Det er nødvendigt at genstarte applikationen. Vil du gøre det nu? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Er du sikker på at du vil rydde op historikken? tooltipContactEdit Rediger kontakt tooltipContactAdd Tilføj kontakt cleanHistory Slet historik Home howToDescription Har du behov for hjælp for at bruge %1? howToTitle HVORDAN MAN BRUGER %1 inviteDescription Inviter dine venner på %1. inviteTitle INVITER DINE VENNER accountAssistantDescription Opret eller ret din %1 konto. accountAssistantTitle KONTO ASSISTENT assistantButton ASSISTENT showTooltips Vis tips inviteButton INVITER Incall acceptVideoDescription Din kontakt vil skifte til video. securedStringFormat Opkaldet er krypteret med: %1. callNotSecured Opkaldet er ikke krypteret. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. incallMenuTitle 'Settings' : Main menu title for settings. incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status chatRoomDetailsTitle "Group information" : Popup title. popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation callButton 'CALL' : Button that lead to a call okButton 'OK' : Button that validate the popup to be redirected to the device list infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Explanation process InviteFriends enterEmailLabel Din vens e-mailadresse messageLabel Besked cancel ANNULLER confirm BEKRÆFT inviteFriendsTitle Inviter dine venner defaultMessage %1 inviterer dig på %2! defaultSubject %1 invitation forcedMessage Hent applikationen på din computer og start samtaler eller chat med venner gratis. Klik her: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 Om %1 Preferences... Indstillinger Services Tjenester Hide %1 Skjul %1 Hide Others Skjul andre Show All Vis alle Quit %1 Afslut %1 MainWindow mainSearchBarPlaceholder Søg en kontakt, start en samtale eller en chat… contactsEntry KONTAKTER autoAnswerStatus auto smartSearchBarTooltip Brug den smarte søgefunktion for at starte audio og video opkald, sende en besked eller oprette en ny kontakt. Indtast din vens SIP-adresse eller brugernavn. newConferenceButton Start konferenceopkald newChatRoom 'Start a chat room' : Tooltip to illustrate a button hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline openTimeline 'Open Timeline' : Tooltip for a button that open the timeline openHome 'Open Home' : Tooltip for a button that open the home view mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Indstillinger about Om quit Luk checkForUpdates 'Check for updates' : Item menu for checking updates Tjek for opdateringer MainWindowTopMenuBar settings Præferencer about Om quit Stop checkForUpdates 'Check for updates' : Item menu for checking updates Tjek for opdateringer ManageAccounts ok OK selectPresenceLabel Status selectAccountLabel Aktiv konto MultimediaParametersDialog ok OK menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button startButton 'Launch' : Start button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. subjectLabel 'Subject' : Label of a text field about the subject of the chat room subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject LastContactsTitle 'Last contacts' : Header for showing last contacts NewConference cancelButton 'Cancel' : Cancel button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject subjectTooltip 'Current subject of the Meeting. It cannot be empty' Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. Notifier newVersionAvailable En ny version (%1) er tilgængelig! newFileMessage Modtaget ny vedhæftet fil! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm BEKRÆFT onlineInstallerExtractingDescription Åbning %1… onlineInstallerDownloadingDescription Downloading %1… onlineInstallerFinishedDescription %1 er nu installeret! onlineInstallerFailedDescription Fejl ved installationen af %1! OutgoingMessage messageError Fejl messageRead Læs messageDelivered Afleveret ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline Tilgængelig presenceBusy Optaget presenceDoNotDisturb Forstyr ikke presenceOffline Offline QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. SettingsAdvanced logsTitle Logs logsFolderLabel Logs folder sendLogs SEND LOGS logsUploadUrlLabel Server URL for uploading af logs logsUploadFailed Fejl ved log uploading. logsEnabledLabel Aktiver logs cleanLogs NUSTIL LOGS cleanLogsDescription Er du sikker på at du vil fjerne alle logs? developerSettingsTitle Udvikler indstillinger developerSettingsEnabledLabel Aktiver udvikler indstillinger logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Mailer kan ikke findes men logfiler blev uploadet til %1 logsMailerSuccess Logfiler blev uploadet til %1 contactsTitle Kontakter noPlugin 'No Plugins to load' : Text in combobox viewlogs SettingsAudio audioTitle Audio parametre playbackDeviceLabel Afspilnings udstyr captureDeviceLabel Optagelses udstyr ringerDeviceLabel Klokkes udstyr ringLabel Klokke echoCancellationLabel Aktiver echo annullering audioCodecsTitle Audio codecs showAudioCodecsLabel Vis audio codecs playbackGainLabel Afspilning forstærkning captureGainLabel Capture gevinst audioTestLabel Optagelsesniveau audioSettingsInCallWarning Lydopkald i gang: Nogle indstillinger er ikke tilgængelige. echoCancellationCalibrationLabel calibratingEchoCancellationInProgress calibratingEchoCancellationDone calibratingEchoCancellationFailed calibratingEchoCancellationNone SettingsCallsChat fileServerLabel Filserver encryptWithLimeLabel Krypter med LIME limeDisabled Deaktiveret limeRequired Påkravet limePreferred Foretrukket chatTitle Chat callsTitle Opkald encryptionLabel Kryptering noEncryption Ingen autoAnswerLabel Autosvar autoAnswerDelayLabel Forsinkelse (ms) autoAnswerWithVideoLabel Autosvar (med video) chatEnabledLabel Aktiver chat callRecorderEnabledLabel Aktiver opkalds optagelse chatNotificationSoundEnabledLabel Aktiver advarsels ringetone chatNotificationSoundLabel Advarsels ringetone conferenceEnabledLabel Aktiver konference contactsTitle Kontakter contactsEnabledLabel Aktiver kontakter muteMicrophoneEnabledLabel Mute mikrofon outgoingCallsEnabledLabel Aktiver udgående opkald showTelKeypadAutomaticallyLabel Vis telefonens tastatur automatisk automaticallyRecordCallsLabel Optag opkald automatisk keepCallsWindowInBackgroundLabel Sæt opkaldets vindue i baggrund callPauseEnabledLabel Aktiver pause på opkald encryptionMandatoryLabel Krydning er obligatorisk hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer SettingsLdapEdit cancel ANNULLER confirm displayNameLabel Navn til visning (valgfrit) displayNameTooltip connectionTitle serverLabel serverTooltip bindDNLabel bindDNTooltip passwordLabel Kodeord useTLSLabel useTLSTooltip useSalLabel useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' verifyTLSLabel AutoMode offMode onMode verifyTLSTooltip searchTitle baseObjectLabel baseObjectPlaceholder baseObjectTooltip filterLabel filterTooltip maxResultsLabel maxResultsTooltip timeoutLabel timeoutTooltip parsingTitle nameAttributesLabel nameAttributesTooltip sipAttributesLabel sipAttributesTooltip domainLabel domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. miscLabel debugLabel debugTooltip SettingsNetwork sendDtmfsLabel DTMFs afsendelses måde allowIpV6Label Tillad IPv6 transportTitle Transport natAndFirewallTitle NAT og firewall enableIceLabel Aktiver ICE stunServerLabel STUN/TURN server enableTurnLabel Aktiver TURN turnUserLabel TURNs bruger turnPasswordLabel TURNs kodeord networkProtocolAndPortsTitle Netværks protokol og porte sipUdpPortLabel SIP UDP port sipTcpPortLabel SIP TCP port audioRtpUdpPortLabel Audio RTP UDP port videoRtpUdpPortLabel Video RTP UDP port dscpFieldsTitle DSCP felter sipFieldLabel SIP audioRtpStreamFieldLabel Audio RTP stream videoRtpStreamFieldLabel Video RTP stream bandwidthControlTitle Båndbredde kontrol downloadSpeedLimitLabel Download hastigheds begrænsning i Kbit/sek uploadSpeedLimitLabel Upload hastigheds begrænsning i Kbit/sek enableAdaptiveRateControlLabel Aktiver tilpasset hastigheds kontrol presenceTitle Presence rlsUriLabel Brug RLS URI rlsUriAuto AUTO rlsUriDisabled ALDRIG showNetworkSettingsLabel Vis netværks indstillinger generalTitle Generelt SettingsSipAccounts defaultIdentityTitle Standards identitet defaultUsernameLabel Brugernavn defaultSipAddressLabel SIP-adresse proxyAccountsTitle Proxy konti eraseAllPasswords NUSTIL KODEORD addAccount OPRET KONTO editHeader Rediger deleteHeader Slet deleteAccountDescription Er du sikker på at du vil slette denne konto? eraseAllPasswordsDescription Er du sikker på at du vil slette alle kodeord? defaultDisplayNameLabel Navn til visning assistantTitle Assistent createAppSipAccountEnabledLabel Aktiver konto oprettelse useAppSipAccountEnabledLabel Aktiver brug af konto useOtherSipAccountEnabledLabel Aktiver generisk konto brug fetchRemoteConfigurationEnabledLabel Aktiver konfigurations afhentning assistantSupportsPhoneNumbersLabel Understøtter telefonnumre defaultDeviceNameLabel 'Device Name' : Label for setting the device name. webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel SIP-adresse transportLabel Transport serverAddressLabel SIP server adresse registrationDurationLabel Optagelses varighed (sek) routeLabel Rute contactParamsLabel Kontakt parametre publishPresenceLabel Publicer tilstedeværelses information avpfIntervalLabel AVPF almindelig RTCP interval (sek) registerEnabledLabel Registrer avpfEnabledLabel Aktiver AVPF cancel ANNULLER confirm BEKRÆFT invalidSipAddress Ugyldig SIP-adresse. invalidServerAddress Ugyldig server adresse. invalidRoute Ugyldig rute. enableIceLabel Aktiver ICE stunServerLabel STUN/TURN server enableTurnLabel Aktiver TURN turnUserLabel TURNs bruger turnPasswordLabel TURNs kodeord natAndFirewallTitle NAT og firewall mainSipAccountSettingsTitle SIP konto hovedindstillinger conferenceURI "Conference URI" : Label of a text edit for filling Conference URI invalidConferenceURI "invalid conference URI" : Error text about conference URI videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) tunnelDomain 'Domain' : Field title of a textfield to set domain. tunnelUsername 'Username' : Field title of a textfield to set username. tunnelSIP 'SIP' : Field title of a switch to set SIP mode. cancel 'Cancel' : Button to cancel the action. ANNULLER setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. proxyHttpHost 'Host' : Placeholder to set hostname. proxyHttpPort 'Port' : Placehoilder to set port. proxyHttpUsername 'Username' : Placeholder to set username. proxyHttpPassword 'Password' : Placeholder to set password. proxyHttpApply 'Apply' : Button to set proxy from changes. serverMode 'Mode' : Field title on form to set tunnel mode. serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. serverTitle 'Server' : Title form to set a server serverHostname 'Hostname' : Field title on form to set hostname. serverPort 'Port' : Field title on form to set port. serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. serverDelay 'Delay' : Field title on form to set the delay of the tunnel. tunnelAddServer 'Add server' : Button for adding a server tunnelApply 'Apply' : Button to apply changes. SettingsUi pathsTitle Stier savedScreenshotsLabel Gemte skærmbilleder folder savedCallsLabel Gemte opkald folder languagesTitle Sprog languagesLabel Sprog systemLocale System lokale cleanAvatars SLET AVATARS cleanAvatarsDescription Er du sikker på at du vil slette alle avatars? downloadLabel Download folder setLocaleDescription Det er nødvendigt at genstarte applikationen. Vil du gøre det nu? otherTitle Andet exitOnCloseLabel Luk applikationen ved at lukke vinduet dataTitle UI data autoStartLabel Autostart applikation fontsTitle 'Fonts' : title of fonts section in settings fontsTextChange 'Text Messages' : Label for changing text message fonts fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates mipmapLabel 'Enable Mipmap' mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Video indgang videoFramerateLabel Hastighed videoCaptureTitle Video parametre videoPresetLabel Video forudindstilling presetDefault Standard presetHighFps Høj FPS presetCustom Tilpasset videoSizeLabel Video opløsning videoCodecsTitle Video codecs showCameraPreview VIDEO FORHÅNDSVISNING showVideoCodecsLabel Vis video codecs videoSettingsInCallWarning Videoopkald i gang: Nogle indstillinger er ikke tilgængelige. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm OK SettingsWindow settingsTitle Indstillinger sipAccountsTab SIP konti audioTab Audio videoTab Video callsAndChatTab Opkald og chat networkTab Netværk uiTab UI validButton OK uiAdvanced Avanceret tunnelTab 'Tunnel' : Tab title for tunnel section in settings. SipAddressDialog cancel ANNULLER contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list SmartSearchBar addContact OPRET KONTAKT Timeline timelineFilter A title for filtering mode. timelineFilterAll 'All' The mode for timelines filtering. timelineFilterCustom 'Custom' The mode for timelines filtering. timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction BRUG useAppSipAccountTitle BRUG EN %1 KONTO useUsernameToLogin Brug brugernavn og kodeord frem for dit telefonnummer. quitWarning Din konto er blevet oprettet men endnu ikke valideret. Er du sikker på at du vil lukke denne dialogboks? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password UseAppSipAccountWithPhoneNumber countryLabel Land phoneNumberLabel Telefonnummer displayNameLabel Navn til visning (valgfrit) UseAppSipAccountWithUsername usernameLabel Brugernavn passwordLabel Kodeord displayNameLabel Navn til visning (valgfrit) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form BRUG useOtherSipAccountTitle BRUG EN SIP KONTO usernameLabel Brugernavn displayNameLabel Navn til visning (valgfrit) sipDomainLabel SIP domænet passwordLabel Kodeord transportLabel Transport addOtherSipAccountError Umuligt at oprette kontoen. understandAction 'I understand' : Popup confirmation for a warning warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. startButton 'Start' : Button label for starting the conference. endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Bekræft følgende SAS med peer. codeA Sig: codeB Din kontakt burde sige: Later 'Later' : Button label to do something in another time. Senere Correct 'Correct' : Button label to confirm a code. Korrigere title 'Communication security' : Title of popup for ZRTP confirmation. Kommunikationssikkerhed country Afghanistan Afghanistan Albania Albanien Algeria Algeriet AmericanSamoa Amerikansk Samoa Andorra Andorra Angola Angola Anguilla Anguilla AntiguaAndBarbuda Antigua og Barbuda Argentina Argentina Armenia Armenien Aruba Aruba Australia Australien Austria Østrig Azerbaijan Aserbajdsjan Bahamas Bahamas Bahrain Bahrain Bangladesh Bangladesh Barbados Barbados Belarus Hviderusland Belgium Belgien Belize Belize Benin Benin Bermuda Bermuda Bhutan Bhutan Bolivia Bolivia BosniaAndHerzegowina Bosnien-Hercegovina Botswana Botswana Brazil Brasilien Brunei Brunei Bulgaria Bulgarien BurkinaFaso Burkina Faso Burundi Burundi Cambodia Cambodja Cameroon Cameroun Canada Canada CapeVerde Kap Verde CaymanIslands Caymanøerne CentralAfricanRepublic Den Centralafrikanske Republik Chad Tchad Chile Chile China Kina Colombia Columbia Comoros Comorerne PeoplesRepublicOfCongo Republikken Congo DemocraticRepublicOfCongo Den Demokratiske Republik Congo CookIslands Cookøerne CostaRica Costa Rica IvoryCoast Elfenbenskysten Croatia Kroatien Cuba Cuba Cyprus Cypern CzechRepublic Tjekkiet Denmark Danmark Djibouti Djibouti Dominica Dominica DominicanRepublic Den Dominikanske Republik Ecuador Ecuador Egypt Ægypten ElSalvador El Salvador EquatorialGuinea Ækvatorialguinea Eritrea Eritrea Estonia Estland Ethiopia Etiopien FalklandIslands Falklandsøerne FaroeIslands Færøerne Fiji Fiji Finland Finland France Frankrig FrenchGuiana Fransk Guyana FrenchPolynesia Fransk Polynesien Gabon Gabon Gambia Gambia Georgia Georgien Germany Tyskland Ghana Ghana Gibraltar Gibraltar Greece Grækenland Greenland Grønland Grenada Grenada Guadeloupe Guadeloupe Guam Guam Guatemala Guatemala Guinea Guinea GuineaBissau Guinea-Bissau Guyana Guyana Haiti Haiti Honduras Honduras HongKong Hongkong Hungary Ungarn Iceland Island India Indien Indonesia Indonesien Iran Iran Iraq Irak Ireland Irland Israel Israel Italy Italien Jamaica Jamaica Japan Japan Jordan Jordan Kazakhstan Kasakhstan Kenya Kenya Kiribati Kiribati DemocraticRepublicOfKorea Den Demokratiske Republik Korea RepublicOfKorea Republikken Korea Kuwait Kuwait Kyrgyzstan Kirgisistan Laos Laos Latvia Letland Lebanon Libanon Lesotho Lesotho Liberia Liberia Libya Libyen Liechtenstein Liechtenstein Lithuania Litauen Luxembourg Luxembourg Macau Macao Macedonia Makedonien Madagascar Madagaskar Malawi Malawi Malaysia Malaysia Maldives Maldiverne Mali Mali Malta Malta MarshallIslands Marshalløerne Martinique Martinique Mauritania Mauretanien Mauritius Mauritius Mayotte Mayotte Mexico Mexico Micronesia Mikronesien Moldova Moldova Monaco Monaco Mongolia Mongoliet Montenegro Montenegro Montserrat Montserrat Morocco Marokko Mozambique Mozambique Myanmar Myanmar Namibia Namibia NauruCountry Nauru Nepal Nepal Netherlands Nederlandene NewCaledonia Ny Kaledonien NewZealand New Zealand Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Niue NorfolkIsland Norfolk Island NorthernMarianaIslands Nordmarianerne Norway Norge Oman Oman Pakistan Pakistan Palau Palau PalestinianTerritories Palæstinensiske områder Panama Panama PapuaNewGuinea Papua Ny Guinea Paraguay Paraguay Peru Peru Philippines Filippinerne Poland Polen Portugal Portugal PuertoRico Puerto Rico Qatar Qatar Reunion Genforening Romania Rumænien RussianFederation Den Russiske Føderation Rwanda Rwanda SaintHelena Saint Helena SaintKittsAndNevis Saint Kitts Og Nevis SaintLucia Saint Lucia SaintPierreAndMiquelon Saint Pierre og Miquelon SaintVincentAndTheGrenadines Saint Vincent Og Grenadinerne Samoa Samoa SanMarino San Marino SaoTomeAndPrincipe São Tomé Og Principe SaudiArabia Saudi-Arabien Senegal Senegal Serbia Serbien Seychelles Seychellerne SierraLeone Sierra Leone Singapore Singapore Slovakia Slovakiet Slovenia Slovenien SolomonIslands Salomonøerne Somalia Somalia SouthAfrica Sydafrika Spain Spanien SriLanka Sri Lanka Sudan Sudan Suriname Surinam Swaziland Swaziland Sweden Sverige Switzerland Schweiz Syria Syrien Taiwan Taiwan Tajikistan Tadsjikistan Tanzania Tanzania Thailand Thailand Togo Togo Tokelau Tokelau Tonga Tonga TrinidadAndTobago Trinidad Og Tobago Tunisia Tunesien Turkey Tyrkiet Turkmenistan Turkmenistan TurksAndCaicosIslands Turks- og Caicosøerne Tuvalu Tuvalu Uganda Uganda Ukraine Ukraine UnitedArabEmirates Forenede Arabiske Emirater UnitedKingdom Det Forenede Kongerige UnitedStates USA Uruguay Uruguay Uzbekistan Usbekistan Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietnam WallisAndFutunaIslands Wallis og Futunaøerne Yemen Yemen Zambia Zambia Zimbabwe - Zimbabwe utils downloadCodecDescription Vil du downloade %1 (%2)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/de.ts000066400000000000000000005230761434616504300236260ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name Helfen Sie uns zu %1 übersetzen ActivateAppSipAccountWithEmail activateAppSipAccount AKTIVIEREN SIE IHR %1 KONTO confirmAction AKTIVIEREN activationSteps Um Ihr Konto zu aktivieren, folgen Sie den Anweisungen, die wir an %1 gesendet haben. Anschließend klicken Sie hier. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount AKTIVIEREN SIE IHR %1-KONTO confirmAction AKTIVIEREN activationSteps Wir haben eine SMS mit einem Validierungs-Code an %1 gesendet. Um die Verifizierung der Telefonnummer abzuschließen, geben Sie bitten den 4-stelligen Code unten ein. App commandLineOptionVerbose Während der Verwendung nach stdout debug informationen ausgeben commandLineOptionConfig Bitte zu verwendende Konfigration angeben applicationDescription Ein freies (gratis) Video-Telefon. commandLineOptionIconified Im Systemtray starten, Hauptmenü nicht anzeigen commandLineOptionConfigArg Datei commandLineOptionHelp Diese Hilfe anzeigen commandLineOptionVersion App-Version anzeigen commandLineOptionCliHelp zeigt das Hilfe-Menü zur Kommandozeilen-Schnittstelle von %1 commandLineDescription sende einen Befehl zur Anwendung für eine Kommandozeile restore Wiederherstellen quit Schließen settings Einstellungen about Über commandLineOptionFetchConfig %1-Konfigurationsdatei angeben. Sie wird mit der aktuellen Konfiguration zusammengeführt. commandLineOptionFetchConfigArg URL, Pfad oder Datei commandLineOptionCall Anrufen commandLineOptionCallArg SIP-Adresse checkForUpdates Auf Aktualisierungen prüfen AssistantAbstractView back ZURÜCK AssistantHome useAppSipAccount %1-KONTO VERWENDEN useOtherSipAccount SIP-KONTO VERWENDEN fetchRemoteConfiguration FERNKONFIGRATION LADEN homeTitle WILLKOMMEN homeDescription Der Assistent hilft Ihnen das SIP-Konto zu konfigurieren und zu nutzen. createAppSipAccount %1-KONTO ERSTELLEN homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. Ich akzeptiere die %2Nutzungsbedingungen%3 und die %4Datenschutzerklärung%5 von %1 AssistantModel loginWithUsernameFailed Login fehlgeschlagen. Bitte überprüfen Sie Ihren Benutzernamen und das Passwort. usernameStatusTooShort Zu kurz (mindestens %1 Zeichen) usernameStatusTooLong Zu lang (maximal %1 Zeichen) usernameStatusInvalidCharacters Ungültige Zeichen gefunden. (regex: `%1`) usernameStatusInvalid Ungültiger Benutzername. passwordStatusTooShort Zu kurz! (mindestens %1 Zeichen) passwordStatusTooLong Zu lang! (maximal %1 Zeichen) passwordStatusInvalidCharacters Ungültige Zeichen gefunden. (regex: `%1`) passwordStatusMissingCharacters Fehlende Zeichen: `%1`. requestFailed Das Senden der Anfrage ist fehlgeschlagen. emailStatusMalformed Falsche E-Mail-Adresse. emailStatusMalformedInvalidCharacters Falsche E-Mail-Adresse oder ungültige Zeichen. cannotSendSms Server-Fehler: SMS konnte nicht versendet werden. accountAlreadyExists Dieses Konto existiert bereits. smsActivationFailed SMS-Aktivierung fehlgeschlagen! emailActivationFailed Bitte vergewissern Sie sich, dass Sie ihr Konto verifiziert haben oder versuchen Sie es erneut. phoneNumberStatusInvalid Ungültige Telefonnummer! phoneNumberStatusTooShort Zu kurz! phoneNumberStatusTooLong Zu lang! phoneNumberStatusInvalidCountryCode Ungültiger Länder-Code! loginWithPhoneNumberFailed Login fehlgeschlagen. Bitte überprüfen Sie Ihre Telefonnummer. unableToAddAccount Dieses Konto kann nicht hinzugefügt werden. AuthenticationRequest cancel ABBRECHEN confirm ANMELDEN identityLabel Identität passwordLabel Passwort authenticationRequestDescription Authentifizierung fehlgeschlagen. Bitte überprüfen Sie Ihr Passwort. userIdLabel Benutzerkennung (optional) realmLabel Realm CallModel callStatsCodec Codec callStatsUploadBandwidth Upload-Bandbreite callStatsDownloadBandwidth Download-Bandbreite callStatsEstimatedDownloadBandwidth Geschätzte Download-Bandbreite callStatsIceState ICE Status callStatsIpFamily IP-Familie callStatsSenderLossRate Sender Verlustrate callStatsReceiverLossRate Empfänger Verlustrate callStatsJitterBuffer Jitter Puffer callStatsSentVideoDefinition Gesendete Video-Definition callStatsReceivedVideoDefinition Empfangene Video-Definition iceStateNotActivated Nicht aktiviert iceStateFailed Fehlgeschlagen iceStateInProgress In Arbeit iceStateReflexiveConnection Reflexive Verbindung iceStateHostConnection Host Verbindung iceStateRelayConnection Relay Verbindung iceStateInvalid Ungültig callErrorDeclined Gegenstelle hat den Anruf abgelehnt. callErrorNotFound Gegenstelle wurde nicht gefunden. callErrorBusy Gegenstelle ist besetzt. callErrorNotAcceptable Die Gegenstelle kann den Anruf nicht annehmen. callStatsReceivedFramerate Erhaltene Framerate callStatsSentFramerate Gesendete Framerate callErrorHangUp Gegenstelle hat aufgelegt. callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics Medienverschlüsselung callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics Verschlüsselungsalgorithmus callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics Schlüsselaustauschprotokoll callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics Hashfunktion callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics Authentifizierungsalgorithmus callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel ABBRECHEN callSipAddressDescription Einen neuen Anruf starten. CallStatistics audioStatsLabel Audio videoStatsLabel Video mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section Medienverschlüsselung CallTransfer cancel ABBRECHEN callTransferDescription Möchten Sie diesen Anruf weiterleiten? Calls acceptAudioCall AUDIO-ANRUF ANNEHMEN acceptVideoCall VIDEO-ANRUF ANNEHMEN terminateCall AUFLEGEN resumeCall ANRUF FORTSETZEN transferCall ANRUF WEITERLEITEN callPause ANRUF PAUSIEREN attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. ANGENOMMENEN ANRUF WEITERLEITEN attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. ANGENOMMENEN ANRUF WEITERLEITEN CallsWindow callsTitle Anrufe acceptClosingDescription Sind Sie sicher, dass sie alle Anrufe beenden möchten? Chat newMessagePlaceholder Geben Sie ihre Nachricht ein noFileTransferUrl Datei kann nicht gesendet werden. Server URL ist nicht konfiguriert. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 tippt gerade… %1 tippen gerade… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. In die Zwischenablage kopiert selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. Auswahl in die Zwischenablage kopiert forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. Wählen Sie das Ziel der weiterzuleitenden Nachricht conferencesCopiedICS Die Einladung wurde kopiert ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. Organisator icsDescription 'Description' : Title for the meeting description. Beschreibung icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. icsJoinButton 'Join' : Action button to join the meeting. deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. icsJoinButton 'Join' : Action button to join the meeting. icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. icsParticipants '%1 participant' : number(=%1) of participant. icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Gesendet an %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Empfangen von %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Gelesen von %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 hat die Nachricht nicht erhalten %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) fileTransferDownload 'Download' : Message link to download a file ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Alle kopieren menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Kopieren menuPlayMe Abspielen! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Zustellstatus menuDelete 'Delete' : Item menu to delete a message Löschen menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Zustellstatus verbergen menuForward 'Forward' : Forward a message from menu Weiterleiten menuReply 'Reply' : Reply to a message from menu Antworten ChatNoticeModel nMinute %1 Minute %1 Minuten nHour %1 Stunde %1 Stunden nDay %1 Tag %1 Tage nWeek %1 Woche %1 Wochen ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Antworten ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Antwort an %1 Cli appCliDescription Methode um die %1 Anwendung per Kommandozeile zu steuern. uriCommandLineSyntax cliCommandLineSyntax commandsName Kommandoliste : showFunctionDescription Zeigt das Hauptfenster der Anwendung. callFunctionDescription Initiiere Anruf zur SIP-Adresse. initiateConferenceFunctionDescription Initiiere eine Konferenz. joinConferenceFunctionDescription Tritt der Konferenz auf dem Host sip-address als display-name bei. Wenn Sie über einen Proxy verbunden sind beachten Sie join-conference-as. joinConferenceAsFunctionDescription Tritt der Konferenz auf dem Host sip-address als guest-sip-address bei. Wenn Sie nicht über einen Proxy verbunden sind beachten Sie join-conference. byeFunctionDescription Beendet einen spezifischen Anruf, alle Anrufe oder den aktuellen Anruf. CodecsViewer codecMime Name codecEncoderDescription Beschreibung codecEncoderClockRate Rate (Hz) codecBitrate Bitrate (Kbit/s) codecRecvFmtp Parameter codecStatus Status Conference conferenceTitle KONFERENZ ConferenceControls conference KONFERENZ ConferenceManager conferenceManagerDescription Verwalten Sie die Teilnehmer Ihrer Konferenz. cancel ABBRECHEN confirm START Conferences conferencesTitle 'Meetings' : Conference list title. conferencesEndedFilter 'Finished' : Filter meetings on end status. conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel ABBRECHEN confirm BESTÄTIGEN ContactEdit removeContactDescription Möchten Sie wirklich diesen Kontakt aus Ihrem Adressbuch entfernen? sipAccounts SIP-KONTEN address ADRESSE emails E-MAIL(S) webSites WEBSEITE(N) avatarChooserTitle Wählen Sie ihr Avatar companies UNTERNEHMEN save SPEICHERN cancel ABBRECHEN sipAccountsPlaceholder SIP Konto companiesPlaceholder Unternehmen emailsPlaceholder Email webSitesPlaceholder Webseite street Straße postalCode Postleitzahl country Land locality Ort abortEditDescriptionText Möchten Sie die Änderungen an diesem Kontakt verwerfen? tooltipShowConversation Konversation anzeigen missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Sie müssen eine Konferenz-URI in den Kontoeinstellungen festlegen um einen konferenzbasierten Chatraum zu erstellen. Contacts searchContactPlaceholder Suche Kontakt selectAllContacts Alle selectConnectedContacts Verbunden addContact KONTAKT HINZUFÜGEN removeContactDescription Möchten sie diesen Kontakt wirklich aus Ihrem Adressbuch löschen? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Sie müssen eine Konferenz-URI in den Kontoeinstellungen festlegen um einen konferenzbasierten Chatraum zu erstellen. Conversation displayCallsAndMessages ALLE displayCalls ANRUFE displayMessages NACHRICHTEN removeAllEntriesDescription Möchten Sie diese Historie wirklich löschen? tooltipContactEdit Kontakt bearbeiten tooltipContactAdd Kontakt hinzufügen cleanHistory Verlauf löschen adminStatus 'Admin' : Admin(istrator) Administrator One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room Gruppeninformationen conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room Gesprächsgeräte conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode Kurzlebige Nachrichten groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room Alle Teilnehmer im Chatraum anrufen searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list Nachrichten durchsuchen conversationMenuDelete 'Delete history' : Item menu to delete the chat's history conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription Wie würden Sie gerne Ihr Konto erstellen? createAppSipAccountTitle ERSTELLE EIN %1 KONTO withPhoneNumber MIT EINER TELEFONNUMMER withEmailAddress MIT EINER E-MAIL-ADRESSE CreateAppSipAccountWithEmail createAppSipAccountTitle %1-KONTO ERSTELLEN confirmAction ERSTELLEN usernameLabel Benutzername emailLabel Email passwordLabel Passwort passwordConfirmationLabel Passwortbestätigung passwordConfirmationError Die eingegebenen Passwörter stimmen nicht überein. quitWarning Ihr Konto wurde erstellt, jedoch noch nicht aktiviert. Sind Sie sicher, dass Sie diese Ansicht verlassen möchten? displayNameLabel Anzeigename (optional) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle %1-KONTO ERSTELLEN countryLabel Land phoneNumberLabel Telefonnummer usernameLabel Benutzername displayNameLabel Anzeigename (optional) confirmAction ERSTELLEN quitWarning Ihr Konto wurde erstellt aber noch nicht verifiziert. Wenn Sie diese Ansicht schließen müssen Sie Ihr Konto innerhalb der nächsten 24 Stunden manuell anlegen und verifizieren. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Bitte wählen Sie eine oder mehrere Dateien dropYourAttachment Anhang fallen lassen attachmentTooltip Datei senden EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation ABBRECHEN startButton 'start' : button text to start ephemeral mode START ephemeralTitle "Ephemeral messages" : Popup title for ephemerals Kurzlebige Nachrichten ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals Neue Nachrichten werden auf beiden Seiten gelöscht wenn sie vom Gegenüber gelesen wurden. Wähle eine Ablauffrist. ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Kurzlebige Nachrichten werden nur in konferenzbasierten Chaträumen unterstützt! Warning about not being in conference based chat room. disabled 'Disabled' Deaktiviert nMinute '%1 minute' %1 Minute %1 Minuten nHour '%1 hour' %1 Stunde %1 Stunden nDay '%1 day' %1 Tag %1 Tage nWeek '%1 week' %1 Woche %1 Wochen Event incomingCall Eingehender Anruf outgoingCall Ausgehender Anruf declinedIncomingCall Abgelehnter eingehender Anruf declinedOutgoingCall Abgelehnter ausgehender Anruf endedCall Abgebrochener Anruf missedIncomingCall Verpasster eingehender Anruf missedOutgoingCall Verpasster ausgehender Anruf FetchRemoteConfiguration confirmAction ABRUFEN fetchRemoteConfigurationTitle HOLE REMOTE-KONFIGURATION urlLabel URL remoteProvisioningError Es ist nicht möglich, diese Fernkonfigrations-URL zu verwenden. remoteProvisioningUpdateDescription Ein Neustart der Anwendung ist notwendig. Möchten Sie die Anwendung jetzt neu starten? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Möchten Sie diesen Verlauf wirklich löschen? tooltipContactEdit Kontakt bearbeiten tooltipContactAdd Kontakt hinzufügen cleanHistory Verlauf löschen Home howToDescription Benötigen Sie hilfe um linephone zu nutzen? howToTitle WIE MAN %1 NUTZT inviteDescription Laden Sie Ihre Freunde bei %1 ein. inviteTitle FREUNDE EINLADEN accountAssistantDescription Erstellen oder verwalten Sie ihr %1 Konto. accountAssistantTitle KONTOASSISTENT assistantButton ASSISTENT showTooltips Zeige Tooltips inviteButton EINLADEN Incall acceptVideoDescription Ihr Kontakt würde gerne die Videoübertragung aktivieren. securedStringFormat Anruf ist verschlüsselt mit: %1. callNotSecured Anruf nicht verschlüsselt. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. incallMenuTitle 'Settings' : Main menu title for settings. incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label Gruppe verlassen ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Teilnehmer hinzufügen addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Suche nach Teilnehmern in der Kontaktliste um sie in den Chatraum einzuladen. Explanation for inviting the selected participants into chat room participantList 'Participant list' Teilnehmerliste adminStatus 'Admin' : Admin(istrator) Administrator word for admin status chatRoomDetailsTitle "Group information" : Popup title. Gruppeninformationen popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation ABBRECHEN callButton 'CALL' : Button that lead to a call ANRUFEN okButton 'OK' : Button that validate the popup to be redirected to the device list OK infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. Ende-zu-Ende-verschlüsselt encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Sofortnachrichten sind in sicheren Gesprächen ende-zu-ende-verschlüsselt. Es ist möglich die Sicherheit zu erhöhen, indem die Teilnehmer authentifiziert werden. Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Um das durchzuführen, rufen Sie den Kontakt an und folgen Sie dem Authentifizierungsprozess. Explanation process InviteFriends enterEmailLabel Email Adresse des Freundes messageLabel Nachricht cancel ABBRECHEN confirm BESTÄTIGEN inviteFriendsTitle Freunde einladen defaultMessage %1 möchte Sie bei %2 einladen ! defaultSubject %1-Einladung forcedMessage Laden Sie die Anwendung auf Ihren Computer um kostenlos mit anderen Linphone Benutzern zu telefonieren und zu chatten. Klicken Sie hier: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 Über %1 Preferences... Einstellungen Services Dienste Hide %1 %1 ausblenden Hide Others Andere Ausblenden Show All Alle Einblenden Quit %1 %1 beenden MainWindow mainSearchBarPlaceholder Suche Kontakte, starte einen Anruf oder Chat… contactsEntry KONTAKTE autoAnswerStatus automatisch smartSearchBarTooltip Verwenden das intelligente Suchfeld um Audio- und Videocalls zu starten, Nachrichten zu senden oder Kontakt hinzuzufügen. Geben Sie die SIP-Adresse oder Username Ihres Kontaktes ein. newConferenceButton Telefonkonferenz starten newChatRoom 'Start a chat room' : Tooltip to illustrate a button Starte einen Chatraum hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline Verlauf verstecken openTimeline 'Open Timeline' : Tooltip for a button that open the timeline Verlauf öffnen openHome 'Open Home' : Tooltip for a button that open the home view Startseite öffnen mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Einstellungen about Über quit Schließen checkForUpdates 'Check for updates' : Item menu for checking updates Auf Aktualisierungen prüfen MainWindowTopMenuBar settings Einstellungen about Über quit Aufhören checkForUpdates 'Check for updates' : Item menu for checking updates Auf Aktualisierungen prüfen ManageAccounts ok OK selectPresenceLabel Anwesenheitsstatus selectAccountLabel Aktives Konto MultimediaParametersDialog ok OK menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button ABBRECHEN startButton 'Launch' : Start button START missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Sie müssen ein Thema eintragen. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. Sie benötigen mindestens %1 Teilnehmer. Sie benötigen mindestens %1 Teilnehmer. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. Sie müssen eine Konferenz-URI in den Kontoeinstellungen festlegen um einen konferenzbasierten Chatraum zu erstellen. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room Starte einen Chatraum askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. Möchten Sie den Chat verschlüsseln? subjectLabel 'Subject' : Label of a text field about the subject of the chat room Betreff subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Aktuelles Thema des Chatraums. Darf nicht leer sein. Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Wähle Teilnehmer participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' In Kontakten suchen oder einen eigenen zum Chatraum hinzufügen. adminStatus 'Admin' : Admin(istrator) Administrator word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Teilnehmer aus Auswahl entfernen This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Erforderlich subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Betreff eingeben LastContactsTitle 'Last contacts' : Header for showing last contacts Letzte Kontakte NewConference cancelButton 'Cancel' : Cancel button ABBRECHEN missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Sie müssen ein Thema eintragen. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. Sie benötigen mindestens %1 Teilnehmer. Sie benötigen mindestens %1 Teilnehmer. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Sie müssen eine Konferenz-URI in den Kontoeinstellungen festlegen um einen konferenzbasierten Chatraum zu erstellen. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference Betreff subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Betreff eingeben subjectTooltip 'Current subject of the Meeting. It cannot be empty' Aktuelles Thema des Chatraums. Darf nicht leer sein. Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. Möchten Sie den Chat verschlüsseln? participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Wähle Teilnehmer participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' In Kontakten suchen oder einen eigenen zum Chatraum hinzufügen. adminStatus 'Admin' : Admin(istrator) Administrator word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Teilnehmer aus Auswahl entfernen This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Erforderlich launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. Sie sind der Gruppe beigetreten conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. Sie haben die Gruppe verlassen conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. %1 ist beigetreten conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group %1 ist ausgetreten conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody %1 ist jetzt Administrator conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody %1 ist nicht mehr Administrator conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. Sicherheitsstufe durch %1 heruntergesetzt conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time Kurzlebige Nachrichten wurden aktiviert: %1 conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. Kurzlebige Nachrichten wurden deaktiviert conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. Neuer Betreff: %1 conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Kurzlebige Nachrichten wurden aktualisiert: %1 unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. Notifier newVersionAvailable Eine neue Version (%1) ist verfügbar! newFileMessage Neuen Anhang erhalten! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm BESTÄTIGEN onlineInstallerExtractingDescription %1 wird extrahiert… onlineInstallerDownloadingDescription %1 wird heruntergeladen… onlineInstallerFinishedDescription %1 ist jetzt installiert! onlineInstallerFailedDescription Fehler beim installieren, %1! OutgoingMessage messageError Fehler messageRead Lesen messageDelivered Zugestellt ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Teilnehmer hinzufügen addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Suche nach Teilnehmern in der Kontaktliste um sie in den Chatraum einzuladen. Explanation for inviting the selected participants into chat room participantList 'Participant list' Teilnehmerliste adminStatus 'Admin' : Admin(istrator) Administrator word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) (Administrator) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline Verfügbar presenceBusy Besetzt presenceDoNotDisturb Nicht stören presenceOffline Offline QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. Aktivieren LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. Deaktivieren LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. Automatisch SettingsAdvanced logsTitle Protokolle logsFolderLabel Protokolleordner sendLogs PROTOKOLLE SENDEN logsUploadUrlLabel Server URL für Log-Upload logsUploadFailed Logs konnten nicht hochgeladen werden. logsEnabledLabel Protokolle aktiviert cleanLogs PROTOKOLLE BEREINIGEN cleanLogsDescription Sind Sie sicher, dass sie alle Logs löschen möchten? developerSettingsTitle Entwicklereinstellungen developerSettingsEnabledLabel Entwicklereinstellungen aktivieren logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Mailer kann nicht gefunden werden, Protokolle wurden in %1 hochgeladen logsMailerSuccess Protokolle wurden auf %1 hochgeladen contactsTitle Adressbuchquelle noPlugin 'No Plugins to load' : Text in combobox Keine Plugins zu laden viewlogs SettingsAudio audioTitle Audioparameter playbackDeviceLabel Wiedergabegerät captureDeviceLabel Aufnahmegerät ringerDeviceLabel Gerät für Anrufgeräusch ringLabel Anrufgeräusch echoCancellationLabel Echounterdrückung aktivieren audioCodecsTitle Audiocodecs showAudioCodecsLabel Zeige Audiocodecs playbackGainLabel Wiedergabe-Verstärkung captureGainLabel Capture Gain audioTestLabel Erfassungsebene audioSettingsInCallWarning Audioanruf wird ausgeführt: Einige Einstellungen sind nicht verfügbar. echoCancellationCalibrationLabel Kalibrierung calibratingEchoCancellationInProgress ...kalibiere… calibratingEchoCancellationDone Kalibriert auf %1ms calibratingEchoCancellationFailed Kalibrierung fehlgeschlagen calibratingEchoCancellationNone Kein Echo entdeckt SettingsCallsChat fileServerLabel Dateiserver encryptWithLimeLabel Mit LIME verschlüsseln limeDisabled Deaktiviert limeRequired Obligatorisch limePreferred Bevorzugt chatTitle Chat callsTitle Anrufe encryptionLabel Verschlüsselung noEncryption Keine autoAnswerLabel Automatische Antwort autoAnswerDelayLabel Verzögerung (in ms) autoAnswerWithVideoLabel Automatische Antwort (mit Video) chatEnabledLabel Chat aktivieren callRecorderEnabledLabel Anrufaufzeichnung aktivieren chatNotificationSoundEnabledLabel Benachrichtigungston aktivieren chatNotificationSoundLabel Benachrichtigungston conferenceEnabledLabel Konferenz aktivieren contactsTitle Kontakte contactsEnabledLabel Kontakte aktivieren muteMicrophoneEnabledLabel Mikrofon-Stummschaltung aktivieren outgoingCallsEnabledLabel Ausgehende Anrufe aktivieren showTelKeypadAutomaticallyLabel Tastenfeld automatisch anzeigen automaticallyRecordCallsLabel Anrufe automatisch aufnehmen keepCallsWindowInBackgroundLabel Anrufe-Fenster im Hintergrund halten callPauseEnabledLabel Anruf pausieren aktiviert encryptionMandatoryLabel Verschlüsselung ist obligatorisch hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms Leere Chaträume verbergen waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered Anrufen wenn registriert chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer Neuer Server SettingsLdapEdit cancel Abbrechen confirm Bestätigen displayNameLabel Anzeigename displayNameTooltip Der Anzeigename des Servers in der Liste. connectionTitle Verbindung serverLabel Server-URL serverTooltip LDAP-Server, z.B. ldap:/// für einen lokalen Server oder ldap://ldap.example.org/ bindDNLabel Bindungs-DN bindDNTooltip Die Bindungs-DN wird zu Authentifizierung am LDAP-Server genutzt.<br>z.B.: cn=ausername,ou=people,dc=bc,dc=com passwordLabel Passwort useTLSLabel TLS verwenden useTLSTooltip LDAP-Verbindungen mit TLS(StartTLS) verschlüsseln. Sie müssen das \'ldap\'-Schema benutzen. \'ldaps\' für LDAP-über-SSL ist nicht standardisiert und sollte nicht mehr benutzt werden.<br>StartTLS ist eine Erweiterung zum LDAP-Protokoll, die das TLS-Protokoll zum Verschlüsseln der Kommunikation nutzt. <br>Es wird eine normale - also unverschlüsselte - Verbindung mit dem LDAP-Server aufgebaut bevor eine Handshake-Verhandlung zwischen Server und dem Client durchgeführt wird. Der Server schickt sein eigenes Zertifikat zur Prüfung, bevor die Verschlüsselung aktiviert wird. useSalLabel SAL verwenden useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' DNS-Auflösung wird von %1 via SAL gemacht. Die IP wird an den LDAP-Server übertragen. Dadurch kann bei der TLS-Verhandlung der Hostname nicht geprüft werden. Sie können die Prüfung deaktivieren um die Verbindung zu erzwingen. verifyTLSLabel TLS-Zertifikat validieren AutoMode Automatisch offMode Aus onMode An verifyTLSTooltip Ob das TLS-Zertifikat geprüft werden muss, wenn zu einem LDAP-Server verbunden wird. searchTitle Suche baseObjectLabel Suchbasis baseObjectPlaceholder Suchbasis baseObjectTooltip Bei Suchanfragen an den LDAP-Server werden nur Einträge berücksichtigt, die unterhalb der Suchbasis liegen. filterLabel Filter filterTooltip Dieser Filter ist die Basis für Suchanfragen.<br>Standardwert: (sn=%s) maxResultsLabel Maximale Ergebniszahl maxResultsTooltip Wie viele Suchergebnisse angezeigt werden. timeoutLabel Zeitlimit timeoutTooltip Verbindungs- und Suchablauffrist. Muss positiv sein.<br>Standardwert: 5s. parsingTitle Parsing nameAttributesLabel Namensattribute nameAttributesTooltip Diese Attribute werden zur Namensgenerierung genutzt. Durch Komma trennen, erstes Attribut hat die höchste Priorität.<br>Standardwert: sn sipAttributesLabel SIP-Attribute sipAttributesTooltip Diese Attribute werden zur SIP-Namensgenerierung der Kontaktadresse genutzt. Durch Komma trennen, erstes Attribut hat die höchste Priorität.<br>Standardwert: mobile,telephoneNumber,homePhone,sn domainLabel Domain domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. Füge die Domain der SIP-Adresse hinzu (sip:username@domain). miscLabel Verschiedenes debugLabel Fehlersuche debugTooltip Bei LDAP-Anfragen ausführliche Logeinträge Logdatei schreiben (hilfreich um TLS-Verbindungen zu untersuchen). SettingsNetwork sendDtmfsLabel MFV Übertragungsmodus allowIpV6Label IPv6 erlauben transportTitle Transport natAndFirewallTitle NAT und Firewall enableIceLabel ICE aktivieren stunServerLabel STUN/TURN-Server enableTurnLabel TURN aktivieren turnUserLabel TURN-Benutzer turnPasswordLabel TURN-Passwort networkProtocolAndPortsTitle Netzwerkprotokoll und -ports sipUdpPortLabel SIP UDP-Port sipTcpPortLabel SIP TCP-Port audioRtpUdpPortLabel Audio RTP UDP Port videoRtpUdpPortLabel Video RTP UDP Port dscpFieldsTitle DSCP-Felder sipFieldLabel SIP audioRtpStreamFieldLabel Audio RTP Stream videoRtpStreamFieldLabel Video RTP Stream bandwidthControlTitle Bandbreitensteuerung downloadSpeedLimitLabel Downloadgeschwindigkeitslimit in Kbit/sek uploadSpeedLimitLabel Uploadgeschwindigkeitslimit in Kbit/sek enableAdaptiveRateControlLabel Aktiviere adaptive Ratenbegrenzung presenceTitle Anwesenheit rlsUriLabel Benutze RLS URI rlsUriAuto AUTOMATISCH rlsUriDisabled NIE showNetworkSettingsLabel Netzwerkeinstellungen anzeigen generalTitle Allgemein SettingsSipAccounts defaultIdentityTitle Standardidentität defaultUsernameLabel Benutzername defaultSipAddressLabel SIP-Adresse proxyAccountsTitle Proxy Zugänge eraseAllPasswords PASSWÖRTER LÖSCHEN addAccount KONTO HINZUFÜGEN editHeader Bearbeiten deleteHeader Löschen deleteAccountDescription Sind Sie sicher, dass sie dieses Konto löschen möchten? eraseAllPasswordsDescription Sind Sie sicher, dass sie alle Passwörter löschen möchten? defaultDisplayNameLabel Anzeigename assistantTitle Assistent createAppSipAccountEnabledLabel Erstellung eines Benutzerkontos aktivieren useAppSipAccountEnabledLabel Nutzung eines Benutzerkontos aktivieren useOtherSipAccountEnabledLabel Nutzung sonstiger Benutzerkonten aktivieren fetchRemoteConfigurationEnabledLabel Abrufen von Konfigurationsdaten aktivieren assistantSupportsPhoneNumbersLabel Telefonnummern werden unterstützt defaultDeviceNameLabel 'Device Name' : Label for setting the device name. webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel SIP-Adresse transportLabel Transport serverAddressLabel SIP-Serveradresse registrationDurationLabel Registrierungsdauer (Sek.) routeLabel Route contactParamsLabel Kontaktparameter publishPresenceLabel Präsenzinformationen publizieren avpfIntervalLabel AVPF regular RTCP interval (Sekunden) registerEnabledLabel Registrieren avpfEnabledLabel AVPF aktivieren cancel ABBRECHEN confirm BESTÄTIGEN invalidSipAddress Ungültige SIP Adresse. invalidServerAddress Ungültige Serveradresse. invalidRoute Ungültige Route. enableIceLabel ICE aktivieren stunServerLabel STUN/TURN Server enableTurnLabel Aktiviere TURN turnUserLabel TURN Benutzer turnPasswordLabel TURN Passwort natAndFirewallTitle NAT und Firewall mainSipAccountSettingsTitle Haupteinstellungen des SIP-Kontos conferenceURI "Conference URI" : Label of a text edit for filling Conference URI Konferenz-URI invalidConferenceURI "invalid conference URI" : Error text about conference URI Ungültige Konferenz-URI videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) Tunnelstatus tunnelDomain 'Domain' : Field title of a textfield to set domain. Domain tunnelUsername 'Username' : Field title of a textfield to set username. Benutzername tunnelSIP 'SIP' : Field title of a switch to set SIP mode. SIP cancel 'Cancel' : Button to cancel the action. Abbrechen setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. HTTP-Proxy einstellen proxyHttpHost 'Host' : Placeholder to set hostname. Hostname proxyHttpPort 'Port' : Placehoilder to set port. Port proxyHttpUsername 'Username' : Placeholder to set username. Benutzername proxyHttpPassword 'Password' : Placeholder to set password. Passwort proxyHttpApply 'Apply' : Button to set proxy from changes. Anwenden serverMode 'Mode' : Field title on form to set tunnel mode. Modus serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. Dual-Modus serverTitle 'Server' : Title form to set a server Server serverHostname 'Hostname' : Field title on form to set hostname. Hostname serverPort 'Port' : Field title on form to set port. Port serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. URL für doppelten Hostnamen serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. Dual-Port serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. Entfernter UDP mirror port serverDelay 'Delay' : Field title on form to set the delay of the tunnel. Verzögerung tunnelAddServer 'Add server' : Button for adding a server Server hinzufügen tunnelApply 'Apply' : Button to apply changes. Anwenden SettingsUi pathsTitle Pfade savedScreenshotsLabel Ordner für gespeicherte Screenshots savedCallsLabel Ordner für gespeicherte Anrufe languagesTitle Sprachen languagesLabel Sprache systemLocale Systemgebietsschema cleanAvatars AVATARE LÖSCHEN cleanAvatarsDescription Sind Sie sicher, dass Sie alle Avatare löschen möchten? downloadLabel Download-Ordner setLocaleDescription Ein Neustart der Anwendung ist notwendig. Möchten Sie die Anwendung jetzt neu starten? otherTitle Anderes exitOnCloseLabel Beende Programm beim Schließen des Fensters dataTitle UI-Daten autoStartLabel Anwendung automatisch starten fontsTitle 'Fonts' : title of fonts section in settings Schriftarten fontsTextChange 'Text Messages' : Label for changing text message fonts Textnachrichten fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts Neue Schriftart wählen checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates Auf Aktualisierungen prüfen mipmapLabel 'Enable Mipmap' Mipmap aktivieren mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. Bei Skalierung oder Transformierung eines Bildes Mipmap-Filterung nutzen. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. Mipmap-Filterung führt bei Verkleinerung im Vergleich zu "Smooth" zu besserer Bildqualität, aber es ist langsamer (sowohl beim Initialisieren als auch bei der Anzeige). minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Videoausgabegerät videoFramerateLabel Bildfrequenz videoCaptureTitle Videoaufzeichnungsparameter videoPresetLabel Videovoreinstellungen presetDefault Standard presetHighFps Hohe FPS presetCustom Individuell videoSizeLabel Videoauflösung videoCodecsTitle Videocodecs showCameraPreview VIDEOVORSCHAU showVideoCodecsLabel Zeige Videocodecs videoSettingsInCallWarning Videoanruf wird ausgeführt: Einige Einstellungen sind nicht verfügbar. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm OK SettingsWindow settingsTitle Einstellungen sipAccountsTab SIP-Konten audioTab Audio videoTab Video callsAndChatTab Anrufe und Chat networkTab Netzwerk uiTab Benutzeroberfläche validButton OK uiAdvanced Erweitert tunnelTab 'Tunnel' : Tab title for tunnel section in settings. Tunnel SipAddressDialog cancel Abbrechen contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact In Kontakten suchen contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip Adresse in den Kontakten suchen oder eigene nutzen. timelineSelectionHeader 'Conversations' : header for a selection in conversation list Gespräche SmartSearchBar addContact KONTAKT HINZUFÜGEN Timeline timelineFilter A title for filtering mode. Filter timelineFilterAll 'All' The mode for timelines filtering. Alle timelineFilterCustom 'Custom' The mode for timelines filtering. Individuell timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). Einfache Räume timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. Sichere Räume timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). Chatgruppen timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. Kurzlebiges timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list In der Liste suchen timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction VERWENDEN useAppSipAccountTitle %1-KONTO VERWENDEN useUsernameToLogin Benutzername und Passwort verwenden, nicht die Telefonnummer. quitWarning Ihr Konto wurde erstellt, aber nicht validiert. Möchten Sie diese Ansicht wirklich verlassen? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password Passwort vergessen? UseAppSipAccountWithPhoneNumber countryLabel Land phoneNumberLabel Telefonnummer displayNameLabel UseAppSipAccountWithUsername usernameLabel Benutzername passwordLabel Passwort displayNameLabel UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form VERWENDEN useOtherSipAccountTitle SIP-KONTO VERWENDEN usernameLabel Benutzername displayNameLabel Anzeigename (optional) sipDomainLabel SIP Domäne passwordLabel Passwort transportLabel Transport addOtherSipAccountError Dieses Konto kann nicht hinzugefügt werden. understandAction 'I understand' : Popup confirmation for a warning warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. ABBRECHEN startButton 'Start' : Button label for starting the conference. START endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Bestätige folgende SAS mit der Gegenstelle. codeA Sagen: codeB Ihr Kontakt sollte sagen: Later 'Later' : Button label to do something in another time. Später Correct 'Correct' : Button label to confirm a code. Richtig title 'Communication security' : Title of popup for ZRTP confirmation. Kommunikationssicherheit country Afghanistan Afghanistan Albania Albanien Algeria Algerien AmericanSamoa Amerikanisch-Samoa Andorra Andorra Angola Angola Anguilla Anguilla AntiguaAndBarbuda Antigua und Barbuda Argentina Argentinien Armenia Armenien Aruba Aruba Australia Australien Austria Österreich Azerbaijan Aserbaidschan Bahamas Bahamas Bahrain Bahrain Bangladesh Bangladesch Barbados Barbados Belarus Weißrussland Belgium Belgien Belize Belize Benin Benin Bermuda Bermuda Bhutan Bhutan Bolivia Bolivien BosniaAndHerzegowina Bosnien und Herzegowina Botswana Botsuana Brazil Brasilien Brunei Brunei Bulgaria Bulgarien BurkinaFaso Burkina Faso Burundi Burundi Cambodia Kambodscha Cameroon Kamerun Canada Kanada CapeVerde Kap Verde CaymanIslands Kaimaninseln CentralAfricanRepublic Zentralafrikanische Republik Chad Tschad Chile Chile China China Colombia Kolumbien Comoros Komoren PeoplesRepublicOfCongo Republik Kongo DemocraticRepublicOfCongo Demokratische Republik Kongo CookIslands Cook-Inseln CostaRica Costa Rica IvoryCoast Elfenbeinküste Croatia Kroatien Cuba Kuba Cyprus Zypern CzechRepublic Tschechische Republik Denmark Dänemark Djibouti Dschibuti Dominica Dominica DominicanRepublic Dominikanische Republik Ecuador Ecuador Egypt Ägypten ElSalvador El Salvador EquatorialGuinea Äquatorialguinea Eritrea Eritrea Estonia Estland Ethiopia Äthiopien FalklandIslands Falklandinseln FaroeIslands Färöer Inseln Fiji Fidschi Finland Finnland France Frankreich FrenchGuiana Französisch-Guayana FrenchPolynesia Französisch-Polynesien Gabon Gabun Gambia Gambia Georgia Georgien Germany Deutschland Ghana Ghana Gibraltar Gibraltar Greece Griechenland Greenland Grönland Grenada Grenada Guadeloupe Guadeloupe Guam Guam Guatemala Guatemala Guinea Guinea GuineaBissau Guinea-Bissau Guyana Guyana Haiti Haiti Honduras Honduras HongKong Hong Kong Hungary Ungarn Iceland Island India Indien Indonesia Indonesien Iran Iran Iraq Irak Ireland Irland Israel Israel Italy Italien Jamaica Jamaika Japan Japan Jordan Jordan Kazakhstan Kasachstan Kenya Kenia Kiribati Kiribati DemocraticRepublicOfKorea Demokratische Republik Korea RepublicOfKorea Republik Korea Kuwait Kuwait Kyrgyzstan Kirgisistan Laos Laos Latvia Lettland Lebanon Libanon Lesotho Lesotho Liberia Liberia Libya Libyen Liechtenstein Liechtenstein Lithuania Litauen Luxembourg Luxemburg Macau Macao Macedonia Mazedonien Madagascar Madagaskar Malawi Malawi Malaysia Malaysia Maldives Malediven Mali Mali Malta Malta MarshallIslands Marshallinseln Martinique Martinique Mauritania Mauretanien Mauritius Mauritius Mayotte Mayotte Mexico Mexiko Micronesia Mikronesien Moldova Moldau Monaco Monaco Mongolia Mongolei Montenegro Montenegro Montserrat Montserrat Morocco Marokko Mozambique Mosambik Myanmar Myanmar Namibia Namibia NauruCountry Nauru Nepal Nepal Netherlands Niederlande NewCaledonia Neukaledonien NewZealand Neuseeland Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Niue NorfolkIsland Norfolkinsel NorthernMarianaIslands Nördliche Marianen Norway Norwegen Oman Oman Pakistan Pakistan Palau Palau PalestinianTerritories Palästinensische Autonomiegebiete Panama Panama PapuaNewGuinea Papua-Neuguinea Paraguay Paraguay Peru Peru Philippines Philippinen Poland Polen Portugal Portugal PuertoRico Puerto Rico Qatar Katar Reunion Wiedervereinigung Romania Rumänien RussianFederation Russische Föderation Rwanda Ruanda SaintHelena St. Helena SaintKittsAndNevis St. Kitts und Nevis SaintLucia St. Lucia SaintPierreAndMiquelon St. Pierre und Miquelon SaintVincentAndTheGrenadines St. Vincent und die Grenadinen Samoa Samoa SanMarino San Marino SaoTomeAndPrincipe São Tomé und Principe SaudiArabia Saudi-Arabien Senegal Senegal Serbia Serbien Seychelles Seychellen SierraLeone Sierra Leone Singapore Singapur Slovakia Slowakei Slovenia Slowenien SolomonIslands Salomonen Somalia Somalia SouthAfrica Südafrika Spain Spanien SriLanka Sri Lanka Sudan Sudan Suriname Suriname Swaziland Swasiland Sweden Schweden Switzerland Schweiz Syria Syrien Taiwan Taiwan Tajikistan Tadschikistan Tanzania Tansania Thailand Thailand Togo Togo Tokelau Tokelau Tonga Tonga TrinidadAndTobago Trinidad und Tobago Tunisia Tunesien Turkey Türkei Turkmenistan Turkmenistan TurksAndCaicosIslands Turks- und Caicosinseln Tuvalu Tuvalu Uganda Uganda Ukraine Ukraine UnitedArabEmirates Vereinigte Arabische Emirate UnitedKingdom Vereinigtes Königreich UnitedStates Vereinigte Staaten von Amerika Uruguay Uruguay Uzbekistan Usbekistan Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietnam WallisAndFutunaIslands Wallis und Futuna Yemen Jemen Zambia Sambia Zimbabwe Simbabwe utils downloadCodecDescription Möchten Sie %1 (%2) herunterladen? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/en.ts000066400000000000000000005230561434616504300236360ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name Help us translate %1 ActivateAppSipAccountWithEmail activateAppSipAccount ACTIVATE YOUR %1 ACCOUNT confirmAction USE ACCOUNT activationSteps To activate your account: follow the instructions that we sent at %1, then click below. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount ACTIVATE YOUR %1 ACCOUNT confirmAction USE ACCOUNT activationSteps An SMS containing a validation code was sent to %1. To complete the phone number verification, please enter the 4-digit code below. App commandLineOptionVerbose log to stdout some debug information while running commandLineOptionConfig specify the %1 configuration file to be used applicationDescription A free (libre) SIP video-phone. commandLineOptionIconified start in the system tray, do not show the main interface commandLineOptionConfigArg file commandLineOptionHelp show this help commandLineOptionVersion show app version commandLineOptionCliHelp displays the help menu to use %1 with the CLI commandLineDescription send an order to the application towards a command line restore Restore quit Quit settings Preferences about About commandLineOptionFetchConfig Specify the %1 configuration file to be fetched. It will be merged with the current configuration. commandLineOptionFetchConfigArg URL, path or file commandLineOptionCall make a call commandLineOptionCallArg SIP address checkForUpdates Check for updates AssistantAbstractView back BACK AssistantHome useAppSipAccount USE A %1 ACCOUNT useOtherSipAccount USE A SIP ACCOUNT fetchRemoteConfiguration FETCH REMOTE CONFIGURATION homeTitle WELCOME homeDescription This assistant will help you configure and use your sip account. createAppSipAccount CREATE A %1 ACCOUNT homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. I accept %1's %2terms of use%3 and %4privacy policy%5 AssistantModel loginWithUsernameFailed Login failed. Please check your username/password. usernameStatusTooShort Too short! (%1 characters min.) usernameStatusTooLong Too long! (%1 characters max.) usernameStatusInvalidCharacters Invalid characters detected. (regex: `%1`) usernameStatusInvalid Invalid username. passwordStatusTooShort Too short! (%1 characters min.) passwordStatusTooLong Too long! (%1 characters max.) passwordStatusInvalidCharacters Invalid characters detected. (regex: `%1`) passwordStatusMissingCharacters Missing characters: `%1`. requestFailed Unable to send the request. emailStatusMalformed Incorrect email address. emailStatusMalformedInvalidCharacters Incorrect email address or invalid characters. cannotSendSms Server error: cannot send SMS. accountAlreadyExists This account already exists. smsActivationFailed SMS activation failed! emailActivationFailed Please verify that you have validated your account or try again. phoneNumberStatusInvalid Invalid phone number! phoneNumberStatusTooShort Too short! phoneNumberStatusTooLong Too long! phoneNumberStatusInvalidCountryCode Invalid country code! loginWithPhoneNumberFailed Login failed. Please check your phone number. unableToAddAccount Unable to add this account. AuthenticationRequest cancel CANCEL confirm LOGIN identityLabel Identity passwordLabel Password authenticationRequestDescription Unable to authenticate. Please verify your password. userIdLabel User ID (optional) realmLabel Realm CallModel callStatsCodec Codec callStatsUploadBandwidth Upload bandwidth callStatsDownloadBandwidth Download bandwidth callStatsEstimatedDownloadBandwidth Estimated download bandwidth callStatsIceState ICE state callStatsIpFamily IP family callStatsSenderLossRate Sender loss rate callStatsReceiverLossRate Receiver loss rate callStatsJitterBuffer Jitter buffer callStatsSentVideoDefinition Sent video definition callStatsReceivedVideoDefinition Received video definition iceStateNotActivated Not activated iceStateFailed Failed iceStateInProgress In progress iceStateReflexiveConnection Reflexive connection iceStateHostConnection Host connection iceStateRelayConnection Relay connection iceStateInvalid Invalid callErrorDeclined Remote party declined the call. callErrorNotFound Remote party was not found. callErrorBusy Remote party is busy. callErrorNotAcceptable Remote party cannot accept the call. callStatsReceivedFramerate Received framerate callStatsSentFramerate Sent framerate callErrorHangUp Remote party hanged up the call. callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics Media encryption callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics Cipher algorithm callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics Key agreement algorithm callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics Hash algorithm callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics Authentication algorithm callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics SAS algorithm CallSipAddress cancel CANCEL callSipAddressDescription Start a new call. CallStatistics audioStatsLabel Audio videoStatsLabel Video mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section Media encryption CallTransfer cancel CANCEL callTransferDescription Do you want to transfer this call? Calls acceptAudioCall ACCEPT AUDIO CALL acceptVideoCall ACCEPT VIDEO CALL terminateCall END CALL resumeCall RESUME CALL transferCall TRANSFER CALL callPause HOLD CALL attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. COMPLETE ATTENDED TRANSFER attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. ATTENDED TRANSFER CALL CallsWindow callsTitle Calls acceptClosingDescription Are you sure you want to terminate all calls? Chat newMessagePlaceholder Enter your message noFileTransferUrl Unable to send file. Server URL not configured. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 is typing… %1 are typing… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. Copied to clipboard selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. Selection copied to clipboard forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. Choose where to forward the message conferencesCopiedICS The invitation has been copied ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. Organizer icsDescription 'Description' : Title for the meeting description. Description icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. Meeting address icsJoinButton 'Join' : Action button to join the meeting. Join deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. Do you really want do delete this meeting? cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. Do you really want do cancel this meeting? icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings Meeting has been cancelled ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. Description icsJoinButton 'Join' : Action button to join the meeting. Join icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. Meeting invite icsParticipants '%1 participant' : number(=%1) of participant. %1 participant %1 participants icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. Meeting has been updated icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. Meeting has been cancelled ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Sent to %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Received by %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Read by %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 did not receive the message %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message Error while sending to %1 %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) Cancel fileTransferDownload 'Download' : Message link to download a file Download ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. Forwarded ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Copy all menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Copy menuPlayMe Play me! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Delivery status menuDelete 'Delete' : Item menu to delete a message Delete menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Hide delivery status menuForward 'Forward' : Forward a message from menu Forward menuReply 'Reply' : Reply to a message from menu Reply ChatNoticeModel nMinute %1 minute %1 minutes nHour %1 hour %1 hours nDay %1 day %1 days nWeek %1 week %1 weeks ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Reply ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Reply to %1 Cli appCliDescription Way to control the %1 application through command lines. uriCommandLineSyntax %1 <sip-address>?method=<method>([&<argument>=<base64-value>]*) cliCommandLineSyntax %1 "<method> ([<argument>=<value>]*)" commandsName commands list : showFunctionDescription Show the main window of the application. callFunctionDescription Initiate a call to the sip-address. byeFunctionDescription End a specific call, all calls or current call. initiateConferenceFunctionDescription Initiate a meeting. joinConferenceFunctionDescription Join the conference hosted by the sip-address as display-name. If you are connected to a proxy config, see join-conference-as. joinConferenceAsFunctionDescription Join the conference hosted by the sip-address as with the guest-sip-address. If you are not connected to a proxy-config, see join-conference. CodecsViewer codecMime Name codecEncoderDescription Description codecEncoderClockRate Rate (Hz) codecBitrate Bitrate (Kbit/s) codecRecvFmtp Parameters codecStatus Status Conference conferenceTitle MEETING ConferenceControls conference MEETING ConferenceManager conferenceManagerDescription Manage participants to your meeting. cancel CANCEL confirm START Conferences conferencesTitle 'Meetings' : Conference list title. Meetings conferencesEndedFilter 'Finished' : Filter meetings on end status. Finished conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. Scheduled conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. Meeting URL has been copied conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. The meeting has been deleted ConfirmDialog cancel CANCEL confirm CONFIRM ContactEdit removeContactDescription Do you really want to remove this contact from your address book? sipAccounts SIP ACCOUNT(S) address ADDRESS emails E-MAIL(S) webSites WEBSITE(S) avatarChooserTitle Choose your avatar companies COMPANIES save SAVE cancel CANCEL sipAccountsPlaceholder SIP account companiesPlaceholder Company emailsPlaceholder Email webSitesPlaceholder Website street Street postalCode Postal code country Country locality Locality abortEditDescriptionText Are you sure you want to cancel the contact modification? tooltipShowConversation Show conversation missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. You need to set the conference URI in your account settings to create a conference based chat room. Contacts searchContactPlaceholder Search contact selectAllContacts All selectConnectedContacts Connected addContact ADD CONTACT removeContactDescription Do you really want to remove this contact from your address book? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. You need to set the conference URI in your account settings to create a conference based chat room. Conversation displayCallsAndMessages ALL displayCalls CALLS displayMessages MESSAGES removeAllEntriesDescription Are you sure you want to clear this history? tooltipContactEdit Edit contact tooltipContactAdd Add contact cleanHistory Delete history conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room Group information conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room Conversation's devices conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode Ephemeral messages adminStatus 'Admin' : Admin(istrator) Admin One word title for describing the current admin status groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room Call all participants in the chat room searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list Search messages conversationMenuDelete 'Delete history' : Item menu to delete the chat's history Delete history conversationMenuViewContact 'View contact' : Item menu to view the contact in address book View contact conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book Add contact conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. Schedule a meeting CreateAppSipAccount createAppSipAccountDescription How would you like to create your account? createAppSipAccountTitle CREATE A %1 ACCOUNT withPhoneNumber WITH A PHONE NUMBER withEmailAddress WITH AN EMAIL ADDRESS CreateAppSipAccountWithEmail createAppSipAccountTitle CREATE A %1 ACCOUNT confirmAction CREATE usernameLabel Username emailLabel Email passwordLabel Password passwordConfirmationLabel Password confirmation passwordConfirmationError The passwords you entered do not match. quitWarning Your account has been created but has not been validated yet. Are you sure you want to quit this view? displayNameLabel Display name (optional) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle CREATE A %1 ACCOUNT countryLabel Country phoneNumberLabel Phone number usernameLabel Username displayNameLabel Display name (optional) confirmAction CREATE quitWarning Your account has been created but has not been validated yet. If you quit this view, you would have to manually add and validate your account within 24 hours. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. Select date dateTimeDialogTime 'Select time' : Menu title to show select time. Select time dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. Select date and time DecorationSticker paused 'paused' : Pause state on sticker, next to username. paused DroppableTextArea fileChooserTitle Please choose one or more files dropYourAttachment Drop your attachment attachmentTooltip Send a file EphemeralChatRoom ephemeralTitle "Ephemeral messages" : Popup title for ephemerals Ephemeral messages ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals New messages will be deleted on both ends once it has been read by your contact. Select a timeout. ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Ephemeral message is only supported in conference based chat room! Warning about not being in conference based chat room. disabled 'Disabled' Disabled nMinute '%1 minute' %1 minute %1 minutes nHour '%1 hour' %1 hour %1 hours nDay '%1 day' %1 day %1 days nWeek '%1 week' %1 week %1 weeks cancelButton 'cancel' : button text for cancelling operation CANCEL startButton 'start' : button text to start ephemeral mode START Event incomingCall Incoming call outgoingCall Outgoing call declinedIncomingCall Declined incoming call declinedOutgoingCall Declined outgoing call endedCall Terminated call missedIncomingCall Missed incoming call missedOutgoingCall Missed outgoing call FetchRemoteConfiguration confirmAction FETCH fetchRemoteConfigurationTitle FETCH REMOTE CONFIGURATION urlLabel URL remoteProvisioningError Unable to set this remote provisioning uri. remoteProvisioningUpdateDescription It is necessary to restart the application. Do you want to restart now? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. Last remote provisioning failed generateLabel 'generate' : title button to generate a code. generate or 'or' : conjunction to choose between options. or remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) Click on %1 to obtain your remote provisioning QR code scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. Scan the QR code with your phone scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. In your app go in assistant - QR code provisioning HistoryView removeAllEntriesDescription Are you sure you want to clear this history? tooltipContactEdit Edit contact tooltipContactAdd Add contact cleanHistory Delete history Home howToDescription Need help on how to use %1? howToTitle HOW TO USE %1 inviteDescription Invite your friends on %1. inviteTitle INVITE YOUR FRIENDS accountAssistantDescription Create or manage your %1 account. accountAssistantTitle ACCOUNT ASSISTANT assistantButton ASSISTANT showTooltips Show tooltips inviteButton INVITE Incall acceptVideoDescription Your contact would like to turn on video. securedStringFormat Call is encrypted with: %1. callNotSecured Call not encrypted. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. You are currently out of the meeting. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. Click on play button to join it back. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Start recording incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. Stop Recording incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Take Snapshot incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. The meeting is not ready. Please Wait… callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. This call is being recorded. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. Waiting for another participant... aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. You are alone in this meeting IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. You are currently out of the meeting. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. Click on play button to join it back. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Start recording incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. Stop Recording incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Take Snapshot incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. The meeting is not ready. Please Wait… callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. This call is being recorded. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. Multimedia parameters incallMenuLayout 'Change layout' : Menu title to change the conference layout. Change layout incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. Invite participants incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. Participants list incallMenuTitle 'Settings' : Main menu title for settings. Settings incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. Mosaic mode incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. Active speaker mode incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. Audio only mode incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. You are currently alone in this meeting InfoChatRoom quitGroupButton 'Exit group' : Button label Exit group ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Add participants addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Search participants in your contact list in order to invite them into the chat room. Explanation for inviting the selected participants into chat room participantList 'Participant list' Participant list adminStatus 'Admin' : Admin(istrator) Admin word for admin status chatRoomDetailsTitle "Group information" : Popup title. Group information popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation CANCEL callButton 'CALL' : Button that lead to a call CALL okButton 'OK' : Button that validate the popup to be redirected to the device list OK infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. End-to-end encrypted encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authenticating participants. Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." To do so, call the contact and follow the authentification process. Explanation process InviteFriends enterEmailLabel Friend's email address messageLabel Message cancel CANCEL confirm CONFIRM inviteFriendsTitle Invite Friends defaultMessage %1 wants to invite you on %2! defaultSubject %1 invitation forcedMessage Download the application on your computer and start calling and chatting with users for free. Click here: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 About %1 Preferences... Preferences Services Services Hide %1 Hide %1 Hide Others Hide Others Show All Show All Quit %1 Quit %1 MainWindow mainSearchBarPlaceholder Search contact, start a call or a chat… contactsEntry CONTACTS autoAnswerStatus auto smartSearchBarTooltip Use the intelligent search bar to directly start audio and video calls, send a message or add a new contact. Just enter your friend's SIP address or username. newConferenceButton Start meeting call newChatRoom 'Start a chat room' : Tooltip to illustrate a button Start a chat room hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline Hide Timeline openTimeline 'Open Timeline' : Tooltip for a button that open the timeline Open Timeline openHome 'Open Home' : Tooltip for a button that open the home view Open Home mainWindowConferencesTitle 'Meetings' : Meeting title for main window. Meetings newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. Conference URI is not set. You have to change it in your account settings in order to create new group chats. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. Video conference URI is not set. You have to change it in your account settings in order to create new conferences. MainWindowMenuBar settings Preferences about About quit Quit checkForUpdates 'Check for updates' : Item menu for checking updates Check for updates MainWindowTopMenuBar settings Preferences about About quit Quit checkForUpdates 'Check for updates' : Item menu for checking updates Check for updates ManageAccounts ok OK selectPresenceLabel Presence status selectAccountLabel Active account MultimediaParametersDialog ok Ok menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. Multimedia parameters NewChatRoom cancelButton 'Cancel' : Cancel button CANCEL startButton 'Launch' : Start button LAUNCH newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room Start a chat room askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. Would you like to encrypt your chat? subjectLabel 'Subject' : Label of a text field about the subject of the chat room Subject subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Current subject of the Chat Room. It cannot be empty. Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Select participants participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' Search in your contacts or add a custom one to the chat room. adminStatus 'Admin' : Admin(istrator) Admin word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Remove this participant from the selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Required missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. You need to fill a subject. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. You need at least %1 participant. You need at least %1 participants. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. You need to set the conference URI in your account settings to create a conference based chat room. subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Give a subject LastContactsTitle 'Last contacts' : Header for showing last contacts Recent contacts NewConference cancelButton 'Cancel' : Cancel button CANCEL missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. You need to fill a subject. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. You need at least %1 participant. You need at least %1 participants. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. You need to set the conference URI in your account settings to create a conference based chat room. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference Start a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference Subject subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Give a subject subjectTooltip 'Current subject of the Meeting. It cannot be empty' Current subject of the Meeting. It cannot be empty. Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. Would you like to encrypt your meeting ? participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Select participants participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' Search in your contacts or add a custom one to the meeting. adminStatus 'Admin' : Admin(istrator) Admin word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Remove this participant from the selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Required launchButton 'Launch' : Launch button Launch updateButton 'Update' : Update button Update updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. Update the meeting newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. Would you like to schedule your meeting? newConferenceDate 'Date' : Date label. Date newConferenceTimeTitle 'Time' : Time label. Time newConferenceDurationTitle 'Duration' : Duration label. Duration newConferenceTimezoneTitle 'Timezone' : Timezone label. Timezone newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference Add a description newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description Description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting This description will describe the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. Send invite via %1 newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. Send invite via Email busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. Operations in progress, please wait confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Do you want to close this form ? Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. You have joined the group conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. You have left the group conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. %1 has joined conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group %1 has left conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody %1 is now an admin conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody %1 is no more an admin conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. Security level degraded by %1 conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time Ephemeral messages have been enabled: %1 conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. Ephemeral messages have been disabled conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. New subject: %1 conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Ephemeral messages have been updated: %1 unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. %1 unread message %1 unread messages Notifier newVersionAvailable A new version (%1) is available! newFileMessage New attachment received! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. New messages received! OnlineInstallerDialog confirm CONFIRM onlineInstallerExtractingDescription Extracting %1… onlineInstallerDownloadingDescription Downloading %1… onlineInstallerFinishedDescription %1 is now installed! onlineInstallerFailedDescription Failed to install %1! OutgoingMessage messageError Error messageRead Read messageDelivered Delivered ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices Conversation's devices ParticipantsListView participantList 'Participant list' Participant list adminStatus 'Admin' : Admin(istrator) Admin word for admin status addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Add participants addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Search participants in your contact list in order to invite them into the chat room. Explanation for inviting the selected participants into chat room participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. Remove this participant from the list ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) (Admin) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Me Presence presenceOnline Available presenceBusy Busy presenceDoNotDisturb Do not disturb presenceOffline Offline QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. Enable LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. Disable LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. Auto SettingsAdvanced logsTitle Logs logsFolderLabel Logs folder sendLogs SEND LOGS logsUploadUrlLabel Logs upload server URL logsUploadFailed Failed to upload logs. logsEnabledLabel Enable logs cleanLogs CLEAN LOGS cleanLogsDescription Are you sure you want to remove all logs? developerSettingsTitle Developer settings developerSettingsEnabledLabel Enable developer settings logsMailerFailed Message when the application try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Mailer cannot be found but logs were uploaded to %1 logsMailerSuccess Logs were uploaded to %1 contactsTitle Address Book Connector noPlugin 'No Plugins to load' : Text in combobox No Plugins to load viewlogs VIEW SettingsAudio audioTitle Audio parameters playbackDeviceLabel Playback device captureDeviceLabel Capture device ringerDeviceLabel Ringer device ringLabel Ring echoCancellationLabel Enable echo cancellation audioCodecsTitle Audio codecs showAudioCodecsLabel Show audio codecs playbackGainLabel Playback gain captureGainLabel Capture gain audioTestLabel Capture level audioSettingsInCallWarning Audio call in progress: some settings are not available. echoCancellationCalibrationLabel Button title for the calibration of echo canceller Calibration calibratingEchoCancellationInProgress Message while calibrating …calibrating… calibratingEchoCancellationDone %1 is a placeholder for the number of ms obtained by the calibration. ms = milliseconds Calibrated to %1ms calibratingEchoCancellationFailed Message when the calibration cannot be done Calibration failed calibratingEchoCancellationNone Message when the calibration did not find any echo No echo detected SettingsCallsChat fileServerLabel File server encryptWithLimeLabel Encrypt with LIME limeDisabled Disabled limeRequired Mandatory limePreferred Preferred chatTitle Chat callsTitle Calls encryptionLabel Encryption noEncryption None autoAnswerLabel Auto answer autoAnswerDelayLabel Delay (in ms) autoAnswerWithVideoLabel Auto answer (with video) chatEnabledLabel Enable chat callRecorderEnabledLabel Enable call recorder chatNotificationSoundEnabledLabel Enable notification sound chatNotificationSoundLabel Notification sound conferenceEnabledLabel Enable conference contactsTitle Contacts contactsEnabledLabel Enable contacts muteMicrophoneEnabledLabel Enable mute microphone outgoingCallsEnabledLabel Enable outgoing calls showTelKeypadAutomaticallyLabel Show DTMF keypad automatically automaticallyRecordCallsLabel Automatically record calls keepCallsWindowInBackgroundLabel Keep call windows in background callPauseEnabledLabel Hold call enabled encryptionMandatoryLabel Encryption is mandatory hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms Hide empty chat rooms waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered Call when registered chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. Enable notifications AutoDownload 'Auto download' : Label for a slider about auto download mode Auto download autoDownloadNever 'Never' : auto download mode description for deactivated feature. Never autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. Always callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. Enable screenshots SettingsLdap newServer Display name of a new server New server SettingsLdapEdit cancel Cancel button label Cancel confirm Confirm button label Confirm displayNameLabel Label Display Name displayNameTooltip Tooltip for display name The display name of the server to be shown in the list. connectionTitle Label Connection serverLabel Label Server URL serverTooltip Tooltip for server URL LDAP Server. eg: ldap:/// for a localhost server or ldap://ldap.example.org/ bindDNLabel Label for a 'Bind DN'. 'Bind' can be a keyword. Check LDAP documentations Bind DN bindDNTooltip 'Bind DN' can be a keyword. Check LDAP documentations The bind DN is the credential that is used to authenticate against an LDAP.<br> eg: cn=ausername,ou=people,dc=bc,dc=com passwordLabel Password useTLSLabel Use TLS useTLSTooltip Encrypt transactions by LDAP over TLS(StartTLS). You must use \'ldap\' scheme. \'ldaps\' for LDAP over SSL is non-standardized and deprecated.<br>StartTLS is an extension to the LDAP protocol which uses the TLS protocol to encrypt communication. <br>It works by establishing a normal - i.e. unsecured - connection with the LDAP server before a handshake negotiation between the server and the web services is carried out. Here, the server sends its certificate to prove its identity before the secure connection is established. useSalLabel Use SAL useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' The DNS resolution is done by %1 using SAL. It will pass an IP to LDAP. By doing that, the TLS negotiation cannot check the hostname. You may deactivate the verifications if wanted to force the connection. verifyTLSLabel Verify Certificates on TLS AutoMode ComboBox Label Auto offMode ComboBox Label Off onMode ComboBox label On verifyTLSTooltip Specify whether the TLS server certificate must be verified when connecting to a LDAP server. searchTitle Search baseObjectLabel 'Base Object'/'Search Base' can be a keyword. Check LDAP documentations Search Base baseObjectPlaceholder 'Base Object'/'Search Base' can be a keyword. Check LDAP documentations Search Base baseObjectTooltip 'Base Object', 'Search Base' and 'Base DN' can be keywords. Check LDAP documentations Base Object/Search Base is a specification for LDAP Search Scopes that specifies that the Search Request should only be performed against the entry specified as the search Base DN.<br>No entries above it will be considered. filterLabel Filter filterTooltip The search is based on this filter to search contacts.<br>Default value: (sn=%s) maxResultsLabel Max Results maxResultsTooltip The max results when requesting searches. parsingTitle Parsing nameAttributesLabel Name Attributes nameAttributesTooltip Check these attributes to build name contact, separated by a comma and the first is the highest priority.<br>The default value is: sn sipAttributesLabel SIP Attributes sipAttributesTooltip Default values : (mobile,telephoneNumber,homePhone,sn) are keywords. Check these attributes to build the SIP username in address of Contact. Attributes are separated by a comma and the first is the highest priority.<br>The default value is: mobile,telephoneNumber,homePhone,sn domainLabel Domain domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. Add the domain to the SIP address (sip:username@domain). miscLabel Miscellaneous label Misc debugLabel Debug debugTooltip Get verbose logs in log file when doing transactions (useful to debug TLS connections). timeoutLabel Timeout timeoutTooltip The connection and search timeout in seconds. It must be positive.<br>Default is 5s. SettingsNetwork sendDtmfsLabel DTMF send method allowIpV6Label Allow IPv6 transportTitle Transport natAndFirewallTitle NAT and Firewall enableIceLabel Enable ICE stunServerLabel STUN/TURN server enableTurnLabel Enable TURN turnUserLabel TURN user turnPasswordLabel TURN password networkProtocolAndPortsTitle Network protocol and Ports sipUdpPortLabel Set SIP/UDP listening port sipTcpPortLabel Set SIP/TCP listening port audioRtpUdpPortLabel Audio RTP UDP port videoRtpUdpPortLabel Video RTP UDP port dscpFieldsTitle DSCP Fields sipFieldLabel SIP audioRtpStreamFieldLabel Audio RTP Stream videoRtpStreamFieldLabel Video RTP Stream bandwidthControlTitle Bandwidth Control downloadSpeedLimitLabel Download speed limit in Kbit/sec uploadSpeedLimitLabel Upload speed limit in Kbit/sec enableAdaptiveRateControlLabel Enable adaptive rate control presenceTitle Presence rlsUriLabel Use RLS URI rlsUriAuto AUTO rlsUriDisabled NEVER showNetworkSettingsLabel Show network settings generalTitle General SettingsSipAccounts defaultIdentityTitle Default identity defaultUsernameLabel Username defaultSipAddressLabel SIP address proxyAccountsTitle Proxy accounts eraseAllPasswords REMOVE ALL PASSWORDS addAccount ADD ACCOUNT editHeader Edit deleteHeader Delete deleteAccountDescription Are you sure you wish to delete this account? eraseAllPasswordsDescription Are you sure you wish to remove all passwords? defaultDisplayNameLabel Display name assistantTitle Assistant createAppSipAccountEnabledLabel Enable account creation useAppSipAccountEnabledLabel Enable account usage useOtherSipAccountEnabledLabel Enable generic account usage fetchRemoteConfigurationEnabledLabel Enable configuration fetch assistantSupportsPhoneNumbersLabel Supports phone numbers defaultDeviceNameLabel 'Device Name' : Label for setting the device name. Device name webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. Registration URL webviewLoginUrlLabel 'Login URL' : Label for login URL. Login URL SettingsSipAccountsEdit sipAddressLabel SIP address transportLabel Transport serverAddressLabel SIP Server address registrationDurationLabel Registration duration (sec) routeLabel Route contactParamsLabel Contact params publishPresenceLabel Publish presence information avpfIntervalLabel AVPF regular RTCP interval (sec) registerEnabledLabel Register avpfEnabledLabel Enable AVPF cancel CANCEL confirm CONFIRM invalidSipAddress Invalid SIP address. invalidServerAddress Invalid server address. invalidRoute Invalid route. enableIceLabel Enable ICE stunServerLabel STUN/TURN server enableTurnLabel Enable TURN turnUserLabel TURN user turnPasswordLabel TURN password natAndFirewallTitle NAT and Firewall mainSipAccountSettingsTitle Main SIP account settings conferenceURI "Conference URI" : Label of a text edit for filling Conference URI Conference URI invalidConferenceURI "invalid conference URI" : Error text about conference URI Invalid conference URI videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. Video Conference URI limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. E2E encryption keys server URL invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. invalid E2E encryption keys server URL SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) Tunnel Status tunnelDomain 'Domain' : Field title of a textfield to set domain. Domain tunnelUsername 'Username' : Field title of a textfield to set username. Username tunnelSIP 'SIP' : Field title of a switch to set SIP mode. SIP cancel 'Cancel' : Button to cancel the action. Cancel setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. Set HTTP proxy proxyHttpHost 'Host' : Placeholder to set hostname. Host proxyHttpPort 'Port' : Placehoilder to set port. Port proxyHttpUsername 'Username' : Placeholder to set username. Username proxyHttpPassword 'Password' : Placeholder to set password. Password proxyHttpApply 'Apply' : Button to set proxy from changes. Apply serverMode 'Mode' : Field title on form to set tunnel mode. Mode serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. Dual mode serverTitle 'Server' : Title form to set a server Server serverHostname 'Hostname' : Field title on form to set hostname. Hostname serverPort 'Port' : Field title on form to set port. Port serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. Dual hostname URL serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. Dual port serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. Remote UDP mirror port serverDelay 'Delay' : Field title on form to set the delay of the tunnel. Delay tunnelAddServer 'Add server' : Button for adding a server Add server tunnelApply 'Apply' : Button to apply changes. Apply SettingsUi pathsTitle Paths savedScreenshotsLabel Saved screenshots folder savedCallsLabel Saved calls folder languagesTitle Languages languagesLabel Language systemLocale System locale cleanAvatars ERASE AVATARS cleanAvatarsDescription Are you sure you want to erase all avatars? downloadLabel Download folder setLocaleDescription It is necessary to restart the application. Do you want to restart now? otherTitle Other exitOnCloseLabel Exit app on close window dataTitle UI Data autoStartLabel Autostart app fontsTitle 'Fonts' : title of fonts section in settings Fonts fontsTextChange 'Text Messages' : Label for changing text message fonts Text Messages fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts Select a new font checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates Check for updates mipmapLabel 'Enable Mipmap' Enable Mipmap mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. This property holds whether the image uses mipmap filtering when scaled or transformed. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering). minimalTimelineFilterLabel 'Minimal Timeline filter' Minimal Timeline filter minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : Show a minimal version of what to display in timeline. versionCheckTypeRelease 'Release' : Keyword for an option to check the release version Release versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version Custom versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version Nightly SettingsVideo videoInputDeviceLabel Video input device videoFramerateLabel Framerate videoCaptureTitle Video capture parameters videoPresetLabel Video preset presetDefault Default presetHighFps High FPS presetCustom Custom videoSizeLabel Video resolution videoCodecsTitle Video codecs showCameraPreview VIDEO PREVIEW showVideoCodecsLabel Show video codecs videoSettingsInCallWarning Video call in progress: some settings are not available. videoDisplayTitle 'Video display' : Title for display parameters Video display videoHybrid 'Hybrid' : Hybrid mode for camera. Hybrid videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. Occupy all space videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. Black bars videoLayout 'Default video layout' : Label to choose the default layout in video conference. Default video layout videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. Active speaker videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. Mosaic videoGridModeLabel 'Mosaic' : Label to choose a camera mode. Mosaic videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. Active speaker videoCallsModeLabel 'Calls' : Label to choose a camera mode. Calls videoModeLabel 'Camera modes' : Label to choose a camera modes. Camera modes SettingsVideoPreview confirm OK SettingsWindow settingsTitle Settings sipAccountsTab SIP accounts audioTab Audio videoTab Video callsAndChatTab Calls and Chat networkTab Network uiTab User Interface validButton OK uiAdvanced Advanced tunnelTab 'Tunnel' : Tab title for tunnel section in settings. Tunnel SipAddressDialog cancel Cancel contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact Search in contacts contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip Search an address in your contacts or use a custom one. timelineSelectionHeader 'Conversations' : header for a selection in conversation list Conversations SmartSearchBar addContact ADD CONTACT Timeline timelineFilter A title for filtering mode. Filter timelineFilterAll 'All' The mode for timelines filtering. All timelineFilterCustom 'Custom' The mode for timelines filtering. Custom timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). Simple rooms timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. Secure rooms timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). Chat groups timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. Ephemerals timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list Search in the list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. All security levels timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. Standard rooms timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. Any conversations timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. Ephemerals on/off timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. Without ephemerals timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. Conferences TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' Are you sure you want to delete and leave this timeline? deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database. UseAppSipAccount confirmAction USE useAppSipAccountTitle USE A %1 ACCOUNT useUsernameToLogin Use username and password rather than your phone number. quitWarning Your account has been created but has not been validated yet. Are you sure you want to quit this view? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password Forgotten password? UseAppSipAccountWithPhoneNumber countryLabel Country phoneNumberLabel Phone number displayNameLabel Display name (optional) UseAppSipAccountWithUsername usernameLabel Username passwordLabel Password displayNameLabel Display name (optional) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form USE useOtherSipAccountTitle USE A SIP ACCOUNT usernameLabel Username displayNameLabel Display name (optional) sipDomainLabel SIP Domain passwordLabel Password transportLabel Transport addOtherSipAccountError Unable to add this account. understandAction 'I understand' : Popup confirmation for a warning I understand warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name Some features require a %1 account, such as group messaging or ephemeral messaging. warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. These features are hidden when you register with a third party SIP account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. To enable it in a commercial project, please contact us. WaitingRoom cancelButton 'Cancel' : Cancel button. Cancel startButton 'Start' : Button label for starting the conference. Start endCallStatus "Call ended" : status of the call in waiting room when the call end. Call ended outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. Outgoing call incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. Incoming call ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. To raise the security level, you can check the following codes with your correspondent. codeA Say: codeB Your contact should say: Later 'Later' : Button label to do something in another time. Later Correct 'Correct' : Button label to confirm a code. Correct title 'Communication security' : Title of popup for ZRTP confirmation. Communication security country Afghanistan Afghanistan Albania Albania Algeria Algeria AmericanSamoa American Samoa Andorra Andorra Angola Angola Anguilla Anguilla AntiguaAndBarbuda Antigua and Barbuda Argentina Argentina Armenia Armenia Aruba Aruba Australia Australia Austria Austria Azerbaijan Azerbaijan Bahamas Bahamas Bahrain Bahrain Bangladesh Bangladesh Barbados Barbados Belarus Belarus Belgium Belgium Belize Belize Benin Benin Bermuda Bermuda Bhutan Bhutan Bolivia Bolivia BosniaAndHerzegowina Bosnia and Herzegovina Botswana Botswana Brazil Brazil Brunei Brunei Bulgaria Bulgaria BurkinaFaso Burkina Faso Burundi Burundi Cambodia Cambodia Cameroon Cameroon Canada Canada CapeVerde Cape Verde CaymanIslands Cayman Islands CentralAfricanRepublic Central African Republic Chad Chad Chile Chile China China Colombia Colombia Comoros Comoros PeoplesRepublicOfCongo Republic of the Congo DemocraticRepublicOfCongo Democratic Republic of Congo CookIslands Cook Islands CostaRica Costa Rica IvoryCoast Ivory Coast Croatia Croatia Cuba Cuba Cyprus Cyprus CzechRepublic Czech Republic Denmark Denmark Djibouti Djibouti Dominica Dominica DominicanRepublic Dominican Republic Ecuador Ecuador Egypt Egypt ElSalvador El Salvador EquatorialGuinea Equatorial Guinea Eritrea Eritrea Estonia Estonia Ethiopia Ethiopia FalklandIslands Falkland Islands FaroeIslands Faroe Islands Fiji Fiji Finland Finland France France FrenchGuiana French Guiana FrenchPolynesia French Polynesia Gabon Gabon Gambia Gambia Georgia Georgia Germany Germany Ghana Ghana Gibraltar Gibraltar Greece Greece Greenland Greenland Grenada Grenada Guadeloupe Guadeloupe Guam Guam Guatemala Guatemala Guinea Guinea GuineaBissau Guinea Bissau Guyana Guyana Haiti Haiti Honduras Honduras HongKong Hong Kong Hungary Hungary Iceland Iceland India India Indonesia Indonesia Iran Iran Iraq Iraq Ireland Ireland Israel Israel Italy Italy Jamaica Jamaica Japan Japan Jordan Jordan Kazakhstan Kazakhstan Kenya Kenya Kiribati Kiribati DemocraticRepublicOfKorea Democratic Republic of Korea RepublicOfKorea Republic of Korea Kuwait Kuwait Kyrgyzstan Kyrgyzstan Laos Laos Latvia Latvia Lebanon Lebanon Lesotho Lesotho Liberia Liberia Libya Libya Liechtenstein Liechtenstein Lithuania Lithuania Luxembourg Luxembourg Macau Macau Macedonia Macedonia Madagascar Madagascar Malawi Malawi Malaysia Malaysia Maldives Maldives Mali Mali Malta Malta MarshallIslands Marshall Islands Martinique Martinique Mauritania Mauritania Mauritius Mauritius Mayotte Mayotte Mexico Mexico Micronesia Micronesia Moldova Moldova Monaco Monaco Mongolia Mongolia Montenegro Montenegro Montserrat Montserrat Morocco Morocco Mozambique Mozambique Myanmar Myanmar Namibia Namibia NauruCountry Nauru Nepal Nepal Netherlands Netherlands NewCaledonia New Caledonia NewZealand New Zealand Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Niue NorfolkIsland Norfolk Island NorthernMarianaIslands Northern Mariana Islands Norway Norway Oman Oman Pakistan Pakistan Palau Palau PalestinianTerritories Palestinian Territories Panama Panama PapuaNewGuinea Papua New Guinea Paraguay Paraguay Peru Peru Philippines Philippines Poland Poland Portugal Portugal PuertoRico Puerto Rico Qatar Qatar Reunion Reunion Romania Romania RussianFederation Russian Federation Rwanda Rwanda SaintHelena Saint Helena SaintKittsAndNevis Saint Kitts And Nevis SaintLucia Saint Lucia SaintPierreAndMiquelon Saint Pierre And Miquelon SaintVincentAndTheGrenadines Saint Vincent And The Grenadines Samoa Samoa SanMarino San Marino SaoTomeAndPrincipe São Tomé And Principe SaudiArabia Saudi Arabia Senegal Senegal Serbia Serbia Seychelles Seychelles SierraLeone Sierra Leone Singapore Singapore Slovakia Slovakia Slovenia Slovenia SolomonIslands Solomon Islands Somalia Somalia SouthAfrica South Africa Spain Spain SriLanka Sri Lanka Sudan Sudan Suriname Suriname Swaziland Swaziland Sweden Sweden Switzerland Switzerland Syria Syria Taiwan Taiwan Tajikistan Tajikistan Tanzania Tanzania Thailand Thailand Togo Togo Tokelau Tokelau Tonga Tonga TrinidadAndTobago Trinidad And Tobago Tunisia Tunisia Turkey Turkey Turkmenistan Turkmenistan TurksAndCaicosIslands Turks And Caicos Islands Tuvalu Tuvalu Uganda Uganda Ukraine Ukraine UnitedArabEmirates United Arab Emirates UnitedKingdom United Kingdom UnitedStates United States Uruguay Uruguay Uzbekistan Uzbekistan Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietnam WallisAndFutunaIslands Wallis And Futuna Islands Yemen Yemen Zambia Zambia Zimbabwe Zimbabwe utils downloadCodecDescription Do you want to download %1 (%2)? formatYears '%1 year' %1 year %1 years formatMonths '%1 month' %1 month %1 months formatWeeks '%1 week' %1 week %1 weeks formatDays '%1 day' %1 day %1 days formatHours '%1 hour' %1 hour %1 hours formatMinutes '%1 minute' %1 minute %1 minutes formatSeconds '%1 second' %1 second %1 seconds linphone-desktop-5.0.2/linphone-app/assets/languages/es.ts000066400000000000000000005167401434616504300236450ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name Ayúdanos a traducir %1 ActivateAppSipAccountWithEmail activateAppSipAccount Activa tu cuenta %1 confirmAction Activar activationSteps Para activar tu cuenta: sigue las instrucciones que enviaremos a %1, entonces de clic debajo. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount Activa tu cuenta %1 confirmAction Activar activationSteps Le hemos enviado un SMS con el código de verificación a %1. Para completar la verificación de su número telefónico entre los 4 dígitos de abajo. App commandLineOptionVerbose Iniciar sesión para excluir cierta información de depuración mientras se ejecuta commandLineOptionConfig Especificar el fichero de configuración de %1 para ser usado applicationDescription Un software libre de videollamadas SIP. commandLineOptionIconified iniciar en la bandeja del sistema, no mostrar en la pantalla principal commandLineOptionConfigArg archivo commandLineOptionHelp mostrar esta ayuda commandLineOptionVersion mostrar versión del programa commandLineOptionCliHelp mostrar el menú de ayuda para usar %1 desde la línea de comandos commandLineDescription enviar una orden a la aplicación hacia la línea de comandos restore Restaurar quit Salir settings Preferencias about Acerca De commandLineOptionFetchConfig Especifique el archivo de configuración que se recuperará %1, se añadirá a la configuración actual commandLineOptionFetchConfigArg URL, ruta o archivo commandLineOptionCall Llamar commandLineOptionCallArg Dirección SIP checkForUpdates Buscar actualizaciones AssistantAbstractView back ATRÁS AssistantHome useAppSipAccount USAR UNA CUENTA DE %1 useOtherSipAccount USAR UNA CUENTA SIP fetchRemoteConfiguration RECOGER CONFIGURACIÓN REMOTA homeTitle BIENVENIDO homeDescription Este asistente le ayudará a configurar y usar su cuenta %1. createAppSipAccount CREAR UNA CUENTA %1 homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. Acepto %1 %2terminos de uso%3 y %4politica de privacidad%5 AssistantModel loginWithUsernameFailed Autenticación fallida. Por favor chequee su usuario y contraseña. usernameStatusTooShort Muy corta (%1 caracteres mínimo) usernameStatusTooLong Muy larga (%1 caracteres máximo) usernameStatusInvalidCharacters Se han detectado caracteres inválidos. (regex: `%1`) usernameStatusInvalid Usuario incorrecto. passwordStatusTooShort Muy corta (%1 caracteres mínimo) passwordStatusTooLong Muy larga (%1 caracteres máximo) passwordStatusInvalidCharacters Se han detectado caracteres inválidos. (regex: `%1`) passwordStatusMissingCharacters Caracteres faltantes: `%1`. requestFailed No se puede enviar la petición. emailStatusMalformed Dirección de correo incorrecta. emailStatusMalformedInvalidCharacters Email incorrecto o carácteres inválidos. cannotSendSms Error del servidor: no se pudo enviar el sms. accountAlreadyExists Esta cuenta ya existe. smsActivationFailed ¡Activación SMS fallida! emailActivationFailed Por favor, verifique que ha validado su cuenta e inténtelo de nuevo. phoneNumberStatusInvalid Número telefónico inválido! phoneNumberStatusTooShort Muy corto! phoneNumberStatusTooLong Muy largo! phoneNumberStatusInvalidCountryCode ¡Código de país inválido! loginWithPhoneNumberFailed Inicio de sesión fallido. Compruebe su número de teléfono. unableToAddAccount Imposible añadir esta cuenta. AuthenticationRequest cancel CANCELAR confirm INICIAR SESIÓN identityLabel Identidad passwordLabel Contraseña authenticationRequestDescription Imposible autentificar. Por favor, verifique su contraseña. userIdLabel ID de usuario (opcional) realmLabel Realm CallModel callStatsCodec Códec callStatsUploadBandwidth Ancho de banda de subida callStatsDownloadBandwidth Ancho de banda de bajada callStatsEstimatedDownloadBandwidth Ancho de banda estimado callStatsIceState Estado ICE callStatsIpFamily Familia de IP callStatsSenderLossRate Tasa de pérdida del emisor callStatsReceiverLossRate Tasa de pérdida del receptor callStatsJitterBuffer Buffer jitter callStatsSentVideoDefinition Definición de video enviada callStatsReceivedVideoDefinition Definición de video recivida iceStateNotActivated No activado iceStateFailed Fallido iceStateInProgress En progreso iceStateReflexiveConnection Conexión reflexiva iceStateHostConnection Conexión de host iceStateRelayConnection Conexión de relé iceStateInvalid Inválido callErrorDeclined La parte remota rechazó la llamada. callErrorNotFound No se encontró la parte remota. callErrorBusy La fiesta remota está ocupada. callErrorNotAcceptable La parte remota no puede aceptar la llamada. callStatsReceivedFramerate Tasa de frames recividos callStatsSentFramerate Tasa de frames enviados callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel CANCELAR callSipAddressDescription Empezar nueva llamada. CallStatistics audioStatsLabel Audio videoStatsLabel Video mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section CallTransfer cancel CANCELAR callTransferDescription ¿Desea transferir esta llamada? Calls acceptAudioCall ACEPTAR LLAMADA DE AUDIO acceptVideoCall ACEPTAR VIDEOLLAMADA terminateCall COLGAR resumeCall RESUMIR LLAMADA transferCall TRANSFERIR LLAMADA callPause PAUSAR LLAMADA attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. Transferencia asistida completa attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. Llamada de transferencia asistida CallsWindow callsTitle Llamadas acceptClosingDescription ¿Está seguro de que desea terminar todas las llamadas? Chat newMessagePlaceholder Introduzca su mensaje noFileTransferUrl Imposible enviar fichero. URL del servidor no configurada. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 está tecleando… %1 están tecleando… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. Copiado al porta papeles selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. Selección copiada al porta papeles forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. Elegir a donde se reenviará el mensaje conferencesCopiedICS La conferencia ICS ha sido copiada ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. Organizador icsDescription 'Description' : Title for the meeting description. Descripción icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. Dirección de la conferencia icsJoinButton 'Join' : Action button to join the meeting. Unirse deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. Desea eliminar esta conferencia? cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. Descripción icsJoinButton 'Join' : Action button to join the meeting. Unirse icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. invitación a la reunión icsParticipants '%1 participant' : number(=%1) of participant. %1 Participante %1 Participantes icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Enviado a %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Recibido por %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Leído por %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 No recibió el mensaje %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message Error mientras se enviaba a %1 %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) Cancelar fileTransferDownload 'Download' : Message link to download a file Descargar ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. Reenviado ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Copiar todo menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Copiar menuPlayMe ¡Reprodúceme! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Estado de la entrega menuDelete 'Delete' : Item menu to delete a message Eliminar menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Ocultar el estado de la entrega menuForward 'Forward' : Forward a message from menu Reenviar menuReply 'Reply' : Reply to a message from menu Responder ChatNoticeModel nMinute %1 minuto %1 minutos nHour %1 hora %1 horas nDay %1 día %1 días nWeek %1 semana %1 semanas ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Responder ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Responder a %1 Cli appCliDescription Manera de controlar la aplicación %1 a través de líneas de comando. uriCommandLineSyntax %1 <sip-address>?method=<method>([&<argument>=<base64-value>] *) cliCommandLineSyntax %1 "<method> ([<argument> = <value>] *)» commandsName lista de comandos: showFunctionDescription Mostrar la pantalla principal de la aplicación. callFunctionDescription Inicializar una llamada a la dirección sip. initiateConferenceFunctionDescription Inicia una conferencia. joinConferenceFunctionDescription Únase a la conferencia organizada por la dirección sip-name como display-name. Si está conectado a una configuración de proxy, consulte join-conference-as. joinConferenceAsFunctionDescription Únase a la conferencia organizada por la dirección de sip-como con la dirección de sip-guest. Si no está conectado a un proxy-config, consulte join-conference. byeFunctionDescription Terminar una llamada específica, todas las llamadas o la llamada actual. CodecsViewer codecMime Nombre codecEncoderDescription Descripcion codecEncoderClockRate Tasa (Hz) codecBitrate Tasa de bits (Kbit/s) codecRecvFmtp Parámetros codecStatus Estatus Conference conferenceTitle CONFERENCIA ConferenceControls conference CONFERENCIA ConferenceManager conferenceManagerDescription Administrar participantes para tu conferencia. cancel CANCELAR confirm INICIAR Conferences conferencesTitle 'Meetings' : Conference list title. Conferencias conferencesEndedFilter 'Finished' : Filter meetings on end status. Finalizado conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. Programado conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. URL de la conferencia ha sido copiada conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel CANCELAR confirm CONFIRMAR ContactEdit removeContactDescription ¿Desea realmente eliminar este contacto de su lista de contactos? sipAccounts CUENTA(S) SIP address DIRECCIÓN emails E-MAILS(S) webSites SITIO(S) WEB avatarChooserTitle Elija su avatar companies COMPAÑÍAS save GUARDAR cancel CANCELAR sipAccountsPlaceholder Cuenta SIP companiesPlaceholder Empresa emailsPlaceholder Email webSitesPlaceholder Sitio web street Calle postalCode Código postal country País locality Localidad abortEditDescriptionText ¿Estás seguro de que quieres cancelar las modificaciones del contacto? tooltipShowConversation Mostrar conversación missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Debe configurar el URL de la conferencia en la configuración de su cuenta para crear una conferencia basada en una sala de chat. Contacts searchContactPlaceholder Buscar contacto selectAllContacts Todo selectConnectedContacts Conectado addContact AÑADIR CONTACTO removeContactDescription ¿Realmente quieres eliminar este contacto de tu lista de contactos? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Debe configurar el URL de la conferencia en la configuración de su cuenta para crear una conferencia basada en una sala de chat. Conversation displayCallsAndMessages TODO displayCalls LLAMADAS displayMessages MENSAJES removeAllEntriesDescription ¿Estás seguro de que quieres limpiar este historial? tooltipContactEdit Editar contacto tooltipContactAdd Añadir contacto cleanHistory Eliminar historial conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room Información del grupo conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room Dispositivos de conversación conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode Mensajes efímeros adminStatus 'Admin' : Admin(istrator) Admin One word title for describing the current admin status groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room Llamar a todos los participantes en la sala de chat searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list Buscando mensajes conversationMenuDelete 'Delete history' : Item menu to delete the chat's history Eliminar conversationMenuViewContact 'View contact' : Item menu to view the contact in address book Ver contacto conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book Añadir contacto conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription ¿Cómo prefieres crear tu cuenta? createAppSipAccountTitle CREAR UNA CUENTA %1 withPhoneNumber CON UN NÚMERO DE TELÉFONO withEmailAddress CON UN CORREO ELECTRÓNICO CreateAppSipAccountWithEmail createAppSipAccountTitle CREAR UNA CUENTA %1 confirmAction CREAR usernameLabel Usuario emailLabel Email passwordLabel Contraseña passwordConfirmationLabel Confirmación de contraseña passwordConfirmationError Las contraseñas que has introducido no coinciden. quitWarning Su cuenta ha sido creada pero no ha sido validada aún. ¿Estás seguro de que deseas cerrar esta pantalla? displayNameLabel Nombre mostrado (opcional) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle CREAR UNA CUENTA %1 countryLabel País phoneNumberLabel Número de teléfono usernameLabel Usuario displayNameLabel Nombre mostrado (opcional) confirmAction CREAR quitWarning Tu cuenta se ha creado pero aún no se ha validado. Si sales de esta vista, tendrás que agregar y validar manualmente tu cuenta en un plazo de 24 horas. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. Seleccionar fecha dateTimeDialogTime 'Select time' : Menu title to show select time. Seleccionar Hora dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. Seleccionar fecha y hora DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Por favor elija uno o más archivos dropYourAttachment Arrastre su adjunto attachmentTooltip Enviar un archivo EphemeralChatRoom ephemeralTitle "Ephemeral messages" : Popup title for ephemerals Mensajes efímeros ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals Los mensajes nuevos se eliminarán en ambos extremos una vez que hayan sido leídos por su contacto. Seleccione un tiempo de espera. ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' ¡Los mensajes efímeros solo se admiten en salas de chat basadas en conferencias! Warning about not being in conference based chat room. disabled 'Disabled' Deshabilitado nMinute '%1 minute' %1 minuto %1 minutos nHour '%1 hour' %1 hora %1 horas nDay '%1 day' %1 día %1 días nWeek '%1 week' %1 semana %1 semanas cancelButton 'cancel' : button text for cancelling operation startButton 'start' : button text to start ephemeral mode Event incomingCall Llamada entrante outgoingCall Llamada saliente declinedIncomingCall Llamada entrante rechazada declinedOutgoingCall Llamada saliente rechazada endedCall Llamada terminada missedIncomingCall Llamada entrante perdida missedOutgoingCall Llamada saliente perdida FetchRemoteConfiguration confirmAction OBTENER fetchRemoteConfigurationTitle OBTENER CONFIGURACIÓN REMOTA urlLabel URL remoteProvisioningError Imposible establecer la uri de aprovisionamiento remota. remoteProvisioningUpdateDescription Es necesario reiniciar la aplicación. ¿Desea reiniciarla ahora? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription ¿Estás seguro de que quieres limpiar este historial? tooltipContactEdit Editar contacto tooltipContactAdd Añadir contacto cleanHistory Eliminar historial Home howToDescription ¿Necesitas ayuda sobre cómo utilizar %1? howToTitle COMO UTILIZAR %1 inviteDescription Invitar a sus amigos a %1. inviteTitle INVITA A TUS AMIGOS accountAssistantDescription Crear o administrar su cuenta %1. accountAssistantTitle ASISTENTE DE CUENTA assistantButton ASISTENTE showTooltips Mostrar tooltips inviteButton INVITAR Incall acceptVideoDescription Su contacto desea que habilite el vídeo. securedStringFormat La llamada está cifrada con: %1. callNotSecured Llamada no cifrada. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. incallMenuTitle 'Settings' : Main menu title for settings. incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) Admin word for admin status chatRoomDetailsTitle "Group information" : Popup title. popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation callButton 'CALL' : Button that lead to a call okButton 'OK' : Button that validate the popup to be redirected to the device list infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Explanation process InviteFriends enterEmailLabel Emails de sus amigos messageLabel Mensaje cancel CANCELAR confirm CONFIRMAR inviteFriendsTitle Invitar Amigos defaultMessage ¡%1 te quiere invitar a %2! defaultSubject Invitación de %1 forcedMessage Descargue la aplicación en su computadora y comience a llamar y chatear con los usuarios de forma gratuita. Haga clic aquí: <a href="%1">%1 </a> MAC_APPLICATION_MENU About %1 Acerca De %1 Preferences... Preferencias Services Servicios Hide %1 Ocultar %1 Hide Others Ocultar Otros Show All Mostrar Todo Quit %1 Salir de %1 MainWindow mainSearchBarPlaceholder Buscar contacto, empezar una llamada o un chat… contactsEntry CONTACTOS autoAnswerStatus automático smartSearchBarTooltip Utilice la barra de búsqueda inteligente para iniciar directamente audio y vídeo , enviar un mensaje o agregar un nuevo contacto. Sólo tienes que entrar la dirección SIP o el nombre de usuario de su amigo. newConferenceButton Iniciar llamada de conferencia newChatRoom 'Start a chat room' : Tooltip to illustrate a button hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline openTimeline 'Open Timeline' : Tooltip for a button that open the timeline openHome 'Open Home' : Tooltip for a button that open the home view mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Preferencias about Sobre nosotros quit Cerrar checkForUpdates 'Check for updates' : Item menu for checking updates Buscar actualizaciones MainWindowTopMenuBar settings Preferencias about Sobre quit Renunciar checkForUpdates 'Check for updates' : Item menu for checking updates Buscar actualizaciones ManageAccounts ok OK selectPresenceLabel Estado de presencia selectAccountLabel Cuenta activa MultimediaParametersDialog ok OK menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button startButton 'Launch' : Start button newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. subjectLabel 'Subject' : Label of a text field about the subject of the chat room subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' adminStatus 'Admin' : Admin(istrator) Admin word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. Debe configurar el URL de la conferencia en la configuración de su cuenta para crear una conferencia basada en una sala de chat. subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject LastContactsTitle 'Last contacts' : Header for showing last contacts NewConference cancelButton 'Cancel' : Cancel button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Debe configurar el URL de la conferencia en la configuración de su cuenta para crear una conferencia basada en una sala de chat. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject subjectTooltip 'Current subject of the Meeting. It cannot be empty' Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' adminStatus 'Admin' : Admin(istrator) Admin word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. Notifier newVersionAvailable ¡Una nueva versión (%1) está disponible! newFileMessage ¡Nuevo adjunto recivido! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm CONFIRMAR onlineInstallerExtractingDescription Extrayendo %1... onlineInstallerDownloadingDescription Descargando %1... onlineInstallerFinishedDescription ¡%1 está ahora instalado! onlineInstallerFailedDescription ¡Error al instalar %1! OutgoingMessage messageError Error messageRead Leído messageDelivered Enviado ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) Admin word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline Disponible presenceBusy Ocupado presenceDoNotDisturb No molestar presenceOffline Desconectado QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. SettingsAdvanced logsTitle Registros logsFolderLabel Carpeta de registros sendLogs ENVIAR REGISTROS logsUploadUrlLabel URL del servidor de carga de registros logsUploadFailed Fallo al subir logs. logsEnabledLabel Activar logs cleanLogs LIMPIAR LOGS cleanLogsDescription ¿Estás seguro de que deseas eliminar todos los registros? developerSettingsTitle Ajustes de desarrollador developerSettingsEnabledLabel Activar ajustes de desarrollador logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) No se puede encontrar el correo, pero los registros se cargaron a %1 logsMailerSuccess Los registros se cargaron a %1 contactsTitle Contactos noPlugin 'No Plugins to load' : Text in combobox viewlogs SettingsAudio audioTitle Parámetros de audio playbackDeviceLabel Dispositivo de reproducción captureDeviceLabel Dispositivo de entrada ringerDeviceLabel Dispositivo de llamada ringLabel Tono de llamada echoCancellationLabel Activar la cancelación de echo audioCodecsTitle Códecs de audio showAudioCodecsLabel Mostrar códecs de audio playbackGainLabel Ganancia de reproducción captureGainLabel Captura de ganancia audioTestLabel Nivel de captura audioSettingsInCallWarning Llamada de audio en curso: algunos ajustes no están disponibles. echoCancellationCalibrationLabel calibratingEchoCancellationInProgress calibratingEchoCancellationDone calibratingEchoCancellationFailed calibratingEchoCancellationNone SettingsCallsChat fileServerLabel Servidor de archivos encryptWithLimeLabel Encriptar con LIME limeDisabled Desactivado limeRequired Obligatorio limePreferred Preferido chatTitle Chat callsTitle Llamadas encryptionLabel Cifrado noEncryption Ninguno autoAnswerLabel Auto responder autoAnswerDelayLabel Retardo (en ms) autoAnswerWithVideoLabel Auto responder (con vídeo) chatEnabledLabel Activar chat callRecorderEnabledLabel Activar grabación de llamadas chatNotificationSoundEnabledLabel Activar sonido de notificación chatNotificationSoundLabel Sonido de notificación conferenceEnabledLabel Activar conferencia contactsTitle Contactos contactsEnabledLabel Activar contactos muteMicrophoneEnabledLabel Activar silencio micrófono outgoingCallsEnabledLabel Activar llamadas salientes showTelKeypadAutomaticallyLabel Mostar teclado de marcado automáticamente automaticallyRecordCallsLabel Grabar llamadas automáticamente keepCallsWindowInBackgroundLabel Mantener pantalla de llamadas en segundo plano callPauseEnabledLabel Llamada en espera activada encryptionMandatoryLabel El cifrado es obligatorio hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer SettingsLdapEdit cancel CANCELAR confirm displayNameLabel Nombre mostrado (opcional) displayNameTooltip connectionTitle serverLabel serverTooltip bindDNLabel bindDNTooltip passwordLabel Contraseña useTLSLabel useTLSTooltip useSalLabel useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' verifyTLSLabel AutoMode offMode onMode verifyTLSTooltip searchTitle baseObjectLabel baseObjectPlaceholder baseObjectTooltip filterLabel filterTooltip maxResultsLabel maxResultsTooltip timeoutLabel timeoutTooltip parsingTitle nameAttributesLabel nameAttributesTooltip sipAttributesLabel sipAttributesTooltip domainLabel domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. miscLabel debugLabel debugTooltip SettingsNetwork sendDtmfsLabel Método de envío DTMFs allowIpV6Label Permitir IPv6 transportTitle Transporte natAndFirewallTitle NAT y cortafuegos enableIceLabel Activar ICE stunServerLabel Servidor STUN/TURN enableTurnLabel Activar TURN turnUserLabel Usuario TURN turnPasswordLabel Contraseña TURN networkProtocolAndPortsTitle Protocolo de red y Puertos sipUdpPortLabel Puerto SIP UDP sipTcpPortLabel Puerto SIP TCP audioRtpUdpPortLabel Puerto audio RTP UDP videoRtpUdpPortLabel Puerto vídeo RTP UDP dscpFieldsTitle Campos DSCP sipFieldLabel SIP audioRtpStreamFieldLabel Stream de audio RTP videoRtpStreamFieldLabel Stream de vídeo RTP bandwidthControlTitle Control de ancho de banda downloadSpeedLimitLabel Velocidad límite de descarga en Kbit/seg uploadSpeedLimitLabel Velocidad límite de subida en Kbit/seg enableAdaptiveRateControlLabel Habilitar tasa de control adaptiva presenceTitle Presencia rlsUriLabel Usar URI RLS rlsUriAuto AUTOMÁTICO rlsUriDisabled NUNCA showNetworkSettingsLabel Mostrar ajustes de red generalTitle General SettingsSipAccounts defaultIdentityTitle Identidad por defecto defaultUsernameLabel Usuario defaultSipAddressLabel Dirección SIP proxyAccountsTitle Cuentas proxy eraseAllPasswords BORRAR CONTRASEÑAS addAccount AÑADIR CUENTA editHeader Editar deleteHeader Eliminar deleteAccountDescription ¿Estás seguro de que deseas eliminar esta cuenta? eraseAllPasswordsDescription ¿Estás seguro de que deseas eliminar todas las contraseñas? defaultDisplayNameLabel Nombre mostrado assistantTitle Asistente createAppSipAccountEnabledLabel Habilitar creación automática useAppSipAccountEnabledLabel Activar uso de cuenta useOtherSipAccountEnabledLabel Activar uso de cuenta automático fetchRemoteConfigurationEnabledLabel Habilitar obtención de configuración assistantSupportsPhoneNumbersLabel Soportar números de teléfono defaultDeviceNameLabel 'Device Name' : Label for setting the device name. webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel Dirección SIP transportLabel Transporte serverAddressLabel Dirección del servidor SIP registrationDurationLabel Duración del registro (seg) routeLabel Ruta contactParamsLabel Parámetros de contacto publishPresenceLabel Publicar información de presencia avpfIntervalLabel Intervalo RTCP regular de AVPF (seg.) registerEnabledLabel Registrar avpfEnabledLabel Habilitar AVPF cancel CANCELAR confirm CONFIRMAR invalidSipAddress Dirección SIP inválida. invalidServerAddress Dirección del servidor inválida. invalidRoute Ruta inválida. enableIceLabel Activar ICE stunServerLabel Servidor STUN/TURN enableTurnLabel Activar TURN turnUserLabel Usuario TURN turnPasswordLabel Contraseña TURN natAndFirewallTitle NAT y cortafuegos mainSipAccountSettingsTitle Ajustes principales de cuenta SIP conferenceURI "Conference URI" : Label of a text edit for filling Conference URI invalidConferenceURI "invalid conference URI" : Error text about conference URI videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) tunnelDomain 'Domain' : Field title of a textfield to set domain. tunnelUsername 'Username' : Field title of a textfield to set username. tunnelSIP 'SIP' : Field title of a switch to set SIP mode. cancel 'Cancel' : Button to cancel the action. CANCELAR setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. proxyHttpHost 'Host' : Placeholder to set hostname. proxyHttpPort 'Port' : Placehoilder to set port. proxyHttpUsername 'Username' : Placeholder to set username. proxyHttpPassword 'Password' : Placeholder to set password. proxyHttpApply 'Apply' : Button to set proxy from changes. serverMode 'Mode' : Field title on form to set tunnel mode. serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. serverTitle 'Server' : Title form to set a server serverHostname 'Hostname' : Field title on form to set hostname. serverPort 'Port' : Field title on form to set port. serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. serverDelay 'Delay' : Field title on form to set the delay of the tunnel. tunnelAddServer 'Add server' : Button for adding a server tunnelApply 'Apply' : Button to apply changes. SettingsUi pathsTitle Rutas savedScreenshotsLabel Carpeta de capturas de pantalla savedCallsLabel Carpeta para llamadas guardadas languagesTitle Idiomas languagesLabel Idioma systemLocale Configuración regional del sistema cleanAvatars ELIMINAR AVATARES cleanAvatarsDescription ¿Estás seguro de que deseas eliminar todos los avatares? downloadLabel Carpeta de descargas setLocaleDescription Es necesario reiniciar la aplicación. ¿Deseas reiniciarla ahora? otherTitle Otro exitOnCloseLabel Salir de la aplicación al cerrar la ventana dataTitle Datos de la interfaz autoStartLabel Auto-iniciar aplicación fontsTitle 'Fonts' : title of fonts section in settings fontsTextChange 'Text Messages' : Label for changing text message fonts fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates mipmapLabel 'Enable Mipmap' mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Dispositivo de entrada de video videoFramerateLabel Tasa de frames videoCaptureTitle Parámetros de captura de video videoPresetLabel Preajuste de vídeo presetDefault Por defecto presetHighFps FPS alto presetCustom Personalizado videoSizeLabel Resolución de vídeo videoCodecsTitle Códecs de vídeo showCameraPreview PREVISUALIZACIÓN DE VIDEO showVideoCodecsLabel Mostrar códecs de vídeo videoSettingsInCallWarning Videollamada en curso: algunos ajustes no están disponibles. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm OK SettingsWindow settingsTitle Ajustes sipAccountsTab Cuentas SIP audioTab Audio videoTab Vídeo callsAndChatTab Llamadas y Chat networkTab Red uiTab Interfaz de Usuario validButton OK uiAdvanced Avanzado tunnelTab 'Tunnel' : Tab title for tunnel section in settings. SipAddressDialog cancel CANCELAR contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list SmartSearchBar addContact AÑADIR CONTACTO Timeline timelineFilter A title for filtering mode. timelineFilterAll 'All' The mode for timelines filtering. timelineFilterCustom 'Custom' The mode for timelines filtering. timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction USO useAppSipAccountTitle USA UNA CUENTA %1 useUsernameToLogin Utilice nombre de usuario y contraseña en lugar de su número de teléfono. quitWarning Tu cuenta se ha creado pero aún no se ha validado. ¿Está seguro de que desea salir de esta vista? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password UseAppSipAccountWithPhoneNumber countryLabel País phoneNumberLabel Número de teléfono displayNameLabel Nombre mostrado (opcional) UseAppSipAccountWithUsername usernameLabel Usuario passwordLabel Contraseña displayNameLabel Nombre mostrado (opcional) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form USE useOtherSipAccountTitle UTILIZE UNA CUENTA SIP usernameLabel Usuario displayNameLabel Nombre mostrado (opcional) sipDomainLabel Dominio SIP passwordLabel Contraseña transportLabel Transporte addOtherSipAccountError Imposible añadir esta cuenta. understandAction 'I understand' : Popup confirmation for a warning warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. startButton 'Start' : Button label for starting the conference. endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Confirma la siguiente SAS con el compañero codeA Dice: codeB Su contacto debería decir: Later 'Later' : Button label to do something in another time. Tarde Correct 'Correct' : Button label to confirm a code. Correcto title 'Communication security' : Title of popup for ZRTP confirmation. Seguridad de la comunicación country Afghanistan Afganistán Albania Albania Algeria Argelia AmericanSamoa Samoa Americana Andorra Andorra Angola Angola Anguilla Anguila AntiguaAndBarbuda Antigua y Barbuda Argentina Argentina Armenia Armenia Aruba Aruba Australia Australia Austria Austria Azerbaijan Azerbaiyán Bahamas Bahamas Bahrain Baréin Bangladesh Bangladés Barbados Barbados Belarus Belarús Belgium Bélgica Belize Bélice Benin Benín Bermuda Bermudas Bhutan Bután Bolivia Bolivia BosniaAndHerzegowina Bosnia y Herzegovina Botswana Botsuana Brazil Brasil Brunei Brunéi Bulgaria Bulgaria BurkinaFaso Burkina Faso Burundi Burundi Cambodia Camboya Cameroon Camerún Canada Canadá CapeVerde Cabo Verde CaymanIslands Islas Caimán CentralAfricanRepublic República Centroafricana Chad Chad Chile Chile China China Colombia Colombia Comoros Comoras PeoplesRepublicOfCongo República del Congo DemocraticRepublicOfCongo República Democrática del Congo CookIslands Islas Cook CostaRica Costa Rica IvoryCoast Costa de Marfil Croatia Croacia Cuba Cuba Cyprus Chipre CzechRepublic República Checa Denmark Dinamarca Djibouti Yibuti Dominica Dominica DominicanRepublic República Dominicana Ecuador Ecuador Egypt Egipto ElSalvador El Salvador EquatorialGuinea Guinea Ecuatorial Eritrea Eritrea Estonia Estonia Ethiopia Etiopía FalklandIslands Islas Malvinas (Falkland) FaroeIslands Islas Feroe Fiji Fiyi Finland Finlandia France Francia FrenchGuiana Guayana Francesa FrenchPolynesia Polinesia Francesa Gabon Gabón Gambia Gambia Georgia Georgia Germany Alemania Ghana Ghana Gibraltar Gibraltar Greece Grecia Greenland Groenlandia Grenada Granada Guadeloupe Guadalupe Guam Guam Guatemala Guatemala Guinea Guinea GuineaBissau Guinea Bissau Guyana Guyana Haiti Haití Honduras Honduras HongKong Hong Kong Hungary Hungría Iceland Islandia India India Indonesia Indonesia Iran Irán Iraq Irak Ireland Irlanda Israel Israel Italy Italia Jamaica Jamaica Japan Japón Jordan Jordania Kazakhstan Kazajstán Kenya Kenia Kiribati Kiribati DemocraticRepublicOfKorea República Democrática de Corea RepublicOfKorea República de Corea Kuwait Kuwait Kyrgyzstan Kirguistán Laos Laos Latvia Letonia Lebanon Líbano Lesotho Lesoto Liberia Liberia Libya Libia Liechtenstein Liechtenstein Lithuania Lituania Luxembourg Luxemburgo Macau Macau Macedonia Macedonia Madagascar Madagascar Malawi Malaui Malaysia Malasia Maldives Maldivas Mali Malí Malta Malta MarshallIslands Islas Marshall Martinique Martinica Mauritania Mauritania Mauritius Mauricio Mayotte Mayotte Mexico México Micronesia Micronesia Moldova Moldavia Monaco Mónaco Mongolia Mongolia Montenegro Montenegro Montserrat Montserrat Morocco Marruecos Mozambique Mozambique Myanmar Myanmar Namibia Namibia NauruCountry Nauru Nepal Nepal Netherlands Holanda NewCaledonia Nueva Caledonia NewZealand Nueva Zelanda Nicaragua Nicaragua Niger Níger Nigeria Nigeria Niue Niue NorfolkIsland Isla Norfolk NorthernMarianaIslands Islas Marianas del Norte Norway Noruega Oman Omán Pakistan Pakistán Palau Palaos PalestinianTerritories Territorios Palestinos Panama Panamá PapuaNewGuinea Papua Nueva Guinea Paraguay Paraguay Peru Perú Philippines Filipinas Poland Polonia Portugal Portugal PuertoRico Puerto Rico Qatar Catar Reunion Reunión Romania Rumania RussianFederation Federación de Rusia Rwanda Ruanda SaintHelena Santa Elena SaintKittsAndNevis San Cristóbal y Nieves SaintLucia Santa Lucía SaintPierreAndMiquelon San Pedro y Miquelón SaintVincentAndTheGrenadines San Vicente y las Granadinas Samoa Samoa SanMarino San Marino SaoTomeAndPrincipe Santo Tomé y Príncipe SaudiArabia Arabia Saudita Senegal Senegal Serbia Serbia Seychelles Seychelles SierraLeone Sierra Leona Singapore Singapur Slovakia Eslovaquia Slovenia Eslovenia SolomonIslands Islas Salomón Somalia Somalia SouthAfrica República de Corea Spain España SriLanka Sri Lanka Sudan Sudán Suriname Surinam Swaziland Suazilandia Sweden Suecia Switzerland Suiza Syria Siria Taiwan Taiwán Tajikistan Tayikistán Tanzania Tanzania Thailand Tailandia Togo Togo Tokelau Tokelau Tonga Tonga TrinidadAndTobago Trinidad y Tobago Tunisia Túnez Turkey Turquía Turkmenistan Turkmenistán TurksAndCaicosIslands Islas Turcas y Caicos Tuvalu Tuvalu Uganda Uganda Ukraine Ucrania UnitedArabEmirates Emiratos Árabes Unidos UnitedKingdom Reino Unido, Reino Unido UnitedStates Estados Unidos de América Uruguay Uruguay Uzbekistan Uzbekistán Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietnam WallisAndFutunaIslands Islas Wallis y Futuna Yemen Yemen Zambia Zambia Zimbabwe Zimbabue utils downloadCodecDescription ¿Deseas descargar %1 (%1)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/fr_FR.ts000066400000000000000000005311251434616504300242260ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name Contribuer aux traductions de %1 ActivateAppSipAccountWithEmail activateAppSipAccount ACTIVER VOTRE COMPTE %1 confirmAction UTILISER LE COMPTE activationSteps Pour activer votre compte : suivez les instructions que vous avez reçues à %1, puis cliquez sur le bouton ci-dessous. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount ACTIVER VOTRE COMPTE %1 confirmAction UTILISER LE COMPTE activationSteps Un code de validation a été envoyé par SMS au %1. Afin de compléter la vérification du numéro de téléphone, veuillez entrer le code à 4 chiffres ci-dessous. App commandLineOptionVerbose afficher dans le flux de sortie 'stdout' les informations de débogage commandLineOptionConfig spécifier le fichier de configuration %1 à utiliser applicationDescription Un logiciel libre de voix sur IP SIP. commandLineOptionIconified lancer l'application dans la zone de notification commandLineOptionConfigArg fichier commandLineOptionHelp affiche cette aide commandLineOptionVersion affiche la version de l'application commandLineOptionCliHelp affiche le menu d'aide pour l'utilisation de %1 en CLI commandLineDescription envoie un ordre à l'application %1, voir --cli-help pour plus de détails restore Restaurer quit Quitter settings Préférences about À propos commandLineOptionFetchConfig Spécifier le fichier de configuration %1 à récupérer. Il sera fusionné avec la configuration actuelle. commandLineOptionFetchConfigArg URL, chemin ou fichier commandLineOptionCall lancer un appel commandLineOptionCallArg adresse SIP checkForUpdates Vérifier les mises à jour AssistantAbstractView back RETOUR AssistantHome useAppSipAccount UTILISER UN COMPTE %1 useOtherSipAccount UTILISER UN COMPTE SIP fetchRemoteConfiguration TÉLÉCHARGER UNE CONFIGURATION homeTitle BIENVENUE homeDescription Cet assistant va vous aider à configurer et utiliser votre compte SIP. createAppSipAccount CRÉER UN COMPTE %1 homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. J'accepte %2les conditions d'utilisation%3 et %4la politique de confidentialité%5 de %1 AssistantModel loginWithUsernameFailed La connexion a échoué. Merci de vérifier le nom d'utilisateur/mot de passe. usernameStatusTooShort Trop court ! (%1 caractères min.) usernameStatusTooLong Trop long ! (%1 caractères max.) usernameStatusInvalidCharacters Caractères invalides détectés (regex : `%1`). usernameStatusInvalid Nom d'utilisateur invalide. passwordStatusTooShort Trop court ! (%1 caractères min.) passwordStatusTooLong Trop long ! (%1 caractères max.) passwordStatusInvalidCharacters Caractères invalides détectés (regex : `%1`). passwordStatusMissingCharacters Caractères manquants : `%1`. requestFailed Impossible d'envoyer la requête. emailStatusMalformed Adresse email incorrecte. emailStatusMalformedInvalidCharacters Adresse email incorrecte ou caractères invalides. cannotSendSms Erreur serveur : impossible d'envoyer un SMS. accountAlreadyExists Ce compte existe déjà. smsActivationFailed L'activation par SMS a échoué ! emailActivationFailed Merci de vérifier que vous avez validé votre compte ou réessayez plus tard. phoneNumberStatusInvalid Numéro de tél. invalide ! phoneNumberStatusTooShort Trop court ! phoneNumberStatusTooLong Trop long ! phoneNumberStatusInvalidCountryCode Indicatif tél. invalide ! loginWithPhoneNumberFailed La connexion a échoué. Merci de vérifier votre numéro. unableToAddAccount Impossible d'ajouter ce compte. AuthenticationRequest cancel ANNULER confirm SE CONNECTER identityLabel Identité passwordLabel Mot de passe authenticationRequestDescription Impossible de vous authentifier. Merci de vérifier votre mot de passe. userIdLabel Nom d'utilisateur (optionnel) realmLabel Domaine CallModel callStatsCodec Codec callStatsUploadBandwidth Bande passante d'envoi callStatsDownloadBandwidth Bande passante de réception callStatsEstimatedDownloadBandwidth Bande passante de réception estimée callStatsIceState État ICE callStatsIpFamily Famille IP callStatsSenderLossRate Taux de perte en envoi callStatsReceiverLossRate Taux de perte en réception callStatsJitterBuffer Tampon de gigue callStatsSentVideoDefinition Définition vidéo envoyée callStatsReceivedVideoDefinition Définition vidéo reçue iceStateNotActivated Désactivé iceStateFailed Échoué iceStateInProgress En cours iceStateReflexiveConnection Connexion réflexive iceStateHostConnection Connexion hôte iceStateRelayConnection Connexion relais iceStateInvalid Invalide callErrorDeclined Le correspondant a décliné l'appel. callErrorNotFound Le correspondant n'a pas été trouvé. callErrorBusy Le correspondant est occupé. callErrorNotAcceptable Le correspondant ne peut accepter votre appel. callStatsReceivedFramerate FPS reçues callStatsSentFramerate FPS envoyées callErrorHangUp Le correspondant a raccroché. callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics Chiffrement du média callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics Algorithme de chiffrement callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics Algorithme d'échange de clef callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics Algorithme de hachage callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics Algorithme d'authentification callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics Algorithme SAS CallSipAddress cancel ANNULER callSipAddressDescription Lancer un nouvel appel. CallStatistics audioStatsLabel Audio videoStatsLabel Vidéo mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section Chiffrement du média CallTransfer cancel ANNULER callTransferDescription Voulez-vous transférer cet appel ? Calls acceptAudioCall ACCEPTER APPEL AUDIO acceptVideoCall ACCEPTER APPEL VIDEO terminateCall RACCROCHER resumeCall REPRENDRE L'APPEL transferCall TRANSFERER L'APPEL callPause SUSPENDRE L'APPEL attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. Valider le transfert attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CONSULTATION AVANT TRANSFERT CallsWindow callsTitle Appels acceptClosingDescription Voulez-vous vraiment quitter tous vos appels en cours ? Chat newMessagePlaceholder Entrer votre message noFileTransferUrl Impossible d'envoyer un fichier. URL du serveur non configurée. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 est en train d'écrire… %1 sont en train d'écrire… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. Copié dans le presse-papier selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. La sélection a été copiée dans le presse-papier forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. Choisir où faire suivre le message conferencesCopiedICS L'invitation a été copiée ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. Organisateur icsDescription 'Description' : Title for the meeting description. Description icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. Adresse de la réunion icsJoinButton 'Join' : Action button to join the meeting. Rejoindre deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. Voulez-vous supprimer cette réunion ? cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. Voulez-vous supprimer cette réunion ? icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings La réunion a été annulée ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. Description icsJoinButton 'Join' : Action button to join the meeting. Rejoindre icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. Invitation à la réunion icsParticipants '%1 participant' : number(=%1) of participant. %1 participant %1 participants icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. La réunion a été modifiée icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. La réunion a été annulée ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Envoyé à %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Reçu par %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Lu par %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 n'a encore rien reçu %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message Erreur lors de l'envoi à %1 %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) Annuler fileTransferDownload 'Download' : Message link to download a file Télécharger ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. Transféré ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Tout copier menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Copier menuPlayMe Joue-moi ! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Statut du message menuDelete 'Delete' : Item menu to delete a message Supprimer menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Cacher le statut du message menuForward 'Forward' : Forward a message from menu Transférer menuReply 'Reply' : Reply to a message from menu Répondre ChatNoticeModel nMinute %1 minute %1 minutes nHour %1 heure %1 heures nDay %1 jour %1 jours nWeek %1 semaine %1 semaines ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Réponse ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Répondre à %1 Cli appCliDescription Moyen d'effectuer des actions sur l'application %1 en ligne de commande. uriCommandLineSyntax %1 <sip-address>?method=<method>([&<argument>=<valeur-en-base64>]*) cliCommandLineSyntax %1 "<method> ([<argument>=<valeur>]*)" commandsName liste des commandes : showFunctionDescription Afficher la fenêtre principale de l'application. callFunctionDescription Lancer un appel vers l'adresse SIP. initiateConferenceFunctionDescription Lancer une réunion. joinConferenceFunctionDescription Rejoindre la conférence hébergée par l'adresse SIP avec votre nom d'affichage. Si vous êtes connecté à une proxy config, voir 'join-conference-as'. joinConferenceAsFunctionDescription Rejoindre la conférence hébergée par l'adresse SIP avec l'adresse SIP invitée. Si vous n'êtes pas connecté à une proxy-config, voir join-conference. byeFunctionDescription Terminer un appel spécifique, tous les appels ou l'appel en cours. CodecsViewer codecMime Nom codecEncoderDescription Description codecEncoderClockRate Fréquence (Hz) codecBitrate Débit (Kbit/s) codecRecvFmtp Paramètres codecStatus Statut Conference conferenceTitle RÉUNION ConferenceControls conference RÉUNION ConferenceManager conferenceManagerDescription Gérer les participants de votre réunion. cancel ANNULER confirm LANCER Conferences conferencesTitle 'Meetings' : Conference list title. Réunions conferencesEndedFilter 'Finished' : Filter meetings on end status. Terminées conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. Programmées conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. Le lien de la réunion à été copié conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. La réunion a été supprimée ConfirmDialog cancel ANNULER confirm CONFIRMER ContactEdit removeContactDescription Voulez-vous vraiment supprimer ce contact de votre carnet d'adresses ? sipAccounts COMPTE(S) SIP address ADRESSE emails EMAIL(S) webSites SITE(S) INTERNET avatarChooserTitle Choisissez votre avatar companies ENTREPRISE(S) save SAUVEGARDER cancel ANNULER sipAccountsPlaceholder Compte SIP companiesPlaceholder Entreprise emailsPlaceholder Email webSitesPlaceholder Site internet street Rue postalCode Code postal country Pays locality Localité abortEditDescriptionText Êtes-vous sûr de vouloir annuler la modification du contact ? tooltipShowConversation Aller à la conversation missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Vous devez définir l'URI de la réunion dans les paramètres de votre compte pour créer une conférence. Contacts searchContactPlaceholder Rechercher un contact selectAllContacts Tous selectConnectedContacts Connectés addContact AJOUTER UN CONTACT removeContactDescription Voulez-vous vraiment supprimer ce contact de votre carnet d'adresses ? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Vous devez définir l'URI de la conférence dans les paramètres de votre compte pour créer une conférence. Conversation displayCallsAndMessages TOUT displayCalls APPELS displayMessages MESSAGES removeAllEntriesDescription Êtes-vous sûr de vouloir supprimer cet historique ? tooltipContactEdit Modifier le contact tooltipContactAdd Ajouter le contact cleanHistory Supprimer l'historique adminStatus 'Admin' : Admin(istrator) Admin One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room Informations du groupe conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room Terminaux de la conversation conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode Messages éphémères groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room Appeler tous les participants de la conversation searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list Rechercher dans les messages conversationMenuDelete 'Delete history' : Item menu to delete the chat's history Supprimer l'historique conversationMenuViewContact 'View contact' : Item menu to view the contact in address book Voir le contact conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book Ajouter un contact conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. Planifier une réunion CreateAppSipAccount createAppSipAccountDescription Comment souhaitez-vous créer votre compte ? createAppSipAccountTitle CRÉER UN COMPTE %1 withPhoneNumber AVEC UN NUMÉRO DE TÉLÉPHONE withEmailAddress AVEC UNE ADRESSE EMAIL CreateAppSipAccountWithEmail createAppSipAccountTitle CRÉER UN COMPTE %1 confirmAction CRÉER usernameLabel Nom d'utilisateur emailLabel Email passwordLabel Mot de passe passwordConfirmationLabel Confirmation du mot de passe passwordConfirmationError Les mots de passe ne correspondent pas. quitWarning Votre compte a été créé mais il n'a pas été validé. Êtes-vous sûr de vouloir quitter cette vue ? displayNameLabel Nom d'affichage (optionnel) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle CRÉER UN COMPTE %1 countryLabel Pays phoneNumberLabel Numéro de téléphone usernameLabel Nom d'utilisateur displayNameLabel Nom d'affichage (optionnel) confirmAction CRÉER quitWarning Votre compte a été créé mais il n'a pas été validé. Si vous quittez cette vue, vous devrez ajouter et valider manuellement votre compte dans les 24 heures. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. Choisir une date dateTimeDialogTime 'Select time' : Menu title to show select time. Choisir l'heure dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. Choisir la date et l'heure DecorationSticker paused 'paused' : Pause state on sticker, next to username. en pause DroppableTextArea fileChooserTitle Merci de choisir un ou plusieurs fichiers dropYourAttachment Déposez votre pièce jointe attachmentTooltip Envoyer un fichier EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation ANNULER startButton 'start' : button text to start ephemeral mode LANCER ephemeralTitle "Ephemeral messages" : Popup title for ephemerals Messages éphémères ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals Les nouveaux messages seront automatiquement effacés des deux côtés lorsqu'ils auront été lus par tous les contacts. Sélectionnez une durée. ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Les messages éphémères ne sont disponibles que pour une conversation définie en mode conférence ! Warning about not being in conference based chat room. disabled 'Disabled' Désactivé nMinute '%1 minute' %1 minute %1 minutes nHour '%1 hour' %1 heure %1 heures nDay '%1 day' %1 jour %1 jours nWeek '%1 week' %1 semaine %1 semaines Event incomingCall Appel entrant outgoingCall Appel sortant declinedIncomingCall Appel entrant refusé declinedOutgoingCall Appel sortant refusé endedCall Fin d'appel missedIncomingCall Appel entrant manqué missedOutgoingCall Appel sortant sans réponse FetchRemoteConfiguration confirmAction TÉLÉCHARGER fetchRemoteConfigurationTitle TÉLÉCHARGER UNE CONFIGURATION urlLabel URL remoteProvisioningError Impossible d'utiliser cette URL de configuration. remoteProvisioningUpdateDescription Voulez-vous redémarrer maintenant pour prendre en compte ces modifications ? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. La dernière configuration n'a pas pu être récupérée generateLabel 'generate' : title button to generate a code. générer or 'or' : conjunction to choose between options. ou remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) Cliquez sur %1 afin d'obtenir un QR code de configuration à distance scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. Scannez le QR code à partir d'un mobile scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. Allez dans l'assistant de l'application - QR code HistoryView removeAllEntriesDescription Êtes-vous sûr de vouloir supprimer cet historique ? tooltipContactEdit Modifier le contact tooltipContactAdd Ajouter le contact cleanHistory Supprimer l'historique Home howToDescription Besoin d'aide pour utiliser %1 ? howToTitle COMMENT UTILISER %1 inviteDescription Inviter vos amis à utiliser %1. inviteTitle INVITER VOS AMIS accountAssistantDescription Créer ou gérer votre compte %1. accountAssistantTitle ASSISTANT DE COMPTE assistantButton ASSISTANT showTooltips Voir les bulles d'aides inviteButton INVITER Incall acceptVideoDescription Votre correspondant souhaite ajouter la vidéo. securedStringFormat L'appel est chiffré avec : %1. callNotSecured Appel non chiffré. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. Vous êtes actuellement en dehors de l'appel de groupe. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. Cliquez sur le bouton "play" pour la rejoindre. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Commencer l'enregistrement incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. Arrêter l'enregistrement incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Prendre une capture d'écran incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. La réunion est en cours de préparation. Veuillez patienter… callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. Cet appel est en train d'être enregistré. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. En attente de participant… aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. Vous êtes actuellement seul dans cette réunion IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. Vous êtes actuellement en dehors de la réunion. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. Cliquez sur le bouton "play" pour la rejoindre. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Commencer l'enregistrement incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. Arrêter l'enregistrement incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Prendre une capture d'écran incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. La réunion est en cours de préparation. Veuillez patienter… callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. Cet appel est en train d'être enregistré. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. Paramètres multimédia incallMenuLayout 'Change layout' : Menu title to change the conference layout. Modifier la disposition incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. Inviter des participants incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. Liste des participants incallMenuTitle 'Settings' : Main menu title for settings. Paramètres incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. Mode mosaïque incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. Mode intervenant actif incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. Mode audio uniquement incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. Vous êtes actuellement seul dans cette réunion InfoChatRoom quitGroupButton 'Exit group' : Button label Quitter le groupe ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Ajouter des participants addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Cherchez des participants parmi vos contacts afin de les inviter dans la conversation de groupe. Explanation for inviting the selected participants into chat room participantList 'Participant list' Liste des participants adminStatus 'Admin' : Admin(istrator) Admin word for admin status chatRoomDetailsTitle "Group information" : Popup title. Informations du groupe popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation ANNULER callButton 'CALL' : Button that lead to a call APPELER okButton 'OK' : Button that validate the popup to be redirected to the device list OK infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. Chiffrement de bout-en-bout encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Les messages instantanés sont chiffrés de bout-en-bout dans les conversations sécurisées. Il est possible d'augmenter le niveau de sécurité en authentifiant les participants. Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Pour se faire, il faut appeler chaque appareil enregistré et suivre le processus d'authentification. Explanation process InviteFriends enterEmailLabel Adresse email de votre ami messageLabel Message cancel ANNULER confirm CONFIRMER inviteFriendsTitle Inviter des amis defaultMessage %1 souhaite vous inviter sur %2 ! defaultSubject Invitation %1 forcedMessage Téléchargez l’application sur votre ordinateur et appelez vos amis ou envoyez-leur un message gratuitement. Cliquez ici : <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 À propos de %1 Preferences... Préférences Services Services Hide %1 Masquer %1 Hide Others Masquer les autres Show All Tout afficher Quit %1 Quitter %1 MainWindow mainSearchBarPlaceholder Chercher un contact, appeler ou envoyer un message… contactsEntry CONTACTS autoAnswerStatus auto smartSearchBarTooltip Utilisez la barre de recherche intelligente pour lancer des appels audio et vidéo, envoyer un message ou ajouter un contact. Entrez simplement l'adresse SIP ou le nom d'utilisateur de votre contact. newConferenceButton Démarrer une réunion newChatRoom 'Start a chat room' : Tooltip to illustrate a button Commencer une conversation hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline Masquer la chronologie openTimeline 'Open Timeline' : Tooltip for a button that open the timeline Afficher la chronologie openHome 'Open Home' : Tooltip for a button that open the home view Ouvrir la page d'accueil mainWindowConferencesTitle 'Meetings' : Meeting title for main window. Réunions newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. L'URI de conférence n'a pas été renseignée. Vous devez la mettre à jour dans les options de comptes pour pouvoir créer de nouvelles conversations de groupe. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. L'URI de conférence vidéo n'a pas été renseigné. Vous devez la mettre à jour dans les options de comptes pour pouvoir créer de nouvelles réunions. MainWindowMenuBar settings Préférences about À propos quit Quitter checkForUpdates 'Check for updates' : Item menu for checking updates Vérifier les mises à jour MainWindowTopMenuBar settings Préférences about A propos quit Quitter checkForUpdates 'Check for updates' : Item menu for checking updates Vérifier les mises à jour ManageAccounts ok OK selectPresenceLabel Statut de présence selectAccountLabel Compte actif MultimediaParametersDialog ok Ok menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. Paramètres multimédia NewChatRoom cancelButton 'Cancel' : Cancel button ANNULER startButton 'Launch' : Start button LANCER missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Vous devez définir un sujet. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. Vous devez ajouter au moins %1 participant. Vous devez ajouter au moins %1 participants. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. Vous devez définir l'URI de la conférence dans les paramètres de votre compte pour créer une réunion. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room Commencer une conversation askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. Voulez-vous chiffrer votre conversation ? subjectLabel 'Subject' : Label of a text field about the subject of the chat room Sujet subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Sujet de la conversation. Il ne peut pas être vide. Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Choisissez les participants participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' Chercher dans vos contacts ou ajouter à la main dans la conversation. adminStatus 'Admin' : Admin(istrator) Admin word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Enlever ce participant de la sélection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Obligatoire subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Définissez un sujet LastContactsTitle 'Last contacts' : Header for showing last contacts Contacts récents NewConference cancelButton 'Cancel' : Cancel button ANNULER missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Vous devez définir un sujet. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. Vous devez ajouter au moins %1 participant. Vous devez ajouter au moins %1 participants. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Vous devez définir l'URI de la conférence dans les paramètres de votre compte pour créer une meeting. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference Démarrer une conférence subjectLabel 'Subject' : Label of a text field about the subject of the conference Sujet subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Définissez un sujet subjectTooltip 'Current subject of the Meeting. It cannot be empty' Sujet de la conversation. Il ne peut pas être vide. Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. Voulez-vous chiffrer votre conversation ? participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Choisissez les participants participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' Chercher dans vos contacts ou ajouter à la main dans la conversation. adminStatus 'Admin' : Admin(istrator) Admin word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Enlever ce participant de la sélection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Obligatoire launchButton 'Launch' : Launch button Démarrer updateButton 'Update' : Update button Mettre à jour updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. Mettre à jour la réunion newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. Voulez-vous programmer cette réunion ? newConferenceDate 'Date' : Date label. Date newConferenceTimeTitle 'Time' : Time label. Heure newConferenceDurationTitle 'Duration' : Duration label. Durée newConferenceTimezoneTitle 'Timezone' : Timezone label. Fuseau horaire newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference Ajouter une description newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description Description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting Cette description détaille la réunion newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. Envoyer les invitations avec %1 newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. Envoyer les invitations par courriel busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. Opérations en cours, merci de patienter confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Voulez-vous fermer ce formulaire ? Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. Vous avez rejoint le groupe conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. Vous avez quitté le groupe conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. %1 a rejoint le groupe conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group %1 a quitté le groupe conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody %1 est admin conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody %1 n'est plus admin conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. Niveau de sécurité dégradé par %1 conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time Les messages éphémères ont été activés : %1 conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. Les messages éphémères ont été désactivés conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. Nouveau sujet : %1 conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Délai d’expiration des messages : %1 unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. %1 message non lu %1 messages non lus Notifier newVersionAvailable Une nouvelle version (%1) est disponible ! newFileMessage Pièce jointe reçue ! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. Nouveaux messages arrivés ! OnlineInstallerDialog confirm CONFIRMER onlineInstallerExtractingDescription Extraction de %1… onlineInstallerDownloadingDescription Téléchargement de %1… onlineInstallerFinishedDescription Installation de %1 terminée ! onlineInstallerFailedDescription L'installation de %1 a échoué ! OutgoingMessage messageError Erreur messageRead Lu messageDelivered Délivré ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices Liste des appareils ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Ajouter des participants addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Cherchez des participants parmi vos contacts afin de les inviter dans la conversation de groupe. Explanation for inviting the selected participants into chat room participantList 'Participant list' Liste des participants adminStatus 'Admin' : Admin(istrator) Admin word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. Enlever ce participant de la sélection ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) (Admin) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Moi Presence presenceOnline En ligne presenceBusy Occupé presenceDoNotDisturb Ne pas déranger presenceOffline Hors-ligne QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. Activer LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. Désactiver LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. Auto SettingsAdvanced logsTitle Traces logsFolderLabel Dossier des traces sendLogs ENVOYER LES TRACES logsUploadUrlLabel URL du serveur de traces logsUploadFailed L'envoi des traces a échoué. logsEnabledLabel Activer les traces de débogage cleanLogs NETTOYER LES TRACES cleanLogsDescription Voulez-vous vraiment supprimer toutes les traces ? developerSettingsTitle Mode développeur developerSettingsEnabledLabel Activer le mode développeur logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Impossible de trouver un logiciel de mail. Les traces ont été téléchargées sur %1 logsMailerSuccess Les traces ont été téléchargées sur %1 contactsTitle Fournisseurs de contacts noPlugin 'No Plugins to load' : Text in combobox Pas de plugin à charger viewlogs AFFICHER SettingsAudio audioTitle Paramètres audio playbackDeviceLabel Périphérique d'écoute captureDeviceLabel Périphérique de capture ringerDeviceLabel Périphérique de sonnerie ringLabel Sonnerie echoCancellationLabel Activer l'annulation d'écho audioCodecsTitle Codecs audio showAudioCodecsLabel Afficher les codecs audio playbackGainLabel Volume d'écoute captureGainLabel Gain du microphone audioTestLabel Niveau microphone audioSettingsInCallWarning Appel en cours : certains paramètres sont inaccessibles. echoCancellationCalibrationLabel Calibration calibratingEchoCancellationInProgress …en cours de calibration… calibratingEchoCancellationDone Calibré en %1ms calibratingEchoCancellationFailed Erreur de calibration calibratingEchoCancellationNone Pas d'écho détecté SettingsCallsChat fileServerLabel Serveur de partage encryptWithLimeLabel Chiffrer avec LIME limeDisabled Désactivé limeRequired Obligatoire limePreferred Préféré chatTitle Conversation callsTitle Appels encryptionLabel Chiffrement noEncryption Aucun autoAnswerLabel Répondre automatiquement autoAnswerDelayLabel Délai (en ms) autoAnswerWithVideoLabel Répondre autom. (avec vidéo) chatEnabledLabel Activer le chat callRecorderEnabledLabel Activer l'enregistrement d'appel chatNotificationSoundEnabledLabel Activer le son des notifications chatNotificationSoundLabel Son des notifications conferenceEnabledLabel Activer la conférence contactsTitle Contacts contactsEnabledLabel Activer les contacts muteMicrophoneEnabledLabel Autoriser la désactivation du microphone outgoingCallsEnabledLabel Activer les appels sortants showTelKeypadAutomaticallyLabel Afficher le clavier tél. automatiquement automaticallyRecordCallsLabel Enregistrement auto. des appels keepCallsWindowInBackgroundLabel Garder la fenêtre d'appels en tâche de fond callPauseEnabledLabel Autoriser la mise en pause encryptionMandatoryLabel Chiffrement obligatoire hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms Cacher les conversations vides waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered Appeler seulement si enregistré chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. Activer les notifications AutoDownload 'Auto download' : Label for a slider about auto download mode Téléchargement automatique autoDownloadNever 'Never' : auto download mode description for deactivated feature. Jamais autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. Toujours callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. Activer les captures d'écran SettingsLdap newServer Nouveau serveur SettingsLdapEdit cancel Annuler confirm Confirmer displayNameLabel Nom d'affichage displayNameTooltip Le nom d'affichage des serveurs qui sera visible dans la liste. connectionTitle Connexion serverLabel URL serveur serverTooltip Serveur LDAP. ie : ldap:// pour un serveur local ou ldap://ldap.example.org/ bindDNLabel Bind DN bindDNTooltip Le bind DN est la référence qui est utilisée pour s'authentifier auprès du protocole LDAP. passwordLabel Mot de passe useTLSLabel Utiliser TLS useTLSTooltip Encode les transactions en utilisant LDAP sur TLS (StartTLS). Vous devez utiliser le protocole \'ldap\'. Utiliser \'ldaps\' pour LDAP sur SSL n'est pas standard et déprécié.<br>StartTLS est une extension du protocole LDAP qui utilise le protocole TLS pour encoder la communication. <br>Elle fonctionne en effectuant une connexion normale (ie. non-sécurisée) avec un serveur LDAP avant d'entamer un protocole d'établissement de liaison entre le serveur et les services web. Ici, le serveur envoie son certificat pour prouver son identité avant d'établir une connexion sécurisée. useSalLabel Utiliser SAL useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' La résolution DNS est effectuée par %1 en utilisant SAL. Il fournira une IP à LDAP. Ce faisant, la négociation TLS ne pourra pas vérifier le nom d'hôte. Vous devrez donc désactiver les vérifications si vous voulez forcer la connexion. verifyTLSLabel Vérifier les certificats TLS AutoMode Auto offMode Désactivé onMode Activé verifyTLSTooltip Indiquer si le certificat du serveur TLS doit être vérifié lors de la connexion à un serveur LDAP. searchTitle Recherche baseObjectLabel Base de recherche baseObjectPlaceholder Base de recherche baseObjectTooltip Base Object/Search Base est une spécification pour les recherches LDAP. Elle indique que la recherche ne doit être effectuée qu'en se basant sur cette entrée.<br>Aucune entrée au-dessus ne sera prise en compte. filterLabel Filtre filterTooltip La recherche se base sur ce filtre pour chercher des contacts. <br>Valeur par défaut : (sn=%s) maxResultsLabel Résultats maximum maxResultsTooltip Le nombre maximum de résultats lors d'une recherche. timeoutLabel Durée sélectionnée timeoutTooltip Le délai de connexion et de recherche en secondes. Il doit être positif.<br>La valeur par défaut est 5s. parsingTitle Analyse nameAttributesLabel Attributs de nom nameAttributesTooltip Vérifiez ces attributs pour construire le nom de contact, séparés par une virgule. Le premier a la plus haute priorité.<br>La valeur par défaut est : sn sipAttributesLabel Attributs SIP sipAttributesTooltip Vérifiez ces attributs pour construire le nom d'utilisateur SIP dans l'adresse du contact. Les attributs sont séparés par une virgule et le premier a la plus haute priorité.<br>La valeur par défaut est : mobile, numéro de téléphone, téléphone fixe,sn domainLabel Domaine domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. Ajouter le domaine à l'adresse SIP (sip:nomdutilisateur@domaine). miscLabel Divers debugLabel Débogage debugTooltip Obtenir des traces dans le fichier traces lors des opérations (utile pour déboguer les connexions TLS). SettingsNetwork sendDtmfsLabel Méthode d'envoi des DTMFs allowIpV6Label Autoriser IPv6 transportTitle Transport natAndFirewallTitle NAT et Pare-feu enableIceLabel Activer ICE stunServerLabel Serveur STUN/TURN enableTurnLabel Activer TURN turnUserLabel Utilisateur TURN turnPasswordLabel Mot de passe TURN networkProtocolAndPortsTitle Protocole réseau et ports sipUdpPortLabel Port d'écoute SIP/UDP sipTcpPortLabel Port d'écoute SIP/TCP audioRtpUdpPortLabel Port Audio RTP UDP videoRtpUdpPortLabel Port Vidéo RTP UDP dscpFieldsTitle Champs DSCP sipFieldLabel SIP audioRtpStreamFieldLabel Flux RTP audio videoRtpStreamFieldLabel Flux RTP vidéo bandwidthControlTitle Gestion de la bande passante downloadSpeedLimitLabel Limite de débit descendant en Kbit/sec uploadSpeedLimitLabel Limite de débit montant en Kbit/sec enableAdaptiveRateControlLabel Activer le contrôle de débit adaptif presenceTitle Présence rlsUriLabel Utiliser l'URI RLS rlsUriAuto AUTO rlsUriDisabled JAMAIS showNetworkSettingsLabel Afficher les paramètres réseaux generalTitle Général SettingsSipAccounts defaultIdentityTitle Identité par défaut defaultUsernameLabel Nom d'utilisateur defaultSipAddressLabel Adresse SIP proxyAccountsTitle Comptes SIP eraseAllPasswords EFFACER LES MOTS DE PASSE addAccount AJOUTER UN COMPTE editHeader Modifier deleteHeader Supprimer deleteAccountDescription Êtes-vous sûr de vouloir supprimer ce compte ? eraseAllPasswordsDescription Êtes-vous sûr de vouloir supprimer tous vos mots de passe ? defaultDisplayNameLabel Nom d'affichage assistantTitle Assistant createAppSipAccountEnabledLabel Activer la création de compte useAppSipAccountEnabledLabel Activer l'utilisation de compte useOtherSipAccountEnabledLabel Activer l'utilisation générique de compte fetchRemoteConfigurationEnabledLabel Activer le téléchargement de conf. assistantSupportsPhoneNumbersLabel Supporter les numéros de tél. defaultDeviceNameLabel 'Device Name' : Label for setting the device name. Nom de l'appareil webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. URL d'enregistrement webviewLoginUrlLabel 'Login URL' : Label for login URL. URL de connexion SettingsSipAccountsEdit sipAddressLabel Adresse SIP transportLabel Transport serverAddressLabel Adresse du serveur SIP registrationDurationLabel Durée d'enregistrement (sec) routeLabel Route contactParamsLabel Paramètres de contact publishPresenceLabel Publier la présence avpfIntervalLabel Intervalle standard RTCP AVPF (sec) registerEnabledLabel S'enregistrer avpfEnabledLabel Activer AVPF cancel ANNULER confirm CONFIRMER invalidSipAddress Adresse SIP invalide. invalidServerAddress Adresse du serveur invalide. invalidRoute Route invalide. enableIceLabel Activer ICE stunServerLabel Serveur STUN/TURN enableTurnLabel Activer TURN turnUserLabel Utilisateur TURN turnPasswordLabel Mot de passe TURN natAndFirewallTitle NAT et Pare-feu mainSipAccountSettingsTitle Paramètres principaux du compte SIP conferenceURI "Conference URI" : Label of a text edit for filling Conference URI URI de conférence invalidConferenceURI "invalid conference URI" : Error text about conference URI URI de conférence invalide videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. URI de conférence vidéo limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. URL du serveur de clés pour le chiffrement de bout en bout invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. URL du serveur de clés pour le chiffrement de bout en bout invalide SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) État du tunnel tunnelDomain 'Domain' : Field title of a textfield to set domain. Domaine tunnelUsername 'Username' : Field title of a textfield to set username. Nom d'utilisateur tunnelSIP 'SIP' : Field title of a switch to set SIP mode. SIP cancel 'Cancel' : Button to cancel the action. Annuler setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. Définir un proxy HTTP proxyHttpHost 'Host' : Placeholder to set hostname. Hôte proxyHttpPort 'Port' : Placehoilder to set port. Port proxyHttpUsername 'Username' : Placeholder to set username. Nom d'utilisateur proxyHttpPassword 'Password' : Placeholder to set password. Mot de passe proxyHttpApply 'Apply' : Button to set proxy from changes. Appliquer serverMode 'Mode' : Field title on form to set tunnel mode. Mode serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. Mode double serverTitle 'Server' : Title form to set a server Serveur serverHostname 'Hostname' : Field title on form to set hostname. Nom d'Hôte serverPort 'Port' : Field title on form to set port. Port serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. Nom d'hôte du second server serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. Port du second serveur serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. Port miroir UDP distant serverDelay 'Delay' : Field title on form to set the delay of the tunnel. Décalage tunnelAddServer 'Add server' : Button for adding a server Ajouter un serveur tunnelApply 'Apply' : Button to apply changes. Appliquer SettingsUi pathsTitle Chemins savedScreenshotsLabel Dossier des captures d'écran savedCallsLabel Dossier des appels enregistrés languagesTitle Langues languagesLabel Langue systemLocale Locale du système cleanAvatars SUPPRIMER LES AVATARS cleanAvatarsDescription Voulez-vous vraiment supprimer tous les avatars ? downloadLabel Dossier des téléchargements setLocaleDescription Voulez-vous redémarrer maintenant pour prendre en compte ces modifications ? otherTitle Divers exitOnCloseLabel Quitter l'application à la fermeture de la fenêtre dataTitle Données autoStartLabel Démarrer auto. l'app fontsTitle 'Fonts' : title of fonts section in settings Polices fontsTextChange 'Text Messages' : Label for changing text message fonts Messages textes fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts Sélectionnez une nouvelle police checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates Vérifier les mises à jour mipmapLabel 'Enable Mipmap' Activer le Mipmap mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. Cette propriété détermine si les images doivent utiliser un filtre Mipmap lorsqu'elles sont remises à l’échelle ou transformée. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. Lorsque les images sont rétrécies, un filtre Mipmap a une meilleure qualité visuelle par rapport à un lissage standard mais nécessite plus de calculs lors de l'initialisation des images et pendant le rendu. minimalTimelineFilterLabel 'Minimal Timeline filter' Filtre minimaliste de la chronologie minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : Afficher une version minimaliste dans la zone de recherche des chronologies. versionCheckTypeRelease 'Release' : Keyword for an option to check the release version Stable versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version Personnalisé versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version En développement SettingsVideo videoInputDeviceLabel Périphérique de capture vidéo videoFramerateLabel Images/s videoCaptureTitle Paramètres de capture vidéo videoPresetLabel Profil vidéo presetDefault Défaut presetHighFps Fluide presetCustom Personnalisé videoSizeLabel Résolution vidéo videoCodecsTitle Codecs vidéo showCameraPreview APERÇU DE LA VIDÉO showVideoCodecsLabel Afficher les codecs vidéo videoSettingsInCallWarning Appel vidéo en cours : certains paramètres ne sont pas disponibles. videoDisplayTitle 'Video display' : Title for display parameters Affichage vidéo videoHybrid 'Hybrid' : Hybrid mode for camera. Hybride videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. Occuper tout l'espace videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. Barres noires videoLayout 'Default video layout' : Label to choose the default layout in video conference. Disposition vidéo par défaut videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. Intervenant actif videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. Mosaïque videoGridModeLabel 'Mosaic' : Label to choose a camera mode. Mosaïque videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. Intervenant actif videoCallsModeLabel 'Calls' : Label to choose a camera mode. Appels videoModeLabel 'Camera modes' : Label to choose a camera modes. Modes de caméra SettingsVideoPreview confirm OK SettingsWindow settingsTitle Paramètres sipAccountsTab Comptes SIP audioTab Audio videoTab Vidéo callsAndChatTab Appels et messages networkTab Réseau uiTab Interface Utilisateur validButton OK uiAdvanced Avancés tunnelTab 'Tunnel' : Tab title for tunnel section in settings. Tunnel SipAddressDialog cancel Annuler contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact Rechercher un contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip Rechercher une adresse dans les contacts ou utiliser une adresse personnalisée. timelineSelectionHeader 'Conversations' : header for a selection in conversation list Conversations SmartSearchBar addContact AJOUTER CE CONTACT Timeline timelineFilter A title for filtering mode. Filtre timelineFilterAll 'All' The mode for timelines filtering. Tous timelineFilterCustom 'Custom' The mode for timelines filtering. Personnalisé timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). Standards timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. Sécurisées timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). Groupes standards timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. Éphémères timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list Rechercher dans la chronologie timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. Tous les niveaux de sécurité timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. Standards timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. Tous les types timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. Éphémères on/off timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. Sans éphémères timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. Conférences TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' Êtes-vous certain de vouloir tout effacer et de quitter cette conversation ? deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' Après la confirmation, cela effacera tout l'historique, quittera la conversation si c'est une conversation de groupe et l'effacera de la base de donnée. UseAppSipAccount confirmAction UTILISER useAppSipAccountTitle UTILISER UN COMPTE %1 useUsernameToLogin Utiliser un nom d'utilisateur et un mot de passe plutôt que votre numéro de téléphone. quitWarning Votre compte a été créé mais n'a pas été validé. Êtes-vous sûr de vouloir quitter cette vue ? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password Mot de passe oublié ? UseAppSipAccountWithPhoneNumber countryLabel Pays phoneNumberLabel Numéro de téléphone displayNameLabel Nom d'affichage (optionnel) UseAppSipAccountWithUsername usernameLabel Nom d'utilisateur passwordLabel Mot de passe displayNameLabel Nom d'affichage (optionnel) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form UTILISER useOtherSipAccountTitle UTILISER UN COMPTE SIP usernameLabel Nom d'utilisateur displayNameLabel Nom d'affichage (optionnel) sipDomainLabel Domaine SIP passwordLabel Mot de passe transportLabel Transport addOtherSipAccountError Impossible d'ajouter ce compte. understandAction 'I understand' : Popup confirmation for a warning J'ai compris warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name Certaines fonctionnalités avancées comme les messages de groupe ou les messages éphémères nécessitent un compte %1. warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. Elles seront masquées dans l'application si vous configurez un compte SIP tiers. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. Si vous souhaitez les activer pour un projet professionnel, contactez-nous. WaitingRoom cancelButton 'Cancel' : Cancel button. Annuler startButton 'Start' : Button label for starting the conference. Lancer endCallStatus "Call ended" : status of the call in waiting room when the call end. Fin d'appel outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. Appel sortant incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. Appel entrant ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Pour accroitre le niveau de sécurité, vous pouvez confirmer les codes suivants avec votre interlocuteur. codeA Dites : codeB Votre interlocuteur devrait dire : Later 'Later' : Button label to do something in another time. Plus tard Correct 'Correct' : Button label to confirm a code. Correct title 'Communication security' : Title of popup for ZRTP confirmation. Sécurité des communications country Afghanistan Afghanistan Albania Albanie Algeria Algérie AmericanSamoa Samoa américaines Andorra Andorre Angola Angola Anguilla Anguilla AntiguaAndBarbuda Antigua-et-Barbuda Argentina Argentine Armenia Arménie Aruba Aruba Australia Australie Austria Autriche Azerbaijan Azerbaïdjan Bahamas Bahamas Bahrain Bahreïn Bangladesh Bangladesh Barbados Barbade Belarus Bélarus Belgium Belgique Belize Belize Benin Bénin Bermuda Bermudes Bhutan Bhoutan Bolivia Bolivie BosniaAndHerzegowina Bosnie-Herzégovine Botswana Botswana Brazil Brésil Brunei Brunei Bulgaria Bulgarie BurkinaFaso Burkina Faso Burundi Burundi Cambodia Cambodge Cameroon Cameroun Canada Canada CapeVerde Cap-Vert CaymanIslands Îles Caïmans CentralAfricanRepublic République centrafricaine Chad Tchad Chile Chili China Chine Colombia Colombie Comoros Comores PeoplesRepublicOfCongo République du Congo DemocraticRepublicOfCongo République démocratique du Congo CookIslands Îles Cook CostaRica Costa Rica IvoryCoast Côte d'Ivoire Croatia Croatie Cuba Cuba Cyprus Chypre CzechRepublic République tchèque Denmark Danemark Djibouti Djibouti Dominica Dominique DominicanRepublic République Dominicaine Ecuador Équateur Egypt Égypte ElSalvador El Salvador EquatorialGuinea Guinée équatoriale Eritrea Érythrée Estonia Estonie Ethiopia Éthiopie FalklandIslands Îles Falkland FaroeIslands Îles Féroé Fiji Fidji Finland Finlande France France FrenchGuiana Guyane française FrenchPolynesia Polynésie française Gabon Gabon Gambia Gambie Georgia Géorgie Germany Allemagne Ghana Ghana Gibraltar Gibraltar Greece Grèce Greenland Groenland Grenada Grenade Guadeloupe Guadeloupe Guam Guam Guatemala Guatemala Guinea Guinée GuineaBissau Guinée Bissau Guyana Guyana Haiti Haïti Honduras Honduras HongKong Hong Kong Hungary Hongrie Iceland Islande India Inde Indonesia Indonésie Iran Iran Iraq Irak Ireland Irlande Israel Israël Italy Italie Jamaica Jamaïque Japan Japon Jordan Jordanie Kazakhstan Kazakhstan Kenya Kenya Kiribati Kiribati DemocraticRepublicOfKorea République démocratique de Corée RepublicOfKorea République de Corée Kuwait Koweït Kyrgyzstan Kirghizistan Laos Laos Latvia Lettonie Lebanon Liban Lesotho Lesotho Liberia Liberia Libya Libye Liechtenstein Liechtenstein Lithuania Lituanie Luxembourg Luxembourg Macau Macao Macedonia Macédoine Madagascar Madagascar Malawi Malawi Malaysia Malaisie Maldives Maldives Mali Mali Malta Malte MarshallIslands Îles Marshall Martinique Martinique Mauritania Mauritanie Mauritius Maurice Mayotte Mayotte Mexico Mexique Micronesia Micronésie Moldova Moldavie Monaco Monaco Mongolia Mongolie Montenegro Monténégro Montserrat Montserrat Morocco Maroc Mozambique Mozambique Myanmar Myanmar Namibia Namibie NauruCountry Nauru Nepal Népal Netherlands Pays-Bas NewCaledonia Nouvelle-Calédonie NewZealand Nouvelle-Zélande Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Nioué NorfolkIsland Île Norfolk NorthernMarianaIslands Îles Mariannes du Nord Norway Norvège Oman Oman Pakistan Pakistan Palau Palau PalestinianTerritories Territoires palestiniens Panama Panama PapuaNewGuinea Papouasie-Nouvelle-Guinée Paraguay Paraguay Peru Pérou Philippines Philippines Poland Pologne Portugal Portugal PuertoRico Porto Rico Qatar Qatar Reunion Réunion Romania Roumanie RussianFederation Fédération de Russie Rwanda Rwanda SaintHelena Sainte-Hélène SaintKittsAndNevis Saint-Kitts-et-Nevis SaintLucia Sainte-Lucie SaintPierreAndMiquelon Saint-Pierre-et-Miquelon SaintVincentAndTheGrenadines Saint-Vincent-et-les Grenadines Samoa Samoa SanMarino Saint-Marin SaoTomeAndPrincipe São Tomé et Principe SaudiArabia Arabie Saoudite Senegal Sénégal Serbia Serbie Seychelles Seychelles SierraLeone Sierra Leone Singapore Singapour Slovakia Slovaquie Slovenia Slovénie SolomonIslands Îles Salomon Somalia Somalie SouthAfrica Afrique du Sud Spain Espagne SriLanka Sri Lanka Sudan Soudan Suriname Suriname Swaziland Swaziland Sweden Suède Switzerland Suisse Syria Syrie Taiwan Taïwan Tajikistan Tadjikistan Tanzania Tanzanie Thailand Thaïlande Togo Togo Tokelau Tokélaou Tonga Tonga TrinidadAndTobago Trinité-et-Tobago Tunisia Tunisie Turkey Turquie Turkmenistan Turkménistan TurksAndCaicosIslands Îles Turques et Caïques Tuvalu Tuvalu Uganda Ouganda Ukraine Ukraine UnitedArabEmirates Émirats arabes unis UnitedKingdom Royaume-Uni UnitedStates Etats Unis Uruguay Uruguay Uzbekistan Ouzbékistan Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietnam WallisAndFutunaIslands Îles Wallis et Futuna Yemen Yémen Zambia Zambie Zimbabwe Zimbabwe utils downloadCodecDescription Voulez-vous télécharger %1 (%2) ? formatYears '%1 year' %1 année %1 années formatMonths '%1 month' %1 mois %1 mois formatWeeks '%1 week' %1 semaine %1 semaines formatDays '%1 day' %1 jour %1 jours formatHours '%1 hour' %1 heure %1 heures formatMinutes '%1 minute' %1 minute %1 minutes formatSeconds '%1 second' %1 seconde %1 secondes linphone-desktop-5.0.2/linphone-app/assets/languages/hu.ts000066400000000000000000005253051434616504300236470ustar00rootroot00000000000000 About ok Rendben aboutTranslation 'Help us translate %1' : %1 is the application name ActivateAppSipAccountWithEmail activateAppSipAccount %1 fiók aktiválása confirmAction Aktiválása activationSteps Fiókja aktiválásához: kövesse az utasításokat, amelyeket a(z) %1 küldtünk, majd kattintson az alábbiakra. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount %1 fiók aktiválása confirmAction Aktiválása activationSteps Az ellenőrzési kódot tartalmazó SMS-t elküldtük a(z) %1-nek. A telefonszám ellenőrzésének befejezéséhez kérjük, írja be az alábbi négyjegyű kódot. App commandLineOptionVerbose küldjön naplót a stdout-hez néhány hibakeresési információ futtatás közben commandLineOptionConfig adja meg a kívánt %1 konfigurációs fájl használata applicationDescription A szabad (ingyenes) SIP videótelefon. commandLineOptionIconified indítsa el a tálcán, ne jelenítse meg a fő felületet commandLineOptionConfigArg fájl commandLineOptionHelp Súgó megjelenítése commandLineOptionVersion az alkalmazás verziójának megjelenítése commandLineOptionCliHelp megjeleníti a súgó menüt a(z) %1 CLI-vel történő használatához commandLineDescription küldjön megrendelést az alkalmazás számára a parancssornak restore Visszaállítás quit Kilépés settings Beállítások about A Linphone névjegye commandLineOptionFetchConfig Adja meg a beolvasandó %1 beállítási fájlt. Összevonásra kerül a jelenlegi konfigurációval. commandLineOptionFetchConfigArg URL-cím, elérési út vagy fájl commandLineOptionCall hívás kezdeményezése commandLineOptionCallArg SIP-cím checkForUpdates Frissítések keresése AssistantAbstractView back Vissza AssistantHome useAppSipAccount %1 fiók használata useOtherSipAccount SIP fiók használata fetchRemoteConfiguration Távoli konfiguráció lekérése homeTitle Isten hozott homeDescription Ez a segéd segít a SIP fiók konfigurálásában és használatában. createAppSipAccount Készítsen egy %1 fiók homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. AssistantModel loginWithUsernameFailed Bejelentkezés sikertelen. Kérjük, ellenőrizze felhasználónevét/jelszavát. usernameStatusTooShort Túl rövid! (legalább %1 karakter) usernameStatusTooLong Túl hosszú! (legfeljebb %1 karakter) usernameStatusInvalidCharacters Érvénytelen karaktereket észleltünk. (regex: `%1`) usernameStatusInvalid Érvénytelen felhasználónév. passwordStatusTooShort Túl rövid! (legalább %1 karakter) passwordStatusTooLong Túl hosszú! (legfeljebb %1 karakter) passwordStatusInvalidCharacters Érvénytelen karaktereket észleltünk. (regex: `%1`) passwordStatusMissingCharacters Hiányzó karakterek: „%1”. requestFailed Nem sikerült elküldeni a kérelmet. emailStatusMalformed Helytelen e-mail cím. emailStatusMalformedInvalidCharacters Helytelen e-mail cím vagy érvénytelen karakterek. cannotSendSms Kiszolgálóhiba: nem lehet SMS-t küldeni. accountAlreadyExists Ez a fiók már létezik. smsActivationFailed Az SMS aktiválása sikertelen! emailActivationFailed Kérjük, ellenőrizze, hogy érvényesítette-e fiókját, vagy próbálja újra. phoneNumberStatusInvalid Érvénytelen telefonszám! phoneNumberStatusTooShort Túl rövid! phoneNumberStatusTooLong Túl hosszú! phoneNumberStatusInvalidCountryCode Érvénytelen országkód! loginWithPhoneNumberFailed Bejelentkezés sikertelen. Kérjük, ellenőrizze telefonszámát. unableToAddAccount Nem sikerült hozzáadni ezt a fiókot. AuthenticationRequest cancel Mégse confirm Bejelentkezés identityLabel Felhasználó passwordLabel Jelszó authenticationRequestDescription Nem sikerült a hitelesítés. Kérjük, ellenőrizze a jelszavát. userIdLabel Felhasználói azonosítás (választható) realmLabel Tartomány CallModel callStatsCodec Kodek callStatsUploadBandwidth Feltöltési sávszélesség callStatsDownloadBandwidth Letöltési sávszélesség callStatsEstimatedDownloadBandwidth Becsült letöltési sávszélesség callStatsIceState ICE állapot callStatsIpFamily IP család callStatsSenderLossRate Feladó adatvesztési aránya callStatsReceiverLossRate Címzett adatvesztési arány callStatsJitterBuffer Csúszás puffer callStatsSentVideoDefinition Küldött videó meghatározás callStatsReceivedVideoDefinition Fogadott videó meghatározás iceStateNotActivated Nincs aktiválva iceStateFailed Sikertelen iceStateInProgress Folyamatban iceStateReflexiveConnection Reflexív kapcsolat iceStateHostConnection Gép kapcsolat iceStateRelayConnection Közvetítő kapcsolat iceStateInvalid Érvénytelen callErrorDeclined A távoli fél elutasította a hívást. callErrorNotFound Távoli fél nem található. callErrorBusy Távoli fél elfoglalt. callErrorNotAcceptable A távoli fél nem fogadhatja el a hívást. callStatsReceivedFramerate Fogadott képkockasebesség callStatsSentFramerate Küldött képkockasebesség callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics Média titkosítás callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics Kivonatoló algoritmus callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel Mégse callSipAddressDescription Új hívás indítása. CallStatistics audioStatsLabel Hang videoStatsLabel Videó mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section Média titkosítás CallTransfer cancel Mégse callTransferDescription Szeretné átirányítani a hívást? Calls acceptAudioCall Hanghívás elfogadása acceptVideoCall Videohívás elfogadása terminateCall Hívás befejezése resumeCall Hívás folytatása transferCall Hívásátirányítása callPause Hívástartás attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. Hívásátirányítás teljes részvétele attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. Hívásátirányítás részvétele CallsWindow callsTitle Hívások acceptClosingDescription Biztos benne, hogy be kívánja fejezni az összes hívást? Chat newMessagePlaceholder Írja be az üzenetét noFileTransferUrl Nem sikerült elküldeni a fájlt. A kiszolgáló URL-je nincs konfigurálva. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 éppen gépel… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. Vágólapra másolva selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. A kijelölés a vágólapra másolva forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. Üzenettovábbítás helye kijelölése conferencesCopiedICS ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. icsDescription 'Description' : Title for the meeting description. Leírás icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. icsJoinButton 'Join' : Action button to join the meeting. deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. Leírás icsJoinButton 'Join' : Action button to join the meeting. icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. icsParticipants '%1 participant' : number(=%1) of participant. icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Küldött: %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Lekért: %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Elolvasta: %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 nem kapta meg az üzenetet %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) Mégse fileTransferDownload 'Download' : Message link to download a file Letöltés ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. Továbbítva ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Összes másolása menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Másolás menuPlayMe Lejátszás! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Kézbesítés állapota menuDelete 'Delete' : Item menu to delete a message Törlés menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Kézbesítési állapot elrejtése menuForward 'Forward' : Forward a message from menu Továbbítás menuReply 'Reply' : Reply to a message from menu Válasz ChatNoticeModel nMinute %1 perc nHour %1 óra nDay %1 nap nWeek %1 hét ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Válasz ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Válaszul erre: %1 Cli appCliDescription A(z) %1 alkalmazás vezérlésének módja a parancssorokon keresztül. uriCommandLineSyntax %1 <sip-cím>?method=<módszer>([&<argumentum>=<base64-érték>]*) cliCommandLineSyntax %1 "<módszer> ([<argumentum>=<érték>]*)" commandsName parancsok listája: showFunctionDescription Alkalmazás főablakának megjelenítése. callFunctionDescription Hívást kezdeményez a sip-address. initiateConferenceFunctionDescription Konferencia kezdeményezése. joinConferenceFunctionDescription Csatlakozzon a SIP-cím által rendezett konferenciához megjelenítendő névként. Ha kapcsolódik egy proxy-konfigurátorhoz, olvassa el a join-conference-as című részt. joinConferenceAsFunctionDescription Csatlakozzon a SIP-cím által üzemeltetett konferenciához, mint a vendég-sip-cím. Ha nem csatlakozik proxy-konfigurátorhoz, olvassa el a csatlakozási konferencia részt. byeFunctionDescription Egy adott hívás, az összes hívás vagy az jelenlegi hívás befejezése. CodecsViewer codecMime Név codecEncoderDescription Leírás codecEncoderClockRate Sebesség (Hz) codecBitrate Bitsebesség (Kbit/mp) codecRecvFmtp Paraméterek codecStatus Állapot Conference conferenceTitle Konferencia ConferenceControls conference Konferencia ConferenceManager conferenceManagerDescription Kezelje a konferencia résztvevőit. cancel Mégse confirm Indítás Conferences conferencesTitle 'Meetings' : Conference list title. conferencesEndedFilter 'Finished' : Filter meetings on end status. conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel Mégse confirm Megerősít ContactEdit removeContactDescription Tényleg eltávolítja ezt a névjegyet a címjegyzékből? sipAccounts SIP fiók(ok) address Cím emails E-mail(ek) webSites Honlap(ok) avatarChooserTitle Avatár kiválasztása companies Cégek save Mentés cancel Mégse sipAccountsPlaceholder SIP fiók companiesPlaceholder Cég emailsPlaceholder E-mail webSitesPlaceholder Honlap street Utca postalCode Irányítószám country Ország locality Helyiség abortEditDescriptionText Biztosan törli a névjegy változtatását? tooltipShowConversation Beszélgetés megjelenítése missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Konferenciaalapú csevegőszoba létrehozásához be kell állítania a konferencia URI-címét a fiókbeállításokban. Contacts searchContactPlaceholder Névjegy keresése selectAllContacts Összes selectConnectedContacts Kapcsolódva addContact Névjegy hozzáadása removeContactDescription Tényleg eltávolítja ezt a névjegyet a címjegyzékből? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Konferenciaalapú csevegőszoba létrehozásához be kell állítania a konferencia URI-címét a fiókbeállításokban. Conversation displayCallsAndMessages Összes displayCalls Hívások displayMessages Üzenetek removeAllEntriesDescription Biztosan törölni kívánja ezt az előzményt? tooltipContactEdit Kapcsolat szerkesztése tooltipContactAdd Kapcsolat hozzáadása cleanHistory Előzmények törlése adminStatus 'Admin' : Admin(istrator) Felügyelet One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room Csoportadatok conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room Beszélgetés eszközei conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode Elmúló üzenetek groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room Összes csevegőszobai résztvevő hívása searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list Üzenetek keresése conversationMenuDelete 'Delete history' : Item menu to delete the chat's history Előzmények törlése conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book Kapcsolat hozzáadása conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription Hogyan szeretné létrehozni a fiókját? createAppSipAccountTitle %1 fiók létrehozása withPhoneNumber Telefonszámmal withEmailAddress E-mail címmel CreateAppSipAccountWithEmail createAppSipAccountTitle %1 fiók létrehozása confirmAction Létrehozás usernameLabel Felhasználónév emailLabel E-mail passwordLabel Jelszó passwordConfirmationLabel Jelszó megerősítése passwordConfirmationError A megadott jelszavak nem egyeznek. quitWarning Fiókját létrehozta, de még nem hitelesítette. Biztosan le szeretne lépni erről a nézetről? displayNameLabel Megjelenítendő név (választható) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle %1 fiók létrehozása countryLabel Ország phoneNumberLabel Telefonszám usernameLabel Felhasználónév displayNameLabel Megjelenítendő név (választható) confirmAction Létrehozás quitWarning Fiókját létrehozta, de még nem hitelesítette. Ha kilép ebből a nézetből, akkor 24 órán belül kézzel kell hozzáadnia és érvényesítenie a fiókját. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Válasszon egy vagy több fájlt dropYourAttachment Adja hozzá ide a mellékletet attachmentTooltip Fájl küldése EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation Mégse startButton 'start' : button text to start ephemeral mode Indítás ephemeralTitle "Ephemeral messages" : Popup title for ephemerals Elmúló üzenetek ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals Az új üzenetek mindkét végén törlődnek, amint a kapcsolattartó elolvasta. Válasszon egy időkorlátot. ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Az elmúló üzenetet csak a konferencia alapú csevegőszoba támogatja! Warning about not being in conference based chat room. disabled 'Disabled' Letiltva nMinute '%1 minute' %1 perc nHour '%1 hour' %1 óra nDay '%1 day' %1 nap nWeek '%1 week' %1 hét Event incomingCall Bejövő hívás outgoingCall Kimenő hívás declinedIncomingCall Bejövő hívás elutasítása declinedOutgoingCall Kimenő hívás elutasítása endedCall Befejezett hívás missedIncomingCall Nem fogadott bejövő hívás missedOutgoingCall Nem fogadott kimenő hívás FetchRemoteConfiguration confirmAction Lekérés fetchRemoteConfigurationTitle Távoli konfiguráció lekérése urlLabel URL remoteProvisioningError Nem sikerült beállítani ezt a távoli létesítési URI-t. remoteProvisioningUpdateDescription Az alkalmazás újraindítása szükséges. Szeretné most újraindítani? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. Az utolsó távoli kiépítés nem sikerült generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Biztosan törölni kívánja ezt az előzményt? tooltipContactEdit Kapcsolat szerkesztése tooltipContactAdd Kapcsolat hozzáadása cleanHistory Előzmények törlése Home howToDescription Segítségre van szüksége a(z) %1 használatához? howToTitle Hogyan kell %1 használni inviteDescription Hívd meg ismerőseidet a %1-ra. inviteTitle Hívd meg a ismerőseidet accountAssistantDescription A(z) %1 fiók létrehozása vagy kezelése. accountAssistantTitle Fióksegéd assistantButton Segéd showTooltips Buboréksúgók megjelenítése inviteButton Meghívás Incall acceptVideoDescription Kapcsolattartója szeretné bekapcsolni a videót. securedStringFormat A hívás titkosítva: %1. callNotSecured A hívás nincs titkosítva. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Felvétel megindítása incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. A felvétel leállítása incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Pillanatkép készítése incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Felvétel megindítása incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. A felvétel leállítása incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Pillanatkép készítése incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. Résztvevők listája incallMenuTitle 'Settings' : Main menu title for settings. Beállítások incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label Csoport elhagyása ok 'OK' : Button label Rendben addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Résztvevők hozzáadása addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Keresse meg a résztvevőket a névjegyzékben, hogy meghívhassa őket a csevegőszobába. Explanation for inviting the selected participants into chat room participantList 'Participant list' Résztvevők listája adminStatus 'Admin' : Admin(istrator) Felügyelet word for admin status chatRoomDetailsTitle "Group information" : Popup title. Csoportadatok popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation Mégse callButton 'CALL' : Button that lead to a call Hívás okButton 'OK' : Button that validate the popup to be redirected to the device list Rendben infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. Végpontok között titkosított encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Végpontok között titkosított azonnali üzenetek a biztonságos beszélgetésekben. Lehetőség van a beszélgetés biztonsági szintjének növelésére a résztvevők hitelesítésével. Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Ehhez hívja fel a kapcsolatot és kövesse a hitelesítési folyamatot. Explanation process InviteFriends enterEmailLabel Ismerős e-mail címe messageLabel Üzenet cancel Mégse confirm Megerősít inviteFriendsTitle Ismerősök meghívása defaultMessage %1 partnerfelkérései a %2-n! defaultSubject %1 meghívás forcedMessage Töltse le az alkalmazást a számítógépén, és kezdje el ingyen hívni és csevegni a felhasználókkal. Kattintson ide: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 A %1 névjegye Preferences... Beállítások Services Szolgáltatások Hide %1 A %1 Elrejtése Hide Others A több Elrejtése Show All Mindet Mutat Quit %1 Kilépés %1 MainWindow mainSearchBarPlaceholder Névjegy keresése, hívás indítása vagy csevegés kezdése… contactsEntry Névjegyek autoAnswerStatus önműködő smartSearchBarTooltip Az intelligens keresősáv segítségével közvetlenül kezdeményezheti audio- és videohívásokat, küldhet üzenetet vagy új névjegyet adhat hozzá. Csak írja be ismerőse SIP-címét vagy felhasználónevét. newConferenceButton Konferencia-beszélgetés indítása newChatRoom 'Start a chat room' : Tooltip to illustrate a button Csevegőszoba indítása hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline Idővonal elrejtése openTimeline 'Open Timeline' : Tooltip for a button that open the timeline Idővonal megnyitása openHome 'Open Home' : Tooltip for a button that open the home view Kezdőlap megnyitása mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Beállítások about A Linphone névjegye quit Kilépés checkForUpdates 'Check for updates' : Item menu for checking updates Frissítések keresése MainWindowTopMenuBar settings Beállítások about A Linphone névjegye quit Kilépés checkForUpdates 'Check for updates' : Item menu for checking updates Frissítések keresése ManageAccounts ok Rendben selectPresenceLabel Jelenléti állapot selectAccountLabel Aktív fiók MultimediaParametersDialog ok Rendben menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button Mégse startButton 'Launch' : Start button Indítás missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Ki kell töltenie egy témát. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. Legalább %1 résztvevő szükséges. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. Konferenciaalapú csevegőszoba létrehozásához be kell állítania a konferencia URI-címét a fiókbeállításokban. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room Csevegőszoba indítása askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. Szeretné titkosítani a csevegését? subjectLabel 'Subject' : Label of a text field about the subject of the chat room Téma subjectTooltip 'Current subject of the Chat Room. It cannot be empty' A csevegőszoba jelenlegi témája. Nem lehet üres. Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Résztvevők kiválasztása participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' Keressen a névjegyek között vagy adjon hozzá egy egyediet a csevegőszobához. adminStatus 'Admin' : Admin(istrator) Felügyelet word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Távolítsa el ezt a résztvevőt a kiválasztásból This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Kötelező subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Téma hozzáadása LastContactsTitle 'Last contacts' : Header for showing last contacts Legutóbbi névjegyek NewConference cancelButton 'Cancel' : Cancel button Mégse missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Ki kell töltenie egy témát. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. Legalább %1 résztvevő szükséges. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Konferenciaalapú csevegőszoba létrehozásához be kell állítania a konferencia URI-címét a fiókbeállításokban. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference Téma subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Téma hozzáadása subjectTooltip 'Current subject of the Meeting. It cannot be empty' A csevegőszoba jelenlegi témája. Nem lehet üres. Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. Szeretné titkosítani a csevegését? participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Résztvevők kiválasztása participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' Keressen a névjegyek között vagy adjon hozzá egy egyediet a csevegőszobához. adminStatus 'Admin' : Admin(istrator) Felügyelő word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Távolítsa el ezt a résztvevőt a kiválasztásból This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Kötelező launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description Leírás newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. A művelet folyamatban van, kérjük várjon confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. Csatlakozott a csoporthoz conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. Kilépett a csoportból conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. %1 csatlakozott conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group %1 kilépett conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody %1 most rendszergazdai jogosultság kapott conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody %1 most rendszergazdai jogosultság eltávolított conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. Csökkent biztonsági szint a következő szerint: %1 conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time Elmúló üzenetek engedélyezve: %1 conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. Elmúló üzenetek letiltva: %1 conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. Új téma: %1 conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Mulandó üzenetek frissítve: %1 unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. %1 olvasatlan üzenet Notifier newVersionAvailable Egy új frissítés (%1) érhető el! newFileMessage Új melléklet érkezett! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm Megerősít onlineInstallerExtractingDescription %1 kicsomagolása… onlineInstallerDownloadingDescription %1 letöltése… onlineInstallerFinishedDescription A(z) %1 sikeresen telepítve lett! onlineInstallerFailedDescription A(z) %1 telepítése nem sikerült! OutgoingMessage messageError Hiba messageRead Olvasott messageDelivered Kézbesítve ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices Beszélgetési eszközök ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Résztvevők hozzáadása addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Keresse meg a résztvevőket a névjegyzékben, hogy meghívhassa őket a csevegőszobába. Explanation for inviting the selected participants into chat room participantList 'Participant list' Résztvevők listája adminStatus 'Admin' : Admin(istrator) Felügyelő word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. Távolítsa el ezt a résztvevőt a kiválasztásból ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) (Felügyelet) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline Elérhető presenceBusy Elfoglalt presenceDoNotDisturb Ne zavarjanak presenceOffline Kapcsolat nélküli QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. Engedélyezés LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. Letiltás LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. Önműködő SettingsAdvanced logsTitle Naplók logsFolderLabel Naplómappa sendLogs Naplók küldése logsUploadUrlLabel Naplók feltöltési kiszolgáló URL-je logsUploadFailed A naplók feltöltése nem sikerült. logsEnabledLabel Naplók engedélyezése cleanLogs Napló kitakarítása cleanLogsDescription Biztosan eltávolítja az összes naplót? developerSettingsTitle Fejlesztői beállítások developerSettingsEnabledLabel Fejlesztői beállítások engedélyezése logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) A Mailer nem található, de a naplók feltöltődtek a %1 logsMailerSuccess A naplókat feltöltötték a %1 contactsTitle Névjegyek-összekötő noPlugin 'No Plugins to load' : Text in combobox Nincsenek betölthető beépülő modulok viewlogs SettingsAudio audioTitle Hang paraméterek playbackDeviceLabel Lejátszóeszköz captureDeviceLabel Felvevőeszköz ringerDeviceLabel Csörgetés ringLabel Csörög echoCancellationLabel Visszhangkioltás engedélyezése audioCodecsTitle Hang kodekek showAudioCodecsLabel Hang kodekek megjelenítése playbackGainLabel Lejátszás nyeresége captureGainLabel Rögzítés nyereség audioTestLabel Felvételi szint audioSettingsInCallWarning Hanghívás folyamatban: egyes beállítások nem érhetők el. echoCancellationCalibrationLabel Visszhangtörlő kalibrálása calibratingEchoCancellationInProgress Visszhangtörlő kalibrálás folyamatban… calibratingEchoCancellationDone %1ezredmásodperc értékre kalibrálva calibratingEchoCancellationFailed Sikertelen kalibrálás calibratingEchoCancellationNone Nem észlelt visszhang SettingsCallsChat fileServerLabel Fájlkiszolgáló encryptWithLimeLabel LIME titkosítás limeDisabled Letiltva limeRequired Kötelező limePreferred Elsődleges chatTitle Csevegés callsTitle Hívások encryptionLabel Titkosítás noEncryption Nincs autoAnswerLabel Önműködő válasz autoAnswerDelayLabel Késés (ms) autoAnswerWithVideoLabel Önműködő válasz (videóval) chatEnabledLabel Csevegés engedélyezése callRecorderEnabledLabel Hívásfelvétel engedélyezése chatNotificationSoundEnabledLabel Értesítési hang engedélyezése chatNotificationSoundLabel Értesítési hang conferenceEnabledLabel Konferencia engedélyezése contactsTitle Névjegyek contactsEnabledLabel Névjegyek engedélyezése muteMicrophoneEnabledLabel A mikrofon némításának engedélyezése outgoingCallsEnabledLabel Kimenő hívások engedélyezése showTelKeypadAutomaticallyLabel A telefonbillentyűzet önműködően megjelenítése automaticallyRecordCallsLabel Hívások önműködő felvétele keepCallsWindowInBackgroundLabel Tartsa a hívások ablakot a háttérben callPauseEnabledLabel A hívástartás engedélyezve encryptionMandatoryLabel A titkosítás kötelező hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms Üres csevegőszobák elrejtése waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered Ha regisztrált, használja híváskor chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. Értesítések engedélyezése AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. Soha autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. Mindig callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer Új kiszolgáló SettingsLdapEdit cancel Mégse confirm Megerősít displayNameLabel Megjelenítendő név displayNameTooltip A listában megjelenítendő kiszolgáló megjelenített neve. connectionTitle Kapcsolat serverLabel Kiszolgáló URL-címe serverTooltip LDAP-kiszolgáló. Például: ldap:/// helyi állomás kiszolgálóhoz vagy ldap://ldap.example.org/ bindDNLabel Kötési DN bindDNTooltip A kötési DN az a hitelesítő adat, amelyet LDAP-kiszolgálóval való hitelesítésre használnak.<br>például: cn=felhasználónév,ou=emberek,dc=bc,dc=com passwordLabel Jelszó useTLSLabel TLS használata useTLSTooltip Titkosítsa a tranzakciókat LDAP segítségével TLS(StartTLS) használatával. Az „ldap” sémát kell használnia. Az SSL-en keresztüli LDAP „ldaps” nem szabványosított és elavult.<br>StartTLS az LDAP protokoll kiterjesztésében, amely a TLS protokollt használja a kommunikáció titkosításához.<br>Úgy működik, hogy normál – azaz nem biztonságos – kapcsolatot létesít az LDAP kiszolgálóval, mielőtt a szerver és a webszolgáltatások között kézfogási egyeztetés zajlik. Itt a szerver elküldi tanúsítványát, hogy igazolja személyazonosságát, mielőtt a biztonságos kapcsolat létrejön. useSalLabel SAL használata useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' A DNS feloldását %1 végzi SAL használatával. Egy IP-címet továbbít az LDAP kiszolgálónak. Ezzel a TLS tárgyalás nem tudta ellenőrizni az állomásnévét. Deaktiválhatja az ellenőrzéseket, ha kényszeríteni szeretné a kapcsolatot. verifyTLSLabel Tanúsítványok ellenőrzése a TLS használatával AutoMode Önműködő offMode Ki onMode Be verifyTLSTooltip Adja meg, hogy az LDAP-kiszolgálóhoz való csatlakozáskor ellenőrizni kell-e a TLS-kiszolgáló tanúsítványát. searchTitle Keresés baseObjectLabel Keresési bázis baseObjectPlaceholder Keresési bázis baseObjectTooltip Az Base Object/Search Base az LDAP keresési hatókörök specifikációja, amely meghatározza, hogy a keresési kérelmet csak a keresési bázis DN-ként megadott bejegyzéssel kell végrehajtani.<br>A feletti bejegyzéseket nem vesszük figyelembe. filterLabel Szűrő filterTooltip A keresés a szűrőn alapul a névjegyek kereséséhez.<br>Alapértelmezett érték: (sn=%s) maxResultsLabel Eredmények legfeljebb száma maxResultsTooltip A keresési kérelmek eredmények legfeljebb száma. timeoutLabel Időkorlát timeoutTooltip A kapcsolat és a keresés időtúllépése másodpercek alatt. Pozitívnak kell lennie.<br>Az alapértelmezett érték 5 másodperc. parsingTitle Elemzés nameAttributesLabel Névattribútumok nameAttributesTooltip Ellenőrizze ezeket az attribútumokat: To, build, Name, Contact. Ezeket vesszők választják el egymástól, amelyek első sorban a legmagasabb prioritással szerepelnek.<br>Az alapértelmezett érték: sn sipAttributesLabel SIP-attribútumok sipAttributesTooltip Ellenőrizze ezeket az attribútumokat a SIP felhasználónév felépítéséhez a kapcsolattartó címében. Az attribútumokat vessző választja el, és az első a legfontosabb.<br>Az alapértelmezett érték: mobile,telephoneNumber,homePhone,sn domainLabel Tartomány domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. Tartomány hozzáadása a SIP-címhez (sip:felhasználónév@tartomány). miscLabel Egyéb debugLabel Hibakeresés debugTooltip Részletes naplófájlok beszerzése naplófájljába tranzakciók során (hasznos a TLS-kapcsolatok hibakereséséhez). SettingsNetwork sendDtmfsLabel DTMF küldési eljárás allowIpV6Label IPv6 engedélyezése transportTitle Szállítás natAndFirewallTitle NAT és tűzfal enableIceLabel ICE engedélyezése stunServerLabel STUN/TURN kiszolgáló enableTurnLabel TURN engedélyezése turnUserLabel TURN felhasználó turnPasswordLabel TURN jelszó networkProtocolAndPortsTitle Hálózati protokoll és kikötők sipUdpPortLabel SIP UDP kikötő sipTcpPortLabel SIP TCP kikötő audioRtpUdpPortLabel Hang RTP UDP kikötő videoRtpUdpPortLabel Videó RTP UDP kikötő dscpFieldsTitle DSCP mezők sipFieldLabel SIP audioRtpStreamFieldLabel Hang RTP adatfolyam videoRtpStreamFieldLabel Videó RTP adatfolyam bandwidthControlTitle Sávszélesség-vezérlés downloadSpeedLimitLabel Letöltés sebességkorlátozása (Kbit/mp) uploadSpeedLimitLabel Feltöltési sebességkorlátozása (Kbit/mp) enableAdaptiveRateControlLabel Adaptív sebességszabályozás engedélyezése presenceTitle Jelenlét rlsUriLabel RLS URI használata rlsUriAuto Önműködő rlsUriDisabled Soha showNetworkSettingsLabel Hálózati beállítások megjelenítése generalTitle Általános SettingsSipAccounts defaultIdentityTitle Alapértelmezett azonosság defaultUsernameLabel Felhasználónév defaultSipAddressLabel SIP cím proxyAccountsTitle Proxyfiókok eraseAllPasswords Jelszavak törlése addAccount Fiók hozzáadása editHeader Szerkesztés deleteHeader Törlés deleteAccountDescription Biztosan törölni kívánja ezt a fiókot? eraseAllPasswordsDescription Biztos, hogy eltávolítja az összes jelszavát? defaultDisplayNameLabel Megjelenítendő név assistantTitle Segéd createAppSipAccountEnabledLabel Fióklétrehozás engedélyezése useAppSipAccountEnabledLabel Fiókhasználat engedélyezése useOtherSipAccountEnabledLabel Általános fiókhasználat engedélyezése fetchRemoteConfigurationEnabledLabel Konfiguráció lekérés engedélyezése assistantSupportsPhoneNumbersLabel Támogatja a telefonszámokat defaultDeviceNameLabel 'Device Name' : Label for setting the device name. Eszköz neve webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel SIP cím transportLabel Szállítás serverAddressLabel SIP kiszolgáló címe registrationDurationLabel Regisztráció időtartama (mp) routeLabel Útvonal contactParamsLabel Névjegy paraméterei publishPresenceLabel Jelenlét láthatóvá tétele avpfIntervalLabel AVPF szokásos RTCP időközönként (mp) registerEnabledLabel Regisztráció avpfEnabledLabel AVPF engedélyezése cancel Mégse confirm Megerősít invalidSipAddress Érvénytelen SIP-cím. invalidServerAddress Érvénytelen kiszolgáló cím. invalidRoute Érvénytelen útvonal. enableIceLabel ICE engedélyezése stunServerLabel STUN/TURN kiszolgáló enableTurnLabel TURN engedélyezése turnUserLabel TURN felhasználó turnPasswordLabel TURN jelszó natAndFirewallTitle NAT és tűzfal mainSipAccountSettingsTitle Fő SIP fiók beállításai conferenceURI "Conference URI" : Label of a text edit for filling Conference URI Konferencia URI-cím invalidConferenceURI "invalid conference URI" : Error text about conference URI Érvénytelen konferencia URI-cím videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) Alagút állapota tunnelDomain 'Domain' : Field title of a textfield to set domain. Tartomány tunnelUsername 'Username' : Field title of a textfield to set username. Felhasználónév tunnelSIP 'SIP' : Field title of a switch to set SIP mode. SIP cancel 'Cancel' : Button to cancel the action. Mégse setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. HTTP-proxy megadása proxyHttpHost 'Host' : Placeholder to set hostname. Állomásnév proxyHttpPort 'Port' : Placehoilder to set port. Kikötő proxyHttpUsername 'Username' : Placeholder to set username. Felhasználónév proxyHttpPassword 'Password' : Placeholder to set password. Jelszó proxyHttpApply 'Apply' : Button to set proxy from changes. Alkalmazás serverMode 'Mode' : Field title on form to set tunnel mode. Alagútmód serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. Kettős mód serverTitle 'Server' : Title form to set a server Kiszolgáló serverHostname 'Hostname' : Field title on form to set hostname. Állomásnév serverPort 'Port' : Field title on form to set port. Kikötő serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. Kettős állomásnév URL-címe serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. Kettős kikötő serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. Távoli UDP-tükörkikötő serverDelay 'Delay' : Field title on form to set the delay of the tunnel. Késleltetés tunnelAddServer 'Add server' : Button for adding a server Kiszolgáló hozzáadása tunnelApply 'Apply' : Button to apply changes. Alkalmazás SettingsUi pathsTitle Utak savedScreenshotsLabel Mentett képernyőképek mappája savedCallsLabel Mentett hívások mappája languagesTitle Nyelvek languagesLabel Nyelv systemLocale Rendszer területi beállítása cleanAvatars Megtestesülés törlése cleanAvatarsDescription Biztos, hogy eltávolítja az összes megtestesülést? downloadLabel Letöltési mappa setLocaleDescription Az alkalmazás újraindítása szükséges. Szeretné most újraindítani? otherTitle Egyéb exitOnCloseLabel Alkalmazás kilépése az ablak bezárásakor dataTitle UI-adatok autoStartLabel Alkalmazás önműködő indítása fontsTitle 'Fonts' : title of fonts section in settings Betűkészletek fontsTextChange 'Text Messages' : Label for changing text message fonts Szöveges üzenetek fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts Új betűkészlet kijelölése checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates Frissítések keresése mipmapLabel 'Enable Mipmap' Mipmap engedélyezése mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. Ez a tulajdonság megmarad, függetlenül attól, hogy a kép mipmap-szűrést használ-e méretezéskor vagy átalakításkor. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. A mipmap-szűrés jobb vizuális minőséget biztosít kicsinyítéskor, mint a sima, de teljesítményköltséggel járhat (mind a kép inicializálásakor, mind a renderelés során). minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version Egyéni versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Videó beviteli eszköz videoFramerateLabel Képkockasebesség videoCaptureTitle Videofelvétel paraméterei videoPresetLabel Videó előbeállítás presetDefault Alapértelmezett presetHighFps Magas képkockák másodpercenkénti szám presetCustom Egyéni videoSizeLabel Videó felbontás videoCodecsTitle Videó kodekek showCameraPreview Videó előnézete showVideoCodecsLabel Videó kodekek megjelenítése videoSettingsInCallWarning Videohívás folyamatban: egyes beállítások nem állnak rendelkezésre. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. Hívások videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm Rendben SettingsWindow settingsTitle Beállítások sipAccountsTab SIP fiókok audioTab Hang videoTab Videó callsAndChatTab Hívások és csevegés networkTab Hálózat uiTab Felhasználói felület validButton Rendben uiAdvanced Haladó tunnelTab 'Tunnel' : Tab title for tunnel section in settings. Alagút SipAddressDialog cancel Mégse contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact Keresés a névjegyek között contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip Keressen egy címet a névjegyei között, vagy használjon egyéni címet. timelineSelectionHeader 'Conversations' : header for a selection in conversation list Beszélgetések SmartSearchBar addContact Névjegy hozzáadása Timeline timelineFilter A title for filtering mode. Szűrő timelineFilterAll 'All' The mode for timelines filtering. Összes timelineFilterCustom 'Custom' The mode for timelines filtering. Egyéni timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). Egyszerű szobák timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. Biztonságos szobák timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). Csevegőcsoportok timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. Elmúlók timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list Keresés a listában timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. Minden biztonsági szint timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. Szabványos szobák timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. Minden beszélgetés timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. Elmúlók be/ki timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. Elmúlók nélkül timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction Használat useAppSipAccountTitle %1 fiók használata useUsernameToLogin A telefonszám helyett használjon felhasználónevet és jelszót. quitWarning Fiókját létrehozta, de még nem hitelesítette. Biztosan le szeretne lépni erről a nézetről? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password Elfelejtett jelszó? UseAppSipAccountWithPhoneNumber countryLabel Ország phoneNumberLabel Telefonszám displayNameLabel Megjelenítendő név (választható) UseAppSipAccountWithUsername usernameLabel Felhasználónév passwordLabel Jelszó displayNameLabel Megjelenítendő név (választható) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form Használat useOtherSipAccountTitle SIP fiók használata usernameLabel Felhasználónév displayNameLabel Megjelenítendő név (választható) sipDomainLabel SIP tartomány passwordLabel Jelszó transportLabel Szállítás addOtherSipAccountError Nem sikerült hozzáadni ezt a fiókot. understandAction 'I understand' : Popup confirmation for a warning Megértettem warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. Mégse startButton 'Start' : Button label for starting the conference. Indítás endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. Kimenő hívás incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. Bejövő hívás ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Erősítse meg a következő SAS-t a társsal. codeA Mond: codeB A kapcsolattartónak el kell mondania: Later 'Later' : Button label to do something in another time. Később Correct 'Correct' : Button label to confirm a code. Helyes title 'Communication security' : Title of popup for ZRTP confirmation. Kommunikációs biztonság country Afghanistan Afganisztán Albania Albánia Algeria Algéria AmericanSamoa Amerikai Szamoa Andorra Andorra Angola Angola Anguilla Anguilla AntiguaAndBarbuda Antigua és Barbuda Argentina Argentína Armenia Örményország Aruba Aruba Australia Ausztrália Austria Ausztria Azerbaijan Azerbajdzsán Bahamas Bahamák Bahrain Bahrein Bangladesh Banglades Barbados Barbados Belarus Fehéroroszország Belgium Belgium Belize Belize Benin Benin Bermuda Bermuda Bhutan Bhután Bolivia Bolívia BosniaAndHerzegowina Bosznia és Hercegovina Botswana Botswana Brazil Brazília Brunei Brunei Bulgaria Bulgária BurkinaFaso Burkina Faso Burundi Burundi Cambodia Kambodzsa Cameroon Kamerun Canada Kanada CapeVerde Zöld-foki-szigetek CaymanIslands Kajmán-szigetek CentralAfricanRepublic Közép-Afrikai Köztársaság Chad Csád Chile Chile China Kína Colombia Colombia Comoros Comore-szigetek PeoplesRepublicOfCongo Kongói Népköztársaság DemocraticRepublicOfCongo Kongói Demokratikus Köztársaság CookIslands Cook-szigetek CostaRica Costa Rica IvoryCoast Elefántcsontpart Croatia Horvátország Cuba Kuba Cyprus Ciprus CzechRepublic Cseh Köztársaság Denmark Dánia Djibouti Dzsibuti Dominica Dominika DominicanRepublic Dominikai Köztársaság Ecuador Ecuador Egypt Egyiptom ElSalvador El Salvador EquatorialGuinea Egyenlítői-Guinea Eritrea Eritrea Estonia Észtország Ethiopia Etiópia FalklandIslands Falkland-szigetek FaroeIslands Feröer-szigetek Fiji Fidzsi-szigetek Finland Finnország France Franciaország FrenchGuiana Francia Guyana FrenchPolynesia Francia Polinézia Gabon Gabon Gambia Gambia Georgia Grúzia Germany Németország Ghana Ghána Gibraltar Gibraltár Greece Görögország Greenland Grönland Grenada Grenada Guadeloupe Guadeloupe Guam Guam Guatemala Guatemala Guinea Guinea GuineaBissau Bissau-Guinea Guyana Guyana Haiti Haiti Honduras Honduras HongKong Hong Kong Hungary Magyarország Iceland Izland India India Indonesia Indonézia Iran Irán Iraq Irak Ireland Írország Israel Izrael Italy Olaszország Jamaica Jamaica Japan Japán Jordan Jordánia Kazakhstan Kazahsztán Kenya Kenya Kiribati Kiribati DemocraticRepublicOfKorea Koreai Népi Demokratikus Köztársaság RepublicOfKorea Koreai Köztársaság Kuwait Kuvait Kyrgyzstan Kirgizisztán Laos Laosz Latvia Lettország Lebanon Libanon Lesotho Lesotho Liberia Libéria Libya Líbia Liechtenstein Liechtenstein Lithuania Litvánia Luxembourg Luxembourg Macau Makaó Macedonia Macedónia Madagascar Madagaszkár Malawi Malawi Malaysia Malaysia Maldives Maldív-szigetek Mali Mali Malta Málta MarshallIslands Marshall-szigetek Martinique Martinique Mauritania Mauritánia Mauritius Mauritius Mayotte Mayotte Mexico Mexikó Micronesia Mikronézia Moldova Moldova Monaco Monaco Mongolia Mongólia Montenegro Montenegró Montserrat Montserrat Morocco Marokkó Mozambique Mozambik Myanmar Myanmar Namibia Namíbia NauruCountry Nauru Nepal Nepál Netherlands Németalföld NewCaledonia Új-Kaledónia NewZealand Új-Zéland Nicaragua Nicaragua Niger Niger Nigeria Nigéria Niue Niue NorfolkIsland Norfolk-sziget NorthernMarianaIslands Észak Mariana szigetek Norway Norvégia Oman Omán Pakistan Pakisztán Palau Palau PalestinianTerritories Palesztin területek Panama Panama PapuaNewGuinea Pápua Új-Guinea Paraguay Paraguay Peru Peru Philippines Fülöp-szigetek Poland Lengyelország Portugal Portugália PuertoRico Puerto Rico Qatar Katar Reunion Réunion Romania Románia RussianFederation Orosz Föderáció Rwanda Ruanda SaintHelena Saint Helena SaintKittsAndNevis Saint Kitts és Nevis SaintLucia Saint Lucia SaintPierreAndMiquelon Saint Pierre és Miquelon SaintVincentAndTheGrenadines Saint Vincent és a Grenadine-szigetek Samoa Szamoa SanMarino San Marino SaoTomeAndPrincipe São Tomé és Príncipe SaudiArabia Szaud-Arábia Senegal Szenegál Serbia Szerbia Seychelles Seychelles SierraLeone Sierra Leone Singapore Szingapúr Slovakia Szlovákia Slovenia Szlovénia SolomonIslands Salamon-szigetek Somalia Szomália SouthAfrica Dél-Afrika Spain Spanyolország SriLanka Srí Lanka Sudan Szudán Suriname Suriname Swaziland Szváziföld Sweden Svédország Switzerland Svájc Syria Szíria Taiwan Tajvan Tajikistan Tádzsikisztán Tanzania Tanzánia Thailand Thaiföld Togo Togo Tokelau Tokelau Tonga Tonga TrinidadAndTobago Trinidad és Tobago Tunisia Tunézia Turkey Törökország Turkmenistan Türkmenisztán TurksAndCaicosIslands Turks- és Caicos-szigetek Tuvalu Tuvalu Uganda Uganda Ukraine Ukrajna UnitedArabEmirates Egyesült Arab Emírségek UnitedKingdom Egyesült Királyság UnitedStates Egyesült Államok Uruguay Uruguay Uzbekistan Üzbegisztán Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietnam WallisAndFutunaIslands Wallis- és Futuna-szigetek Yemen Jemen Zambia Zambia Zimbabwe Zimbabwe utils downloadCodecDescription Szeretné letölteni a(z) %1 (%2)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' %1 hét formatDays '%1 day' %1 nap formatHours '%1 hour' %1 óra formatMinutes '%1 minute' %1 perc formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/it.ts000066400000000000000000005204151434616504300236440ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name Aiutaci a tradurre %1 ActivateAppSipAccountWithEmail activateAppSipAccount ATTIVA IL TUO ACCOUNT %1 confirmAction ATTIVA activationSteps Per attivare il tuo account segui le istruzioni cliccando sul link: %1. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount ATTIVA IL TUO ACCOUNT %1 confirmAction ATTIVA activationSteps Abbiamo inviato un SMS con un codice di convalida a %1. Per completare la verifica del numero di telefono, inserisci il codice di 4 cifre in basso. App commandLineOptionVerbose Accedi allo stdout di alcune informazioni di debug durante l'esecuzione commandLineOptionConfig specifica il file di configurazione di %1 da utilizzare applicationDescription Un video-telefono SIP libero. commandLineOptionIconified avvia nella barra delle applicazioni, non mostrare l'interfaccia principale commandLineOptionConfigArg file commandLineOptionHelp mostra questo aiuto commandLineOptionVersion mostra la versione dell'app commandLineOptionCliHelp visualizza il menu guida per utilizzare %1 con la CLI commandLineDescription invia un comando all'applicazione tramite la riga di comando restore Ripristina quit Esci settings Preferenze about Informazioni commandLineOptionFetchConfig Specifica il file di configurazione %1 da cercare. Sarà accorpato alla configurazione in uso. commandLineOptionFetchConfigArg URL, cammino o file commandLineOptionCall iniziare una chiamata commandLineOptionCallArg indirizzo SIP checkForUpdates Cerca aggiornamenti AssistantAbstractView back INDIETRO AssistantHome useAppSipAccount USARE UN ACCOUNT %1 useOtherSipAccount USA UN ACCOUNT SIP fetchRemoteConfiguration RECUPERA UNA CONFIGURAZIONE REMOTA homeTitle BENVENUTO homeDescription Questo assistente ti aiuterà a configurare e utilizzare il tuo account sip. createAppSipAccount CREA UN ACCOUNT %1 homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. Accetto %2le condizioni di utilizzo e %4la politica sulla privacy%5 di %1 AssistantModel loginWithUsernameFailed Accesso fallito. Controlla l'utente e la password. usernameStatusTooShort Troppo corto! (min. %1 caratteri) usernameStatusTooLong Troppo lungo! (max. %1 caratteri) usernameStatusInvalidCharacters Rilevati caratteri non validi. (regex: `%1`) usernameStatusInvalid Nome utente non valido. passwordStatusTooShort Troppo corto! (min. %1 caratteri) passwordStatusTooLong Troppo lungo! (max. %1 caratteri) passwordStatusInvalidCharacters Rilevati caratteri non validi. (regex: `%1`) passwordStatusMissingCharacters Caratteri mancanti: `%1`. requestFailed Impossibile inviare la richiesta. emailStatusMalformed Indirizzo email errato. emailStatusMalformedInvalidCharacters Indirizzo email errato o caratteri non validi. cannotSendSms Errore del server: impossibile inviare SMS. accountAlreadyExists Questo account esiste già. smsActivationFailed Attivazione SMS fallita! emailActivationFailed Verifica di aver convalidato il tuo account o riprova. phoneNumberStatusInvalid Numero di telefono non valido! phoneNumberStatusTooShort Troppo corto! phoneNumberStatusTooLong Troppo lungo! phoneNumberStatusInvalidCountryCode Codice paese non valido! loginWithPhoneNumberFailed Accesso fallito. Per favore controlla il tuo numero di telefono. unableToAddAccount Impossibile aggiungere questo account. AuthenticationRequest cancel ANNULLA confirm LOGIN identityLabel Identità passwordLabel Password authenticationRequestDescription Impossibile autenticarsi. Verifica la password. userIdLabel User ID (opzionale) realmLabel Realm CallModel callStatsCodec Codec callStatsUploadBandwidth Banda di upload callStatsDownloadBandwidth Banda di download callStatsEstimatedDownloadBandwidth Banda di download stimata callStatsIceState Stato ICE callStatsIpFamily Famiglia IP callStatsSenderLossRate Tasso di perdita del mittente callStatsReceiverLossRate Tasso di perdita del ricevente callStatsJitterBuffer Buffer Jitter callStatsSentVideoDefinition Definizione del video inviata callStatsReceivedVideoDefinition Definizione del video ricevuta iceStateNotActivated Non attivato iceStateFailed Errore iceStateInProgress In carimento iceStateReflexiveConnection Connessione riflessiva iceStateHostConnection Connessione host iceStateRelayConnection Connessione relay iceStateInvalid Non valido callErrorDeclined Il destinatario ha rifiutato la chiamata. callErrorNotFound Destinatario non trovato. callErrorBusy Destinatario occupato. callErrorNotAcceptable La parte remota non può accettare la chiamata. callStatsReceivedFramerate Framerate di ricezione callStatsSentFramerate Framerate di invio callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel ANNULLA callSipAddressDescription Avvia una nuova chiamata. CallStatistics audioStatsLabel Audio videoStatsLabel Video mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section CallTransfer cancel ANNULLA callTransferDescription Vuoi trasferire la chiamata? Calls acceptAudioCall ACCETTA LA CHIAMATA AUDIO acceptVideoCall ACCETTA LA CHIAMATA VIDEO terminateCall RIAGGANCIA resumeCall RIPRENDI CHIAMATA transferCall TRASFERISCI CHIAMATA callPause PAUSA CHIAMATA attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. VALIDA IL TRANSFERIMENTO attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CHIEDI PRIMA DI EFFETTUARE IL TRASFERIMENTO CallsWindow callsTitle Chiamate acceptClosingDescription Sei sicuro di voler terminare tutte le chiamate? Chat newMessagePlaceholder Inserisci il tuo messaggio noFileTransferUrl Impossibile inviare il file. URL del server non configurato. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 sta scrivendo… %1 stanno scrivendo… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. Copiato negli appunti selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. Selezione copiata negli appunti forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. Scegli dove inoltrare il messaggio conferencesCopiedICS La conferenza ICS è stata copiata ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. Organizzatore icsDescription 'Description' : Title for the meeting description. Descrizione icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. Indirizzo conferenza icsJoinButton 'Join' : Action button to join the meeting. Partecipa deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. Vuoi veramente cancellare questo meeting ? cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. Descrizione icsJoinButton 'Join' : Action button to join the meeting. Partecipa icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. Invito al meeting icsParticipants '%1 participant' : number(=%1) of participant. %1 partecipante %1 partecipanti icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Inviato a %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Ricevuto da %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Letto da %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 non ha ancora ricevuto il messaggio %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message Errore durante l'invio a %1 %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) Terminare fileTransferDownload 'Download' : Message link to download a file Scaricare ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. Inoltrato ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Copia tutto menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Copia menuPlayMe Avviami! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Stato del messaggio menuDelete 'Delete' : Item menu to delete a message Cancella menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Nascondi lo stato del messaggio menuForward 'Forward' : Forward a message from menu Inoltra menuReply 'Reply' : Reply to a message from menu Rispondi ChatNoticeModel nMinute %1 minuto %1 minuti nHour %1 ora %1 ore nDay %1 giorno %1 giorni nWeek %1 settimana %1 settimane ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Rispondi ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Rispondi a %1 Cli appCliDescription Modo per controllare l'applicazione %1 tramite riga di comando. uriCommandLineSyntax cliCommandLineSyntax commandsName lista dei comandi: showFunctionDescription Mostra la finestra principale dell'applicazione. callFunctionDescription Inizia una chiamata all'indirizzo sip. initiateConferenceFunctionDescription Inizia una conferenza. joinConferenceFunctionDescription Partecipa alla conferenza dell'indirizzo SIP con il tuo nome. Se sei connesso a un proxy, vedi 'join-conference-as'. joinConferenceAsFunctionDescription Partecipa alla conferenza dell'indirizzo SIP con il tuo nome. Se non sei connesso a un proxy, vedi 'join-conference'. byeFunctionDescription Termina una chiamata specifica, tutte le chiamate o la chiamata corrente. CodecsViewer codecMime Nome codecEncoderDescription Descrizione codecEncoderClockRate Frequenza (Hz) codecBitrate Bitrate (Kbit/s) codecRecvFmtp Parametri codecStatus Status Conference conferenceTitle CONFERENZA ConferenceControls conference CONFERENZA ConferenceManager conferenceManagerDescription Gestisci i partecipanti alla tua conferenza. cancel ANNULLA confirm AVVIA Conferences conferencesTitle 'Meetings' : Conference list title. Conferenze conferencesEndedFilter 'Finished' : Filter meetings on end status. Terminate conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. Schedulate conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. L'URL della conferenze è stato copiato conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel ANNULLA confirm CONFERMA ContactEdit removeContactDescription Vuoi veramente rimuovere questo contatto dalla tua rubrica? sipAccounts SIP ACCOUNT address INDIRIZZO emails E-MAIL(S) webSites WEB SITE(S) avatarChooserTitle Scegli il tuo avatar companies AZIENDE save SALVA cancel ANNULLA sipAccountsPlaceholder Account SIP companiesPlaceholder Azienda emailsPlaceholder Email webSitesPlaceholder Web site street Strada postalCode Codice postale country Paese locality Località abortEditDescriptionText Sei sicuro di voler annullare la modifica del contatto? tooltipShowConversation Mostra conversazione missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Per creare una chat di gruppo è necessario impostare la URI del gruppo nelle impostazioni del tuo account. Contacts searchContactPlaceholder Cerca contatto selectAllContacts Tutti selectConnectedContacts Connesso addContact AGGIUNGI UN CONTATTO removeContactDescription Vuoi veramente rimuovere questo contatto dalla tua rubrica? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Per creare una chat di gruppo è necessario impostare la URI del gruppo nelle impostazioni del tuo account. Conversation displayCallsAndMessages TUTTI displayCalls CHIAMATE displayMessages MESSAGGI removeAllEntriesDescription Sei sicuro di voler cancellare questa cronologia? tooltipContactEdit Modifica contatto tooltipContactAdd Aggiungi contatto cleanHistory Cancella cronologia adminStatus 'Admin' : Admin(istrator) Amministratore One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room Informazioni gruppo conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room Dispositivi dei partecipanti conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode Messaggi effimeri groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room Chiama tutti i partecipanti del gruppo searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list Cerca nei messaggi conversationMenuDelete 'Delete history' : Item menu to delete the chat's history Elimina conversationMenuViewContact 'View contact' : Item menu to view the contact in address book Mostra il contatto conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book Aggiungi il contatto conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription Come ti piacerebbe creare il tuo account? createAppSipAccountTitle CREA UN ACCOUNT %1 withPhoneNumber CON UN NUMERO DI TELEFONO withEmailAddress CON UN INDIRIZZO E-MAIL CreateAppSipAccountWithEmail createAppSipAccountTitle CREA UN ACCOUNT %1 confirmAction CREA usernameLabel Nome utente emailLabel E-mail passwordLabel Password passwordConfirmationLabel Conferma password passwordConfirmationError Le password inserite non combaciano. quitWarning Il tuo account è stato creato ma non è ancora stato convalidato. Sei sicuro di voler uscire da questa schermata? displayNameLabel Nome visualizzato (facoltativo) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle CREA UN ACCOUNT %1 countryLabel Paese phoneNumberLabel Numero di telefono usernameLabel Nome utente displayNameLabel Nome visualizzato (facoltativo) confirmAction CREA quitWarning Il tuo account è stato creato ma non è ancora stato convalidato. Se esci da questa schermata, dovrai aggiungere e convalidare manualmente il tuo account entro 24 ore. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. Seleziona la data dateTimeDialogTime 'Select time' : Menu title to show select time. Imposta l'orario dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. Imposta data ed ora DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Seleziona uno o più file dropYourAttachment Rilascia il tuo allegato attachmentTooltip Invia un file EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation ANNULLA startButton 'start' : button text to start ephemeral mode INIZIA ephemeralTitle "Ephemeral messages" : Popup title for ephemerals Messaggi effimeri ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals I nuovi messaggi verranno cancellati in entrambe le parti una volta letti dal tuo contatto. Imposta un timeout. ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' I messaggi effimeri sono supportati solo nelle chat di gruppo! Warning about not being in conference based chat room. disabled 'Disabled' Disabilitato nMinute '%1 minute' %1 minuto %1 minuti nHour '%1 hour' %1 ora %1 ore nDay '%1 day' %1 giorno %1 giorni nWeek '%1 week' %1 settimana %1 settimane Event incomingCall Chiamata in arrivo outgoingCall Chiamata in uscita declinedIncomingCall Chiamata in arrivo rifiutata declinedOutgoingCall Chiamata in uscita rifiutata endedCall Chiamata terminata missedIncomingCall Chiamata in arrivo persa missedOutgoingCall Chiamata in uscita persa FetchRemoteConfiguration confirmAction RECUPERA fetchRemoteConfigurationTitle RECUPERA LA CONFIGURAZIONE REMOTA urlLabel URL remoteProvisioningError Impossibile impostare questo uri di provisioning remoto. remoteProvisioningUpdateDescription È necessario riavviare l'applicazione. Vuoi riavviare ora? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. L'ultima configurazione remota è fallita generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Sei sicuro di voler cancellare questa cronologia? tooltipContactEdit Modifica contatto tooltipContactAdd Aggiungi contatto cleanHistory Cancella cronologia Home howToDescription Hai bisogno di aiuto su come utilizzare %1? howToTitle COME USARE %1 inviteDescription Invita i tuoi amici su %1. inviteTitle INVITA I TUOI AMICI accountAssistantDescription Crea o gestisci il tuo account %1. accountAssistantTitle ASSISTENTE ACCOUNT assistantButton ASSISTENTE showTooltips Mostra suggerimenti inviteButton INVITA Incall acceptVideoDescription Il tuo contatto vorrebbe passare al video. securedStringFormat Chiamata cifrata con: %1. callNotSecured Chiamata non cifrata. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. Ora sei fuori dalla conferenza. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. Premi il tasto play per ritornare nella conferenza. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Inizia la registrazione incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. termina la registrazione incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Esegui uno snapshot incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. La video conferenza non è ancora iniziata: attendere per favore… callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. La chiamata viene registrata. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. In attesa di un altro partecipante... aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. Ora sei fuori dalla conferenza. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. Clicca su play per riprendere la conferenza. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Inizia la registrazione incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. Ferma la registrazione incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Cattura la schermata incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. La video conferenza non è ancora iniziata. Attendere… callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. Questa chiamata viene registrata. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. Parametri multimediali incallMenuLayout 'Change layout' : Menu title to change the conference layout. Modifica il layout incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. Invita i partecipanti incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. Elenco dei partecipanti incallMenuTitle 'Settings' : Main menu title for settings. Impostazioni incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. Modalità mosaico incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. Modalità partecipante attivo incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. Modalità solo audio incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. Attualmente sei l'unico in questa conferenza InfoChatRoom quitGroupButton 'Exit group' : Button label Esci dal gruppo ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Aggiungi partecipanti addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Cerca partecipanti dalla tua lista contatti per invitarli nella chat di gruppo. Explanation for inviting the selected participants into chat room participantList 'Participant list' Lista partecipanti adminStatus 'Admin' : Admin(istrator) Amministratore word for admin status chatRoomDetailsTitle "Group information" : Popup title. Informazioni gruppo popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation ANNULLA callButton 'CALL' : Button that lead to a call CHIAMA okButton 'OK' : Button that validate the popup to be redirected to the device list OK infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. Crittografia end-to-end encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." I messaggi istantanei sono criptati end-to-end. Autenticando i partecipanti è possibile aumentare il livello di sicurezza di una conversazione. Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Per farlo, chiama il contatto e segui il processo di autenticazione. Explanation process InviteFriends enterEmailLabel L'indirizzo email dell'amico messageLabel Messaggio cancel ANNULLA confirm CONFERMA inviteFriendsTitle Invita gli amici defaultMessage %1 vuole invitarti su %2! defaultSubject %1 invito forcedMessage Scarica l'applicazione sul tuo computer e inizia a chiamare e chattare con gli utenti gratuitamente. Clicca: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 Informazioni su %1 Preferences... Preferenze Services Servizi Hide %1 Nascondi %1 Hide Others Nascondi Altre Show All Mostra Tutte Quit %1 Esci da %1 MainWindow mainSearchBarPlaceholder Cerca un contatto, avvia una chiamata o una chat… contactsEntry CONTATTI autoAnswerStatus auto smartSearchBarTooltip Usa la barra di ricerca intelligente per avviare direttamente chiamate audio e video, inviare un messaggio o aggiungere un nuovo contatto. Basta inserire l'indirizzo SIP o nome utente del tuo amico. newConferenceButton Avvia una conferenza telefonica newChatRoom 'Start a chat room' : Tooltip to illustrate a button Crea una chat di gruppo hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline Nascondi Timeline openTimeline 'Open Timeline' : Tooltip for a button that open the timeline Apri Timeline openHome 'Open Home' : Tooltip for a button that open the home view Apri Home mainWindowConferencesTitle 'Meetings' : Meeting title for main window. Riunioni newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. L'URI della conferenza non è stato impostato. Devi cambiarlo nelle impostazioni del tuo account per poter creare nuove chat di gruppo. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. L'URI della video conferenza non è stato impostato. Devi cambiarlo nelle impostazioni del tuo account per poter creare nuove conferenze. MainWindowMenuBar settings Preferenze about Informazioni quit Esci checkForUpdates 'Check for updates' : Item menu for checking updates Controlla aggiornamenti MainWindowTopMenuBar settings Preferenze about Circa quit Provando checkForUpdates 'Check for updates' : Item menu for checking updates Controlla aggiornamenti ManageAccounts ok OK selectPresenceLabel Stato di presenza selectAccountLabel Account attivo MultimediaParametersDialog ok Ok menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. Parametri multimediali NewChatRoom cancelButton 'Cancel' : Cancel button ANNULLA startButton 'Launch' : Start button AVVIA missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Devi compilare l'oggetto. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. è richiesto almeno %1 partecipante. Sono richiesti almeno %1 partecipanti. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. Per creare una chat di gruppo è necessario impostare la URI del gruppo nelle impostazioni del tuo account. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room Avvia una chat di gruppo askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. Vuoi criptare la tua chat? subjectLabel 'Subject' : Label of a text field about the subject of the chat room Oggetto subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Oggetto attuale della Chat di Gruppo. Non può essere vuoto. Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Seleziona partecipanti participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' Cerca tra i tuoi contatti o aggiungine uno alla chat di gruppo. adminStatus 'Admin' : Admin(istrator) Amministratore word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Elimina questo partecipante dalla selezione This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Obbligatorio subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Inserisci un oggetto LastContactsTitle 'Last contacts' : Header for showing last contacts Contatti recenti NewConference cancelButton 'Cancel' : Cancel button ANNULLA missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Devi compilare l'oggetto. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. è richiesto almeno %1 partecipante. Sono richiesti almeno %1 partecipanti. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Per creare una chat di gruppo è necessario impostare la URI del gruppo nelle impostazioni del tuo account. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference Inizia una videoconferenza subjectLabel 'Subject' : Label of a text field about the subject of the conference Oggetto subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Inserisci un oggetto subjectTooltip 'Current subject of the Meeting. It cannot be empty' Oggetto attuale della Conferenza. Non può essere vuoto. Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. Vuoi criptare la conferenza? participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Seleziona partecipanti participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' Cerca tra i tuoi contatti o inseriscine uno nella conferenza. adminStatus 'Admin' : Admin(istrator) Amministratore word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Rimuovi questo partecipante dalla selezione This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Obbligatorio launchButton 'Launch' : Launch button Lancia updateButton 'Update' : Update button Aggiorna updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. Aggiorna la conferenza newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. Vuoi pianificare la tua conferenza? newConferenceDate 'Date' : Date label. Data newConferenceTimeTitle 'Time' : Time label. Ora newConferenceDurationTitle 'Duration' : Duration label. Durata newConferenceTimezoneTitle 'Timezone' : Timezone label. Fuso orario newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference Aggiungi una descrizione newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description Descrizione newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting La descrizione descriverà la conferenza newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. Invita tramite %1 newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. Invita tramite Email busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. Sei entrato nel gruppo conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. Hai abbandonato il gruppo conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. %1 si è aggiunto conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group %1 ha abbandonato conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody Ora %1 è un amministratore conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody %1 non è più un amministratore conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. Livello di sicurezza ridotto da %1 conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time I messaggi effimeri sono stati abilitati: %1 conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. I messaggi effimeri sono stati disabilitati conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. Nuovo oggetto: %1 conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time I messaggi effimeri sono stati aggiornati: %1 unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. %1 messaggio non letto %1 messaggi non letti Notifier newVersionAvailable È disponibile una nuova versione (%1)! newFileMessage Nuovo allegato ricevuto! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. E' stato ricevuto un nuovo messaggio! OnlineInstallerDialog confirm CONFERMA onlineInstallerExtractingDescription Estrazione di %1… onlineInstallerDownloadingDescription Download %1… onlineInstallerFinishedDescription %1 è ora installato! onlineInstallerFailedDescription Impossibile installare %1! OutgoingMessage messageError Errore messageRead Leggi messageDelivered Consegnato ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices Dispositivi della conversazione ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Aggiungi partecipanti addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Cerca partecipanti nella tua rubrica per invitarli nella chat di gruppo. Explanation for inviting the selected participants into chat room participantList 'Participant list' Lista partecipanti adminStatus 'Admin' : Admin(istrator) Amministratore word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. Rimuovi questo partecipante dalla lista ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) (Amministratore) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Me Presence presenceOnline Disponibile presenceBusy Occupato presenceDoNotDisturb Non disturbare presenceOffline Offline QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. Abilita LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. Disabilita LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. Automatico SettingsAdvanced logsTitle Log logsFolderLabel Cartella dei log sendLogs MANDA I LOG logsUploadUrlLabel URL del server per l'upload dei log logsUploadFailed Impossibile caricare i log. logsEnabledLabel Abilita i log cleanLogs CANCELLA I LOG cleanLogsDescription Sei sicuro di voler rimuovere tutti i log? developerSettingsTitle Impostazioni per sviluppatori developerSettingsEnabledLabel Abilita impostazioni per sviluppatori logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Impossibile trovare il mailer ma i log sono stati caricati su %1 logsMailerSuccess I log sono stati caricati a %1 contactsTitle Connettore Rubrica noPlugin 'No Plugins to load' : Text in combobox Nessun Plugin da caricare viewlogs VISTA SettingsAudio audioTitle Parametri Audio playbackDeviceLabel Dispositivo di riproduzione captureDeviceLabel Dispositivo di acquisizione ringerDeviceLabel Dispositivo per la suoneria ringLabel Squilla echoCancellationLabel Abilita l'eliminazione dell'eco audioCodecsTitle Codec Audio showAudioCodecsLabel Mostra i codec audio playbackGainLabel Guadagno di riproduzione captureGainLabel Acquisizione guadagno audioTestLabel Livello di acquisizione audioSettingsInCallWarning Chiamata audio in corso: alcune impostazioni non sono disponibili. echoCancellationCalibrationLabel Calibrazione calibratingEchoCancellationInProgress ...calibrazione in corso… calibratingEchoCancellationDone Calibrato a %1ms calibratingEchoCancellationFailed Calibrazione fallita calibratingEchoCancellationNone Echo non rilevato SettingsCallsChat fileServerLabel File server encryptWithLimeLabel Cifrato con LIME limeDisabled Disabilitato limeRequired Obbligatorio limePreferred Preferito chatTitle Chat callsTitle Chiamate encryptionLabel Cifratura noEncryption Nessuno autoAnswerLabel Risposta automatica autoAnswerDelayLabel Ritardo (in ms) autoAnswerWithVideoLabel Risposta automatica (con video) chatEnabledLabel Abilita chat callRecorderEnabledLabel Abilita registrazione chiamata chatNotificationSoundEnabledLabel Abilita il suono di notifica chatNotificationSoundLabel Suono di notifica conferenceEnabledLabel Abilita conferenza contactsTitle Contatti contactsEnabledLabel Abilita i contatti muteMicrophoneEnabledLabel Abilita microfono muto outgoingCallsEnabledLabel Abilita chiamate in uscita showTelKeypadAutomaticallyLabel Mostra automaticamente la tastiera del telefono automaticallyRecordCallsLabel Registra automaticamente le chiamate keepCallsWindowInBackgroundLabel Mantieni la finestra chiamate in background callPauseEnabledLabel Chiamata in pausa abilitata encryptionMandatoryLabel La crittografia è obbligatoria hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms Nascondi chat vuote waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered Chiama dopo la registrazione chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. Abilita notifiche AutoDownload 'Auto download' : Label for a slider about auto download mode Download automatico autoDownloadNever 'Never' : auto download mode description for deactivated feature. Mai autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. Sempre callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer Nuovo server SettingsLdapEdit cancel Annulla confirm Conferma displayNameLabel Nome visualizzato displayNameTooltip Nome del server da visualizzare sulla lista. connectionTitle Connessione serverLabel URL del server serverTooltip Server LDAP. es: ldap:/// per un server locale oppure ldap://ldap.example.org/ bindDNLabel Bind DN bindDNTooltip passwordLabel Password useTLSLabel Usa TLS useTLSTooltip useSalLabel Usa SAL useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' %1 usa SAL per la risoluzione DNS. Passerà l'indirizzo IP a LDAP. Facendo cosi la negoziazione TLS non può controllare il nome dell'host. Si possono disattivare le verifiche se si vuole forzare la connessione. verifyTLSLabel Verifica i certificati su TLS AutoMode Auto offMode Off onMode On verifyTLSTooltip Specifica se il certificato del server TLS deve essere verificato quando si effettua una connessione al server LDAP. searchTitle Cerca baseObjectLabel baseObjectPlaceholder baseObjectTooltip filterLabel Filtro filterTooltip La ricerca si basa su questo filtro quando si cercano contatti.<br>Valore di default: (sn=%s) maxResultsLabel Massimo numero di risultati maxResultsTooltip Massimo numero di risultati quando si effettua una ricerca. timeoutLabel Scadenza timeoutTooltip Scadenza del tempo di connessione e ricerca in secondi. Deve essere un numero positivo.<br>Se omesso è 5s. parsingTitle Parsing nameAttributesLabel Attributi del nome nameAttributesTooltip Controlla questi attributi per costruire il nome del contatto, separati da una virgola usando il primo con la priorità più alta.<br> Il valore di default è: sn sipAttributesLabel Attributi SIP sipAttributesTooltip domainLabel Dominio domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. Aggiunge il dominio all'indirizzo SIP (sip:nomeutente@dominio). miscLabel Misc debugLabel Debug debugTooltip Registra logs dettagliati quando si effettuano transazioni (utili per il debugging delle connessioni TLS) SettingsNetwork sendDtmfsLabel Metodo di invio DTMF allowIpV6Label Abilita IPv6 transportTitle Trasporto natAndFirewallTitle NAT e Firewall enableIceLabel Abilita ICE stunServerLabel Server STUN/TURN enableTurnLabel Abilita TURN turnUserLabel Utente TURN turnPasswordLabel Password TURN networkProtocolAndPortsTitle Protocollo di rete e porte sipUdpPortLabel Porta UDP SIP sipTcpPortLabel Porta TCP SIP audioRtpUdpPortLabel Porta UDP audio RTP videoRtpUdpPortLabel Porta UDP video RTP dscpFieldsTitle Campi DSCP sipFieldLabel SIP audioRtpStreamFieldLabel Stream RTP audio videoRtpStreamFieldLabel Stream RTP video bandwidthControlTitle Controllo della larghezza di banda downloadSpeedLimitLabel Limite di velocità di download in Kbit/sec uploadSpeedLimitLabel Limite di velocità di upload in Kbit/sec enableAdaptiveRateControlLabel Abilita controllo adattivo della velocità presenceTitle Presenza rlsUriLabel Usa URI RLS rlsUriAuto AUTO rlsUriDisabled MAI showNetworkSettingsLabel Mostra le impostazioni di rete generalTitle Generale SettingsSipAccounts defaultIdentityTitle Identità predefinita defaultUsernameLabel Nome utente defaultSipAddressLabel Indirizzo SIP proxyAccountsTitle Account proxy eraseAllPasswords CANCELLA LE PASSWORD addAccount AGGIUNGI ACCOUNT editHeader Modifica deleteHeader Cancella deleteAccountDescription Sei sicuro di voler eliminare questo account? eraseAllPasswordsDescription Sei sicuro di voler cancellare tutte le password? defaultDisplayNameLabel Nome da visualizzare assistantTitle Assistente createAppSipAccountEnabledLabel Abilita la creazione dell'account useAppSipAccountEnabledLabel Abilita l'utilizzo dell'account useOtherSipAccountEnabledLabel Abilita l'utilizzo dell'account generico fetchRemoteConfigurationEnabledLabel Abilita il recupero della configurazione assistantSupportsPhoneNumbersLabel Supporta i numeri di telefono defaultDeviceNameLabel 'Device Name' : Label for setting the device name. Nome dispositivo webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. Indirizzo registrazione webviewLoginUrlLabel 'Login URL' : Label for login URL. Indirizzo di accesso SettingsSipAccountsEdit sipAddressLabel Indirizzo SIP transportLabel Trasporto serverAddressLabel Indirizzo del server SIP registrationDurationLabel Durata della registrazione (sec) routeLabel Instradamento contactParamsLabel Parametri di contatto publishPresenceLabel Pubblica informazioni sulla presenza avpfIntervalLabel Intervallo regolare RTCP di AVPF (sec) registerEnabledLabel Registra avpfEnabledLabel Abilita AVPF cancel ANNULLA confirm CONFERMA invalidSipAddress Indirizzo SIP non valido. invalidServerAddress Indirizzo del server non valido. invalidRoute Instradamento non valido. enableIceLabel Abilita ICE stunServerLabel Server STUN/TURN enableTurnLabel Abilita TURN turnUserLabel Utente TURN turnPasswordLabel Password TURN natAndFirewallTitle NAT e Firewall mainSipAccountSettingsTitle Impostazioni dell'account SIP principale conferenceURI "Conference URI" : Label of a text edit for filling Conference URI Indirizzo conferenza invalidConferenceURI "invalid conference URI" : Error text about conference URI Indirizzo conferenza invalido videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) Stato del tunnel tunnelDomain 'Domain' : Field title of a textfield to set domain. Dominio tunnelUsername 'Username' : Field title of a textfield to set username. Nome utente tunnelSIP 'SIP' : Field title of a switch to set SIP mode. SIP cancel 'Cancel' : Button to cancel the action. Annulla setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. Inserisci proxy HTTP proxyHttpHost 'Host' : Placeholder to set hostname. Ospite proxyHttpPort 'Port' : Placehoilder to set port. Porta proxyHttpUsername 'Username' : Placeholder to set username. Nome utente proxyHttpPassword 'Password' : Placeholder to set password. Password proxyHttpApply 'Apply' : Button to set proxy from changes. Applica serverMode 'Mode' : Field title on form to set tunnel mode. Modalità serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. Modalità doppia serverTitle 'Server' : Title form to set a server Server serverHostname 'Hostname' : Field title on form to set hostname. Nome ospite serverPort 'Port' : Field title on form to set port. Porta serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. Indirizzo nome ospito doppio serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. Doppia porta serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. Porta UDP mirror remota serverDelay 'Delay' : Field title on form to set the delay of the tunnel. Ritardo tunnelAddServer 'Add server' : Button for adding a server Aggiungi server tunnelApply 'Apply' : Button to apply changes. Applica SettingsUi pathsTitle Percorsi savedScreenshotsLabel Cartella per gli screenshot salvati savedCallsLabel Cartella per le chiamate salvate languagesTitle Lingue languagesLabel Lingua systemLocale Impostazioni locali del sistema cleanAvatars CANCELLA AVATAR cleanAvatarsDescription Sei sicuro di voler cancellare tutti gli avatar? downloadLabel Cartella per i download setLocaleDescription È necessario riavviare l'applicazione. Vuoi riavviare ora? otherTitle Altro exitOnCloseLabel Esci dall'app alla chiusura della finestra dataTitle Dati UI autoStartLabel Autoavvio dell'app fontsTitle 'Fonts' : title of fonts section in settings Tipografia fontsTextChange 'Text Messages' : Label for changing text message fonts Messaggi di testo fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts Scegli nuova tipografia checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates Controlla aggiornamenti mipmapLabel 'Enable Mipmap' Abilita mappa Mip mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Dispositivo di input video videoFramerateLabel Framerate videoCaptureTitle Parametri di acquisizione video videoPresetLabel Preimpostazioni video presetDefault Predefinito presetHighFps FPS elevati presetCustom Personalizzato videoSizeLabel Risoluzione Video videoCodecsTitle Codec Video showCameraPreview ANTEPRIMA VIDEO showVideoCodecsLabel Mostra codec video videoSettingsInCallWarning Videochiamata in corso: alcune impostazioni non sono disponibili. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm OK SettingsWindow settingsTitle Impostazioni sipAccountsTab Account SIP audioTab Audio videoTab Video callsAndChatTab Chiamate e Chat networkTab Rete uiTab Interfaccia utente validButton OK uiAdvanced Avanzate tunnelTab 'Tunnel' : Tab title for tunnel section in settings. SipAddressDialog cancel Annulla contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list SmartSearchBar addContact AGGIUNGI UN CONTATTO Timeline timelineFilter A title for filtering mode. timelineFilterAll 'All' The mode for timelines filtering. timelineFilterCustom 'Custom' The mode for timelines filtering. timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction USA useAppSipAccountTitle USA UN ACCOUNT %1 useUsernameToLogin Usa nome utente e password anziché il tuo numero di telefono. quitWarning Il tuo account è stato creato ma non è ancora stato validato. Sei sicuro di voler uscire da questa schermata? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password UseAppSipAccountWithPhoneNumber countryLabel Paese phoneNumberLabel Numero di telefono displayNameLabel Nome visualizzato (facoltativo) UseAppSipAccountWithUsername usernameLabel Nome utente passwordLabel Password displayNameLabel Nome visualizzato (facoltativo) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form USA useOtherSipAccountTitle USA UN ACCOUNT SIP usernameLabel Nome utente displayNameLabel Nome visualizzato (facoltativo) sipDomainLabel Dominio SIP passwordLabel Password transportLabel Trasporto addOtherSipAccountError Impossibile aggiungere questo account. understandAction 'I understand' : Popup confirmation for a warning warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. Annulla startButton 'Start' : Button label for starting the conference. endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Confermare il seguente SAS con la controparte. codeA Di: codeB Il tuo contatto dovrebbe dire: Later 'Later' : Button label to do something in another time. Poi Correct 'Correct' : Button label to confirm a code. Corretto title 'Communication security' : Title of popup for ZRTP confirmation. Sicurezza delle comunicazioni country Afghanistan Afghanistan Albania Albania Algeria Algeria AmericanSamoa Samoa Americane Andorra Andorra Angola Angola Anguilla Anguilla AntiguaAndBarbuda Antigua e Barbuda Argentina Argentina Armenia Armenia Aruba Aruba Australia Australia Austria Austria Azerbaijan Azerbaigian Bahamas Bahamas Bahrain Bahrein Bangladesh Bangladesh Barbados Barbados Belarus Bielorussia Belgium Belgio Belize Belize Benin Benin Bermuda Bermuda Bhutan Bhutan Bolivia Bolivia BosniaAndHerzegowina Bosnia ed Erzegovina Botswana Botswana Brazil Brasile Brunei Brunei Bulgaria Bulgaria BurkinaFaso Burkina Faso Burundi Burundi Cambodia Cambogia Cameroon Camerun Canada Canada CapeVerde Capo Verde CaymanIslands Isole Cayman CentralAfricanRepublic Repubblica Centrafricana Chad Ciad Chile Cile China Cina Colombia Colombia Comoros Comore PeoplesRepublicOfCongo Repubblica Popolare del Congo DemocraticRepublicOfCongo Repubblica Democratica del Congo CookIslands Isole Cook CostaRica Costa Rica IvoryCoast Costa d'Avorio Croatia Croazia Cuba Cuba Cyprus Cipro CzechRepublic Repubblica Ceca Denmark Danimarca Djibouti Gibuti Dominica Dominica DominicanRepublic Repubblica Dominicana Ecuador Ecuador Egypt Egitto ElSalvador El Salvador EquatorialGuinea Guinea Equatoriale Eritrea Eritrea Estonia Estonia Ethiopia Etiopia FalklandIslands Isole Falkland FaroeIslands Isole Faroe Fiji Figi Finland Finlandia France Francia FrenchGuiana Guyana Francese FrenchPolynesia Polinesia Francese Gabon Gabon Gambia Gambia Georgia Georgia Germany Germania Ghana Ghana Gibraltar Gibilterra Greece Grecia Greenland Groenlandia Grenada Grenada Guadeloupe Guadalupa Guam Guam Guatemala Guatemala Guinea Guinea GuineaBissau Guinea-Bissau Guyana Guyana Haiti Haiti Honduras Honduras HongKong Hong Kong Hungary Ungheria Iceland Islanda India India Indonesia Indonesia Iran Iran Iraq Iraq Ireland Irlanda Israel Israele Italy Italia Jamaica Jamaica Japan Giappone Jordan Giordania Kazakhstan Kazakistan Kenya Kenya Kiribati Kiribati DemocraticRepublicOfKorea Corea del Nord RepublicOfKorea Corea del Sud Kuwait Kuwait Kyrgyzstan Kyrgyzstan Laos Laos Latvia Lettonia Lebanon Libano Lesotho Lesotho Liberia Liberia Libya Libia Liechtenstein Liechtenstein Lithuania Lituania Luxembourg Lussemburgo Macau Macao Macedonia Macedonia Madagascar Madagascar Malawi Malawi Malaysia Malaysia Maldives Maldive Mali Mali Malta Malta MarshallIslands Isole Marshall Martinique Martinica Mauritania Mauritania Mauritius Mauritius Mayotte Mayotte Mexico Messico Micronesia Micronesia Moldova Moldavia Monaco Monaco Mongolia Mongolia Montenegro Montenegro Montserrat Montserrat Morocco Marocco Mozambique Mozambico Myanmar Birmania Namibia Namibia NauruCountry Nauru Nepal Nepal Netherlands Paesi Bassi NewCaledonia Nuova Caledonia NewZealand Nuova Zelanda Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Niue NorfolkIsland Isola Norfolk NorthernMarianaIslands Isole Marianne Settentrionali Norway Norvegia Oman Oman Pakistan Pakistan Palau Palau PalestinianTerritories Territori Palestinesi Panama Panama PapuaNewGuinea Papua Nuova Guinea Paraguay Paraguay Peru Perù Philippines Filippine Poland Polonia Portugal Portogallo PuertoRico Porto Rico Qatar Qatar Reunion Riunione Romania Romania RussianFederation Russia Rwanda Ruanda SaintHelena Sant'Elena SaintKittsAndNevis Saint Kitts e Nevis SaintLucia Saint Lucia SaintPierreAndMiquelon Saint-Pierre e Miquelon SaintVincentAndTheGrenadines Saint Vincent e Grenadine Samoa Samoa SanMarino San Marino SaoTomeAndPrincipe São Tomé e Príncipe SaudiArabia Arabia Saudita Senegal Senegal Serbia Serbia Seychelles Seychelles SierraLeone Sierra Leone Singapore Singapore Slovakia Slovacchia Slovenia Slovenia SolomonIslands Isole Salomone Somalia Somalia SouthAfrica Sudafrica Spain Spagna SriLanka Sri Lanka Sudan Sudan Suriname Suriname Swaziland Swaziland Sweden Svezia Switzerland Svizzera Syria Siria Taiwan Taiwan Tajikistan Tagikistan Tanzania Tanzania Thailand Thailandia Togo Togo Tokelau Tokelau Tonga Tonga TrinidadAndTobago Trinidad e Tobago Tunisia Tunisia Turkey Turchia Turkmenistan Turkmenistan TurksAndCaicosIslands Turks e Caicos Tuvalu Tuvalu Uganda Uganda Ukraine Ucraina UnitedArabEmirates Emirati Arabi Uniti UnitedKingdom Regno Unito UnitedStates Stati Uniti d'America Uruguay Uruguay Uzbekistan Uzbekistan Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietnam WallisAndFutunaIslands Wallis e Futuna Yemen Yemen Zambia Zambia Zimbabwe Zimbabwe utils downloadCodecDescription Vuoi scaricare %1 (%2)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/ja.ts000066400000000000000000005223561434616504300236300ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name ActivateAppSipAccountWithEmail activateAppSipAccount %1アカウントを有効にする confirmAction アクティブにします activationSteps アカウントを有効化するには:%1 に送信された指示に従い、下をクリックしてください。 ActivateAppSipAccountWithPhoneNumber activateAppSipAccount %1アカウントを有効にする confirmAction アクティブにします activationSteps 検証コードを含む SMS が %1 に送信されました。 電話番号の確認を完了するには、以下の 4 桁のコードを入力してください。 App commandLineOptionVerbose 実行中にデバッグ情報を標準出力へ出力する commandLineOptionConfig 使用する %1 構成ファイルを指定 applicationDescription 無料(自由な)SIPビデオ電話。 commandLineOptionIconified システムトレイで起動し、メインインターフェイスを表示しない commandLineOptionConfigArg ファイル commandLineOptionHelp ヘルプを表示する commandLineOptionVersion アプリケーションバージョンを表示する commandLineOptionCliHelp CLI で %1 を使用するためのヘルプメニューを表示します。 commandLineDescription CLIからアプリケーションを操作する restore [復元] quit 終了 settings 設定 about バージョン情報 commandLineOptionFetchConfig commandLineOptionFetchConfigArg commandLineOptionCall commandLineOptionCallArg checkForUpdates AssistantAbstractView back 戻る AssistantHome useAppSipAccount %1アカウントを使用する useOtherSipAccount SIPアカウントを使用する fetchRemoteConfiguration リモート設定を取得する homeTitle ようこそ homeDescription このアシスタントはあなたの%1アカウントの設定を助けます。 createAppSipAccount %1アカウントを作成する homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. AssistantModel loginWithUsernameFailed ログインに失敗しました。ユーザ名・パスワードを確認してください。 usernameStatusTooShort 短すぎます(最低 %1文字) usernameStatusTooLong 長すぎます(最大 %1文字) usernameStatusInvalidCharacters 無効な文字が検出されました。(正規表現: `%1`) usernameStatusInvalid 無効なユーザ名. passwordStatusTooShort 短すぎます(最低 %1文字) passwordStatusTooLong 長すぎます(最大 %1文字) passwordStatusInvalidCharacters 無効な文字が検出されました。(正規表現: `%1`) passwordStatusMissingCharacters 誤った文字: `%1` requestFailed リクエストを送信できませんでした。 emailStatusMalformed メールアドレスが間違っています。 emailStatusMalformedInvalidCharacters メールアドレスが間違っているか、無効な文字があります。 cannotSendSms サーバエラー: SMSを送信できませんでした。 accountAlreadyExists このアカウントはすでに存在します。 smsActivationFailed SMS認証に失敗しました。! emailActivationFailed アカウントを確認したことを確認するか、もう一度お試しください。 phoneNumberStatusInvalid 無効な電話番号! phoneNumberStatusTooShort 短すぎます phoneNumberStatusTooLong 長すぎます phoneNumberStatusInvalidCountryCode 無効な国番号! loginWithPhoneNumberFailed ログインに失敗しました。電話番号を確認して下さい。 unableToAddAccount このアカウントを追加出来ませんでした。 AuthenticationRequest cancel キャンセル confirm ログイン identityLabel アイデンティティ passwordLabel パスワード authenticationRequestDescription 認証に失敗しました。パスワードを確認してください。 userIdLabel ユーザID(オプション) realmLabel レルム CallModel callStatsCodec コーデック callStatsUploadBandwidth 上り帯域 callStatsDownloadBandwidth 下り帯域 callStatsEstimatedDownloadBandwidth 推定ダウンロード帯域幅 callStatsIceState ICE状態 callStatsIpFamily 知的財産 callStatsSenderLossRate 送信ロスレート callStatsReceiverLossRate 受信ロスレート callStatsJitterBuffer Jitter buffer callStatsSentVideoDefinition 送信済みビデオの定義 callStatsReceivedVideoDefinition 受信したビデオの定義 iceStateNotActivated 有効ではありません iceStateFailed 失敗 iceStateInProgress 処理中 iceStateReflexiveConnection リフレクシブ結合 iceStateHostConnection ホスト接続 iceStateRelayConnection リレー接続 iceStateInvalid 無効 callErrorDeclined 相手に拒否されました。 callErrorNotFound 相手が存在しません。 callErrorBusy 相手が話し中です。 callErrorNotAcceptable 相手が通話を受け入れることが出来ません。 callStatsReceivedFramerate 受信フレームレート callStatsSentFramerate 送信フレームレート callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel キャンセル callSipAddressDescription 新しい通話を始める. CallStatistics audioStatsLabel 音声 videoStatsLabel 映像 mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section CallTransfer cancel キャンセル callTransferDescription この通話を転送しますか? Calls acceptAudioCall 音声通話を受け入れる acceptVideoCall ビデオ通話を受け入れる terminateCall 電話を切る resumeCall 電話を再開する transferCall 電話を転送する callPause 電話を保留する attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CallsWindow callsTitle 通話 acceptClosingDescription 全ての通話を終了してもよろしいですか? Chat newMessagePlaceholder メッセージを入力してください noFileTransferUrl ファイルを送信できませんでした。 サーバURLが設定されていません。 chatTyping '%1 is typing...' indicate that someone is composing in chat allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. conferencesCopiedICS ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. icsDescription 'Description' : Title for the meeting description. icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. icsJoinButton 'Join' : Action button to join the meeting. deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. icsJoinButton 'Join' : Action button to join the meeting. icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. icsParticipants '%1 participant' : number(=%1) of participant. icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) fileTransferDownload 'Download' : Message link to download a file ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard menuCopy 'Copy' : Text menu to copy selected text in message into clipboard -了解 menuPlayMe 再生する! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message menuDelete 'Delete' : Item menu to delete a message menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message menuForward 'Forward' : Forward a message from menu menuReply 'Reply' : Reply to a message from menu ChatNoticeModel nMinute nHour nDay nWeek ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Cli appCliDescription コマンドラインから %1 アプリケーションを制御する方法。 uriCommandLineSyntax %1 <sip-address>?method=<方法>([&<argument>=<base64-value>]*) cliCommandLineSyntax %1 "<method> ([<argument>=<value>]*)" commandsName コマンドリスト: showFunctionDescription アプリケーションのメイン画面を表示する. callFunctionDescription SIPアドレスへの呼び出しを開始します。 initiateConferenceFunctionDescription 会議を開始する. joinConferenceFunctionDescription 表示名として、sip-address が主催する会議に参加します。 プロキシ設定に接続している場合は、「参加会議」を参照してください。 joinConferenceAsFunctionDescription ゲスト-sip-address と同様に、sip-address が主催する会議に参加します。 プロキシ設定に接続していない場合は、「参加会議」を参照してください。 byeFunctionDescription CodecsViewer codecMime 名前 codecEncoderDescription 説明 codecEncoderClockRate レート (Hz) codecBitrate ビットレート (Kbit/s) codecRecvFmtp パラメータ codecStatus 状態 Conference conferenceTitle 会議 ConferenceControls conference 会議 ConferenceManager conferenceManagerDescription 会議の参加者を管理します。 cancel キャンセル confirm 開始 Conferences conferencesTitle 'Meetings' : Conference list title. conferencesEndedFilter 'Finished' : Filter meetings on end status. conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel キャンセル confirm 確認 ContactEdit removeContactDescription あなたのアドレス帳からこの連絡先を削除してもよろしいですか? sipAccounts SIPアカウント address アドレス emails メールアドレス webSites Webサイト avatarChooserTitle アバターを選択してください companies 企業 save 保存 cancel キャンセル sipAccountsPlaceholder SIPアカウント companiesPlaceholder 会社 emailsPlaceholder メールアドレス webSitesPlaceholder Webサイト street 通り postalCode 郵便番号 country locality 局所性 abortEditDescriptionText 連絡先の変更をキャンセルしてもよろしいですか? tooltipShowConversation 会話を表示 missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Contacts searchContactPlaceholder 連絡先を検索 selectAllContacts 全て selectConnectedContacts 接続済 addContact 連絡先に追加 removeContactDescription あなたのアドレス帳からこの連絡先を削除してもよろしいですか? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Conversation displayCallsAndMessages 全て displayCalls を呼び出します displayMessages メッセージ removeAllEntriesDescription 履歴をクリアしてよろしいですか? tooltipContactEdit 連絡先の編集 tooltipContactAdd 連絡先の追加 cleanHistory 履歴の削除 adminStatus 'Admin' : Admin(istrator) One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list conversationMenuDelete 'Delete history' : Item menu to delete the chat's history conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription どのようにアカウントを作成しますか? createAppSipAccountTitle %1アカウントを作成する withPhoneNumber 電話番号を利用 withEmailAddress メールアドレスを利用 CreateAppSipAccountWithEmail createAppSipAccountTitle %1アカウントを作成 confirmAction 作成 usernameLabel ユーザ名 emailLabel メールアドレス passwordLabel パスワード passwordConfirmationLabel パスワード(確認) passwordConfirmationError 入力されたパスワードが一致しません。 quitWarning アカウントは作成されましたが、まだ確認されていません。この画面を終了してもよろしいですか? displayNameLabel 表示名(オプション) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle %1アカウントを作成する countryLabel phoneNumberLabel 電話番号 usernameLabel ユーザ名 displayNameLabel 表示名(オプション) confirmAction 作成 quitWarning アカウントは作成されましたが、まだ確認が完了していません。このビューを終了するには、24時間以内に手動でアカウントを追加して検証する必要があります。 DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle 1 つまたは複数のファイルを選択してください dropYourAttachment 添付ファイルをドロップする attachmentTooltip ファイルを送信 EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation startButton 'start' : button text to start ephemeral mode ephemeralTitle "Ephemeral messages" : Popup title for ephemerals ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Warning about not being in conference based chat room. disabled 'Disabled' nMinute '%1 minute' nHour '%1 hour' nDay '%1 day' nWeek '%1 week' Event incomingCall 着信 outgoingCall 発信 declinedIncomingCall 拒否された着信通話 declinedOutgoingCall 拒否された発信通話 endedCall 通話終了 missedIncomingCall 不在着信 missedOutgoingCall 不在の発信 FetchRemoteConfiguration confirmAction フェッチ fetchRemoteConfigurationTitle リモート設定の取得 urlLabel URL remoteProvisioningError リモートプロビジョニングファイルがセット出来ませんでした。 remoteProvisioningUpdateDescription アプリケーションを再起動する必要があります。今すぐ再起動しますか? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription 履歴をクリアしてよろしいですか? tooltipContactEdit 連絡先の編集 tooltipContactAdd 連絡先の追加 cleanHistory 履歴の削除 Home howToDescription %1の使い方にヘルプが必要ですか? howToTitle %1 の使用方法 inviteDescription あなたの友達に%1を紹介. inviteTitle あなたの友人を招待する accountAssistantDescription %1アカウントを作成/管理する. accountAssistantTitle アカウントアシスタント assistantButton アシスタント showTooltips ツールチップを表示 inviteButton 招待 Incall acceptVideoDescription あなたの連絡先がビデオをオンにしました。 securedStringFormat 通話は %1 で暗号化されています。 callNotSecured 通話は暗号化されていません。 incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. incallMenuTitle 'Settings' : Main menu title for settings. incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status chatRoomDetailsTitle "Group information" : Popup title. popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation callButton 'CALL' : Button that lead to a call okButton 'OK' : Button that validate the popup to be redirected to the device list infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Explanation process InviteFriends enterEmailLabel 友達のメールアドレス messageLabel メッセージ cancel キャンセル confirm 確認 inviteFriendsTitle 友達を招待 defaultMessage %1 があなたを%2に招待しています! defaultSubject %1への招待 forcedMessage お使いのコンピュータにアプリケーションをダウンロードして、通話を開始し、無料でユーザーとチャットします。 ここをクリック:<a href="%1">%1</a> MAC_APPLICATION_MENU About %1 %1 バージョン情報 Preferences... 設定 Services サービス Hide %1 %1 を隠す Hide Others 他の人を隠す Show All すべて表示する Quit %1 %1 を終了 MainWindow mainSearchBarPlaceholder 連絡先を検索するか、通話もしくはチャットを始める… contactsEntry 連絡先 autoAnswerStatus 自動 smartSearchBarTooltip インテリジェントな検索バーを使用してオーディオとビデオを直接開始 を呼び出したり、メッセージを送信したり、新しい連絡先を追加したりできます。 入力するだけです あなたの友人のSIPアドレスまたはユーザー名。 newConferenceButton 電話会議の開始 newChatRoom 'Start a chat room' : Tooltip to illustrate a button hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline openTimeline 'Open Timeline' : Tooltip for a button that open the timeline openHome 'Open Home' : Tooltip for a button that open the home view mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings 設定 about バージョン情報 quit 終了 checkForUpdates 'Check for updates' : Item menu for checking updates MainWindowTopMenuBar settings 環境設定 about バージョン情報 quit やめろ checkForUpdates 'Check for updates' : Item menu for checking updates ManageAccounts ok OK selectPresenceLabel プレゼンス状態 selectAccountLabel 有効なアカウント MultimediaParametersDialog ok OK menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button startButton 'Launch' : Start button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. subjectLabel 'Subject' : Label of a text field about the subject of the chat room subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject LastContactsTitle 'Last contacts' : Header for showing last contacts NewConference cancelButton 'Cancel' : Cancel button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject subjectTooltip 'Current subject of the Meeting. It cannot be empty' Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. Notifier newVersionAvailable 新しいバージョン (%1) が利用可能です! newFileMessage 新しい添付ファイルが届きました! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm 確認する onlineInstallerExtractingDescription %1 を抽出しています... onlineInstallerDownloadingDescription %1 をダウンロードしています... onlineInstallerFinishedDescription %1 がインストールされました! onlineInstallerFailedDescription %1 のインストールに失敗しました! OutgoingMessage messageError エラー messageRead 読む messageDelivered 配信済 ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline 利用可能 presenceBusy 話中 presenceDoNotDisturb 不在 presenceOffline オフライン QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. SettingsAdvanced logsTitle ログ logsFolderLabel ログフォルダー sendLogs ログを送信 logsUploadUrlLabel ログのアップロード先URL logsUploadFailed ログのアップロードに失敗しました logsEnabledLabel ログ 有効 cleanLogs ログを削除する cleanLogsDescription 全てのログを削除してもよろしいですか? developerSettingsTitle 開発者設定 developerSettingsEnabledLabel 開発者設定を有効にする logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) メーラは見つかりませんが、ログが %1 にアップロードされました logsMailerSuccess ログが %1 にアップロードされました contactsTitle 連絡先 noPlugin 'No Plugins to load' : Text in combobox viewlogs SettingsAudio audioTitle オーディオ設定 playbackDeviceLabel 再生デバイス captureDeviceLabel キャプチャーデバイス ringerDeviceLabel 着信音デバイス ringLabel 着信音 echoCancellationLabel エコーキャンセラ-を有効にする audioCodecsTitle オーディオコーデック showAudioCodecsLabel オーディオコーデックを表示する playbackGainLabel 再生ゲイン captureGainLabel キャプチャーゲイン audioTestLabel キャプチャ・レベル audioSettingsInCallWarning 音声通話中:一部の設定が使用できません。 echoCancellationCalibrationLabel calibratingEchoCancellationInProgress calibratingEchoCancellationDone calibratingEchoCancellationFailed calibratingEchoCancellationNone SettingsCallsChat fileServerLabel ファイルサーバ encryptWithLimeLabel LIME で暗号化 limeDisabled 無効 limeRequired 必須 limePreferred 優先 chatTitle チャット callsTitle 通話 encryptionLabel 暗号化 noEncryption 無し autoAnswerLabel 自動応答 autoAnswerDelayLabel 応答までの時間(ミリ秒) autoAnswerWithVideoLabel 自動応答(映像) chatEnabledLabel チャットを有効にする callRecorderEnabledLabel コールレコーダを有効にする chatNotificationSoundEnabledLabel 通知音を有効にする chatNotificationSoundLabel 通知音 conferenceEnabledLabel 会議を有効にする contactsTitle 連絡先 contactsEnabledLabel 連絡先の有効化 muteMicrophoneEnabledLabel マイクのミュートを有効にする outgoingCallsEnabledLabel 発信コールを有効にする showTelKeypadAutomaticallyLabel 電話のキーパッドを自動的に表示する automaticallyRecordCallsLabel 通話を自動的に録音する keepCallsWindowInBackgroundLabel 通話ウィンドウをバックグラウンドに保持 callPauseEnabledLabel 保留コールの有効化 encryptionMandatoryLabel 暗号化を必須にする hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer SettingsLdapEdit cancel キャンセル confirm displayNameLabel 表示名(オプション) displayNameTooltip connectionTitle serverLabel serverTooltip bindDNLabel bindDNTooltip passwordLabel パスワード useTLSLabel useTLSTooltip useSalLabel useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' verifyTLSLabel AutoMode offMode onMode verifyTLSTooltip searchTitle baseObjectLabel baseObjectPlaceholder baseObjectTooltip filterLabel filterTooltip maxResultsLabel maxResultsTooltip timeoutLabel timeoutTooltip parsingTitle nameAttributesLabel nameAttributesTooltip sipAttributesLabel sipAttributesTooltip domainLabel domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. miscLabel debugLabel debugTooltip SettingsNetwork sendDtmfsLabel DTMF送信方法 allowIpV6Label IPv6を有効にする transportTitle トランスポート natAndFirewallTitle NAT and Firewall enableIceLabel ICEを有効にする stunServerLabel STUN/TURN server enableTurnLabel TURNを有効にする turnUserLabel TURNユーザ turnPasswordLabel TURNパスワード networkProtocolAndPortsTitle ネットワークプロトコルとポート sipUdpPortLabel SIP UDPポート sipTcpPortLabel SIP TCPポート audioRtpUdpPortLabel オーディオRTP UDPポート videoRtpUdpPortLabel ビデオRTP UDPポート dscpFieldsTitle DSCPフィールド sipFieldLabel SIP audioRtpStreamFieldLabel 音声RTPストリーム videoRtpStreamFieldLabel ビデオRTPストリーム bandwidthControlTitle 帯域制御 downloadSpeedLimitLabel ダウンロード速度制限 Kbit/sec uploadSpeedLimitLabel アップロード速度制限 Kbit/sec enableAdaptiveRateControlLabel 適応型レート制御を有効にする presenceTitle プレゼンス rlsUriLabel RLS URIを使う rlsUriAuto 自動 rlsUriDisabled 決して showNetworkSettingsLabel ネットワーク設定を表示する generalTitle 一般 SettingsSipAccounts defaultIdentityTitle デフォルトのアイデンティティ defaultUsernameLabel ユーザ名 defaultSipAddressLabel SIPアドレス proxyAccountsTitle Proxyアカウント eraseAllPasswords パスワード消去 addAccount アカウントの追加 editHeader 編集 deleteHeader 削除 deleteAccountDescription アカウントを削除してもよろしいですか? eraseAllPasswordsDescription パスワードを消してもよろしいですか? defaultDisplayNameLabel 表示名 assistantTitle アシスタント createAppSipAccountEnabledLabel アカウント作成を有効にする useAppSipAccountEnabledLabel アカウントの使用を有効にする useOtherSipAccountEnabledLabel 一般的なアカウントの使用を有効にする fetchRemoteConfigurationEnabledLabel 構成フェッチの有効化 assistantSupportsPhoneNumbersLabel 電話番号をサポート defaultDeviceNameLabel 'Device Name' : Label for setting the device name. webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel SIPアドレス transportLabel トランスポート serverAddressLabel SIPサーバ アドレス registrationDurationLabel 登録間隔(秒) routeLabel ルート contactParamsLabel Contactパラメータ publishPresenceLabel プレゼンス情報を公開する avpfIntervalLabel AVPF 通常のRTCP間隔(秒) registerEnabledLabel 登録 avpfEnabledLabel AVPFを有効にする cancel キャンセル confirm 確認 invalidSipAddress 無効なSIPアドレス invalidServerAddress 無効なサーバアドレスです。 invalidRoute 無効なルート enableIceLabel ICE を有効にする stunServerLabel STUN/URNサーバ enableTurnLabel TURNを有効にする turnUserLabel TURNユーザー turnPasswordLabel TURNパスワード natAndFirewallTitle NAT とファイアウォール mainSipAccountSettingsTitle メイン SIP アカウント設定 conferenceURI "Conference URI" : Label of a text edit for filling Conference URI invalidConferenceURI "invalid conference URI" : Error text about conference URI videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) tunnelDomain 'Domain' : Field title of a textfield to set domain. tunnelUsername 'Username' : Field title of a textfield to set username. tunnelSIP 'SIP' : Field title of a switch to set SIP mode. cancel 'Cancel' : Button to cancel the action. キャンセル setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. proxyHttpHost 'Host' : Placeholder to set hostname. proxyHttpPort 'Port' : Placehoilder to set port. proxyHttpUsername 'Username' : Placeholder to set username. proxyHttpPassword 'Password' : Placeholder to set password. proxyHttpApply 'Apply' : Button to set proxy from changes. serverMode 'Mode' : Field title on form to set tunnel mode. serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. serverTitle 'Server' : Title form to set a server serverHostname 'Hostname' : Field title on form to set hostname. serverPort 'Port' : Field title on form to set port. serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. serverDelay 'Delay' : Field title on form to set the delay of the tunnel. tunnelAddServer 'Add server' : Button for adding a server tunnelApply 'Apply' : Button to apply changes. SettingsUi pathsTitle パス savedScreenshotsLabel スクリーンショットの保存先フォルダ savedCallsLabel 保存済み通話フォルダ languagesTitle 言語 languagesLabel 言語 systemLocale システム・ロケール cleanAvatars アバターを削除 cleanAvatarsDescription アバターを削除してもよろしいですか? downloadLabel ダウンロードフォルダー setLocaleDescription アプリケーションを再起動する必要があります。今すぐ再起動しますか? otherTitle その他 exitOnCloseLabel ウィンドウを閉じるとアプリケーションを終了する dataTitle UI データ autoStartLabel アプリの自動起動 fontsTitle 'Fonts' : title of fonts section in settings fontsTextChange 'Text Messages' : Label for changing text message fonts fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates mipmapLabel 'Enable Mipmap' mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel ビデオ入力デバイス videoFramerateLabel フレームレート videoCaptureTitle ビデオキャプチャー設定 videoPresetLabel ビデオプリセット presetDefault 標準 presetHighFps 高FPS presetCustom カスタム videoSizeLabel 映像解像度 videoCodecsTitle ビデオコーデック showCameraPreview ビデオプレビュー showVideoCodecsLabel ビデオコーデックを表示する videoSettingsInCallWarning ビデオ通話中:一部の設定が使用できません。 videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm OK SettingsWindow settingsTitle 設定 sipAccountsTab SIPアカウント audioTab 音声 videoTab 映像 callsAndChatTab 通話及びチャット networkTab ネットワーク uiTab ユーザインターフェース validButton OK uiAdvanced 詳細 tunnelTab 'Tunnel' : Tab title for tunnel section in settings. SipAddressDialog cancel キャンセル contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list SmartSearchBar addContact 連絡先に追加 Timeline timelineFilter A title for filtering mode. timelineFilterAll 'All' The mode for timelines filtering. timelineFilterCustom 'Custom' The mode for timelines filtering. timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction 使う useAppSipAccountTitle %1アカウントを使用する useUsernameToLogin 電話番号ではなく、ユーザー名とパスワードを使用します。 quitWarning アカウントは作成されましたが、まだ確認されていません。この画面を終了してもよろしいですか? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password UseAppSipAccountWithPhoneNumber countryLabel phoneNumberLabel 電話番号 displayNameLabel 表示名(オプション) UseAppSipAccountWithUsername usernameLabel ユーザ名 passwordLabel パスワード displayNameLabel 表示名(オプション) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form 使う useOtherSipAccountTitle SIPアカウントを使う usernameLabel ユーザ名 displayNameLabel 表示名(オプション) sipDomainLabel SIPドメイン passwordLabel パスワード transportLabel トランスポート addOtherSipAccountError このアカウントを追加出来ませんでした。 understandAction 'I understand' : Popup confirmation for a warning warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. startButton 'Start' : Button label for starting the conference. endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. ピアと次のSASを確認します。 codeA 次のように入力します。 codeB 連絡先の内容は次のとおりです。 Later 'Later' : Button label to do something in another time. 後で Correct 'Correct' : Button label to confirm a code. title 'Communication security' : Title of popup for ZRTP confirmation. 通信セキュリティ country Afghanistan アフガニスタン Albania アルバニア Algeria アルジェリア AmericanSamoa アメリカ領サモア Andorra アンドラ Angola アンゴラ Anguilla アンギラ AntiguaAndBarbuda アンティグア・バーブーダ Argentina アルゼンチン Armenia アルメニア Aruba アルバ Australia オーストラリア Austria オーストリア Azerbaijan アゼルバイジャン Bahamas バハマ Bahrain バーレーン Bangladesh バングラデシュ Barbados バルバドス Belarus ベラルーシ Belgium ベルギー Belize ベリーズ Benin ベナン Bermuda バミューダだ Bhutan ブータン Bolivia ボリビア BosniaAndHerzegowina ボスニア・ヘルツェゴビナ Botswana ボツワナ Brazil ブラジル Brunei ブルネイ Bulgaria ブルガリア BurkinaFaso ブルキナファソ Burundi ブルンジ Cambodia カンボジア Cameroon カメルーン Canada カナダ CapeVerde カーボベルデ CaymanIslands ケイマン諸島 CentralAfricanRepublic 中央アフリカ共和国 Chad チャド Chile チリ China 中国 Colombia コロンビア Comoros コモロ PeoplesRepublicOfCongo コンゴ共和国 DemocraticRepublicOfCongo コンゴ民主共和国 CookIslands クック諸島 CostaRica コスタリカ IvoryCoast コートジボワール Croatia クロアチア Cuba キューバ Cyprus キプロス CzechRepublic チェコ共和国 Denmark デンマーク Djibouti ジブチ Dominica ドミニカか DominicanRepublic ドミニカ共和国 Ecuador エクアドル Egypt エジプト ElSalvador エルサルバドル EquatorialGuinea 赤道ギニア Eritrea エリトリア Estonia エストニア Ethiopia エチオピア FalklandIslands フォークランド諸島 FaroeIslands フェロー諸島 Fiji フィジー Finland フィンランド France フランス FrenchGuiana フランス領ギアナ FrenchPolynesia フランス領ポリネシア Gabon ガボン Gambia ガンビア Georgia ジョージア Germany ドイツ Ghana ガーナ Gibraltar ジブラルタル Greece ギリシャ Greenland グリーンランド Grenada グレナダ Guadeloupe グアドループ Guam グアム Guatemala グアテマラ Guinea ギニア GuineaBissau ギニア・ビサウ Guyana ガイアナ Haiti ハイチ Honduras ホンジュラス HongKong 香港 Hungary ハンガリー Iceland アイスランド India インド Indonesia インドネシア Iran イラン Iraq イラク Ireland アイルランド Israel イスラエル Italy イタリア Jamaica ジャマイカ Japan 日本 Jordan ジョーダン Kazakhstan カザフスタン Kenya ケニア Kiribati キリバス DemocraticRepublicOfKorea 朝鮮民主主義共和国 RepublicOfKorea 大韓民国 Kuwait クウェート Kyrgyzstan キルギス Laos ラオス Latvia ラトビア Lebanon レバノン Lesotho レソト Liberia リベリア Libya リビア Liechtenstein リヒテンシュタイン Lithuania リトアニア Luxembourg ルクセンブルク Macau マカオ Macedonia マケドニア Madagascar マダガスカル Malawi マラウイ Malaysia マレーシア Maldives モルディブ Mali マリ Malta マルタ MarshallIslands マーシャル諸島 Martinique マルティニーク Mauritania モーリタニア Mauritius モーリシャス Mayotte マヨット Mexico メキシコ Micronesia ミクロネシア Moldova モルドバ Monaco モナコ Mongolia モンゴル Montenegro モンテネグロ Montserrat モントセラト Morocco モロッコ Mozambique モザンビーク Myanmar ミャンマー Namibia ナミビア NauruCountry ナウル Nepal ネパール Netherlands オランダ NewCaledonia ニューカレドニア NewZealand ニュージーランド Nicaragua ニカラグア Niger ニジェール Nigeria ナイジェリア Niue ニウエ NorfolkIsland ノーフォーク島 NorthernMarianaIslands 北マリアナ諸島 Norway ノルウェー Oman オマーン Pakistan パキスタン Palau パラオ PalestinianTerritories パレスチナ自治区 Panama パナマ PapuaNewGuinea パプアニューギニア Paraguay パラグアイ Peru ペルー Philippines フィリピン Poland ポーランド Portugal ポルトガル PuertoRico プエルトリコ Qatar カタール Reunion 再会 Romania ルーマニア RussianFederation ロシア連邦 Rwanda ルワンダ SaintHelena セントヘレナ SaintKittsAndNevis セントクリストファー・ネイビス SaintLucia セントルシア SaintPierreAndMiquelon サンピエール島・ミクロン島 SaintVincentAndTheGrenadines セントビンセントおよびグレナディーン Samoa サモア SanMarino サンマリノ SaoTomeAndPrincipe サントメ・プリンシペ SaudiArabia サウジアラビア Senegal セネガル Serbia セルビア Seychelles セーシェル SierraLeone シエラレオネ Singapore シンガポール Slovakia スロバキア Slovenia スロベニア SolomonIslands ソロモン諸島 Somalia ソマリア SouthAfrica 南アフリカ Spain スペイン SriLanka スリランカ Sudan スーダン Suriname スリナム Swaziland スワジランド Sweden スウェーデン Switzerland スイス Syria シリア Taiwan 台湾 Tajikistan タジキスタン Tanzania タンザニア Thailand タイ Togo トーゴ Tokelau トケラウ Tonga トンガ TrinidadAndTobago トリニダード・トバゴ Tunisia チュニジア Turkey トルコ Turkmenistan トルクメニスタン TurksAndCaicosIslands タークス・カイコス諸島 Tuvalu ツバル Uganda ウガンダ Ukraine ウクライナ UnitedArabEmirates アラブ首長国連邦 UnitedKingdom イギリス UnitedStates アメリカ合衆国 Uruguay ウルグアイ Uzbekistan ウズベキスタン Vanuatu バヌアツ Venezuela ベネズエラ Vietnam ベトナム WallisAndFutunaIslands ウォリス・フトゥナ諸島 Yemen イエメン Zambia ザンビア Zimbabwe ジンバブエ utils downloadCodecDescription %1 (%2) をダウンロードしますか? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/lt.ts000066400000000000000000005225771434616504300236620ustar00rootroot00000000000000 About ok GERAI aboutTranslation 'Help us translate %1' : %1 is the application name ActivateAppSipAccountWithEmail activateAppSipAccount Aktyvuokite savo %1 SĄSKAITĄ confirmAction AKTYVUOTI activationSteps Norėdami aktyvuoti savo paskyrą: sekite jums į %1 išsiųstas instrukcijas, o tuomet spustelėkite žemiau. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount Aktyvuokite savo %1 SĄSKAITĄ confirmAction AKTYVUOTI activationSteps SMS žinutę su patvirtinimo kodu išsiuntėme į %1. Norėdami užbaigti savo telefono numerio patvirtinimą, žemiau įveskite 4 skaitmenų kodą. App commandLineOptionVerbose paleidus, registruoti kai kurią derinimo informaciją į stdout commandLineOptionConfig nurodyti norimą naudoti %1 konfigūracijos failą applicationDescription Nemokamas (laisvas) SIP vaizdo telefonas. commandLineOptionIconified paleisti sistemos dėkle, nerodyti pagrindinės sąsajos commandLineOptionConfigArg failas commandLineOptionHelp rodyti šią pagalbą commandLineOptionVersion rodyti programos versiją commandLineOptionCliHelp rodo pagalbos meniu, skirtą naudoti %1 su komandų eilute commandLineDescription siųsti programai įsakymą į komandų eilutę restore Atkurti quit Išeiti settings Nuostatos about Apie commandLineOptionFetchConfig commandLineOptionFetchConfigArg URL, Keliai or failas commandLineOptionCall commandLineOptionCallArg checkForUpdates AssistantAbstractView back ATGAL AssistantHome useAppSipAccount NAUDOTI %1 PASKYRĄ useOtherSipAccount NAUDOTI SIP PASKYRĄ fetchRemoteConfiguration GAUTI NUOTOLINĘ KONFIGŪRACIJĄ homeTitle SVEIKI homeDescription Šis pagelbiklis padės jums sukonfigūruoti ir naudoti savo %1 paskyrą. createAppSipAccount SUSIKURTI %1 PASKYRĄ homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. AssistantModel loginWithUsernameFailed Prisijungimas nepavyko. Patikrinkite savo naudotojo vardą/slaptažodį. usernameStatusTooShort Per trumpas! (mažiausiai %1 simb.) usernameStatusTooLong Per ilgas! (daugiausiai %1 simb.) usernameStatusInvalidCharacters Aptikti neteisingi simboliai. (reguliarusis reiškinys: "%1") usernameStatusInvalid Neteisingas naudotojo vardas. passwordStatusTooShort Per trumpas! (mažiausiai %1 simb.) passwordStatusTooLong Per ilgas! (daugiausiai %1 simb.) passwordStatusInvalidCharacters Aptikti neteisingi simboliai. (reguliarusis reiškinys: "%1") passwordStatusMissingCharacters Trūkstami simboliai: "%1". requestFailed Nepavyko išsiųsti užklausos. emailStatusMalformed Neteisingas el. pašto adresas. emailStatusMalformedInvalidCharacters Neteisingas el. pašto adresas arba neteisingi simboliai. cannotSendSms Serverio klaida: nepavyko išsiųsti sms. accountAlreadyExists Tokia paskyra jau yra. smsActivationFailed Aktyvavimas SMS žinute nepavyko! emailActivationFailed Įsitikinkite, kad esate patvirtinę savo paskyrą ir bandykite dar kartą. phoneNumberStatusInvalid Neteisingas telefono numeris! phoneNumberStatusTooShort Per trumpas! phoneNumberStatusTooLong Per ilgas! phoneNumberStatusInvalidCountryCode Neteisingas šalies kodas! loginWithPhoneNumberFailed Prisijungimas nepavyko. Patikrinkite savo telefono numerį. unableToAddAccount Nepavyko pridėti šios paskyros. AuthenticationRequest cancel ATSISAKYTI confirm PRISIJUNGTI identityLabel Tapatybė passwordLabel Slaptažodis authenticationRequestDescription Nepavyko nustatyti tapatybę. Patikrinkite savo slaptažodį. userIdLabel Naudotojo ID (nebūtina) realmLabel Karalystė CallModel callStatsCodec Kodekas callStatsUploadBandwidth Išsiuntimo sparta callStatsDownloadBandwidth Atsiuntimo sparta callStatsEstimatedDownloadBandwidth Apskaičiuotoji atsiuntimo sparta callStatsIceState ICE būsena callStatsIpFamily IP šeima callStatsSenderLossRate Siuntėjo nuostolių procentas callStatsReceiverLossRate Imtuvo nuostolių lygis callStatsJitterBuffer Jitter buferis callStatsSentVideoDefinition Išsiųsto vaizdo apibrėžimas callStatsReceivedVideoDefinition Gauto vaizdo apibrėžimas iceStateNotActivated Neaktyvuota iceStateFailed Nepavyko iceStateInProgress Eigoje iceStateReflexiveConnection Refleksinis ryšys iceStateHostConnection Pagrindinio kompiuterio ryšys iceStateRelayConnection Relės jungtis iceStateInvalid Neteisinga callErrorDeclined Nuotolinė šalis atsisakė skambučio. callErrorNotFound Nuotolinis vakarėlis nerastas. callErrorBusy Nuotolinis vakarėlis užimtas. callErrorNotAcceptable Nuotolinė šalis negali priimti skambučio. callStatsReceivedFramerate Gautas kadrų dažnis callStatsSentFramerate Išsiųstas kadrų dažnis callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel ATSISAKYTI callSipAddressDescription Pradėti naują skambutį. CallStatistics audioStatsLabel Garsas videoStatsLabel Vaizdas mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section CallTransfer cancel ATSISAKYTI callTransferDescription Ar norite perduoti šį skambutį? Calls acceptAudioCall Priimti garso skambutį acceptVideoCall Priimti vaizdo skambutį terminateCall Baigti skambutį resumeCall PRATĘSTI SKAMBUTĮ transferCall SIUNTIMAS callPause PRISTABDYTI SKAMBUTĮ attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CallsWindow callsTitle Skambučiai acceptClosingDescription Ar norite baigti visus skambučius? Chat newMessagePlaceholder Įrašykite savo žinutę noFileTransferUrl Nepavyko išsiųsti failą. Nesukonfigūruotas serverio url. chatTyping '%1 is typing...' indicate that someone is composing in chat allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. conferencesCopiedICS ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. icsDescription 'Description' : Title for the meeting description. icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. icsJoinButton 'Join' : Action button to join the meeting. deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. icsJoinButton 'Join' : Action button to join the meeting. icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. icsParticipants '%1 participant' : number(=%1) of participant. icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) fileTransferDownload 'Download' : Message link to download a file ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Kopijuoti menuPlayMe Žaisk su manim! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message menuDelete 'Delete' : Item menu to delete a message menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message menuForward 'Forward' : Forward a message from menu menuReply 'Reply' : Reply to a message from menu ChatNoticeModel nMinute nHour nDay nWeek ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Cli appCliDescription Būdai valdyti %1 programą per komandų eilutę. uriCommandLineSyntax %1 <sip-address>?method=<metodas>([&<argument>=<base64-value>]*) cliCommandLineSyntax %1 "<method> ([<argument>=<value>]*)" commandsName komandų sąrašas : showFunctionDescription Rodyti pagrindinį programos langą. callFunctionDescription Inicijuoti skambutį į sip adresą. initiateConferenceFunctionDescription Inicijuoti konferenciją. joinConferenceFunctionDescription Prisijunkite prie konferencijos, kuriai nurodytas sip adresas, rodomu vardu. Jei esate prisijungę prie tarpinio serverio konfigūracijos, žr. joinConferenceAsFunctionDescription Prisijunkite prie konferencijos, kuriai skirtas sip adresas, kaip su svečio sip adresu. Jei nesate prisijungę prie tarpinio serverio konfigūracijos, skaitykite prisijungimo konferenciją. byeFunctionDescription CodecsViewer codecMime Pavadinimas codecEncoderDescription Aprašas codecEncoderClockRate Dažnis (Hz) codecBitrate Pralaidumas (Kbit/s) codecRecvFmtp Parametrai codecStatus Būsena Conference conferenceTitle KONFERENCIJA ConferenceControls conference KONFERENCIJA ConferenceManager conferenceManagerDescription Tvarkykite savo konferencijos dalyvius. cancel ATSISAKYTI confirm PRADĖTI Conferences conferencesTitle 'Meetings' : Conference list title. conferencesEndedFilter 'Finished' : Filter meetings on end status. conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel ATSISAKYTI confirm PATVIRTINTI ContactEdit removeContactDescription Ar tikrai norite pašalinti šį kontaktą iš savo adresų knygos? sipAccounts SIP PASKYRA(-OS) address ADRESAS emails EL. PAŠTAS(-AI) webSites SVETAINĖ(-S) avatarChooserTitle Pasirinkite savo avatarą companies KOMPANIJOS save ĮRAŠYTI cancel ATSISAKYTI sipAccountsPlaceholder SIP paskyra companiesPlaceholder Kompanija emailsPlaceholder El. paštas webSitesPlaceholder Svetainė street Gatvė postalCode Pašto kodas country Šalis locality Vietovė abortEditDescriptionText Ar tikrai norite atšaukti kontaktų pakeitimą? tooltipShowConversation Rodyti pokalbį missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Contacts searchContactPlaceholder Ieškoti kontakto selectAllContacts Visi selectConnectedContacts Prisijungę addContact PRIDĖTI KONTAKTĄ removeContactDescription Ar tikrai norite pašalinti šį kontaktą iš savo adresų knygos? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Conversation displayCallsAndMessages VISKAS displayCalls SKAMBUČIAI displayMessages ŽINUTĖS removeAllEntriesDescription Ar tikrai norite išvalyti šią istoriją? tooltipContactEdit Redaguoti kontaktą tooltipContactAdd Pridėti kontaktą cleanHistory Ištrinti istoriją adminStatus 'Admin' : Admin(istrator) One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list conversationMenuDelete 'Delete history' : Item menu to delete the chat's history conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription Kaip norėtumėte susikurti savo paskyrą? createAppSipAccountTitle SUSIKURTI %1 PASKYRĄ withPhoneNumber NAUDOJANT TELEFONO NUMERĮ withEmailAddress NAUDOJANT EL. PAŠTO ADRESĄ CreateAppSipAccountWithEmail createAppSipAccountTitle SUSIKURTI %1 PASKYRĄ confirmAction SUKURTI usernameLabel Naudotojo vardas emailLabel El. paštas passwordLabel Slaptažodis passwordConfirmationLabel Pakartokite slaptažodį passwordConfirmationError Jūsų įvesti slaptažodžiai nesutampa. quitWarning Jūsų paskyra buvo sukurta, tačiau dar nebuvo patvirtinta. Ar tikrai norite išeiti iš šio rodinio? displayNameLabel Rodomas vardas (nebūtina) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle SUSIKURTI %1 PASKYRĄ countryLabel Šalis phoneNumberLabel Telefono numeris usernameLabel Naudotojo vardas displayNameLabel Rodomas vardas (nebūtina) confirmAction SUKURTI quitWarning Jūsų paskyra buvo sukurta, bet dar nepatvirtinta. Jei atsisakysite šio rodinio, turėtumėte rankiniu būdu pridėti ir patvirtinti savo sąskaitą per 24 valandas. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Pasirinkite vieną ar daugiau failų dropYourAttachment Vilkite priedą attachmentTooltip Siųsti failą EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation startButton 'start' : button text to start ephemeral mode ephemeralTitle "Ephemeral messages" : Popup title for ephemerals ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Warning about not being in conference based chat room. disabled 'Disabled' nMinute '%1 minute' nHour '%1 hour' nDay '%1 day' nWeek '%1 week' Event incomingCall Gaunamasis skambutis outgoingCall Išsiunčiamasis skambutis declinedIncomingCall Atmestas gaunamasis skambutis declinedOutgoingCall Atmestas išsiunčiamasis skambutis endedCall Nutrauktas skambutis missedIncomingCall Praleistas gaunamasis skambutis missedOutgoingCall Praleistas išsiunčiamasis skambutis FetchRemoteConfiguration confirmAction GAUTI fetchRemoteConfigurationTitle GAUTI NUOTOLINĘ KONFIGŪRACIJĄ urlLabel URL remoteProvisioningError Neįmanoma nustatyti šio nuotolinio aprūpinimo uri. remoteProvisioningUpdateDescription Yra būtina paleisti programą iš naujo. Ar norite tai atlikti dabar? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Ar tikrai norite išvalyti šią istoriją? tooltipContactEdit Redaguoti kontaktą tooltipContactAdd Pridėti kontaktą cleanHistory Ištrinti istoriją Home howToDescription Reikia pagalbos kaip naudotis %1? howToTitle KAIP NAUDOTIS %1 inviteDescription Pakvieskite savo draugus į %1. inviteTitle PAKVIESTI SAVO DRAUGUS accountAssistantDescription Susikurkite ar tvarkykite savo %1 paskyrą. accountAssistantTitle PASKYRŲ PAGELBIKLIS assistantButton PAGELBIKLIS showTooltips Rodyti paaiškinimus inviteButton PAKVIESTI Incall acceptVideoDescription Jūsų kontaktas norėtų įjungti vaizdą. securedStringFormat Skambutis yra šifruotas, naudojant: %1. callNotSecured Skambutis nėra šifruotas. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. incallMenuTitle 'Settings' : Main menu title for settings. incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label ok 'OK' : Button label GERAI addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status chatRoomDetailsTitle "Group information" : Popup title. popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation callButton 'CALL' : Button that lead to a call okButton 'OK' : Button that validate the popup to be redirected to the device list infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Explanation process InviteFriends enterEmailLabel Draugo el. pašto adresas messageLabel Žinutė cancel ATSISAKYTI confirm PATVIRTINTI inviteFriendsTitle Pakviesti draugus defaultMessage %1 nori pakviesti jus į %2! defaultSubject %1 pakvietimas forcedMessage Atsisiųskite programą į savo kompiuterį ir skambinkite bei kalbėkitės su naudotojais nemokamai. Spustelėkite čia: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 Preferences... Services Hide %1 Hide Others Show All Quit %1 MainWindow mainSearchBarPlaceholder Ieškoti kontaktų, pradėti skambutį ar pokalbį… contactsEntry KONTAKTAI autoAnswerStatus automatinis smartSearchBarTooltip Naudokite išmaniąją paieškos juostą, norėdami tiesiogiai pradėti garso ir vaizdo skambučius, siųsti žinutę ar pridėti naują kontaktą. Tiesiog, įveskite savo draugo SIP adresą ar naudotojo vardą. newConferenceButton Pradėti konferencinį pokalbį newChatRoom 'Start a chat room' : Tooltip to illustrate a button hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline openTimeline 'Open Timeline' : Tooltip for a button that open the timeline openHome 'Open Home' : Tooltip for a button that open the home view mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Nuostatos about Apie quit Išeiti checkForUpdates 'Check for updates' : Item menu for checking updates MainWindowTopMenuBar settings Nuostatos about Apie quit Mesti checkForUpdates 'Check for updates' : Item menu for checking updates ManageAccounts ok GERAI selectPresenceLabel Dalyvavimo būsena selectAccountLabel Aktyvi paskyra MultimediaParametersDialog ok GERAI menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button startButton 'Launch' : Start button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. subjectLabel 'Subject' : Label of a text field about the subject of the chat room subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject LastContactsTitle 'Last contacts' : Header for showing last contacts NewConference cancelButton 'Cancel' : Cancel button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject subjectTooltip 'Current subject of the Meeting. It cannot be empty' Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. Notifier newVersionAvailable Yra prieinama nauja versija (%1)! newFileMessage Gautas naujas priedas! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm PATVIRTINTI onlineInstallerExtractingDescription Išskleidžiama %1... onlineInstallerDownloadingDescription Atsiunčiama %1... onlineInstallerFinishedDescription Dabar, %1 yra įdiegta! onlineInstallerFailedDescription Nepavyko įdiegti %1! OutgoingMessage messageError Klaida messageRead Perskaityta messageDelivered Pristatyta ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline Pasiekiamas presenceBusy Užsiėmęs presenceDoNotDisturb Netrukdyti presenceOffline Atsijungęs QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. SettingsAdvanced logsTitle Žurnalai logsFolderLabel Žurnalų aplankas sendLogs SIŲSTI ŽURNALUS logsUploadUrlLabel Žurnalų įkėlimo serverio url logsUploadFailed Nepavyko įkelti žurnalų. logsEnabledLabel Žurnalai įjungti cleanLogs IŠVALYTI ŽURNALUS cleanLogsDescription Ar tikrai norite pašalinti visus žurnalus? developerSettingsTitle Kūrėjo nustatymai developerSettingsEnabledLabel Įjungti kūrėjo nustatymus logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Mailerio nerandama, bet žurnalai buvo įkelti į %1 logsMailerSuccess Žurnalai buvo įkelti į %1 contactsTitle Kontaktai noPlugin 'No Plugins to load' : Text in combobox viewlogs SettingsAudio audioTitle Garso parametrai playbackDeviceLabel Atkūrimo įrenginys captureDeviceLabel Fiksavimo įrenginys ringerDeviceLabel Skambėjimo įtaisas ringLabel Žiedas echoCancellationLabel Įjungti aido malšinimą audioCodecsTitle Garso kodekai showAudioCodecsLabel Rodyti garso kodekus playbackGainLabel Atkūrimo prieaugis captureGainLabel Užfiksuokite pelną audioTestLabel Fiksavimo lygis audioSettingsInCallWarning Vyksta garso skambutis: kai kurie nustatymai negalimi. echoCancellationCalibrationLabel calibratingEchoCancellationInProgress calibratingEchoCancellationDone calibratingEchoCancellationFailed calibratingEchoCancellationNone SettingsCallsChat fileServerLabel Failų serveris encryptWithLimeLabel Šifruoti naudojant LIME limeDisabled Išjungta limeRequired Privaloma limePreferred Pageidautina chatTitle Pokalbiai callsTitle Skambučiai encryptionLabel Šifravimas noEncryption Nėra autoAnswerLabel Automatiškai atsiliepti autoAnswerDelayLabel Delsa (ms) autoAnswerWithVideoLabel Automatinis atsakymas (su vaizdo įrašu) chatEnabledLabel Įgalinti pokalbį callRecorderEnabledLabel Įgalinti skambučių įrašymo įrenginį chatNotificationSoundEnabledLabel Įjungti pranešimų garsą chatNotificationSoundLabel Pranešimų garsas conferenceEnabledLabel Įgalinti konferenciją contactsTitle Kontaktai contactsEnabledLabel Įgalinti kontaktus muteMicrophoneEnabledLabel Įjungti mikrofono nutildymą outgoingCallsEnabledLabel Įgalinti išeinančius skambučius showTelKeypadAutomaticallyLabel Rodyti telefono klaviatūrą automatiškai automaticallyRecordCallsLabel Automatiškai įrašyti skambučius keepCallsWindowInBackgroundLabel Laikykite skambučių langus fone callPauseEnabledLabel Aktyvuoti pokalbį encryptionMandatoryLabel Šifravimas yra privalomas hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer SettingsLdapEdit cancel ATSISAKYTI confirm displayNameLabel Rodomas vardas (nebūtina) displayNameTooltip connectionTitle serverLabel serverTooltip bindDNLabel bindDNTooltip passwordLabel Slaptažodis useTLSLabel useTLSTooltip useSalLabel useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' verifyTLSLabel AutoMode offMode onMode verifyTLSTooltip searchTitle baseObjectLabel baseObjectPlaceholder baseObjectTooltip filterLabel filterTooltip maxResultsLabel maxResultsTooltip timeoutLabel timeoutTooltip parsingTitle nameAttributesLabel nameAttributesTooltip sipAttributesLabel sipAttributesTooltip domainLabel domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. miscLabel debugLabel debugTooltip SettingsNetwork sendDtmfsLabel DTMF siuntimo būdas allowIpV6Label Leisti IPv6 transportTitle Perdavimas natAndFirewallTitle NAT ir užkarda enableIceLabel Įjungti ICE stunServerLabel STUN/TURN serveris enableTurnLabel Įjungti TURN turnUserLabel TURN naudotojas turnPasswordLabel TURN slaptažodis networkProtocolAndPortsTitle Tinklo protokolas ir prievada sipUdpPortLabel SIP UDP prievadas sipTcpPortLabel SIP TCP prievadas audioRtpUdpPortLabel Garso RTP UDP prievadas videoRtpUdpPortLabel Vaizdo RTP UDP prievadas dscpFieldsTitle DSCP laukai sipFieldLabel SIP audioRtpStreamFieldLabel Garso RTP srautas videoRtpStreamFieldLabel Vaizdo RTP srautas bandwidthControlTitle Siuntimo spartos valdymas downloadSpeedLimitLabel Atsiuntimo spartos riba, Kbit/sek. uploadSpeedLimitLabel Išsiuntimo spartos riba, Kbit/sek. enableAdaptiveRateControlLabel Įgalinti adaptyviosios normos valdymą presenceTitle Buvimas rlsUriLabel Naudoti RLS URI rlsUriAuto AUTOMATINIS rlsUriDisabled NIEKADA showNetworkSettingsLabel Rodyti tinklo nustatymus generalTitle Bendra SettingsSipAccounts defaultIdentityTitle Numatytoji tapatybė defaultUsernameLabel Naudotojo vardas defaultSipAddressLabel SIP adresas proxyAccountsTitle Įgaliotojo serverio paskyros eraseAllPasswords IŠTRINTI SLAPTAŽODŽIUS addAccount PRIDĖTI PASKYRĄ editHeader Taisyti deleteHeader Ištrinti deleteAccountDescription Ar tikrai norite ištrinti šią paskyrą? eraseAllPasswordsDescription Ar tikrai norite ištrinti visus slaptažodžius? defaultDisplayNameLabel Rodomas vardas assistantTitle Pagelbiklis createAppSipAccountEnabledLabel Įjungti paskyros sukūrimą useAppSipAccountEnabledLabel Įjungti paskyros naudojimą useOtherSipAccountEnabledLabel Įgalinti bendrosios paskyros naudojimą fetchRemoteConfigurationEnabledLabel Įgalinti konfigūracijos gavimą assistantSupportsPhoneNumbersLabel Palaiko telefonų numerius defaultDeviceNameLabel 'Device Name' : Label for setting the device name. webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel SIP adresas transportLabel Perdavimas serverAddressLabel SIP serverio adresas registrationDurationLabel Registracijos trukmė (sek.) routeLabel Maršrutas contactParamsLabel Kontakto parametrai publishPresenceLabel Paskelbkite informaciją apie buvimą avpfIntervalLabel AVPF įprastas RTCP intervalas (sek.) registerEnabledLabel Registruotis avpfEnabledLabel Įjungti AVPF cancel ATSISAKYTI confirm PATVIRTINTI invalidSipAddress Neteisingas SIP adresas. invalidServerAddress Neteisingas serverio adresas. invalidRoute Netinkamas maršrutas. enableIceLabel Įjungti ICE stunServerLabel STUN/TURN serveris enableTurnLabel Įjungti TURN turnUserLabel TURN naudotojas turnPasswordLabel TURN slaptažodis natAndFirewallTitle NAT ir užkarda mainSipAccountSettingsTitle Pagrindinės SIP paskyros nustatymai conferenceURI "Conference URI" : Label of a text edit for filling Conference URI invalidConferenceURI "invalid conference URI" : Error text about conference URI videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) tunnelDomain 'Domain' : Field title of a textfield to set domain. tunnelUsername 'Username' : Field title of a textfield to set username. tunnelSIP 'SIP' : Field title of a switch to set SIP mode. cancel 'Cancel' : Button to cancel the action. ATSISAKYTI setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. proxyHttpHost 'Host' : Placeholder to set hostname. proxyHttpPort 'Port' : Placehoilder to set port. proxyHttpUsername 'Username' : Placeholder to set username. proxyHttpPassword 'Password' : Placeholder to set password. proxyHttpApply 'Apply' : Button to set proxy from changes. serverMode 'Mode' : Field title on form to set tunnel mode. serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. serverTitle 'Server' : Title form to set a server serverHostname 'Hostname' : Field title on form to set hostname. serverPort 'Port' : Field title on form to set port. serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. serverDelay 'Delay' : Field title on form to set the delay of the tunnel. tunnelAddServer 'Add server' : Button for adding a server tunnelApply 'Apply' : Button to apply changes. SettingsUi pathsTitle Keliai savedScreenshotsLabel Įrašytų ekrano kopijų aplankas savedCallsLabel Išsaugotų skambučių aplankas languagesTitle Kalbos languagesLabel Kalba systemLocale Sistemos lokalės cleanAvatars IŠTRINTI AVATARUS cleanAvatarsDescription Ar tikrai norite ištrinti visus avatarus? downloadLabel Atsiuntimų aplankas setLocaleDescription Yra būtina paleisti programą iš naujo. Ar norite tai atlikti dabar? otherTitle Kita exitOnCloseLabel Užverus langą, išeiti iš programos dataTitle Naudotojo sąsajos duomenys autoStartLabel Automatinio paleidimo programa fontsTitle 'Fonts' : title of fonts section in settings fontsTextChange 'Text Messages' : Label for changing text message fonts fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates mipmapLabel 'Enable Mipmap' mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Vaizdo įvesties įrenginys videoFramerateLabel Kadrų dažnis videoCaptureTitle Vaizdo paėmimo parametrai videoPresetLabel Išankstinės vaizdo parinktys presetDefault Numatytosios presetHighFps Didelis kadr./s skaičius presetCustom Tinkinta videoSizeLabel Vaizdo raiška videoCodecsTitle Vaizdo kodekai showCameraPreview VAIZDO PERŽIŪRA showVideoCodecsLabel Rodyti vaizdo kodekus videoSettingsInCallWarning Vyksta vaizdo skambutis: kai kurie nustatymai negalimi. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm GERAI SettingsWindow settingsTitle Nustatymai sipAccountsTab SIP paskyros audioTab Garsas videoTab Vaizdas callsAndChatTab Skambučiai ir pokalbiai networkTab Tinklas uiTab Naudotojo sąsaja validButton GERAI uiAdvanced Išplėstiniai tunnelTab 'Tunnel' : Tab title for tunnel section in settings. SipAddressDialog cancel ATSISAKYTI contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list SmartSearchBar addContact PRIDĖTI KONTAKTĄ Timeline timelineFilter A title for filtering mode. timelineFilterAll 'All' The mode for timelines filtering. timelineFilterCustom 'Custom' The mode for timelines filtering. timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction NAUDOTI useAppSipAccountTitle NAUDOTI %1 SĄSKAITĄ useUsernameToLogin Verčiau naudoti naudotojo vardą ir slaptažodį, o ne telefono numerį. quitWarning Jūsų paskyra buvo sukurta, tačiau dar nebuvo patvirtinta. Ar tikrai norite išeiti iš šio rodinio? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password UseAppSipAccountWithPhoneNumber countryLabel Šalis phoneNumberLabel Telefono numeris displayNameLabel Rodomas vardas (nebūtina) UseAppSipAccountWithUsername usernameLabel Naudotojo vardas passwordLabel Slaptažodis displayNameLabel Rodomas vardas (nebūtina) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form NAUDOTI useOtherSipAccountTitle NAUDOTI SIP PASKYRĄ usernameLabel Naudotojo vardas displayNameLabel Rodomas vardas (nebūtina) sipDomainLabel SIP sritis passwordLabel Slaptažodis transportLabel Perdavimas addOtherSipAccountError Nepavyko pridėti šios paskyros. understandAction 'I understand' : Popup confirmation for a warning warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. startButton 'Start' : Button label for starting the conference. endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Patvirtinkite šią SAS su kolega. codeA Sakyk: codeB Jūsų kontaktas turėtų sakyti: Later 'Later' : Button label to do something in another time. Vėliau Correct 'Correct' : Button label to confirm a code. Teisingas title 'Communication security' : Title of popup for ZRTP confirmation. Ryšių saugumas country Afghanistan Albania Algeria AmericanSamoa Andorra Angola Anguilla AntiguaAndBarbuda Argentina Armenia Aruba Australia Austria Azerbaijan Bahamas Bahrain Bangladesh Barbados Belarus Belgium Belize Benin Bermuda Bhutan Bolivia BosniaAndHerzegowina Botswana Brazil Brunei Bulgaria BurkinaFaso Burundi Cambodia Cameroon Canada CapeVerde CaymanIslands CentralAfricanRepublic Chad Chile China Colombia Comoros PeoplesRepublicOfCongo DemocraticRepublicOfCongo CookIslands CostaRica IvoryCoast Croatia Cuba Cyprus CzechRepublic Denmark Djibouti Dominica DominicanRepublic Ecuador Egypt ElSalvador EquatorialGuinea Eritrea Estonia Ethiopia FalklandIslands FaroeIslands Fiji Finland France FrenchGuiana FrenchPolynesia Gabon Gambia Georgia Germany Ghana Gibraltar Greece Greenland Grenada Guadeloupe Guam Guatemala Guinea GuineaBissau Guyana Haiti Honduras HongKong Hungary Iceland India Indonesia Iran Iraq Ireland Israel Italy Jamaica Japan Jordan Kazakhstan Kenya Kiribati DemocraticRepublicOfKorea RepublicOfKorea Kuwait Kyrgyzstan Laos Latvia Lebanon Lesotho Liberia Libya Liechtenstein Lithuania Luxembourg Macau Macedonia Madagascar Malawi Malaysia Maldives Mali Malta MarshallIslands Martinique Mauritania Mauritius Mayotte Mexico Micronesia Moldova Monaco Mongolia Montenegro Montserrat Morocco Mozambique Myanmar Namibia NauruCountry Nepal Netherlands NewCaledonia NewZealand Nicaragua Niger Nigeria Niue NorfolkIsland NorthernMarianaIslands Norway Oman Pakistan Palau PalestinianTerritories Panama PapuaNewGuinea Paraguay Peru Philippines Poland Portugal PuertoRico Qatar Reunion Romania RussianFederation Rwanda SaintHelena SaintKittsAndNevis SaintLucia SaintPierreAndMiquelon SaintVincentAndTheGrenadines Samoa SanMarino SaoTomeAndPrincipe SaudiArabia Senegal Serbia Seychelles SierraLeone Singapore Slovakia Slovenia SolomonIslands Somalia SouthAfrica Spain SriLanka Sudan Suriname Swaziland Sweden Switzerland Syria Taiwan Tajikistan Tanzania Thailand Togo Tokelau Tonga TrinidadAndTobago Tunisia Turkey Turkmenistan TurksAndCaicosIslands Tuvalu Uganda Ukraine UnitedArabEmirates UnitedKingdom UnitedStates Uruguay Uzbekistan Vanuatu Venezuela Vietnam WallisAndFutunaIslands Yemen Zambia Zimbabwe utils downloadCodecDescription Ar norite atsisiųsti %1 (%2)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/pt.ts000066400000000000000000003767651434616504300236740ustar00rootroot00000000000000 About ok OK ActivateAppSipAccountWithEmail activateAppSipAccount ACTIVAR A SUA CONTA %1 confirmAction USAR CONTA activationSteps Para activar a sua conta: siga as instruções que enviámos em %1, depois clique abaixo. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount ACTIVAR A SUA CONTA %1 confirmAction USAR CONTA activationSteps Um SMS contendo um código de validação foi enviado para %1. Para concluir a verificação do número de telefone, insira o código de 4 dígitos abaixo. App commandLineOptionVerbose log para stdout algumas informações de depuração durante a execução commandLineOptionConfig especifique o arquivo de configuração %1 a ser usado applicationDescription Um vídeo-fone SIP gratuito (libre). commandLineOptionIconified iniciar na bandeja do sistema, não mostrar a interface principal commandLineOptionConfigArg ficheiro commandLineOptionHelp mostrar esta ajuda commandLineOptionVersion mostrar a versão do aplicativo commandLineOptionCliHelp commandLineDescription restore quit settings about commandLineOptionFetchConfig commandLineOptionFetchConfigArg commandLineOptionCall commandLineOptionCallArg checkForUpdates AssistantAbstractView back AssistantHome useAppSipAccount useOtherSipAccount fetchRemoteConfiguration homeTitle homeDescription createAppSipAccount AssistantModel loginWithUsernameFailed usernameStatusTooShort usernameStatusTooLong usernameStatusInvalidCharacters usernameStatusInvalid passwordStatusTooShort passwordStatusTooLong passwordStatusInvalidCharacters passwordStatusMissingCharacters requestFailed emailStatusMalformed emailStatusMalformedInvalidCharacters cannotSendSms accountAlreadyExists smsActivationFailed emailActivationFailed phoneNumberStatusInvalid phoneNumberStatusTooShort phoneNumberStatusTooLong phoneNumberStatusInvalidCountryCode loginWithPhoneNumberFailed unableToAddAccount AuthenticationRequest cancel confirm identityLabel passwordLabel authenticationRequestDescription userIdLabel realmLabel CallModel callStatsCodec callStatsUploadBandwidth callStatsDownloadBandwidth callStatsEstimatedDownloadBandwidth callStatsIceState callStatsIpFamily callStatsSenderLossRate callStatsReceiverLossRate callStatsJitterBuffer callStatsSentVideoDefinition callStatsReceivedVideoDefinition iceStateNotActivated iceStateFailed iceStateInProgress iceStateReflexiveConnection iceStateHostConnection iceStateRelayConnection iceStateInvalid callErrorDeclined callErrorNotFound callErrorBusy callErrorNotAcceptable callStatsReceivedFramerate callStatsSentFramerate CallSipAddress cancel callSipAddressDescription CallStatistics audioStatsLabel videoStatsLabel CallTransfer cancel callTransferDescription Calls acceptAudioCall acceptVideoCall terminateCall resumeCall transferCall callPause attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CallsWindow callsTitle acceptClosingDescription Chat newMessagePlaceholder noFileTransferUrl chatTyping '%1 is typing...' indicate that someone is composing in chat allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered. ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard menuCopy 'Copy' : Text menu to copy selected text in message into clipboard menuPlayMe menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message menuDelete 'Delete' : Item menu to delete a message menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message menuForward 'Forward' : Forward a message from menu menuReply 'Reply' : Reply to a message from menu ChatNoticeModel nMinute nHour nDay nWeek ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Cli appCliDescription uriCommandLineSyntax cliCommandLineSyntax commandsName showFunctionDescription callFunctionDescription byeFunctionDescription initiateConferenceFunctionDescription joinConferenceFunctionDescription joinConferenceAsFunctionDescription CodecsViewer codecMime codecEncoderDescription codecEncoderClockRate codecBitrate codecRecvFmtp codecStatus Conference conferenceTitle ConferenceControls conference ConferenceManager conferenceManagerDescription cancel confirm ConfirmDialog cancel confirm ContactEdit removeContactDescription sipAccounts address emails webSites avatarChooserTitle companies save cancel sipAccountsPlaceholder companiesPlaceholder emailsPlaceholder webSitesPlaceholder street postalCode country locality abortEditDescriptionText tooltipShowConversation missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Contacts searchContactPlaceholder selectAllContacts selectConnectedContacts addContact removeContactDescription missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Conversation displayCallsAndMessages displayCalls displayMessages removeAllEntriesDescription tooltipContactEdit tooltipContactAdd cleanHistory conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode adminStatus 'Admin' : Admin(istrator) One word title for describing the current admin status groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list CreateAppSipAccountWithEmail createAppSipAccountTitle confirmAction usernameLabel emailLabel passwordLabel passwordConfirmationLabel passwordConfirmationError quitWarning displayNameLabel CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle countryLabel phoneNumberLabel usernameLabel displayNameLabel confirmAction quitWarning DroppableTextArea fileChooserTitle dropYourAttachment attachmentTooltip EphemeralChatRoom ephemeralTitle "Ephemeral messages" : Popup title for ephemerals ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Warning about not being in conference based chat room. disabled 'Disabled' nMinute '%1 minute' nHour '%1 hour' nDay '%1 day' nWeek '%1 week' cancelButton 'cancel' : button text for cancelling operation startButton 'start' : button text to start ephemeral mode Event incomingCall outgoingCall declinedIncomingCall declinedOutgoingCall endedCall missedIncomingCall missedOutgoingCall FetchRemoteConfiguration confirmAction fetchRemoteConfigurationTitle urlLabel remoteProvisioningError remoteProvisioningUpdateDescription lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. HistoryView removeAllEntriesDescription tooltipContactEdit tooltipContactAdd cleanHistory Home howToDescription howToTitle inviteDescription inviteTitle accountAssistantDescription accountAssistantTitle assistantButton showTooltips inviteButton Incall acceptVideoDescription pendingRequestLabel securedStringFormat callNotSecured takeSnapshotLabel startRecordingLabel stopRecordingLabel IncallFullscreenWindow takeSnapshotLabel startRecordingLabel stopRecordingLabel InfoChatRoom quitGroupButton 'Exit group' : Button label ok 'OK' : Button label addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status chatRoomDetailsTitle "Group information" : Popup title. popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation callButton 'CALL' : Button that lead to a call okButton 'OK' : Button that validate the popup to be redirected to the device list infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Explanation process InviteFriends enterEmailLabel messageLabel cancel confirm inviteFriendsTitle defaultMessage defaultSubject forcedMessage MainWindow mainSearchBarPlaceholder contactsEntry autoAnswerStatus smartSearchBarTooltip newConferenceButton newChatRoom 'Open Conference' : Tooltip to illustrate a button hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline openTimeline 'Open Timeline' : Tooltip for a button that open the timeline openHome 'Open Home' : Tooltip for a button that open the home view MainWindowMenuBar settings about quit checkForUpdates 'Check for updates' : Item menu for checking updates MainWindowTopMenuBar settings about quit checkForUpdates 'Check for updates' : Item menu for checking updates ManageAccounts ok selectPresenceLabel selectAccountLabel MultimediaParameters ok NewChatRoom cancelButton 'Cancel' : Cancel button startButton 'Launch' : Start button newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. subjectLabel 'Subject' : Label of a text field about the subject of the chat room subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject LastContactsTitle 'Last contacts' : Header for showing last contacts Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Notifier newVersionAvailable newFileMessage OnlineInstallerDialog confirm onlineInstallerExtractingDescription onlineInstallerDownloadingDescription onlineInstallerFinishedDescription onlineInstallerFailedDescription OutgoingMessage messageError messageRead messageDelivered ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) Little Header in one word for a column in participant Presence presenceOnline presenceBusy presenceDoNotDisturb presenceOffline QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. SettingsAdvanced logsTitle logsFolderLabel sendLogs logsUploadUrlLabel logsUploadFailed logsEnabledLabel cleanLogs cleanLogsDescription developerSettingsTitle developerSettingsEnabledLabel logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) logsMailerSuccess contactsTitle noPlugin 'No Plugins to load' : Text in combobox SettingsAudio audioTitle playbackDeviceLabel captureDeviceLabel ringerDeviceLabel ringLabel echoCancellationLabel audioCodecsTitle showAudioCodecsLabel playbackGainLabel captureGainLabel audioTestLabel audioSettingsInCallWarning echoCancellationCalibrationLabel Button title for the calibration of echo canceller calibratingEchoCancellationInProgress Message while calibrating calibratingEchoCancellationDone %1 is a placeholder for the number of ms obtained by the calibration. ms = milliseconds calibratingEchoCancellationFailed Message when the calibration cannot be done calibratingEchoCancellationNone Message when the calibration did not find any echo SettingsCallsChat fileServerLabel encryptWithLimeLabel limeDisabled limeRequired limePreferred chatTitle callsTitle encryptionLabel noEncryption autoAnswerLabel autoAnswerDelayLabel autoAnswerWithVideoLabel chatEnabledLabel callRecorderEnabledLabel chatNotificationSoundEnabledLabel chatNotificationSoundLabel conferenceEnabledLabel contactsTitle contactsEnabledLabel muteMicrophoneEnabledLabel outgoingCallsEnabledLabel showTelKeypadAutomaticallyLabel automaticallyRecordCallsLabel keepCallsWindowInBackgroundLabel callPauseEnabledLabel encryptionMandatoryLabel hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. SettingsLdap newServer Display name of a new server SettingsLdapEdit cancel Cancel button label confirm Confirm button label displayNameLabel Label displayNameTooltip Tooltip for display name connectionTitle Label serverLabel Label serverTooltip Tooltip for server URL bindDNLabel Label for a 'Bind DN'. 'Bind' can be a keyword. Check LDAP documentations bindDNTooltip 'Bind DN' can be a keyword. Check LDAP documentations passwordLabel useTLSLabel useTLSTooltip useSalLabel useSalTooltip verifyTLSLabel AutoMode ComboBox Label offMode ComboBox Label onMode ComboBox label verifyTLSTooltip searchTitle baseObjectLabel 'Base Object'/'Search Base' can be a keyword. Check LDAP documentations baseObjectPlaceholder 'Base Object'/'Search Base' can be a keyword. Check LDAP documentations baseObjectTooltip 'Base Object', 'Search Base' and 'Base DN' can be keywords. Check LDAP documentations filterLabel filterTooltip maxResultsLabel maxResultsTooltip parsingTitle nameAttributesLabel nameAttributesTooltip sipAttributesLabel sipAttributesTooltip Default values : (mobile,telephoneNumber,homePhone,sn) are keywords. domainLabel domainTooltip miscLabel Miscellaneous label debugLabel debugTooltip timeoutLabel timeoutTooltip SettingsNetwork sendDtmfsLabel allowIpV6Label transportTitle natAndFirewallTitle enableIceLabel stunServerLabel enableTurnLabel turnUserLabel turnPasswordLabel networkProtocolAndPortsTitle sipUdpPortLabel sipTcpPortLabel audioRtpUdpPortLabel videoRtpUdpPortLabel dscpFieldsTitle sipFieldLabel audioRtpStreamFieldLabel videoRtpStreamFieldLabel bandwidthControlTitle downloadSpeedLimitLabel uploadSpeedLimitLabel enableAdaptiveRateControlLabel presenceTitle rlsUriLabel rlsUriAuto rlsUriDisabled showNetworkSettingsLabel generalTitle SettingsSipAccounts defaultIdentityTitle defaultUsernameLabel defaultSipAddressLabel proxyAccountsTitle eraseAllPasswords addAccount editHeader deleteHeader deleteAccountDescription eraseAllPasswordsDescription defaultDisplayNameLabel assistantTitle createAppSipAccountEnabledLabel useAppSipAccountEnabledLabel useOtherSipAccountEnabledLabel fetchRemoteConfigurationEnabledLabel assistantSupportsPhoneNumbersLabel SettingsSipAccountsEdit sipAddressLabel transportLabel serverAddressLabel registrationDurationLabel routeLabel contactParamsLabel publishPresenceLabel avpfIntervalLabel registerEnabledLabel avpfEnabledLabel cancel confirm invalidSipAddress invalidServerAddress invalidRoute enableIceLabel stunServerLabel enableTurnLabel turnUserLabel turnPasswordLabel natAndFirewallTitle mainSipAccountSettingsTitle conferenceURI "Conference URI" : Label of a text edit for filling Conference URI invalidConferenceURI "invalid conference URI" : Error text about conference URI SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) tunnelDomain 'Domain' : Field title of a textfield to set domain. tunnelUsername 'Username' : Field title of a textfield to set username. tunnelSIP 'SIP' : Field title of a switch to set SIP mode. cancel 'Cancel' : Button to cancel the action. setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. proxyHttpHost 'Host' : Placeholder to set hostname. proxyHttpPort 'Port' : Placehoilder to set port. proxyHttpUsername 'Username' : Placeholder to set username. proxyHttpPassword 'Password' : Placeholder to set password. proxyHttpApply 'Apply' : Button to set proxy from changes. serverMode 'Mode' : Field title on form to set tunnel mode. serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. serverTitle 'Server' : Title form to set a server serverHostname 'Hostname' : Field title on form to set hostname. serverPort 'Port' : Field title on form to set port. serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. serverDelay 'Delay' : Field title on form to set the delay of the tunnel. tunnelAddServer 'Add server' : Button for adding a server tunnelApply 'Apply' : Button to apply changes. SettingsUi pathsTitle savedScreenshotsLabel savedCallsLabel languagesTitle languagesLabel systemLocale cleanAvatars cleanAvatarsDescription downloadLabel setLocaleDescription otherTitle exitOnCloseLabel dataTitle autoStartLabel fontsTitle 'Fonts' : title of fonts section in settings fontsTextChange 'Text Messages' : Label for changing text message fonts fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates mipmapLabel 'Enable Mipmap' mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. SettingsVideo videoInputDeviceLabel videoFramerateLabel videoCaptureTitle videoPresetLabel presetDefault presetHighFps presetCustom videoSizeLabel videoCodecsTitle showCameraPreview showVideoCodecsLabel videoSettingsInCallWarning SettingsVideoPreview confirm SettingsWindow settingsTitle sipAccountsTab audioTab videoTab callsAndChatTab networkTab uiTab validButton uiAdvanced tunnelTab 'Tunnel' : Tab title for tunnel section in settings. SipAddressDialog cancel contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list SmartSearchBar addContact Timeline timelineFilter A title for filtering mode. timelineFilterAll 'All' The mode for timelines filtering. timelineFilterCustom 'Custom' The mode for timelines filtering. timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. UseAppSipAccount confirmAction useAppSipAccountTitle useUsernameToLogin quitWarning passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password UseAppSipAccountWithPhoneNumber countryLabel phoneNumberLabel UseAppSipAccountWithUsername usernameLabel passwordLabel UseOtherSipAccount confirmAction useOtherSipAccountTitle usernameLabel displayNameLabel sipDomainLabel passwordLabel transportLabel addOtherSipAccountError ZrtpTokenAuthenticationDialog confirmSas codeA codeB deny accept country Afghanistan Albania Algeria AmericanSamoa Andorra Angola Anguilla AntiguaAndBarbuda Argentina Armenia Aruba Australia Austria Azerbaijan Bahamas Bahrain Bangladesh Barbados Belarus Belgium Belize Benin Bermuda Bhutan Bolivia BosniaAndHerzegowina Botswana Brazil Brunei Bulgaria BurkinaFaso Burundi Cambodia Cameroon Canada CapeVerde CaymanIslands CentralAfricanRepublic Chad Chile China Colombia Comoros PeoplesRepublicOfCongo DemocraticRepublicOfCongo CookIslands CostaRica IvoryCoast Croatia Cuba Cyprus CzechRepublic Denmark Djibouti Dominica DominicanRepublic Ecuador Egypt ElSalvador EquatorialGuinea Eritrea Estonia Ethiopia FalklandIslands FaroeIslands Fiji Finland France FrenchGuiana FrenchPolynesia Gabon Gambia Georgia Germany Ghana Gibraltar Greece Greenland Grenada Guadeloupe Guam Guatemala Guinea GuineaBissau Guyana Haiti Honduras HongKong Hungary Iceland India Indonesia Iran Iraq Ireland Israel Italy Jamaica Japan Jordan Kazakhstan Kenya Kiribati DemocraticRepublicOfKorea RepublicOfKorea Kuwait Kyrgyzstan Laos Latvia Lebanon Lesotho Liberia Libya Liechtenstein Lithuania Luxembourg Macau Macedonia Madagascar Malawi Malaysia Maldives Mali Malta MarshallIslands Martinique Mauritania Mauritius Mayotte Mexico Micronesia Moldova Monaco Mongolia Montenegro Montserrat Morocco Mozambique Myanmar Namibia NauruCountry Nepal Netherlands NewCaledonia NewZealand Nicaragua Niger Nigeria Niue NorfolkIsland NorthernMarianaIslands Norway Oman Pakistan Palau PalestinianTerritories Panama PapuaNewGuinea Paraguay Peru Philippines Poland Portugal PuertoRico Qatar Reunion Romania RussianFederation Rwanda SaintHelena SaintKittsAndNevis SaintLucia SaintPierreAndMiquelon SaintVincentAndTheGrenadines Samoa SanMarino SaoTomeAndPrincipe SaudiArabia Senegal Serbia Seychelles SierraLeone Singapore Slovakia Slovenia SolomonIslands Somalia SouthAfrica Spain SriLanka Sudan Suriname Swaziland Sweden Switzerland Syria Taiwan Tajikistan Tanzania Thailand Togo Tokelau Tonga TrinidadAndTobago Tunisia Turkey Turkmenistan TurksAndCaicosIslands Tuvalu Uganda Ukraine UnitedArabEmirates UnitedKingdom UnitedStates Uruguay Uzbekistan Vanuatu Venezuela Vietnam WallisAndFutunaIslands Yemen Zambia Zimbabwe linphone-utils downloadCodecDescription Later 'Later' : Button label to do something in another time. Mais tarde Correct 'Correct' : Button label to confirm a code. Correcto title 'Communication security' : Title of popup for ZRTP confirmation. Segurança de comunicação linphone-desktop-5.0.2/linphone-app/assets/languages/pt_BR.ts000066400000000000000000005233641434616504300242440ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name ActivateAppSipAccountWithEmail activateAppSipAccount ATIVAR A SUA CONTA %1 confirmAction ATIVAR activationSteps Para ativar sua conta: siga as instruções que enviamos em %1 e clique abaixo. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount ATIVAR A SUA CONTA %1 confirmAction ATIVAR activationSteps Um SMS contendo um código de validação foi enviado para %1. Para concluir a verificação do número de telefone, insira o código de 4 dígitos abaixo. App commandLineOptionVerbose log para stdout algumas informações de depuração durante a execução commandLineOptionConfig especificar o arquivo de configuração %1 a ser usado applicationDescription Um telefone de vídeo SIP grátis. commandLineOptionIconified iniciar na bandeja do sistema, não mostrar a interface principal commandLineOptionConfigArg arquivo commandLineOptionHelp mostrar esta ajuda commandLineOptionVersion mostrar versão do aplicativo commandLineOptionCliHelp exibe o menu de ajuda para usar %1 com a CLI commandLineDescription enviar uma ordem para o aplicativo em direção a uma linha de comando restore Restaurar quit Desistir settings Preferências about Sobre commandLineOptionFetchConfig Especifique o arquivo de configuração %1 a ser recuperado. Ele será mesclado com a configuração atual. commandLineOptionFetchConfigArg Endereço URL, caminho ou arquivo commandLineOptionCall fazer uma chamada commandLineOptionCallArg Endereço SIP checkForUpdates Verifique se há atualizações AssistantAbstractView back COSTAS AssistantHome useAppSipAccount USAR UMA CONTA %1 useOtherSipAccount USAR UMA CONTA SIP fetchRemoteConfiguration BUSCAR CONFIGURAÇÃO REMOTA homeTitle BEM-VINDOS homeDescription Este assistente irá ajudá-lo a configurar e usar sua conta sip. createAppSipAccount CRIAR UMA CONTA %1 homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. AssistantModel loginWithUsernameFailed Falha no login. Verifique seu nome de usuário/senha. usernameStatusTooShort Muito curto! (%1 caracteres mín.) usernameStatusTooLong Demasiado tempo! (máx.%1 caracteres) usernameStatusInvalidCharacters Caracteres inválidos detectados. (regex: `%1`) usernameStatusInvalid Nome de usuário inválido. passwordStatusTooShort Muito curto! (%1 caracteres mín.) passwordStatusTooLong Demasiado tempo! (máx.%1 caracteres) passwordStatusInvalidCharacters Caracteres inválidos detectados. (regex: `%1`) passwordStatusMissingCharacters Caracteres ausentes: `%1`. requestFailed Não foi possível enviar a solicitação. emailStatusMalformed Endereço de e-mail incorreto. emailStatusMalformedInvalidCharacters Endereço de e-mail incorreto ou caracteres inválidos. cannotSendSms Erro do servidor: não é possível enviar sms. accountAlreadyExists Esta conta já existe. smsActivationFailed Falha na ativação do SMS! emailActivationFailed Verifique se você validou sua conta ou tente novamente. phoneNumberStatusInvalid Número de telefone inválido! phoneNumberStatusTooShort Muito curto! phoneNumberStatusTooLong Demasiado tempo! phoneNumberStatusInvalidCountryCode Código de país inválido! loginWithPhoneNumberFailed Falha no login. Por favor, verifique seu número de telefone. unableToAddAccount Não foi possível adicionar esta conta. AuthenticationRequest cancel CANCELAR confirm LOGIN identityLabel Identidade passwordLabel Senha authenticationRequestDescription Não foi possível autenticar. Verifique sua senha. userIdLabel ID do usuário (opcional) realmLabel Reino CallModel callStatsCodec Codec callStatsUploadBandwidth Largura de banda de upload callStatsDownloadBandwidth Largura de banda de download callStatsEstimatedDownloadBandwidth Largura de banda estimada de download callStatsIceState Estado ICE callStatsIpFamily Família IP callStatsSenderLossRate Taxa de perda do remetente callStatsReceiverLossRate Taxa de perda do receptor callStatsJitterBuffer Buffer de jitter callStatsSentVideoDefinition Definição de vídeo enviada callStatsReceivedVideoDefinition Definição de vídeo recebida iceStateNotActivated Não ativado iceStateFailed Fracassou iceStateInProgress Em curso iceStateReflexiveConnection Conexão reflexiva iceStateHostConnection Conexão do host iceStateRelayConnection Conexão de relé iceStateInvalid Inválido callErrorDeclined O grupo remoto recusou a chamada. callErrorNotFound Grupo remoto não foi encontrado. callErrorBusy A festa remota está ocupada. callErrorNotAcceptable A parte remota não pode aceitar a chamada. callStatsReceivedFramerate Framerate recebido callStatsSentFramerate Framerate enviado callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel CANCELAR callSipAddressDescription Comece uma nova chamada. CallStatistics audioStatsLabel Áudio videoStatsLabel Vídeo mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section CallTransfer cancel CANCELAR callTransferDescription Deseja transferir esta chamada? Calls acceptAudioCall ACEITAR CHAMADA DE ÁUDIO acceptVideoCall ACEITAR CHAMADA DE VÍDEO terminateCall FINALIZAR CHAMADA resumeCall RETOMAR CHAMADA transferCall CHAMADA DE TRANSFERÊNCIA callPause CHAMADA DE ESPERA attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. CONCLUIR A TRANSFERÊNCIA ATENDIDA attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CHAMADA DE TRANSFERÊNCIA ATENDIDA CallsWindow callsTitle Ligações acceptClosingDescription Tem certeza de que deseja encerrar todas as chamadas? Chat newMessagePlaceholder Introduza a sua mensagem noFileTransferUrl Não foi possível enviar o arquivo. URL do servidor não configurado. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 está digitando… %1 estão digitando… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. Copiado para a área de transferência selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. Seleção copiada para a área de transferência forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. conferencesCopiedICS ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. icsDescription 'Description' : Title for the meeting description. icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. icsJoinButton 'Join' : Action button to join the meeting. deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. icsJoinButton 'Join' : Action button to join the meeting. icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. icsParticipants '%1 participant' : number(=%1) of participant. icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Enviar para %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Recebido por %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Lido por %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 não recebeu a mensagem %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) fileTransferDownload 'Download' : Message link to download a file ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Copiar tudo menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Cópiar menuPlayMe Brinca comigo! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Status de entrega menuDelete 'Delete' : Item menu to delete a message Apagar menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Ocultar status de entrega menuForward 'Forward' : Forward a message from menu menuReply 'Reply' : Reply to a message from menu ChatNoticeModel nMinute %1 minuto %1 minutos nHour %1 hora %1 horas nDay %1 dia %1 dias nWeek %1 semana %1 semanas ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Cli appCliDescription Maneira de controlar o aplicativo %1 através das linhas de comando. uriCommandLineSyntax cliCommandLineSyntax commandsName lista de comandos: showFunctionDescription Mostra a janela principal do aplicativo. callFunctionDescription Inicie uma chamada para o endereço do SIP. initiateConferenceFunctionDescription Iniciar uma conferência. joinConferenceFunctionDescription Participe da conferência hospedada pelo sip-address como display-name. Se você estiver conectado a uma configuração de proxy, consulte join-conference-as. joinConferenceAsFunctionDescription Junte-se à conferência organizada pelo sip-address como com o guest-sip-address. Se você não estiver conectado a um proxy-config, consulte join-conference. byeFunctionDescription Encerre uma chamada específica, todas as chamadas ou a chamada atual. CodecsViewer codecMime Nome codecEncoderDescription Descrição codecEncoderClockRate Taxa (Hz) codecBitrate Taxa de bits (kbit/s) codecRecvFmtp Parâmetros codecStatus Situação Conference conferenceTitle CONFERÊNCIA ConferenceControls conference CONFERÊNCIA ConferenceManager conferenceManagerDescription Gerencie os participantes da sua conferência. cancel CANCELAR confirm INICIAR Conferences conferencesTitle 'Meetings' : Conference list title. conferencesEndedFilter 'Finished' : Filter meetings on end status. conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel CANCELAR confirm CONFIRMAR ContactEdit removeContactDescription Deseja realmente remover este contato do seu catálogo de endereços? sipAccounts CONTA (S) SIP address ENDEREÇO emails E-MAIL (S) webSites SITE (S) avatarChooserTitle Escolha o seu avatar companies EMPRESAS save SALVAR cancel CANCELAR sipAccountsPlaceholder Conta SIP companiesPlaceholder Empresa emailsPlaceholder E-mail webSitesPlaceholder Site street Rua postalCode Código postal country País locality Localidade abortEditDescriptionText Tem certeza de que deseja cancelar a modificação de contato? tooltipShowConversation Mostrar conversa missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Você precisa definir o URI da conferência nas configurações da sua conta para criar uma sala de bate-papo baseada em conferência. Contacts searchContactPlaceholder Pesquisar contato selectAllContacts Todos selectConnectedContacts Conectado addContact ADICIONAR CONTATO removeContactDescription Deseja realmente remover este contato do seu catálogo de endereços? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Você precisa definir o URI da conferência nas configurações da sua conta para criar uma sala de bate-papo baseada em conferência. Conversation displayCallsAndMessages TODOS displayCalls CHAMADAS displayMessages MENSAGENS removeAllEntriesDescription Tem certeza de que deseja limpar esse histórico? tooltipContactEdit Editar contato tooltipContactAdd Adicionar contato cleanHistory Excluir histórico adminStatus 'Admin' : Admin(istrator) Admin One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room Informação do grupo conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room Dispositivos de conversa conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode Mensagens efêmeras groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room Ligue para todos os participantes na sala de chat searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list Pesquisar mensagens conversationMenuDelete 'Delete history' : Item menu to delete the chat's history conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription Como você gostaria de criar sua conta? createAppSipAccountTitle CRIAR UMA CONTA %1 withPhoneNumber COM UM NÚMERO DE TELEFONE withEmailAddress COM UM ENDEREÇO DE E-MAIL CreateAppSipAccountWithEmail createAppSipAccountTitle CRIAR UMA CONTA %1 confirmAction CRIAR usernameLabel Nome de usuário emailLabel E-mail passwordLabel Senha passwordConfirmationLabel Confirmação de senha passwordConfirmationError As senhas inseridas não correspondem. quitWarning Sua conta foi criada, mas ainda não foi validada. Tem certeza de que deseja sair dessa exibição? displayNameLabel Nome de exibição (opcional) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle CRIAR UMA CONTA %1 countryLabel País phoneNumberLabel Número de telefone usernameLabel Nome de usuário displayNameLabel Nome de exibição (opcional) confirmAction CRIAR quitWarning Sua conta foi criada, mas ainda não foi validada. Se sair desta vista, terá de adicionar e validar manualmente a sua conta dentro de 24 horas. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Escolha um ou mais arquivos dropYourAttachment Solte seu anexo attachmentTooltip Enviar um arquivo EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation CANCELAR startButton 'start' : button text to start ephemeral mode INICIAR ephemeralTitle "Ephemeral messages" : Popup title for ephemerals Mensagens efêmeras ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals As novas mensagens serão excluídas em ambas as extremidades assim que forem lidas por seu contato. Selecione um tempo limite. ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Mensagens efêmeras são suportadas apenas em salas de bate-papo baseadas em conferência! Warning about not being in conference based chat room. disabled 'Disabled' Desabilitado nMinute '%1 minute' %1 minuto %1 minutos nHour '%1 hour' %1 hora %1 horas nDay '%1 day' %1 dia %1 dias nWeek '%1 week' %1 semana %1 semanas Event incomingCall Chamada recebida outgoingCall Chamada de saída declinedIncomingCall Chamada recebida recusada declinedOutgoingCall Chamada de saída recusada endedCall Chamada encerrada missedIncomingCall Chamada recebida perdida missedOutgoingCall Chamada de saída perdida FetchRemoteConfiguration confirmAction BUSCAR fetchRemoteConfigurationTitle BUSCAR CONFIGURAÇÃO REMOTA urlLabel URL remoteProvisioningError Não é possível definir este uri de provisionamento remoto. remoteProvisioningUpdateDescription É necessário reiniciar o aplicativo. Deseja reiniciar agora? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Tem certeza de que deseja limpar esse histórico? tooltipContactEdit Editar contato tooltipContactAdd Adicionar contato cleanHistory Excluir histórico Home howToDescription Precisa de ajuda para usar o% 1? howToTitle COMO USAR %1 inviteDescription Convide seus amigos em %1. inviteTitle CONVIDE SEUS AMIGOS accountAssistantDescription Crie ou gerencie sua conta %1. accountAssistantTitle ASSISTENTE DE CONTA assistantButton ASSISTENTE showTooltips Mostrar dicas de ferramentas inviteButton CONVIDAR Incall acceptVideoDescription Seu contato gostaria de ativar o vídeo. securedStringFormat A chamada está encriptada com: %1. callNotSecured Chamada não criptografada. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. incallMenuTitle 'Settings' : Main menu title for settings. incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label Sair do grupo ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Adicione participantes addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Pesquise participantes em sua lista de contatos para convidá-los para a sala de chat. Explanation for inviting the selected participants into chat room participantList 'Participant list' Lista de participantes adminStatus 'Admin' : Admin(istrator) Admin word for admin status chatRoomDetailsTitle "Group information" : Popup title. Informação do grupo popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation CANCELAR callButton 'CALL' : Button that lead to a call LIGAR okButton 'OK' : Button that validate the popup to be redirected to the device list OK infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. Criptografado de ponta a ponta encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." As mensagens instantâneas são criptografadas de ponta a ponta em conversas seguras. É possível atualizar o nível de segurança de uma conversa autenticando os participantes. Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Para isso, ligue para o contato e siga o processo de autenticação. Explanation process InviteFriends enterEmailLabel Endereço de e-mail do amigo messageLabel Mensagem cancel CANCELAR confirm CONFIRMAR inviteFriendsTitle Convidar amigos defaultMessage %1 quer convidá-lo em %2! defaultSubject Convite de %1 forcedMessage Baixe o aplicativo no seu computador e comece a ligar e conversar com os usuários gratuitamente. Clique aqui: <a href="%1">%1 </a> MAC_APPLICATION_MENU About %1 Sobre o %1 Preferences... Preferências Services Serviços Hide %1 Ocultar o %1 Hide Others Ocultar Outros Show All Mostrar Tudo Quit %1 Encerrar %1 MainWindow mainSearchBarPlaceholder Pesquisar contato, iniciar uma chamada ou um bate-papo… contactsEntry CONTATOS autoAnswerStatus Automático smartSearchBarTooltip Use a barra de pesquisa inteligente para iniciar diretamente o áudio e o vídeo, enviar uma mensagem ou adicionar um novo contato. Basta digitar endereço SIP ou nome de usuário do seu amigo. newConferenceButton Iniciar chamada em conferência newChatRoom 'Start a chat room' : Tooltip to illustrate a button Iniciar uma sala de chat hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline Ocultar linha do tempo openTimeline 'Open Timeline' : Tooltip for a button that open the timeline Abrir linha do tempo openHome 'Open Home' : Tooltip for a button that open the home view Abrir a página inicial mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Preferências about Sobre quit Desistir checkForUpdates 'Check for updates' : Item menu for checking updates Verifique se há atualizações MainWindowTopMenuBar settings Preferências about Sobre quit Desistir checkForUpdates 'Check for updates' : Item menu for checking updates Verifique se há atualizações ManageAccounts ok ESTÁ BEM selectPresenceLabel Status de presença selectAccountLabel Conta ativa MultimediaParametersDialog ok menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button CANCELAR startButton 'Launch' : Start button LANÇAR missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Você precisa preencher um assunto. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. Você precisa de pelo menos %1 participante. Você precisa de pelo menos %1 participantes. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. Você precisa definir o URI da conferência nas configurações da sua conta para criar uma sala de bate-papo baseada em conferência. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room Iniciar uma sala de chat askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. Você gostaria de criptografar seu bate-papo? subjectLabel 'Subject' : Label of a text field about the subject of the chat room Assunto subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Assunto atual da sala de bate-papo. Não pode estar vazio. Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Selecionar os participantes participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' Pesquise em seus contatos ou adicione um personalizado à sala de chat. adminStatus 'Admin' : Admin(istrator) Admin word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Remover este participante da seleção This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Obrigatório subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Dê um assunto LastContactsTitle 'Last contacts' : Header for showing last contacts Contatos recentes NewConference cancelButton 'Cancel' : Cancel button CANCELAR missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Você precisa preencher um assunto. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. Você precisa de pelo menos %1 participante. Você precisa de pelo menos %1 participantes. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Você precisa definir o URI da conferência nas configurações da sua conta para criar uma sala de bate-papo baseada em conferência. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference Assunto subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Dê um assunto subjectTooltip 'Current subject of the Meeting. It cannot be empty' Assunto atual da sala de bate-papo. Não pode estar vazio. Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. Você gostaria de criptografar seu bate-papo? participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Selecionar os participantes participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' Pesquise em seus contatos ou adicione um personalizado à sala de chat. adminStatus 'Admin' : Admin(istrator) Admin word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Remover este participante da seleção This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Obrigatório launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. Você entrou no grupo conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. Você saiu do grupo conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. %1 juntou-se conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group %1 saiu conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody %1 agora é um administrador conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody %1 não é mais um administrador conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. Nível de segurança degradado em %1 conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time Mensagens efêmeras foram habilitadas: %1 conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. Mensagens efêmeras foram desativadas conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. Novo assunto: %1 conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Mensagens efêmeras foram atualizadas: %1 unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. Notifier newVersionAvailable Está disponível uma nova versão (%1)! newFileMessage Novo anexo recebido! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm CONFIRMAR onlineInstallerExtractingDescription Extraindo %1… onlineInstallerDownloadingDescription Transferindo %1… onlineInstallerFinishedDescription %1 já está instalado! onlineInstallerFailedDescription Falha ao instalar %1! OutgoingMessage messageError Erro messageRead Leia messageDelivered Entregue ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Adicione participantes addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Pesquise participantes em sua lista de contatos para convidá-los para a sala de chat. Explanation for inviting the selected participants into chat room participantList 'Participant list' Lista de participantes adminStatus 'Admin' : Admin(istrator) Admin word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) (Admin) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline Disponível presenceBusy Ocupado presenceDoNotDisturb Não perturbe presenceOffline Offline QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. Habilitar LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. Desabilitar LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. Auto SettingsAdvanced logsTitle Toros logsFolderLabel Pasta Logs sendLogs ENVIAR LOGS logsUploadUrlLabel URL do servidor de upload de logs logsUploadFailed Falha ao carregar logs. logsEnabledLabel Ativar logs cleanLogs LIMPAR LOGS cleanLogsDescription Tem certeza de que deseja remover todos os logs? developerSettingsTitle Configurações do desenvolvedor developerSettingsEnabledLabel Ativar configurações do desenvolvedor logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) O mailer não pode ser encontrado, mas os logs foram enviados para %1 logsMailerSuccess Os registos foram enviados para %1 contactsTitle Conectar ao catálogo de endereços noPlugin 'No Plugins to load' : Text in combobox Nenhum plug-in para carregar viewlogs SettingsAudio audioTitle Parâmetros de áudio playbackDeviceLabel Dispositivo de reprodução captureDeviceLabel Capturar dispositivo ringerDeviceLabel Dispositivo de toque ringLabel Anel echoCancellationLabel Ativar cancelamento de eco audioCodecsTitle Codecs de áudio showAudioCodecsLabel Mostrar codecs de áudio playbackGainLabel Ganho de reprodução captureGainLabel Ganho de captura audioTestLabel Nível de captura audioSettingsInCallWarning Chamada de áudio em andamento: algumas configurações não estão disponíveis. echoCancellationCalibrationLabel Calibração calibratingEchoCancellationInProgress ...calibrando… calibratingEchoCancellationDone Calibrado para %1ms calibratingEchoCancellationFailed Calibração falhou calibratingEchoCancellationNone Nenhum eco detectado SettingsCallsChat fileServerLabel Servidor de arquivos encryptWithLimeLabel Criptografar com LIME limeDisabled Incapacitado limeRequired Obrigatório limePreferred Preferia chatTitle Chat callsTitle Ligações encryptionLabel Criptografia noEncryption Nenhum autoAnswerLabel Resposta automática autoAnswerDelayLabel Atraso (em ms) autoAnswerWithVideoLabel Resposta automática (com vídeo) chatEnabledLabel Ativar bate-papo callRecorderEnabledLabel Ativar gravador de chamadas chatNotificationSoundEnabledLabel Ativar som de notificação chatNotificationSoundLabel Som de notificação conferenceEnabledLabel Ativar conferência contactsTitle Contatos contactsEnabledLabel Ativar contatos muteMicrophoneEnabledLabel Ativar microfone silencioso outgoingCallsEnabledLabel Ativar chamadas de saída showTelKeypadAutomaticallyLabel Mostrar o teclado do telefone automaticamente automaticallyRecordCallsLabel Gravar chamadas automaticamente keepCallsWindowInBackgroundLabel Manter janelas de chamadas em segundo plano callPauseEnabledLabel Retenção de chamada ativada encryptionMandatoryLabel A criptografia é obrigatória hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms Ocultar salas de bate-papo vazias waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered Ligar quando registrado chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer Novo servidor SettingsLdapEdit cancel Cancelar confirm Confirme displayNameLabel Nome de exibição displayNameTooltip O nome de exibição do servidor a ser mostrado na lista. connectionTitle Conexão serverLabel URL do servidor serverTooltip Servidor LDAP. por exemplo: ldap: /// para um servidor localhost ou ldap: //ldap.example.org/ bindDNLabel Bind DN bindDNTooltip O DN de ligação é a credencial usada para autenticação em um LDAP. <br> por exemplo: cn=ausername,ou=people,dc=bc,dc=com passwordLabel Senha useTLSLabel Use TLS useTLSTooltip Criptografe as transações por LDAP sobre TLS (StartTLS). Você deve usar o esquema \'ldap\'. \'ldaps\' para LDAP sobre SSL não é padronizado e está obsoleto. <br>StartTLS é uma extensão do protocolo LDAP que usa o protocolo TLS para criptografar a comunicação. <br>Funciona estabelecendo uma conexão normal - ou seja, não segura - com o servidor LDAP antes que uma negociação de handshake entre o servidor e os serviços da web seja realizada. Aqui, o servidor envia seu certificado para provar sua identidade antes que a conexão segura seja estabelecida. useSalLabel Use SAL useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' A resolução DNS é feita pelo %1 usando SAL. Ele vai passar um IP para o LDAP. Com isso, a negociação TLS não conseguiu verificar o nome do host. Você pode desativar as verificações se quiser forçar a conexão. verifyTLSLabel Verificar certificados em TLS AutoMode Auto offMode Desligado onMode Ligado verifyTLSTooltip Especifique se o certificado do servidor TLS deve ser verificado ao se conectar a um servidor LDAP. searchTitle Procurar baseObjectLabel Base de Pesquisa baseObjectPlaceholder Base de Pesquisa baseObjectTooltip Objeto Base/Base de Pesquisa é uma especificação para Escopos de Pesquisa LDAP que especifica que a Solicitação de Pesquisa deve ser executada apenas na entrada especificada como DN de Base de Pesquisa. <br>Nenhuma entrada acima será considerada. filterLabel Filtro filterTooltip A pesquisa é baseada neste filtro para pesquisar contatos. <br>Valor padrão: (sn=%s) maxResultsLabel Resultados máximos maxResultsTooltip Os resultados máximos ao solicitar pesquisas. timeoutLabel Timeout timeoutTooltip A conexão e o tempo limite de pesquisa em segundos. Deve ser positivo. <br>O padrão é 5s. parsingTitle Análise nameAttributesLabel Atributos de nome nameAttributesTooltip Verifique esses atributos para criar o contato do nome, separados por uma vírgula e o primeiro é o de maior prioridade. <br>O valor padrão é: sn sipAttributesLabel Atributos SIP sipAttributesTooltip Verifique esses atributos para construir o nome de usuário SIP no endereço de contato. Os atributos são separados por uma vírgula e o primeiro é a prioridade mais alta. <br>O valor padrão é: mobile, telephoneNumber, homePhone, sn domainLabel Domínio domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. Adicione o domínio ao endereço SIP (sip:username@domain). miscLabel Diversos debugLabel Depurar debugTooltip Obtenha logs detalhados no arquivo de log ao fazer transações (útil para depurar conexões TLS). SettingsNetwork sendDtmfsLabel método de envio de DTMFS allowIpV6Label Permitir IPv6 transportTitle Transporte natAndFirewallTitle NAT e Firewall enableIceLabel Ativar ICE stunServerLabel Servidor STUN/TUNR enableTurnLabel Ativar Turn turnUserLabel Usuário de Turn turnPasswordLabel Palavra-passe de Turn networkProtocolAndPortsTitle Protocolo de rede e portas sipUdpPortLabel Porta SIP UDP sipTcpPortLabel Porta SIP TCP audioRtpUdpPortLabel Porta UDP de áudio RTP videoRtpUdpPortLabel Porta UDP RTP de vídeo dscpFieldsTitle Campos DSCP sipFieldLabel SABOREIE audioRtpStreamFieldLabel Stream RTP de áudio videoRtpStreamFieldLabel Stream de vídeo RTP bandwidthControlTitle Controle de largura de banda downloadSpeedLimitLabel Limite de velocidade de download em kbit/s uploadSpeedLimitLabel Limite de velocidade de carregamento em kbit/s enableAdaptiveRateControlLabel Ativar controle de taxa adaptável presenceTitle Presença rlsUriLabel Usar URI do RLS rlsUriAuto AUTOMÁTICO rlsUriDisabled NUNCA showNetworkSettingsLabel Mostrar configurações de rede generalTitle Geral SettingsSipAccounts defaultIdentityTitle Identidade padrão defaultUsernameLabel Nome de usuário defaultSipAddressLabel Endereço SIP proxyAccountsTitle Contas proxy eraseAllPasswords REMOVER TODAS AS SENHAS addAccount ADICIONAR CONTA editHeader Editar deleteHeader Excluir deleteAccountDescription Tem certeza de que deseja excluir esta conta? eraseAllPasswordsDescription Tem certeza de que deseja remover todas as senhas? defaultDisplayNameLabel Nome de exibição assistantTitle Assistente createAppSipAccountEnabledLabel Ativar a criação de conta useAppSipAccountEnabledLabel Ativar o uso da conta useOtherSipAccountEnabledLabel Ativar o uso genérico da conta fetchRemoteConfigurationEnabledLabel Ativar busca de configuração assistantSupportsPhoneNumbersLabel Suporta números de telefone defaultDeviceNameLabel 'Device Name' : Label for setting the device name. webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel Endereço SIP transportLabel Transporte serverAddressLabel Endereço do servidor SIP registrationDurationLabel Duração do registo (seg) routeLabel Rota contactParamsLabel Parâmetros de contato publishPresenceLabel Publicar informações de presença avpfIntervalLabel Intervalo RTCP regular AVPF (seg) registerEnabledLabel Registe-se avpfEnabledLabel Ativar AVPF cancel CANCELAR confirm CONFIRMAR invalidSipAddress Endereço SIP inválido. invalidServerAddress Endereço de servidor inválido. invalidRoute Rota inválida. enableIceLabel Ativar ICE stunServerLabel Servidor STUN/TUNR enableTurnLabel Ativar Turn turnUserLabel Usuário de Turn turnPasswordLabel Palavra-passe de Turn natAndFirewallTitle NAT e Firewall mainSipAccountSettingsTitle Configurações principais da conta SIP conferenceURI "Conference URI" : Label of a text edit for filling Conference URI URI de conferência invalidConferenceURI "invalid conference URI" : Error text about conference URI URI de conferência inválido videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) 'Status do túnel': título do campo para apresentar o status do túnel (ativado ou não) tunnelDomain 'Domain' : Field title of a textfield to set domain. Domínio tunnelUsername 'Username' : Field title of a textfield to set username. Nome do usuário tunnelSIP 'SIP' : Field title of a switch to set SIP mode. SIP cancel 'Cancel' : Button to cancel the action. Cancelar setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. Definir proxy HTTP proxyHttpHost 'Host' : Placeholder to set hostname. Host proxyHttpPort 'Port' : Placehoilder to set port. Porta proxyHttpUsername 'Username' : Placeholder to set username. Nome do usuário proxyHttpPassword 'Password' : Placeholder to set password. Senha proxyHttpApply 'Apply' : Button to set proxy from changes. Aplicar serverMode 'Mode' : Field title on form to set tunnel mode. Modo serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. Dois modos serverTitle 'Server' : Title form to set a server Servidor serverHostname 'Hostname' : Field title on form to set hostname. Hostname serverPort 'Port' : Field title on form to set port. Porta serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. Segundo URL hostname serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. Porta segundaria serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. Porta de espelho UDP remota serverDelay 'Delay' : Field title on form to set the delay of the tunnel. Atraso tunnelAddServer 'Add server' : Button for adding a server Adicionar servidor tunnelApply 'Apply' : Button to apply changes. Aplicar SettingsUi pathsTitle Caminhos savedScreenshotsLabel Pasta de capturas de tela salvas savedCallsLabel Pasta de chamadas salvas languagesTitle Línguas languagesLabel Idioma systemLocale Localidade do sistema cleanAvatars APAGAR AVATARES cleanAvatarsDescription Tem certeza de que deseja apagar todos os avatares? downloadLabel Fazer download da pasta setLocaleDescription É necessário reiniciar o aplicativo. Deseja reiniciar agora? otherTitle Outros exitOnCloseLabel Sair da aplicação ao fechar a janela dataTitle Dados da interface do usuário autoStartLabel Aplicação Autostart fontsTitle 'Fonts' : title of fonts section in settings Fontes fontsTextChange 'Text Messages' : Label for changing text message fonts Mensagens de texto fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates mipmapLabel 'Enable Mipmap' mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Dispositivo de entrada de vídeo videoFramerateLabel Taxa de quadros videoCaptureTitle Parâmetros de captura de vídeo videoPresetLabel Predefinição de vídeo presetDefault Default presetHighFps Alto FPS presetCustom Personalizado videoSizeLabel Resolução de vídeo videoCodecsTitle Codecs de vídeo showCameraPreview VISUALIZAÇÃO DE VÍDEO showVideoCodecsLabel Mostrar codecs de vídeo videoSettingsInCallWarning Chamada de vídeo em andamento: algumas configurações não estão disponíveis. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm ESTÁ BEM SettingsWindow settingsTitle Configurações sipAccountsTab Contas SIP audioTab Áudio videoTab Vídeo callsAndChatTab Chamadas e bate-papo networkTab Rede uiTab Interface do usuário validButton ESTÁ BEM uiAdvanced Avançado tunnelTab 'Tunnel' : Tab title for tunnel section in settings. SipAddressDialog cancel contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list SmartSearchBar addContact ADICIONAR CONTATO Timeline timelineFilter A title for filtering mode. timelineFilterAll 'All' The mode for timelines filtering. timelineFilterCustom 'Custom' The mode for timelines filtering. timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction USO useAppSipAccountTitle USAR UMA CONTA %1 useUsernameToLogin Use nome de usuário e senha em vez de seu número de telefone. quitWarning Sua conta foi criada, mas ainda não foi validada. Tem certeza de que deseja sair dessa exibição? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password UseAppSipAccountWithPhoneNumber countryLabel País phoneNumberLabel Número de telefone displayNameLabel UseAppSipAccountWithUsername usernameLabel Nome de usuário passwordLabel Senha displayNameLabel UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form USO useOtherSipAccountTitle USAR UMA CONTA SIP usernameLabel Nome de usuário displayNameLabel Nome de exibição (opcional) sipDomainLabel Domínio SIP passwordLabel Senha transportLabel Transporte addOtherSipAccountError Não foi possível adicionar esta conta. understandAction 'I understand' : Popup confirmation for a warning warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. CANCELAR startButton 'Start' : Button label for starting the conference. endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Confirme o seguinte SAS com peer. codeA Diga: codeB Seu contato deve dizer: Later 'Later' : Button label to do something in another time. Tarde Correct 'Correct' : Button label to confirm a code. Correcto title 'Communication security' : Title of popup for ZRTP confirmation. Segurança das comunicações country Afghanistan Afeganistão Albania Albânia Algeria Argélia AmericanSamoa Americanos Amoa Andorra Andorra Angola Angola Anguilla Anguila AntiguaAndBarbuda Antiguae Barbuda Argentina Argentina Armenia Arménia Aruba Aruba Australia Austrália Austria Áustria Azerbaijan Azerbaijão Bahamas Bahamas Bahrain Barém Bangladesh Bangladesh Barbados Barbados Belarus Bielorrússia Belgium Bélgica Belize Belize Benin Benim Bermuda Bermudas Bhutan Butão Bolivia Bolívia BosniaAndHerzegowina Bósnia e Herzegowina Botswana Botsuana Brazil Brasil Brunei Brunei Bulgaria Bulgária BurkinaFaso Burkinafaso Burundi Burúndi Cambodia Camboja Cameroon Camarões Canada Canadá CapeVerde Cabo Verde CaymanIslands Ilhas Caymanas CentralAfricanRepublic República Centro-Africana Chad Chade Chile Chile China China Colombia Colômbia Comoros Comores PeoplesRepublicOfCongo República do Congo DemocraticRepublicOfCongo República Democrática do Congo CookIslands Ilhas Cookies CostaRica Costa Rica IvoryCoast Costa do Marfim Croatia Croácia Cuba Cuba Cyprus Chipre CzechRepublic República Tcheca Denmark Dinamarca Djibouti Djibuti Dominica Dominica DominicanRepublic República Dominicana Ecuador Equador Egypt Egito ElSalvador El Salvador EquatorialGuinea Guiné Equatorial Eritrea Eritreia Estonia Estónia Ethiopia Etiópia FalklandIslands Malvinas FaroeIslands Ilhas Faroé Fiji Fiji Finland Finlândia France França FrenchGuiana Francesa Guiana FrenchPolynesia Polinésia Francesa Gabon Gabão Gambia Gâmbia Georgia Geórgia Germany Alemanha Ghana Gana Gibraltar Gibraltar Greece Grécia Greenland Gronelândia Grenada Granada Guadeloupe Guadalupe Guam Guam Guatemala Guatemala Guinea Guiné GuineaBissau Guiné-Bissau Guyana Guiana Haiti Haiti Honduras Honduras HongKong Hong Kong Hungary Hungria Iceland Islândia India Índia Indonesia Indonésia Iran Irã Iraq Iraque Ireland Irlanda Israel Israel Italy Itália Jamaica Jamaica Japan Japão Jordan Jordânia Kazakhstan Cazaquistão Kenya Quênia Kiribati Kiribati DemocraticRepublicOfKorea Democratica República da Coreia RepublicOfKorea República da Coreia Kuwait Kuwait Kyrgyzstan Quirguizistão Laos Laos Latvia Letónia Lebanon Líbano Lesotho Lesoto Liberia Libéria Libya Líbia Liechtenstein Liechtenstein Lithuania Lituânia Luxembourg Luxemburgo Macau Macau Macedonia Macedónia Madagascar Madagascar Malawi Malawi Malaysia Malásia Maldives Maldivas Mali Mali Malta Malta MarshallIslands Ilhas Marshallis Martinique Martinica Mauritania Mauritânia Mauritius Maurícia Mayotte Mayotte Mexico México Micronesia Micronésia Moldova Moldávia Monaco Mónaco Mongolia Mongólia Montenegro Montenegro Montserrat Monserrate Morocco Marrocos Mozambique Moçambique Myanmar Mianmar Namibia Namíbia NauruCountry Nauru Nepal Nepal Netherlands Holanda NewCaledonia Nova Caledônia NewZealand Nova Zelândia Nicaragua Nicarágua Niger Níger Nigeria Nigéria Niue Niue NorfolkIsland Ilha Norfolk NorthernMarianaIslands Ilhas do Norte da Mariana Norway Noruega Oman Omã Pakistan Paquistão Palau Palau PalestinianTerritories Palestinianos anteriores Panama Panamá PapuaNewGuinea Papua Nova Guiné Paraguay Paraguai Peru Peru Philippines Filipinas Poland Polónia Portugal Portugal PuertoRico Porto Rico Qatar Catar Reunion Reunião Romania Roménia RussianFederation Federação Russa Rwanda Ruanda SaintHelena Santa Lena SaintKittsAndNevis São Cristóvão e Nevis SaintLucia Santa Lucia SaintPierreAndMiquelon São Pedro e Miquelon SaintVincentAndTheGrenadines São Vicente e Granadinas Samoa Samoa SanMarino São Marino SaoTomeAndPrincipe São Tomé e Príncipe SaudiArabia Arábia Saudita Senegal Senegal Serbia Sérvia Seychelles Seicheles SierraLeone Serra Leoa Singapore Singapura Slovakia Eslováquia Slovenia Eslovénia SolomonIslands Ilhas Salomão Somalia Somália SouthAfrica África do Sul Spain Espanha SriLanka Sri Lanka Sudan Sudão Suriname Suriname Swaziland Suazilândia Sweden Suécia Switzerland Suíça Syria Síria Taiwan Taiwan Tajikistan Tajiquistão Tanzania Tanzânia Thailand Tailândia Togo Togo Tokelau Tokelau Tonga Tonga TrinidadAndTobago Trinidad e Tobago Tunisia Tunísia Turkey Turquia Turkmenistan Turquemenistão TurksAndCaicosIslands Ilhas Turcas e Caicos Tuvalu Tuvalu Uganda Uganda Ukraine Ucrânia UnitedArabEmirates Emirados Árabes Unidos UnitedKingdom Reino Unido UnitedStates Estados Unidos Uruguay Uruguai Uzbekistan Uzbequistão Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietname WallisAndFutunaIslands Ilhas Wallis e Futuna Yemen Iémen Zambia Zâmbia Zimbabwe Zimbabué utils downloadCodecDescription Deseja transferir %1 (%2)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/ru.ts000066400000000000000000005667461434616504300236770ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name Помогите нам перевести %1 ActivateAppSipAccountWithEmail activateAppSipAccount АКТИВИРУЙТЕ ВАШ АККАУНТ %1 confirmAction ИСПОЛЬЗОВАТЬ АККАУНТ activationSteps Чтобы активировать аккаунт, следуйте инструкциям, которые мы отправили на %1, затем нажмите ниже. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount АКТИВИРУЙТЕ ВАШ АККАУНТ %1 confirmAction ИСПОЛЬЗОВАТЬ АККАУНТ activationSteps Мы отправили SMS с кодом подтверждения на %1. Для завершения подтверждения вашего номера телефона, пожалуйста, введите ниже 4-значный код. App commandLineOptionVerbose выводить в stdout некоторую отладочную информацию во время работы commandLineOptionConfig укажите конфигурационный файл %1 для использования applicationDescription Свободное приложение для SIP видео звонков. commandLineOptionIconified запускать свёрнутым в область уведомлений, не показывать основной интерфейс commandLineOptionConfigArg файл commandLineOptionHelp показать эту справку commandLineOptionVersion показать версию приложения commandLineOptionCliHelp показывает меню помощи в использовании %1 из командной строки CLI commandLineDescription отправка команды приложения через командную строку restore Восстановить quit Выйти settings Предпочтения about О программе commandLineOptionFetchConfig Укажите файл конфигурации %1, который необходимо извлечь. Он будет объединён с текущей конфигурацией. commandLineOptionFetchConfigArg URL, путь или файл commandLineOptionCall позвонить commandLineOptionCallArg SIP-адрес checkForUpdates Проверить обновления AssistantAbstractView back НАЗАД AssistantHome useAppSipAccount ИСПОЛЬЗОВАТЬ АККАУНТ %1 useOtherSipAccount ИСПОЛЬЗОВАТЬ SIP АККАУНТ fetchRemoteConfiguration ЗАГРУЗИТЬ УДАЛЁННУЮ КОНФИГУРАЦИЮ homeTitle ДОБРО ПОЖАЛОВАТЬ homeDescription Этот ассистент поможет вам настроить и использовать ваш sip аккаунт. createAppSipAccount СОЗДАТЬ АККАУНТ %1 homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. Я принимаю %1 %2условия использования%3 и %4политику конфиденциальности%5 AssistantModel loginWithUsernameFailed Не удалось войти. Пожалуйста, проверьте правильность ввода имени пользователя и пароля. usernameStatusTooShort Слишком короткое! (минимум %1 символов) usernameStatusTooLong Слишком длинное! (максимум %1 символов) usernameStatusInvalidCharacters Обнаружены недопустимые символы. (regex: `%1`) usernameStatusInvalid Недопустимое имя пользователя. passwordStatusTooShort Слишком короткий! (минимум %1 символов) passwordStatusTooLong Слишком длинный! (максимум %1 символов) passwordStatusInvalidCharacters Обнаружены недопустимые символы. (regex: `%1`) passwordStatusMissingCharacters Не хватает символов: `%1`. requestFailed Не удалось отправить запрос. emailStatusMalformed Недопустимый адрес почты. emailStatusMalformedInvalidCharacters Недопустимый адрес почты или недопустимые символы. cannotSendSms Ошибка сервера: не удалось отправить SMS. accountAlreadyExists Такой аккаунт уже существует. smsActivationFailed SMS активация не удалась! emailActivationFailed Пожалуйста, удостоверьтесь в том, что вы подтвердили ваш аккаунт, или попробуйте снова. phoneNumberStatusInvalid Недопустимый номер телефона! phoneNumberStatusTooShort Слишком короткий! phoneNumberStatusTooLong Слишком длинный! phoneNumberStatusInvalidCountryCode Недопустимый код страны! loginWithPhoneNumberFailed Не удалось войти. Пожалуйста проверьте правильность ввода номера телефона. unableToAddAccount Не удалось добавить этот аккаунт. AuthenticationRequest cancel ОТМЕНА confirm ВОЙТИ identityLabel Логин passwordLabel Пароль authenticationRequestDescription Не удалось выполнить аутентификацию. Пожалуйста, проверьте правильность ввода пароля. userIdLabel ID пользователя (не обязательно) realmLabel Область CallModel callStatsCodec Кодек callStatsUploadBandwidth Исходящая скорость callStatsDownloadBandwidth Входящая скорость callStatsEstimatedDownloadBandwidth Предполагаемая входящая скорость callStatsIceState Состояние ICE callStatsIpFamily Семейство IP callStatsSenderLossRate Потери исходящих пакетов callStatsReceiverLossRate Потери входящих пакетов callStatsJitterBuffer Буфер колебаний задержки callStatsSentVideoDefinition Разрешение исходящего видео callStatsReceivedVideoDefinition Разрешение входящего видео iceStateNotActivated Не активирован iceStateFailed Неудачно iceStateInProgress В процессе iceStateReflexiveConnection Рефлексивное соединение iceStateHostConnection Подключение хоста iceStateRelayConnection Релейное соединение iceStateInvalid Недопустимый callErrorDeclined Удалённая сторона отклонила звонок. callErrorNotFound Удалённая сторона не найдена. callErrorBusy Удалённая сторона занята. callErrorNotAcceptable Удалённая сторона не может принять звонок. callStatsReceivedFramerate Входящая частота кадров callStatsSentFramerate Исходящая частота кадров callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics Шифрование потока callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics Алгоритм хеширования callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel ОТМЕНА callSipAddressDescription Начать новый звонок. CallStatistics audioStatsLabel Аудио videoStatsLabel Видео mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section Шифрование потока CallTransfer cancel ОТМЕНА callTransferDescription Хотите перенаправить этот звонок? Calls acceptAudioCall ПРИНЯТЬ АУДИО ЗВОНОК acceptVideoCall ПРИНЯТЬ ВИДЕО ЗВОНОК terminateCall ЗАВЕРШИТЬ ЗВОНОК resumeCall ПРОДОЛЖИТЬ ЗВОНОК transferCall ПЕРЕНАПРАВИТЬ ЗВОНОК callPause ПРИОСТАНОВИТЬ ЗВОНОК attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. ЗВОНОК ПЕРЕВОДА С УЧАСТИЕМ ЗАВЕРШЁН attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. ЗВОНОК ПЕРЕВОДА С УЧАСТИЕМ CallsWindow callsTitle Звонки acceptClosingDescription Вы уверены, что хотите завершить все звонки? Chat newMessagePlaceholder Введите сообщение noFileTransferUrl Не удалось отправить файл. Не настроен адрес сервера. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 печатает… %1 печатают… %1 печатают… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. Скопировано в буфер обмена selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. Выделение скопировано в буфер обмена forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. Выберите, куда переслать сообщение conferencesCopiedICS ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. Организатор icsDescription 'Description' : Title for the meeting description. Описание icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. Адрес встречи icsJoinButton 'Join' : Action button to join the meeting. Присоединиться deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. Вы действительно хотите удалить эту встречу? cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. Описание icsJoinButton 'Join' : Action button to join the meeting. Присоединиться icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. Приглашение на встречу icsParticipants '%1 participant' : number(=%1) of participant. %d участников %d участников %d участников icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Отправлено %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Получено %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Прочитано %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 не получил сообщение %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message Ошибка при отправке на %1 %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) Отмена fileTransferDownload 'Download' : Message link to download a file Скачать ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. Перенаправлено ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Скопировать все menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Копировать menuPlayMe Играй! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Состояние доставки menuDelete 'Delete' : Item menu to delete a message Удалить menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Скрыть состояние доставки menuForward 'Forward' : Forward a message from menu Переслать menuReply 'Reply' : Reply to a message from menu Ответить ChatNoticeModel nMinute %1 минута %1 минуты %1 минут nHour %1 час %1 часа %1 часов nDay %1 день %1 дня %1 дней nWeek %1 неделя %1 недели %1 недель ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Ответить ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Ответить %1 Cli appCliDescription Способ управлять %1 через командную строку. uriCommandLineSyntax %1 sip:<адрес-sip>?method=<метод>([&<аргумент>=<base64-значение>]*) cliCommandLineSyntax %1 "<метод> ([<аргумент>=<значение>]*)" commandsName список команд: showFunctionDescription Показать главное окно приложения. callFunctionDescription Начать звонок на SIP адрес. initiateConferenceFunctionDescription Начать конференцию. joinConferenceFunctionDescription Присоединитесь к конференции по sip-адресу. Если вы подключены к конфигурации прокси, см. join-conference-as. joinConferenceAsFunctionDescription Присоединитесь к конференции по sip-адресу. Если вы не подключены к конфигурации прокси, см. join-conference. byeFunctionDescription Завершить определённый звонок, все звонки или текущий звонок. CodecsViewer codecMime Название codecEncoderDescription Описание codecEncoderClockRate Частота (Гц) codecBitrate Битрейт (Кбит/с) codecRecvFmtp Параметры codecStatus Статус Conference conferenceTitle КОНФЕРЕНЦИЯ ConferenceControls conference КОНФЕРЕНЦИЯ ConferenceManager conferenceManagerDescription Управляйте участниками вашей конференции. cancel ОТМЕНА confirm НАЧАТЬ Conferences conferencesTitle 'Meetings' : Conference list title. Встречи conferencesEndedFilter 'Finished' : Filter meetings on end status. Оконченная conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. Запланированная conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. URL конференции скопирован conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel ОТМЕНА confirm ПОДТВЕРДИТЬ ContactEdit removeContactDescription Вы действительно хотите удалить этот контакт из вашей адресной книги? sipAccounts SIP АККАУНТ(Ы) address АДРЕС emails E-MAIL webSites САЙТ(Ы) avatarChooserTitle Выберите ваш аватар companies КОМПАНИИ save СОХРАНИТЬ cancel ОТМЕНА sipAccountsPlaceholder SIP аккаунт companiesPlaceholder Компания emailsPlaceholder Email webSitesPlaceholder Сайт street Улица postalCode Почтовый индекс country Страна locality Населённый пункт abortEditDescriptionText Вы уверены, что хотите отменить изменение контакта? tooltipShowConversation Показать разговор missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Вам необходимо установить URI конференции в настройках вашего аккаунта, чтобы создать чат-комнату на основе конференции. Contacts searchContactPlaceholder Найти контакт selectAllContacts Все selectConnectedContacts Подключенные addContact ДОБАВИТЬ КОНТАКТ removeContactDescription Вы действительно хотите удалить этот контакт из вашей адресной книги? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Вам необходимо установить URI конференции в настройках вашего аккаунта, чтобы создать чат-комнату на основе конференции. Conversation displayCallsAndMessages ВСЕ displayCalls ЗВОНКИ displayMessages СООБЩЕНИЯ removeAllEntriesDescription Вы уверены, что хотите очистить эту историю? tooltipContactEdit Изменить контакт tooltipContactAdd Добавить контакт cleanHistory Удалить историю adminStatus 'Admin' : Admin(istrator) Администратор One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room Информация о группе conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room Устройства для общения conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode Недолговечные сообщения groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room Обзвон всех участников чат-комнаты searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list Поиск сообщений conversationMenuDelete 'Delete history' : Item menu to delete the chat's history Удалить историю conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book Добавить контакт conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. Запланировать встречу CreateAppSipAccount createAppSipAccountDescription Как бы вы хотели создать аккаунт? createAppSipAccountTitle СОЗДАТЬ АККАУНТ %1 withPhoneNumber С ПОМОЩЬЮ НОМЕРА ТЕЛЕФОНА withEmailAddress С ПОМОЩЬЮ EMAIL CreateAppSipAccountWithEmail createAppSipAccountTitle СОЗДАТЬ АККАУНТ %1 confirmAction СОЗДАТЬ usernameLabel Имя пользователя emailLabel Email passwordLabel Пароль passwordConfirmationLabel Подтверждение пароля passwordConfirmationError Введённые пароли не совпадают. quitWarning Ваш аккаунт был создан, но ещё не подтверждён. Вы уверены, что хотите выйти? displayNameLabel Отображаемое имя (не обязательно) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle СОЗДАТЬ АККАУНТ %1 countryLabel Страна phoneNumberLabel Номер телефона usernameLabel Имя пользователя displayNameLabel Отображаемое имя (не обязательно) confirmAction СОЗДАТЬ quitWarning Ваш аккаунт был создан, но ещё не подтверждён. Если вы выйдете, вам потребуется вручную добавить и подтвердить свой аккаунт в течении 24 часов. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. (пауза) DroppableTextArea fileChooserTitle Пожалуйста, выберите один или несколько файлов dropYourAttachment Поместите вложение attachmentTooltip Отправить файл EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation ОТМЕНА startButton 'start' : button text to start ephemeral mode СТАРТ ephemeralTitle "Ephemeral messages" : Popup title for ephemerals Недолговечные сообщения ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals Новые сообщения будут удалены с обоих сторон, как только они будут прочитаны вашим контактом. ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Недолговечное сообщение поддерживается только в конференции, основанной на чат-комнате! Warning about not being in conference based chat room. disabled 'Disabled' Отключено nMinute '%1 minute' %1 минута %1 минуты %1 минут nHour '%1 hour' %1 час %1 часа %1 часов nDay '%1 day' %1 день %1 дня %1 дней nWeek '%1 week' %1 неделя %1 недели %1 недель Event incomingCall Входящий звонок outgoingCall Исходящий звонок declinedIncomingCall Отклонённый входящий звонок declinedOutgoingCall Отклонённый исходящий звонок endedCall Завершённый звонок missedIncomingCall Пропущенный входящий звонок missedOutgoingCall Пропущенный исходящий звонок FetchRemoteConfiguration confirmAction ЗАГРУЗИТЬ fetchRemoteConfigurationTitle ЗАГРУЗИТЬ УДАЛЁННУЮ КОНФИГУРАЦИЮ urlLabel URL remoteProvisioningError Невозможно установить эту удалённую настройку uri. remoteProvisioningUpdateDescription Требуется перезапустить приложение. Хотите перезапустить сейчас? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. Последняя удалённая инициализация не удалась generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Вы уверены, что хотите очистить эту историю? tooltipContactEdit Изменить контакт tooltipContactAdd Добавить контакт cleanHistory Удалить историю Home howToDescription Нужна помощь в использовании %1? howToTitle КАК ПОЛЬЗОВАТЬСЯ %1 inviteDescription Пригласите друзей использовать %1. inviteTitle ПРИГЛАСИТЬ ДРУЗЕЙ accountAssistantDescription Создание и управление аккаунтом %1. accountAssistantTitle АССИСТЕНТ АККАУНТА assistantButton АССИСТЕНТ showTooltips Показывать подсказки inviteButton ПРИГЛАСИТЬ Incall acceptVideoDescription Ваш контакт хотел бы включить видео. securedStringFormat Звонок зашифрован с: %1. callNotSecured Звонок не зашифрован. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. В настоящее время вы вне встречи. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. Нажмите на кнопку воспроизведения, чтобы присоединиться к ней обратно. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Начать запись incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. Остановить запись incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Сделать скриншот incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. Видеоконференция не готова. Пожалуйста, подождите… callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. Этот звонок записывается. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. В настоящее время вы вне встречи. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. Нажмите на кнопку воспроизведения, чтобы присоединиться к ней обратно. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. Начать запись incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. Остановить запись incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. Сделать скриншот incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. Видеоконференция не готова. Пожалуйста, подождите… callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. Этот звонок записывается. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. Параметры мультимедиа incallMenuLayout 'Change layout' : Menu title to change the conference layout. Изменить макет incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. Пригласить участников incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. Список участников incallMenuTitle 'Settings' : Main menu title for settings. Настройки incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. Мозаичный режим incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. Активизировать режим динамика incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. Режим только аудио incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label Выйти из группы ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Добавить участников addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Найдите участников в своём списке контактов, чтобы пригласить их в чат-комнату. Explanation for inviting the selected participants into chat room participantList 'Participant list' Список участников adminStatus 'Admin' : Admin(istrator) Администратор word for admin status chatRoomDetailsTitle "Group information" : Popup title. Информация о группе popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation ОТМЕНА callButton 'CALL' : Button that lead to a call ЗВОНОК okButton 'OK' : Button that validate the popup to be redirected to the device list ОК infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. Сквозное шифрование encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Мгновенные сообщения в защищённых разговорах шифруются сквозным шифрованием. Можно повысить уровень безопасности беседы путем аутентификации участников. Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Для этого позвоните контакту и следуйте процессу аутентификации. Explanation process InviteFriends enterEmailLabel Email друга messageLabel Сообщение cancel ОТМЕНА confirm ПОДТВЕРДИТЬ inviteFriendsTitle Пригласить друзей defaultMessage %1 приглашает вас использовать %2 ! defaultSubject Приглащение %1 forcedMessage Загрузите приложение на ваш компьютер и начните звонить и общаться бесплатно. Ссылка для загрузки: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 О %1 Preferences... настройки Services Hide %1 Скрывать %1 Hide Others Скрыть другие Show All Показать все Quit %1 Выйти из %1 MainWindow mainSearchBarPlaceholder Найти контакт, начать звонок или чат… contactsEntry КОНТАКТЫ autoAnswerStatus авто smartSearchBarTooltip Используйте умную поисковую строку, чтобы сразу начать аудио или видео звонок, отправить сообщение или добавить новый контакт. Просто введите SIP адрес или имя пользователя вашего контакта. newConferenceButton Начать конференц звонок newChatRoom 'Start a chat room' : Tooltip to illustrate a button Начать чат-комнату hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline Скрыть шкалу времени openTimeline 'Open Timeline' : Tooltip for a button that open the timeline Открыть шкалу времени openHome 'Open Home' : Tooltip for a button that open the home view Открыть главную mainWindowConferencesTitle 'Meetings' : Meeting title for main window. Встречи newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Настройки about О программе quit Выйти checkForUpdates 'Check for updates' : Item menu for checking updates Проверить обновления MainWindowTopMenuBar settings Предпочтения about О программе quit Выйти checkForUpdates 'Check for updates' : Item menu for checking updates Проверить обновления ManageAccounts ok OK selectPresenceLabel Статус selectAccountLabel Активный аккаунт MultimediaParametersDialog ok OK menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. Параметры мультимедиа NewChatRoom cancelButton 'Cancel' : Cancel button ОТМЕНА startButton 'Launch' : Start button ЗАПУСК missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Вам нужно заполнить тему. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. Вам нужен как минимум %1 участник. Вам нужно как минимум %1 участника. Вам нужно как минимум %1 участников. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. Вам необходимо установить URI конференции в настройках вашего аккаунта, чтобы создать конференцию, основанную на чат-комнате. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room Начать чат-комнату askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. Хотите зашифровать свой чат? subjectLabel 'Subject' : Label of a text field about the subject of the chat room Тема subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Текущая тема чат-комнаты. Она не может быть пустой. Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Выберите участников participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' Найти в своих контактах или добавить своё в чат-комнату. adminStatus 'Admin' : Admin(istrator) Администратор word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Удалить этого участника из подборки This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Требуемый subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Задайте тему LastContactsTitle 'Last contacts' : Header for showing last contacts Недавние контакты NewConference cancelButton 'Cancel' : Cancel button ОТМЕНА missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Вам нужно заполнить тему. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. Вам нужен как минимум %1 участник. Вам нужно как минимум %1 участника. Вам нужно как минимум %1 участников. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Вам необходимо установить URI конференции в настройках вашего аккаунта, чтобы создать конференцию, основанную на чат-комнате. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference Начать видеоконференцию subjectLabel 'Subject' : Label of a text field about the subject of the conference Тема subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Задайте тему subjectTooltip 'Current subject of the Meeting. It cannot be empty' Текущая тема конференции. Она не может быть пустой. Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. Хотите зашифровать встречу? participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Выберите участников participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' Найти в своих контактах или добавить своё в конференцию. adminStatus 'Admin' : Admin(istrator) Администратор word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Удалить этого участника из подборки This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Требуемый launchButton 'Launch' : Launch button Запуск updateButton 'Update' : Update button Обновить updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. Обновить конференцию newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. Вы хотите запланировать конференцию? newConferenceDate 'Date' : Date label. Дата newConferenceTimeTitle 'Time' : Time label. Время newConferenceDurationTitle 'Duration' : Duration label. Продолжительность newConferenceTimezoneTitle 'Timezone' : Timezone label. Часовой пояс newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference Добавить описание newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description Описание newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting Это описание будет описывать конференцию newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. Отправить приглашение через %1 newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. Отправить приглашение через электронную почту busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. Выполняется операция, пожалуйста, подождите confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. Вы присоединились к группе conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. Вы покинули группу conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. %1 присоединился conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group %1 ушёл conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody %1 теперь администратор conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody %1 больше не администратор conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. Уровень безопасности снижен на %1 conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time Недолговечные сообщения включены: %1 conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. Недолговечные сообщения отключены conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. Новая тема: %1 conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Недолговечные сообщения были обновлены: %1 unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. %1 непрочитанное сообщение %1 непрочитанных сообщения %1 непрочитанных сообщений Notifier newVersionAvailable Доступна новая (%1) версия! newFileMessage Получен новый файл! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm ПОДТВЕРДИТЬ onlineInstallerExtractingDescription Извлечение %1… onlineInstallerDownloadingDescription Загрузка %1… onlineInstallerFinishedDescription %1 установлен! onlineInstallerFailedDescription Не удалось установить %1! OutgoingMessage messageError Ошибка messageRead Прочитано messageDelivered Доставлено ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices Устройства для общения ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Добавить участников addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Найдите участников в своём списке контактов, чтобы пригласить их в чат-комнату. Explanation for inviting the selected participants into chat room participantList 'Participant list' Список участников adminStatus 'Admin' : Admin(istrator) Администратор word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. Удалить этого участника из списка ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) (Администратор) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Я Presence presenceOnline Доступен presenceBusy Занят presenceDoNotDisturb Не беспокоить presenceOffline Офлайн QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. Включить LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. Отключить LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. Авто SettingsAdvanced logsTitle Журналы logsFolderLabel Папка журналов sendLogs ОТПРАВКА ЖУРНАЛОВ logsUploadUrlLabel URL севрера для отправки журналов logsUploadFailed Не удалось отправить журналы. logsEnabledLabel Журналы включены cleanLogs УДАЛИТЬ ЖУРНАЛЫ cleanLogsDescription Вы уверены, что хотите удалить все журналы? developerSettingsTitle Настройки для разработчиков developerSettingsEnabledLabel Включить настройки для разработчиков logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Не удалось найти почтовый ящик, но журналы были загружены в %1 logsMailerSuccess Журналы были загружены в %1 contactsTitle Коннектор адресной книги noPlugin 'No Plugins to load' : Text in combobox Нет плагинов для загрузки viewlogs SettingsAudio audioTitle Параметры аудио playbackDeviceLabel Устройство воспроизведения captureDeviceLabel Устройство захвата ringerDeviceLabel Устройство звонка ringLabel Звонок echoCancellationLabel Включить подавление эха audioCodecsTitle Аудио кодеки showAudioCodecsLabel Показать аудио кодеки playbackGainLabel Усиление воспроизведения captureGainLabel Прирост захвата audioTestLabel Уровень захвата audioSettingsInCallWarning Аудио звонок в процессе: некоторые настройки недоступны. echoCancellationCalibrationLabel Калибровка calibratingEchoCancellationInProgress …калибровка… calibratingEchoCancellationDone Откалиброван на %1 мс calibratingEchoCancellationFailed Калибровка не удалась calibratingEchoCancellationNone Эхо не обнаружено SettingsCallsChat fileServerLabel Файловый сервер encryptWithLimeLabel Шифровать с помощью LIME limeDisabled Отключено limeRequired Обязательно limePreferred Предпочтительно chatTitle Чат callsTitle Звонки encryptionLabel Шифрование noEncryption Нет autoAnswerLabel Принимать звонок автоматически autoAnswerDelayLabel Задержка (в мс) autoAnswerWithVideoLabel Принимать звонок автоматически (с видео) chatEnabledLabel Включить чат callRecorderEnabledLabel Включить запись звонков chatNotificationSoundEnabledLabel Включить звук уведомлений chatNotificationSoundLabel Звук уведомлений conferenceEnabledLabel Включить конференцию contactsTitle Контакты contactsEnabledLabel Включить контакты muteMicrophoneEnabledLabel Выключить микрофон outgoingCallsEnabledLabel Включение исходящих звонков showTelKeypadAutomaticallyLabel Показывать телефонную клавиатуру автоматически automaticallyRecordCallsLabel Автоматически записывать звонки keepCallsWindowInBackgroundLabel Поддерживать звонки в свернутом режиме callPauseEnabledLabel Звонок поставлен на паузу encryptionMandatoryLabel Регистрация обязательна hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms Скрыть пустые чат-комнаты waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered Звоните, когда зарегистрируетесь chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. Включить уведомления AutoDownload 'Auto download' : Label for a slider about auto download mode Автоматическая загрузка autoDownloadNever 'Never' : auto download mode description for deactivated feature. Никогда autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. Всегда callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer Новый сервер SettingsLdapEdit cancel ОТМЕНА confirm Подтвердить displayNameLabel Отображаемое имя displayNameTooltip Отображаемое имя сервера, которое будет отображаться в списке. connectionTitle Подключение serverLabel URL-адрес сервера serverTooltip LDAP-сервер. например: ldap:/// для локального сервера или ldap://ldap.example.org/ bindDNLabel Имя для подключения bindDNTooltip Имя для подключения — это учётные данные, которые используются для аутентификации в LDAP.<br> Например: cn=ausername,ou=people,dc=bc,dc=com passwordLabel Пароль useTLSLabel Использовать TLS useTLSTooltip Шифрование транзакций с помощью LDAP через TLS (StartTLS). Вы должны использовать схему \'ldap\'. \'ldaps\' для LDAP через SSL не стандартизирован и устарел.<br>StartTLS - это расширение протокола LDAP, которое использует протокол TLS для шифрования связи. <br>Он работает, устанавливая обычное, т. е. незащищённое, соединение с сервером LDAP перед выполнением согласования рукопожатия между сервером и веб-службами. Здесь сервер отправляет свой сертификат, чтобы подтвердить свою личность, прежде чем будет установлено безопасное соединение. useSalLabel Использовать SAL useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' Разрешение DNS выполняется %1 с использованием SAL. Он передаст IP в LDAP. При этом согласование TLS не может проверить имя хоста. Вы можете деактивировать проверки, если хотите принудительно установить соединение. verifyTLSLabel Проверка сертификатов по TLS AutoMode Авто offMode Выкл onMode Вкл verifyTLSTooltip Укажите, должен ли сертификат сервера TLS проверяться при подключении к серверу LDAP. searchTitle Поиск baseObjectLabel База поиска baseObjectPlaceholder База поиска baseObjectTooltip Базовый объект/база поиска — это спецификация для областей поиска LDAP, в которой указано, что запрос на поиск должен выполняться только для записи, указанной в качестве DN базы поиска.<br>Никакие записи выше этого значения не рассматриваются. filterLabel Фильтр filterTooltip Поиск основан на этом фильтре для поиска контактов.<br>Значение по умолчанию: (sn=%s) maxResultsLabel Максимальные результаты maxResultsTooltip Максимальные результаты при запросе поиска. timeoutLabel Тайм-аут timeoutTooltip Время ожидания подключения и поиска в секундах. Оно должно быть положительным.<br>По умолчанию 5 с. parsingTitle Парсинг nameAttributesLabel Атрибуты имени nameAttributesTooltip Отметьте эти атрибуты, чтобы создать контакт по имени, разделённые запятой, и первый из них имеет наивысший приоритет.<br>Значение по умолчанию: sn sipAttributesLabel SIP-атрибуты sipAttributesTooltip Отметьте эти атрибуты, чтобы построить имя пользователя SIP в адресе контакта. Атрибуты разделяются запятой, и первый из них имеет наивысший приоритет.<br>Значение по умолчанию: mobile,telephoneNumber,homePhone,sn domainLabel Домен domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. Добавьте домен к SIP-адресу (sip:username@domain). miscLabel Разное debugLabel Отладка debugTooltip Получайте подробные журналы в файле журнала при выполнении транзакций (полезно для отладки соединений TLS). SettingsNetwork sendDtmfsLabel Метод отправки DTMF allowIpV6Label Разрешить IPv6 transportTitle Транспорт natAndFirewallTitle NAT и межсетевой экран enableIceLabel Включить ICE stunServerLabel Сервер STUN/TURN enableTurnLabel Включить TURN turnUserLabel Пользователь TURN turnPasswordLabel Пароль TURN networkProtocolAndPortsTitle Сетевой протокол и порты sipUdpPortLabel UDP порт SIP sipTcpPortLabel TCP порт SIP audioRtpUdpPortLabel UDP порт RTP аудио videoRtpUdpPortLabel UDP порт RTP видео dscpFieldsTitle Поля DSCP sipFieldLabel SIP audioRtpStreamFieldLabel Поток RTP аудио videoRtpStreamFieldLabel Поток RTP видео bandwidthControlTitle Управление полосой пропускания downloadSpeedLimitLabel Ограничить входящую скорость до Кбит/сек uploadSpeedLimitLabel Ограничить исходящую скорость до Кбит/сек enableAdaptiveRateControlLabel Включить адаптивное управление скоростью presenceTitle Статус rlsUriLabel Использовать RLS URI rlsUriAuto АВТО rlsUriDisabled НИКОГДА showNetworkSettingsLabel Показать сетевые настройки generalTitle Основные SettingsSipAccounts defaultIdentityTitle Удостоверение по умолчанию defaultUsernameLabel Имя пользователя defaultSipAddressLabel Адрес SIP proxyAccountsTitle Прокси аккаунты eraseAllPasswords УДАЛИТЬ ПАРОЛИ addAccount ДОБАВИТЬ АККАУНТ editHeader Изменить deleteHeader Удалить deleteAccountDescription Вы уверены, что хотите удалить этот аккаунт? eraseAllPasswordsDescription Вы уверены, что хотите удалить все пароли? defaultDisplayNameLabel Отображаемое имя assistantTitle Помощник createAppSipAccountEnabledLabel Включить создание аккаунта useAppSipAccountEnabledLabel Включить использования аккаунта useOtherSipAccountEnabledLabel Включить использование общего аккаунта fetchRemoteConfigurationEnabledLabel Включить выбор конфигурации assistantSupportsPhoneNumbersLabel Поддерживает номера телефонов defaultDeviceNameLabel 'Device Name' : Label for setting the device name. Имя устройства webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. URL регистрации webviewLoginUrlLabel 'Login URL' : Label for login URL. URL логина SettingsSipAccountsEdit sipAddressLabel Адрес SIP transportLabel Транспорт serverAddressLabel Адрес сервера SIP registrationDurationLabel Длительность регистрациии (сек) routeLabel Маршрут contactParamsLabel Контактные параметры publishPresenceLabel Публиковать статус avpfIntervalLabel AVPF обычный RTCP интервал (сек) registerEnabledLabel Регистрация avpfEnabledLabel Включить AVPF cancel ОТМЕНА confirm ПОДТВЕРДИТЬ invalidSipAddress Недопустимый SIP адрес. invalidServerAddress Недопустимый адрес сервера. invalidRoute Неверный маршрут. enableIceLabel Включить ICE stunServerLabel Сервер STUN/TURN enableTurnLabel Включить TURN turnUserLabel Пользователь TURN turnPasswordLabel Пароль TURN natAndFirewallTitle NAT и межсетевой экран mainSipAccountSettingsTitle Основные настройки SIP аккаунта conferenceURI "Conference URI" : Label of a text edit for filling Conference URI URI конференции invalidConferenceURI "invalid conference URI" : Error text about conference URI Недопустимый URI конференции videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) Статус туннеля tunnelDomain 'Domain' : Field title of a textfield to set domain. Домен tunnelUsername 'Username' : Field title of a textfield to set username. Имя пользователя tunnelSIP 'SIP' : Field title of a switch to set SIP mode. SIP cancel 'Cancel' : Button to cancel the action. ОТМЕНА setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. Установить HTTP-прокси proxyHttpHost 'Host' : Placeholder to set hostname. Хост proxyHttpPort 'Port' : Placehoilder to set port. Порт proxyHttpUsername 'Username' : Placeholder to set username. Имя пользователя proxyHttpPassword 'Password' : Placeholder to set password. Пароль proxyHttpApply 'Apply' : Button to set proxy from changes. Принять serverMode 'Mode' : Field title on form to set tunnel mode. Режим serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. Двойной режим serverTitle 'Server' : Title form to set a server Сервер serverHostname 'Hostname' : Field title on form to set hostname. Имя хоста serverPort 'Port' : Field title on form to set port. Порт serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. URL двойного имени хоста serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. Двойной порт serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. Порт удалённого зеркала UDP serverDelay 'Delay' : Field title on form to set the delay of the tunnel. Задержка tunnelAddServer 'Add server' : Button for adding a server Добавить сервер tunnelApply 'Apply' : Button to apply changes. Применить SettingsUi pathsTitle Пути savedScreenshotsLabel Папка для сохранения снимков savedCallsLabel Папка для записи звонков languagesTitle Языки languagesLabel Язык systemLocale Системный cleanAvatars УДАЛИТЬ АВАТАРЫ cleanAvatarsDescription Вы уверены, что хотите удалить все аватары? downloadLabel Папка для сохранения загрузок setLocaleDescription Необходим перезапуск программы. Хотите перезапустить сейчас? otherTitle Прочее exitOnCloseLabel Завершать программу при закрытии окна dataTitle Данные пользовательского интерфейса autoStartLabel Автозапуск fontsTitle 'Fonts' : title of fonts section in settings Шрифты fontsTextChange 'Text Messages' : Label for changing text message fonts Текстовые сообщения fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts Выберите новый шрифт checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates Проверить обновления mipmapLabel 'Enable Mipmap' Включить мип-карту mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. Это свойство определяет, использует ли изображение фильтрацию MIP-карты при масштабировании или преобразовании. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. Фильтрация MIP-карт обеспечивает лучшее визуальное качество при уменьшении масштаба по сравнению со сглаживанием, но это может привести к снижению производительности (как при инициализации изображения, так и во время рендеринга). minimalTimelineFilterLabel 'Minimal Timeline filter' Фильтр минимальной шкалы времени minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : Показать минимальную версию того, что нужно отображать на временной шкале. versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version Настраиваемый versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Устройство-источник видео videoFramerateLabel Частота кадров videoCaptureTitle Параметры захвата видео videoPresetLabel Пресет видео presetDefault По умолчанию presetHighFps Высокая частота кадров presetCustom Настраиваемый videoSizeLabel Разрешение видео videoCodecsTitle Видео кодеки showCameraPreview ПРЕДПРОСМОТР ВИДЕО showVideoCodecsLabel Показать видео кодеки videoSettingsInCallWarning Видеозвонок выполняется: некоторые настройки недоступны. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. Звонки videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm OK SettingsWindow settingsTitle Настройки sipAccountsTab SIP аккаунты audioTab Аудио videoTab Видео callsAndChatTab Звонки и чат networkTab Сеть uiTab Интерфейс validButton OK uiAdvanced Дополнительные tunnelTab 'Tunnel' : Tab title for tunnel section in settings. Туннель SipAddressDialog cancel ОТМЕНА contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact Поиск в контактах contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip Найти адрес в своих контактах или использовать собственный. timelineSelectionHeader 'Conversations' : header for a selection in conversation list Разговоры SmartSearchBar addContact ДОБАВИТЬ КОНТАКТ Timeline timelineFilter A title for filtering mode. Фильтр timelineFilterAll 'All' The mode for timelines filtering. Все timelineFilterCustom 'Custom' The mode for timelines filtering. Пользовательский timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). Простые комнаты timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. Безопасные комнаты timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). Чат-группы timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. Недолговечные timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list Поиск в списке timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. Все уровни безопасности timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. Стандартные комнаты timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. Любые разговоры timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. Недолговечные вкл/выкл timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. Без недолговечных timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. Конференции TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction ИСПОЛЬЗОВАТЬ useAppSipAccountTitle ИСПОЛЬЗОВАТЬ АККАУНТ %1 useUsernameToLogin Использовать имя пользователя и пароль вместо номера телефона. quitWarning Ваш аккаунт был создан, но ещё не подтверждён. Вы уверены, что хотите выйти? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password Забытый пароль? UseAppSipAccountWithPhoneNumber countryLabel Страна phoneNumberLabel Номер телефона displayNameLabel Отображаемое имя (необязательно) UseAppSipAccountWithUsername usernameLabel Имя пользователя passwordLabel Пароль displayNameLabel Отображаемое имя (не обязательно) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form ИСПОЛЬЗОВАТЬ useOtherSipAccountTitle ИСПОЛЬЗОВАТЬ SIP АККАУНТ usernameLabel Имя пользователя displayNameLabel Отображаемое имя (не обязательно) sipDomainLabel Домен SIP passwordLabel Пароль transportLabel Транспорт addOtherSipAccountError Не удалось добавить этот аккаунт. understandAction 'I understand' : Popup confirmation for a warning Я понимаю warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name Для некоторых функций требуется аккаунт %1, например для группового обмена сообщениями или обмена недолговечными сообщениями. warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. Эти функции скрыты, когда вы регистрируетесь со сторонним SIP-аккаунтом. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. Чтобы включить их в коммерческом проекте, свяжитесь с нами. WaitingRoom cancelButton 'Cancel' : Cancel button. Отмена startButton 'Start' : Button label for starting the conference. Начать endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. Исходящий звонок incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. Входящий звонок ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Подтвердите следующую строку аутентификации с контактом. codeA Скажите: codeB Ваш контакт должен сказать: Later 'Later' : Button label to do something in another time. Позже Correct 'Correct' : Button label to confirm a code. Правильный title 'Communication security' : Title of popup for ZRTP confirmation. Безопасность связи country Afghanistan Афганистан Albania Албания Algeria Алжир AmericanSamoa Американское Самоа Andorra Андорра Angola Ангола Anguilla Ангилья AntiguaAndBarbuda Антигуа и Барбуда Argentina Аргентина Armenia Армения Aruba Аруба Australia Австралия Austria Австрия Azerbaijan Азербайджан Bahamas Багамы Bahrain Бахрейн Bangladesh Бангладеш Barbados Барбадос Belarus Беларусь Belgium Бельгия Belize Белиз Benin Бенин Bermuda Бермуды Bhutan Бутан Bolivia Боливия BosniaAndHerzegowina Босния и Герцеговина Botswana Ботсвана Brazil Бразилия Brunei Бруней Bulgaria Болгария BurkinaFaso Буркина-Фасо Burundi Бурунди Cambodia Камбоджа Cameroon Камерун Canada Канада CapeVerde Кабо-Верде CaymanIslands Каймановы острова CentralAfricanRepublic Центральноафриканская Республика Chad Чад Chile Чили China Китай Colombia Колумбия Comoros Коморы PeoplesRepublicOfCongo Республика Конго DemocraticRepublicOfCongo Демократическая Республика Конго CookIslands Острова Кука CostaRica Коста-Рика IvoryCoast Берег Слоновой Кости Croatia Хорватия Cuba Куба Cyprus Кипр CzechRepublic Чешская Республика Denmark Дания Djibouti Джибути Dominica Доминика DominicanRepublic Доминиканская Республика Ecuador Эквадор Egypt Египет ElSalvador Сальвадор EquatorialGuinea Экваториальная Гвинея Eritrea Эритрея Estonia Эстония Ethiopia Эфиопия FalklandIslands Фолклендские острова FaroeIslands Фарерские острова Fiji Фиджи Finland Финляндия France Франция FrenchGuiana Французская Гвиана FrenchPolynesia Французская Полинезия Gabon Габон Gambia Гамбия Georgia Грузия Germany Германия Ghana Гана Gibraltar Гибралтар Greece Греция Greenland Гренландия Grenada Гренада Guadeloupe Гваделупа Guam Гуам Guatemala Гватемала Guinea Гвинея GuineaBissau Гвинея-Бисау Guyana Гайана Haiti Гаити Honduras Гондурас HongKong Гонконг Hungary Венгрия Iceland Исландия India Индия Indonesia Индонезия Iran Иран Iraq Ирак Ireland Ирландия Israel Израиль Italy Италия Jamaica Ямайка Japan Япония Jordan Иордания Kazakhstan Казахстан Kenya Кения Kiribati Кирибати DemocraticRepublicOfKorea Демократическая Республика Корея RepublicOfKorea Республика Корея Kuwait Кувейт Kyrgyzstan Кыргызстан Laos Лаос Latvia Латвия Lebanon Ливан Lesotho Лесото Liberia Либерия Libya Ливия Liechtenstein Лихтенштейн Lithuania Литва Luxembourg Люксембург Macau Макао Macedonia Македония Madagascar Мадагаскар Malawi Малави Malaysia Малайзия Maldives Мальдивы Mali Мали Malta Мальта MarshallIslands Маршалловы Острова Martinique Мартиника Mauritania Мавритания Mauritius Маврикий Mayotte Майотта Mexico Мексика Micronesia Микронезия Moldova Молдова Monaco Монако Mongolia Монголия Montenegro Черногория Montserrat Монсеррат Morocco Марокко Mozambique Мозамбик Myanmar Мьянма Namibia Намибия NauruCountry Науру Nepal Непал Netherlands Нидерланды NewCaledonia Новая Каледония NewZealand Новая Зеландия Nicaragua Никарагуа Niger Нигер Nigeria Нигерия Niue Ниуэ NorfolkIsland Остров Норфолк NorthernMarianaIslands Северные Марианские острова Norway Норвегия Oman Оман Pakistan Пакистан Palau Палау PalestinianTerritories Палестинские территории Panama Панама PapuaNewGuinea Папуа-Новая Гвинея Paraguay Парагвай Peru Перу Philippines Филиппины Poland Польша Portugal Португалия PuertoRico Пуэрто-Рико Qatar Катар Reunion Воссоединение Romania Румыния RussianFederation Российская Федерация Rwanda Руанда SaintHelena Остров Святой Елены SaintKittsAndNevis Сент-Китс и Невис SaintLucia Сент-Люсия SaintPierreAndMiquelon Сен-Пьер и Микелон SaintVincentAndTheGrenadines Сент-Винсент и Гренадины Samoa Самоа SanMarino Сан-Марино SaoTomeAndPrincipe Сан-Томе и Принсипи SaudiArabia Саудовская Аравия Senegal Сенегал Serbia Сербия Seychelles Сейшелы SierraLeone Сьерра-Леоне Singapore Сингапур Slovakia Словакия Slovenia Словения SolomonIslands Соломоновы Острова Somalia Сомали SouthAfrica Южная Африка Spain Испания SriLanka Шри-Ланка Sudan Судан Suriname Суринам Swaziland Свазиленд Sweden Швеция Switzerland Швейцария Syria Сирия Taiwan Тайвань Tajikistan Таджикистан Tanzania Танзания Thailand Таиланд Togo Того Tokelau Токелау Tonga Тонга TrinidadAndTobago Тринидад и Тобаго Tunisia Тунис Turkey Турция Turkmenistan Туркменистан TurksAndCaicosIslands Острова Теркс и Кайкос Tuvalu Тувалу Uganda Уганда Ukraine Украина UnitedArabEmirates Объединенные Арабские Эмираты UnitedKingdom Соединенное Королевство UnitedStates Соединенные Штаты Америки Uruguay Уругвай Uzbekistan Узбекистан Vanuatu Вануату Venezuela Венесуэла Vietnam Вьетнам WallisAndFutunaIslands Острова Уоллис и Футуна Yemen Йемен Zambia Замбия Zimbabwe Зимбабве utils downloadCodecDescription Вы хотите загрузить %1 (%2)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' %1 неделя %1 неделя %1 неделя formatDays '%1 day' %1 день %1 день %1 день formatHours '%1 hour' %1 час %1 час %1 час formatMinutes '%1 minute' %1 минута %1 минута %1 минута formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/sv.ts000066400000000000000000005127671434616504300236730ustar00rootroot00000000000000 About ok OK aboutTranslation 'Help us translate %1' : %1 is the application name ActivateAppSipAccountWithEmail activateAppSipAccount AKTIVERA DITT %1-KONTO confirmAction AKTIVERA activationSteps För att aktivera ditt konto: Följ anvisningarna som vi skickade till %1, och klicka sedan nedan. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount AKTIVERA DITT %1-KONTO confirmAction AKTIVERA activationSteps Vi har skickat ett SMS med en valideringskod till %1. För att slutföra din telefonnummerverifiering, ange den 4-siffriga koden nedan. App commandLineOptionVerbose logga till stdout viss felsökningsinformation under körning commandLineOptionConfig specificera %1 konfigurationsfilen som skall användas applicationDescription En gratis (libre) SIP-videotelefon. commandLineOptionIconified starta i systemfältet, visa inte huvudgränssnittet commandLineOptionConfigArg fil commandLineOptionHelp visa denna hjälp commandLineOptionVersion visa appversion commandLineOptionCliHelp Visar hjälpmenyn för att använda %1 med kommandotolken commandLineDescription skicka en order till programmet via en kommandrad restore Återställ quit Avsluta settings Inställningar about Om commandLineOptionFetchConfig commandLineOptionFetchConfigArg commandLineOptionCall commandLineOptionCallArg SIP-adress checkForUpdates AssistantAbstractView back TILLBAKA AssistantHome useAppSipAccount ANVÄND ETT %1-KONTO useOtherSipAccount ANVÄND ETT SIP-KONTO fetchRemoteConfiguration HÄMTA FJÄRRKONFIGURATION homeTitle VÄLKOMMEN homeDescription Denna assistent hjälper dig att konfigurera och använda ditt %1-konto. createAppSipAccount SKAPA ETT %1-KONTO homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. AssistantModel loginWithUsernameFailed Inloggningen misslyckades. Kontrollera ditt användarnamn/lösenord. usernameStatusTooShort För kort! (minst %1 tecken) usernameStatusTooLong För långt! (max %1 tecken) usernameStatusInvalidCharacters Ogiltiga tecken upptäckta. (regex: `%1`) usernameStatusInvalid Ogiltigt användarnamn. passwordStatusTooShort För kort! (minst %1 tecken) passwordStatusTooLong För långt! (max %1 tecken) passwordStatusInvalidCharacters Ogiltiga tecken upptäckta. (regex: `%1`) passwordStatusMissingCharacters Saknade tecken: `%1`. requestFailed Det gick inte att skicka förfrågan. emailStatusMalformed Felaktig e-postadress. emailStatusMalformedInvalidCharacters Felaktig e-postadress eller ogiltiga tecken. cannotSendSms Serverfel: det går inte att skicka sms. accountAlreadyExists Detta konto finns redan. smsActivationFailed SMS-aktivering misslyckades! emailActivationFailed Kontrollera att du har validerat ditt konto eller försök igen. phoneNumberStatusInvalid Ogiltigt telefonnummer! phoneNumberStatusTooShort För kort! phoneNumberStatusTooLong För långt! phoneNumberStatusInvalidCountryCode Ogiltig landskod! loginWithPhoneNumberFailed Inloggningen misslyckades. Kolla ditt telefonnummer. unableToAddAccount Det gick inte att lägga till det här kontot. AuthenticationRequest cancel AVBRYT confirm LOGGA IN identityLabel Identitet passwordLabel Lösenord authenticationRequestDescription Det går inte att autentisera. Verifiera ditt lösenord. userIdLabel Användar-ID (valfritt) realmLabel Domän CallModel callStatsCodec Kodek callStatsUploadBandwidth Sändningsbandbredd callStatsDownloadBandwidth Hämtningsbandbredd callStatsEstimatedDownloadBandwidth Beräknad hämtningsbandbredd callStatsIceState ICE-tillstånd callStatsIpFamily IP-familj callStatsSenderLossRate Förlusthastighet för avsändare callStatsReceiverLossRate Förlusthastighet för mottagare callStatsJitterBuffer Skakbuffert callStatsSentVideoDefinition Skickad videodefinition callStatsReceivedVideoDefinition Mottagen videodefinition iceStateNotActivated Inte aktiverad iceStateFailed Misslyckades iceStateInProgress Pågående iceStateReflexiveConnection Reflexiv anslutning iceStateHostConnection Värdanslutning iceStateRelayConnection Reläanslutning iceStateInvalid Ogiltig callErrorDeclined Fjärrparten avböjde samtalet. callErrorNotFound Fjärrparten hittades inte. callErrorBusy Fjärrparten är upptagen. callErrorNotAcceptable Fjärrparten kan inte acceptera samtalet. callStatsReceivedFramerate Mottagen bildfrekvens callStatsSentFramerate Skickad bildfrekvens callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics Mediekryptering callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel AVBRYT callSipAddressDescription Starta ett nytt samtal. CallStatistics audioStatsLabel Ljud videoStatsLabel Video mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section Mediekryptering CallTransfer cancel AVBRYT callTransferDescription Vill du överföra detta samtalet? Calls acceptAudioCall ACCEPTERA LJUDSAMTAL acceptVideoCall ACCEPTERA VIDEOSAMTAL terminateCall LÄGG PÅ resumeCall ÅTERUPPTA SAMTAL transferCall ÖVERFÖR SAMTAL callPause PAUSA SAMTAL attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CallsWindow callsTitle Samtal acceptClosingDescription Är du säker på att du vill avsluta alla samtal? Chat newMessagePlaceholder Ange ditt meddelande noFileTransferUrl Det gick inte att skicka filen. Serverwebbadressen är inte konfigurerad. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 skriver… %1 skriver… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. conferencesCopiedICS ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. icsDescription 'Description' : Title for the meeting description. Beskrivning icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. icsJoinButton 'Join' : Action button to join the meeting. deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. Beskrivning icsJoinButton 'Join' : Action button to join the meeting. icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. icsParticipants '%1 participant' : number(=%1) of participant. icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) Avbryt fileTransferDownload 'Download' : Message link to download a file Ladda ner ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Kopiera menuPlayMe Spela mig! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Leveransstatus menuDelete 'Delete' : Item menu to delete a message Ta bort menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message menuForward 'Forward' : Forward a message from menu menuReply 'Reply' : Reply to a message from menu Svara ChatNoticeModel nMinute nHour nDay nWeek ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Svara ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Cli appCliDescription Sätt att styra %1-programmet via kommandoraden. uriCommandLineSyntax %1-<sip-address>?method=<method>([&<argument>=<base64-value>]*) cliCommandLineSyntax %1 "<method> ([<argument>=<value>]*)" commandsName lista över kommandon : showFunctionDescription Visa huvudfönstret av programmet. callFunctionDescription Starta ett samtal till sip-adressen. initiateConferenceFunctionDescription Starta en konferens. joinConferenceFunctionDescription Gå med i konferensen som sip-adressen är värd för, som visningsnamn. Om du är ansluten till en proxy-konfiguration, se gå-med-i-konferens-som. joinConferenceAsFunctionDescription Gå med i konferensen som sip-adressen står som värd för med gäst-sip-adress. Om du inte är ansluten till en proxy-konfiguration, see gå-med-i-konferens. byeFunctionDescription CodecsViewer codecMime Namn codecEncoderDescription Beskrivning codecEncoderClockRate Hastighet (Hz) codecBitrate Bithastighet (Kbit/s) codecRecvFmtp Parametrar codecStatus Status Conference conferenceTitle KONFERENS ConferenceControls conference KONFERENS ConferenceManager conferenceManagerDescription Hantera deltagare till din konferens. cancel AVBRYT confirm BÖRJA Conferences conferencesTitle 'Meetings' : Conference list title. conferencesEndedFilter 'Finished' : Filter meetings on end status. conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel AVBRYT confirm BEKRÄFTA ContactEdit removeContactDescription Vill du verkligen ta bort denna kontakt från din adressbok? sipAccounts SIP-KONTO(N) address ADRESS emails E-POSTADRESS(ER) webSites WEBBPLATS(ER) avatarChooserTitle Välj din avatar companies FÖRETAG save SPARA cancel AVBRYT sipAccountsPlaceholder SIP-konto companiesPlaceholder Företag emailsPlaceholder E-post webSitesPlaceholder Webbplats street Väg postalCode Postnummer country Land locality Lokalitet abortEditDescriptionText Är du säker på att du vill avbryta kontaktändringen? tooltipShowConversation Visa konversation missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Contacts searchContactPlaceholder Sök kontakt selectAllContacts Alla selectConnectedContacts Ansluten addContact LÄGG TILL KONTAKT removeContactDescription Vill du verkligen ta bort denna kontakt från din adressbok? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Conversation displayCallsAndMessages ALLA displayCalls SAMTAL displayMessages MEDDELANDEN removeAllEntriesDescription Är du säker på att du vill rensa den här historiken? tooltipContactEdit Redigera kontakt tooltipContactAdd Lägg till kontakt cleanHistory Radera historik adminStatus 'Admin' : Admin(istrator) One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list conversationMenuDelete 'Delete history' : Item menu to delete the chat's history conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription Hur vill du skapa ditt konto? createAppSipAccountTitle SKAPA ETT %1-KONTO withPhoneNumber MED ETT TELEFONNUMMER withEmailAddress MED E-POSTADRESS CreateAppSipAccountWithEmail createAppSipAccountTitle SKAPA ETT %1-KONTO confirmAction SKAPA usernameLabel Användarnamn emailLabel E-post passwordLabel Lösenord passwordConfirmationLabel Lösenordsbekräftelse passwordConfirmationError Lösenorden du angav matchade inte. quitWarning Ditt konto har skapats men har ännu inte validerats. Är du säker på att du vill avsluta den här vyn? displayNameLabel Visningsnamn (valfritt) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle SKAPA ETT %1-KONTO countryLabel Land phoneNumberLabel Telefonnummer usernameLabel Användarnamn displayNameLabel Visningsnamn (valfritt) confirmAction SKAPA quitWarning Ditt konto har skapats men har ännu inte validerats. Om du avslutar den här vyn måste du manuellt lägga till och validera ditt konto inom 24 timmar. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Välj en eller flera filer dropYourAttachment Släpp din bilaga attachmentTooltip Skicka en fil EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation startButton 'start' : button text to start ephemeral mode ephemeralTitle "Ephemeral messages" : Popup title for ephemerals ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Warning about not being in conference based chat room. disabled 'Disabled' nMinute '%1 minute' nHour '%1 hour' nDay '%1 day' nWeek '%1 week' Event incomingCall Inkommande samtal outgoingCall Utgående samtal declinedIncomingCall Avböjt inkommande samtal declinedOutgoingCall Avböjt utgående samtal endedCall Avslutat samtal missedIncomingCall Missat inkommande samtal missedOutgoingCall Missat utgående samtal FetchRemoteConfiguration confirmAction HÄMTA fetchRemoteConfigurationTitle HÄMTA FJÄRRKONFIGURATION urlLabel WEBBADRESS remoteProvisioningError Kan inte sätta denna fjärravsättnings-uri. remoteProvisioningUpdateDescription Det är nödvändigt att starta om programmet. Vill du starta om nu? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Är du säker på att du vill rensa den här historiken? tooltipContactEdit Redigera kontakt tooltipContactAdd Lägg till kontakt cleanHistory Radera historik Home howToDescription Behöver du hjälp med hur du använder %1? howToTitle HUR DU ANVÄNDER %1 inviteDescription Bjud in dina vänner på %1. inviteTitle BJUD IN DINA VÄNNER accountAssistantDescription Skapa eller hantera ditt %1-konto. accountAssistantTitle KONTO ASSISTENT assistantButton ASSISTENT showTooltips Visa verktygstips inviteButton BJUD IN Incall acceptVideoDescription Din kontakt vill gärna slå på video. securedStringFormat Samtalet är krypterat med: %1. callNotSecured Samtal inte krypterat. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. incallMenuTitle 'Settings' : Main menu title for settings. incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label ok 'OK' : Button label OK addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status chatRoomDetailsTitle "Group information" : Popup title. popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation callButton 'CALL' : Button that lead to a call okButton 'OK' : Button that validate the popup to be redirected to the device list infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Explanation process InviteFriends enterEmailLabel Väns e-postadress messageLabel Meddelande cancel AVBRYT confirm BEKRÄFTA inviteFriendsTitle Bjud in vänner defaultMessage %1 vill bjuda in dig på %2! defaultSubject %1 inbjudan forcedMessage Hämta programmet på din dator och börja ringa och chatta med %1-användare gratis. Klicka här: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 Om %1 Preferences... Inställningar Services Tjänster Hide %1 Göm %1 Hide Others Göm Övriga Show All Visa Alla Quit %1 Avsluta %1 MainWindow mainSearchBarPlaceholder Sök kontakt, starta ett samtal eller en chatt… contactsEntry KONTAKTER autoAnswerStatus automatiskt smartSearchBarTooltip Använd det intelligenta sökfältet för att omedelbart starta ljud- och videosamtal, skicka ett meddelande, eller lägga till en ny kontakt. Ange helt enkelt din väns SIP-adress eller användarnamn. newConferenceButton Starta konferenssamtal newChatRoom 'Start a chat room' : Tooltip to illustrate a button hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline openTimeline 'Open Timeline' : Tooltip for a button that open the timeline openHome 'Open Home' : Tooltip for a button that open the home view mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Inställningar about Om quit Avsluta checkForUpdates 'Check for updates' : Item menu for checking updates MainWindowTopMenuBar settings Inställningar about Ungefär quit Sluta checkForUpdates 'Check for updates' : Item menu for checking updates ManageAccounts ok OK selectPresenceLabel Närvarostatus selectAccountLabel Aktivt konto MultimediaParametersDialog ok OK menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button startButton 'Launch' : Start button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. subjectLabel 'Subject' : Label of a text field about the subject of the chat room subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject LastContactsTitle 'Last contacts' : Header for showing last contacts NewConference cancelButton 'Cancel' : Cancel button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject subjectTooltip 'Current subject of the Meeting. It cannot be empty' Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. Notifier newVersionAvailable En ny version (%1) är tillgänglig! newFileMessage Ny bilaga mottagen! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm BEKRÄFTA onlineInstallerExtractingDescription Extraherar %1... onlineInstallerDownloadingDescription Hämtar %1... onlineInstallerFinishedDescription % 1 är nu installerad! onlineInstallerFailedDescription Misslyckades med att installera% 1! OutgoingMessage messageError Fel messageRead Läs messageDelivered Levererad ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline Tillgänglig presenceBusy Upptagen presenceDoNotDisturb Stör inte presenceOffline Frånkopplad QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. SettingsAdvanced logsTitle Loggar logsFolderLabel Loggmapp sendLogs SKICKA LOGGAR logsUploadUrlLabel Server-URL för loggar logsUploadFailed Misslyckades med att sända loggar. logsEnabledLabel Aktivera loggar cleanLogs RENSA LOGGAR cleanLogsDescription Är du säker på att du vill ta bort alla loggar? developerSettingsTitle Utvecklarinställningar developerSettingsEnabledLabel Aktivera utvecklarinställningar logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Mailer kan inte hittas, men loggarna laddades upp till %1 logsMailerSuccess Loggar laddades upp till %1 contactsTitle Kontakter noPlugin 'No Plugins to load' : Text in combobox viewlogs SettingsAudio audioTitle Ljudparametrar playbackDeviceLabel Uppspelningsenhet captureDeviceLabel Inspelningsenhet ringerDeviceLabel Ringenhet ringLabel Ring echoCancellationLabel Aktivera ekodämpning audioCodecsTitle Ljudkodek showAudioCodecsLabel Visa ljudkodek playbackGainLabel Uppspelning captureGainLabel Fånga vinst audioTestLabel Fånga nivå audioSettingsInCallWarning Ljudsamtal pågår: vissa inställningar är inte tillgängliga. echoCancellationCalibrationLabel calibratingEchoCancellationInProgress calibratingEchoCancellationDone calibratingEchoCancellationFailed calibratingEchoCancellationNone SettingsCallsChat fileServerLabel Filserver encryptWithLimeLabel Kryptera med LIME limeDisabled Inaktiverad limeRequired Obligatorisk limePreferred Föredraget chatTitle Chatt callsTitle Samtal encryptionLabel Kryptering noEncryption Inga autoAnswerLabel Automatiskt svar autoAnswerDelayLabel Fördröjning (i ms) autoAnswerWithVideoLabel Automatiskt svar (med video) chatEnabledLabel Aktivera chatt callRecorderEnabledLabel Aktivera samtalsinspelare chatNotificationSoundEnabledLabel Aktivera aviseringsljud chatNotificationSoundLabel Aviseringsljud conferenceEnabledLabel Aktivera konferens contactsTitle Kontakter contactsEnabledLabel Aktivera kontakter muteMicrophoneEnabledLabel Aktivera tysta mikrofon outgoingCallsEnabledLabel Aktivera utgående samtal showTelKeypadAutomaticallyLabel Visa telefonens knappsats automatiskt automaticallyRecordCallsLabel Spela in samtal automatiskt keepCallsWindowInBackgroundLabel Behåll samtalsfönster i bakgrunden callPauseEnabledLabel Håll samtal aktiverat encryptionMandatoryLabel Kryptering är obligatorisk hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer SettingsLdapEdit cancel AVBRYT confirm displayNameLabel Visningsnamn (valfritt) displayNameTooltip connectionTitle serverLabel serverTooltip bindDNLabel bindDNTooltip passwordLabel Lösenord useTLSLabel useTLSTooltip useSalLabel useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' verifyTLSLabel AutoMode offMode onMode verifyTLSTooltip searchTitle baseObjectLabel baseObjectPlaceholder baseObjectTooltip filterLabel filterTooltip maxResultsLabel maxResultsTooltip timeoutLabel timeoutTooltip parsingTitle nameAttributesLabel nameAttributesTooltip sipAttributesLabel sipAttributesTooltip domainLabel domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. miscLabel debugLabel debugTooltip SettingsNetwork sendDtmfsLabel DTMF:s sändningsmetod allowIpV6Label Tillåt IPv6 transportTitle Transport natAndFirewallTitle NAT och brandvägg enableIceLabel Aktivera ICE stunServerLabel STUN/TURN-server enableTurnLabel Aktivera TURN turnUserLabel TURN-användare turnPasswordLabel TURN-lösenord networkProtocolAndPortsTitle Nätverksprotokoll och portar sipUdpPortLabel SIP UDP-port sipTcpPortLabel SIP TCP-port audioRtpUdpPortLabel Ljud-RTP UDP-port videoRtpUdpPortLabel Video-RTP UDP-port dscpFieldsTitle DSCP-fält sipFieldLabel SIP audioRtpStreamFieldLabel Audio RTP-ström videoRtpStreamFieldLabel Video RTP-ström bandwidthControlTitle Bandbreddskontroll downloadSpeedLimitLabel Hämtningshastighetsgräns i Kbit/sek uploadSpeedLimitLabel Sändningshastighetsgräns i Kbit/sek enableAdaptiveRateControlLabel Aktivera adaptiv hastighetskontroll presenceTitle Närvaro rlsUriLabel Använd RLS-URI rlsUriAuto AUTOMATISKT rlsUriDisabled ALDRIG showNetworkSettingsLabel Visa nätverksinställningar generalTitle Allmänt SettingsSipAccounts defaultIdentityTitle Standardidentitet defaultUsernameLabel Användarnamn defaultSipAddressLabel SIP-adress proxyAccountsTitle Proxy-konton eraseAllPasswords RADERA LÖSENORD addAccount LÄGG TILL KONTO editHeader Redigera deleteHeader Ta bort deleteAccountDescription Är du säker på att du vill radera detta konto? eraseAllPasswordsDescription Är du säker på att du vill radera alla lösenord? defaultDisplayNameLabel Visningsnamn assistantTitle Assistent createAppSipAccountEnabledLabel Aktivera kontoupprättning useAppSipAccountEnabledLabel Aktivera kontoanvändning useOtherSipAccountEnabledLabel Aktivera generisk kontoanvändning fetchRemoteConfigurationEnabledLabel Aktivera konfigurationshämtning assistantSupportsPhoneNumbersLabel Stöder telefonnummer defaultDeviceNameLabel 'Device Name' : Label for setting the device name. webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel SIP-adress transportLabel Transport serverAddressLabel SIP-serveradress registrationDurationLabel Registreringsvaraktighet (sek) routeLabel Rutt contactParamsLabel Kontaktparametrar publishPresenceLabel Publicera närvaroinformation avpfIntervalLabel AVPF normalt RTCP-intervall (sek) registerEnabledLabel Registrera avpfEnabledLabel Aktivera AVPF cancel AVBRYT confirm BEKRÄFTA invalidSipAddress Ogiltig SIP-adress. invalidServerAddress Ogiltig serveradress. invalidRoute Ogiltig rutt. enableIceLabel Aktivera ICE stunServerLabel STUN/TURN-server enableTurnLabel Aktivera TURN turnUserLabel TURN-användare turnPasswordLabel TURN-lösenord natAndFirewallTitle NAT och brandvägg mainSipAccountSettingsTitle Huvud SIP-kontoinställningar conferenceURI "Conference URI" : Label of a text edit for filling Conference URI invalidConferenceURI "invalid conference URI" : Error text about conference URI videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) tunnelDomain 'Domain' : Field title of a textfield to set domain. tunnelUsername 'Username' : Field title of a textfield to set username. tunnelSIP 'SIP' : Field title of a switch to set SIP mode. cancel 'Cancel' : Button to cancel the action. AVBRYT setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. proxyHttpHost 'Host' : Placeholder to set hostname. proxyHttpPort 'Port' : Placehoilder to set port. proxyHttpUsername 'Username' : Placeholder to set username. proxyHttpPassword 'Password' : Placeholder to set password. proxyHttpApply 'Apply' : Button to set proxy from changes. serverMode 'Mode' : Field title on form to set tunnel mode. serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. serverTitle 'Server' : Title form to set a server serverHostname 'Hostname' : Field title on form to set hostname. serverPort 'Port' : Field title on form to set port. serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. serverDelay 'Delay' : Field title on form to set the delay of the tunnel. tunnelAddServer 'Add server' : Button for adding a server tunnelApply 'Apply' : Button to apply changes. SettingsUi pathsTitle Sökvägar savedScreenshotsLabel Mapp med sparade skärmdumpar savedCallsLabel Mapp med sparade samtal languagesTitle Språk languagesLabel Språk systemLocale Systemlokal cleanAvatars RADERA AVATARER cleanAvatarsDescription Är du säker på att du vill radera alla avatarer? downloadLabel Hämtningsmapp setLocaleDescription Det är nödvändigt att starta om programmet. Vill du starta om nu? otherTitle Annat exitOnCloseLabel Avsluta app på stäng fönster dataTitle Användargränssnittsdata autoStartLabel Autostarta appen fontsTitle 'Fonts' : title of fonts section in settings fontsTextChange 'Text Messages' : Label for changing text message fonts fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates mipmapLabel 'Enable Mipmap' mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Videoinmatningsenhet videoFramerateLabel Bildfrekvens videoCaptureTitle Videoinspelningsparametrar videoPresetLabel Videoförinställning presetDefault Standard presetHighFps Hög FPS presetCustom Anpassad videoSizeLabel Videoupplösning videoCodecsTitle Videokodekar showCameraPreview VIDEOFÖRHANDSVISNING showVideoCodecsLabel Visa videokodekar videoSettingsInCallWarning Videosamtal pågår: vissa inställningar är inte tillgängliga. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm OK SettingsWindow settingsTitle Inställningar sipAccountsTab SIP-konto audioTab Ljud videoTab Video callsAndChatTab Samtal och chatt networkTab Nätverk uiTab Användargränssnitt validButton OK uiAdvanced Avancerat tunnelTab 'Tunnel' : Tab title for tunnel section in settings. SipAddressDialog cancel AVBRYT contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list SmartSearchBar addContact LÄGG TILL KONTAKT Timeline timelineFilter A title for filtering mode. timelineFilterAll 'All' The mode for timelines filtering. timelineFilterCustom 'Custom' The mode for timelines filtering. timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction ANVÄND useAppSipAccountTitle ANVÄND ETT %1-KONTO useUsernameToLogin Använd användarnamn och lösenord istället för ditt telefonnummer. quitWarning Ditt konto har skapats men har ännu inte validerats. Är du säker på att du vill avsluta den här vyn? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password UseAppSipAccountWithPhoneNumber countryLabel Land phoneNumberLabel Telefonnummer displayNameLabel Visningsnamn (valfritt) UseAppSipAccountWithUsername usernameLabel Användarnamn passwordLabel Lösenord displayNameLabel Visningsnamn (valfritt) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form ANVÄND useOtherSipAccountTitle ANVÄND ETT SIP-KONTO usernameLabel Användarnamn displayNameLabel Visningsnamn (valfritt) sipDomainLabel SIP-domän passwordLabel Lösenord transportLabel Transport addOtherSipAccountError Det gick inte att lägga till det här kontot. understandAction 'I understand' : Popup confirmation for a warning warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. startButton 'Start' : Button label for starting the conference. endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Bekräfta följande SAS med partner codeA Säg: codeB Din kontaktperson bör säga: Later 'Later' : Button label to do something in another time. Senare Correct 'Correct' : Button label to confirm a code. Korrigera title 'Communication security' : Title of popup for ZRTP confirmation. Kommunikationssäkerhet country Afghanistan Afghanistan Albania Albanien Algeria Algeriet AmericanSamoa Amerikanska Samoa Andorra Andorra Angola Angola Anguilla Anguilla AntiguaAndBarbuda Antigua och Barbuda Argentina Argentina Armenia Armenien Aruba Aruba Australia Australien Austria Österrike Azerbaijan Azerbajdzjan Bahamas - Bahamas Bahrain Bahrain Bangladesh Bangladesh Barbados Barbados Belarus Vitryssland Belgium Belgien Belize Belize Benin Benin Bermuda Bermuda Bhutan Bhutan Bolivia Bolivia BosniaAndHerzegowina Bosnien och Hercegovina Botswana Botswana Brazil Brasilien Brunei Brunei Bulgaria Bulgarien BurkinaFaso Burkina Faso Burundi Burundi Cambodia Kambodja Cameroon Kamerun Canada Kanada CapeVerde Kap Verde CaymanIslands Caymanöarna CentralAfricanRepublic Centralafrikanska republiken Chad Tchad Chile Chile China Kina Colombia Colombia Comoros Komorerna PeoplesRepublicOfCongo Republiken Kongo DemocraticRepublicOfCongo Demokratiska republiken Kongo CookIslands Cooköarna CostaRica Costa Rica IvoryCoast Elfenbenskusten Croatia Kroatien Cuba Kuba Cyprus Cypern CzechRepublic Tjeckien Denmark Danmark Djibouti Djibouti Dominica Dominica DominicanRepublic Dominikanska republiken Ecuador Ecuador Egypt Egypten ElSalvador El Salvador EquatorialGuinea Ekvatorialguinea Eritrea Eritrea Estonia Estland Ethiopia Etiopien FalklandIslands Falklandsöarna FaroeIslands Färöarna Fiji Fiji Finland Finland France Frankrike FrenchGuiana Franska Guyana FrenchPolynesia Franska Polynesien Gabon Gabon Gambia Gambia Georgia Georgien Germany Tyskland Ghana Ghana Gibraltar Gibraltar Greece Grekland Greenland Grönland Grenada Grenada Guadeloupe Guadeloupe Guam Guam Guatemala Guatemala Guinea Guinea GuineaBissau Guinea-Bissau Guyana Guyana Haiti Haiti Honduras Honduras HongKong Hongkong Hungary Ungern Iceland Island India Indien Indonesia Indonesien Iran Iran Iraq Irak Ireland Irland Israel Israel Italy Italien Jamaica Jamaica Japan Japan Jordan Jordanien Kazakhstan Kazakstan Kenya Kenya Kiribati Kiribati DemocraticRepublicOfKorea Demokratiska republiken Korea RepublicOfKorea Republiken Korea Kuwait Kuwait Kyrgyzstan Kirgizistan Laos Laos Latvia Lettland Lebanon Libanon Lesotho Lesotho Liberia Liberia Libya Libyen Liechtenstein Liechtenstein Lithuania Litauen Luxembourg Luxemburg Macau Macao Macedonia Makedonien Madagascar Madagaskar Malawi Malawi Malaysia Malaysia Maldives Maldiverna Mali Mali Malta Malta MarshallIslands Marshallöarna Martinique Martinique Mauritania Mauretanien Mauritius Mauritius Mayotte Mayotte Mexico Mexiko Micronesia Mikronesien Moldova Moldavien Monaco Monaco Mongolia Mongoliet Montenegro Montenegro Montserrat Montserrat Morocco Marocko Mozambique Moçambique Myanmar Myanmar Namibia Namibia NauruCountry Nauru Nepal Nepal Netherlands Nederländerna NewCaledonia Nya Kaledonien NewZealand Nya Zeeland Nicaragua Nicaragua Niger Niger Nigeria Nigeria Niue Niue NorfolkIsland Norfolkön NorthernMarianaIslands Nordmarianerna Norway Norge Oman Oman Pakistan Pakistan Palau Palau PalestinianTerritories Palestinska territorier Panama Panama PapuaNewGuinea Papua Nya Guinea Paraguay Paraguay Peru Peru Philippines Filippinerna Poland Polen Portugal Portugal PuertoRico Puerto Rico Qatar Qatar Reunion Återförening Romania Rumänien RussianFederation Ryssland Rwanda Rwanda SaintHelena Sankt Helena SaintKittsAndNevis Saint Kitts och Nevis SaintLucia Saint Lucia SaintPierreAndMiquelon Saint-Pierre och Miquelon SaintVincentAndTheGrenadines Saint Vincent Och Grenadinerna Samoa Samoa SanMarino San Marino SaoTomeAndPrincipe São Tomé och Principe SaudiArabia Saudiarabien Senegal Senegal Serbia Serbien Seychelles Seychellerna SierraLeone Sierra Leone Singapore Singapore Slovakia Slovakien Slovenia Slovenien SolomonIslands Salomonöarna Somalia Somalia SouthAfrica Sydafrika Spain Spanien SriLanka Sri Lanka Sudan Sudan Suriname Surinam Swaziland Swaziland Sweden Sverige Switzerland Schweiz Syria Syrien Taiwan Taiwan Tajikistan Tadzjikistan Tanzania Tanzania Thailand Thailand Togo Togo Tokelau Tokelau Tonga Tonga TrinidadAndTobago Trinidad Och Tobago Tunisia Tunisien Turkey Kalkon Turkmenistan Turkmenistan TurksAndCaicosIslands Turks- och Caicosöarna Tuvalu Tuvalu Uganda Uganda Ukraine Ukraina UnitedArabEmirates Förenade Arabemiraten UnitedKingdom Förenade kungariket UnitedStates USA Uruguay Uruguay Uzbekistan Uzbekistan Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietnam WallisAndFutunaIslands Wallis- och Futunaöarna Yemen Jemen Zambia Zambia Zimbabwe Zimbabwe utils downloadCodecDescription Vill du hämta %1 (%2)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/tr.ts000066400000000000000000005210161434616504300236530ustar00rootroot00000000000000 About ok TAMAM aboutTranslation 'Help us translate %1' : %1 is the application name %s'u çevirmemize yardım et ActivateAppSipAccountWithEmail activateAppSipAccount %1 HESABINIZI ETKİNLEŞTİRİN confirmAction ETKİNLEŞTİR activationSteps Hesabınızı etkinleştirmek için: %1 adresine gönderdiğimiz yönergeleri takip edin ardından aşağıdakine tıklayın. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount %1 HESABINIZI ETKİNLEŞTİRİN confirmAction ETKİNLEŞTİR activationSteps %1 numarasına doğrulama kodu içeren SMS gönderdik. Telefon numarası doğrulamanızı tamamlamak için lütfen 4 haneli kodu aşağıya girin. App commandLineOptionVerbose çalışırken bazı hata ayıklama bilgilerini stdout'a günlükle commandLineOptionConfig kullanılacak %1 yapılandırma dosyasını belirt applicationDescription Özgür SIP görüntülü telefonu. commandLineOptionIconified sistem tepsisinde başla, ana arayüzü gösterme commandLineOptionConfigArg dosya commandLineOptionHelp bu yardımı göster commandLineOptionVersion uygulama sürümünü göster commandLineOptionCliHelp %1'u komut satırı arayüzüyle kullanmak için yardım menüsünü gösterir commandLineDescription komut satırıyla uygulamaya emir gönder restore Kurtar quit Çıkış settings Tercihler about Hakkında commandLineOptionFetchConfig Alınacak %1 yapılandırma dosyasını belirtin. Bu, geçerli yapılandırmayla birleştirilecektir. commandLineOptionFetchConfigArg URL, yol veya dosya commandLineOptionCall çağrı yap commandLineOptionCallArg SIP adresi checkForUpdates Güncellemeleri denetle AssistantAbstractView back GERİ AssistantHome useAppSipAccount %1 HESABI KULLAN useOtherSipAccount SIP HESABI KULLAN fetchRemoteConfiguration UZAKTAN YAPILANDIRMA AL homeTitle HOŞ GELDİNİZ homeDescription Bu yardımcı, %1 hesabınızı yapıldırmanız ve kullanmanız için size yardım edecek. createAppSipAccount %1 HESABI OLUŞTUR homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. %1 %2kullanım koşullarını%3 ve %4gizlilik ilkesini%5 kabul ediyorum AssistantModel loginWithUsernameFailed Giriş başarısız. Lütfen kullanıcı adı/parolanızı denetleyin. usernameStatusTooShort Çok kısa! (en az %1 karakter) usernameStatusTooLong Çok uzun! (en çok %1 karakter) usernameStatusInvalidCharacters Geçersiz karakter saptandı. (regex: `%1`) usernameStatusInvalid Geçersiz kullanıcı adı. passwordStatusTooShort Çok kısa! (en az %1 karakter) passwordStatusTooLong Çok uzun! (en çok %1 karakter) passwordStatusInvalidCharacters Geçersiz karakter saptandı. (regex: `%1`) passwordStatusMissingCharacters Eksik karakterler: `%1`. requestFailed İstek gönderilemiyor. emailStatusMalformed Doğru olmayan e-posta adresi. emailStatusMalformedInvalidCharacters Doğru olmayan e-posta adresi veya geçersiz karakterler. cannotSendSms Sunucu hatası: sms gönderilemedi. accountAlreadyExists Bu hesap zaten var. smsActivationFailed SMS etkinleştirme başarısız! emailActivationFailed Lütfen hesabınızı doğruladığınızı onaylayın veya yeniden deneyin. phoneNumberStatusInvalid Geçersiz telefon numarası! phoneNumberStatusTooShort Çok kısa! phoneNumberStatusTooLong Çok uzun! phoneNumberStatusInvalidCountryCode Geçersiz ülke kodu! loginWithPhoneNumberFailed Giriş başarısız. Lütfen telefon numaranızı denetleyin. unableToAddAccount Bu hesap eklenemiyor. AuthenticationRequest cancel İPTAL confirm GİR identityLabel Kimlik passwordLabel Parola authenticationRequestDescription Yetkilendirilemiyor. Lütfen parolanızı onaylayın. userIdLabel Kullanıcı kimliği (isteğe bağlı) realmLabel Yetki alanı CallModel callStatsCodec Çözücü callStatsUploadBandwidth Yükleme bant genişliği callStatsDownloadBandwidth İndirme bant genişliği callStatsEstimatedDownloadBandwidth Hesaplanan indirme bant genişliği callStatsIceState ICE durumu callStatsIpFamily IP ailesi callStatsSenderLossRate Gönderici kayıp oranı callStatsReceiverLossRate Alıcı kayıp oranı callStatsJitterBuffer Gecikme arabelleği callStatsSentVideoDefinition Giden görüntü tanımı callStatsReceivedVideoDefinition Alınan görüntü tanımı iceStateNotActivated Etkin değil iceStateFailed Başarısız iceStateInProgress Sürüyor iceStateReflexiveConnection Dönüşlü bağlantı iceStateHostConnection Ana makine bağlantısı iceStateRelayConnection Röle bağlantısı iceStateInvalid Geçersiz callErrorDeclined Uzak şahıs çağrıyı reddetti. callErrorNotFound Uzak şahıs bulunamadı. callErrorBusy Uzak şahıs meşgul. callErrorNotAcceptable Uzak şahıs çağrıyı kabul edemez. callStatsReceivedFramerate Alınan çerçeve oranı callStatsSentFramerate Giden çerçeve oranı callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel İPTAL callSipAddressDescription Yeni çağrı başlat. CallStatistics audioStatsLabel Ses videoStatsLabel Görüntü mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section CallTransfer cancel İPTAL callTransferDescription Bu çağrıyı aktarmak ister misiniz? Calls acceptAudioCall SESLİ ÇAĞRIYI KABUL ET acceptVideoCall GÖRÜNTÜLÜ ÇAĞRIYI KABUL ET terminateCall TELEFONU KAPAT resumeCall ÇAĞRIYI DEVAM ETTİR transferCall ÇAĞRIYI AKTAR callPause ÇAĞRIYI DURAKLAT attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CallsWindow callsTitle Çağrılar acceptClosingDescription Tüm çağrılara son vermek istediğinize emin misiniz? Chat newMessagePlaceholder İletinizi girin noFileTransferUrl Dosya gönderilemiyor. Sunucu url'si yapılandırılmadı. chatTyping '%1 is typing...' indicate that someone is composing in chat %1 yazıyor… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. Panoya kopyalandı selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. Seçim panoya kopyalandı forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. İletinin yönlendirileceği yeri seç conferencesCopiedICS ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. icsDescription 'Description' : Title for the meeting description. icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. icsJoinButton 'Join' : Action button to join the meeting. deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. icsJoinButton 'Join' : Action button to join the meeting. icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. icsParticipants '%1 participant' : number(=%1) of participant. icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message Gönderildi: %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message Aldı: %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message Okudu: %1 - %2 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 iletiyi almadı %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) fileTransferDownload 'Download' : Message link to download a file ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. İletildi ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard Tümünü kopyala menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Kopyala menuPlayMe Beni oynat! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message Dağıtım durumu menuDelete 'Delete' : Item menu to delete a message Sil menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message Dağıtım durumunu gizle menuForward 'Forward' : Forward a message from menu Yönlendir menuReply 'Reply' : Reply to a message from menu Yanıtla ChatNoticeModel nMinute %1 dakika nHour %1 saat nDay %1 gün nWeek %1 hafta ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. Yanıt ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Yanıtla: %1 Cli appCliDescription %1 uygulamasını komut satırlarıyla denetleme yolu. uriCommandLineSyntax cliCommandLineSyntax commandsName komut listesi: showFunctionDescription Uygulamanın ana penceresini göster. callFunctionDescription Sip adresine çağrı başlat. initiateConferenceFunctionDescription Toplantı başlat. joinConferenceFunctionDescription Sip adresinin ev sahipliğindeki toplantıya görünen ad ile katıl. Eğer vekil yapılandırmaya bağlandıysanız "toplantıya şunun ile katıl"a bakın. joinConferenceAsFunctionDescription Sip adresinin ev sahipliğindeki toplantıya misafir sip adresi ile katıl. Eğer vekil yapılandırmaya bağlandıysanız "toplantıya-katıl"a bakın. byeFunctionDescription Belirli çağrıyı, tüm çağrıları veya geçerli çağrıyı bitir. CodecsViewer codecMime Ad codecEncoderDescription Açıklama codecEncoderClockRate Oran (Hz) codecBitrate Bit oranı (Kbit/s) codecRecvFmtp Parametreler codecStatus Durum Conference conferenceTitle TOPLANTI ConferenceControls conference TOPLANTI ConferenceManager conferenceManagerDescription Toplantı katılımcılarınızı yönetin. cancel İPTAL confirm BAŞLAT Conferences conferencesTitle 'Meetings' : Conference list title. conferencesEndedFilter 'Finished' : Filter meetings on end status. conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel İPTAL confirm ONAYLA ContactEdit removeContactDescription Bu kişiyi adres defterinizden silmek istediğinize emin misiniz? sipAccounts SIP HESAPLARI address ADRES emails E-POSTA(LAR) webSites WEB SİTE(LERİ) avatarChooserTitle Avatarınızı seçin companies ŞİRKETLER save KAYDET cancel İPTAL sipAccountsPlaceholder SIP hesabı companiesPlaceholder Şirket emailsPlaceholder E-posta webSitesPlaceholder Web site street Sokak postalCode Posta kodu country Ülke locality Semt abortEditDescriptionText Kişi düzenlemesini iptal etmek istediğinize emin misiniz? tooltipShowConversation Sohbeti göster missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Toplantı tabanlı konuşma odası oluşturmak için hesap ayarlarınızda toplantı URI'si belirlemelisiniz. Contacts searchContactPlaceholder Kişi ara selectAllContacts Tümü selectConnectedContacts Bağlı addContact KİŞİ EKLE removeContactDescription Bu kişiyi adres defterinizden silmek istediğinize emin misiniz? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Toplantı tabanlı konuşma odası oluşturmak için hesap ayarlarınızda toplantı URI'si belirlemelisiniz. Conversation displayCallsAndMessages TÜMÜ displayCalls ÇAĞRILAR displayMessages İLETİLER removeAllEntriesDescription Bu geçmişi temizlemek istediğinize emin misiniz? tooltipContactEdit Kişi düzenle tooltipContactAdd Kişi ekle cleanHistory Geçmişi sil adminStatus 'Admin' : Admin(istrator) Yönetici One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room Küme bilgisi conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room Konuşmanın aygıtları conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode Kısa ömürlü iletiler groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room Konuşma odasındaki tüm katılımcıları ara searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list İletileri ara conversationMenuDelete 'Delete history' : Item menu to delete the chat's history conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription Hesabınızı nasıl oluşturmak istersiniz? createAppSipAccountTitle %1 HESABI OLUŞTUR withPhoneNumber TELEFON NUMARASIYLA withEmailAddress E-POSTA ADRESİYLE CreateAppSipAccountWithEmail createAppSipAccountTitle %1 HESABI OLUŞTUR confirmAction OLUŞTUR usernameLabel Kullanıcı adı emailLabel E-posta passwordLabel Parola passwordConfirmationLabel Parola onayı passwordConfirmationError Girdiğiniz parolalar eşleşmiyor. quitWarning Hesabınız oluşturuldu ancak henüz doğrulanmadı. Bu görünümden çıkmak istediğinize emin misiniz? displayNameLabel Görünen ad (isteğe bağlı) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle %1 HESABI OLUŞTUR countryLabel Ülke phoneNumberLabel Telefon numarası usernameLabel Kullanıcı adı displayNameLabel Görünen ad (isteğe bağlı) confirmAction OLUŞTUR quitWarning Hesabınız oluşturuldu ancak henüz doğrulanmadı. Eğer bu görünümden çıkarsanız, hesabınızı 24 saat içinde doğrulamanız ve kendi kendinize yeniden eklemek zorunda kalırsınız. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Lütfen bir veya daha çok dosya seçin dropYourAttachment Ekinizi atın attachmentTooltip Dosya gönder EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation İPTAL startButton 'start' : button text to start ephemeral mode BAŞLAT ephemeralTitle "Ephemeral messages" : Popup title for ephemerals Kısa ömürlü iletiler ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals Yeni iletiler kişinizce okununca her iki uçta da silinecektir. Zaman aşımı seç. ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Kısa ömürlü ileti, yalnızca toplantı tabanlı konuşma odasında desteklenir! Warning about not being in conference based chat room. disabled 'Disabled' Devre dışı nMinute '%1 minute' %1 dakika nHour '%1 hour' %1 saat nDay '%1 day' %1 gün nWeek '%1 week' %1 hafta Event incomingCall Gelen çağrı outgoingCall Giden çağrı declinedIncomingCall Reddilen gelen çağrı declinedOutgoingCall Reddedilen giden çağrı endedCall Son verilen çağrı missedIncomingCall Yanıtsız gelen çağrı missedOutgoingCall Yanıtsız giden çağrı FetchRemoteConfiguration confirmAction AL fetchRemoteConfigurationTitle UZAKTAN YAPILANDIRMA AL urlLabel URL remoteProvisioningError Bu uzaktan ön hazırlık uri'si belirlenemiyor. remoteProvisioningUpdateDescription Uygulamanın yeniden başlaması gerekiyor. Şimdi yeniden başlatmak ister misiniz? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. Son uzaktan ön hazırlık başarısız generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Bu geçmişi temizlemek istediğinize emin misiniz? tooltipContactEdit Kişiyi düzenle tooltipContactAdd Kişi ekle cleanHistory Geçmişi sil Home howToDescription %1'un nasıl kullanılacağıyla ilgili yardıma mı gereksiniyorsunuz? howToTitle %1 NASIL KULLANILIR inviteDescription %1'daki arkadaşlarınızı davet edin. inviteTitle ARKADAŞLARINIZI DAVET EDİN accountAssistantDescription %1 hesabı oluştur veya yönet. accountAssistantTitle HESAP YARDIMCISI assistantButton YARDIMCI showTooltips Bilgi çubuklarını göster inviteButton DAVET ET Incall acceptVideoDescription Kişiniz görüntüyü açmak istiyor. securedStringFormat Çağrı şununla şifrelendi: %1. callNotSecured Çağrı şifrelenmedi. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. incallMenuTitle 'Settings' : Main menu title for settings. incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label Kümeden çık ok 'OK' : Button label Tamam addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Katılımcı ekle addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Konuşma odasına davet etmek için kişi listenizde katılımcı arayın. Explanation for inviting the selected participants into chat room participantList 'Participant list' Katılımcı listesi adminStatus 'Admin' : Admin(istrator) Yönetici word for admin status chatRoomDetailsTitle "Group information" : Popup title. Küme bilgisi popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation İPTAL callButton 'CALL' : Button that lead to a call ARA okButton 'OK' : Button that validate the popup to be redirected to the device list TAMAM infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. Uçtan uca şifrelendi encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Anlık iletiler güvenli konuşmalarda uçtan uca şifrelenmiştir. Katılımcıların kimliğini doğrulayarak konuşmanın güvenlik düzeyi yükseltilebilir. Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Bunu yapmak için kişinizi arayın ve kimlik doğrulama sürecini izleyin. Explanation process InviteFriends enterEmailLabel Arkadaşın e-posta adresi messageLabel İleti cancel İPTAL confirm ONAYLA inviteFriendsTitle Arkadaşları Davet Et defaultMessage %1 sizi %2'da davet ediyor ! defaultSubject %1 daveti forcedMessage Uygulamayı bilgisayarınıza indirin ve kullanıcıları ücretsiz olarak aramaya ve konuşmaya başlayın. Buraya tıklayın: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 %1 Hakkında Preferences... Tercihler Services Servisler Hide %1 %1 uygulamas gizle Hide Others Diğerlerini gizle Show All Tümünü göster Quit %1 %1 Uygulamadan Çık MainWindow mainSearchBarPlaceholder Kişi ara, çağrı veya sohbet başlat… contactsEntry KİŞİLER autoAnswerStatus kendiliğinden smartSearchBarTooltip Sesli veya görüntülü çağrı başlatmak, ileti göndermek ve kişi eklemek için akıllı arama çubuğunu kullanın. Yalnızca arkadaşınızın SIP adresini veya kullanıcı adını girin. newConferenceButton Konferans aramasını başlat newChatRoom 'Start a chat room' : Tooltip to illustrate a button Konuşma odası başlat hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline Zaman Çizelgesini Gizle openTimeline 'Open Timeline' : Tooltip for a button that open the timeline Zaman Çizelgesini Aç openHome 'Open Home' : Tooltip for a button that open the home view Evi Aç mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Tercihler about Hakkında quit Çıkış checkForUpdates 'Check for updates' : Item menu for checking updates Güncellemeleri denetle MainWindowTopMenuBar settings Tercihler about Hakkında quit Bırakın checkForUpdates 'Check for updates' : Item menu for checking updates Güncellemeleri denetle ManageAccounts ok TAMAM selectPresenceLabel Bulunma durumu selectAccountLabel Etkin hesap MultimediaParametersDialog ok menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button İPTAL startButton 'Launch' : Start button BAŞLAT missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Konuyu doldurmalısınız. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. En az %1 katılımcınız olmalı. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. Toplantı tabanlı konuşma odası oluşturmak için hesap ayarlarınızda toplantı URI'si belirlemelisiniz. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room Konuşma odası başlat askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. Konuşmanızı şifrelemek ister misiniz? subjectLabel 'Subject' : Label of a text field about the subject of the chat room Konu subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Konuşma Odasının şu anki konusu. Boş olamaz. Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Katılımcıları seç participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' Kişilerinizde arayın veya konuşma odasına özel birini ekleyin. adminStatus 'Admin' : Admin(istrator) Yönetici word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Bu katılımcıyı seçimden kaldır This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Gerekli subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Konu bul LastContactsTitle 'Last contacts' : Header for showing last contacts Son kişiler NewConference cancelButton 'Cancel' : Cancel button İPTAL missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. Konuyu doldurmalısınız. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. En az %1 katılımcınız olmalı. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Toplantı tabanlı konuşma odası oluşturmak için hesap ayarlarınızda toplantı URI'si belirlemelisiniz. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference Konu subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject Konu bul subjectTooltip 'Current subject of the Meeting. It cannot be empty' Konuşma Odasının şu anki konusu. Boş olamaz. Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. Konuşmanızı şifrelemek ister misiniz? participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. Katılımcıları seç participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' Kişilerinizde arayın veya konuşma odasına özel birini ekleyin. adminStatus 'Admin' : Admin(istrator) Yönetici word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection Bu katılımcıyı seçimden kaldır This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) Gerekli launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. Kümeye katıldınız conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. Kümeden ayrıldınız conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. %1 katıldı conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group %1 ayrıldı conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody %1 artık yönetici conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody %1 artık yönetici değil conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. %1 tarafından güvenlik düzeyi düşürüldü conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time Kısa ömürlü iletiler etkinleştirildi: %1 conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. Kısa ömürlü iletiler devre dışı bırakıldı conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. Yeni konu: %1 conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time Kısa ömürlü iletiler güncellendi: %1 unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. %1 okunmamış ileti Notifier newVersionAvailable Yeni sürüm (%1) var! newFileMessage Yeni ek alındı! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm ONAYLA onlineInstallerExtractingDescription %1 çıkarılıyor... onlineInstallerDownloadingDescription %1 indiriliyor... onlineInstallerFinishedDescription %1 kuruldu! onlineInstallerFailedDescription %1 kurulumu başarısız! OutgoingMessage messageError Hata messageRead Okundu messageDelivered İletildi ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices Konuşmanın aygıtları ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room Katılımcı ekle addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Konuşma odasına davet etmek için kişi listenizde katılımcı arayın. Explanation for inviting the selected participants into chat room participantList 'Participant list' Katılımcı listesi adminStatus 'Admin' : Admin(istrator) Yönetici word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) (Yönetici) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline Uygun presenceBusy Meşgul presenceDoNotDisturb Rahatsız etme presenceOffline Çevrim dışı QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. Etkinleştir LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. Devre dışı bırak LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. Kendiliğinden SettingsAdvanced logsTitle Günlükler logsFolderLabel Günlük klasörü sendLogs GÜNLÜKLERİ GÖNDER logsUploadUrlLabel Günlüklerin yükleneceği sunucu url'si logsUploadFailed Günlük yükleme başarısız. logsEnabledLabel Günlükler etkin cleanLogs GÜNLÜKLERİ TEMİZLE cleanLogsDescription Tüm günlükleri temizlemek istediğinize emin misiniz? developerSettingsTitle Geliştirici ayarları developerSettingsEnabledLabel Geliştirici ayarlarını etkinleştir logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Posta bulunamadı, ancak %1 konumuna günlükler yüklendi logsMailerSuccess Günlükler %1 dosyasına yüklendi contactsTitle Adres Defteri Bağlayıcı noPlugin 'No Plugins to load' : Text in combobox Yüklenecek eklenti yok viewlogs SettingsAudio audioTitle Ses parametreleri playbackDeviceLabel Kayıttan oynatma aygıtı captureDeviceLabel Yakalama aygıtı ringerDeviceLabel Zil aygıtı ringLabel Zil echoCancellationLabel Yankı gidermeyi etkinleştir audioCodecsTitle Ses çözücüler showAudioCodecsLabel Ses çözücüleri göster playbackGainLabel Oynatma kazancı captureGainLabel Kazancı yakalayın audioTestLabel Yakalama seviyesi audioSettingsInCallWarning Sesli çağrı devam ediyor: bazı ayarlar kullanılamıyor. echoCancellationCalibrationLabel Kalibrasyon calibratingEchoCancellationInProgress …kalibre ediliyor… calibratingEchoCancellationDone Kalibre edildi: %1ms calibratingEchoCancellationFailed Kalibrasyon başarısız calibratingEchoCancellationNone Yankı saptanmadı SettingsCallsChat fileServerLabel Dosya sunucusu encryptWithLimeLabel LIME ile şifrele limeDisabled Devre dışı limeRequired Zorunlu limePreferred Yeğlenen chatTitle Sohbet callsTitle Çağrılar encryptionLabel Şfireleme noEncryption Hiçbiri autoAnswerLabel Kendiliğinden yanıtla autoAnswerDelayLabel Gecikme (ms türünde) autoAnswerWithVideoLabel Kendiliğinden yanıtla (görüntüyle) chatEnabledLabel Konuşmayı etkinleştir callRecorderEnabledLabel Çağrı kaydediciyi etkinleştir chatNotificationSoundEnabledLabel Bildirim sesini etkinleştir chatNotificationSoundLabel Bildirim sesi conferenceEnabledLabel Toplantıyı etkinleştir contactsTitle Kişiler contactsEnabledLabel Kişileri etkinleştir muteMicrophoneEnabledLabel Mikrofonu kısmayı etkinleştir outgoingCallsEnabledLabel Giden çağrıları ektinleştir showTelKeypadAutomaticallyLabel Telefon numara takımını kendiliğinden göster automaticallyRecordCallsLabel Çağrıları kendiliğinden kaydet keepCallsWindowInBackgroundLabel Çağrı pencerelerini arka planda tut callPauseEnabledLabel Çağrı duraklatma etkin encryptionMandatoryLabel Şifreleme zorunludur hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms Boş konuşma odalarını gizle waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered Kaydolunca ara chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. Bildirimleri etkinleştir AutoDownload 'Auto download' : Label for a slider about auto download mode Kendiliğinden indir autoDownloadNever 'Never' : auto download mode description for deactivated feature. Asla autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. Her zaman callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer Yeni sunucu SettingsLdapEdit cancel İptal Et confirm Onayla displayNameLabel Görünen Ad displayNameTooltip Sunucunun listede gösterilecek adı. connectionTitle Bağlantı serverLabel Sunucu URL'si serverTooltip LDAP Sunucusu. ör: yerel sunucu için ldap:/// veya ldap://ldap.ornek.org/ bindDNLabel Bind DN bindDNTooltip Bind DN, LDAP'a karşı doğrulanırken kullanılan kimlik bilgisidir.<br> ör: cn=kullaniciadi,ou=insanlar,dc=bc,dc=com passwordLabel Parola useTLSLabel TLS kullan useTLSTooltip LDAP işlemlerini TLS(StartTLS) üstünden şifrele. \'ldap\' şeması kullanmalısınız. SSL üstünden LDAP için \'ldaps\' standart dışıdır ve terk edilmiştir.<br>StartTLS, LDAP iletişim kuralına bir eklentidir ve iletişimi şifrelemek için TLS iletişim kuralını kullanır. <br>Sunucu ve web servislerinin sunulması arasında el sıkışma uzlaşısı öncesinde LDAP sunucusuyla olağan - örn. güvensiz - bağlantı kurarak çalışır. Burada, sunucu güvenli bağlantı kurulmadan önce kimliğini kanıtlayan sertifikayı gönderir. useSalLabel SAL kullan useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' DNS çözümleme SAL kullanılarak %1 tarafından yapılır. LDAP'a bir IP gönderir. Bunu yaparak, TLS uzlaşısı ana makine adını denetleyemez. Bağlantıyı dayatmak isteniyorsa doğrulamaları devre dışı bırakmanız gerekebilir. verifyTLSLabel TLS Üstünde Sertifikaları Doğrula AutoMode Kendiliğinden offMode Kapalı onMode Açık verifyTLSTooltip LDAP sunucusuna bağlanırken TLS sunucu sertifikalarının doğrulanıp doğrulanmayacağını belirle. searchTitle Ara baseObjectLabel baseObjectPlaceholder baseObjectTooltip filterLabel filterTooltip maxResultsLabel maxResultsTooltip timeoutLabel Zaman aşımı timeoutTooltip Saniye türünde bağlantı ve arama zaman aşımı. Pozitif olmalıdır.<br>Öntanımlı: 5s. parsingTitle nameAttributesLabel nameAttributesTooltip sipAttributesLabel sipAttributesTooltip domainLabel Etki alanı domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. SIP adresine etki alanı ekle (sip:kullanici@etkialanı). miscLabel Türlü debugLabel Hata ayıkla debugTooltip İşlem yaparken günlük dosyasına ayrıntılı günlükle (TLS bağlantısı hataları ayıklanırken kullanışlıdır). SettingsNetwork sendDtmfsLabel DTMF'leri gönderme yöntemi allowIpV6Label IPv6'ya izin ver transportTitle Taşıma natAndFirewallTitle NAT ve Güvenlik Duvarı enableIceLabel ICE'yi etkinleştir stunServerLabel STUN/TURN sunucusu enableTurnLabel TURN'u etkinleştir turnUserLabel TURN kullanıcısı turnPasswordLabel TURN parolası networkProtocolAndPortsTitle Ağ iletişim kuralları ve bağlantı noktaları sipUdpPortLabel SIP UDP bağlantı noktası sipTcpPortLabel SIP TCP bağlantı noktası audioRtpUdpPortLabel Ses RTP UDP bağlantı noktası videoRtpUdpPortLabel Görüntü RTP UDP bağlantı noktası dscpFieldsTitle DSCP Alanları sipFieldLabel SIP audioRtpStreamFieldLabel Ses RTP Akışı videoRtpStreamFieldLabel Görüntü RTP Akışı bandwidthControlTitle Bant Genişliği Denetimi downloadSpeedLimitLabel Kbit/sn türünde indirme hız sınırı uploadSpeedLimitLabel Kbit/sn türünde yükleme hız sınırı enableAdaptiveRateControlLabel Uyarlanır oran denetimini etkinleştir presenceTitle Bulunma rlsUriLabel RLS URI'si kullan rlsUriAuto KENDİLİĞİNDEN rlsUriDisabled ASLA showNetworkSettingsLabel Ağ ayarlarını göster generalTitle Genel SettingsSipAccounts defaultIdentityTitle Öntanımlı kimlik defaultUsernameLabel Kullanıcı adı defaultSipAddressLabel SIP adresi proxyAccountsTitle Vekil hesapları eraseAllPasswords PAROLALARI SİL addAccount HESAP EKLE editHeader Düzenle deleteHeader Sil deleteAccountDescription Bu hesabı silmek istediğinize emin misiniz? eraseAllPasswordsDescription Tüm parolaları silmek istediğinize emin misiniz? defaultDisplayNameLabel Görünen ad assistantTitle Yardimci createAppSipAccountEnabledLabel Hesap oluşturmayı etkinleştir useAppSipAccountEnabledLabel Hesap kullanımını etkinleştir useOtherSipAccountEnabledLabel Genel hesap kullanımını etkinleştir fetchRemoteConfigurationEnabledLabel Yapılandırma almayı etkinleştir assistantSupportsPhoneNumbersLabel Telefon numaralarını destekler defaultDeviceNameLabel 'Device Name' : Label for setting the device name. Aygıt adı webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel SIP adresi transportLabel Taşıma serverAddressLabel SIP Sunucu adresi registrationDurationLabel Kaydolma uzunluğu (saniye) routeLabel Rota contactParamsLabel Kişi parametreleri publishPresenceLabel Bulunma bilgisini yayımla avpfIntervalLabel AVPF düzenli RTCP aralığı (saniye) registerEnabledLabel Kaydol avpfEnabledLabel AVPF'yi etkinleştir cancel İPTAL confirm ONAYLA invalidSipAddress Geçersiz SIP adresi. invalidServerAddress Geçersiz sunucu adresi. invalidRoute Geçersiz rota. enableIceLabel ICE'yi etkinleştir stunServerLabel STUN/TURN sunucusu enableTurnLabel TURN'u etkinleştir turnUserLabel TURN kullanıcısı turnPasswordLabel TURN parolası natAndFirewallTitle NAT ve Güvenlik Duvarı mainSipAccountSettingsTitle Ana SIP hesabı ayarları conferenceURI "Conference URI" : Label of a text edit for filling Conference URI Toplantı URI'si invalidConferenceURI "invalid conference URI" : Error text about conference URI Geçersiz toplantı URI'si videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) Geçit Durumu tunnelDomain 'Domain' : Field title of a textfield to set domain. Etki alanı tunnelUsername 'Username' : Field title of a textfield to set username. Kullanıcı adı tunnelSIP 'SIP' : Field title of a switch to set SIP mode. SIP cancel 'Cancel' : Button to cancel the action. İptal Et setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. HTTP vekil belirle proxyHttpHost 'Host' : Placeholder to set hostname. Ana makine proxyHttpPort 'Port' : Placehoilder to set port. Bağlantı noktası proxyHttpUsername 'Username' : Placeholder to set username. Kullanıcı adı proxyHttpPassword 'Password' : Placeholder to set password. Parola proxyHttpApply 'Apply' : Button to set proxy from changes. Uygula serverMode 'Mode' : Field title on form to set tunnel mode. Kip serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. İkili kip serverTitle 'Server' : Title form to set a server Sunucu serverHostname 'Hostname' : Field title on form to set hostname. Ana makine adı serverPort 'Port' : Field title on form to set port. Bağlantı noktası serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. İkili ana makine adı URL'si serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. İkili bağlantı noktası serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. Uzak UDP yansı bağlantı noktası serverDelay 'Delay' : Field title on form to set the delay of the tunnel. Gecikme tunnelAddServer 'Add server' : Button for adding a server Sunucu ekle tunnelApply 'Apply' : Button to apply changes. Uygula SettingsUi pathsTitle Yollar savedScreenshotsLabel Kaydedilen ekran görüntüleri klasörü savedCallsLabel Kaydedilen çağrılar klasörü languagesTitle Diller languagesLabel Dil systemLocale Sistem yereli cleanAvatars AVATARLARI SİL cleanAvatarsDescription Tüm avatarları silmek istediğinize emin misiniz? downloadLabel İndirme klasörü setLocaleDescription Uygulamanın yeniden başlaması gerekiyor. Şimdi yeniden başlatmak ister misiniz? otherTitle Diğer exitOnCloseLabel Pencere kapatıldığında uygulamadan çık dataTitle Kullanıcı Arayüzü Verisi autoStartLabel Uygulamayı kendiliğinden başlat fontsTitle 'Fonts' : title of fonts section in settings Yazı tipleri fontsTextChange 'Text Messages' : Label for changing text message fonts Metin İletileri fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts Yeni yazı tipi seç checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates Güncellemeleri denetle mipmapLabel 'Enable Mipmap' Mipmap'i etkinleştir mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. Bu özellik, resmin ölçeklenirken veya dönüştürülürken mipmap süzgecini kullanıp kullanmayacağını barındırır. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. Bu özellik, resmin ölçeklenirken veya dönüştürülürken mipmap süzgecini kullanıp kullanmayacağını barındırır. {2?} minimalTimelineFilterLabel 'Minimal Timeline filter' Kısa Zaman Çizelgesi süzgeci minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : Zaman çizelgesinde sunulacakların kısa sürümünü göster. versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Görüntü giriş aygıtı videoFramerateLabel Çerçeve oranı videoCaptureTitle Görüntü yakalama parametreleri videoPresetLabel Görüntü ön ayarı presetDefault Öntanımlı presetHighFps Yüksek saniye başına çerçeve presetCustom Özel videoSizeLabel Görüntü çözünürlüğü videoCodecsTitle Görüntü çözücüler showCameraPreview GÖRÜNTÜ ÖNİZLEMESİ showVideoCodecsLabel Görüntü çözücüleri göster videoSettingsInCallWarning Görüntülü arama devam ediyor: bazı ayarlar mevcut değil. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm TAMAM SettingsWindow settingsTitle Ayarlar sipAccountsTab SIP hesapları audioTab Ses videoTab Görüntü callsAndChatTab Çağrı ve Sohbet networkTab uiTab Kullanıcı Arayüzü validButton TAMAM uiAdvanced Gelişmiş tunnelTab 'Tunnel' : Tab title for tunnel section in settings. Geçit SipAddressDialog cancel İptal Et contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact Kişilerde ara contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip Kişilerinizde adres arayın veya özel birini kullanın. timelineSelectionHeader 'Conversations' : header for a selection in conversation list Konuşmalar SmartSearchBar addContact KİŞİ EKLE Timeline timelineFilter A title for filtering mode. Süzgeç timelineFilterAll 'All' The mode for timelines filtering. Tümü timelineFilterCustom 'Custom' The mode for timelines filtering. Özel timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). Basit odalar timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. Güvenli odalar timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). Konuşma kümeleri timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. Kısa ömürlüler timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list Listede ara timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. Tüm güvenlik düzeyleri timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. Standart odalar timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. Herhangi konuşmalar timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. Kısa ömürlüler açık/kapalı timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. Kısa ömürlüler olmadan timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. Toplantılar TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction KULLAN useAppSipAccountTitle %1 HESABI KULLAN useUsernameToLogin Telefon numaranız yerine kullanıcı adı ve parola kullanın. quitWarning Hesabınız oluşturuldu ancak henüz doğrulanmadı. Bu görünümden çıkmak istediğinize emin misiniz? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password Unutulan parola? UseAppSipAccountWithPhoneNumber countryLabel Ülke phoneNumberLabel Telefon numarası displayNameLabel UseAppSipAccountWithUsername usernameLabel Kullanıcı adı passwordLabel Parola displayNameLabel UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form KULLAN useOtherSipAccountTitle SIP HESABI KULLAN usernameLabel Kullanıcı adı displayNameLabel Görünen ad (isteğe bağlı) sipDomainLabel SIP Alan Adı passwordLabel Parola transportLabel Taşıma addOtherSipAccountError Bu hesap eklenemiyor. understandAction 'I understand' : Popup confirmation for a warning Anlıyorum warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name Küme iletileri veya kısa ömürlü iletiler gibi bazı özellikler %1 hesabı gerektirir. warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. Bu özellikler üçüncü taraf SIP hesabıyla kaydolduğunuzda gizlenir. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. Ticari projede etkinleştirmek için lütfen bizimle iletişime geçin. WaitingRoom cancelButton 'Cancel' : Cancel button. İPTAL startButton 'Start' : Button label for starting the conference. BAŞLAT endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Aşağıdaki kısa yetkilendirme dizgesini eşinizle onaylayın. codeA Söyleyin: codeB Kişiniz şunu söylemeli: Later 'Later' : Button label to do something in another time. Sonra Correct 'Correct' : Button label to confirm a code. Haklı title 'Communication security' : Title of popup for ZRTP confirmation. İletişim güvenliği country Afghanistan Afganistan Albania Arnavutluk Algeria Cezayir AmericanSamoa Amerikan Samoası Andorra Andorra Angola Angola Anguilla Anguilla AntiguaAndBarbuda Antigua ve Barbuda Argentina Arjantin Armenia Ermenistan Aruba Aruba Australia Avustralya Austria Avusturya Azerbaijan Azerbaycan Bahamas Bahamalar Bahrain Bahreyn Bangladesh Bangladeş Barbados Barbados Belarus Belarus Belgium Belçika Belize Belize Benin Benin Bermuda Bermuda Bhutan Bhutan Bolivia Bolivya BosniaAndHerzegowina Bosna-Hersek Botswana Botsvana Brazil Brezilya Brunei Bruney Bulgaria Bulgaristan BurkinaFaso Burkina Faso Burundi Burundi Cambodia Kamboçya Cameroon Kamerun Canada Kanada CapeVerde Yeşil Burun Adaları CaymanIslands Cayman Adaları CentralAfricanRepublic Orta Afrika Cumhuriyeti Chad Çad Chile Şili China Çin Colombia Kolombiya Comoros Komorlar PeoplesRepublicOfCongo Kongo Cumhuriyeti DemocraticRepublicOfCongo Demokratik Kongo Cumhuriyeti CookIslands Cook Adaları CostaRica Kosta Rika IvoryCoast Fildişi Sahili Croatia Hırvatistan Cuba Küba Cyprus Kıbrıs CzechRepublic Çek Cumhuriyeti Denmark Danimarka Djibouti Cibuti Dominica Dominika DominicanRepublic Dominik Cumhuriyeti Ecuador Ekvador Egypt Mısır ElSalvador El Salvador EquatorialGuinea Ekvator Ginesi Eritrea Eritre Estonia Estonya Ethiopia Etyopya FalklandIslands Falkland Adaları FaroeIslands Faroe Adaları Fiji Fiji Finland Finlandiya France Fransa FrenchGuiana Fransız Guyanası FrenchPolynesia Fransız Polinezyası Gabon Gabon Gambia Gambiya Georgia Georgia Germany Almanya Ghana Gana Gibraltar Cebelitarık Greece Yunanistan Greenland Grönland Grenada Grenada Guadeloupe Guadeloupe Guam Guam'a Guatemala Guatemala Guinea Gine GuineaBissau Gine Bissau Guyana Guyana Haiti Haiti Honduras Honduras HongKong Hong Kong Hungary Macaristan Iceland İzlanda India Hindistan Indonesia Endonezya Iran İran Iraq Irak Ireland İrlanda Israel İsrail Italy İtalya Jamaica Jamaika Japan Japonya Jordan Ürdün Kazakhstan Kazakistan Kenya Kenya Kiribati Kiribati DemocraticRepublicOfKorea Kore Demokratik Cumhuriyeti RepublicOfKorea Kore Cumhuriyeti Kuwait Kuveyt Kyrgyzstan Kırgızistan Laos Laos Latvia Letonya Lebanon Lübnan Lesotho Lesotho Liberia Liberya Libya Libya Liechtenstein Lihtenştayn Lithuania Litvanya Luxembourg Lüksemburg Macau Makao Macedonia Makedonya Madagascar Madagaskar Malawi Malavi Malaysia Malezya Maldives Maldivler Mali Mali Malta Malta MarshallIslands Marshall Adaları Martinique Martinik Mauritania Moritanya Mauritius Mauritius Mayotte Mayotte Mexico Meksika Micronesia Mikronezya Moldova Moldova Monaco Monako Mongolia Moğolistan Montenegro Karadağ Montserrat Montserrat Morocco Fas Mozambique Mozambik Myanmar Myanmar Namibia Namibya NauruCountry Nauru Nepal Nepal Netherlands Hollanda NewCaledonia Yeni Kaledonya NewZealand Yeni Zelanda Nicaragua Nikaragua Niger Nijer Nigeria Nijerya Niue Niue NorfolkIsland Norfolk Adası NorthernMarianaIslands Kuzey Mariana Adaları Norway Norveç Oman Umman Pakistan Pakistan Palau Palau PalestinianTerritories Filistin Toprakları Panama Panama PapuaNewGuinea Papua Yeni Gine Paraguay Paraguay Peru Peru Philippines Filipinler Poland Polonya Portugal Portekiz PuertoRico Porto Riko Qatar Katar Reunion Buluşma Romania Romanya RussianFederation Rusya Federasyonu Rwanda Ruanda SaintHelena Saint Helena SaintKittsAndNevis Saint Kitts ve Nevis SaintLucia Saint Lucia SaintPierreAndMiquelon Saint Pierre ve Miquelon SaintVincentAndTheGrenadines Saint Vincent ve Grenadinler Samoa Samoa SanMarino San Marino SaoTomeAndPrincipe São Tomé ve Principe SaudiArabia Suudi Arabistan Senegal Senegal Serbia Sırbistan Seychelles Seyşeller SierraLeone Sierra Leone Singapore Singapur Slovakia Slovakya Slovenia Slovenya SolomonIslands Solomon Adaları Somalia Somali SouthAfrica Güney Afrika Cumhuriyeti Spain İspanya SriLanka Sri Lanka Sudan Sudan Suriname Surinam Swaziland Svaziland Sweden İsveç Switzerland İsviçre Syria Suriye Taiwan Tayvan Tajikistan Tacikistan Tanzania Tanzanya Thailand Tayland Togo Togo Tokelau Tokelau Tonga Tonga TrinidadAndTobago Trinidad ve Tobago Tunisia Tunus Turkey Türkiye Turkmenistan Türkmenistan TurksAndCaicosIslands Turks ve Caicos Adaları Tuvalu Tuvalu Uganda Uganda Ukraine Ukrayna UnitedArabEmirates Birleşik Arap Emirlikleri UnitedKingdom Birleşik Krallık UnitedStates Amerika Birleşik Devletleri Uruguay Uruguay Uzbekistan Özbekistan Vanuatu Vanuatu Venezuela Venezuela Vietnam Vietnam WallisAndFutunaIslands Wallis ve Futuna Adaları Yemen Yemen Zambia Zambiya Zimbabwe Zimbabve utils downloadCodecDescription %1 (%2) indirilmesini istiyor musunuz? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/uk.ts000066400000000000000000005414301434616504300236470ustar00rootroot00000000000000 About ok Так aboutTranslation 'Help us translate %1' : %1 is the application name ActivateAppSipAccountWithEmail activateAppSipAccount АКТИВУЙТЕ ВАШУ ОБЛІКІВКУ %1 confirmAction АКТИВУВАТИ activationSteps Щоб активувати обліківку, дотримуйтесь дієвказів, котрі ми відіслали на %1, потім натисніть нижче. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount АКТИВУЙТЕ ВАШУ ОБЛІКІВКУ %1 confirmAction АКТИВУВАТИ activationSteps Ми відправили SMS з кодом підтвердження на %1. Для завершення підтвердження вашого номера телефону, будь ласка, уведіть нижче 4-значний код. App commandLineOptionVerbose виводити в stdout декотру зневаджувальну інформацію під час роботи commandLineOptionConfig вкажіть конфігураційний файл %1 applicationDescription Вільний(безкоштовний) застосунок для SIP відео дзвінків. commandLineOptionIconified запускати згорнутою в ділянку сповіщень, не показувати основний інфтерфейс commandLineOptionConfigArg файл commandLineOptionHelp показати цю довідку commandLineOptionVersion показати версію застосунку commandLineOptionCliHelp показує меню допомоги у використанні %1 з командного рядка commandLineDescription відправка команди додатку через командний рядок restore Відновити quit Вийти settings Налаштування about Про commandLineOptionFetchConfig commandLineOptionFetchConfigArg commandLineOptionCall commandLineOptionCallArg checkForUpdates AssistantAbstractView back НАЗАД AssistantHome useAppSipAccount ВИКОРИСТОВУВАТИ ОБЛІКІВКУ %1 useOtherSipAccount ВИКОРИСТОВУВАТИ SIP ОБЛІКІВКУ fetchRemoteConfiguration ЗАВАНТАЖИТИ ВІДДАЛЕНУ КОНФІГУРАЦІЮ homeTitle ЛАСКАВО ПРОСИМО homeDescription Цей асистент допоможе вам налаштувати та використовувати вашу обліківку %1. createAppSipAccount СТВОРИТИ ОБЛІКІВКУ %1 homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. AssistantModel loginWithUsernameFailed Не вдалося увійти. Будь ласка, перевірте правильність уведення імени користувача та паролю. usernameStatusTooShort Закоротке! (щонайм. %1 символів) usernameStatusTooLong Задовге! (щонайб. %1 символів) usernameStatusInvalidCharacters Виявлено неприпустимі символи. (regex: `%1`) usernameStatusInvalid Неприпустиме ім'я користувача. passwordStatusTooShort Закороткий! (щонайм. %1 символів) passwordStatusTooLong Задовгий! (щонайб. %1 символів) passwordStatusInvalidCharacters Виявлено неприпустимі символи. (regex: `%1`) passwordStatusMissingCharacters Бракує символів: `%1`. requestFailed Не вдалося відправити запит. emailStatusMalformed Неприпустима адреса пошти. emailStatusMalformedInvalidCharacters Неприпустима адреса пошти або неприпустимі символи. cannotSendSms Помилка сервера: не вдалося надіслати SMS. accountAlreadyExists Така обліківка вже існує. smsActivationFailed SMS активація не вдалася! emailActivationFailed Будь ласка, переконайтеся в тому, що ви підтвердили вашу обліківку, або спробуйте знову. phoneNumberStatusInvalid Неприпустимий номер телефону! phoneNumberStatusTooShort Закороткий! phoneNumberStatusTooLong Задовгий! phoneNumberStatusInvalidCountryCode Неприпустимий код країни! loginWithPhoneNumberFailed Не вдалося увійти. Будь ласка перевірте правильність уведення номеру телефону. unableToAddAccount Не вдалося додати цю обліківку. AuthenticationRequest cancel СКАСУВАТИ confirm УВІЙТИ identityLabel Логін passwordLabel Пароль authenticationRequestDescription Не вдалося виконати розпізнання. Будь ласка, перевірте правильність уведення паролю. userIdLabel ID користувача (не обов'язково) realmLabel Область CallModel callStatsCodec Кодек callStatsUploadBandwidth Вихідна швидкість callStatsDownloadBandwidth Вхідна швидкість callStatsEstimatedDownloadBandwidth Передбачувана вхідна швидкість callStatsIceState Стан ICE callStatsIpFamily Родина IP callStatsSenderLossRate Втрати вихідних пакетів callStatsReceiverLossRate Втрати вхідних пакетів callStatsJitterBuffer Буфер коливань затримки callStatsSentVideoDefinition Роздільність вихідного відео callStatsReceivedVideoDefinition Роздільність вхідного відео iceStateNotActivated Не активовано iceStateFailed Невдало iceStateInProgress В процесі iceStateReflexiveConnection Рефлексивне з'єднання iceStateHostConnection Під'єднання хосту iceStateRelayConnection Релейне з'єднання iceStateInvalid Неприпустимо callErrorDeclined Віддалена сторона відхилила виклик. callErrorNotFound Віддалена сторона не знайдена. callErrorBusy Віддалена сторона зайнята. callErrorNotAcceptable Віддалена сторона не може прийняти виклик. callStatsReceivedFramerate Вхідна частота кадрів callStatsSentFramerate Вихідна частота кадрів callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel СКАСУВАТИ callSipAddressDescription Почати новий виклик. CallStatistics audioStatsLabel Звук videoStatsLabel Відео mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section CallTransfer cancel СКАСУВАТИ callTransferDescription Волієте переспрямувати цей виклик? Calls acceptAudioCall ПРИЙНЯТИ ЗВУКОВИЙ ВИКЛИК acceptVideoCall ПРИЙНЯТИ ВІДЕО ВИКЛИК terminateCall ЗАВЕРШИТИ ВИКЛИК resumeCall ПРОДОВЖИТИ ВИКЛИК transferCall ПЕРЕСПРЯМУВАТИ ВИКЛИК callPause ПРИЗУПИНИТИ ВИКЛИК attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. CallsWindow callsTitle Дзвінки acceptClosingDescription Ви впевнені, що бажаєте завершити усі виклики? Chat newMessagePlaceholder Уведіть повідомлення noFileTransferUrl Не вдалося відіслати файл. Не налаштовано адресу сервера. chatTyping '%1 is typing...' indicate that someone is composing in chat allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. conferencesCopiedICS ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. icsDescription 'Description' : Title for the meeting description. icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. icsJoinButton 'Join' : Action button to join the meeting. deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. icsJoinButton 'Join' : Action button to join the meeting. icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. icsParticipants '%1 participant' : number(=%1) of participant. icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) fileTransferDownload 'Download' : Message link to download a file ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard menuCopy 'Copy' : Text menu to copy selected text in message into clipboard Копіювати menuPlayMe Грай! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message menuDelete 'Delete' : Item menu to delete a message menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message menuForward 'Forward' : Forward a message from menu menuReply 'Reply' : Reply to a message from menu ChatNoticeModel nMinute nHour nDay nWeek ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Cli appCliDescription Спосіб керувати %1 через командний рядок. uriCommandLineSyntax %1 sip:<адреса-sip>?method=<метод>([&<аргумент>=<base64-значення>]*) cliCommandLineSyntax %1 "<method> ([<argument>=<value>]*)" commandsName перелік команд: showFunctionDescription Показати головне вікно застосунку. callFunctionDescription Почати виклик SIP адреси. initiateConferenceFunctionDescription Почати конференцію. joinConferenceFunctionDescription Приєднайтеся до конференції за sip-адресою. Якщо ви під'єднані до конфігурації проксі, див. join-conference-as. joinConferenceAsFunctionDescription Приєднайтеся до конференції за sip-адресою. Якщо ви не під'єднані до конфігурації проксі, див. join-conference-as. byeFunctionDescription CodecsViewer codecMime Назва codecEncoderDescription Опис codecEncoderClockRate Частота (Гц) codecBitrate Бітрейт (Кбіт/с) codecRecvFmtp Параметри codecStatus Статус Conference conferenceTitle КОНФЕРЕНЦІЯ ConferenceControls conference КОНФЕРЕНЦІЯ ConferenceManager conferenceManagerDescription Керуйте учасниками вашої конференції. cancel СКАСУВАТИ confirm ПОЧАТИ Conferences conferencesTitle 'Meetings' : Conference list title. conferencesEndedFilter 'Finished' : Filter meetings on end status. conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel СКАСУВАТИ confirm СХВАЛИТИ ContactEdit removeContactDescription Ви справді бажаєте вилучити цей контакт з вашої адресної книги? sipAccounts SIP ОБЛІКІВКА(И) address АДРЕСА emails E-MAIL webSites САЙТ(И) avatarChooserTitle Оберіть вашу мармизку companies КОМПАНІЇ save ЗБЕРЕГТИ cancel СКАСУВАТИ sipAccountsPlaceholder SIP обліківка companiesPlaceholder Компанія emailsPlaceholder Email webSitesPlaceholder Сайт street Вулиця postalCode Поштовий індекс country Країна locality Населений пункт abortEditDescriptionText Ви впевнені, що бажаєте скасувати зміну контакту? tooltipShowConversation Показати розмову missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Contacts searchContactPlaceholder Знайти контакт selectAllContacts Усі selectConnectedContacts Під'єднані addContact ДОДАТИ КОНТАКТ removeContactDescription Ви справді бажаєте вилучити цей контакт з вашої адресної книги? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. Conversation displayCallsAndMessages УСІ displayCalls ВИКЛИКИ displayMessages ПОВІДОМЛЕННЯ removeAllEntriesDescription Ви впевнені, що волієте вичистити цю історію? tooltipContactEdit Редагувати контакт tooltipContactAdd Додати контакт cleanHistory Вилучити історію adminStatus 'Admin' : Admin(istrator) One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list conversationMenuDelete 'Delete history' : Item menu to delete the chat's history conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription Як би ви воліли створити обліківку? createAppSipAccountTitle СТВОРИТИ ОБЛІКІВКУ %1 withPhoneNumber ЗА ДОПОМОГОЮ НОМЕРУ ТЕЛЕФОНУ withEmailAddress ЗА ДОПОМОГОЮ EMAIL CreateAppSipAccountWithEmail createAppSipAccountTitle СТВОРИТИ ОБЛІКІВКУ %1 confirmAction СТВОРИТИ usernameLabel Ім'я користувача emailLabel Email passwordLabel Пароль passwordConfirmationLabel Схвалення паролю passwordConfirmationError Уведені паролі не збігаються. quitWarning Ваша обліківка була створена, але ще не підтверджена. Ви впевнені, що волієте вийти? displayNameLabel Показуване ім'я (не обов'язково) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle СТВОРИТИ ОБЛІКІВКУ %1 countryLabel Країна phoneNumberLabel Номер телефону usernameLabel Ім'я користувача displayNameLabel Показуване ім'я (не обов'язково) confirmAction СТВОРИТИ quitWarning Вашу обліківку було створено, але ще не підтверджено. Якщо ви вийдете, вам знадобиться вручну додати та підтвердити свою обліківку впродовж 24 годин. DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle Будь ласка, оберіть один або декілька файлів dropYourAttachment Помістіть вкладення attachmentTooltip Відіслати файл EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation startButton 'start' : button text to start ephemeral mode ephemeralTitle "Ephemeral messages" : Popup title for ephemerals ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' Warning about not being in conference based chat room. disabled 'Disabled' nMinute '%1 minute' nHour '%1 hour' nDay '%1 day' nWeek '%1 week' Event incomingCall Вхідний виклик outgoingCall Вихідний виклик declinedIncomingCall Відхилений вхідний виклик declinedOutgoingCall Відхилений вихідний виклик endedCall Закінчений виклик missedIncomingCall Пропущений вхідний виклик missedOutgoingCall Пропущений вихідний виклик FetchRemoteConfiguration confirmAction ЗАВАНТАЖИТИ fetchRemoteConfigurationTitle ЗАВАНТАЖИТИ ВІДДАЛЕНУ КОНФІГУРАЦІЮ urlLabel URL remoteProvisioningError Неможливо встановити це віддалене налаштування uri. remoteProvisioningUpdateDescription Потрібно перезапустити застосунок. Бажаєте перезапустити зараз? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription Ви впевнені, що волієте вичистити цю історію? tooltipContactEdit Редагувати контакт tooltipContactAdd Додати контакт cleanHistory Вилучити історію Home howToDescription Потрібна допомога у використанні %1? howToTitle ЯК КОРИСТУВАТИСЯ %1 inviteDescription Запросіть друзів використовувати %1. inviteTitle ЗАПРОСІТЬ ДРУЗІВ accountAssistantDescription Створення і керування обліківкою %1. accountAssistantTitle АСИСТЕНТ ОБЛІКІВКИ assistantButton АСИСТЕНТ showTooltips Показувати підказки inviteButton ЗАПРОСИТИ Incall acceptVideoDescription Ваш контакт волів би увімкнути відео. securedStringFormat Виклик зашифрований з: %1. callNotSecured Виклик не зашифрований. incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. incallMenuTitle 'Settings' : Main menu title for settings. incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label ok 'OK' : Button label Так addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status chatRoomDetailsTitle "Group information" : Popup title. popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation callButton 'CALL' : Button that lead to a call okButton 'OK' : Button that validate the popup to be redirected to the device list infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." Explanation process InviteFriends enterEmailLabel Email друга messageLabel Повідомлення cancel СКАСУВАТИ confirm СХВАЛИТИ inviteFriendsTitle Запросити друзів defaultMessage %1 запрошує вас використовувати %2 ! defaultSubject Запрошення %1 forcedMessage Завантажте застосунок на ваш комп'ютер та почніть телефонувати і спілкуватися безкоштовно Посилання для завантаження: <a href="%1">%1</a> MAC_APPLICATION_MENU About %1 Про %1 Preferences... Налаштування Services Послуги Hide %1 Приховати %1 Hide Others Приховати інші Show All показати все Quit %1 Вийти %1 MainWindow mainSearchBarPlaceholder Знайти контакт, почати дзвінок або чат… contactsEntry КОНТАКТИ autoAnswerStatus авто smartSearchBarTooltip Використовуйте розумний пошуковий рядок, щоб одразу почати звуковий або відео виклик відіслати повідомлення чи додати новий контакт. Просто уведіть SIP адресу чи ім'я користувача вашого контакту. newConferenceButton Почати конференц-дзвінок newChatRoom 'Start a chat room' : Tooltip to illustrate a button hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline openTimeline 'Open Timeline' : Tooltip for a button that open the timeline openHome 'Open Home' : Tooltip for a button that open the home view mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings Налаштування about Про quit Вийти checkForUpdates 'Check for updates' : Item menu for checking updates MainWindowTopMenuBar settings Уподобання about Про це quit Кинути checkForUpdates 'Check for updates' : Item menu for checking updates ManageAccounts ok Так selectPresenceLabel Статус selectAccountLabel Активна обліківка MultimediaParametersDialog ok Так menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button startButton 'Launch' : Start button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. subjectLabel 'Subject' : Label of a text field about the subject of the chat room subjectTooltip 'Current subject of the Chat Room. It cannot be empty' Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject LastContactsTitle 'Last contacts' : Header for showing last contacts NewConference cancelButton 'Cancel' : Cancel button missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject subjectTooltip 'Current subject of the Meeting. It cannot be empty' Explanation about the subject of the meeting askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' adminStatus 'Admin' : Admin(istrator) word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. Notifier newVersionAvailable Доступна нова (%1) версія! newFileMessage Отримано новий файл! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm ПІДТВЕРДИТИ onlineInstallerExtractingDescription Витягування %1… onlineInstallerDownloadingDescription Завантаження %1… onlineInstallerFinishedDescription %1 встановлено! onlineInstallerFailedDescription Не вдалося встановити %1! OutgoingMessage messageError Помилка messageRead Прочитано messageDelivered Доставлено ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' Explanation for inviting the selected participants into chat room participantList 'Participant list' adminStatus 'Admin' : Admin(istrator) word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline Доступний presenceBusy Зайнятий presenceDoNotDisturb Не бентежити presenceOffline Не в мережі QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. SettingsAdvanced logsTitle Журнали logsFolderLabel Тека журналів sendLogs НАДСИЛАННЯ ЖУРНАЛІВ logsUploadUrlLabel URL сервера для надсилання журналів logsUploadFailed Не вдалося надіслати журнали logsEnabledLabel Журнали включені cleanLogs ВИЛУЧИТИ ЖУРНАЛИ cleanLogsDescription Ви впевнені, що волієте вилучити усі журнали? developerSettingsTitle Налаштування для розробників developerSettingsEnabledLabel Увімкнути налаштування для розробників logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) Не вдалося знайти поштову програму, але журнали було вивантажено до %1 logsMailerSuccess Журнали було вивантажено до %1 contactsTitle Контакти noPlugin 'No Plugins to load' : Text in combobox viewlogs SettingsAudio audioTitle Параметри звуку playbackDeviceLabel Пристрій відтворення captureDeviceLabel Пристрій захоплення ringerDeviceLabel Пристрій дзвінка ringLabel Дзвінок echoCancellationLabel Увімкнути пригнічення луни audioCodecsTitle Звукові кодеки showAudioCodecsLabel Показати звукові кодеки playbackGainLabel Посилення відтворення captureGainLabel Посилення захоплення audioTestLabel Рівень захоплення audioSettingsInCallWarning Прогрес аудіодзвінку: деякі настройки недоступні. echoCancellationCalibrationLabel calibratingEchoCancellationInProgress calibratingEchoCancellationDone calibratingEchoCancellationFailed calibratingEchoCancellationNone SettingsCallsChat fileServerLabel Файловий сервер encryptWithLimeLabel Шифрувати за допомогою LIME limeDisabled Вимкнено limeRequired Обов'язково limePreferred Бажано chatTitle Чат callsTitle Дзвінки encryptionLabel Шифрування noEncryption Ні autoAnswerLabel Приймати виклик самочинно autoAnswerDelayLabel Затримка (в мс) autoAnswerWithVideoLabel Приймати виклик самочинно (з відео) chatEnabledLabel Увімкнути чат callRecorderEnabledLabel Увімкнути запис дзвінків chatNotificationSoundEnabledLabel Увімкнути звук сповіщень chatNotificationSoundLabel Звук сповіщень conferenceEnabledLabel Увімкнути конференцію contactsTitle Контакти contactsEnabledLabel Увімкнути контакти muteMicrophoneEnabledLabel Увімкнути звук мікрофону outgoingCallsEnabledLabel Увімкнення вихідних викликів showTelKeypadAutomaticallyLabel Показувати телефонну набірницю автоматично automaticallyRecordCallsLabel Автоматично записувати дзвінки keepCallsWindowInBackgroundLabel Підтримувати дзвінки у згорнутому режимі callPauseEnabledLabel Виклик призупинено encryptionMandatoryLabel Обов'язковий заселення hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer SettingsLdapEdit cancel СКАСУВАТИ confirm displayNameLabel Показуване ім'я (не обов'язково) displayNameTooltip connectionTitle serverLabel serverTooltip bindDNLabel bindDNTooltip passwordLabel Пароль useTLSLabel useTLSTooltip useSalLabel useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' verifyTLSLabel AutoMode offMode onMode verifyTLSTooltip searchTitle baseObjectLabel baseObjectPlaceholder baseObjectTooltip filterLabel filterTooltip maxResultsLabel maxResultsTooltip timeoutLabel timeoutTooltip parsingTitle nameAttributesLabel nameAttributesTooltip sipAttributesLabel sipAttributesTooltip domainLabel domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. miscLabel debugLabel debugTooltip SettingsNetwork sendDtmfsLabel Метод відправки DTMF allowIpV6Label Дозволити IPv6 transportTitle Транспорт natAndFirewallTitle NAT та файрвол enableIceLabel Увімкнути ICE stunServerLabel Сервер STUN/TURN enableTurnLabel Увімкнути TURN turnUserLabel Користувач TURN turnPasswordLabel Пароль TURN networkProtocolAndPortsTitle Мережевий протокол і порти sipUdpPortLabel UDP порт SIP sipTcpPortLabel TCP порт SIP audioRtpUdpPortLabel UDP порт RTP аудіо videoRtpUdpPortLabel UDP порт RTP відео dscpFieldsTitle Поля DSCP sipFieldLabel SIP audioRtpStreamFieldLabel Потік RTP аудіо videoRtpStreamFieldLabel Потік RTP відео bandwidthControlTitle Керування смугою пропускання downloadSpeedLimitLabel Обмежити вхідну швидкість до Кбіт/сек uploadSpeedLimitLabel Обмежити вихідну швидкість до Кбіт/сек enableAdaptiveRateControlLabel Увімкнути адаптивне керування швидкістю presenceTitle Статус rlsUriLabel Використовувати RLS URI rlsUriAuto АВТО rlsUriDisabled НІКОЛИ showNetworkSettingsLabel Показати мережеві налаштування generalTitle Основні SettingsSipAccounts defaultIdentityTitle Типове посвідчення defaultUsernameLabel Ім'я користувача defaultSipAddressLabel Адреса SIP proxyAccountsTitle Проксі обліківки eraseAllPasswords ВИЛУЧИТИ ПАРОЛІ addAccount ДОДАТИ ОБЛІКІВКУ editHeader Змінити deleteHeader Вилучити deleteAccountDescription Ви впевнені, що бажаєте вилучити цю обліківку? eraseAllPasswordsDescription Ви впевнені, що бажаєте вилучити усі паролі? defaultDisplayNameLabel Показуване ім'я assistantTitle Помічник createAppSipAccountEnabledLabel Увімкнути створення обліківки useAppSipAccountEnabledLabel Увімкнути використання обліківки useOtherSipAccountEnabledLabel Увімкнути використання спільної обліківки fetchRemoteConfigurationEnabledLabel Увімкнути вибір конфігурації assistantSupportsPhoneNumbersLabel Підтримує номери телефонів defaultDeviceNameLabel 'Device Name' : Label for setting the device name. webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel Адреса SIP transportLabel Транспорт serverAddressLabel Адреса сервера SIP registrationDurationLabel Тривалість реєстрації (сек) routeLabel Маршрут contactParamsLabel Контактні параметри publishPresenceLabel Оголошувати статус avpfIntervalLabel AVPF звичайний RTCP інтервал (сек) registerEnabledLabel Реєстрація avpfEnabledLabel Увімкнути AVPF cancel СКАСУВАТИ confirm ПІДТВЕРДИТИ invalidSipAddress Неприпустима SIP адреса. invalidServerAddress Неприпустима адреса сервера. invalidRoute Неправильний маршрут. enableIceLabel Увімкнути ICE stunServerLabel Сервер STUN/TURN enableTurnLabel Увімкнути TURN turnUserLabel Користувач TURN turnPasswordLabel Пароль TURN natAndFirewallTitle NAT і файрвол mainSipAccountSettingsTitle Основні налаштування SIP обліківки conferenceURI "Conference URI" : Label of a text edit for filling Conference URI invalidConferenceURI "invalid conference URI" : Error text about conference URI videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) tunnelDomain 'Domain' : Field title of a textfield to set domain. tunnelUsername 'Username' : Field title of a textfield to set username. tunnelSIP 'SIP' : Field title of a switch to set SIP mode. cancel 'Cancel' : Button to cancel the action. СКАСУВАТИ setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. proxyHttpHost 'Host' : Placeholder to set hostname. proxyHttpPort 'Port' : Placehoilder to set port. proxyHttpUsername 'Username' : Placeholder to set username. proxyHttpPassword 'Password' : Placeholder to set password. proxyHttpApply 'Apply' : Button to set proxy from changes. serverMode 'Mode' : Field title on form to set tunnel mode. serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. serverTitle 'Server' : Title form to set a server serverHostname 'Hostname' : Field title on form to set hostname. serverPort 'Port' : Field title on form to set port. serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. serverDelay 'Delay' : Field title on form to set the delay of the tunnel. tunnelAddServer 'Add server' : Button for adding a server tunnelApply 'Apply' : Button to apply changes. SettingsUi pathsTitle Шляхи savedScreenshotsLabel Тека для збереження знятків savedCallsLabel Тека для запису дзвінків languagesTitle Мови languagesLabel Мова systemLocale Системна cleanAvatars ВИЛУЧИТИ МАРМИЗКИ cleanAvatarsDescription Ви впевнені, що волієте вилучити усі мармизки? downloadLabel Тека завантажень setLocaleDescription Необхідний перезапуск програми. Бажаєте перезапустити зараз? otherTitle Инше exitOnCloseLabel Завершувати програму при закритті вікна dataTitle Дані користувацького інтерфейсу autoStartLabel Самозапуск fontsTitle 'Fonts' : title of fonts section in settings fontsTextChange 'Text Messages' : Label for changing text message fonts fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates mipmapLabel 'Enable Mipmap' mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel Пристрій-джерело відео videoFramerateLabel Частота кадрів videoCaptureTitle Параметри захоплення відео videoPresetLabel Пресет відео presetDefault Типово presetHighFps Висока частота кадрів presetCustom Користувацьке videoSizeLabel Роздільність відео videoCodecsTitle Відео кодеки showCameraPreview ПОПЕРЕГЛЯД ВІДЕО showVideoCodecsLabel Показати Відео кодеки videoSettingsInCallWarning Відеодзвінок в процесі: деякі настройки недоступні. videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm Так SettingsWindow settingsTitle Налаштування sipAccountsTab SIP обліківки audioTab Звук videoTab Відео callsAndChatTab Дзвінки і чат networkTab Мережа uiTab Інтерфейс validButton Так uiAdvanced Додаткове tunnelTab 'Tunnel' : Tab title for tunnel section in settings. SipAddressDialog cancel СКАСУВАТИ contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list SmartSearchBar addContact ДОДАТИ КОНТАКТ Timeline timelineFilter A title for filtering mode. timelineFilterAll 'All' The mode for timelines filtering. timelineFilterCustom 'Custom' The mode for timelines filtering. timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction ВИКОРИСТОВУВАТИ useAppSipAccountTitle ВИКОРИСТОВУВАТИ ОБЛІКІВКУ %1 useUsernameToLogin Використовувати ім'я користувача та пароль замість номеру телефону. quitWarning Вашу обліківку було створено, але ще не підтверджено. Ви впевнені, що бажаєте вийти? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password UseAppSipAccountWithPhoneNumber countryLabel Країна phoneNumberLabel Номер телефону displayNameLabel Показуване ім'я (не обов'язково) UseAppSipAccountWithUsername usernameLabel Ім'я користувача passwordLabel Пароль displayNameLabel Показуване ім'я (не обов'язково) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form ВИКОРИСТОВУВАТИ useOtherSipAccountTitle ВИКОРИСТОВУВАТИ SIP ОБЛІКІВКУ usernameLabel Ім'я користувача displayNameLabel Показуване ім'я (не обов'язково) sipDomainLabel Домен SIP passwordLabel Пароль transportLabel Транспорт addOtherSipAccountError Не вдалося додати цю обліківку. understandAction 'I understand' : Popup confirmation for a warning warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. startButton 'Start' : Button label for starting the conference. endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. Підтвердіть такий рядок автентифікації з контактом. codeA Скажіть: codeB Ваш контакт повинен сказати: Later 'Later' : Button label to do something in another time. Пізніше Correct 'Correct' : Button label to confirm a code. Правильний title 'Communication security' : Title of popup for ZRTP confirmation. Безпека зв'язку country Afghanistan Афганістан Albania Албанія Algeria Алжір AmericanSamoa Американське Самоа Andorra Андорра Angola Анґола Anguilla Анґілья AntiguaAndBarbuda Антиґуа і Барбуда Argentina Арґентина Armenia Вірменія Aruba Аруба Australia Австралія Austria Австрія Azerbaijan Азербайджан Bahamas Багами Bahrain Бахрейн Bangladesh Банґладеш Barbados Барбадос Belarus Білорусь Belgium Бельґія Belize Беліз Benin Бенін Bermuda Бермудські Острови Bhutan Бутан Bolivia Болівія BosniaAndHerzegowina Боснія і Герцеґовина Botswana Ботсвана Brazil Бразилія Brunei Бруней Bulgaria Болґарія BurkinaFaso Буркіна-Фасо Burundi Бурунді Cambodia Камбоджа Cameroon Камерун Canada Канада CapeVerde Кабо-Верде CaymanIslands Кайманові острови CentralAfricanRepublic Центральноафриканська Республіка Chad Чад Chile Чилі China Китай Colombia Колумбія Comoros Коморські острови PeoplesRepublicOfCongo Народна Республіка Конґо DemocraticRepublicOfCongo Демократична Республіка Конґо CookIslands Острови Кука CostaRica Коста-Ріка IvoryCoast Кот-д'Івуар Croatia Хорватія Cuba Куба Cyprus Кіпр CzechRepublic Чеська Республіка Denmark Данія Djibouti Джибуті Dominica Домініка DominicanRepublic Домініканська республіка Ecuador Еквадор Egypt Єгипет ElSalvador Сальвадор EquatorialGuinea Екваторіальна Ґвінея Eritrea Еритрея Estonia Естонія Ethiopia Ефіопія FalklandIslands Фолклендські (Мальвінські) острови FaroeIslands Фарерські острови Fiji Фіджі Finland Фінляндія France Франція FrenchGuiana Французька Ґвіана FrenchPolynesia Французька Полінезія Gabon Ґабон Gambia Ґамбія Georgia Грузія Germany Німеччина Ghana Гана Gibraltar Ґібралтар Greece Греція Greenland Ґренландія Grenada Ґренада Guadeloupe Ґваделупа Guam Ґуам Guatemala Ґватемала Guinea Ґвінея GuineaBissau Ґвінея-Бісау Guyana Ґайана Haiti Гаїті Honduras Гондурас HongKong Гонконґ Hungary Угорщина Iceland Ісландія India Індія Indonesia Індонезія Iran Іран Iraq Ірак Ireland Ірландія Israel Ізраїль Italy Італія Jamaica Ямайка Japan Японія Jordan Йорданія Kazakhstan Казахстан Kenya Кенія Kiribati Кірібаті DemocraticRepublicOfKorea Демократична Республіка Корея RepublicOfKorea Республіка Корея Kuwait Кувейт Kyrgyzstan Кирґизстан Laos Лаос Latvia Латвія Lebanon Ліван Lesotho Лесото Liberia Ліберія Libya Лівія Liechtenstein Ліхтенштейн Lithuania Литва Luxembourg Люксембурґ Macau Макао Macedonia Македонія Madagascar Мадаґаскар Malawi Малаві Malaysia Малайзія Maldives Мальдіви Mali Малі Malta Мальта MarshallIslands Маршаллові острови Martinique Мартініка Mauritania Мавританія Mauritius Маврикій Mayotte Майотта Mexico Мексика Micronesia Мікронезія Moldova Молдова Monaco Монако Mongolia Монґолія Montenegro Монтенеґро Montserrat Монтсеррат Morocco Марокко Mozambique Мозамбік Myanmar М'янма Namibia Намібія NauruCountry Науру Nepal Непал Netherlands Нідерланди NewCaledonia Нова Каледонія NewZealand Нова Зеландія Nicaragua Нікараґуа Niger Ніґер Nigeria Нігерія Niue Ніуе NorfolkIsland Острів Норфолк NorthernMarianaIslands Північні Маріанські острови Norway Норвегія Oman Оман Pakistan Пакістан Palau Палау PalestinianTerritories Палестинські території Panama Панама PapuaNewGuinea Папуа Нова Ґвінея Paraguay Параґвай Peru Перу Philippines Філіппіни Poland Польща Portugal Портуґалія PuertoRico Пуерто-Ріко Qatar Катар Reunion Реюньйон Romania Румунія RussianFederation Російська Федерація Rwanda Руанда SaintHelena Острів Св. Олени SaintKittsAndNevis Сент-Кітс і Невіс SaintLucia Сент-Люсія SaintPierreAndMiquelon Сен-П'єр і Мікелон SaintVincentAndTheGrenadines Сент-Вінсент і Ґренадіни Samoa Самоа SanMarino Сан-Маріно SaoTomeAndPrincipe Сан-Томе і Прінсіпі SaudiArabia Саудівська Аравія Senegal Сенеґал Serbia Сербія Seychelles Сейшельські острови SierraLeone Сьєрра-Леоне Singapore Сінґапур Slovakia Словаччина Slovenia Словенія SolomonIslands Соломонові острови Somalia Сомалі SouthAfrica Південна Африка Spain Еспанія SriLanka Шрі-Ланка Sudan Судан Suriname Сурінам Swaziland Свазіленд Sweden Швеція Switzerland Швейцарія Syria Сирія Taiwan Тайвань Tajikistan Таджикистан Tanzania Танзанія Thailand Таїланд Togo Тоґо Tokelau Токелау Tonga Тонґа TrinidadAndTobago Тринідад і Тобаґо Tunisia Туніс Turkey Турція Turkmenistan Туркменістан TurksAndCaicosIslands Острови Теркс і Кайкос Tuvalu Тувалу Uganda Уґанда Ukraine Україна UnitedArabEmirates Об'єднані Арабські Емірати UnitedKingdom Сполучене Королівство UnitedStates Сполучені Штати Uruguay Уруґвай Uzbekistan Узбекистан Vanuatu Вануату Venezuela Венесуела Vietnam В'єтнам WallisAndFutunaIslands Уолліс і Футуна Yemen Ємен Zambia Замбія Zimbabwe Зімбабве utils downloadCodecDescription Бажаєте завантажити %1 (%2)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' formatDays '%1 day' formatHours '%1 hour' formatMinutes '%1 minute' formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/languages/zh_CN.ts000066400000000000000000005122651434616504300242350ustar00rootroot00000000000000 About ok aboutTranslation 'Help us translate %1' : %1 is the application name ActivateAppSipAccountWithEmail activateAppSipAccount 激活你的 %1 账户 confirmAction 激活 activationSteps 按照我们发往 %1 的指示来激活账户,然后点击下面. ActivateAppSipAccountWithPhoneNumber activateAppSipAccount 激活你的 %1 账户 confirmAction 激活 activationSteps 我们发送了一条短信至 %1 ,要完成手机号码验证,请输入短信中的 4 位验证码. App commandLineOptionVerbose 在运行时向 stdout 输出调试日志 commandLineOptionConfig 指定要使用的配置文件:%1 applicationDescription 一个免费(且自由)的 SIP 网络电话. commandLineOptionIconified 启动时最小化到系统托盘,不显示主界面 commandLineOptionConfigArg 文件 commandLineOptionHelp 显示关于这个的帮助 commandLineOptionVersion 显示应用版本 commandLineOptionCliHelp 用 CLI 来显示使用 %1 的帮助菜单 commandLineDescription 向命令行中发送给应用程序的指令 restore 恢复 quit 退出 settings 偏好设置 about 关于 commandLineOptionFetchConfig 指定要获取的配置文件: %1。 它将与当前配置合并。 commandLineOptionFetchConfigArg URL、路径或文件 commandLineOptionCall 打个电话 commandLineOptionCallArg SIP地址 checkForUpdates AssistantAbstractView back 返回 AssistantHome useAppSipAccount 使用 %1 账户 useOtherSipAccount 使用一个 SIP 账户 fetchRemoteConfiguration 获取远程配置文件 homeTitle 欢迎 homeDescription 该助手会帮助你配置并使用你的 SIP 账户. createAppSipAccount 创建一个 %1 账户 homeCgu 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. AssistantModel loginWithUsernameFailed 登录失败,请检查你的用户名/密码 usernameStatusTooShort 太短了!(最少需要 %1 个字符) usernameStatusTooLong 太长了!(最长 %1 个字符) usernameStatusInvalidCharacters 检测到无效字符。(regex: `%1`) usernameStatusInvalid 无效的用户名. passwordStatusTooShort 太短了!(最少 %1 个字符) passwordStatusTooLong 太长了!(最长 %1 个字符) passwordStatusInvalidCharacters 检测到无效字符。(regex: `%1`) passwordStatusMissingCharacters 缺少字符:`%1` 。 requestFailed 无法发送请求 emailStatusMalformed 邮箱地址错误 emailStatusMalformedInvalidCharacters 邮箱地址错误或检测到无效字符 cannotSendSms 服务器错误:无法发送短信. accountAlreadyExists 这个账户已存在. smsActivationFailed 短信激活失败! emailActivationFailed 请确保您已经验证了您的账户或重试. phoneNumberStatusInvalid 无效的手机号码! phoneNumberStatusTooShort 太短了! phoneNumberStatusTooLong 太长了! phoneNumberStatusInvalidCountryCode 无效的国家号码! loginWithPhoneNumberFailed 登录失败,请检查你的手机号码 unableToAddAccount 无法添加此账户。 AuthenticationRequest cancel 取消 confirm 登录 identityLabel 身份 passwordLabel 密码 authenticationRequestDescription 无法验证,请检查你的密码 userIdLabel 用户 ID (可选) realmLabel 领域 CallModel callStatsCodec 编码解码器 callStatsUploadBandwidth 上传带宽 callStatsDownloadBandwidth 下载带宽 callStatsEstimatedDownloadBandwidth 预估下载带宽 callStatsIceState ICE 状态 callStatsIpFamily IP 系列 callStatsSenderLossRate 发送者丢包率 callStatsReceiverLossRate 接收者丢包率 callStatsJitterBuffer 抖动缓冲器 callStatsSentVideoDefinition 发送的视频分辨率 callStatsReceivedVideoDefinition 接收的视频分辨率 iceStateNotActivated 未激活 iceStateFailed 失败 iceStateInProgress 进行中 iceStateReflexiveConnection 反射连接 iceStateHostConnection 主机连接 iceStateRelayConnection 中继连接 iceStateInvalid 无效 callErrorDeclined 对方拒绝了通话 callErrorNotFound 未找到对方 callErrorBusy 对方处于忙碌状态 callErrorNotAcceptable 对方无法接通 callStatsReceivedFramerate 接收的帧率 callStatsSentFramerate 发送的帧率 callErrorHangUp callStatsMediaEncryption 'Media encryption' : label in encryption section of call statistics 媒体加密 callStatsCipherAlgo 'Cipher algorithm' : label in encryption section of call statistics callStatsKeyAgreementAlgo 'Key agreement algorithm' : label in encryption section of call statistics callStatsHashAlgo 'Hash algorithm' : label in encryption section of call statistics 哈希算法 callStatsAuthAlgo 'Authentication algorithm' : label in encryption section of call statistics callStatsSasAlgo 'SAS algorithm' : label in encryption section of call statistics CallSipAddress cancel 取消 callSipAddressDescription 开始新的通话. CallStatistics audioStatsLabel 语音 videoStatsLabel 视频 mediaEncryptionLabel 'Media encryption' : title in call statistics for the encryption section 媒体加密 CallTransfer cancel 取消 callTransferDescription 你想要转移当前通话吗? Calls acceptAudioCall 接受语音通话 acceptVideoCall 接受视频通话 terminateCall 挂断 resumeCall 继续通话 transferCall 转移通话 callPause 暂挂通话 attendedTransferComplete 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. 转移完成 attendedTransferCall 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. 转接电话 CallsWindow callsTitle 通话 acceptClosingDescription 你确定要中止所有通话吗? Chat newMessagePlaceholder 输入你的消息 noFileTransferUrl 无法发送文件。 未配置服务器地址。 chatTyping '%1 is typing...' indicate that someone is composing in chat %1 正在输入… allTextCopied "Copied to clipboard" : when a user copy a text from the menu, this message show up. selectedTextCopied "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. forwardDialogTitle 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. conferencesCopiedICS ChatCalendarMessage icsOrganizer 'Organizer' : Label Title for the organizer. icsDescription 'Description' : Title for the meeting description. 描述信息 icsconferenceAddressTitle 'Meeting address' : Title for the meeting address. icsJoinButton 'Join' : Action button to join the meeting. deleteConferenceInfo 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. cancelConferenceInfo 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS Title for cancelled meetings ChatConferenceInvitationMessage icsDescription 'Description' : Title for the meeting description. 描述信息 icsJoinButton 'Join' : Action button to join the meeting. icsMeetingInvite 'Meeting invite' : ICS title that is an invitation. icsParticipants '%1 participant' : number(=%1) of participant. icsUpdatedMeetingInvite 'Meeting has been updated' : ICS title for an updated invitation. icsCancelledMeetingInvite 'Meeting has been cancelled' : ICS title for a cancelled invitation. ChatDeliveries deliveryDelivered 'Send to %1 - %2' Little message to indicate the state of a message 发送到 %1 - %2 %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. deliveryDeliveredToUser 'Retrieved by %1 - %2' Little message to indicate the state of a message 由 %1 - %2 接收 %1 is someone, %2 is a date/time. The state is that the message has been retrieved deliveryDisplayed 'Read by %1 - %2' Little message to indicate the state of a message 由%1 - %2 读取 %1 is someone, %2 is a date/time. The state that the message has been read. deliveryNotDelivered "%1 have nothing received" Little message to indicate the state of a message %1 没有收到消息 %1 is someone. The state is that the message hasn't been delivered. deliveryError "Error while sending to %1" Little message to indicate the state of a message %1 is someone. The state is that the message hasn't been delivered because of an error. ChatFileMessage fileTransferCancel 'Cancel' : Message link to cancel a transfer (upload/download) 取消 fileTransferDownload 'Download' : Message link to download a file 下载 ChatForwardMessage Forwarded 'Forwarded' : Header on a message that contains a forward. 已转发 ChatMenu menuCopyAll 'Copy all' : Text menu to copy all message text into clipboard 全部复制 menuCopy 'Copy' : Text menu to copy selected text in message into clipboard 复制 menuPlayMe 玩玩看! menuDeliveryStatus 'Delivery status' : Item menu that lead to IMDN of a message 发送状态 menuDelete 'Delete' : Item menu to delete a message 删除 menuHideDeliveryStatus 'Hide delivery status' : Item menu that lead to IMDN of a message 隐藏发送状态 menuForward 'Forward' : Forward a message from menu 转发 menuReply 'Reply' : Reply to a message from menu 回复 ChatNoticeModel nMinute %1 分钟 nHour %1 小时 nDay %1 天 nWeek %1 周 ChatReplyMessage headerReply 'Reply' : Header on a message that contains a reply. 回复 ChatReplyPreview titleReply 'Reply to %1' : Title for a reply preview to know who said what. Cli appCliDescription 使用命令行工具控制 %1 应用的方法. uriCommandLineSyntax cliCommandLineSyntax commandsName 命令列表: showFunctionDescription 显示应用程序的主窗口. callFunctionDescription 向这个 SIP 地址拨号. initiateConferenceFunctionDescription 开始一个会议. joinConferenceFunctionDescription 以显示的名字加入由此 SIP 地址创建的会议。如果你连接到了代理,请参阅“以身份加入会议”。 joinConferenceAsFunctionDescription 以访客身份加入此 SIP 地址创建的会议。如果你未连接到代理,请参阅“join-conference”. byeFunctionDescription 结束特定通话、所有通话或当前通话。 CodecsViewer codecMime 名字 codecEncoderDescription 描述信息 codecEncoderClockRate 率(赫兹) codecBitrate 比特率(Kbit/s) codecRecvFmtp 参数 codecStatus 状态 Conference conferenceTitle 会议 ConferenceControls conference 会议 ConferenceManager conferenceManagerDescription 管理会议参与者. cancel 取消 confirm 开始 Conferences conferencesTitle 'Meetings' : Conference list title. conferencesEndedFilter 'Finished' : Filter meetings on end status. conferencesScheduledFilter 'Scheduled' : Filter meetings on scheduled status. conferencesCopiedURL 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. conferencesDeleted 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. ConfirmDialog cancel 取消 confirm 确定 ContactEdit removeContactDescription 你真的要从地址簿中移除该联系人吗? sipAccounts SIP 账户(可有多个) address 地址 emails 电子邮箱(可有多个) webSites 网站(可有多个) avatarChooserTitle 选择你的头像 companies 公司 save 保存 cancel 取消 sipAccountsPlaceholder SIP 账户 companiesPlaceholder 公司 emailsPlaceholder 电子邮箱 webSitesPlaceholder 网站 street 街道 postalCode 邮政编码 country 国家 locality 地区 abortEditDescriptionText 你确定要取消对联系人的更改吗? tooltipShowConversation 显示对话 missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. 您需要在帐户设置中设置会议 URI 以创建基于会议的聊天室。 Contacts searchContactPlaceholder 搜索联系人 selectAllContacts 全部 selectConnectedContacts 已连接 addContact 添加联系人 removeContactDescription 你真的要从地址簿中移除该联系人吗? missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. 您需要在帐户设置中设置会议 URI 以创建基于会议的聊天室。 Conversation displayCallsAndMessages 全部 displayCalls 通话 displayMessages 信息 removeAllEntriesDescription 你确定要清除该历史吗? tooltipContactEdit 编辑联系人 tooltipContactAdd 添加联系人 cleanHistory 删除历史记录 adminStatus 'Admin' : Admin(istrator) 管理员 One word title for describing the current admin status conversationMenuGroupInformations 'Group information' : Item menu to get information about the chat room 群组信息 conversationMenuDevices "Conversation's devices" : Item menu to get all participant devices of the chat room 通话设备 conversationMenuEphemeral 'Ephemeral messages' : Item menu to enable ephemeral mode 临时信息 groupChatCallButton "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room 呼叫聊天室中的所有人 searchMessagesPlaceholder 'Search in messages' : this is a placeholder when searching something in the timeline list 查找信息 conversationMenuDelete 'Delete history' : Item menu to delete the chat's history 删除历史记录 conversationMenuViewContact 'View contact' : Item menu to view the contact in address book conversationMenuAddContact 'Add contact' : Item menu to add the contact to address book 添加联系人 conversationMenuScheduleMeeting 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. CreateAppSipAccount createAppSipAccountDescription 您想要如何创建账户? createAppSipAccountTitle 创建一个 %1 账户 withPhoneNumber 用手机号码 withEmailAddress 用电子邮箱地址 CreateAppSipAccountWithEmail createAppSipAccountTitle 创建一个 %1 账号 confirmAction 创建 usernameLabel 用户名 emailLabel 电子邮箱 passwordLabel 密码 passwordConfirmationLabel 再次输入密码 passwordConfirmationError 两次输入的密码不一致. quitWarning 您的账户已创建,但未验证。你确定要退出当前界面吗? displayNameLabel 显示名字(可选) CreateAppSipAccountWithPhoneNumber createAppSipAccountTitle 创建一个 %1 账户 countryLabel 国家 phoneNumberLabel 手机号码 usernameLabel 用户名 displayNameLabel 显示名字(可选) confirmAction 创建 quitWarning 您的账户已创建,但未验证。如果您退出当前界面,您将需要在 24 小时之内手动添加并验证您的账户。 DateTimeDialog dateTimeDialogDate 'Select date' : Menu title to show select date. dateTimeDialogTime 'Select time' : Menu title to show select time. dateTimeDialogDateTime 'Select date and time' : Menu title to show select date and time. DecorationSticker paused 'paused' : Pause state on sticker, next to username. DroppableTextArea fileChooserTitle 请选择一个或多个文件 dropYourAttachment 拖放您的附件 attachmentTooltip 发送文件 EphemeralChatRoom cancelButton 'cancel' : button text for cancelling operation 取消 startButton 'start' : button text to start ephemeral mode 开始 ephemeralTitle "Ephemeral messages" : Popup title for ephemerals 临时信息 ephemeralText 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals 一旦您的联系人阅读了新消息,两端的新消息都会被删除。 请选择超时时间。 ephemeralNotInConference! 'Ephemeral message is only supported in conference based chat room!' 仅在基于会议的聊天室中支持临时消息! Warning about not being in conference based chat room. disabled 'Disabled' 禁用 nMinute '%1 minute' %1 分钟 nHour '%1 hour' %1 小时 nDay '%1 day' %1 天 nWeek '%1 week' %1 周 Event incomingCall 来电 outgoingCall 去电 declinedIncomingCall 已挂断来电 declinedOutgoingCall 已挂断去电 endedCall 已中止通话 missedIncomingCall 未接来电 missedOutgoingCall 未接去电 FetchRemoteConfiguration confirmAction 获取 fetchRemoteConfigurationTitle 获取远程配置文件 urlLabel 链接 remoteProvisioningError 无法设定该远程服务地址 remoteProvisioningUpdateDescription 需要重启应用程序。您想要立刻重启吗? lastProvisioningFailed 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. generateLabel 'generate' : title button to generate a code. or 'or' : conjunction to choose between options. remoteProvisioningHow 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) scanQRCode 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. scanQRCodeWhere 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. HistoryView removeAllEntriesDescription 你确定要清除该历史吗? tooltipContactEdit 编辑联系人 tooltipContactAdd 添加联系人 cleanHistory 删除历史记录 Home howToDescription 需要关于如何使用 %1 的帮助? howToTitle 如何使用 %1 inviteDescription 在 %1 上邀请您的朋友. inviteTitle 邀请您的朋友 accountAssistantDescription 创建或管理您的 %1 账户. accountAssistantTitle 账户助手 assistantButton 助手 showTooltips 显示工具提示 inviteButton 邀请 Incall acceptVideoDescription 你的联系人希望开启视频通话 securedStringFormat 通话使用 %1 加密. callNotSecured 通话未加密 incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. 开始录制 incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. 停止录制 incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. 截取快照 incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. incallWaitParticipantMessage 'Waiting for another participant...' : Waiting message for more participant. aloneInConference ''You are alone in this conference' : Text in message banner when the user is the only participant. IncallFullscreen incallPauseWarning 'You are currently out of the conference.' : Pause message in video conference. incallPauseHint 'Click on play button to join it back.' : Explain what to do when being in pause in conference. incallStartRecordTooltip 'Start recording' : Tootltip when straing record. 开始录制 incallStopRecordTooltip 'Stop Recording' : Tooltip when stopping record. 停止录制 incallSnapshotTooltip 'Take Snapshot' : Tooltip for takking snapshot. 截取快照 incallWaitMessage 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. callWarningRecord 'This call is being recorded.' : Warn the user that the remote is currently recording the call. IncallMenu incallMenuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. incallMenuLayout 'Change layout' : Menu title to change the conference layout. incallMenuInvite 'Invite participants' : Menu title to invite participants in admin mode. incallMenuParticipants 'Participants list' : Menu title to show participants in non-admin mode. 成员列表 incallMenuTitle 'Settings' : Main menu title for settings. 设置 incallMenuGridLayout 'Mosaic mode' : Grid layout for video conference. incallMenuActiveSpeakerLayout 'Active speaker mode' : Active speaker layout for video conference. incallMenuAudioLayout 'Audio only mode' : Audio only layout for video conference. incallMenuParticipantsAlone 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. InfoChatRoom quitGroupButton 'Exit group' : Button label 退出群组 ok 'OK' : Button label addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room 添加成员 addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' 在您的联系人列表中搜索成员来邀请他们进入聊天室。 Explanation for inviting the selected participants into chat room participantList 'Participant list' 成员列表 adminStatus 'Admin' : Admin(istrator) 管理员 word for admin status chatRoomDetailsTitle "Group information" : Popup title. 群组信息 popup display data about the current chat room InfoEncryption cancelButton 'CANCEL' : button text for cancelling operation 取消 callButton 'CALL' : Button that lead to a call 通话 okButton 'OK' : Button that validate the popup to be redirected to the device list infoEncryptionTitle 'End-to-end encrypted' Popup title about encryption information. 端到端加密 encryptionExplanation "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." 即时消息在安全对话中进行端到端加密。 可以通过对参与者进行身份验证来升级对话的安全级别。 Explanation of Encryption encryptionProcessExplanation "To do so, call the contact and follow the authentification process." 为此,请致电联系人并按照身份验证流程进行操作。 Explanation process InviteFriends enterEmailLabel 朋友的电子邮箱地址 messageLabel 消息 cancel 取消 confirm 确定 inviteFriendsTitle 邀请朋友 defaultMessage %1 想要邀请你到 %2 ! defaultSubject %1 邀请 forcedMessage 下载该应用程序到您的电脑并开始免费与用户畅聊 点击这里:<a href="%1">%1</a> MAC_APPLICATION_MENU About %1 %1 关于 Preferences... Services Hide %1 Hide Others Show All Quit %1 %1 退出 MainWindow mainSearchBarPlaceholder 搜索联系人,并开始通话或聊天… contactsEntry 联系人 autoAnswerStatus 自动 smartSearchBarTooltip 使用智能搜索栏直接开始语音或视频通话 、发送消息或添加新联系人。只需键入 您朋友的 SIP 地址或用户名即可。 newConferenceButton 开始电话会议 newChatRoom 'Start a chat room' : Tooltip to illustrate a button 创建聊天室 hideTimeline 'Hide Timeline' : Tooltip for a button that hide the timeline 隐藏时间线 openTimeline 'Open Timeline' : Tooltip for a button that open the timeline 公开时间线 openHome 'Open Home' : Tooltip for a button that open the home view 开放主页 mainWindowConferencesTitle 'Meetings' : Meeting title for main window. newChatRoomUriMissing 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. newConferenceUriMissing 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. MainWindowMenuBar settings 偏好设置 about 关于 quit 退出 checkForUpdates 'Check for updates' : Item menu for checking updates MainWindowTopMenuBar settings 偏好 about 关于 quit 退出 checkForUpdates 'Check for updates' : Item menu for checking updates ManageAccounts ok selectPresenceLabel 在线状态 selectAccountLabel 活动账户 MultimediaParametersDialog ok menuMultimedia 'Multimedia parameters' : Menu title to show multimedia devices configuration. NewChatRoom cancelButton 'Cancel' : Cancel button 取消 startButton 'Launch' : Start button 实施 missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. 你需要填写一个主题。 missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. 你需要至少%1成员 missingConferenceURI 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. 您需要在帐户设置中设置会议 URI 以创建基于会议的聊天室。 newChatRoomTitle 'Start a chat room' : Title of a popup about creation of a chat room 创建聊天室 askEncryption 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. 是否加密你的聊天? subjectLabel 'Subject' : Label of a text field about the subject of the chat room 主题 subjectTooltip 'Current subject of the Chat Room. It cannot be empty' 聊天室的当前主题。 它不能为空。 Explanation about the subject of the chat room participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. 选择成员 participantSelectionTooltip 'Search in your contacts or add a custom one to the chat room.' 搜索您的联系人或向聊天室添加自定义联系人。 adminStatus 'Admin' : Admin(istrator) 管理员 word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection 从选择中删除此成员 This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) 要求 subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject 指定一个主题 LastContactsTitle 'Last contacts' : Header for showing last contacts 最近联系人 NewConference askEncryption 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. 是否加密你的聊天? cancelButton 'Cancel' : Cancel button 取消 missingSubject 'You need to fill a subject.' : Tooltip to warn a user on missing field. 你需要填写一个主题。 missingParticipants 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. 你需要至少%1成员 missingConferenceURI 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. 您需要在帐户设置中设置会议 URI 以创建基于会议的聊天室。 newConferenceTitle 'Start a video conference' : Title of a popup about creation of a video conference subjectLabel 'Subject' : Label of a text field about the subject of the conference 主题 subjectPlaceholder 'Give a subject' : Placeholder in a form about setting a subject 指定一个主题 subjectTooltip 'Current subject of the Meeting. It cannot be empty' 聊天室的当前主题。 它不能为空。 Explanation about the subject of the meeting participantSelectionPlaceholder 'Select participants' : Placeholder for a search on participant to add them in selection. 选择成员 participantSelectionTooltip 'Search in your contacts or add a custom one to the conference.' 搜索您的联系人或向聊天室添加自定义联系人。 adminStatus 'Admin' : Admin(istrator) 管理员 word for admin status removeParticipantSelection 'Remove this participant from the selection' : Explanation about removing participant from a selection 从选择中删除此成员 This is a tooltip requiredField 'Required' : Word relative to a star to explain that it is a requirement (Field form) 要求 launchButton 'Launch' : Launch button updateButton 'Update' : Update button updateConferenceTitle 'Update the meeting' : Title of a popup about updating configuration of a video conference. newConferenceScheduleTitle 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. newConferenceDate 'Date' : Date label. newConferenceTimeTitle 'Time' : Time label. newConferenceDurationTitle 'Duration' : Duration label. newConferenceTimezoneTitle 'Timezone' : Timezone label. newConferenceDescriptionTitle 'Add a description' : Label of a text field about the description of the conference newConferenceDescriptionPlaceholder 'Description' : Placeholder in a form about setting a description 描述信息 newConferenceDescriptionTooltip 'This description will describe the meeting' : Explanation about the description of the meeting newConferenceSendLinphoneInviteLabel 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. newConferenceSendEmailInviteLabel 'Send invite via Email' : Label for checkbox for sending invitations with mailer. busyOperations 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. 操作正在进行中,请稍候 confirmFormExit 'Do you want to close this form ?' : confirmation text for exiting the creatoin form Notice conferenceCreatedEvent 'You have joined the group' : Little message to show on the event when the user join the chat group. 你已加入群组 conferenceCreatedTerminated 'You have left the group' : Little message to show on the event when the user leave the chat group. 你已退出群组 conferenceParticipantAddedEvent '%1 has joined' : Little message to show on the event when someone join the chat group. %1 已加入 conferenceParticipantRemovedEvent '%1 has left' : Little message to show on the event when someone leave the chat group %1 已离开 conferenceParticipantSetAdminEvent '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody %1 现在成为管理员 conferencePArticipantUnsetAdminEvent '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody %1 不再是管理员 conferenceSecurityEvent 'Security level degraded by %1': Little message to show on the event when a security level has been lost. 由于%1,安全级别降低了 conferenceEphemeralMessageEnabledEvent 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time %1 已启用临时消息 conferenceEphemeralMessageDisabledEvent 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. 临时消息被禁用 conferenceSubjectChangedEvent 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. 新主题: %1 conferenceEphemeralMessageLifetimeChangedEvent 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time 临时消息已被更新: %1 unreadMessageNotice '%1 unread messages' : Little message to show on an event where unread messages begin. %1 未读消息 Notifier newVersionAvailable 新版本( %1 )可用! newFileMessage 收到新附件! newChatRoomMessages 'New messages received!' Notification that warn the user of new messages. OnlineInstallerDialog confirm 确定 onlineInstallerExtractingDescription 正在解压 %1 … onlineInstallerDownloadingDescription 正在下载 %1 … onlineInstallerFinishedDescription %1 已成功安装! onlineInstallerFailedDescription %1 安装失败! OutgoingMessage messageError 错误 messageRead 读取 messageDelivered 已送达 ParticipantsDevices conversationDevicesTitle 'Conversation's devices' : Title of window that show all devices 对话中的设备 ParticipantsListView addParticipantPlaceholder 'Add Participants' : Placeholder in a search bar for adding participant to the chat room 添加成员 addParticipantTooltip 'Search participants in your contact list in order to invite them into the chat room.' 在您的联系人列表中搜索成员来邀请他们进入聊天室。 Explanation for inviting the selected participants into chat room participantList 'Participant list' 成员列表 adminStatus 'Admin' : Admin(istrator) 管理员 word for admin status participantsListRemoveTooltip 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. 从选择中删除此成员 ParticipantsView participantsAdminHeader '(Admin)' : One word for Admin(istrator) (管理员) Little Header in one word for a column in participant participantsMe 'Me' : One word for myself. Presence presenceOnline 有空 presenceBusy 忙碌 presenceDoNotDisturb 请勿打扰 presenceOffline 离线 QObject LinphoneEnums_TunnelModeEnable 'Enable' : One word for button action to enable tunnel mode. LinphoneEnums_TunnelModeDisable 'Disable' : One word for button action to disable tunnel mode. 禁用 LinphoneEnums_TunnelModeAuto 'Auto' : One word for button action to set the auto tunnel mode. 自动 SettingsAdvanced logsTitle 日志 logsFolderLabel 日志文件夹 sendLogs 发送日志 logsUploadUrlLabel 日志上传服务器地址 logsUploadFailed 上传日志失败 logsEnabledLabel 启用日志 cleanLogs 清理日志 cleanLogsDescription 你确定要移除所有日志吗? developerSettingsTitle 开发者设置 developerSettingsEnabledLabel 启用开发者设置 logsMailerFailed Message when Linphone try to open a mailer to send logs, after uploading them to the server (%1 would be the upload url) 找不到邮件程序,但日志已上传到 %1 logsMailerSuccess 日志已上传到 %1 contactsTitle 联系人 noPlugin 'No Plugins to load' : Text in combobox 没有插件可加载 viewlogs SettingsAudio audioTitle 音频参数 playbackDeviceLabel 播放设备 captureDeviceLabel 捕获设备 ringerDeviceLabel 响铃设备 ringLabel 响铃 echoCancellationLabel 启用回音消除 audioCodecsTitle 音频编解码器 showAudioCodecsLabel 显示音频编解码器 playbackGainLabel 播放增益 captureGainLabel 捕获增益 audioTestLabel 捕获级别 audioSettingsInCallWarning 音频通话正在进行中:某些设置不可用。 echoCancellationCalibrationLabel 校准 calibratingEchoCancellationInProgress …校准中… calibratingEchoCancellationDone 校准至 %1ms calibratingEchoCancellationFailed 校准失败 calibratingEchoCancellationNone 未检测到回声 SettingsCallsChat fileServerLabel 文件服务器 encryptWithLimeLabel 使用 LIME 加密 limeDisabled 已禁用 limeRequired 必须 limePreferred 偏好 chatTitle 聊天 callsTitle 通话 encryptionLabel 加密 noEncryption autoAnswerLabel 自动接听 autoAnswerDelayLabel 延时(毫秒) autoAnswerWithVideoLabel 自动接听(使用视频) chatEnabledLabel 启用聊天 callRecorderEnabledLabel 启用通话录制器 chatNotificationSoundEnabledLabel 启用通知铃声 chatNotificationSoundLabel 通知铃声 conferenceEnabledLabel 启用会议 contactsTitle 联系人 contactsEnabledLabel 启用联系人 muteMicrophoneEnabledLabel 启用麦克风静音 outgoingCallsEnabledLabel 启用去电 showTelKeypadAutomaticallyLabel 自动显示拨号盘 automaticallyRecordCallsLabel 自动录制通话 keepCallsWindowInBackgroundLabel 在后台保留通话窗口 callPauseEnabledLabel 暂挂通话已启用 encryptionMandatoryLabel 硬化是强制性的 hideEmptyChatRoomsLabel 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms 隐藏空的聊天室 waitRegistrationForCallLabel 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered 注册时拨打 chatNotificationsEnabledLabel 'Enable notifications': settings label for enabling notifications. AutoDownload 'Auto download' : Label for a slider about auto download mode autoDownloadNever 'Never' : auto download mode description for deactivated feature. 永不 autoDownloadAlways 'Always' : auto download mode description for activated feature without any constraints. 总是 callScreenshotEnabledLabel 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. SettingsLdap newServer 新服务器 SettingsLdapEdit cancel 取消 confirm 确认 displayNameLabel 显示名称 displayNameTooltip 服务器名称会在列表中显示。 connectionTitle 连接 serverLabel 服务器URL serverTooltip LDAP 服务器。例如:ldap:/// for a localhost server or ldap://ldap.example.org/ bindDNLabel 绑定DN bindDNTooltip 绑定的 DN 是用于针对 LDAP 进行身份验证的凭据。<br> 例如:cn=ausername,ou=people,dc=bc,dc=com passwordLabel 密码 useTLSLabel 使用TLS useTLSTooltip 通过 LDAP over TLS(StartTLS) 加密交易。 您必须使用\'ldap\' 方案。 LDAP over SSL 的 \'ldaps\' 未标准化且已弃用。<br>StartTLS 是 LDAP 协议的扩展,它使用 TLS 协议来加密通信。 <br>它的工作原理是在服务器和 Web 服务之间进行握手协商之前,与 LDAP 服务器建立正常的(即不安全的)连接。 在这里,服务器在建立安全连接之前发送其证书以证明其身份。 useSalLabel 使用SAL useSalTooltip 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' DNS 解析已由 %1 使用 SAL 完成。 它会将 IP 传递给 LDAP。 通过这样做,TLS 协商无法检查主机名。 如果想强制连接,您可以停用验证。 verifyTLSLabel TLS证书验证 AutoMode 自动 offMode 关闭 onMode 开启 verifyTLSTooltip 指定连接到 LDAP 服务器时是否必须验证 TLS 服务器证书。 searchTitle 查找 baseObjectLabel 搜索库 baseObjectPlaceholder 搜索库 baseObjectTooltip 基本对象/搜索库是 LDAP 搜索范围的规范,它指定搜索请求应仅针对指定为搜索库 DN 的条目执行。<br>不会考虑上面的任何条目。 filterLabel 筛选 filterTooltip 搜索基于此过滤器来搜索联系人。<br>默认值:(sn=%s) maxResultsLabel 最多的结果 maxResultsTooltip 请求搜索时的最多结果。 timeoutLabel 超时 timeoutTooltip 连接和搜索的超时时间(秒)。一定为正数。<br>默认为 5 秒。 parsingTitle 解析中 nameAttributesLabel 名称属性 nameAttributesTooltip 检查这些属性来建立姓名联系人,用逗号分隔,第一个优先级最高。<br>默认值为:sn sipAttributesLabel SIP 属性 sipAttributesTooltip 检查这些属性来在联系人地址中构创建 SIP 用户名。 属性用逗号分隔,第一个优先级最高。<br>默认值为:mobile,telephoneNumber,homePhone,sn domainLabel 域名 domainTooltip 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. 添加域名到SIP地址(sip:username@domain)。 miscLabel Misc debugLabel 调试 debugTooltip 执行事务时在 Linphone 日志文件中获取详细日志(用于调试 TLS 连接)。 SettingsNetwork sendDtmfsLabel DTMF 发送方式 allowIpV6Label 允许 IPv6 transportTitle 传输 natAndFirewallTitle NAT 和防火墙 enableIceLabel 启用 ICE stunServerLabel STUN/TURN 服务器 enableTurnLabel 启用 TURN turnUserLabel TURN 用户名 turnPasswordLabel TURN 密码 networkProtocolAndPortsTitle 网络协议和端口 sipUdpPortLabel SIP UDP 端口 sipTcpPortLabel SIP TCP 端口 audioRtpUdpPortLabel 音频 RTP UDP 端口 videoRtpUdpPortLabel 视频 RTP UDP 端口 dscpFieldsTitle DSCP 域 sipFieldLabel SIP audioRtpStreamFieldLabel 音频 RTP 串流 videoRtpStreamFieldLabel 视频 RTP 串流 bandwidthControlTitle 带宽控制 downloadSpeedLimitLabel 下载速度限制 Kbit/s uploadSpeedLimitLabel 上传速度限制 Kbit/s enableAdaptiveRateControlLabel 启用自适应控流 presenceTitle 在线状态 rlsUriLabel 使用 RLS URI rlsUriAuto 自动 rlsUriDisabled 永不 showNetworkSettingsLabel 显示网络设置 generalTitle 将军 SettingsSipAccounts defaultIdentityTitle 默认身份 defaultUsernameLabel 用户名 defaultSipAddressLabel SIP 地址 proxyAccountsTitle 代理账户 eraseAllPasswords 删除所有密码 addAccount 添加帐户 editHeader 编辑 deleteHeader 删除 deleteAccountDescription 您确定要删除此帐户吗? eraseAllPasswordsDescription 是否确定要删除所有密码? defaultDisplayNameLabel 显示名称 assistantTitle 助理 createAppSipAccountEnabledLabel 启用账户创建 useAppSipAccountEnabledLabel 启用账户使用情况 useOtherSipAccountEnabledLabel 启用通用账户使用 fetchRemoteConfigurationEnabledLabel 启用配置提取 assistantSupportsPhoneNumbersLabel 支持电话号码 defaultDeviceNameLabel 'Device Name' : Label for setting the device name. 设备名称 webviewRegistrationUrlLabel 'Registration URL' : Label for registration URL. webviewLoginUrlLabel 'Login URL' : Label for login URL. SettingsSipAccountsEdit sipAddressLabel SIP 地址 transportLabel 交通工具 serverAddressLabel SIP 服务器地址 registrationDurationLabel 登记期限 (秒) routeLabel 路线 contactParamsLabel 联系参数 publishPresenceLabel 发布状况信息 avpfIntervalLabel AVPF常规RTCP间隔(秒) registerEnabledLabel 注册 avpfEnabledLabel 启用 AVPF cancel 取消 confirm 确认 invalidSipAddress SIP 地址无效。 invalidServerAddress 无效的服务器地址。 invalidRoute 无效的路线。 enableIceLabel 启用 ICE stunServerLabel STUN/TURN服务器 enableTurnLabel 启用TURN turnUserLabel TURN用户 turnPasswordLabel TURN密码 natAndFirewallTitle NAT 和防火墙 mainSipAccountSettingsTitle 主 SIP 帐户设置 conferenceURI "Conference URI" : Label of a text edit for filling Conference URI 会议URI invalidConferenceURI "invalid conference URI" : Error text about conference URI 无效的会议URI videoConferenceURI "Video Conference URI" : Label of a text edit for filling Video conference URI. limeServerUrl 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. invalidLimeServerUrl "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. SettingsTunnel tunnelStatus 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) tunnelDomain 'Domain' : Field title of a textfield to set domain. 域名 tunnelUsername 'Username' : Field title of a textfield to set username. 用户名 tunnelSIP 'SIP' : Field title of a switch to set SIP mode. SIP cancel 'Cancel' : Button to cancel the action. 取消 setHTTPProxy 'Set HTTP proxy' : Button to set the new proxy. proxyHttpHost 'Host' : Placeholder to set hostname. proxyHttpPort 'Port' : Placehoilder to set port. 端口 proxyHttpUsername 'Username' : Placeholder to set username. 用户名 proxyHttpPassword 'Password' : Placeholder to set password. 密码 proxyHttpApply 'Apply' : Button to set proxy from changes. serverMode 'Mode' : Field title on form to set tunnel mode. 模式 serverDualMode 'Dual mode' : Field title on form to set dual mode of the tunnel. serverTitle 'Server' : Title form to set a server serverHostname 'Hostname' : Field title on form to set hostname. 主机名 serverPort 'Port' : Field title on form to set port. 端口 serverDualHostname 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. serverDualPort 'Dual port' : Field title on form to set the second port for the dual configuration. serverRemoteUDPMirrorPort 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. serverDelay 'Delay' : Field title on form to set the delay of the tunnel. tunnelAddServer 'Add server' : Button for adding a server tunnelApply 'Apply' : Button to apply changes. SettingsUi pathsTitle 路径 savedScreenshotsLabel 已保存的截图文件夹 savedCallsLabel 已保存的呼叫文件夹 languagesTitle 语言 languagesLabel 语言 systemLocale 系统区域设置 cleanAvatars 擦除头像 cleanAvatarsDescription 你确定要擦掉所有的化身? downloadLabel 下载文件夹 setLocaleDescription 有必要重新启动应用程序。 你想立即重新启动吗? otherTitle 其他 exitOnCloseLabel 在关闭窗口中退出应用程序 dataTitle 用户界面数据 autoStartLabel 自动启动应用程序 fontsTitle 'Fonts' : title of fonts section in settings 字体 fontsTextChange 'Text Messages' : Label for changing text message fonts 短信 fontsPopupTitle 'Select a new font' : Popup title for choosing new fonts 选择新字体 checkForUpdateLabel 'Check for updates' : Label switch for enabling check for updates mipmapLabel 'Enable Mipmap' mipmapTooltip1 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. mipmapTooltip2 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. minimalTimelineFilterLabel 'Minimal Timeline filter' minimalTimelineFilterTooltip 'Show a minimal version of what to display in timeline.' : versionCheckTypeRelease 'Release' : Keyword for an option to check the release version versionCheckTypeCustom 'Custom' : Keyword for an option to check the custom version 自定义 versionCheckTypeNightly 'Nightly' : Keyword for an option to check the nightly version SettingsVideo videoInputDeviceLabel 视频输入设备 videoFramerateLabel 帧率 videoCaptureTitle 视频捕捉参数 videoPresetLabel 视频预设 presetDefault 默认值 presetHighFps 高 FPS presetCustom 定制 videoSizeLabel 视频分辨率 videoCodecsTitle 视频编解码器 showCameraPreview 视频预览 showVideoCodecsLabel 显示视频编解码器 videoSettingsInCallWarning 视频呼叫正在进行中:某些设置不可用。 videoDisplayTitle 'Video display' : Title for display parameters videoHybrid 'Hybrid' : Hybrid mode for camera. videoOccupyAllSpace 'Occupy all space' : Camera mode for a centered cropping view. videoBlackBars 'Black bars' : Camera mode for a fit view with black bars to keep ratio. videoLayout 'Default video layout' : Label to choose the default layout in video conference. videoActiveSpeakerLayout 'Active speaker' : Active speaker layout for video conference. videoMosaicLayout 'Mosaic' : Mosaic layout invideo conference. videoGridModeLabel 'Mosaic' : Label to choose a camera mode. videoActiveSpeakerModeLabel 'Active speaker' : Label to choose a camera mode. videoCallsModeLabel 'Calls' : Label to choose a camera mode. 电话 videoModeLabel 'Camera modes' : Label to choose a camera modes. SettingsVideoPreview confirm SettingsWindow settingsTitle 设置 sipAccountsTab SIP 账户 audioTab 音频 videoTab 视频 callsAndChatTab 通话和聊天 networkTab 网络 uiTab 用户界面 validButton uiAdvanced 先进 tunnelTab 'Tunnel' : Tab title for tunnel section in settings. 隧道协议 SipAddressDialog cancel 取消 contactsSearchPlaceholder 'Search in contacts' : Placeholder for a search a contact 搜索联系人 contactsSearchTooltip 'Search an address in your contacts or use a custom one.' : tooltip timelineSelectionHeader 'Conversations' : header for a selection in conversation list 对话 SmartSearchBar addContact 添加联系人 Timeline timelineFilter A title for filtering mode. 筛选 timelineFilterAll 'All' The mode for timelines filtering. 全部 timelineFilterCustom 'Custom' The mode for timelines filtering. 自定义 timelineFilterSimpleRooms 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). 基础聊天室 timelineFilterSecureRooms 'Secure rooms' : Filter item. Selecting it will show all secure rooms. 安全室 timelineFilterChatGroups 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). 聊天群 timelineFilterEphemerals 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. 阅后即焚 timelineSearchPlaceholderText 'Search in the list' : ths is a placeholder when searching something in the timeline list 在列表中查找 timelineFilterAllSecureLevelRooms 'All security levels' : Filter item. Selecting it will not do any filter on security level. timelineFilterStandardRooms 'Standard rooms' : Filter item. Selecting it will show all simple rooms. timelineFilterAnyChatRooms 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. timelineFilterAnyEphemerals 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. timelineFilterNoEphemerals 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. timelineFilterConferences 'Conferences' : Filter item. Selecting it will show all conferences. TimelineItem deleteTimeline 'Are you sure you want to delete and leave this timeline?' deleteTimelineTooltip 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' UseAppSipAccount confirmAction 使用 useAppSipAccountTitle 使用 %1 账户 useUsernameToLogin 使用用户名和密码,而不是您的电话号码。 quitWarning 您的账户已创建,但尚未验证。 是否确定要退出此视图? passwordRecovery 'Forgotten password?' : text for an url shortcut to change the password 忘记密码? UseAppSipAccountWithPhoneNumber countryLabel 国家 phoneNumberLabel 电话号码 displayNameLabel 显示名(可选) UseAppSipAccountWithUsername usernameLabel 用户名 passwordLabel 密码 displayNameLabel 显示名(可选) UseOtherSipAccount confirmAction 'Use' : Popup confirmation for a form 使用 useOtherSipAccountTitle 使用 SIP 帐户 usernameLabel 用户名 displayNameLabel 显示名称(可选) sipDomainLabel SIP 域 passwordLabel 密码 transportLabel 交通工具 addOtherSipAccountError 无法添加此账户。 understandAction 'I understand' : Popup confirmation for a warning 我明白 warningFeatures 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name warningThirdParty 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. warningContact 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. WaitingRoom cancelButton 'Cancel' : Cancel button. 取消 startButton 'Start' : Button label for starting the conference. endCallStatus "Call ended" : status of the call in waiting room when the call end. outgoingCallStatus "Outgoing call" : status of the call in waiting room when user is calling. 去電 incomingCallStatus "Incoming call" : status of the call in waiting room when user receive a call. 来电 ZrtpTokenAuthenticationDialog confirmSas 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. 与同行确认以下 SAS。 codeA 说: codeB 您的联系人应该说: Later 'Later' : Button label to do something in another time. 稍后 Correct 'Correct' : Button label to confirm a code. 正确 title 'Communication security' : Title of popup for ZRTP confirmation. 通讯保安 country Afghanistan 阿富汗 Albania 阿尔巴尼亚 Algeria 阿尔及利亚 AmericanSamoa 美属萨摩亚 Andorra 安道尔 Angola 安哥拉 Anguilla 安圭拉 AntiguaAndBarbuda 安提瓜和巴布达 Argentina 阿根廷 Armenia 亚美尼亚 Aruba 阿魯巴 Australia 澳大利亚 Austria 奥地利 Azerbaijan 阿塞拜疆 Bahamas 巴哈马 Bahrain 巴林 Bangladesh 孟加拉国 Barbados 巴巴多斯 Belarus 白俄罗斯 Belgium 比利时 Belize 伯利兹 Benin 贝宁 Bermuda 百慕大 Bhutan 不丹 Bolivia 玻利维亚 BosniaAndHerzegowina 波斯尼亚和黑塞哥维那 Botswana 博茨瓦纳 Brazil 巴西 Brunei 文莱 Bulgaria 保加利亚 BurkinaFaso 布基纳法索 Burundi 布隆迪 Cambodia 柬埔寨 Cameroon 喀麦隆 Canada 加拿大 CapeVerde 佛得角 CaymanIslands 开曼群岛 CentralAfricanRepublic 中非共和国 Chad 乍得 Chile 智利 China 中国 Colombia 哥伦比亚 Comoros 科摩罗 PeoplesRepublicOfCongo 刚果共和国 DemocraticRepublicOfCongo 刚果民主共和国 CookIslands 库克群岛 CostaRica 哥斯达黎加 IvoryCoast 象牙海岸 Croatia 克罗地亚 Cuba 古巴 Cyprus 塞浦路斯 CzechRepublic 捷克共和国 Denmark 丹麦 Djibouti 吉布提 Dominica 多米尼克 DominicanRepublic 多米尼加共和国 Ecuador 厄瓜多尔 Egypt 埃及 ElSalvador 萨尔瓦多 EquatorialGuinea 赤道几内亚 Eritrea 厄立特里亚 Estonia 爱沙尼亚 Ethiopia 埃塞俄比亚 FalklandIslands 福克兰群岛 FaroeIslands 法罗群岛 Fiji 斐济 Finland 芬兰 France 法国 FrenchGuiana 法属圭亚那 FrenchPolynesia 法属波利尼西亚 Gabon 加蓬 Gambia 冈比亚 Georgia 格鲁吉亚 Germany 德国 Ghana 加纳 Gibraltar 直布罗陀 Greece 希腊 Greenland 格陵兰 Grenada 格林纳达 Guadeloupe 瓜德罗普岛 Guam 关岛 Guatemala 危地马拉 Guinea 几内亚 GuineaBissau 几内亚比绍 Guyana 圭亚那 Haiti 海地 Honduras 洪都拉斯 HongKong 香港 Hungary 匈牙利 Iceland 冰岛 India 印度 Indonesia 印度尼西亚 Iran 伊朗 Iraq 伊拉克 Ireland 爱尔兰 Israel 以色列 Italy 意大利 Jamaica 牙买加 Japan 日本 Jordan 约旦 Kazakhstan 哈萨克斯坦 Kenya 肯尼亚 Kiribati 基里巴斯 DemocraticRepublicOfKorea 朝鲜民主主义人民共和国 RepublicOfKorea 大韩民国 Kuwait 科威特 Kyrgyzstan 吉尔吉斯斯坦 Laos 老挝 Latvia 拉脱维亚 Lebanon 黎巴嫩 Lesotho 莱索托 Liberia 利比里亚 Libya 利比亚 Liechtenstein 列支敦士登 Lithuania 立陶宛 Luxembourg 卢森堡 Macau 澳门 Macedonia 马其顿 Madagascar 马达加斯加 Malawi 马拉维 Malaysia 马来西亚 Maldives 马尔代夫 Mali 马里 Malta 马耳他 MarshallIslands 马绍尔群岛 Martinique 马提尼克 Mauritania 毛里塔尼亚 Mauritius 毛里求斯 Mayotte 马约特 Mexico 墨西哥 Micronesia 密克罗尼西亚 Moldova 摩尔多瓦 Monaco 摩纳哥 Mongolia 蒙古 Montenegro 黑山 Montserrat 蒙特塞拉特 Morocco 摩洛哥 Mozambique 莫桑比克 Myanmar 缅甸 Namibia 纳米比亚 NauruCountry 瑙鲁 Nepal 尼泊尔 Netherlands 荷兰 NewCaledonia 新喀里多尼亚 NewZealand 新西兰 Nicaragua 尼加拉瓜 Niger 尼日尔 Nigeria 尼日利亚 Niue 纽埃 NorfolkIsland 諾福克島 NorthernMarianaIslands 北马里亚纳群岛 Norway 挪威 Oman 阿曼 Pakistan 巴基斯坦 Palau 帕劳 PalestinianTerritories 巴勒斯坦领土 Panama 巴拿马 PapuaNewGuinea 巴布亚新几内亚 Paraguay 巴拉圭 Peru 秘鲁 Philippines 菲律宾 Poland 波兰 Portugal 葡萄牙 PuertoRico 波多黎各 Qatar 卡塔尔 Reunion 团聚 Romania 罗马尼亚 RussianFederation 俄罗斯联邦 Rwanda 卢旺达 SaintHelena 圣赫勒拿岛 SaintKittsAndNevis 圣基茨和尼维斯 SaintLucia 圣卢西亚 SaintPierreAndMiquelon 圣皮埃尔和密克隆岛 SaintVincentAndTheGrenadines 圣文森特和格林纳丁斯 Samoa 萨摩亚 SanMarino 圣马力诺 SaoTomeAndPrincipe 圣多美和普林西比 SaudiArabia 沙特阿拉伯 Senegal 塞内加尔 Serbia 塞尔维亚 Seychelles 塞舌尔 SierraLeone 塞拉利昂 Singapore 新加坡 Slovakia 斯洛伐克 Slovenia 斯洛文尼亚 SolomonIslands 所罗门群岛 Somalia 索马里 SouthAfrica 南非 Spain 西班牙 SriLanka 斯里兰卡 Sudan 苏丹 Suriname 苏里南 Swaziland 斯威士兰 Sweden 瑞典 Switzerland 瑞士 Syria 叙利亚 Taiwan 台湾 Tajikistan 塔吉克斯坦 Tanzania 坦桑尼亚 Thailand 泰国 Togo 多哥 Tokelau 托克劳 Tonga 汤加 TrinidadAndTobago 特立尼达和多巴哥 Tunisia 突尼斯 Turkey 土耳其 Turkmenistan 土库曼斯坦 TurksAndCaicosIslands 特克斯和凯科斯群岛 Tuvalu 图瓦卢 Uganda 乌干达 Ukraine 乌克兰 UnitedArabEmirates 阿拉伯联合酋长国 UnitedKingdom 大不列颠及北爱尔兰联合王国 UnitedStates 美国 Uruguay 乌拉圭 Uzbekistan 乌兹别克斯坦 Vanuatu 瓦努阿图 Venezuela 委内瑞拉 Vietnam 越南 WallisAndFutunaIslands 瓦利斯和富图纳群岛 Yemen 也门 Zambia 赞比亚 Zimbabwe 津巴布韦 utils downloadCodecDescription 是否要下载 %1 (%2)? formatYears '%1 year' formatMonths '%1 month' formatWeeks '%1 week' %1 周 formatDays '%1 day' %1 天 formatHours '%1 hour' %1 小时 formatMinutes '%1 minute' %1 分钟 formatSeconds '%1 second' linphone-desktop-5.0.2/linphone-app/assets/linphone.desktop.cmake000066400000000000000000000007231434616504300251730ustar00rootroot00000000000000[Desktop Entry] Name=@APPLICATION_NAME@ GenericName=SIP Phone Comment=@APPLICATION_DESCRIPTION@ Type=Application Exec=@EXECUTABLE_NAME@ %u Icon=@EXECUTABLE_NAME@ Terminal=false Categories=Network;Telephony; MimeType=x-scheme-handler/sip-@EXECUTABLE_NAME@;x-scheme-handler/sip;x-scheme-handler/sips-@EXECUTABLE_NAME@;x-scheme-handler/sips;x-scheme-handler/tel;x-scheme-handler/callto;x-scheme-handler/@EXECUTABLE_NAME@-config; X-PulseAudio-Properties=media.role=phone linphone-desktop-5.0.2/linphone-app/assets/linphonerc-factory000066400000000000000000000005311434616504300244330ustar00rootroot00000000000000[misc] log_collection_upload_server_url=https://www.linphone.org:444/lft.php [sound] ec_filter=MSWebRTCAEC [app] record_aware=1 [rtp] accept_any_encryption=1 [sip] chat_messages_aggregation_delay=1000 chat_messages_aggregation=1 zrtp_key_agreements_suites=MS_ZRTP_KEY_AGREEMENT_K255_KYB512 [video] max_conference_size=vga [ui] use_qrcode=0linphone-desktop-5.0.2/linphone-app/assets/qt.conf.in000066400000000000000000000001211434616504300225750ustar00rootroot00000000000000[Platforms] WindowsArguments = dpiawareness=@APP_QT_CONF_DPI@ @APP_QT_CONF_PATH@ linphone-desktop-5.0.2/linphone-app/build/000077500000000000000000000000001434616504300205005ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/build/CMakeLists.txt000066400000000000000000000040141434616504300232370ustar00rootroot00000000000000############################################################################ # CMakeLists.txt # Copyright (C) 2018 Belledonne Communications, Grenoble France # ############################################################################ # # 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 2 # 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ if(NOT CPACK_PACKAGE_NAME) set(CPACK_PACKAGE_NAME "linphone") endif () set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../../LICENSE.txt") set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${PROJECT_VERSION}) set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_IGNORE_FILES "^${PROJECT_SOURCE_DIR}/.git*" "^${CMAKE_BINARY_DIR}" "/\\\\..+" "OUTPUT" "WORK" "linphone-sdk" "cmake_builder" "submodules" "qt5.spec" "libmng.spec" ) bc_compute_full_version(PROJECT_VERSION_BUILD) if(PROJECT_VERSION_BUILD) set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${PROJECT_VERSION_BUILD}") endif() message("-- Package file name is ${CPACK_PACKAGE_FILE_NAME}") set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_FILE_NAME}) bc_generate_rpm_specfile("rpm/linphone.spec.cmake" "${PROJECT_SOURCE_DIR}/linphone.spec") include(CPack) linphone-desktop-5.0.2/linphone-app/build/rpm/000077500000000000000000000000001434616504300212765ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/build/rpm/linphone.spec.cmake000066400000000000000000000046571434616504300250610ustar00rootroot00000000000000# -*- rpm-spec -*- %define _prefix @CMAKE_INSTALL_PREFIX@ %define pkg_prefix @BC_PACKAGE_NAME_PREFIX@ %define _datarootdir %{_prefix}/share %define _datadir %{_datarootdir} %define _docdir %{_datadir}/doc %define build_number @PROJECT_VERSION_BUILD@ %if %{build_number} %define build_number_ext -%{build_number} %endif Name: @CPACK_PACKAGE_NAME@ Version: @PROJECT_VERSION@ Release: %{build_number}%{?dist} Summary: A free (libre) SIP video-phone. Group: Communications/Telephony License: GPL URL: https://www.linphone.org Source0: %{name}-%{version}%{?build_number_ext}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot Requires: %{pkg_prefix}bctoolbox Requires: %{pkg_prefix}belcard Requires: %{pkg_prefix}liblinphone Requires: %{pkg_prefix}mediastreamer Requires: %{pkg_prefix}minizip Requires: %{pkg_prefix}qt %description A free (libre) SIP video-phone. %if 0%{?rhel} && 0%{?rhel} <= 7 %global cmake_name cmake3 %define ctest_name ctest3 %else %global cmake_name cmake %define ctest_name ctest %endif %define custom_debug_package %{!?_enable_debug_packages:%debug_package}%{?_enable_debug_package:%{nil}} %custom_debug_package %prep %setup -n %{name}-%{version}%{?build_number_ext} %build %{expand:%%%cmake_name} . -DCMAKE_BUILD_TYPE=@CMAKE_BUILD_TYPE@ -DCMAKE_PREFIX_PATH:PATH=%{_prefix} @RPM_ALL_CMAKE_OPTIONS@ make %{?_smp_mflags} %install make install DESTDIR=%{buildroot} %clean rm -rf $RPM_BUILD_ROOT %post /sbin/ldconfig for i in 16 22 24 32 64 128 do xdg-icon-resource install --novendor --mode system --theme hicolor --context apps --size $i %{_datarootdir}/icons/hicolor/${i}x${i}/apps/linphone.png linphone done xdg-desktop-menu install --novendor --mode system %{_datarootdir}/applications/linphone.desktop %postun xdg-desktop-menu uninstall --mode system linphone.desktop for i in 16 22 24 32 64 128 do xdg-icon-resource uninstall --mode system --theme hicolor --context apps --size $i linphone done /sbin/ldconfig %files %defattr(-,root,root,-) %doc LICENSE.txt CHANGELOG.md README.md %{_bindir}/ %{_datadir}/ %changelog * Tue Nov 27 2018 ronan.abhamon - Do not set CMAKE_INSTALL_LIBDIR and never with _libdir! * Thu Mar 15 2018 ronan.abhamon - Initial RPM release. linphone-desktop-5.0.2/linphone-app/build/rpm/qt5.spec000066400000000000000000000055061434616504300226710ustar00rootroot00000000000000# -*- rpm-spec -*- %define _qt5_version 5.12.12 %define _qt5_dir /opt/com.belledonne-communications/linphone %define _qt5_archdatadir %{_qt5_dir} %define _qt5_bindir %{_qt5_dir}/bin %define _qt5_docdir %{_qt5_dir}/doc %define _qt5_headerdir %{_qt5_dir}/include %define _qt5_libdir %{_qt5_dir}/lib %define _qt5_plugindir %{_qt5_dir}/plugins %define _qt5_translationdir %{_qt5_dir}/translations Name: linphone-qt Summary: Qt5 Version: %{_qt5_version} Release: 1 License: LGPLv2 with exceptions or GPLv3 with exceptions Url: http://qt-project.org/ Source0: %{name}-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot %description Qt is a software toolkit for developing applications. %package devel Summary: Development libraries for liblinphone Group: Development/Libraries Requires: %{name} = %{version}-%{release} %description devel Qt is a software toolkit for developing applications. %prep %setup -n %{name}-%{version} #Notes for Qt 5.12 and above #qt-xcb includes libxcb-* but libxcb #qt-xkbcommon cannot be used anymore #-xkbcommon enables xkb support using system libs %build ./configure \ -opensource \ -confirm-license \ -release \ -shared \ -c++std c++11 \ -silent \ -nomake examples \ -nomake tests \ -dbus \ -feature-dbus \ -feature-accessibility \ -qt-freetype \ -qt-harfbuzz \ -qt-libjpeg \ -qt-libpng \ -qt-pcre \ -qt-xcb \ -xkbcommon \ -system-freetype \ -feature-freetype -fontconfig \ -skip wayland \ -system-zlib \ -archdatadir %{_qt5_archdatadir} \ -bindir %{_qt5_bindir} \ -docdir %{_qt5_docdir} \ -headerdir %{_qt5_headerdir} \ -libdir %{_qt5_libdir} \ -plugindir %{_qt5_plugindir} \ -prefix %{_qt5_dir} \ -translationdir %{_qt5_translationdir} make -j12 %install find . \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i 's/#!\/usr\/bin\/python/#!\/usr\/bin\/python3/g' make install INSTALL_ROOT=%{buildroot} -j12 # Some files got ambiguous python shebangs, we fix them to avoid install errors # Because in centos8 shebangs like #!/usr/bin/python are FORBIDDEN (see https://fedoraproject.org/wiki/Changes/Make_ambiguous_python_shebangs_error) %files %defattr(-,root,root,-) %license LICENSE.LGPL* LICENSE.FDL %{_qt5_archdatadir}/phrasebooks/*.qph %{_qt5_archdatadir}/qml/ %{_qt5_bindir}/ %{_qt5_docdir}/global/ %{_qt5_libdir}/libQt5*.so* %{_qt5_plugindir}/*/*.so* %{_qt5_translationdir}/ %files devel %defattr(-,root,root,-) %{_qt5_archdatadir}/mkspecs/ %{_qt5_headerdir}/Qt*/ %{_qt5_libdir}/*.a %{_qt5_libdir}/*.la %{_qt5_libdir}/*.prl %{_qt5_libdir}/cmake/Qt5*/*.cmake %{_qt5_libdir}/libQt5*.so %{_qt5_libdir}/pkgconfig/Qt5*.pc %changelog * Wed Dec 12 2018 Ghislain Mary - Simplify %files sections * Wed Mar 21 2018 Ronan Abhamon - Initial RPM linphone-desktop-5.0.2/linphone-app/cmake/000077500000000000000000000000001434616504300204615ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/cmake/FindMinizip.cmake000066400000000000000000000031311434616504300237010ustar00rootroot00000000000000############################################################################ # FindMinizip.cmake # Copyright (C) 2018 Belledonne Communications, Grenoble France # ############################################################################ # # 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 2 # 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ # # - Find the minizip include file and library # # MINIZIP_FOUND - system has minizip # MINIZIP_INCLUDE_DIRS - the minizip include directory # MINIZIP_LIBRARIES - The libraries needed to use minizip find_path(MINIZIP_INCLUDE_DIRS NAMES mz.h PATH_SUFFIXES include ) if(MINIZIP_INCLUDE_DIRS) set(HAVE_MZ_H 1) endif() find_library(MINIZIP_LIBRARIES NAMES minizip minizipd ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Minizip DEFAULT_MSG MINIZIP_INCLUDE_DIRS MINIZIP_LIBRARIES HAVE_MZ_H ) mark_as_advanced(MINIZIP_INCLUDE_DIRS MINIZIP_LIBRARIES HAVE_MZ_H) linphone-desktop-5.0.2/linphone-app/cmake_builder/000077500000000000000000000000001434616504300221675ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/cmake_builder/additional_steps.cmake000066400000000000000000000066351434616504300265310ustar00rootroot00000000000000############################################################################ # additional_steps.cmake # Copyright (C) 2017-2018 Belledonne Communications, Grenoble France # ############################################################################ # # 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 2 # 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ include("${CMAKE_CURRENT_LIST_DIR}/../application_info.cmake") if (LINPHONE_BUILDER_TARGET STREQUAL linphoneqt AND WIN32) # Create a shortcut to linphone.exe in install prefix. set(SHORTCUT_PATH "${CMAKE_INSTALL_PREFIX}/${EXECUTABLE_NAME}.lnk") set(SHORTCUT_TARGET_PATH "${CMAKE_INSTALL_PREFIX}/bin/${EXECUTABLE_NAME}.exe") set(SHORTCUT_WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}") configure_file("${CMAKE_CURRENT_LIST_DIR}/linphone_package/windows/winshortcut.vbs.in" "${CMAKE_CURRENT_BINARY_DIR}/winshortcut.vbs" @ONLY) add_custom_command(OUTPUT "${SHORTCUT_PATH}" COMMAND "cscript" "${CMAKE_CURRENT_BINARY_DIR}/winshortcut.vbs") add_custom_target(linphoneqt_winshortcut ALL DEPENDS "${SHORTCUT_PATH}" TARGET_linphone_builder) # Create a shortcut to the solution file in the top directory. set(SHORTCUT_PATH "${CMAKE_SOURCE_DIR}/../../Project.sln.lnk") set(SHORTCUT_TARGET_PATH "${LINPHONE_BUILDER_WORK_DIR}/cmake/Project.sln") set(SHORTCUT_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/../..") configure_file("${CMAKE_CURRENT_LIST_DIR}/linphone_package/windows/winshortcut.vbs.in" "${CMAKE_CURRENT_BINARY_DIR}/solutionshortcut.vbs" @ONLY) execute_process(COMMAND "cscript" "${CMAKE_CURRENT_BINARY_DIR}/solutionshortcut.vbs") endif () # Packaging. if (ENABLE_PACKAGING) get_cmake_property(_varnames VARIABLES) set(ENABLE_VARIABLES ) foreach (_varname ${_varnames}) if (_varname MATCHES "^ENABLE_.*") list(APPEND ENABLE_VARIABLES -D${_varname}=${${_varname}}) endif () endforeach () if (LINPHONE_BUILDER_TARGET STREQUAL linphoneqt) # Linphone and linphone SDK packages. linphone_builder_apply_flags() linphone_builder_set_ep_directories(linphone_package) linphone_builder_expand_external_project_vars() ExternalProject_Add(TARGET_linphone_package DEPENDS TARGET_linphone_builder TMP_DIR ${ep_tmp} BINARY_DIR ${ep_build} SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/linphone_package" DOWNLOAD_COMMAND "" CMAKE_GENERATOR ${CMAKE_GENERATOR} CMAKE_ARGS ${LINPHONE_BUILDER_EP_ARGS} -DCMAKE_INSTALL_PREFIX=${LINPHONE_BUILDER_WORK_DIR}/PACKAGE -DTOOLS_DIR=${CMAKE_BINARY_DIR}/programs -DLINPHONE_OUTPUT_DIR=${CMAKE_INSTALL_PREFIX} -DLINPHONE_DESKTOP_DIR=${CMAKE_CURRENT_LIST_DIR}/.. -DLINPHONE_SOURCE_DIR=${EP_linphone_SOURCE_DIR} ${ENABLE_VARIABLES} -DLINPHONE_BUILDER_SIGNING_IDENTITY=${LINPHONE_BUILDER_SIGNING_IDENTITY} ) endif () endif () linphone-desktop-5.0.2/linphone-app/cmake_builder/install.cmake000066400000000000000000000027111434616504300246400ustar00rootroot00000000000000############################################################################ # packaging.cmake # Copyright (C) 2020 Belledonne Communications, Grenoble France # ############################################################################ # # 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 2 # 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ include(../application_info.cmake) if (APPLE) #execute_process(COMMAND install_name_tool -id "@executable_path/../lib/libminizip.dylib" "${CMAKE_INSTALL_PREFIX}/lib/libminizip.dylib") execute_process(COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" "${CMAKE_BINARY_DIR}/${EXECUTABLE_NAME}") execute_process(COMMAND install_name_tool -add_rpath "@executable_path/../lib/" "${CMAKE_BINARY_DIR}/${EXECUTABLE_NAME}") else () endif () linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/000077500000000000000000000000001434616504300254565ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/CMakeLists.txt000066400000000000000000000675451434616504300302370ustar00rootroot00000000000000################################################################################ # # Copyright (c) 2017-2020 Belledonne Communications SARL. # # This file is part of linphone-desktop # (see https://www.linphone.org). # # 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 . # ################################################################################ cmake_minimum_required(VERSION 3.1) project(LINPHONE_PACKAGE) # Dummy project. if(ENABLE_BUILD_VERBOSE) message("Linphone-packages paths : ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}") endif() include("${CMAKE_CURRENT_SOURCE_DIR}/../../application_info.cmake") find_package(Git) #Policy set to allow link from other directory (cmake 3.13) if (POLICY CMP0079) cmake_policy(SET CMP0079 NEW) endif () set(QT_PATH "${Qt5Core_DIR}/../../..") # ============================================================================== # Build package version. # ============================================================================== bc_compute_full_version(APP_PROJECT_VERSION) if (GIT_EXECUTABLE AND NOT(APP_PROJECT_VERSION)) execute_process( COMMAND ${GIT_EXECUTABLE} describe --always OUTPUT_VARIABLE APP_PROJECT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../.." ) elseif (NOT(APP_PROJECT_VERSION)) set(APP_PROJECT_VERSION "0.0.0") endif () bc_parse_full_version(${APP_PROJECT_VERSION} LINPHONE_MAJOR_VERSION LINPHONE_MINOR_VERSION LINPHONE_MICRO_VERSION LINPHONE_BRANCH_VERSION) set(LINPHONE_VERSION ${LINPHONE_MAJOR_VERSION}.${LINPHONE_MINOR_VERSION}.${LINPHONE_MICRO_VERSION}) #string(REGEX REPLACE "([0-9.]+)-?.*" "\\1" LINPHONE_VERSION "${APP_PROJECT_VERSION}") #string(REPLACE "." ";" SPLITTED_LINPHONE_VERSION "${LINPHONE_VERSION}") #list(LENGTH SPLITTED_LINPHONE_VERSION SPLITTED_LINPHONE_VERSION_LENGTH) #list(GET SPLITTED_LINPHONE_VERSION 0 LINPHONE_MAJOR_VERSION) #list(GET SPLITTED_LINPHONE_VERSION 1 LINPHONE_MINOR_VERSION) #if (SPLITTED_LINPHONE_VERSION_LENGTH GREATER 2) # list(GET SPLITTED_LINPHONE_VERSION 2 LINPHONE_MICRO_VERSION) #endif () set(PACKAGE_VERSION "${APP_PROJECT_VERSION}") message(STATUS "Versions : ${APP_PROJECT_VERSION}, ${LINPHONE_VERSION}") # ============================================================================== # Preparing the Linphone SDK bundle. # ============================================================================== if (WIN32) set(LIBDIR ${CMAKE_INSTALL_BINDIR}) set(LIBPREFIX "") set(LIBEXT "dll") set(PLUGINEXT "dll") elseif (APPLE) set(LIBDIR ${CMAKE_INSTALL_LIBDIR}) set(LIBPREFIX "lib") set(LIBEXT "dylib") set(PLUGINEXT "so") else() set(LIBDIR ${CMAKE_INSTALL_LIBDIR}) set(LIBPREFIX "lib") set(LIBEXT "so") set(PLUGINEXT "so") endif () # Removed unless it is useful to get a zip from SDK : The SDK is packaged with binaries. #if (WIN32) # find_program(7Z_PROGRAM 7z PATHS "$ENV{ProgramFiles}/7-Zip") # if (7Z_PROGRAM) # execute_process( # COMMAND ${7Z_PROGRAM} a -tzip ${CMAKE_BINARY_DIR}/${EXECUTABLE_NAME}-sdk-${APP_PROJECT_VERSION}-win32.zip "@${EXECUTABLE_NAME}-sdk.list" # WORKING_DIRECTORY ${LINPHONE_OUTPUT_DIR} # ) # else () # message(WARNING "7z has not been found, cannot generate the SDK!") # endif () #elseif (APPLE) # execute_process( # COMMAND rm -rf "${CMAKE_BINARY_DIR}/${EXECUTABLE_NAME}-sdk-${APP_PROJECT_VERSION}-mac.zip" # COMMAND zip -ry "${CMAKE_BINARY_DIR}/${EXECUTABLE_NAME}-sdk-${APP_PROJECT_VERSION}-mac.zip" . -i "@${EXECUTABLE_NAME}-sdk.list" # WORKING_DIRECTORY ${LINPHONE_OUTPUT_DIR} # ) #endif () # ============================================================================== # Specific deployment. # ============================================================================== set(APP_QT_CONF_DPI "1") if (WIN32) file(GLOB LIB_FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}/*.dll") install(FILES ${LIB_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/") file(GLOB LIB_FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_BINDIR}/*.dll") if( ENABLE_OPENH264)# Remove openH264 lib from the installation. this codec will be download by user foreach(item ${LIB_FILES}) get_filename_component(LIBRARY_FILENAME ${item} NAME) if("${LIBRARY_FILENAME}" MATCHES "^openh264.*.dll$") list(REMOVE_ITEM LIB_FILES ${item}) endif() endforeach(item) endif() install(FILES ${LIB_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/") file(GLOB EXE_FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_BINDIR}/*.exe") if(MSVC AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) file(GLOB PDB_FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_BINDIR}/*.pdb") install(FILES ${PDB_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) file(GLOB PDB_FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}/*.pdb") install(FILES ${PDB_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endif() install(FILES ${EXE_FILES} DESTINATION "${CMAKE_INSTALL_BINDIR}/") file(GLOB PLUGINS_FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}/mediastreamer/plugins/*") install(FILES ${PLUGINS_FILES} DESTINATION "plugins/mediastreamer/" ) file(GLOB GRAMMAR_FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/Belr/grammars/*") install(FILES ${GRAMMAR_FILES} DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/belr/grammars/" ) install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/images" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}" USE_SOURCE_PERMISSIONS OPTIONAL) install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/sounds/linphone/" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/sounds/${EXECUTABLE_NAME}" USE_SOURCE_PERMISSIONS) if(ENABLE_APP_PACKAGE_ROOTCA) install(FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/Linphone/rootca.pem" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}/") endif() install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/linphonerc-factory" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}") set(APP_QT_CONF_DPI "0") set(APP_QT_CONF_PATH "") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../../assets/qt.conf.in" "${CMAKE_CURRENT_BINARY_DIR}/../../qt.conf" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/../../qt.conf" DESTINATION "${CMAKE_INSTALL_BINDIR}") install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/assistant" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}" USE_SOURCE_PERMISSIONS) install(TARGETS ${APP_PLUGIN} ARCHIVE DESTINATION "${LIBDIR}" LIBRARY DESTINATION "${LIBDIR}" RUNTIME DESTINATION "${LIBDIR}" ) if(ENABLE_SANITIZER AND MSVC) if( CMAKE_SIZEOF_VOID_P EQUAL 8) find_file(SANITIZER_LIB clang_rt.asan_dynamic-x86_64.dll) else() find_file(SANITIZER_LIB clang_rt.asan_dynamic-i386.dll) endif() install(FILES "${SANITIZER_LIB}" DESTINATION "${CMAKE_INSTALL_BINDIR}/") endif() ############################## set(BIN_ARCH "win32") if( CMAKE_SIZEOF_VOID_P EQUAL 8) set(MINGW_PACKAGE_PREFIX "mingw-w64-x86_64-") set(MINGW_TYPE "mingw64") set(BIN_ARCH "win64") else() set(MINGW_PACKAGE_PREFIX "mingw-w64-i686-") set(MINGW_TYPE "mingw32") endif() find_program(MSYS2_PROGRAM NAMES msys2_shell.cmd HINTS "C:/msys64/" ) # list(REMOVE_ITEM SHARE_CONTENT "${CMAKE_INSTALL_DATAROOTDIR}/belr" "${CMAKE_INSTALL_DATAROOTDIR}/Belr" "${CMAKE_INSTALL_DATAROOTDIR}/images" "${CMAKE_INSTALL_DATAROOTDIR}/${APPLICATION_NAME}" "${CMAKE_INSTALL_DATAROOTDIR}/${APPLICATION_NAME}" "${CMAKE_INSTALL_DATAROOTDIR}/sounds") # foreach (ITEM IN LISTS SHARE_CONTENT) # list(APPEND SHARE_CONTENT_EXCLUDE PATTERN "${ITEM}" EXCLUDE) # endforeach () elseif (APPLE) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/macos/Info.plist.in" "${CMAKE_CURRENT_BINARY_DIR}/../../Info.plist" @ONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/macos/entitlements.xml.in" "${CMAKE_CURRENT_BINARY_DIR}/../../entitlements.xml" @ONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/macos/linphone.icns" "${CMAKE_CURRENT_BINARY_DIR}/../../${EXECUTABLE_NAME}.icns" COPYONLY) set(APP_QT_CONF_PATH "[Paths]\nPlugins = PlugIns\nImports = Resources/qml\nQml2Imports = Resources/qml") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../../assets/qt.conf.in" "${CMAKE_CURRENT_BINARY_DIR}/../../qt.conf" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/../../qt.conf" DESTINATION "${APPLICATION_NAME}.app/Contents/Resources/") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/../../Info.plist" DESTINATION "${APPLICATION_NAME}.app/Contents") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/../../${EXECUTABLE_NAME}.icns" DESTINATION "${APPLICATION_NAME}.app/Contents/Resources") install(CODE "execute_process(COMMAND rsync -a \"${LINPHONE_OUTPUT_DIR}/Frameworks\" \"\${CMAKE_INSTALL_PREFIX}/${APPLICATION_NAME}.app/Contents/\" )") #Use rsync to bypass symlinks override issues of frameworks file(GLOB SHARED_LIBRARIES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}/lib*.dylib") if( ENABLE_OPENH264 )# Remove openH264 lib from the installation. this codec will be download by user foreach(item ${SHARED_LIBRARIES}) get_filename_component(LIBRARY_FILENAME ${item} NAME) if("${LIBRARY_FILENAME}" MATCHES "^libopenh264.*.dylib$") list(REMOVE_ITEM SHARED_LIBRARIES ${item}) endif() endforeach(item) endif() install(FILES ${SHARED_LIBRARIES} DESTINATION "${APPLICATION_NAME}.app/Contents/Frameworks") install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/images" DESTINATION "${APPLICATION_NAME}.app/Contents/Resources/${CMAKE_INSTALL_DATAROOTDIR}" USE_SOURCE_PERMISSIONS OPTIONAL) if(ENABLE_APP_PACKAGE_ROOTCA) install(FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/Linphone/rootca.pem" DESTINATION "${APPLICATION_NAME}.app/Contents/Resources/${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}") endif() install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/linphonerc-factory" DESTINATION "${APPLICATION_NAME}.app/Contents/Resources/${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}") install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/assistant" DESTINATION "${APPLICATION_NAME}.app/Contents/Resources/${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}" USE_SOURCE_PERMISSIONS) file(GLOB SHARED_LIBRARIES "${CMAKE_CURRENT_BINARY_DIR}/../../${APPLICATION_NAME}.app/Contents/Frameworks/lib*.dylib") foreach (LIBRARY ${SHARED_LIBRARIES}) get_filename_component(LIBRARY_FILENAME ${LIBRARY} NAME) message("Changing RPATH of ${LIBRARY_FILENAME} from '${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}' to '@executable_path/../Frameworks'") execute_process(COMMAND install_name_tool -rpath "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}" "@executable_path/../Frameworks" "${LIBRARY}") endforeach () install(TARGETS ${APP_PLUGIN} ARCHIVE DESTINATION "${APPLICATION_NAME}.app/Contents/Frameworks" LIBRARY DESTINATION "${APPLICATION_NAME}.app/Contents/Frameworks" RUNTIME DESTINATION "${APPLICATION_NAME}.app/Contents/Frameworks" ) install(CODE "execute_process(COMMAND install_name_tool -add_rpath \"@executable_path/../Frameworks/\" \"\${CMAKE_INSTALL_PREFIX}/${APPLICATION_NAME}.app/Contents/MacOS/${EXECUTABLE_NAME}\")") install(CODE "execute_process(COMMAND install_name_tool -add_rpath \"@executable_path/../lib/\" \"\${CMAKE_INSTALL_PREFIX}/${APPLICATION_NAME}.app/Contents/MacOS/${EXECUTABLE_NAME}\")") if (LINPHONE_BUILDER_SIGNING_IDENTITY) install(CODE "file(GLOB FRAMEWORKS_NAMES \"\${CMAKE_INSTALL_PREFIX}/${APPLICATION_NAME}.app/Contents/Frameworks/*\") foreach (FRAMEWORK \${FRAMEWORKS_NAMES}) execute_process(COMMAND \"codesign\" \"--options\" \"runtime,library\" \"--force\" \"--deep\" \"--verbose\" \"-s\" \"${LINPHONE_BUILDER_SIGNING_IDENTITY}\" \"\${FRAMEWORK}\") endforeach ()") install(CODE "execute_process(COMMAND \"codesign\" \"--force\" \"--deep\" \"--options\" \"runtime,library\" \"--verbose\" \"-s\" \"${LINPHONE_BUILDER_SIGNING_IDENTITY}\" \"\${CMAKE_INSTALL_PREFIX}/${APPLICATION_NAME}.app/Contents/Frameworks/mediastreamer2.framework/Versions/A/Libraries/libmswebrtc.so\")") if(ENABLE_VIDEO) install(CODE "execute_process(COMMAND \"codesign\" \"--force\" \"--deep\" \"--options\" \"runtime,library\" \"--verbose\" \"-s\" \"${LINPHONE_BUILDER_SIGNING_IDENTITY}\" \"\${CMAKE_INSTALL_PREFIX}/${APPLICATION_NAME}.app/Contents/Frameworks/mediastreamer2.framework/Versions/A/Libraries/libmsqogl.so\")") endif() install(CODE "execute_process(COMMAND \"codesign\" \"--entitlements\" \"${CMAKE_CURRENT_BINARY_DIR}/../../entitlements.xml\" \"--force\" \"--deep\" \"--options\" \"runtime,library\" \"--verbose\" \"-s\" \"${LINPHONE_BUILDER_SIGNING_IDENTITY}\" \"\${CMAKE_INSTALL_PREFIX}/${APPLICATION_NAME}.app\")") endif () # install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_NAME}.app" DESTINATION "." USE_SOURCE_PERMISSIONS) else()# Not Windows and Apple foreach (LIBRARY ${SHARED_LIBRARIES}) get_filename_component(LIBRARY_FILENAME ${LIBRARY} NAME) message("Changing RPATH of ${LIBRARY_FILENAME} from '${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}' to '$ORIGIN/../${CMAKE_INSTALL_LIBDIR}'") execute_process(COMMAND install_name_tool -rpath "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}" "$ORIGIN/../lib" "${LIBRARY}") execute_process(COMMAND install_name_tool -addrpath "$ORIGIN/../lib64" "${LIBRARY}") endforeach () install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_BINDIR}/" DESTINATION "${CMAKE_INSTALL_BINDIR}" USE_SOURCE_PERMISSIONS) #Just in case. This is useless because we have to use CMAKE_INSTALL_LIBDIR if( EXISTS "${LINPHONE_OUTPUT_DIR}/lib/") file(GLOB SHARED_LIBRARIES "${LINPHONE_OUTPUT_DIR}/lib/*.so*") if( ENABLE_OPENH264 )# Remove openH264 lib from the installation. this codec will be download by user foreach(item ${SHARED_LIBRARIES}) get_filename_component(LIBRARY_FILENAME ${item} NAME) if("${LIBRARY_FILENAME}" MATCHES "^libopenh264.*$") list(REMOVE_ITEM SHARED_LIBRARIES ${item}) endif() endforeach(item) endif() install(FILES ${SHARED_LIBRARIES} DESTINATION "lib") endif() if( EXISTS "${LINPHONE_OUTPUT_DIR}/lib64/") file(GLOB SHARED_LIBRARIES "${LINPHONE_OUTPUT_DIR}/lib64/*.so*") if( ENABLE_OPENH264 )# Remove openH264 lib from the installation. this codec will be download by user foreach(item ${SHARED_LIBRARIES}) get_filename_component(LIBRARY_FILENAME ${item} NAME) if("${LIBRARY_FILENAME}" MATCHES "^libopenh264.*$") list(REMOVE_ITEM SHARED_LIBRARIES ${item}) endif() endforeach(item) endif() install(FILES ${SHARED_LIBRARIES} DESTINATION "lib64") endif() install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}" USE_SOURCE_PERMISSIONS PATTERN "linphone" EXCLUDE PATTERN "sounds" EXCLUDE ) if(ENABLE_APP_PACKAGE_ROOTCA) install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/linphone/" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}" USE_SOURCE_PERMISSIONS) else() install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/linphone/" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}" USE_SOURCE_PERMISSIONS PATTERN "rootca.pem" EXCLUDE ) endif() install(DIRECTORY "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/sounds/linphone/" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/sounds/${EXECUTABLE_NAME}" USE_SOURCE_PERMISSIONS) if(ENABLE_BUILD_VERBOSE) message("INSTALLATION : ${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/" ) endif() file(GLOB PLUGINS_FILES "${LINPHONE_OUTPUT_DIR}/${CMAKE_INSTALL_LIBDIR}/mediastreamer/plugins/*") install(FILES ${PLUGINS_FILES} DESTINATION "plugins/mediastreamer/" ) # Install desktop/icon files. configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../../assets/linphone.desktop.cmake" "${CMAKE_CURRENT_BINARY_DIR}/../../${EXECUTABLE_NAME}.desktop" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/../../${EXECUTABLE_NAME}.desktop" DESTINATION "${CMAKE_INSTALL_DATADIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) set(APP_QT_CONF_PATH "") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../../assets/qt.conf.in" "${CMAKE_CURRENT_BINARY_DIR}/../../qt.conf" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/../../qt.conf" DESTINATION "${CMAKE_INSTALL_BINDIR}") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/images/linphone_logo.svg" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps/" RENAME "${EXECUTABLE_NAME}.svg") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/linphonerc-factory" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}") install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/assistant" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${EXECUTABLE_NAME}" USE_SOURCE_PERMISSIONS) set(ICON_DIRS 16x16 22x22 24x24 32x32 64x64 128x128 256x256) foreach (DIR ${ICON_DIRS}) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/icons/hicolor/${DIR}/apps/icon.png" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/${DIR}/apps/" RENAME "${EXECUTABLE_NAME}.png") endforeach () install(TARGETS ${APP_PLUGIN} ARCHIVE DESTINATION "${LIBDIR}" LIBRARY DESTINATION "${LIBDIR}" RUNTIME DESTINATION "${LIBDIR}" ) # WEBVIEW if(ENABLE_APP_WEBVIEW) install(FILES "${QT_PATH}/plugins/webview/libqtwebview_webengine.so" DESTINATION "plugins/webview") #Workaround : linuxdeploy doesn't deploy it endif() endif () if (MSVC) # string(REGEX REPLACE "Visual Studio ([0-9]+).*" "\\1" MSVC_VERSION "${CMAKE_GENERATOR}") include(InstallRequiredSystemLibraries) set(MSVC_VERSION ${MSVC_TOOLSET_VERSION}) set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE) endif() find_file(UCRTBASE_LIB "ucrtbase.dll" PATHS "C:/Windows/System32") install(FILES ${UCRTBASE_LIB} DESTINATION "${CMAKE_INSTALL_BINDIR}") # find_file(MSVCP_LIB "msvcp${MSVC_VERSION}0.dll" PATHS "C:/Windows/System32") # # Starting with Visual Studio 2015 (MSVC_VERSION==14) the msvcr dll has been renamed to vcruntime. # find_file(VCRUNTIME_LIB "vcruntime${MSVC_VERSION}0.dll" PATHS "C:/Windows/System32") # if (NOT VCRUNTIME_LIB) # find_file(VCRUNTIME_LIB "msvcr${MSVC_VERSION}0.dll" PATHS "C:/Windows/System32") # endif () # message("MSDIRS : ${MSVCP_LIB} ${UCRTBASE_LIB} ${VCRUNTIME_LIB} => ${MSVC_VERSION}, ${CMAKE_GENERATOR}") # install(FILES ${MSVCP_LIB} ${UCRTBASE_LIB} ${VCRUNTIME_LIB} DESTINATION "${CMAKE_INSTALL_BINDIR}") # if (CMAKE_BUILD_TYPE STREQUAL "Debug") # find_file(MSVCPD_LIB "msvcp${MSVC_VERSION}0d.dll" PATHS "C:/Windows/System32") # find_file(UCRTBASED_LIB "ucrtbased.dll" PATHS "C:/Windows/System32") # find_file(VCRUNTIMED_LIB "vcruntime${MSVC_VERSION}0d.dll" PATHS "C:/Windows/System32") # if (NOT VCRUNTIMED_LIB) # find_file(VCRUNTIMED_LIB "msvcr${MSVC_VERSION}0d.dll" PATHS "C:/Windows/System32") # endif () # install(FILES ${MSVCPD_LIB} ${UCRTBASED_LIB} ${VCRUNTIMED_LIB} DESTINATION "${CMAKE_INSTALL_BINDIR}") # endif () endif () # ============================================================================== # CPack. # ============================================================================== if(${ENABLE_APP_PACKAGING}) set(CPACK_BINARY_STGZ OFF) set(CPACK_BINARY_TGZ OFF) set(CPACK_BINARY_TZ OFF) set(CPACK_PACKAGE_NAME "${APPLICATION_NAME}") set(CPACK_PACKAGE_VENDOR "${APPLICATION_VENDOR}") set(CPACK_PACKAGE_VERSION_MAJOR ${LINPHONE_MAJOR_VERSION}) set(CPACK_PACKAGE_VERSION_MINOR ${LINPHONE_MINOR_VERSION}) if (LINPHONE_MICRO_VERSION) set(CPACK_PACKAGE_VERSION_PATCH ${LINPHONE_MICRO_VERSION}) endif () set(CPACK_PACKAGE_EXECUTABLES "${EXECUTABLE_NAME};${APPLICATION_NAME}") if(ENABLE_APP_LICENSE) set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/../../../LICENSE.txt") else() unset(CPACK_RESOURCE_FILE_LICENSE) endif() set(CPACK_RESOURCE_FILE_LICENSE_PROVIDED ENABLE_APP_LICENSE) set(CPACK_PACKAGE_INSTALL_DIRECTORY "${APPLICATION_NAME}") set(CPACK_PACKAGE_DIRECTORY "${CMAKE_INSTALL_PREFIX}/Packages") set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/icon.ico") set(PERFORM_SIGNING 0) if (APPLE) set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${PACKAGE_VERSION}-mac") set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_CURRENT_SOURCE_DIR}/macos/background_dmg.jpg") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/macos/linphone_dmg.scpt.in" "${CMAKE_CURRENT_BINARY_DIR}/linphone_dmg.scpt" @ONLY) set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/linphone_dmg.scpt") set(CPACK_BINARY_DRAGNDROP ON) set(PACKAGE_EXT "dmg") message(STATUS "Set DragNDrop CPack generator in OUTPUT/Packages") elseif(NOT(WIN32)) set(DO_APPIMAGE YES) set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${PACKAGE_VERSION}") set(PACKAGE_EXT "AppImage") message(STATUS "Set AppImage CPack generator in OUTPUT/Packages") else() set(CPACK_GENERATOR "NSIS") set(DO_GENERATOR YES) set(PACKAGE_EXT "exe") string(COMPARE EQUAL ${CPACK_GENERATOR} "NSIS" IS_NSIS) set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${PACKAGE_VERSION}-${BIN_ARCH}") if (${IS_NSIS}) find_program(NSIS_PROGRAM makensis) if(NOT NSIS_PROGRAM) message(STATUS "Installing windows tools for nsis") execute_process( COMMAND "${MSYS2_PROGRAM}" "-${MINGW_TYPE}" "-here" "-full-path" "-defterm" "-shell" "sh" "-l" "-c" "pacman -Sy ${MINGW_PACKAGE_PREFIX}nsis --noconfirm --needed" ) endif() set(PACKAGE_EXT "exe") # Use magic `NSIS.template.in` template from the current source directory to force uninstallation # and ensure that linphone is not running before installation. set(CPACK_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/windows") set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\windows\\\\nsis_banner.bmp") set(CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/icon.ico") set(CPACK_NSIS_MUI_UNIICON "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/icon.ico") set(CPACK_NSIS_DISPLAY_NAME "${APPLICATION_NAME}") if (LINPHONE_MICRO_VERSION) set(CPACK_NSIS_PACKAGE_NAME "${APPLICATION_NAME} ${LINPHONE_MAJOR_VERSION}.${LINPHONE_MINOR_VERSION}.${LINPHONE_MICRO_VERSION}") else () set(CPACK_NSIS_PACKAGE_NAME "${APPLICATION_NAME} ${LINPHONE_MAJOR_VERSION}.${LINPHONE_MINOR_VERSION}") endif () set(CPACK_NSIS_URL_INFO_ABOUT ${APPLICATION_URL}) file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}" DOS_STYLE_BINARY_DIR) string(REPLACE "\\" "\\\\" ESCAPED_DOS_STYLE_BINARY_DIR "${DOS_STYLE_BINARY_DIR}") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/windows/install.nsi.in" "${CMAKE_CURRENT_BINARY_DIR}/install.nsi" @ONLY) set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "!include \\\"${ESCAPED_DOS_STYLE_BINARY_DIR}\\\\install.nsi\\\"") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/windows/uninstall.nsi.in" "${CMAKE_CURRENT_BINARY_DIR}/uninstall.nsi" @ONLY) set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "!include \\\"${ESCAPED_DOS_STYLE_BINARY_DIR}\\\\uninstall.nsi\\\"") set(CPACK_NSIS_EXECUTABLES_DIRECTORY "bin") set(CPACK_NSIS_MUI_FINISHPAGE_RUN "${EXECUTABLE_NAME}.exe") message(STATUS "Set NSIS CPack generator in OUTPUT/Packages") else () set(PACKAGE_EXT "msi") set(CPACK_WIX_UPGRADE_GUID "C748668E-53D0-4088-A548-E33A76615A3B") set(CPACK_WIX_PRODUCT_ICON "${CMAKE_CURRENT_SOURCE_DIR}/../../assets/icon.ico") set(CPACK_WIX_PROPERTY_ARPURLINFOABOUT "${APPLICATION_URL}") message(STATUS "Set MSI CPack generator in OUTPUT/Packages") # TODO: Deal with install/uninstall.nsi endif () if(LINPHONE_WINDOWS_SIGN_TOOL AND LINPHONE_WINDOWS_SIGN_TIMESTAMP_URL) find_program(SIGNTOOL ${LINPHONE_WINDOWS_SIGN_TOOL}) set(TIMESTAMP_URL ${LINPHONE_WINDOWS_SIGN_TIMESTAMP_URL}) if (SIGNTOOL) set(SIGNTOOL_COMMAND ${SIGNTOOL}) message("Found requested signtool") set(PERFORM_SIGNING 1) else () message(STATUS "Could not find requested signtool! Code signing disabled (${LINPHONE_WINDOWS_SIGN_TOOL})") endif () elseif(LINPHONE_WINDOWS_SIGNING_DIR) # Sign the installer. set(TIMESTAMP_URL "http://timestamp.digicert.com") set(PFX_FILE "${LINPHONE_WINDOWS_SIGNING_DIR}/linphone.pfx") set(PASSPHRASE_FILE "${LINPHONE_WINDOWS_SIGNING_DIR}/passphrase.txt") get_filename_component(WINSDK_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]" REALPATH CACHE) find_program(SIGNTOOL signtool PATHS ${WINSDK_DIR}/${CMAKE_INSTALL_BINDIR}) if (EXISTS ${PFX_FILE}) if (SIGNTOOL) set(SIGNTOOL_COMMAND ${SIGNTOOL}) message("Found signtool and certificate ${PFX_FILE}") set(PERFORM_SIGNING 1) else () message(STATUS "Could not find signtool! Code signing disabled (${SIGNTOOL})") endif () else () message(STATUS "No signtool certificate found; assuming development machine (${PFX_FILE})") endif () endif () endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/packaging.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/packaging.cmake" @ONLY) install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/packaging.cmake") if(LINPHONE_SDK_MAKE_RELEASE_FILE_URL) bc_make_release_file("${APP_PROJECT_VERSION}" "${LINPHONE_SDK_MAKE_RELEASE_FILE_URL}/${CPACK_PACKAGE_FILE_NAME}.${PACKAGE_EXT}") endif() include(CPack) endif() function(deployqt_hack target qml_dir) find_package(Qt5 COMPONENTS Core REQUIRED) get_target_property(qmake_executable Qt5::qmake IMPORTED_LOCATION) get_filename_component(_qt_bin_dir "${qmake_executable}" DIRECTORY) #Note: CMAKE_CURRENT_SOURCE_DIR point to linphone-app because deployqt_hack is called there. add_custom_command(TARGET ${target} POST_BUILD COMMAND "${CMAKE_COMMAND}" "-E" "copy_directory" "${CMAKE_CURRENT_SOURCE_DIR}/ui" "${qml_dir}" WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/..") if(NOT ENABLE_APP_WEBVIEW) set(RM_COMMAND "rm") if(CMAKE_VERSION VERSION_LESS 3.17) set(RM_COMMAND "remove") endif() add_custom_command(TARGET ${target} POST_BUILD COMMAND "${CMAKE_COMMAND}" "-E" "${RM_COMMAND}" "-f" "${qml_dir}/views/App/Main/Assistant/CreateAppSipAccountWithWebView.qml" WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/..") endif() if(WIN32) #Windeployqt hack for CPack. WindeployQt cannot be used only with a simple 'install(CODE "execute_process' or CPack will not have all required files. find_program(DEPLOYQT_PROGRAM windeployqt HINTS "${_qt_bin_dir}") if (NOT DEPLOYQT_PROGRAM) message(FATAL_ERROR "Could not find the windeployqt program. Make sure it is in the PATH.") endif () add_custom_command(TARGET ${target} POST_BUILD COMMAND "${CMAKE_COMMAND}" -E remove_directory "${CMAKE_CURRENT_BINARY_DIR}/winqt/" COMMAND "${CMAKE_COMMAND}" -E env PATH="${_qt_bin_dir}" "${DEPLOYQT_PROGRAM}" --qmldir "${qml_dir}" --plugindir "${CMAKE_CURRENT_BINARY_DIR}/winqt/plugins" --verbose 0 --no-compiler-runtime --dir "${CMAKE_CURRENT_BINARY_DIR}/winqt/" "$" COMMENT "Deploying Qt..." WORKING_DIRECTORY "${CMAKE_INSTALL_PREFIX}/.." ) install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/winqt/" DESTINATION bin) set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) include(InstallRequiredSystemLibraries) elseif(APPLE) find_program(DEPLOYQT_PROGRAM macdeployqt HINTS "${_qt_bin_dir}") if (NOT DEPLOYQT_PROGRAM) message(FATAL_ERROR "Could not find the macdeployqt program. Make sure it is in the PATH.") endif() install(CODE "execute_process(COMMAND ${DEPLOYQT_PROGRAM} \${CMAKE_INSTALL_PREFIX}/${APPLICATION_NAME}.app -qmldir=${qml_dir} -no-strip )") endif() endfunction() linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/macos/000077500000000000000000000000001434616504300265605ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/macos/Info.plist.in000066400000000000000000000060501434616504300311360ustar00rootroot00000000000000 CFBundleDevelopmentRegion English CFBundleName @APPLICATION_NAME@ CFBundleDisplayName @APPLICATION_NAME@ CFBundleExecutable @EXECUTABLE_NAME@ CFBundleGetInfoString @PACKAGE_VERSION@, (C) @COPYRIGHT_RANGE_DATE@ @APPLICATION_NAME@ @APPLICATION_URL@ CFBundleIconFile @EXECUTABLE_NAME@.icns CFBundleIdentifier @APPLICATION_ID@ CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleShortVersionString @PACKAGE_VERSION@ CFBundleSignature ???? CFBundleVersion @PACKAGE_VERSION@ NSHumanReadableCopyright Copyright @COPYRIGHT_RANGE_DATE@ @APPLICATION_VENDOR@ LSMinimumSystemVersion @CMAKE_OSX_DEPLOYMENT_TARGET@ NSAppSleepDisabled YES CFBundleURLTypes CFBundleURLName @APPLICATION_ID@ CFBundleURLSchemes sip sip-@EXECUTABLE_NAME@ sips sips-@EXECUTABLE_NAME@ tel callto @EXECUTABLE_NAME@-config NSPrincipalClass NSApplication NSHighResolutionCapable True NSCameraUsageDescription Streaming Video between devices NSMicrophoneUsageDescription Streaming Audio between devices NSPhotoLibraryUsageDescription Store attachment and allow camera record when selected NSPhotoLibraryAddUsageDescription Store attachment and allow camera record when selected NSDesktopFolderUsageDescription Storing configuration files NSDocumentsFolderUsageDescription Can use Documents folder to put logs NSDownloadsFolderUsageDescription Getting files from Download folder for sending attachment NSAppleEventsUsageDescription Used for sending notifications and working with Appnap NSBluetoothPeripheralUsageDescription Use for wireless headsets kTCCServiceMediaLibrary Used media library as attachement or for the ringer com.apple.security.device.camera com.apple.security.device.microphone com.apple.security.device.audio-input com.apple.security.personal-information.photos-library linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/macos/background_dmg.jpg000066400000000000000000000463061434616504300322410ustar00rootroot00000000000000JFIFHHExifMM*>F(iNHH02100100http://ns.adobe.com/xap/1.0/ 72 72 Inch Exif Version 2.1 FlashPix Version 1.0 Uncalibrated 700 450 CC   1Q1sh;[Ff[$ s,e?lKX-%8ud }>~=غDe#r*`깠6FW%<2XU4\?he^⹸]~u^Pr Cm,uYeF^YWV~'hLem"un9b'l+-*K9ٔUYxzhUĸG[Ut"+Jd-#+Y@|?!;Xygp ,!JPR[#'LY==S1ЫKaMcwKcR#>GEPxG~ܕ{DuN:hy1~`͹ tpbr@)]2,2h9SEhRE#8H͌dm#;(d=u#S(yd=uH {B/7`56P 04@7!Lu?aESGpe[iiM(v^eDkyqm'Fn.c06}-@8%͉ xH(t-/KY7N.ΩV !PR#pp(=R6R+`QSZ2 ӕW[n:owLh+ CpqH)1ZgT,SdU-ȴNE[nDfpWwS+pUcq/.%ũ4~suw=' RZ0u,(yj:Ou^h<-Չ.$0, `hxjO^$,[ɉiծ_#-MBur`MF2ãs#0X7-t] ҕU_/ Oa`'k? SLT5tYس ]AF$6hIKxA5P@%bX0Np ؜)`<;a=vsF"z|z _[o'$( %~??3 !"$12`a#3AQSTbqs?iy]L81v!ln|0"6~wqu ;d;WUǶNj_n#F6q۫Ub?(9Ϸ"T2ޗ3!Ĕ2cvmBl!MMh3٪mvYahڦMl 0(!( iO/P%+Z+'YUq$ H̺*\4lX,t޸xlχN=5,=/Wn^ח*?3n-`bl}n\!Y͛IB>[0$P {wz 98P2Ƀko?W::~eKKcë# ?4~PB2DбYX2a R@FHJ"FU;[r??wrk,q(5VfՇ1 @9~c8?: 1!`A0P"#$2QSTabrs?>W҆䡯g]_m] 2눧pa 'zI]$Gej=b(x431,nk7{Z99 ݙ_ef Q@"NCEnT6 tKP8hs\2vypqKj(_jaMQ%,=ǫ c^GTMwD5EP;z!#Z,uAG![J`v w\hUʹW*\`JM;X>Q  !"1A#2BQ`tu$346PRabvSdqs 05@CTc?6k_x})VvܺsH@#YgDwuqT<oVS7VǛ&n60#)3< Q柼RWc Hv2Jt>?m-C.)S6#ܫVyWX!Y1N-182Zƒ]"OHc"3dsЖ?xq:FCd|.z)Ȳ#½[2`&^X=b6Okj*J@AǬ?Z u'q˥?k|O|Aslخ3r- u ^>kkrv WPh޵tޜmyK%oϞ&8nZdQ v +XH# mvgTګټByL|SGN{ /:,&gF(&;j\QfZa^VN(蓍s&\%r}ds[kJ0==%O%6䬻U׫tkXA{}&L='>^@j1Zy$~"@i>[!BX#]UCvQ{uY<^$\p1)&'oGzl[qžRMS l[dMf3"C11:O͸ɯ ~6ӽhm)R HT!IR~ap51.^[ jPs%z@_5aq$C Ql=*ژ⟸Oix+fW>Nӈb^‹F"§'ޘYeY_n9{n>R~SJo]%Cc5ic;wYkZg圅Thc)G&U&Æ[bjԭp,r0u,"gʼ6.΃%I6ݱ}|@4/ū {GZ y䢵H :n>$ȀȔ*/EC GԈX׻%1S拘,*L$[D-,U85R)`׺ `FOo_!ʯ<}V; 3[a-/3Y>RCsY1[#s=/ۦ K]oR[53\,W.fa%<8׌Ze|vM~:5;FCsϫ}[KNTS-X-QY|VOYHڵY"1ߋxЫ 2Kee^R% kf gth#_2zV& WZzWPI#,iOs>XĪ[]VYomTk&ӔdMĀ chr?O!_SR~~_/ڶer6elRMnx89y_z YfAʟ4@Az*8 vkO9sj㳹iN 8edLS^fNE:Zמ?˱ 8r}gfG'L9Kܷ9N9K謇j[ z;!TQjm140u,v,SBd31Pz@ }jf4-,χ9RW`xV3zܶTĎ:Oc{"WղѴ!_11Rrǁ\+Du3s%=Q2c}[\r",+!wMYLNƯXbhq _)QvOC#Zո'vYܭcyK<27;Jp.mm^Sœz}s6{2ylvlۭ?qXDFjO]6L>gG3MF8AsM%u $q{޳zM^*/7ClX9dD"=k[.y{_ίڕ^=}n#;H2(Hj}v) N=+cW+}vޠmml5c]L"rWk,]LCoN" QZwr[du`iIu9Б o4fuڬD/t4ao_s8n\G'J[5 R䲻4jr\{cIx/ūbd2IN2K+7NZ[4N:ݸk1*4;p~OI~$.O=̦xWx*0/|J5u3xzY*DgV[0LQL0{c-e^F-Z;y%LV Mժ4Z|1+\jckԬ$}A4͓fSxf?/BJVuIO} cD u0t0Q=F }gt_& UDz";p^\UJ-I> "իf̈́O-i+8ɟ.RZ`򴠂Ҳ C<[dbvm+0'=EWAc%>8KV*h"ժEuA02"5"jɋ^QO:aM0GU);hS18|V(+}uS?n{tٷ]m{i11LOx\L|\\^Lb,Hu"^S={mj`vXIۑu֫џ~B*Q^ǏsӅ~3̏~2Q+r9>_GG(=.Om'_g[uІ`=i?wQ#ݴmf7,Xƣ3Ƕ8! "bdfcX;ƾ6az[@A)GqvJb#Rg`c㙈_ζ7nAua1=*OdMORO iTyRq&KYpx_JM[f‹W i6ys4LY K$9 L˸Tlj)/0$F.bm_ji# &%5CkŢ]0~=E4 As'z qz5ߍ[vԛ[D.Y[C`ڔ`}d4՝WAJ>մNRmr]q8~"ckӮ5tWY-,LDZ>bh=/~nMePE@SY jBj0dfu9 OԵxƣT S4B"Z : 7SH㿯^+xGC~S EQ_О^1 z= mڙ&X=iN'\Zm18qβݳ=\:uL 3\{xǧqcb_xug5vԹuJl!N2Q64Pƪ;nljKݴ ' g2*Y)) '[e &BC0BBQv>K[**:6+;zcB[VQ K"ԴE!1xuorb>er~Cl@'ah*&[8 S: k%=3:_?!!!!(J;LLw$0_cRKW,d+9NE!"LV 6 gUG???qSR}n,k!,U^ȉpA"Ŝ O@slԎM8dSL )~G<+'*&-H3lƺLLuoN?s8M;ɵ~T|K9nC `B&-CPALgVDD&~{6*c2ݶDNƬs$R6 bW,"Ni5 f"M7䥎U[b`9`㺕=IJbyq+'ڰRן'(?wyi_ !ZqN%{{[0b-.tƻ֬3+79%|dr284 U,PaiA ^'}/yrh{東%ΰM+Wǵ,"NLkA j2뙈sVŪjڼ 4^BfP(-H+y1N%{{Y4"-dDi.tƻ֬%+6\+!1AQ`a Pq0@?!<ޱ=ku/]TV(q''6/x#?AJ:vSJ_FNE$U}T= Ga.3^e@^{͑vL|v: 5g !^3KݎLlHeT$J@ ?yV z3 }d8cD`R?v-snuCQ2T `yt8Q? Lj'NT)9)gt0sIωY4hMs+l;_=M9aͺXl !8"tvȏ??SpePruv>VŻ,,H[zH2^wl!@OѶ!h:xUrQp'v;حEiRs泝_&h$E',sd9 HAq~/7jOIHbh`@WS )+7 _ dcRK|C3d@9TB ]B k4#ɋt6 E@zEo:#̒:rty eQ(d h0JQS?ALŘuHp8{h%eRF`tZǦ08X29~Jvܗ~fQoz;ptDCS";8‹,E+NK 8Q8< Q@#j D} qw?K)K}~:R\zRQ@FB?) O2⡈TFmz5ncR̺ߧ 3=g;SJ4w2X,Z9CL G{7/%b-b)\5cۻtw3k7}bfV0.b ,A) aJ1jZP0ᛗ8+Z|Ӗo{jROd[M%$qHCܼ6MieN͏:5XkG?\*0NOI4p|V.~n^ Kt1YoM8X90C7/`pE/nc>sú]hADCUݲl ^fQ%EN[=Mg~1Ǯ)C2V-s"R xxKV OiL'1,{Kk|Sj&|tIX[,F^_W&[PRqeB=ar@Uq",0'S&k/cP  s?1@wLz!ݧ^q;_y m5lp-I B Ȉ\4A%H 5 ΰtiT$cUu5ndW61 aLe"6ޏ=\@YVzЄIKsAD!0qLZAzxWq=]#g~!9߆ǰUHʘ*P ^E ɞ6P3ldUi g1.*Q]n&JvHI_.m=ʊ 3w eAitt&_l6&!mwg8`Px}׵+=s_'Cׯ7kͿ~khkzbLhyxK\g0 m*jN|dtw80<IƠHՆF/y]a]=4ya V W1۸!Tő8}Z8U Y0 !iD_Ng' *5Z W[2~;U(JgJ5,ugQϮcLxD0uIyMNfEhRka0/%mV N|c~ׯӘD!s&4a/FPJ~dږ'`<c q <`K6sutnRUn} /.yAh8$blρkç iD%Ub$ˢwd(~ Bw|0Edƥbj-=5P 3GR5bj Ŷؕ6l̂@0C(H-PV_mNcm~fIq'-ķ0} -wc6UZ}?Xj@ ].0;x_}O!R RGR1Is{ QXW2I,1@ AD-40&bVU?1.M/=ӅH2Ew$Uff&K5>Tnެp.Ah*5/79(7ظNL uH4( `qb8^*#+>T,$5 V*2,s\.4 'uPMT [YԃgD #7׮ "[C÷nRs1%S L x' ddȿb>s /w8M] 䡙i ʭ\fW.hx źi% KBĕ28I~jV0oSZ'΀Y7;Uk1j9TU2~&?O,kل}zwMi{ T˫&ZM/H k*iW_d8t< uQyNzM8:JGh YPu\!(G:noXZN]sWU!49v+EX0{+uc`3`n 2rIحc췟:-Ӊk.linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/macos/entitlements.xml.in000066400000000000000000000011741434616504300324250ustar00rootroot00000000000000 com.apple.security.assets.music.read-write com.apple.security.microphone com.apple.security.device.camera com.apple.security.device.audio-input com.apple.security.network.client com.apple.security.personal-information.photos-library com.apple.security.files.user-selected.read-write linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/macos/linphone.icns000066400000000000000000001121001434616504300312450ustar00rootroot00000000000000icns@ic098 jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2d#Creator: JasPer Version 1.900.1R \@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP]@@HHPHHPHHPHHPHHP ߅8r=^$2{B~nmHyao B_ᵧ('uZ/N|;"w 9FT4x+I )r!T5rfYR'U.Os}UkˋIs\ս.&KVگbz,`߅|,v qJqﲍt:q 89_PeZ2 #h1Jyk68H-nӄm S UC~}7ur%?%b1Ӝp4e{ii.RzM?06ea2Ϥ "hQ pٝ!SVr4P.Ԛs߅ /c.۽ T,z3g3Cu^CWjr(TDڄ0l$Z ¯Yȫs A)SBX^k6=7dI/*:e/:*$Rt3hg VvvYoNqZ=n331źj_*[S,)|Wk`Z]ak7Cc߁yJhmR+M'TWPB} 01` %G) zm:)dRA$끿2~ ,_Q_c=wy:yes1I.?~.b$`AѰ=F:UGcaMJ@ ( ݰW]!7>S_7aiG6G-=`Aq?=PZF_3ɺ=Y]I5hGIIdN$X a`*.7DU-{)7g0%#[vq.)>|DY ѯH$nV{.˰+yN`($wr'`% U'?Ov{+{ JЗ w;@JxW/zB,%ԈA/_3:t3+rw"Q7?HK 2N葬PburILqh?u|t)^LW^#ʚAW +hЦ?xUL5 +&:[|@,72TP %PV(בnl3?uf_M'/^cim*Ş€#?: rSTRQ3.ãY-mEѪ GLIUя'.FnaMLha .ypFCb[W欢Lx Ll}\e1 evX5+T޿OgPC br ?: K' li]^Գf96:|@j5Mn]O>ZLT\|i}ó Wv馏`ږsZ^ g lpqe}؜af<(@jtmRXY\n3& dnk; V"P5l]un[#l49n Ig5Hp(z)K]-&Gm{Dp 2cԆňHA>Xَ:I 1"/`Zc ^Pq.G5b_ڠkKJJ 8g;`2Ń5;Ϛ彃3[Γ F<`]vKqj̯;@ mAHaƗ_BF!i^n$@%==Q$5@:ɪR®UO&ݑzc^K6h=sK"G4@@xMb;5gWl~ 6d}-R&yG,$`|1톭;(}dշY[< DG;Y}8Q58*}Gj%=)&I(1o]NIxU(UntXȞ ܻ`j=5CxX`&~+ t'luqZ@a ;/OawF#9 Dg 8ڇC3%ׯIbb<CrW\P>E4ذfiT,L,<Ëc7F؇$/Vm[ "(˘n<8?!{]Ϫ;Ke7h&LN Yvփ]! 4y==vy(ܐT*giLV4q~b"_,-aFbA[ϘT8j讈=r܌)^R:whű:΀ߚI+.j?|d $QD0U~~қPS= !ꛍ:v{nos}ZT9S`TNun<'\KZ$KW%. ȵpo SRe}@cϛJ|moL)em"MUY̱,|d\zR}5~_m1exnGވg\sE`#YԃȯPh0µ{yQm<# {Bp(8VΥ:TwP4ja|B5NrCPقDjwPb^:bif UZ}Yư@&p\saueWѧnVޙnt}~:`L#WK|wZR C{Pct Vܣ簚Gz%z(h#[D&#hüLz[sK$Kee3E~Dvp'OF J UD{H$ >r@}PY:+\2)V 'FʹkD `܇T PgVKl)Y2[g̼'4bdOqڇ)W?$p9<ãzeä0޴_OHBtM1!PCt㧗V@`'0.߽#0 8J7kj_6:6aĸfmNll9朔>^X9^{k oti)~8k\_[kmIEtA=bR 9YzR*d(#:Yφ=ՠ8q{ExϪ(<,,kGpS3r8& (xP3FЉ'뎡=,Gw͛HkaA^NRkl>aUj R!qO/4>Aذdc9Gxnev{t?մ G%sXz i A੄#r\繍ю")x{Pw/d~S}#DDzSuQ9 N= r :;f*fs1bwF-6(T-w|(ԍOBJE= 7W9U{ !%KPc0|+\yuӺnޢ!UyvՂ0^鰭ȇ9Rh!'R`[$S)2{7_B>;QˏOͬu%YP1"Π>WN ]r\ [,`N %}dX:ToCV+UB;6;:G7gmgP19E(ءhj]%!leǚ30Rv7ŭCъhc$(02N*Zug[l>p5GT@RW%w?N9}6]/,/bHx@xZj00+򁾻˾w0i6t:c1' {w10ĆQ; Η3: ,4;9< { )ݏpF=$[JiJ)_6Fk3VV'pPE^:|$wҡ|d1qi/xV&mW0Knsƍ :|*zڑu946i2oHKL@߻0P 2}FVQ]hبa3rܡ^|6F9@ѿt#%h&Иǫ 4\3n^];8ץW ׵JAQj]?9z M^ Š6}dA 3-9F۠t}x෺;hy@(GIih~eq8)fYYX7ٮ8Gb(EŴL2$,yMI9r-nbԻ34a3o\@<c=QgE`}d26muW0=uƉBRE"1)AWp6A(e->yUf`BPNZ|^*Wh =R{|.sfs R!rM}XI;VD502+Iw)dQ&2P8 ue|ϥB]5Vu}VS A `!T* 0s. ^دLUf3pA+[&VuUsѧ#r e"t8KF@XfTاetw^PRK~ޕ䵲[E 4*CqUNei _0c봹7\lFRMqa;Jx!zBJa~3_ߟJ?@0帳J.<$FĜ+2O}Ae:qW<Mg! oU6u1.l,x,'x Uh͝1S3fz+0ư0|ni"~ۚ{,lO*/ EaxfR㷰E/_T.?[ n]˭[29 xdy,lt,^9O"A*vqFXkD/mlRXfAPXu:рC"*S_}C5k%,zksr[b?\2L!/han չhj-Y rAc>ON`Z =qKv+9i02l;X׹.z8>QEx@;iGr}F)|SM]5O/+X:-#p<.]zAx-AQBu9mǐwF%~o[TBܗ.=y]G*j|_->bP<|cl",.)!:Wͼ{Yꅣ^ck qSU'Sg9 Rbia}vJUсr;{d*$K̚BJecN1cD-ܱ1BHi.j$?\DNދu?Xmѯ( w/^{CHz3^akBjF֧n/lfȍv,*rRr /YR IP4^kqS6*cF'劫hʥZ6BCjImrw~C |)nCoj ˒CAnhE)s7x뼪i":L3UWlȝ;Rdt^yc=/zٍeۚᳲ UrY)g+^ɪ콬ub`Q%hnhr& -hҊ2N]JN6'qU`ӹ `")Tf_B@p~}_*r3qnUnn`W,/.eL!^"4dd}h Қ^ Jd7d pm'QQ\V!2I fPm(/0:3Zi< GVT;]hibtF%FVS O.p&/wɿEީ*XYiYƖ\XLZ.Qy$qޔm)k D Cm _)Ehu@ J_. ` T+?6D7Uq䨹br:lksp|3JM#-ivubhʨ;KGrf)-'->v!fe#4:!r[OT Ӻ 1KxyvzZOlMI l.]4>id>YW8{Чz|K%2f+nA [ۂP ;^2 q.I4H poכo>oXFy0vw.0U(Q+?gT -7 6/VM_5]9O-׊a$- Ǜ:T=cڳ@4[۰^ ;U&ǏEP&7zYa@BqF^ 8@)& in;_z97a B}5i1Pٳf|efw֏?sO̞Aj=05V˪Q ueŨ:Rɯ@Mۤ%C=Af$lP Յfr\aew]*,xMO xF5:;=r8.4!7 KdUt!jNn]9gT hd6գ8>P?~KD{De29aovO>n:n;`H@D\~apLlLs(zA*]2+m\Z*㐋>(aWPW/IR6XF5'Y CB3kė-mWk~Ofy!=AAhJE%R&("0 n)%{@? 'BnuCEHmRp]ܱضOf6MmtMҜJ6J/f:{}PzӤ&#q;4 md+:*ϜH!.-9tK7#!I@oB>p ^Ly]v9AgV) k 0bL!6(k/ TXjܠ`d.cYT h?dCm. &'0Sy\ݔl4-ךnsV#YC5VZ E`-8Ka0S=҈+K*̚_Isex&_FC$S}g|㱢*w" IiMǰ|rrKؑJUւ%{5/E[Fr5~^~Z'NHK>h<cy0E4I.lWH[4#~^cEYth[d k.b`ڻAxCе7dO~w[?'<ش e2LK8ت{7 _K(SV(*[ JC WbfR`"<DTd@B\s/_ Upgһd {xgmJlUђwdD[A1-VX'BI{Ky=)]Ty*Eidյ;bFcrLo&]m\1\\./4ŏohEK`!=y<U Bڦ<&*KMwJs&𾯷FoS1t[]14^7ۥ۫ Aݓ|~`N k)ݔX/J/>ےSv' 8vAkT:pڤ+ohu> wz)r#t:!pMgբ1&!Xi$lխiSe}aa3b5d:Eږc2?E=gLX9L좃ut~~3ލI?X%lHqSM[qץU5~f9؁h x}~(zI pQ>A4jY/6bJXl|?;nXHN"5u%K~ 8@}()?JQ̞z6vʷe PM4:b3"3K*BCgr0$5ImfChly¶-A-.b>ZIsl L$e''a';ä_=>E+аr ]gKn,FQ?۷n%{\ ń>Wd_<uq /m{Qmar >HoQB`Z.7<ɶĝkmeb5u5CySyX"Ng+&s7oo4 Fk Փ71iWw3}/E ss#ҸK>DCmIҽyD%]̼n;zg|nsp*eBL eM!'UwT]Z/sTᣛ*z,V2,--Wi! njo|0a `n慲EV]qLdN!} ZrjAxN_C~Y=INz\:mW%8.ܢHFYjk2.(:Z EYWFR`?-z9(N;BnfªhYƫl:@soQbsjWK;\C%g}Tx5ov!1;b ]%t:@/<>t)\+T^m"4ޏvhqMRb_*[]|\ell!7H[+;AZ&n?Nb%G"N5!Vyf^vA/RS[Mfg,KK+]Hzy.? ,)y * (lk1'tysems /_LLɶ38?+\^0-'!|wEq~fTp_*NQ!(GA\SsrO3nդEE X :;ajT3^&"MNy}VP!ؚJ '4}ߞ)3p~_jt7( yPՋ5ّ T\s?GX_oIV-&+Wp&]SYn#Th SB^Ff_v 8aoăQ%p ]rtѸJLusV~rЪ'Nr3jه7UPj[-6=M8 u##m/`_7&EUlF\5vuغB@1{2KJBckrQ`Rƥ݂xݤ{Bl ;wbjUȧ^h?Ё@/1"3ա^)AsvؓŸ@%7oY2Lǩq0Dd8abo"^\ 4C8R޶uy\ˆV1|vƒχU)| CөSp\C=$,N7v Cbޯj{'̤n{eFFVo1DgVa)Lmu{ty vË9/030 ܲh ¦ލ2q܄H@*eMxrJ=LXUp\/Afĭ{R9sYxzQDqNh?$NW,Sm \v&%mklͷ#x;{x}wf'/fcmq xU678W,Vmiaz\(Wj_,9&cXw#X[ס)xBQp=}yNyGJdZ 1euR»R0lt+b8}4z=>z gG!jZݡlpdy`&(8NT$ í"<0[qhQ+3M'd-wUh~KwP .ҵ(6~+V: 5sr)2+zRGB&qP#MK A\I;8 v O p;RZdr Ë'VX9'@ڐ8bf,E_w dNr?eX{1O$bCg@´z#ؤHC H*RJ"j/3Zyqe38Fە~d/}{)U.ũTplɰneIz*Ű0 [b'2dkX/xg'ZG9}uu|K.ѥyRm.Bs9zAŽ9JwA CTxv'Y#u"M$ 5SZqzJvBj"aCv~aTXc>RQ?B5vL2>\]wλ!n?gӿ᠟Yci i9jxktsޕ-(9Sf6ylvNqj*~[8q[ɽ0:v4c^xUyxWH;U\(QWqDhL6v<8CB#aUKۀ/MI {sR 3Y Un?pll?/g8 D~ h@_hM^t.+a_e!jzA\EU/NX呪xi.N@aM+S ]XnTVbo)a }Ijjk]gj@2yq0ޑxJߚe1f;vg>AI9TL&r&qץYxO<)]gӊƴ)7O%P\1j>G]nl®{1p)7߂K] UKM׍*ߞ%sCw6ϛɰeC@U4ϖ 7xό^Rk@ܑn/ Cr6ln_?#>vޏtHMS|K6sk=SxlU+&Sc^^77]oshT5a2HgiEZ;/tl- ZMߋ6?b+ޥӳ,qOtMvk 0PTɋII!U݅d!9ȲQ"Jtt?դ)3#ڱ~9njp{kuh4~h.23z:%e ă:YEWp~ORSyX**ZNr׮ƽLd)5w>zumM=..J` gpQ>iΤF)D2vU Wt$r?A0;TN4O c3_L_T⟸7TsWrMRV63k:#4l65_][-6au*;$}Uvo_t IѬslbũM?VUؽ0ah'm 'xu6ɪ2/Z?(T3kB8E  S.6cA^m4foΫ{$V ۓ7G+|&>PLP VȀ@%?ouc=+9P5ẗCџݳy[Uwr;v;tJg)rngپu%S,3\p)o!kpls=([38̞##9ӾVh`F?dW6Wچyd_˗C͟ 'î-j: p}PA@ R"IMNJ_$uOYsmU)A1{3n`Hr"**wµp>=nޗ>j\9(D"}Ok o\0LdMxa~[!2Ί?ٗcHFԣI㧬̅1irɨKx;E̋ G;x1OAʣYe)kpH>B~Ljq99NHY hlT?7AZ#\הoBͅ }teJF6&FqL8>e-Jn][ca Tm)C}(N'FeIsl=ѹܗ_z8ҹy",^u? a@rHP8z>:5]]R`%7QaKDnEznq&x31O?9@hkeAfŤ* B.@9 mg1wFAVu$c)1`Su _Q95xHP'<#UʽiVXd8 -Ok):GF66k`7`ۗizWp $^6f}[t,G:l ROeIP`PڏAr!D3iZ#\lyv"Aq}ȕ^RB{8),{\nA ­ 9B0a^`_g1Y}@1lGK*j|8]\.bZ5V~UWo6*k/긚r'YW펳HisF%SMkBJw'kSbw;-c I BCU}׈O̜hLo9(V1ZSkL p3V ,@K']dW!,8@ 3P N$D HikXTQBtzk3}@P J-LUASաvhb:5gÎE4RyסzL6aֽ>$4F.ӧL9G8&OA/-1dnU} :5H S!i9YQ:d8 gٟ /4̵tfu\PK b _ :+D8ЛEXŒT5Z_=w/2K{#+xizpTz^g|$e8ۜQU$BcEMraOl\Ӹ$l SJFxm]n w &x!ޓ>UUĝq݄,G45'{'" 춲!;IwBn/ˍUrU@+#>SLɤ_3߂~ r9dBgqZ@f?'`Zm±vt_^qØ!ɂC.Nfrמ1srVƁPI%Dh'$)yċ)E{;Cȅ1qҕpIkJQL˜;7.J2Nqͥʉlz%=/CmБ "I8,ŅV&Lreǭ82ݝ ;U_Irbp>f. In<|94!^lJj ^}G2M e5 `}Pc2sHA6gku_:f_p%_(hn|A-6^"FUek˿-d>b~t5Hxk^uicLңJ?Y+JeBVw!t[.;]h6tv" aeRכ tYNbPԷ5v "*W%<+]K/m<Ĉ3/ƴ}N \c4;MDb3Y% :5k|j7 ,N' Z8L`G^a ?N~Qk7<4UVZR3w=T\D7>U'-Ѹ._%pp @&bʛdM)} K@z.<6"Iw\%,$Ӊ4=l~aV u4ԟ(V4Kep L /gȨ:!ejY0$U0N"諔Z">z8=28IIJQݓRj#,}.rnL̻< r%kKItL؂,E)w.bw[s2~ȳ̶ ϛUzR%aW`m85||`*p|M>Kk@o_%kC|6RȿAjK@n% 8$D>JT>F0*\>K>J/IᭃA܅k($`FlO{ @/.O;Htih޸j Q>N~먐l'<0xMfórF`UX\(kN07c,!gL/\RJ:-#I%jʂCvll&+cN>;?$%h.}FvF[e)'-tjmS\Nd&b,@R >ډ%ul6%laJ=.v;O fZfLLkZOj KW) <@|[g.;^g7R9T귒wwp&y_B,fʼn8I4TC؆/0v2) r;ɇ?N#GuǀwM b^bUUHce]ۏ, OWjXV؃ȃXf5v]Rh; pdb =VP]i7 kGrK4`[{˛| fy6qEy+漺 0$}!7B ,$ea/Cj8ysze}? `NIVrSDll;eF""?8q%QVo)[P5pS.={~oeLrԿ_*/RfB8u0F!\mrxidS;)[ce]T[tE5mQҦ]ԅWI9:buM;\06pQI>B.3tb8/eE ~^Ϟ /WƯp1 gQW'6)_\IXD*p |iÎf-ֶD=.eOO!z[֧ VX=IKtwR6Z2΂=1怩9pj:SW2]pkHnb 9](b/o]Qqe6{rhM@ qx?V멆{s3 uZgQel  7_hE07/`ⷮq]qJMɄ`(S;~BN;G`@@FPs]J"RVZՒr|S J3y;ZF_QR/?-äJ=>6,g-> Wst3̀H3%3JCdAM R.e16R"e=ZsW^䇵-(~ rs)ELVf]H!C$K e"V̦Ђ  qSgNTz2>m >%~1W(&Xz%3xB'J)ıST>ґ7'Pcj+XyQ s$l{PyH2=QD1, =D4D:5p؟7L QHpV|2J}AfW\)J?0jFJz*(t!Dq-NQ+};Vӳx,XgߔMrOJnl\H$PdrIkIx>@6@Q1W 7dU.c-MP)Ii#dnsbVĽ8;E&.tnA{GQv2M^`De3x-!<$ǪjsFO,C<Fց'dr6];Ɇ 3#x yhUi8[p8ij<>s (:` "N`^r{Qӛ=.D--Dˮ@l*W]=Ah9o>uq ~D&f~d(mIw 94&D> $^Mء_tpN3WFn5b%t<9H[ݬ`( 6>~gGndHb]?'V+iltGiMz(= (0Udi0c<ߨq,ˌ V{6jO%'vN8gGZn 2z$AZhhJ)R/_!$=<~5Ȯ9En4iz2^GigQHݎ柊P$圮=Vڐ::X7>?ɊbTѥNcօ-nLHtI!jZ2<gѪv˯b7WWuf&ROG—B=\6D/-&'4OrùZ O5 U1ltFL2ys,e{7h5pLc2e$<_\NP_G&Fʑ5pWv3( wą?J)0 S36 ;}iz/'QS@M;\]:aMGEާS3/r%(r/ Դ{:c*U-ՂMD3 QP?3A{4"Ab §]rcM5uW/QnآRR DM1tD}?oԥ å,oeAŽTe~tIFyE92YFm.p%lClI>}]{ D@ن^3٢$q\7(dB$=}[z(EՃ۪1<wI§Vf!^Zrَ۲a}ԕMxƁB8OVM%~T+HHd% _}Wy} GưAC ~UƀSMkF|l9-o\iz zc=)O{o0WjWsX4̖,hW]'w ޽>8FS71\˭F}5дlqhȌAO%+Pbt' eu4N<,k1=e?{PTO %/QbT:=qnݛ\U9j٧` /7A(w3ߧ3UH|#ucm%G'&.iD_:IO&J5 啱qq.y<\Y-#\$-kS}_r&#W nn*tbwܞ=e$ \Y0U.S$/*tu/UzE%sNzK~:6z?tMIҌījJXeR~$\WK!UMruQ GV&5z/NTibb!q8X\?ґ(4UZh`N' zjzz51#s lDP-+XI !@ Ρm ,fI$֐3("@V9q4Dyn~g_@YEM(Ո^ (GҫhL}2`J/S"F|wơ*QHZPBH$}]7t^ر0d@sU4%Jm,ęj.ECU$}[û1H!hu)Kwǎj,I2SXl LGaE ;1b9 ot^6b%I?wA{8#9Xz?%:ӿ5j;lߵܻO}Oߗ6ߵb._n}_SQֿN~^/}OjG?ҟw~OssoRPY $9\|;Zi~:ٰ,WI+ ƴCPI3hGBH&7Ⱦ ƈyg \CzdۯXrX.fY^ogM=18fX{ sΤRl,eZi!yCD5WQZ;% OIvx@ㆮ_CbPv| ,-ѳmEaM,Xb<]{˥_7Z*szw1(WKd>}yV E = )^. R#mm3rl/ʝndc=B!jGM<޺qkEO.u k1/E9n|jnMR%3SW産NjNd '@|[geH ^z= u`!h}B?<*ICǏhm"֨ "ꯦ-ߦM+͛dt CeGnITEMo=.02I[A 8^SID5Ӂ3#:k܆2Ɩ*K auD$e ֆ:Cvw] q?!`f3UL6oQJ h+ɹŢqJy'}μs\K6c-yG.J5!sWl r7 ず@_E͍^;`O!+G7*4៲(3Jp8'MpD=.q`cg|RPjMY өeg-$ˌdҍ N?L(*H rB)4BiplPU|ylcܬ{zȐ|)?jN(9Vp`16aϻtkEY<91[,4oM}:9Vű`&LCa(_E$!LP4׷aK3Ѻ@$( J[ Sj47Zԇ,x @Oe:$O] ^WԒ  ɀ MS÷Vfw,&Xl<9CZ F Gqo)LB:ʔdU3r xL vrࠊ$ s-2cyQ+o?3<9-|#ا~U=ed'k;2k7roՃBS#LodF©Ĕ2cSeWoz\]H>պ:Rn{@Zq(.G+܆BjT4y ~F q&\[a"ҝ̺HK [ 1&BgKLw?AVoNdS]mmtoЮ 8htz2(3*Ȉ_rIkF2ŘeRJiC.k&zG]?baGM؇pz\ױ_}M5Ux<fs(oe׀-5o"XWk$KzH d#ӠQq04P2Y\,qW`RWJp e'JeT5m'^ɇ ]ֻx-@dNum>ـ䂞h'e0X% ܽI?mw'\b ܾz= R(\: Xe. %w+Bh[[r+J˄XPEߗ8R%t?RǛQ DŽL^n'uc4Ui "kpDQcipG%% Fp81ش$O/ ˙VmMzfW;5ڱtCk*) wh lLo2-ZLlŲFF1{B!kw>,2.@r¯n :R̩GiO1FmJӫ.m`pc۷)>S w~ĔKT#橕5H1b4ImI㫐uW1+a L Ԋ-ϣ |dT'I^;K32'vTƞ6i7$IG`ٚq] tuizqF,'Cql4 |^ҋ*}\Co?Ry=}KCoޏ18b i68wK=I%q1.M&!JeMpbLEH V毰{Z6ʙ/ic.J%vQ46& +̊x"]QL{ؓۦ<r jP LEv$9k [HE~h_.ğ Kڑ*NyU@nI%>!$iEQ-crNmI ޠ䌄r{UOl+O] kRZ$][s[T@BBz*LcdB|@wW$oIU*ӝDj] lG0Suke +e_WrFTn;KFy:eepA*eމs9"'7P~7"$.&/`رH} Y..+b܂5xnDIs;]uH.dE}6o 7%MnwkA~(?G֯@k IrϮpi@II¸P`zəw/ *LCSɹS9 ;q% əcO{a@ /F2=k,AӉ svC(v( vyi-ovRfet\|hFy,"'GG&Ht3F}B$":8"9ϕ5ڗ{)j$Oֆz]48A GߒE&"4ᏓOgd. `L).G/6 H,JRJR:MNc`o!Q])i - g^kJu v҆kN-`ૄcfֵe\qL%[Ciϲ6C8#^Vć 3*JgP~vyP[ɌBzF4,~ilw@cY~MtOޝNhyD#i|qޚfzaN]W2sQ'vB?LH+&BlG&N]@1?3Gb m':iޠDٝi!%A:Z# {љTd|_mx="fo)^) &Dt O19ڦZ:oM^o ebӹ_.Z5-'=pTL-,Og ^knq*t rf}KYbq;,f w:'hkD766Rfmp՜nja mNiqcyvx[saCxoF 6~Pۻ=Pȭ; S?#n _ZtUwߩ7XL~Dj,eSw_y--r+GUYP<_& w[O((vӍIuY$-hm 3,{TXfs\'iZC>5WR<]ټh£B5Dٻ[R-gPNP?Q?83cH72WPo I@}9jˉΐ3 %\'V)a7E~~_{Qk?k}ۨ~ڏ~֟BڵN6ۂ^?jW'0[%k9y[{pUiۯW Sew^ۥDG43rerR ǔ#%{F%`9CVREjJB_E y,U)gLʘrZnYj(M⥇yb͖s7NZiNNxɛ&!x7^ֿJ)MqG Α/\Chq [6>w&Ӂ׊3g;Ѝ_;f7,D' &,,и]S`v|XnRA 1N<;w3pՂb%[kA"@_݂] ^T͠9|ːˋ؍z`,9E&Kqy5w > eHW5 H1s.W"E!`䡿42 h~P;7H0Asɗ;C߭C,0nu [9( 4[%Μ&6BjenW n _˧qT>Ud{%7U!ztq|aJŇ 7b#},jK-Ʀ\¹}t\o֫t Pu&;XZ[>TKvont鸻xs1p(!CBթ)+"ۊ(i EzOƚ7䱋MsOOKмS rdMP('О6ʚkw ٵqPFKЉ8U45X4zߎ`D4S+]{ЊQs fC7dc'Wa ;}đ,jTb CN*mV>3{ϓƯrXHa20`@ .TynFMc4fF Ftn G^ OHRrE<7sbE_Y39e.[r)%E?!Ȥy: N˟:Y/Qt[&J>vړ ? *d_ ެ\Gʰ'@~s]I)l~,c%5J[<Ax#$^w a˳WY`gV;x>ڨb(:{ +(RM^-Ko}`\8`-$fRx:t?i)H pIZ8~R$"He6~@BW5b\t5ʰ cv疬ɡpS~uP?W|h&@̨1ܚ" is>rlh\%gjiۯ"uRHa./W8ʣ=@H(!T}3t;y`nӼT?x  LmRr$S\+*O 2RՉ)uϝ{1}> iaڄ7y/qeaUD5.V;-Q꩎ 0PůIDWa'fG)|0cQOGR\ l[9t F[XQ^61G仍Br%߫y#<<>rȗE$|i by>nkDZױbY/e|QS7բ]݋#]i{-a[G2VqaDb,ce ՔȑCh؅mAn`!ɔ@օN9O~ .fSW}oǟfCn"5E9Xlb.C9Ҡgq([v'x2)rf0%F3#V%?Cx=tPxI zu9]MqI'XޝY)\F뵋?Vb%D8n@wŎ8͘ _7ȦØl!^៧%,S!ϩpְ/"=-_zQN Lʡ"A2҅b.!BZvطs( +H.2AeYl&o 7KFq\τSez5 ZESQu@p_l"%~ ^" >!YnnD<O11\4P#^鲃f1sS c.Viys#1.Ko-B"37S2vK-vJIT+Է<:C/B0%+2,ϻzf[-5,`&(NQEƧW5)}Nd7uq [/<3$ͦ$jӷԇ*q*u"ȩ1нn2SGauekZ#L.*%,cz?lq)QUxɕ{0E ]*)/~n*н謹JhM\%8pbTȲp2j|Ё,U̞yBpW)L&\~yM՜~?fr0Ɲudyq0Ikp,}ʲ;Ӧ{v>JH?i"׼XfPۀ: fOE47W7bɔs?&LfKR?#N~Ks~|llg&nlIQ ?זn8e OB*nz;b8'.Ι2brC}iTpxƊ_ĉnU=Wpj˄T7?u˛s<œ\BśpG2Xu='gn<:G?.S[ ~^մ諫zlb$ G=΋De>q'(/X=0GnPܪջ>uA,f$z U]E9Lh=gfoJ=0 o6CQѐR~{P,S { F\ܒ%=/TgP' ▃@NC\H  n~uO@rQ}e0>X DmkBK+U_ڬjӘ+Wֿ341{)#}YR^g`ăniG\>}O9[ %j6òvYM u+ 38’q{zk5s!X:g5#0E ~G"DX,54XԿ`~ӂ9̥Ü+"VH3i&MK(a's_^g` ӧJyI GZ_V2֜w"g =yS% g0~Oj a9Nv ROީJdo%%%?P5Es!tRs A# 1ZmTĦ؊tb0W ̟f$&L/`H锾kWb .!+gHIst.UX: `\@qtY}"n҅@ޅy3!۳8so1R'2;|7|?=u ֠0[D<܈ Vh}fȊW['Os(ra!6l2xP6񏋆Rn<0M=#˔g)D;0.MMW{煍4U NJ`.2'c*WBMcC"վǣ 6IGE@:x$h'+OJ#Z)I=XZji H QDƓHyZ)r 휕+g!ϑjiG*&; Yg__/Yc_Lz *~Hq g(6 C܏@ j%Q% 88:$,.$ezg;)QEKʻ}?.޲3 XcoQlinphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/macos/linphone_dmg.scpt.in000077500000000000000000000024401434616504300325260ustar00rootroot00000000000000on run argv set image_name to item 1 of argv tell application "Finder" tell disk image_name -- open the image the first time and save a DS_Store with just -- background and icon setup open set current view of container window to icon view set theViewOptions to the icon view options of container window set background picture of theViewOptions to file ".background:background.jpg" set arrangement of theViewOptions to not arranged set icon size of theViewOptions to 100 delay 1 close -- next setup the position of the app and Applications symlink -- plus hide all the window decoration open update without registering applications tell container window set sidebar width to 0 set statusbar visible to false set toolbar visible to false set the bounds to { 300, 100, 1000, 520 } set position of item "@APPLICATION_NAME@.app" to { 200, 280 } set position of item "Applications" to { 500, 280 } set position of item "include" to { 200, 1000 } end tell update without registering applications delay 1 close -- one last open and close so you can see everything looks correct open delay 5 close end tell delay 1 end tell end run linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/packaging.cmake.in000066400000000000000000000103001434616504300310030ustar00rootroot00000000000000############################################################################ # packaging.cmake # Copyright (C) 2017-2018 Belledonne Communications, Grenoble France # ############################################################################ # # 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 2 # 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, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # ############################################################################ set(DO_TGZ @CPACK_BINARY_TGZ@) set(DO_TZ @CPACK_BINARY_TZ@) set(DO_STGZ @CPACK_BINARY_STGZ@) set(DO_DRAGNDROP @CPACK_BINARY_DRAGNDROP@) set(DO_GENERATOR @DO_GENERATOR@) set(DO_APPIMAGE @DO_APPIMAGE@) if (NOT "${CMAKE_INSTALL_PREFIX}" MATCHES .*/_CPack_Packages/.*) if(DO_TGZ) execute_process( COMMAND ${CMAKE_CPACK_COMMAND} -G TGZ RESULT_VARIABLE CPACK_COMMAND_RESULT) if(CPACK_COMMAND_RESULT) message(FATAL_ERROR "Failed to create TGZ package!") endif() endif() if(DO_TZ) execute_process( COMMAND ${CMAKE_CPACK_COMMAND} -G TZ RESULT_VARIABLE CPACK_COMMAND_RESULT) if(CPACK_COMMAND_RESULT) message(FATAL_ERROR "Failed to create TZ package!") endif() endif() if(DO_STGZ) execute_process( COMMAND ${CMAKE_CPACK_COMMAND} -G STGZ RESULT_VARIABLE CPACK_COMMAND_RESULT) if(CPACK_COMMAND_RESULT) message(FATAL_ERROR "Failed to create STGZ package!") endif() endif() if(DO_DRAGNDROP) execute_process( COMMAND ${CMAKE_CPACK_COMMAND} -G DragNDrop RESULT_VARIABLE CPACK_COMMAND_RESULT) if(CPACK_COMMAND_RESULT) message(FATAL_ERROR "Failed to create DragAndDrop package!") endif() endif() if(DO_GENERATOR) execute_process( COMMAND ${CMAKE_CPACK_COMMAND} -G @CPACK_GENERATOR@ RESULT_VARIABLE CPACK_COMMAND_RESULT) if(CPACK_COMMAND_RESULT) message(FATAL_ERROR "Failed to create @CPACK_GENERATOR@ package!") endif() endif() if(DO_APPIMAGE) set(ENV QML_SOURCES_PATHS="@LINPHONE_QML_DIR@") set(ENV QML_MODULES_PATHS="@LINPHONE_QML_DIR@") execute_process( COMMAND mkdir -p "WORK/Packages/AppImageDir/" WORKING_DIRECTORY "@CMAKE_INSTALL_PREFIX@/..") execute_process( COMMAND cp -rf "@LINPHONE_QML_DIR@" "WORK/Packages/AppImageDir/" WORKING_DIRECTORY "@CMAKE_INSTALL_PREFIX@/..") execute_process( COMMAND "@CMAKE_CURRENT_SOURCE_DIR@/../../tools/create_appimage.sh" @EXECUTABLE_NAME@ @CPACK_PACKAGE_FILE_NAME@ @QT_PATH@ @LINPHONE_BUILDER_SIGNING_IDENTITY@ RESULT_VARIABLE CPACK_COMMAND_RESULT WORKING_DIRECTORY "@CMAKE_INSTALL_PREFIX@/.." ) if(CPACK_COMMAND_RESULT) message(FATAL_ERROR "Failed to create AppImage package with this command : '@CMAKE_CURRENT_SOURCE_DIR@/../../tools/create_appimage.sh @EXECUTABLE_NAME@ @QT_PATH@ @APP_PROJECT_VERSION@' at @CMAKE_INSTALL_PREFIX@/..\nMaybe the .appimage already exists and is running. Please remove the file before packaging if it is the case.") endif() endif() if (@PERFORM_SIGNING@) if(@PASSPHRASE_FILE@) execute_process( COMMAND "@CMAKE_CURRENT_SOURCE_DIR@/../../tools/sign_package.bat" "@PASSPHRASE_FILE@" "@SIGNTOOL_COMMAND@" "@PFX_FILE@" "@TIMESTAMP_URL@" @CPACK_PACKAGE_FILE_NAME@.@PACKAGE_EXT@ RESULT_VARIABLE SIGNING_RESULT WORKING_DIRECTORY "@CPACK_PACKAGE_DIRECTORY@" ) else() execute_process( COMMAND "@CMAKE_CURRENT_SOURCE_DIR@/../../tools/sign_package.bat" "@SIGNTOOL_COMMAND@" "@TIMESTAMP_URL@" @CPACK_PACKAGE_FILE_NAME@.@PACKAGE_EXT@ RESULT_VARIABLE SIGNING_RESULT WORKING_DIRECTORY "@CPACK_PACKAGE_DIRECTORY@" ) endif() if(SIGNING_RESULT) message(FATAL_ERROR "Failed to sign the package! ${SIGNING_RESULT} ${RESULT_VARIABLE}") endif() endif () endif () linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/000077500000000000000000000000001434616504300277715ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/antlr3c.list.in000066400000000000000000000000461434616504300326410ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@antlr3c*.@LIBEXT@ linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/bctoolbox.list.in000066400000000000000000000002031434616504300332610ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@bctoolbox*.@LIBEXT@ include/bctoolbox/* lib/bctoolbox*.lib lib/pkgconfig/bctoolbox*.pc share/bctoolbox/cmake/* linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/bcunit.list.in000066400000000000000000000000441434616504300325550ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@bcunit*.@LIBEXT@linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/bellesip.list.in000066400000000000000000000001771434616504300330770ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@bellesip*.@LIBEXT@ include/belle-sip/* lib/bellesip*.lib lib/pkgconfig/belle-sip.pc share/BelleSIP/cmake/* linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/bv16.list.in000066400000000000000000000000421434616504300320450ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@bv16*.@LIBEXT@linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/bzrtp.list.in000066400000000000000000000000661434616504300324360ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@bzrtp*.@LIBEXT@ share/bzrtp/cmake linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/ffmpeg.list.in000066400000000000000000000002321434616504300325340ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@avcodec*.@LIBEXT@ @LIBDIR@/@LIBPREFIX@avutil*.@LIBEXT@ @LIBDIR@/@LIBPREFIX@swscale*.@LIBEXT@ @LIBDIR@/@LIBPREFIX@swresample*.@LIBEXT@ linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/gsm.list.in000066400000000000000000000000411434616504300320540ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@gsm*.@LIBEXT@linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/linphone.list.in000066400000000000000000000002201434616504300331010ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@linphone*.@LIBEXT@ include/linphone/* lib/linphone*.lib share/doc/linphone-* share/Linphone/cmake/* share/sounds/linphone/* linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/linphonecxx.list.in000066400000000000000000000001551434616504300336330ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@linphone*.@LIBEXT@ include/linphone++/* share/doc/linphoneCxx-* share/LinphoneCxx/cmake/*linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/mbedtls.list.in000066400000000000000000000001661434616504300327300ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@mbedcrypto*.@LIBEXT@ @LIBDIR@/@LIBPREFIX@mbedx509*.@LIBEXT@ @LIBDIR@/@LIBPREFIX@mbedtls*.@LIBEXT@ linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/ms2.list.in000066400000000000000000000004101434616504300317670ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@mediastreamer_base*.@LIBEXT@ @LIBDIR@/@LIBPREFIX@mediastreamer_voip*.@LIBEXT@ include/mediastreamer2/* lib/mediastreamer_base*.lib lib/mediastreamer_voip*.lib share/doc/mediastreamer2-* share/images/nowebcamCIF.jpg share/Mediastreamer2/cmake/* linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/ms2plugins.list.in000066400000000000000000000000501434616504300333710ustar00rootroot00000000000000lib/mediastreamer/plugins/*.@PLUGINEXT@ linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/opus.list.in000066400000000000000000000000421434616504300322550ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@opus*.@LIBEXT@linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/ortp.list.in000066400000000000000000000001721434616504300322570ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@ortp*.@LIBEXT@ include/ortp/* lib/ortp*.lib lib/pkgconfig/ortp.pc share/doc/oRTP-* share/oRTP/cmake/* linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/polarssl.list.in000066400000000000000000000000471434616504300331330ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@polarssl*.@LIBEXT@ linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/speex.list.in000066400000000000000000000000431434616504300324140ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@speex*.@LIBEXT@linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/sqlite3.list.in000066400000000000000000000000461434616504300326570ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@sqlite3*.@LIBEXT@ linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/srtp.list.in000066400000000000000000000000421434616504300322570ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@srtp*.@LIBEXT@linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/vcard.list.in000066400000000000000000000002151434616504300323700ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@belr*.@LIBEXT@ @LIBDIR@/@LIBPREFIX@belcard*.@LIBEXT@ lib/belr*.lib lib/belcard*.lib share/Belr/cmake share/Belcard/cmake linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/xml2.list.in000066400000000000000000000000421434616504300321510ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@xml2*.@LIBEXT@linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/sdk_content/zlib.list.in000066400000000000000000000000421434616504300322270ustar00rootroot00000000000000@LIBDIR@/@LIBPREFIX@zlib*.@LIBEXT@linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/windows/000077500000000000000000000000001434616504300271505ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/windows/NSIS.template.in000066400000000000000000000703351434616504300320760ustar00rootroot00000000000000; CPack install script designed for a nmake build ;-------------------------------- ; You must define these values !define VERSION "@CPACK_PACKAGE_VERSION@" !define PATCH "@CPACK_PACKAGE_VERSION_PATCH@" !define INST_DIR "@CPACK_TEMPORARY_DIRECTORY@" ;-------------------------------- ;Variables Var MUI_TEMP Var STARTMENU_FOLDER Var SV_ALLUSERS Var START_MENU Var DO_NOT_ADD_TO_PATH Var ADD_TO_PATH_ALL_USERS Var ADD_TO_PATH_CURRENT_USER Var INSTALL_DESKTOP Var IS_DEFAULT_INSTALLDIR ;-------------------------------- ;Include Modern UI !include "MUI.nsh" ;Default installation folder InstallDir "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" ;-------------------------------- ;General ;Name and file Name "@CPACK_NSIS_PACKAGE_NAME@" OutFile "@CPACK_TOPLEVEL_DIRECTORY@/@CPACK_OUTPUT_FILE_NAME@" ;Set compression SetCompressor @CPACK_NSIS_COMPRESSOR@ ;Require administrator access RequestExecutionLevel admin @CPACK_NSIS_DEFINES@ !include Sections.nsh ;--- Component support macros: --- ; The code for the add/remove functionality is from: ; http://nsis.sourceforge.net/Add/Remove_Functionality ; It has been modified slightly and extended to provide ; inter-component dependencies. Var AR_SecFlags Var AR_RegFlags @CPACK_NSIS_SECTION_SELECTED_VARS@ ; Loads the "selected" flag for the section named SecName into the ; variable VarName. !macro LoadSectionSelectedIntoVar SecName VarName SectionGetFlags ${${SecName}} $${VarName} IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits !macroend ; Loads the value of a variable... can we get around this? !macro LoadVar VarName IntOp $R0 0 + $${VarName} !macroend ; Sets the value of a variable !macro StoreVar VarName IntValue IntOp $${VarName} 0 + ${IntValue} !macroend !macro InitSection SecName ; This macro reads component installed flag from the registry and ;changes checked state of the section on the components page. ;Input: section index constant name specified in Section command. ClearErrors ;Reading component status from registry ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" "Installed" IfErrors "default_${SecName}" ;Status will stay default if registry value not found ;(component was never installed) IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit ; Note whether this component was installed before !insertmacro StoreVar ${SecName}_was_installed $AR_RegFlags IntOp $R0 $AR_RegFlags & $AR_RegFlags ;Writing modified flags SectionSetFlags ${${SecName}} $AR_SecFlags "default_${SecName}:" !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected !macroend !macro FinishSection SecName ; This macro reads section flag set by user and removes the section ;if it is not selected. ;Then it writes component installed flag to registry ;Input: section index constant name specified in Section command. SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags ;Checking lowest bit: IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED} IntCmp $AR_SecFlags 1 "leave_${SecName}" ;Section is not selected: ;Calling Section uninstall macro and writing zero installed flag !insertmacro "Remove_${${SecName}}" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" \ "Installed" 0 Goto "exit_${SecName}" "leave_${SecName}:" ;Section is selected: WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@\Components\${SecName}" \ "Installed" 1 "exit_${SecName}:" !macroend !macro RemoveSection_CPack SecName ; This macro is used to call section's Remove_... macro ;from the uninstaller. ;Input: section index constant name specified in Section command. !insertmacro "Remove_${${SecName}}" !macroend ; Determine whether the selection of SecName changed !macro MaybeSelectionChanged SecName !insertmacro LoadVar ${SecName}_selected SectionGetFlags ${${SecName}} $R1 IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits ; See if the status has changed: IntCmp $R0 $R1 "${SecName}_unchanged" !insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected" !insertmacro "Deselect_required_by_${SecName}" goto "${SecName}_unchanged" "${SecName}_was_selected:" !insertmacro "Select_${SecName}_depends" "${SecName}_unchanged:" !macroend ;--- End of Add/Remove macros --- ;-------------------------------- ;Interface Settings !define MUI_HEADERIMAGE !define MUI_ABORTWARNING ;-------------------------------- ; path functions !verbose 3 !include "WinMessages.NSH" !verbose 4 ;---------------------------------------- ; based upon a script of "Written by KiCHiK 2003-01-18 05:57:02" ;---------------------------------------- !verbose 3 !include "WinMessages.NSH" !verbose 4 ;==================================================== ; get_NT_environment ; Returns: the selected environment ; Output : head of the stack ;==================================================== !macro select_NT_profile UN Function ${UN}select_NT_profile StrCmp $ADD_TO_PATH_ALL_USERS "1" 0 environment_single DetailPrint "Selected environment for all users" Push "all" Return environment_single: DetailPrint "Selected environment for current user only." Push "current" Return FunctionEnd !macroend !insertmacro select_NT_profile "" !insertmacro select_NT_profile "un." ;---------------------------------------------------- !define NT_current_env 'HKCU "Environment"' !define NT_all_env 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !ifndef WriteEnvStr_RegKey !ifdef ALL_USERS !define WriteEnvStr_RegKey \ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !else !define WriteEnvStr_RegKey 'HKCU "Environment"' !endif !endif ; AddToPath - Adds the given dir to the search path. ; Input - head of the stack ; Note - Win9x systems requires reboot Function AddToPath Exch $0 Push $1 Push $2 Push $3 # don't add if the path doesn't exist IfFileExists "$0\*.*" "" AddToPath_done ReadEnvStr $1 PATH ; if the path is too long for a NSIS variable NSIS will return a 0 ; length string. If we find that, then warn and skip any path ; modification as it will trash the existing path. StrLen $2 $1 IntCmp $2 0 CheckPathLength_ShowPathWarning CheckPathLength_Done CheckPathLength_Done CheckPathLength_ShowPathWarning: Messagebox MB_OK|MB_ICONEXCLAMATION "Warning! PATH too long installer unable to modify PATH!" Goto AddToPath_done CheckPathLength_Done: Push "$1;" Push "$0;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$0\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done GetFullPathName /SHORT $3 $0 Push "$1;" Push "$3;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Push "$1;" Push "$3\;" Call StrStr Pop $2 StrCmp $2 "" "" AddToPath_done Call IsNT Pop $1 StrCmp $1 1 AddToPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" a FileSeek $1 -1 END FileReadByte $1 $2 IntCmp $2 26 0 +2 +2 # DOS EOF FileSeek $1 -1 END # write over EOF FileWrite $1 "$\r$\nSET PATH=%PATH%;$3$\r$\n" FileClose $1 SetRebootFlag true Goto AddToPath_done AddToPath_NT: StrCmp $ADD_TO_PATH_ALL_USERS "1" ReadAllKey ReadRegStr $1 ${NT_current_env} "PATH" Goto DoTrim ReadAllKey: ReadRegStr $1 ${NT_all_env} "PATH" DoTrim: StrCmp $1 "" AddToPath_NTdoIt Push $1 Call Trim Pop $1 StrCpy $0 "$1;$0" AddToPath_NTdoIt: StrCmp $ADD_TO_PATH_ALL_USERS "1" WriteAllKey WriteRegExpandStr ${NT_current_env} "PATH" $0 Goto DoSend WriteAllKey: WriteRegExpandStr ${NT_all_env} "PATH" $0 DoSend: SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 AddToPath_done: Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ; RemoveFromPath - Remove a given dir from the path ; Input: head of the stack Function un.RemoveFromPath Exch $0 Push $1 Push $2 Push $3 Push $4 Push $5 Push $6 IntFmt $6 "%c" 26 # DOS EOF Call un.IsNT Pop $1 StrCmp $1 1 unRemoveFromPath_NT ; Not on NT StrCpy $1 $WINDIR 2 FileOpen $1 "$1\autoexec.bat" r GetTempFileName $4 FileOpen $2 $4 w GetFullPathName /SHORT $0 $0 StrCpy $0 "SET PATH=%PATH%;$0" Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoop: FileRead $1 $3 StrCpy $5 $3 1 -1 # read last char StrCmp $5 $6 0 +2 # if DOS EOF StrCpy $3 $3 -1 # remove DOS EOF so we can compare StrCmp $3 "$0$\r$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0$\n" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "$0" unRemoveFromPath_dosLoopRemoveLine StrCmp $3 "" unRemoveFromPath_dosLoopEnd FileWrite $2 $3 Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopRemoveLine: SetRebootFlag true Goto unRemoveFromPath_dosLoop unRemoveFromPath_dosLoopEnd: FileClose $2 FileClose $1 StrCpy $1 $WINDIR 2 Delete "$1\autoexec.bat" CopyFiles /SILENT $4 "$1\autoexec.bat" Delete $4 Goto unRemoveFromPath_done unRemoveFromPath_NT: StrCmp $ADD_TO_PATH_ALL_USERS "1" unReadAllKey ReadRegStr $1 ${NT_current_env} "PATH" Goto unDoTrim unReadAllKey: ReadRegStr $1 ${NT_all_env} "PATH" unDoTrim: StrCpy $5 $1 1 -1 # copy last char StrCmp $5 ";" +2 # if last char != ; StrCpy $1 "$1;" # append ; Push $1 Push "$0;" Call un.StrStr ; Find `$0;` in $1 Pop $2 ; pos of our dir StrCmp $2 "" unRemoveFromPath_done ; else, it is in path # $0 - path to add # $1 - path var StrLen $3 "$0;" StrLen $4 $2 StrCpy $5 $1 -$4 # $5 is now the part before the path to remove StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove StrCpy $3 $5$6 StrCpy $5 $3 1 -1 # copy last char StrCmp $5 ";" 0 +2 # if last char == ; StrCpy $3 $3 -1 # remove last char StrCmp $ADD_TO_PATH_ALL_USERS "1" unWriteAllKey WriteRegExpandStr ${NT_current_env} "PATH" $3 Goto unDoSend unWriteAllKey: WriteRegExpandStr ${NT_all_env} "PATH" $3 unDoSend: SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 unRemoveFromPath_done: Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 FunctionEnd ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Uninstall sutff ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ########################################### # Utility Functions # ########################################### ;==================================================== ; IsNT - Returns 1 if the current system is NT, 0 ; otherwise. ; Output: head of the stack ;==================================================== ; IsNT ; no input ; output, top of the stack = 1 if NT or 0 if not ; ; Usage: ; Call IsNT ; Pop $R0 ; ($R0 at this point is 1 or 0) !macro IsNT un Function ${un}IsNT Push $0 ReadRegStr $0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" CurrentVersion StrCmp $0 "" 0 IsNT_yes ; we are not NT. Pop $0 Push 0 Return IsNT_yes: ; NT!!! Pop $0 Push 1 FunctionEnd !macroend !insertmacro IsNT "" !insertmacro IsNT "un." ; StrStr ; input, top of stack = string to search for ; top of stack-1 = string to search in ; output, top of stack (replaces with the portion of the string remaining) ; modifies no other variables. ; ; Usage: ; Push "this is a long ass string" ; Push "ass" ; Call StrStr ; Pop $R0 ; ($R0 at this point is "ass string") !macro StrStr un Function ${un}StrStr Exch $R1 ; st=haystack,old$R1, $R1=needle Exch ; st=old$R1,haystack Exch $R2 ; st=old$R1,old$R2, $R2=haystack Push $R3 Push $R4 Push $R5 StrLen $R3 $R1 StrCpy $R4 0 ; $R1=needle ; $R2=haystack ; $R3=len(needle) ; $R4=cnt ; $R5=tmp loop: StrCpy $R5 $R2 $R3 $R4 StrCmp $R5 $R1 done StrCmp $R5 "" done IntOp $R4 $R4 + 1 Goto loop done: StrCpy $R1 $R2 "" $R4 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Exch $R1 FunctionEnd !macroend !insertmacro StrStr "" !insertmacro StrStr "un." Function Trim ; Added by Pelaca Exch $R1 Push $R2 Loop: StrCpy $R2 "$R1" 1 -1 StrCmp "$R2" " " RTrim StrCmp "$R2" "$\n" RTrim StrCmp "$R2" "$\r" RTrim StrCmp "$R2" ";" RTrim GoTo Done RTrim: StrCpy $R1 "$R1" -1 Goto Loop Done: Pop $R2 Exch $R1 FunctionEnd Function ConditionalAddToRegisty Pop $0 Pop $1 StrCmp "$0" "" ConditionalAddToRegisty_EmptyString WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" \ "$1" "$0" ;MessageBox MB_OK "Set Registry: '$1' to '$0'" DetailPrint "Set install registry entry: '$1' to '$0'" ConditionalAddToRegisty_EmptyString: FunctionEnd ;-------------------------------- !ifdef CPACK_USES_DOWNLOAD Function DownloadFile IfFileExists $INSTDIR\* +2 CreateDirectory $INSTDIR Pop $0 ; Skip if already downloaded IfFileExists $INSTDIR\$0 0 +2 Return StrCpy $1 "@CPACK_DOWNLOAD_SITE@" try_again: NSISdl::download "$1/$0" "$INSTDIR\$0" Pop $1 StrCmp $1 "success" success StrCmp $1 "Cancelled" cancel MessageBox MB_OK "Download failed: $1" cancel: Return success: FunctionEnd !endif ;-------------------------------- ; Installation types @CPACK_NSIS_INSTALLATION_TYPES@ ;-------------------------------- ; Component sections @CPACK_NSIS_COMPONENT_SECTIONS@ ;-------------------------------- ; Define some macro setting for the gui @CPACK_NSIS_INSTALLER_MUI_ICON_CODE@ @CPACK_NSIS_INSTALLER_ICON_CODE@ @CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@ @CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE@ ;-------------------------------- ;Pages !insertmacro MUI_PAGE_WELCOME !if @CPACK_RESOURCE_FILE_LICENSE_PROVIDED@ !insertmacro MUI_PAGE_LICENSE "@CPACK_RESOURCE_FILE_LICENSE@" !endif Page custom InstallOptionsPage !insertmacro MUI_PAGE_DIRECTORY ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER @CPACK_NSIS_PAGE_COMPONENTS@ !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ;-------------------------------- ;Languages !insertmacro MUI_LANGUAGE "English" ;first language is the default language !insertmacro MUI_LANGUAGE "Albanian" !insertmacro MUI_LANGUAGE "Arabic" !insertmacro MUI_LANGUAGE "Basque" !insertmacro MUI_LANGUAGE "Belarusian" !insertmacro MUI_LANGUAGE "Bosnian" !insertmacro MUI_LANGUAGE "Breton" !insertmacro MUI_LANGUAGE "Bulgarian" !insertmacro MUI_LANGUAGE "Croatian" !insertmacro MUI_LANGUAGE "Czech" !insertmacro MUI_LANGUAGE "Danish" !insertmacro MUI_LANGUAGE "Dutch" !insertmacro MUI_LANGUAGE "Estonian" !insertmacro MUI_LANGUAGE "Farsi" !insertmacro MUI_LANGUAGE "Finnish" !insertmacro MUI_LANGUAGE "French" !insertmacro MUI_LANGUAGE "German" !insertmacro MUI_LANGUAGE "Greek" !insertmacro MUI_LANGUAGE "Hebrew" !insertmacro MUI_LANGUAGE "Hungarian" !insertmacro MUI_LANGUAGE "Icelandic" !insertmacro MUI_LANGUAGE "Indonesian" !insertmacro MUI_LANGUAGE "Irish" !insertmacro MUI_LANGUAGE "Italian" !insertmacro MUI_LANGUAGE "Japanese" !insertmacro MUI_LANGUAGE "Korean" !insertmacro MUI_LANGUAGE "Kurdish" !insertmacro MUI_LANGUAGE "Latvian" !insertmacro MUI_LANGUAGE "Lithuanian" !insertmacro MUI_LANGUAGE "Luxembourgish" !insertmacro MUI_LANGUAGE "Macedonian" !insertmacro MUI_LANGUAGE "Malay" !insertmacro MUI_LANGUAGE "Mongolian" !insertmacro MUI_LANGUAGE "Norwegian" !insertmacro MUI_LANGUAGE "Polish" !insertmacro MUI_LANGUAGE "Portuguese" !insertmacro MUI_LANGUAGE "PortugueseBR" !insertmacro MUI_LANGUAGE "Romanian" !insertmacro MUI_LANGUAGE "Russian" !insertmacro MUI_LANGUAGE "Serbian" !insertmacro MUI_LANGUAGE "SerbianLatin" !insertmacro MUI_LANGUAGE "SimpChinese" !insertmacro MUI_LANGUAGE "Slovak" !insertmacro MUI_LANGUAGE "Slovenian" !insertmacro MUI_LANGUAGE "Spanish" !insertmacro MUI_LANGUAGE "Swedish" !insertmacro MUI_LANGUAGE "Thai" !insertmacro MUI_LANGUAGE "TradChinese" !insertmacro MUI_LANGUAGE "Turkish" !insertmacro MUI_LANGUAGE "Ukrainian" !insertmacro MUI_LANGUAGE "Welsh" ;-------------------------------- ;Reserve Files ;These files should be inserted before other files in the data block ;Keep these lines before any File command ;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA) ReserveFile "NSIS.InstallOptions.ini" !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS ;-------------------------------- ;Installer Sections Section "-Core installation" ;Use the entire tree produced by the INSTALL target. Keep the ;list of directories here in sync with the RMDir commands below. SetOutPath "$INSTDIR" @CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS@ @CPACK_NSIS_FULL_INSTALL@ ;Store installation folder WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR ;Create uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" Push "DisplayName" Push "@CPACK_NSIS_DISPLAY_NAME@" Call ConditionalAddToRegisty Push "DisplayVersion" Push "@CPACK_PACKAGE_VERSION@" Call ConditionalAddToRegisty Push "Publisher" Push "@CPACK_PACKAGE_VENDOR@" Call ConditionalAddToRegisty Push "UninstallString" Push "$INSTDIR\Uninstall.exe" Call ConditionalAddToRegisty Push "NoRepair" Push "1" Call ConditionalAddToRegisty !ifdef CPACK_NSIS_ADD_REMOVE ;Create add/remove functionality Push "ModifyPath" Push "$INSTDIR\AddRemove.exe" Call ConditionalAddToRegisty !else Push "NoModify" Push "1" Call ConditionalAddToRegisty !endif ; Optional registration Push "DisplayIcon" Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@" Call ConditionalAddToRegisty Push "HelpLink" Push "@CPACK_NSIS_HELP_LINK@" Call ConditionalAddToRegisty Push "URLInfoAbout" Push "@CPACK_NSIS_URL_INFO_ABOUT@" Call ConditionalAddToRegisty Push "Contact" Push "@CPACK_NSIS_CONTACT@" Call ConditionalAddToRegisty !insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_DESKTOP "NSIS.InstallOptions.ini" "Field 5" "State" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" @CPACK_NSIS_CREATE_ICONS@ @CPACK_NSIS_CREATE_ICONS_EXTRA@ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe" ;Read a value from an InstallOptions INI file !insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State" !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_ALL_USERS "NSIS.InstallOptions.ini" "Field 3" "State" !insertmacro MUI_INSTALLOPTIONS_READ $ADD_TO_PATH_CURRENT_USER "NSIS.InstallOptions.ini" "Field 4" "State" ; Write special uninstall registry entries Push "StartMenu" Push "$STARTMENU_FOLDER" Call ConditionalAddToRegisty Push "DoNotAddToPath" Push "$DO_NOT_ADD_TO_PATH" Call ConditionalAddToRegisty Push "AddToPathAllUsers" Push "$ADD_TO_PATH_ALL_USERS" Call ConditionalAddToRegisty Push "AddToPathCurrentUser" Push "$ADD_TO_PATH_CURRENT_USER" Call ConditionalAddToRegisty Push "InstallToDesktop" Push "$INSTALL_DESKTOP" Call ConditionalAddToRegisty !insertmacro MUI_STARTMENU_WRITE_END @CPACK_NSIS_EXTRA_INSTALL_COMMANDS@ SectionEnd Section "-Add to path" Push $INSTDIR\bin StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 doNotAddToPath StrCmp $DO_NOT_ADD_TO_PATH "1" doNotAddToPath 0 Call AddToPath doNotAddToPath: SectionEnd ;-------------------------------- ; Create custom pages Function InstallOptionsPage !insertmacro MUI_HEADER_TEXT "Install Options" "Choose options for installing @CPACK_NSIS_PACKAGE_NAME@" !insertmacro MUI_INSTALLOPTIONS_DISPLAY "NSIS.InstallOptions.ini" FunctionEnd ;-------------------------------- ; determine admin versus local install Function un.onInit ClearErrors UserInfo::GetName IfErrors noLM Pop $0 UserInfo::GetAccountType Pop $1 StrCmp $1 "Admin" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Admin group' Goto done StrCmp $1 "Power" 0 +3 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Power Users group' Goto done noLM: ;Get installation folder from registry if available done: FunctionEnd ;--- Add/Remove callback functions: --- !macro SectionList MacroName ;This macro used to perform operation on multiple sections. ;List all of your components in following manner here. @CPACK_NSIS_COMPONENT_SECTION_LIST@ !macroend Section -FinishComponents ;Removes unselected components and writes component status to registry !insertmacro SectionList "FinishSection" !ifdef CPACK_NSIS_ADD_REMOVE ; Get the name of the installer executable System::Call 'kernel32::GetModuleFileNameA(i 0, t .R0, i 1024) i r1' StrCpy $R3 $R0 ; Strip off the last 13 characters, to see if we have AddRemove.exe StrLen $R1 $R0 IntOp $R1 $R0 - 13 StrCpy $R2 $R0 13 $R1 StrCmp $R2 "AddRemove.exe" addremove_installed ; We're not running AddRemove.exe, so install it CopyFiles $R3 $INSTDIR\AddRemove.exe addremove_installed: !endif SectionEnd ;--- End of Add/Remove callback functions --- ;-------------------------------- ; Component dependencies Function .onSelChange !insertmacro SectionList MaybeSelectionChanged FunctionEnd ;-------------------------------- ;Uninstaller Section Section "Uninstall" ReadRegStr $START_MENU SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "StartMenu" ;MessageBox MB_OK "Start menu is in: $START_MENU" ReadRegStr $DO_NOT_ADD_TO_PATH SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "DoNotAddToPath" ReadRegStr $ADD_TO_PATH_ALL_USERS SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathAllUsers" ReadRegStr $ADD_TO_PATH_CURRENT_USER SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "AddToPathCurrentUser" ;MessageBox MB_OK "Add to path: $DO_NOT_ADD_TO_PATH all users: $ADD_TO_PATH_ALL_USERS" ReadRegStr $INSTALL_DESKTOP SHCTX \ "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "InstallToDesktop" ;MessageBox MB_OK "Install to desktop: $INSTALL_DESKTOP " @CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS@ ;Remove files we installed. ;Keep the list of directories here in sync with the File commands above. @CPACK_NSIS_DELETE_FILES@ @CPACK_NSIS_DELETE_DIRECTORIES@ !ifdef CPACK_NSIS_ADD_REMOVE ;Remove the add/remove program Delete "$INSTDIR\AddRemove.exe" !endif ;Remove the uninstaller itself. Delete "$INSTDIR\Uninstall.exe" DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ;Remove the installation directory if it is empty. RMDir "$INSTDIR" ; Remove the registry entries. DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" ; Removes all optional components !insertmacro SectionList "RemoveSection_CPack" !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" @CPACK_NSIS_DELETE_ICONS@ @CPACK_NSIS_DELETE_ICONS_EXTRA@ ;Delete empty start menu parent diretories StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" startMenuDeleteLoop: ClearErrors RMDir $MUI_TEMP GetFullPathName $MUI_TEMP "$MUI_TEMP\.." IfErrors startMenuDeleteLoopDone StrCmp "$MUI_TEMP" "$SMPROGRAMS" startMenuDeleteLoopDone startMenuDeleteLoop startMenuDeleteLoopDone: ; If the user changed the shortcut, then untinstall may not work. This should ; try to fix it. StrCpy $MUI_TEMP "$START_MENU" Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" @CPACK_NSIS_DELETE_ICONS_EXTRA@ ;Delete empty start menu parent diretories StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" secondStartMenuDeleteLoop: ClearErrors RMDir $MUI_TEMP GetFullPathName $MUI_TEMP "$MUI_TEMP\.." IfErrors secondStartMenuDeleteLoopDone StrCmp "$MUI_TEMP" "$SMPROGRAMS" secondStartMenuDeleteLoopDone secondStartMenuDeleteLoop secondStartMenuDeleteLoopDone: DeleteRegKey /ifempty SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" Push $INSTDIR\bin StrCmp $DO_NOT_ADD_TO_PATH_ "1" doNotRemoveFromPath 0 Call un.RemoveFromPath doNotRemoveFromPath: SectionEnd ;-------------------------------- ; determine admin versus local install ; Is install for "AllUsers" or "JustMe"? ; Default to "JustMe" - set to "AllUsers" if admin or on Win9x ; This function is used for the very first "custom page" of the installer. ; This custom page does not show up visibly, but it executes prior to the ; first visible page and sets up $INSTDIR properly... ; Choose different default installation folder based on SV_ALLUSERS... ; "Program Files" for AllUsers, "My Documents" for JustMe... Function .onInit ClearErrors ExecWait '"cmd" /C ""%SystemRoot%\System32\tasklist" /NH /FI "IMAGENAME eq @CPACK_NSIS_MUI_FINISHPAGE_RUN@" | "%SystemRoot%\System32\find" /I /C "@CPACK_NSIS_MUI_FINISHPAGE_RUN@""' $0 IntCmp $0 1 notRunning MessageBox MB_OK|MB_ICONEXCLAMATION "@CPACK_NSIS_MUI_FINISHPAGE_RUN@ is running. Please close it first from the application menu and restart the installation." /SD IDOK Abort notRunning: ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "UninstallString" StrCmp $0 "" inst MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION \ "@CPACK_PACKAGE_NAME@ is already installed. $\n$\nDo you want to continue and uninstall the old version before installing the new one?" \ IDOK uninst Abort ;Run the uninstaller uninst: ClearErrors StrLen $2 "\Uninstall.exe" StrCpy $3 $0 -$2 # remove "\Uninstall.exe" from UninstallString to get path ExecWait '$0 _?=$3' ;Do not copy the uninstaller to a temp file IfErrors uninst_failed inst uninst_failed: MessageBox MB_OK|MB_ICONSTOP "Uninstall failed." Abort inst: ; Reads components status for registry !insertmacro SectionList "InitSection" ; check to see if /D has been used to change ; the install directory by comparing it to the ; install directory that is expected to be the ; default StrCpy $IS_DEFAULT_INSTALLDIR 0 StrCmp "$INSTDIR" "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" 0 +2 StrCpy $IS_DEFAULT_INSTALLDIR 1 StrCpy $SV_ALLUSERS "JustMe" ; if default install dir then change the default ; if it is installed for JustMe StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@" ClearErrors UserInfo::GetName IfErrors noLM Pop $0 UserInfo::GetAccountType Pop $1 StrCmp $1 "Admin" 0 +4 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Admin group' StrCpy $SV_ALLUSERS "AllUsers" Goto done StrCmp $1 "Power" 0 +4 SetShellVarContext all ;MessageBox MB_OK 'User "$0" is in the Power Users group' StrCpy $SV_ALLUSERS "AllUsers" Goto done noLM: StrCpy $SV_ALLUSERS "AllUsers" ;Get installation folder from registry if available done: StrCmp $SV_ALLUSERS "AllUsers" 0 +3 StrCmp "$IS_DEFAULT_INSTALLDIR" "1" 0 +2 StrCpy $INSTDIR "@CPACK_NSIS_INSTALL_ROOT@\@CPACK_PACKAGE_INSTALL_DIRECTORY@" StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage !insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini" noOptionsPage: FunctionEnd linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/windows/appDetailsWindows.rc.in000066400000000000000000000024121434616504300335430ustar00rootroot00000000000000A ICON MOVEABLE PURE LOADONCALL DISCARDABLE "assets/icon.ico" # if defined(UNDER_CE) # include # else # include # endif VS_VERSION_INFO VERSIONINFO PRODUCTVERSION ${version_major},${version_minor},${version_patch},0 FILEVERSION ${version_major},${version_minor},${version_patch},0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904B0" BEGIN VALUE "CompanyName", "${APPLICATION_VENDOR}\0" VALUE "FileDescription", "${APPLICATION_NAME} - ${APPLICATION_DESCRIPTION}\0" VALUE "FileVersion", "${FULL_VERSION}" VALUE "ProductVersion", "${version_major}.${version_minor}.${version_patch}\0" VALUE "LegalCopyright", "Copyright (C) ${COPYRIGHT_RANGE_DATE} ${APPLICATION_VENDOR} ${APPLICATION_URL}\0" VALUE "OriginalFilename", "${EXECUTABLE_NAME}.exe\0" VALUE "ProductName", "${APPLICATION_NAME}\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END /* End of Version info */linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/windows/install.nsi.in000066400000000000000000000122311434616504300317350ustar00rootroot00000000000000 # Application details WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@" "" "" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@" "" "" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities" "ApplicationDescription" "@APPLICATION_DESCRIPTION@" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities" "ApplicationName" "@APPLICATION_NAME@" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities" "FriendlyAppName" "@APPLICATION_NAME@" WriteRegStr HKLM "SOFTWARE\RegisteredApplications" "@APPLICATION_NAME@" "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities" WriteRegStr HKLM "SOFTWARE\IM Providers\@APPLICATION_NAME@" "ProcessName" "@EXECUTABLE_NAME@.exe" WriteRegStr HKLM "SOFTWARE\IM Providers\@APPLICATION_NAME@" "FriendlyName" "@APPLICATION_NAME@" # PROTOCOL declaration WriteRegStr HKCR "sip" "" "URL:sip Protocol" WriteRegStr HKCR "sip" "URL Protocol" "" WriteRegStr HKCR "sip-@EXECUTABLE_NAME@" "" "URL:sip-@EXECUTABLE_NAME@ Protocol" WriteRegStr HKCR "sip-@EXECUTABLE_NAME@" "URL Protocol" "" WriteRegStr HKCR "@EXECUTABLE_NAME@-config" "" "URL:@EXECUTABLE_NAME@-config Protocol" WriteRegStr HKCR "@EXECUTABLE_NAME@-config" "URL Protocol" "" WriteRegStr HKCR "sips" "" "URL:sips Protocol" WriteRegStr HKCR "sips" "URL Protocol" "" WriteRegStr HKCR "sips-@EXECUTABLE_NAME@" "" "URL:sips-@EXECUTABLE_NAME@ Protocol" WriteRegStr HKCR "sips-@EXECUTABLE_NAME@" "URL Protocol" "" WriteRegStr HKCR "tel" "" "URL:tel Protocol" WriteRegStr HKCR "tel" "URL Protocol" "" WriteRegStr HKCR "callto" "" "URL:callto Protocol" WriteRegStr HKCR "callto" "URL Protocol" "" # Application protocol handlers ## SIP WriteRegStr HKCR "@APPLICATION_NAME@.sip" "" "@APPLICATION_NAME@ sip Protocol" WriteRegStr HKCR "@APPLICATION_NAME@.sip\Shell" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.sip\Shell\Open" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.sip\Shell\Open\Command" "" "$INSTDIR\bin\@EXECUTABLE_NAME@.exe $\"%1$\"" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities\URLAssociations" "sip" "@APPLICATION_NAME@.sip" ## SIP-@EXECUTABLE_NAME@ WriteRegStr HKCR "@APPLICATION_NAME@.sip-@EXECUTABLE_NAME@" "" "@APPLICATION_NAME@ sip-@EXECUTABLE_NAME@ Protocol" WriteRegStr HKCR "@APPLICATION_NAME@.sip-@EXECUTABLE_NAME@\Shell" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.sip-@EXECUTABLE_NAME@\Shell\Open" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.sip-@EXECUTABLE_NAME@\Shell\Open\Command" "" "$INSTDIR\bin\@EXECUTABLE_NAME@.exe $\"%1$\"" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities\URLAssociations" "sip-@EXECUTABLE_NAME@" "@APPLICATION_NAME@.sip-@EXECUTABLE_NAME@" ## SIPS WriteRegStr HKCR "@APPLICATION_NAME@.sips" "" "@APPLICATION_NAME@ sips Protocol" WriteRegStr HKCR "@APPLICATION_NAME@.sips\Shell" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.sips\Shell\Open" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.sips\Shell\Open\Command" "" "$INSTDIR\bin\@EXECUTABLE_NAME@.exe $\"%1$\"" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities\URLAssociations" "sips" "@APPLICATION_NAME@.sips" ## SIPS-@EXECUTABLE_NAME@ WriteRegStr HKCR "@APPLICATION_NAME@.sips-@EXECUTABLE_NAME@" "" "@APPLICATION_NAME@ sips-@EXECUTABLE_NAME@ Protocol" WriteRegStr HKCR "@APPLICATION_NAME@.sips-@EXECUTABLE_NAME@\Shell" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.sips-@EXECUTABLE_NAME@\Shell\Open" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.sips-@EXECUTABLE_NAME@\Shell\Open\Command" "" "$INSTDIR\bin\@EXECUTABLE_NAME@.exe $\"%1$\"" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities\URLAssociations" "sips-@EXECUTABLE_NAME@" "@APPLICATION_NAME@.sips-@EXECUTABLE_NAME@" ## @EXECUTABLE_NAME@-CONFIG WriteRegStr HKCR "@APPLICATION_NAME@.@EXECUTABLE_NAME@-config" "" "@APPLICATION_NAME@ @EXECUTABLE_NAME@-config Protocol" WriteRegStr HKCR "@APPLICATION_NAME@.@EXECUTABLE_NAME@-config\Shell" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.@EXECUTABLE_NAME@-config\Shell\Open" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.@EXECUTABLE_NAME@-config\Shell\Open\Command" "" "$INSTDIR\bin\@EXECUTABLE_NAME@.exe $\"%1$\"" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities\URLAssociations" "@EXECUTABLE_NAME@-config" "@APPLICATION_NAME@.@EXECUTABLE_NAME@-config" ## TEL WriteRegStr HKCR "@APPLICATION_NAME@.tel" "" "@APPLICATION_NAME@ tel Protocol" WriteRegStr HKCR "@APPLICATION_NAME@.tel\Shell" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.tel\Shell\Open" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.tel\Shell\Open\Command" "" "$INSTDIR\bin\@EXECUTABLE_NAME@.exe $\"%1$\"" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities\URLAssociations" "tel" "@APPLICATION_NAME@.tel" ## CALLTO WriteRegStr HKCR "@APPLICATION_NAME@.callto" "" "@APPLICATION_NAME@ callto Protocol" WriteRegStr HKCR "@APPLICATION_NAME@.callto\Shell" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.callto\Shell\Open" "" "" WriteRegStr HKCR "@APPLICATION_NAME@.callto\Shell\Open\Command" "" "$INSTDIR\bin\@EXECUTABLE_NAME@.exe $\"%1$\"" WriteRegStr HKLM "SOFTWARE\@APPLICATION_VENDOR@\@APPLICATION_NAME@\Capabilities\URLAssociations" "callto" "@APPLICATION_NAME@.callto" linphone-desktop-5.0.2/linphone-app/cmake_builder/linphone_package/windows/nsis_banner.bmp000066400000000000000000021460661434616504300321700ustar00rootroot00000000000000BM66(ꆆ膆膆膆Ɔ݆̆ᆆᆆᆆᆆ§龾ͱཽʭťַͱϲ龾вᆆַ˯𾾾ᠠ묬цᆆҖﯯ‰چ쮮說نᆆڋꑑȈцᆆӋՆꖖʉꑑن㒒ᆆЊꉉцᆆȆdž獍نމᆆڈɆ̆цᆆֆ󊊊Іنꊊᆆ銊݆ن᫫םʗцᆆ̆尰뻻†༼Ԗ񋋋̘ӻםنǎᆆ֥‹ʆІ‡цᆆȆ㏏ꆆć醆҆Ѝنdž򛛛ᆆӆَƆ莎Ά􇇇ֈцᆆۆꆆ߆񆆆ڈĆꏏنᆆ퓓نᆆ̆цᆆÆ܆񊊊ІɆↆↆنцdžᆆ톆ↆ솆Ćцᆆ̆ↆdž円膆򆆆نᆆ̆ƈ솆цᆆ醆ʆ͆䆆톆نᆆنՆцᆆІІֆن膆Ćᆆ膆醆цᆆ†솆󆆆Ć膆نԆچᆆцᆆܕІↆ׆݆نƆ醆ᆆنцᆆ槧؆߆񆆆نᆆ膆†نцᆆަنކنᆆ膆†نцᆆӜᆆن҆نᆆ膆†نцᆆДᆆنʆنᆆ膆†نцᆆ쟟ᆆن̆ņنᆆ膆نцᆆކކ醆†نᆆ膆݆цᆆꋋ؆ᆆƆنᆆ膆䆆цᆆІ膆†نĆ膆ᆆن솆솆цᆆ󆆆򆆆ۆ†솆نφ׆ᆆ҆چц݆Ɇ͆†چنↆ†ᆆdž߆Æ䆆Æц؆††Æنᆆ҆ᆆцɆچۆ߆Ɇ†ߊنᆆچۆІц߆Ն׆ꆆ†цنĆᆆ񆆆ԆцچنԆ†ن鈈ᆆ܆񉉉͆̆ццц匌؆ĆԆdž̆̆ن򖖖ᆆᆆ싋ʈц陙݆͊̈؆ʆ솆نےᆆߒ쇇䚚ццÆ㇇ՆԆنφᆆ튊𓓓цȆކنᆆՆڇцΆĆ醆ĆڇنۏᆆЇޏ̍цɍޗώĆ熆ކޏن厎ڔᆆ㘘ŋ컻ś૫촴Ɯ˫ЫƫښժǜÆ嫫񯯯УꫫѥƟ򆆆ↆ͆ӆ䆆솆Ȇ߆ֆ鏏ņㆆΆό뭭켼ּІ򙙙򆆆醆ڜ݆ǎFGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<٤yzzRTT;==:<<:<<:<<:<<:<<:<<:<<:<<:<<;==MNNdff۱qrrTVV?AA:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<=??FGGLNN^__oqqFGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<IKK:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>oqq:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<ꞟRTT:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<\^^\^^:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>Z[[:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@acc:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<;==:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>EGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@Z\\xyyȳmnnNPP:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<<>>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<@BB:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>wxxꡢQSS:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@EGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>BDD:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>qrr:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<;==xyy:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<@BB:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>Z[[:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<_aa;==:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<><>>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<=??:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>Wfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@[]]:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<@BB磌]^^:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>yzzhii:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<[]]˻z{{IKK:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<[]] ifffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffk:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<\^^:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>.}fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff-}:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@<>>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<><>>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>FGG:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>PQQ:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@{||:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<<>>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<|}}:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@xyy:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<<>>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<~fffffffffffffff gjBfffffffffffffffffffffffffffffffffffffffffffffffffffffL:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@fffffffffffffBBfffffffffffffffffffffffffffffffffffffffffffffL:<<:<<:<<:<<:<<:<<:<<<>>홚JLL:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>hjj:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<@BB{||^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<;==rtt:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``㵶:<<:<<:<<:<<:<<:<<鬬rssACC:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@:<<:<<:<<:<<Ө_aaACC:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<>]^^:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``ɡaccMNN<>>:<<:<<:<<:<<:<<:<<<>>IKK[]]rss:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``CEE:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<>:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<^``MOO:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<@@VXX:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:<<:< #include #include // Overload this class to make a plugin for the address book importer #ifdef ENABLE_APP_EXPORT_PLUGIN #define LINPHONEAPP_DLL_API Q_DECL_EXPORT #else #define LINPHONEAPP_DLL_API Q_DECL_IMPORT #endif class QPluginLoader; class PluginDataAPI; class LinphonePlugin { // These macro are an example to use in the custom plugin //Q_OBJECT //Q_PLUGIN_METADATA(IID LinphonePlugin_iid FILE "PluginExample.json")// You have to set the Capabilities for your plugin //Q_INTERFACES(LinphonePlugin) //----------------------------------------------------------- public: virtual ~LinphonePlugin() {} // Specific to DataAPI. See their section virtual QString getGUIDescriptionToJson() const = 0;// Describe the GUI to be used for the plugin. Json are in Utf8 virtual PluginDataAPI * createInstance(void* core, QPluginLoader * pluginLoader) = 0;// Create an instance of the plugin in LinphoneAppPluginType. }; #define LinphonePlugin_iid "linphoneApp.LinphonePlugin/1.0" Q_DECLARE_INTERFACE(LinphonePlugin, LinphonePlugin_iid) #endif // LINPHONE_APP_PLUGIN_H linphone-desktop-5.0.2/linphone-app/include/LinphoneApp/PluginDataAPI.hpp000066400000000000000000000043501434616504300263360ustar00rootroot00000000000000#ifndef LINPHONE_APP_PLUGIN_DATA_H #define LINPHONE_APP_PLUGIN_DATA_H #include #ifdef ENABLE_APP_EXPORT_PLUGIN #include "LinphonePlugin.hpp" #else #include #endif class QPluginLoader; class LinphonePlugin; // This class regroup Data interface for importing contacts class LINPHONEAPP_DLL_API PluginDataAPI : public QObject { Q_OBJECT public: typedef enum{ALL=-1, NOTHING=0, CONTACTS=1, LAST} PluginCapability;// LAST must not be used. It is for internal process only. PluginDataAPI(LinphonePlugin * plugin, void * linphoneCore, QPluginLoader * pluginLoader); virtual ~PluginDataAPI(); virtual bool isValid(const bool &pRequestData=true, QString * pError= nullptr) = 0; // Test if the passed data is valid. Used for saving. virtual void setInputFields(const PluginCapability& capability = ALL, const QVariantMap &inputFields = QVariantMap());// Set all inputs for the selected capability virtual QMap getInputFields(const PluginCapability& capability);// Get all inputs virtual QMap getInputFieldsToSave(const PluginCapability& capability = ALL);// Get all inputs to save in config file. // Configuration management void setSectionConfiguration(const QString& section); virtual void loadConfiguration(const PluginCapability& capability = ALL); virtual void saveConfiguration(const PluginCapability& capability = ALL); virtual void cleanAllConfigurations();// Remove all saved configuration QPluginLoader * getPluginLoader();// Used to retrieve the loader that created this instance, in order to unload it virtual void run(const PluginCapability& actionType)=0; signals: void dataReceived(const PluginCapability& actionType, QVector > data); //------------------------------------ void inputFieldsChanged(const PluginCapability&, const QVariantMap &inputFields); // Input fields have been changed void message(const QtMsgType& type, const QString &message); // Send a message to GUI protected: QMap mInputFields; void * mLinphoneCore; LinphonePlugin * mPlugin; QPluginLoader * mPluginLoader; private: QString mSectionConfigurationName; }; #endif // LINPHONE_APP_PLUGIN_DATA_H linphone-desktop-5.0.2/linphone-app/include/LinphoneApp/PluginExample.json000066400000000000000000000003331434616504300267050ustar00rootroot00000000000000{ "Name" : "ExamplePlugin", "Version" : "1.0.0", "Capabilities" : "Contacts", "Description" : "This is an example for describing your plugin. Replace all fields above to be usable by the Application." } linphone-desktop-5.0.2/linphone-app/include/LinphoneApp/PluginNetworkHelper.hpp000066400000000000000000000020021434616504300277140ustar00rootroot00000000000000#ifndef LINPHONE_APP_NETWORK_HELPER_H #define LINPHONE_APP_NETWORK_HELPER_H #include #include // This class is used to define network operation to retrieve Addresses from Network #ifdef ENABLE_APP_EXPORT_PLUGIN #include "LinphonePlugin.hpp" #else #include #endif class LINPHONEAPP_DLL_API PluginNetworkHelper : public QObject { Q_OBJECT public: PluginNetworkHelper(); virtual ~PluginNetworkHelper(); virtual QString prepareRequest()const=0; // Called when requesting an Url. void request(); QPointer mNetworkReply; QNetworkAccessManager mManager; signals: void requestFinished(const QByteArray &data); // The request is over and have data void message(const QtMsgType &type, const QString &message); private: void handleReadyData(); void handleFinished (); void handleError (QNetworkReply::NetworkError code); void handleSslErrors (const QList &sslErrors); QByteArray mBuffer; }; #endif // LINPHONE_APP_NETWORK_HELPER_H linphone-desktop-5.0.2/linphone-app/resources.qrc000066400000000000000000000772151434616504300221360ustar00rootroot00000000000000 assets/fonts/NotoSans-hinted/NotoSans-BoldItalic.ttf assets/fonts/NotoSans-hinted/NotoSans-Bold.ttf assets/fonts/NotoSans-hinted/NotoSans-Italic.ttf assets/fonts/NotoSans-hinted/NotoSans-Regular.ttf assets/fonts/NotoSans-hinted/NotoSansUI-BoldItalic.ttf assets/fonts/NotoSans-hinted/NotoSansUI-Bold.ttf assets/fonts/NotoSans-hinted/NotoSansUI-Italic.ttf assets/fonts/NotoSans-hinted/NotoSansUI-Regular.ttf assets/images/add_custom.svg assets/images/add_participant_custom.svg assets/images/admin_selected_custom.svg assets/images/attachment_custom.svg assets/images/auto_answer_custom.svg assets/images/back_custom.svg assets/images/burger_menu_custom.svg assets/images/calendar_custom.svg assets/images/calendar_participants_custom.svg assets/images/call_accept_custom.svg assets/images/call_chat_secure_custom.svg assets/images/call_chat_unsecure_custom.svg assets/images/call_custom.svg assets/images/call_history_custom.svg assets/images/call_quality_0_custom.svg assets/images/call_quality_1_custom.svg assets/images/call_quality_2_custom.svg assets/images/call_quality_3_custom.svg assets/images/call_quality_4_custom.svg assets/images/call_menu_custom.svg assets/images/call_sign_connected.svg assets/images/call_sign_ended.svg assets/images/call_sign_incoming.svg assets/images/call_sign_outgoing.svg assets/images/call_sign_paused.svg assets/images/camera_off_custom.svg assets/images/camera_on_custom.svg assets/images/camera_preview_custom.svg assets/images/cancel_custom.svg assets/images/chat_custom.svg assets/images/chat_amount.svg assets/images/chat_audio_pause_custom.svg assets/images/chat_audio_play_custom.svg assets/images/chat_audio_soundwave_custom.svg assets/images/chat_audio_preview_pause_custom.svg assets/images/chat_audio_preview_play_custom.svg assets/images/chat_audio_preview_stop_custom.svg assets/images/chat_count.svg assets/images/chat_delivered.svg assets/images/chat_error.svg assets/images/chat_menu_custom.svg assets/images/chat_is_composing_0.svg assets/images/chat_is_composing_1.svg assets/images/chat_is_composing_2.svg assets/images/chat_is_composing_3.svg assets/images/chat_micro_custom.svg assets/images/chat_read.svg assets/images/chat_room_custom.svg assets/images/close_custom.svg assets/images/collapsed_custom.svg assets/images/conference_custom.svg assets/images/conference_audio_only_custom.svg assets/images/conference_layout_grid_custom.svg assets/images/conference_layout_active_speaker_custom.svg assets/images/conference_merge_custom.svg assets/images/contact_add_custom.svg assets/images/contact_card_photo_custom.svg assets/images/contact_custom.svg assets/images/contact_delete_custom.svg assets/images/contact_edit_custom.svg assets/images/contact_view_custom.svg assets/images/current_account_status_online.svg assets/images/current_account_status_offline.svg assets/images/current_account_status_dnd.svg assets/images/current_account_status_busy.svg assets/images/dialpad_custom.svg assets/images/declined_incoming_call_custom.svg assets/images/declined_outgoing_call_custom.svg assets/images/delete_custom.svg assets/images/download_custom.svg assets/images/drop_down_custom.svg assets/images/edit_custom.svg assets/images/ended_call_custom.svg assets/images/expanded_custom.svg assets/images/file_custom.svg assets/images/file_sign.svg assets/images/file_extension_custom.svg assets/images/file_unknown_custom.svg assets/images/filter_custom.svg assets/images/filter_params_custom.svg assets/images/folder_custom.svg assets/images/fullscreen_custom.svg assets/images/generic_error.svg assets/images/group_chat_custom.svg assets/images/hangup_custom.svg assets/images/history_custom.svg assets/images/home_account_assistant.svg assets/images/home_custom.svg assets/images/home_invite_friends.svg assets/images/home_use_linphone.svg assets/images/ics_edit_custom.svg assets/images/incoming_call_custom.svg assets/images/led_green.svg assets/images/led_orange.svg assets/images/led_red.svg assets/images/led_white.svg assets/images/meetings_custom.svg assets/images/menu_copy_text_custom.svg assets/images/menu_reply_custom.svg assets/images/menu_forward_custom.svg assets/images/menu_imdn_info_custom.svg assets/images/menu_vdots_custom.svg assets/images/menu_info_custom.svg assets/images/menu_devices_custom.svg assets/images/menu_ephemeral_custom.svg assets/images/message_sign.svg assets/images/micro_off_custom.svg assets/images/micro_on_custom.svg assets/images/missed_incoming_call_custom.svg assets/images/missed_outgoing_call_custom.svg assets/images/move_to_bottom_custom.svg assets/images/new_call_custom.svg assets/images/new_chat_group_custom.svg assets/images/options_custom.svg assets/images/outgoing_call_custom.svg assets/images/panel_arrow_custom.svg assets/images/panel_hidden_normal.svg assets/images/panel_hidden_hovered.svg assets/images/panel_hidden_pressed.svg assets/images/panel_shown_normal.svg assets/images/panel_shown_hovered.svg assets/images/panel_shown_pressed.svg assets/images/participants_custom.svg assets/images/pause_custom.svg assets/images/play_custom.svg assets/images/recording_sign.svg assets/images/record_custom.svg assets/images/remove_participant_custom.svg assets/images/screen_sharing_custom.svg assets/images/screenshot_custom.svg assets/images/search_custom.svg assets/images/schedule_custom.svg assets/images/secure_level_unsafe.svg assets/images/secure_level_1.svg assets/images/secure_level_2.svg assets/images/secure_off.svg assets/images/secure_on.svg assets/images/secure_pq_zrtp.svg assets/images/send_custom.svg assets/images/settings_advanced_custom.svg assets/images/settings_audio_custom.svg assets/images/settings_call_custom.svg assets/images/settings_network_custom.svg assets/images/settings_sip_accounts_custom.svg assets/images/settings_video_custom.svg assets/images/snapshot_sign.svg assets/images/speaker_off_custom.svg assets/images/speaker_on_custom.svg assets/images/stop_fullscreen_custom.svg assets/images/timer_custom.svg assets/images/tel_keypad_voicemail_custom.svg assets/images/tooltip_arrow_bottom_custom.svg assets/images/tooltip_arrow_left_custom.svg assets/images/tooltip_arrow_right_custom.svg assets/images/tooltip_arrow_top_custom.svg assets/images/transfer_custom.svg assets/images/update_sign.svg assets/images/video_call_accept_custom.svg assets/images/video_call_custom.svg assets/images/warning.svg ui/modules/Common/Animations/BusyIndicator.qml ui/modules/Common/Constants/Constants.qml ui/modules/Common/Dialog/ConfirmDialog.qml ui/modules/Common/Dialog/DateTimeDialog.qml ui/modules/Common/Dialog/DialogDescription.qml ui/modules/Common/Dialog/DialogPlus.qml ui/modules/Common/Dialog/DialogTitle.qml ui/modules/Common/Form/ActionBar.qml ui/modules/Common/Form/ActionButton.qml ui/modules/Common/Form/ActionSwitch.qml ui/modules/Common/Form/Buttons/AbstractTextButton.qml ui/modules/Common/Form/Buttons/ExclusiveButtons.qml ui/modules/Common/Form/Buttons/FileChooserButton.qml ui/modules/Common/Form/Buttons/SmallButton.qml ui/modules/Common/Form/Buttons/TextButtonA.qml ui/modules/Common/Form/Buttons/TextButtonB.qml ui/modules/Common/Form/Buttons/TextButtonC.qml ui/modules/Common/Form/CheckBoxText.qml ui/modules/Common/Form/ComboBox.js ui/modules/Common/Form/ComboBox.qml ui/modules/Common/Form/CommonItemDelegate.qml ui/modules/Common/Form/DroppableTextArea.qml ui/modules/Common/Form/Fields/HexField.qml ui/modules/Common/Form/Fields/NumericField.qml ui/modules/Common/Form/Fields/PasswordField.qml ui/modules/Common/Form/Fields/PortField.qml ui/modules/Common/Form/Fields/ScrollableListViewField.qml ui/modules/Common/Form/Fields/TextAreaField.qml ui/modules/Common/Form/Fields/TextField.qml ui/modules/Common/Form/ListForm.js ui/modules/Common/Form/ListForm.qml ui/modules/Common/Form/ListItemSelector.js ui/modules/Common/Form/ListItemSelector.qml ui/modules/Common/Form/Mosaic.qml ui/modules/Common/Form/MouseArea.qml ui/modules/Common/Form/Placements/FormEmptyLine.qml ui/modules/Common/Form/Placements/FormGroup.qml ui/modules/Common/Form/Placements/FormHGroup.qml ui/modules/Common/Form/Placements/FormLine.qml ui/modules/Common/Form/Placements/Form.qml ui/modules/Common/Form/Placements/FormTableEntry.qml ui/modules/Common/Form/Placements/FormTableLine.qml ui/modules/Common/Form/Placements/FormTable.qml ui/modules/Common/Form/Placements/FormVGroup.qml ui/modules/Common/Form/RadioButton.qml ui/modules/Common/Form/SearchBox.qml ui/modules/Common/Form/Slider.qml ui/modules/Common/Form/StackView.qml ui/modules/Common/Form/StaticListForm.qml ui/modules/Common/Form/Switch.qml ui/modules/Common/Form/Tab/TabBar.qml ui/modules/Common/Form/Tab/TabButton.qml ui/modules/Common/Form/Tab/TabContainer.qml ui/modules/Common/Form/TransparentTextInput.qml ui/modules/Common/Helpers/DragBox.qml ui/modules/Common/Helpers/InvertedMouseArea.qml ui/modules/Common/Image/Icon.qml ui/modules/Common/Image/RoundedImage.qml ui/modules/Common/Indicators/MediaProgressBar.qml ui/modules/Common/Indicators/RoundProgressBar.qml ui/modules/Common/Indicators/VuMeter.qml ui/modules/Common/Menus/ApplicationMenuEntry.qml ui/modules/Common/Menus/ApplicationMenu.qml ui/modules/Common/Menus/DropDownDynamicMenu.qml ui/modules/Common/Menus/DropDownStaticMenuEntry.qml ui/modules/Common/Menus/DropDownStaticMenu.qml ui/modules/Common/Menus/MenuItem.qml ui/modules/Common/Menus/Menu.qml ui/modules/Common/Misc/Borders.qml ui/modules/Common/Misc/ForceScrollBar.qml ui/modules/Common/Misc/MessageBanner.qml ui/modules/Common/Misc/Paned.qml ui/modules/Common/Picker/DatePicker.qml ui/modules/Common/Picker/TimePicker.qml ui/modules/Common/Popup/DesktopPopup.qml ui/modules/Common/Popup/Popup.qml ui/modules/Common/Popup/PopupShadow.qml ui/modules/Common/qmldir ui/modules/Common/Styles/Animations/BusyIndicatorStyle.qml ui/modules/Common/Styles/Dialog/DateTimeDialogStyle.qml ui/modules/Common/Styles/Dialog/DialogStyle.qml ui/modules/Common/Styles/Form/ActionBarStyle.qml ui/modules/Common/Styles/Form/ActionSwitchStyle.qml ui/modules/Common/Styles/Form/Buttons/AbstractTextButtonStyle.qml ui/modules/Common/Styles/Form/Buttons/ExclusiveButtonsStyle.qml ui/modules/Common/Styles/Form/Buttons/FileChooserButtonStyle.qml ui/modules/Common/Styles/Form/Buttons/SmallButtonStyle.qml ui/modules/Common/Styles/Form/Buttons/TextButtonAStyle.qml ui/modules/Common/Styles/Form/Buttons/TextButtonBStyle.qml ui/modules/Common/Styles/Form/Buttons/TextButtonCStyle.qml ui/modules/Common/Styles/Form/CheckBoxTextStyle.qml ui/modules/Common/Styles/Form/ComboBoxStyle.qml ui/modules/Common/Styles/Form/CommonItemDelegateStyle.qml ui/modules/Common/Styles/Form/DroppableTextAreaStyle.qml ui/modules/Common/Styles/Form/Fields/NumericFieldStyle.qml ui/modules/Common/Styles/Form/Fields/TextAreaFieldStyle.qml ui/modules/Common/Styles/Form/Fields/TextFieldStyle.qml ui/modules/Common/Styles/Form/ListFormStyle.qml ui/modules/Common/Styles/Form/Placements/FormHGroupStyle.qml ui/modules/Common/Styles/Form/Placements/FormLineStyle.qml ui/modules/Common/Styles/Form/Placements/FormStyle.qml ui/modules/Common/Styles/Form/Placements/FormTableLineStyle.qml ui/modules/Common/Styles/Form/Placements/FormTableStyle.qml ui/modules/Common/Styles/Form/Placements/FormVGroupStyle.qml ui/modules/Common/Styles/Form/RadioButtonStyle.qml ui/modules/Common/Styles/Form/SearchBoxStyle.qml ui/modules/Common/Styles/Form/SliderStyle.qml ui/modules/Common/Styles/Form/StackViewStyle.qml ui/modules/Common/Styles/Form/SwitchStyle.qml ui/modules/Common/Styles/Form/Tab/TabButtonStyle.qml ui/modules/Common/Styles/Form/Tab/TabContainerStyle.qml ui/modules/Common/Styles/Form/TransparentTextInputStyle.qml ui/modules/Common/Styles/Indicators/MediaProgressBarStyle.qml ui/modules/Common/Styles/Indicators/RoundProgressBarStyle.qml ui/modules/Common/Styles/Indicators/VuMeterStyle.qml ui/modules/Common/Styles/Menus/ApplicationMenuStyle.qml ui/modules/Common/Styles/Menus/DropDownStaticMenuStyle.qml ui/modules/Common/Styles/Menus/MenuItemStyle.qml ui/modules/Common/Styles/Menus/MenuStyle.qml ui/modules/Common/Styles/Misc/ForceScrollBarStyle.qml ui/modules/Common/Styles/Misc/MessageBannerStyle.qml ui/modules/Common/Styles/Misc/PanedStyle.qml ui/modules/Common/Styles/Picker/DatePickerStyle.qml ui/modules/Common/Styles/Picker/TimePickerStyle.qml ui/modules/Common/Styles/Popup/PopupStyle.qml ui/modules/Common/Styles/qmldir ui/modules/Common/Styles/Tooltip/TooltipStyle.qml ui/modules/Common/Styles/Window/WindowStyle.qml ui/modules/Common/Text/Text.qml ui/modules/Common/Tooltip/TooltipArea.qml ui/modules/Common/Tooltip/Tooltip.qml ui/modules/Common/View/ScrollableListView.qml ui/modules/Common/Window/ApplicationWindow.qml ui/modules/Common/Window/VirtualWindow.qml ui/modules/Common/Window/Window.js ui/modules/Common/Window/Window.qml ui/modules/Konami/Konami.qml ui/modules/Konami/qmldir ui/modules/Linphone/Account/AccountStatus.qml ui/modules/Linphone/Blocks/CardBlock.qml ui/modules/Linphone/Blocks/RequestBlock.qml ui/modules/Linphone/Calls/CallControls.qml ui/modules/Linphone/Calls/Calls.js ui/modules/Linphone/Calls/Calls.qml ui/modules/Linphone/Calls/CallStatistics.qml ui/modules/Linphone/Calls/ConferenceControls.qml ui/modules/Linphone/Calls/IncallAvatar.qml ui/modules/Linphone/Camera/CameraItem.qml ui/modules/Linphone/Camera/CameraView.qml ui/modules/Linphone/Chat/Chat.js ui/modules/Linphone/Chat/Chat.qml ui/modules/Linphone/Chat/ChatContent.qml ui/modules/Linphone/Chat/ChatDeliveries.qml ui/modules/Linphone/Chat/ChatMenu.qml ui/modules/Linphone/Chat/ChatAudioMessage.qml ui/modules/Linphone/Chat/ChatAudioPreview.qml ui/modules/Linphone/Chat/ChatCalendarMessage.qml ui/modules/Linphone/Chat/ChatConferenceInvitationMessage.qml ui/modules/Linphone/Chat/ChatFileMessage.qml ui/modules/Linphone/Chat/ChatFilePreview.qml ui/modules/Linphone/Chat/ChatForwardMessage.qml ui/modules/Linphone/Chat/ChatMessagePreview.qml ui/modules/Linphone/Chat/ChatReplyMessage.qml ui/modules/Linphone/Chat/ChatReplyPreview.qml ui/modules/Linphone/Chat/ChatTextMessage.qml ui/modules/Linphone/Chat/Event.qml ui/modules/Linphone/Chat/IncomingMessage.qml ui/modules/Linphone/Chat/Message.js ui/modules/Linphone/Chat/Message.qml ui/modules/Linphone/Chat/Notice.qml ui/modules/Linphone/Chat/OutgoingMessage.qml ui/modules/Linphone/Codecs/CodecAttribute.qml ui/modules/Linphone/Codecs/CodecLegend.qml ui/modules/Linphone/Codecs/CodecsViewer.qml ui/modules/Linphone/Contact/Avatar.qml ui/modules/Linphone/Contact/ContactDescription.qml ui/modules/Linphone/Contact/ContactMessageCounter.qml ui/modules/Linphone/Contact/Contact.qml ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml ui/modules/Linphone/Dialog/SipAddressDialog.qml ui/modules/Linphone/Dialog/MultimediaParametersDialog.qml ui/modules/Linphone/Dialog/ZrtpTokenAuthenticationDialog.qml ui/modules/Linphone/File/FileView.qml ui/modules/Linphone/History/History.qml ui/modules/Linphone/History/History.js ui/modules/Linphone/History/Event.qml ui/modules/Linphone/Menus/SipAddressesMenu.qml ui/modules/Linphone/Menus/IncallMenu.qml ui/modules/Linphone/Misc/MessageCounter.qml ui/modules/Linphone/Notifications/NotificationBasic.qml ui/modules/Linphone/Notifications/NotificationNewVersionAvailable.qml ui/modules/Linphone/Notifications/Notification.qml ui/modules/Linphone/Notifications/NotificationReceivedCall.qml ui/modules/Linphone/Notifications/NotificationReceivedFileMessage.qml ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml ui/modules/Linphone/Notifications/NotificationRecordingCompleted.qml ui/modules/Linphone/Notifications/NotificationSnapshotWasTaken.qml ui/modules/Linphone/Presence/PresenceLevel.qml ui/modules/Linphone/qmldir ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml ui/modules/Linphone/Sticker/AvatarSticker.qml ui/modules/Linphone/Sticker/CameraSticker.qml ui/modules/Linphone/Sticker/DecorationSticker.qml ui/modules/Linphone/Sticker/Sticker.qml ui/modules/Linphone/Styles/Account/AccountStatusStyle.qml ui/modules/Linphone/Styles/Blocks/CardBlockStyle.qml ui/modules/Linphone/Styles/Blocks/RequestBlockStyle.qml ui/modules/Linphone/Styles/Calls/CallControlsStyle.qml ui/modules/Linphone/Styles/Calls/CallsStyle.qml ui/modules/Linphone/Styles/Calls/CallStatisticsStyle.qml ui/modules/Linphone/Styles/Calls/ConferenceControlsStyle.qml ui/modules/Linphone/Styles/Camera/CameraViewStyle.qml ui/modules/Linphone/Styles/Chat/ChatStyle.qml ui/modules/Linphone/Styles/Chat/ChatAudioMessageStyle.qml ui/modules/Linphone/Styles/Chat/ChatAudioPreviewStyle.qml ui/modules/Linphone/Styles/Chat/ChatFilePreviewStyle.qml ui/modules/Linphone/Styles/Chat/ChatCalendarMessageStyle.qml ui/modules/Linphone/Styles/Chat/ChatForwardMessageStyle.qml ui/modules/Linphone/Styles/Chat/ChatReplyMessageStyle.qml ui/modules/Linphone/Styles/Codecs/CodecsViewerStyle.qml ui/modules/Linphone/Styles/Contact/AvatarStyle.qml ui/modules/Linphone/Styles/Contact/ContactDescriptionStyle.qml ui/modules/Linphone/Styles/Contact/ContactMessageCounterStyle.qml ui/modules/Linphone/Styles/Contact/ContactStyle.qml ui/modules/Linphone/Styles/Dialog/MultimediaParametersDialogStyle.qml ui/modules/Linphone/Styles/Dialog/OnlineInstallerDialogStyle.qml ui/modules/Linphone/Styles/Dialog/SipAddressDialogStyle.qml ui/modules/Linphone/Styles/Dialog/ZrtpTokenAuthenticationDialogStyle.qml ui/modules/Linphone/Styles/History/HistoryStyle.qml ui/modules/Linphone/Styles/Menus/SipAddressesMenuStyle.qml ui/modules/Linphone/Styles/Menus/IncallMenuStyle.qml ui/modules/Linphone/Styles/Misc/MessageCounterStyle.qml ui/modules/Linphone/Styles/Notifications/NotificationBasicStyle.qml ui/modules/Linphone/Styles/Notifications/NotificationReceivedCallStyle.qml ui/modules/Linphone/Styles/Notifications/NotificationReceivedFileMessageStyle.qml ui/modules/Linphone/Styles/Notifications/NotificationReceivedMessageStyle.qml ui/modules/Linphone/Styles/Notifications/NotificationStyle.qml ui/modules/Linphone/Styles/qmldir ui/modules/Linphone/Styles/Sticker/AvatarStickerStyle.qml ui/modules/Linphone/Styles/Sticker/CameraStickerStyle.qml ui/modules/Linphone/Styles/Sticker/DecorationStickerStyle.qml ui/modules/Linphone/Styles/Sticker/StickerStyle.qml ui/modules/Linphone/Styles/TelKeypad/TelKeypadStyle.qml ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml ui/modules/Linphone/Styles/View/ParticipantsListViewStyle.qml ui/modules/Linphone/Styles/View/ParticipantsViewStyle.qml ui/modules/Linphone/Styles/View/SipAddressesViewStyle.qml ui/modules/Linphone/TelKeypad/TelKeypadButton.qml ui/modules/Linphone/TelKeypad/TelKeypad.js ui/modules/Linphone/TelKeypad/TelKeypad.qml ui/modules/Linphone/Timeline/Timeline.js ui/modules/Linphone/Timeline/Timeline.qml ui/modules/Linphone/Timeline/TimelineItem.qml ui/modules/Linphone/View/ParticipantsListView.qml ui/modules/Linphone/View/ParticipantsView.qml ui/modules/Linphone/View/SipAddressesView.qml ui/scripts/Utils/port-tools.js ui/scripts/Utils/qmldir ui/scripts/Utils/utils.js ui/views/App/qmldir ui/views/App/Calls/AbstractStartingCall.qml ui/views/App/Calls/CallsWindow.js ui/views/App/Calls/CallsWindow.qml ui/views/App/Calls/Conference.qml ui/views/App/Calls/Dialogs/CallSipAddress.qml ui/views/App/Calls/Dialogs/CallTransfer.qml ui/views/App/Calls/Dialogs/ConferenceManager.qml ui/views/App/Calls/Incall.js ui/views/App/Calls/Incall.qml ui/views/App/Calls/IncallActiveSpeaker.qml ui/views/App/Calls/IncallFullscreen.qml ui/views/App/Calls/IncallGrid.qml ui/views/App/Calls/IncomingCall.qml ui/views/App/Calls/WaitingRoom.qml ui/views/App/Dialog/NewConference.qml ui/views/App/Main/Assistant/ActivateAppSipAccountWithEmail.qml ui/views/App/Main/Assistant/ActivateAppSipAccountWithPhoneNumber.qml ui/views/App/Main/Assistant/AssistantAbstractView.qml ui/views/App/Main/Assistant/AssistantHome.qml ui/views/App/Main/Assistant/CreateAppSipAccount.qml ui/views/App/Main/Assistant/CreateAppSipAccountWithEmail.qml ui/views/App/Main/Assistant/CreateAppSipAccountWithPhoneNumber.qml ui/views/App/Main/Assistant/FetchRemoteConfiguration.qml ui/views/App/Main/Assistant.qml ui/views/App/Main/Assistant/UseAppSipAccount.qml ui/views/App/Main/Assistant/UseAppSipAccountWithPhoneNumber.qml ui/views/App/Main/Assistant/UseAppSipAccountWithUsername.qml ui/views/App/Main/Assistant/UseOtherSipAccount.qml ui/views/App/Main/Conferences.qml ui/views/App/Main/ContactEdit.js ui/views/App/Main/ContactEdit.qml ui/views/App/Main/Contacts.qml ui/views/App/Main/Conversation.js ui/views/App/Main/Conversation.qml ui/views/App/Main/Dialogs/About.qml ui/views/App/Main/Dialogs/AuthenticationRequest.js ui/views/App/Main/Dialogs/AuthenticationRequest.qml ui/views/App/Main/Dialogs/EphemeralChatRoom.qml ui/views/App/Main/Dialogs/InfoChatRoom.qml ui/views/App/Main/Dialogs/InfoEncryption.qml ui/views/App/Main/Dialogs/ManageAccount.js ui/views/App/Main/Dialogs/ManageAccounts.qml ui/views/App/Main/Dialogs/NewChatRoom.qml ui/views/App/Main/Dialogs/ParticipantsDevices.qml ui/views/App/Main/Home.qml ui/views/App/Main/HistoryView.qml ui/views/App/Main/HistoryView.js ui/views/App/Main/InviteFriends.qml ui/views/App/Main/MainWindow.js ui/views/App/Main/MainWindowMenuBar.qml ui/views/App/Main/MainWindow.qml ui/views/App/Main/MainWindowTopMenuBar.qml ui/views/App/Settings/Dialogs/SettingsLdapEdit.qml ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.js ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml ui/views/App/Settings/Dialogs/SettingsVideoPreview.qml ui/views/App/Settings/SettingsAdvanced.js ui/views/App/Settings/SettingsAdvanced.qml ui/views/App/Settings/SettingsAudio.qml ui/views/App/Settings/SettingsCallsChat.qml ui/views/App/Settings/SettingsLdap.qml ui/views/App/Settings/SettingsNetwork.qml ui/views/App/Settings/SettingsSipAccounts.js ui/views/App/Settings/SettingsSipAccounts.qml ui/views/App/Settings/SettingsTunnel.qml ui/views/App/Settings/SettingsUi.js ui/views/App/Settings/SettingsUi.qml ui/views/App/Settings/SettingsVideo.js ui/views/App/Settings/SettingsVideo.qml ui/views/App/Settings/SettingsWindow.qml ui/views/App/Styles/Calls/CallStyle.qml ui/views/App/Styles/Calls/CallFullscreenStyle.qml ui/views/App/Styles/Calls/CallsWindowStyle.qml ui/views/App/Styles/Calls/ConferenceStyle.qml ui/views/App/Styles/Calls/IncallStyle.qml ui/views/App/Styles/Calls/WaitingRoomStyle.qml ui/views/App/Styles/Calls/Dialogs/CallSipAddressStyle.qml ui/views/App/Styles/Calls/Dialogs/CallTransferStyle.qml ui/views/App/Styles/Calls/Dialogs/ConferenceManagerStyle.qml ui/views/App/Styles/Dialog/NewConferenceStyle.qml ui/views/App/Styles/Main/Assistant/ActivateAppSipAccountWithEmailStyle.qml ui/views/App/Styles/Main/Assistant/ActivateAppSipAccountWithPhoneNumberStyle.qml ui/views/App/Styles/Main/Assistant/AssistantAbstractViewStyle.qml ui/views/App/Styles/Main/Assistant/AssistantHomeStyle.qml ui/views/App/Styles/Main/Assistant/CreateAppSipAccountStyle.qml ui/views/App/Styles/Main/Assistant/FetchRemoteConfigurationStyle.qml ui/views/App/Styles/Main/AssistantStyle.qml ui/views/App/Styles/Main/Assistant/UseAppSipAccountStyle.qml ui/views/App/Styles/Main/ConferencesStyle.qml ui/views/App/Styles/Main/ContactEditStyle.qml ui/views/App/Styles/Main/ContactsStyle.qml ui/views/App/Styles/Main/ConversationStyle.qml ui/views/App/Styles/Main/Dialogs/AboutStyle.qml ui/views/App/Styles/Main/Dialogs/AuthenticationRequestStyle.qml ui/views/App/Styles/Main/Dialogs/EphemeralChatRoomStyle.qml ui/views/App/Styles/Main/Dialogs/InfoChatRoomStyle.qml ui/views/App/Styles/Main/Dialogs/InfoEncryptionStyle.qml ui/views/App/Styles/Main/Dialogs/NewChatRoomStyle.qml ui/views/App/Styles/Main/Dialogs/ManageAccountsStyle.qml ui/views/App/Styles/Main/Dialogs/ParticipantsDevicesStyle.qml ui/views/App/Styles/Main/HomeStyle.qml ui/views/App/Styles/Main/InviteFriendsStyle.qml ui/views/App/Styles/Main/HistoryViewStyle.qml ui/views/App/Styles/Main/MainWindowStyle.qml ui/views/App/Styles/qmldir ui/views/App/Styles/Settings/Dialogs/SettingsSipAccountsEditStyle.qml ui/views/App/Styles/Settings/Dialogs/SettingsVideoPreviewStyle.qml ui/views/App/Styles/Settings/SettingsAdvancedStyle.qml ui/views/App/Styles/Settings/SettingsAudioStyle.qml ui/views/App/Styles/Settings/SettingsUiStyle.qml ui/views/App/Styles/Settings/SettingsWindowStyle.qml assets/images/linphone_logo.svg ui/dev-modules/Units/Units.qml assets/icon.ico linphone-desktop-5.0.2/linphone-app/src/000077500000000000000000000000001434616504300201705ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/app/000077500000000000000000000000001434616504300207505ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/app/App.cpp000066400000000000000000001150111434616504300221730ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "App.hpp" #ifdef Q_OS_WIN #include #include #include #endif // ifdef Q_OS_WIN #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "cli/Cli.hpp" #include "components/Components.hpp" #include "logger/Logger.hpp" #include "paths/Paths.hpp" #include "providers/AvatarProvider.hpp" #include "providers/ImageProvider.hpp" #include "providers/ExternalImageProvider.hpp" #include "providers/QRCodeProvider.hpp" #include "providers/ThumbnailProvider.hpp" #include "translator/DefaultTranslator.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "components/other/desktop-tools/DesktopTools.hpp" #include "components/timeline/TimelineModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "components/timeline/TimelineProxyModel.hpp" #include "components/participant/ParticipantModel.hpp" #include "components/participant/ParticipantListModel.hpp" #include "components/participant/ParticipantProxyModel.hpp" // ============================================================================= using namespace std; namespace { #ifdef Q_OS_LINUX const QString AutoStartDirectory(QDir::homePath().append(QStringLiteral("/.config/autostart/"))); #elif defined(Q_OS_MACOS) const QString OsascriptExecutable(QStringLiteral("osascript")); #else const QString AutoStartSettingsFilePath( QStringLiteral("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run") ); #endif // ifdef Q_OS_LINUX } // ----------------------------------------------------------------------------- #ifdef Q_OS_LINUX static inline bool autoStartEnabled () { return QDir(AutoStartDirectory).exists() && QFile(AutoStartDirectory + EXECUTABLE_NAME ".desktop").exists(); } #elif defined(Q_OS_MACOS) static inline QString getMacOsBundlePath () { QDir dir(QCoreApplication::applicationDirPath()); if (dir.dirName() != QLatin1String("MacOS")) return QString(); dir.cdUp(); dir.cdUp(); QString path(dir.path()); if (path.length() > 0 && path.right(1) == "/") path.chop(1); return path; } static inline QString getMacOsBundleName () { return QFileInfo(getMacOsBundlePath()).baseName(); } static inline bool autoStartEnabled () { const QByteArray expectedWord(getMacOsBundleName().toUtf8()); if (expectedWord.isEmpty()) { qInfo() << QStringLiteral("Application is not installed. Autostart unavailable."); return false; } QProcess process; process.start(OsascriptExecutable, { "-e", "tell application \"System Events\" to get the name of every login item" }); if (!process.waitForFinished()) { qWarning() << QStringLiteral("Unable to execute properly: `%1` (%2).").arg(OsascriptExecutable).arg(process.errorString()); return false; } // TODO: Move in utils? const QByteArray buf(process.readAll()); for (const char *p = buf.data(), *word = p, *end = p + buf.length(); p <= end; ++p) { switch (*p) { case ' ': case '\r': case '\n': case '\t': case '\0': if (word != p) { if (!strncmp(word, expectedWord, size_t(p - word))) return true; word = p + 1; } default: break; } } return false; } #else static inline bool autoStartEnabled () { return QSettings(AutoStartSettingsFilePath, QSettings::NativeFormat).value(EXECUTABLE_NAME).isValid(); } #endif // ifdef Q_OS_LINUX // ----------------------------------------------------------------------------- static inline bool installLocale (App &app, QTranslator &translator, const QLocale &locale) { bool ok = translator.load(locale, Constants::LanguagePath) && app.installTranslator(&translator); if(ok) QLocale::setDefault(locale); return ok; } static inline string getConfigPathIfExists (const QCommandLineParser &parser) { QString filePath = parser.isSet("config") ? parser.value("config") : ""; string configPath; if(!QUrl(filePath).isRelative()){ configPath = Utils::appStringToCoreString(FileDownloader::synchronousDownload(filePath, Utils::coreStringToAppString(Paths::getConfigDirPath(false)), true)); } if( configPath == "") configPath = Paths::getConfigFilePath(filePath, false); if( configPath == "" ) configPath = Paths::getConfigFilePath("", false); return configPath; } bool App::setFetchConfig (QCommandLineParser *parser) { bool fetched = false; QString filePath = parser->value("fetch-config"); if( !filePath.isEmpty()){ if(QUrl(filePath).isRelative()){// this is a file path filePath = Utils::coreStringToAppString(Paths::getConfigFilePath(filePath, false)); if(!filePath.isEmpty()) filePath = "file://"+filePath; } if(!filePath.isEmpty()){ auto instance = CoreManager::getInstance(); if(instance){ auto core = instance->getCore(); if(core){ filePath.replace('\\','/'); if(core->setProvisioningUri(Utils::appStringToCoreString(filePath)) == 0){ parser->process(cleanParserKeys(parser, QStringList("fetch-config")));// Remove this parameter from the parser fetched = true; }else fetched = false; } } } if(!fetched){ qWarning() <<"Remote provisionning cannot be retrieved. Command have beend cleaned"; createParser(); } } return fetched; } // ----------------------------------------------------------------------------- App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) { connect(this, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(stateChanged(Qt::ApplicationState))); setWindowIcon(QIcon(Constants::WindowIconPath)); char * tz = getenv("TZ"); if(!tz){// If not set, set the environement variable for uses of mktime from the SDK. #ifdef Q_OS_WIN _putenv(("TZ="+QTimeZone::systemTimeZoneId().toStdString()).c_str()); _tzset(); #else setenv("TZ", QTimeZone::systemTimeZoneId().toStdString().c_str(), 1); tzset(); #endif } bctbx_set_default_encoding(Constants::LinphoneLocaleEncoding);// Use UTF-8 for internals. Linphone uses UTF-8 so there will be no loss on data with less precise encodings. Qt will do the rest. createParser(); mParser->parse(this->arguments()); // Get configuration for translators shared_ptr config = Utils::getConfigIfExists (QString::fromStdString(getConfigPathIfExists(*mParser))); // Init locale. mTranslator = new DefaultTranslator(this); mDefaultTranslator = new DefaultTranslator(this); initLocale(config); Logger::init(config); createParser();// Recreate parser in order to use translations from config. mParser->process(*this); if (mParser->isSet("verbose")) Logger::getInstance()->setVerbose(true); // List available locales. for (const auto &locale : QDir(Constants::LanguagePath).entryList()) mAvailableLocales << QLocale(locale); if (mParser->isSet("help")) { mParser->showHelp(); } if (mParser->isSet("cli-help")) { Cli::showHelp(); ::exit(EXIT_SUCCESS); } if (mParser->isSet("version")) mParser->showVersion(); mAutoStart = autoStartEnabled(); qInfo() << QStringLiteral("Starting " APPLICATION_NAME " (bin: " EXECUTABLE_NAME ")"); qInfo() << QStringLiteral("Use locale: %1").arg(mLocale); } App::~App () { qInfo() << QStringLiteral("Destroying app..."); } void App::stop(){ qInfo() << QStringLiteral("Stopping app..."); if( mEngine ){ delete mEngine; processEvents(QEventLoop::AllEvents); } CoreManager::uninit(); processEvents(QEventLoop::AllEvents); // Process all needed events on engine deletion. if( mParser) delete mParser; } // ----------------------------------------------------------------------------- QStringList App::cleanParserKeys(QCommandLineParser * parser, QStringList keys){ QStringList oldArguments = parser->optionNames(); QStringList parameters; parameters << "dummy"; for(int i = 0 ; i < oldArguments.size() ; ++i){ if( !keys.contains(oldArguments[i])){ if( mParser->value(oldArguments[i]).isEmpty()) parameters << "--"+oldArguments[i]; else parameters << "--"+oldArguments[i]+"="+parser->value(oldArguments[i]); } } return parameters; } void App::processArguments(QHash args){ QList keys = args.keys(); QStringList parameters = cleanParserKeys(mParser, keys); for(auto i = keys.begin() ; i != keys.end() ; ++i){ parameters << "--"+(*i)+"="+args.value(*i); } if(!mParser->parse(parameters)) qWarning() << "Parsing error : " << mParser->errorText(); } static QQuickWindow *createSubWindow (QQmlApplicationEngine *engine, const char *path) { qInfo() << QStringLiteral("Creating subwindow: `%1`.").arg(path); QQmlComponent component(engine, QUrl(path)); if (component.isError()) { qWarning() << component.errors(); abort(); } qInfo() << QStringLiteral("Subwindow status: `%1`.").arg(component.status()); QObject *object = component.create(); Q_ASSERT(object); QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); object->setParent(engine); return qobject_cast(object); } // ----------------------------------------------------------------------------- void App::initContentApp () { std::string configPath; shared_ptr config; bool mustBeIconified = false; bool needRestart = true; // Destroy qml components and linphone core if necessary. if (mEngine) { needRestart = false; setFetchConfig(mParser); setOpened(false); qInfo() << QStringLiteral("Restarting app..."); delete mEngine; mNotifier = nullptr; // CoreManager::uninit(); removeTranslator(mTranslator); removeTranslator(mDefaultTranslator); delete mTranslator; delete mDefaultTranslator; mTranslator = new DefaultTranslator(this); mDefaultTranslator = new DefaultTranslator(this); configPath = getConfigPathIfExists(*mParser); config = Utils::getConfigIfExists (QString::fromStdString(configPath)); initLocale(config); } else { configPath = getConfigPathIfExists(*mParser); config = Utils::getConfigIfExists(QString::fromStdString(configPath)); // Update and download codecs. VideoCodecsModel::updateCodecs(); VideoCodecsModel::downloadUpdatableCodecs(this); // Don't quit if last window is closed!!! setQuitOnLastWindowClosed(false); // Deal with received messages and CLI. QObject::connect(this, &App::receivedMessage, this, [](int, const QByteArray &byteArray) { QString command(byteArray); qInfo() << QStringLiteral("Received command from other application: `%1`.").arg(command); Cli::executeCommand(command); }); #ifndef Q_OS_MACOS mustBeIconified = mParser->isSet("iconified"); #endif // ifndef Q_OS_MACOS mColorListModel = new ColorListModel(); mImageListModel = new ImageListModel(); } // Change colors if necessary. mColorListModel->useConfig(config); mImageListModel->useConfig(config); // There is no more database for callback. Setting it in the configuration before starting the core will do migration. // When the migration is done by SDK, further migrations on call logs will do nothing. It is safe to use . config->setString("storage", "call_logs_db_uri", Paths::getCallHistoryFilePath()); // Init core. CoreManager::init(this, Utils::coreStringToAppString(configPath)); // Init engine content. mEngine = new QQmlApplicationEngine(this); // Provide `+custom` folders for custom components and `5.9` for old components. { QStringList selectors("custom"); const QVersionNumber &version = QLibraryInfo::version(); if (version.majorVersion() == 5 && version.minorVersion() == 9) selectors.push_back("5.9"); (new QQmlFileSelector(mEngine, mEngine))->setExtraSelectors(selectors); } qInfo() << QStringLiteral("Activated selectors:") << QQmlFileSelector::get(mEngine)->selector()->allSelectors(); // Set modules paths. mEngine->addImportPath(":/ui/modules"); mEngine->addImportPath(":/ui/scripts"); mEngine->addImportPath(":/ui/views"); // Provide avatars/thumbnails providers. mEngine->addImageProvider(AvatarProvider::ProviderId, new AvatarProvider()); mEngine->addImageProvider(ImageProvider::ProviderId, new ImageProvider()); mEngine->addImageProvider(ExternalImageProvider::ProviderId, new ExternalImageProvider()); mEngine->addImageProvider(QRCodeProvider::ProviderId, new QRCodeProvider()); mEngine->addImageProvider(ThumbnailProvider::ProviderId, new ThumbnailProvider()); mEngine->rootContext()->setContextProperty("applicationName", APPLICATION_NAME); #ifdef APPLICATION_URL mEngine->rootContext()->setContextProperty("applicationUrl", APPLICATION_URL); #else mEngine->rootContext()->setContextProperty("applicationUrl", ""); #endif #ifdef APPLICATION_VENDOR mEngine->rootContext()->setContextProperty("applicationVendor", APPLICATION_VENDOR); #else mEngine->rootContext()->setContextProperty("applicationVendor", ""); #endif #ifdef APPLICATION_LICENCE mEngine->rootContext()->setContextProperty("applicationLicence", APPLICATION_LICENCE); #else mEngine->rootContext()->setContextProperty("applicationLicence", ""); #endif mEngine->rootContext()->setContextProperty("copyrightRangeDate", COPYRIGHT_RANGE_DATE); mEngine->rootContext()->setContextProperty("Colors", mColorListModel->getQmlData()); mEngine->rootContext()->setContextProperty("Images", mImageListModel->getQmlData()); mEngine->rootContext()->setContextProperty("qtIsNewer_5_15_0", QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) ); registerTypes(); registerSharedTypes(); registerToolTypes(); registerSharedToolTypes(); // Enable notifications. mNotifier = new Notifier(mEngine); // Load main view. qInfo() << QStringLiteral("Loading main view..."); mEngine->load(QUrl(Constants::QmlViewMainWindow)); if (mEngine->rootObjects().isEmpty()) qFatal("Unable to open main window."); QObject::connect( CoreManager::getInstance(), &CoreManager::coreManagerInitialized, CoreManager::getInstance(), [this, mustBeIconified]() mutable { if(CoreManager::getInstance()->started()) openAppAfterInit(mustBeIconified); } ); // Execute command argument if needed. const QString commandArgument = getCommandArgument(); if (!commandArgument.isEmpty()) { Cli::CommandFormat format; Cli::executeCommand(commandArgument, &format); if (format == Cli::UriFormat || format == Cli::UrlFormat ) mustBeIconified = true; } } // ----------------------------------------------------------------------------- QString App::getCommandArgument () { const QStringList &arguments = mParser->positionalArguments(); return arguments.empty() ? QString("") : arguments[0]; } // ----------------------------------------------------------------------------- #ifdef Q_OS_MACOS bool App::event (QEvent *event) { if (event->type() == QEvent::FileOpen) { const QString url = static_cast(event)->url().toString(); if (isSecondary()) { sendMessage(url.toLocal8Bit(), -1); ::exit(EXIT_SUCCESS); } Cli::executeCommand(url); }else if(event->type() == QEvent::ApplicationStateChange){ auto state = static_cast(event); if( state->applicationState() == Qt::ApplicationActive) smartShowWindow(getMainWindow()); } return SingleApplication::event(event); } #endif // ifdef Q_OS_MACOS // ----------------------------------------------------------------------------- QQuickWindow *App::getCallsWindow () const { if (CoreManager::getInstance()->getCore()->getConfig()->getInt( SettingsModel::UiSection, "disable_calls_window", 0 )) return nullptr; return mCallsWindow; } QQuickWindow *App::getMainWindow () const { return qobject_cast( const_cast(mEngine)->rootObjects().at(0) ); } QQuickWindow *App::getSettingsWindow () const { return mSettingsWindow; } // ----------------------------------------------------------------------------- void App::smartShowWindow (QQuickWindow *window) { if (!window) return; window->setVisible(true); // Force show, maybe redundant with setVisible if(window->visibility() == QWindow::Maximized)// Avoid to change visibility mode window->showMaximized(); else window->show(); window->raise();// Raise ensure to get focus on Mac window->requestActivate(); } // ----------------------------------------------------------------------------- bool App::hasFocus () const { return getMainWindow()->isActive() || (mCallsWindow && mCallsWindow->isActive()); } void App::stateChanged(Qt::ApplicationState pState) { DesktopTools::applicationStateChanged(pState); auto core = CoreManager::getInstance(); if(core) core->stateChanged(pState); } // ----------------------------------------------------------------------------- void App::createParser () { delete mParser; mParser = new QCommandLineParser(); mParser->setApplicationDescription(tr("applicationDescription")); mParser->addPositionalArgument("command", tr("commandLineDescription").replace("%1", APPLICATION_NAME), "[command]"); mParser->addOptions({ { { "h", "help" }, tr("commandLineOptionHelp") }, { "cli-help", tr("commandLineOptionCliHelp").replace("%1", APPLICATION_NAME) }, { { "v", "version" }, tr("commandLineOptionVersion") }, { "config", tr("commandLineOptionConfig").replace("%1", EXECUTABLE_NAME), tr("commandLineOptionConfigArg") }, { "fetch-config", tr("commandLineOptionFetchConfig").replace("%1", EXECUTABLE_NAME), tr("commandLineOptionFetchConfigArg") }, { { "c", "call" }, tr("commandLineOptionCall").replace("%1", EXECUTABLE_NAME),tr("commandLineOptionCallArg") }, #ifndef Q_OS_MACOS { "iconified", tr("commandLineOptionIconified") }, #endif // ifndef Q_OS_MACOS { { "V", "verbose" }, tr("commandLineOptionVerbose") } }); } // ----------------------------------------------------------------------------- template static QObject *makeSharedSingleton (QQmlEngine *, QJSEngine *) { QObject *object = (*function)(); QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); return object; } template static inline void registerSharedSingletonType (const char *name) { qmlRegisterSingletonType(Constants::MainQmlUri, 1, 0, name, makeSharedSingleton); } template static QObject *makeSharedSingleton (QQmlEngine *, QJSEngine *) { QObject *object = (CoreManager::getInstance()->*function)(); QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); return object; } template static QObject *makeSharedSingleton (QQmlEngine *, QJSEngine *) { QObject *object = (CoreManager::getInstance()->*function)(); QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); return object; } template static inline void registerSharedSingletonType (const char *name) { qmlRegisterSingletonType(Constants::MainQmlUri, 1, 0, name, makeSharedSingleton); } template static inline void registerSharedSingletonType (const char *name) { qmlRegisterSingletonType(Constants::MainQmlUri, 1, 0, name, makeSharedSingleton); } template static inline void registerUncreatableType (const char *name) { qmlRegisterUncreatableType(Constants::MainQmlUri, 1, 0, name, QLatin1String("Uncreatable")); } template static inline void registerSingletonType (const char *name) { qmlRegisterSingletonType(Constants::MainQmlUri, 1, 0, name, [](QQmlEngine *engine, QJSEngine *) -> QObject *{ return new T(engine); }); } template static inline void registerType (const char *name) { qmlRegisterType(Constants::MainQmlUri, 1, 0, name); } template static inline void registerToolType (const char *name) { qmlRegisterSingletonType(name, 1, 0, name, [](QQmlEngine *engine, QJSEngine *) -> QObject *{ return new T(engine); }); } template static QObject *makeSharedTool (QQmlEngine *, QJSEngine *) { QObject *object = (Owner::getInstance()->*function)(); QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); return object; } template static inline void registerSharedToolType (const char *name) { qmlRegisterSingletonType(name, 1, 0, name, makeSharedTool); } void App::registerTypes () { qInfo() << QStringLiteral("Registering types..."); qRegisterMetaType>(); qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType > >(); qRegisterMetaType>(); qRegisterMetaType>(); qRegisterMetaType>(); qRegisterMetaType>(); qRegisterMetaType>(); qRegisterMetaType>(); qRegisterMetaType>(); qRegisterMetaType>(); //qRegisterMetaType>(); LinphoneEnums::registerMetaTypes(); registerType("AssistantModel"); registerType("AuthenticationNotifier"); registerType("CallsListProxyModel"); registerType("Camera"); registerType("ChatRoomProxyModel"); registerType("ConferenceHelperModel"); registerType("ConferenceProxyModel"); registerType("ConferenceInfoModel"); registerType("ConferenceInfoProxyModel"); registerType("ContactsListProxyModel"); registerType("ContactsImporterListProxyModel"); registerType("ContentProxyModel"); registerType("FileDownloader"); registerType("FileExtractor"); registerType("HistoryProxyModel"); registerType("LdapProxyModel"); registerType("ParticipantImdnStateProxyModel"); registerType("SipAddressesProxyModel"); registerType("SearchSipAddressesModel"); registerType("SearchSipAddressesProxyModel"); registerType("TimeZoneProxyModel"); registerType("ColorProxyModel"); registerType("ImageColorsProxyModel"); registerType("ImageProxyModel"); registerType("TimelineProxyModel"); registerType("ParticipantProxyModel"); registerType("ParticipantDeviceProxyModel"); registerType("SoundPlayer"); registerType("TelephoneNumbersModel"); registerSingletonType("AudioCodecsModel"); registerSingletonType("OwnPresenceModel"); registerSingletonType("Presence"); //registerSingletonType("TimelineModel"); registerSingletonType("UrlHandlers"); registerSingletonType("VideoCodecsModel"); registerUncreatableType("CallModel"); registerUncreatableType("ChatCallModel"); registerUncreatableType("ChatMessageModel"); registerUncreatableType("ChatNoticeModel"); registerUncreatableType("ChatRoomModel"); registerUncreatableType("ColorModel"); registerUncreatableType("ImageModel"); registerUncreatableType("ConferenceAddModel"); registerUncreatableType("ConferenceModel"); registerUncreatableType("ContactModel"); registerUncreatableType("ContactsImporterModel"); registerUncreatableType("ContentModel"); registerUncreatableType("ContentListModel"); registerUncreatableType("HistoryModel"); registerUncreatableType("LdapModel"); registerUncreatableType("RecorderModel"); registerUncreatableType("SearchResultModel"); registerUncreatableType("SipAddressObserver"); registerUncreatableType("VcardModel"); registerUncreatableType("TimelineModel"); registerUncreatableType("TunnelModel"); registerUncreatableType("TunnelConfigModel"); registerUncreatableType("TunnelConfigProxyModel"); registerUncreatableType("ParticipantModel"); registerUncreatableType("ParticipantListModel"); registerUncreatableType("ParticipantDeviceModel"); registerUncreatableType("ParticipantDeviceListModel"); registerUncreatableType("ParticipantImdnStateModel"); registerUncreatableType("ParticipantImdnStateListModel"); qmlRegisterUncreatableMetaObject(LinphoneEnums::staticMetaObject, "LinphoneEnums", 1, 0, "LinphoneEnums", "Only enums"); } void App::registerSharedTypes () { qInfo() << QStringLiteral("Registering shared types..."); registerSharedSingletonType("App"); registerSharedSingletonType("CoreManager"); registerSharedSingletonType("SettingsModel"); registerSharedSingletonType("AccountSettingsModel"); registerSharedSingletonType("SipAddressesModel"); registerSharedSingletonType("CallsListModel"); registerSharedSingletonType("ContactsListModel"); registerSharedSingletonType("ContactsImporterListModel"); registerSharedSingletonType("LdapListModel"); registerSharedSingletonType("TimelineListModel"); registerSharedSingletonType("RecorderManager"); //qmlRegisterSingletonType(Constants::MainQmlUri, 1, 0, "ColorList", mColorListModel); //registerSharedSingletonType("ColorCpp"); } void App::registerToolTypes () { qInfo() << QStringLiteral("Registering tool types..."); registerToolType("Clipboard"); registerToolType("DesktopTools"); registerToolType("TextToSpeech"); registerToolType("Units"); registerToolType("ContactsImporterPluginsManager"); registerToolType("UtilsCpp"); registerToolType("ConstantsCpp"); } void App::registerSharedToolTypes () { qInfo() << QStringLiteral("Registering shared tool types..."); registerSharedToolType("ColorsList"); } // ----------------------------------------------------------------------------- void App::setTrayIcon () { QQuickWindow *root = getMainWindow(); QSystemTrayIcon* systemTrayIcon = (mSystemTrayIcon?mSystemTrayIcon : new QSystemTrayIcon(nullptr));// Workaround : QSystemTrayIcon cannot be deleted because of setContextMenu (indirectly) // trayIcon: Right click actions. QAction *settingsAction = new QAction(tr("settings"), root); root->connect(settingsAction, &QAction::triggered, root, [this] { App::smartShowWindow(getSettingsWindow()); }); QAction *updateCheckAction = new QAction(tr("checkForUpdates"), root); root->connect(updateCheckAction, &QAction::triggered, root, [this] { checkForUpdates(true); }); QAction *aboutAction = new QAction(tr("about"), root); root->connect(aboutAction, &QAction::triggered, root, [root] { App::smartShowWindow(root); QMetaObject::invokeMethod( root, Constants::AttachVirtualWindowMethodName, Qt::DirectConnection, Q_ARG(QVariant, QUrl(Constants::AboutPath)), Q_ARG(QVariant, QVariant()), Q_ARG(QVariant, QVariant()) ); }); QAction *restoreAction = new QAction(tr("restore"), root); root->connect(restoreAction, &QAction::triggered, root, [root] { smartShowWindow(root); }); QAction *quitAction = new QAction(tr("quit"), root); root->connect(quitAction, &QAction::triggered, this, &App::quit); // trayIcon: Left click actions. static QMenu *menu = new QMenu();// Static : Workaround about a bug with setContextMenu where it cannot be called more than once. root->connect(systemTrayIcon, &QSystemTrayIcon::activated, [root]( QSystemTrayIcon::ActivationReason reason ) { if (reason == QSystemTrayIcon::Trigger) { if (root->visibility() == QWindow::Hidden) smartShowWindow(root); else root->hide(); } }); menu->setTitle(APPLICATION_NAME); // Build trayIcon menu. menu->addAction(settingsAction); menu->addAction(updateCheckAction); menu->addAction(aboutAction); menu->addSeparator(); menu->addAction(restoreAction); menu->addSeparator(); menu->addAction(quitAction); if(!mSystemTrayIcon) systemTrayIcon->setContextMenu(menu);// This is a Qt bug. We cannot call setContextMenu more than once. So we have to keep an instance of the menu. systemTrayIcon->setIcon(QIcon(Constants::WindowIconPath)); systemTrayIcon->setToolTip(APPLICATION_NAME); systemTrayIcon->show(); if(!mSystemTrayIcon) mSystemTrayIcon = systemTrayIcon; if(!QSystemTrayIcon::isSystemTrayAvailable()) qInfo() << "System tray is not available"; } // ----------------------------------------------------------------------------- void App::initLocale (const shared_ptr &config) { // Try to use preferred locale. QString locale; // Use english. This default translator is used if there are no found translations in others loads mLocale = Constants::DefaultLocale; if (!installLocale(*this, *mDefaultTranslator, QLocale(mLocale))) qFatal("Unable to install default translator."); if (config) locale = Utils::coreStringToAppString(config->getString(SettingsModel::UiSection, "locale", "")); if (!locale.isEmpty() && installLocale(*this, *mTranslator, QLocale(locale))) { mLocale = locale; return; } // Try to use system locale. QLocale sysLocale = QLocale(QLocale::system().name());// Use Locale from name because Qt has a bug where it didn't use the QLocale::language (aka : translator.language != lolcale.language) on Mac. if (installLocale(*this, *mTranslator, sysLocale)) { mLocale = sysLocale.name(); return; } } QString App::getConfigLocale () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->getConfig()->getString( SettingsModel::UiSection, "locale", "" ) ); } void App::setConfigLocale (const QString &locale) { CoreManager::getInstance()->getCore()->getConfig()->setString( SettingsModel::UiSection, "locale", Utils::appStringToCoreString(locale) ); emit configLocaleChanged(locale); } QString App::getLocale () const { return mLocale; } // ----------------------------------------------------------------------------- #ifdef Q_OS_LINUX void App::setAutoStart (bool enabled) { if (enabled == mAutoStart) return; QDir dir(AutoStartDirectory); if (!dir.exists() && !dir.mkpath(AutoStartDirectory)) { qWarning() << QStringLiteral("Unable to build autostart dir path: `%1`.").arg(AutoStartDirectory); return; } const QString confPath(AutoStartDirectory + EXECUTABLE_NAME ".desktop"); qInfo() << QStringLiteral("Updating `%1`...").arg(confPath); QFile file(confPath); if (!enabled) { if (file.exists() && !file.remove()) { qWarning() << QLatin1String("Unable to remove autostart file: `" EXECUTABLE_NAME ".desktop`."); return; } mAutoStart = enabled; emit autoStartChanged(enabled); return; } if (!file.open(QFile::WriteOnly)) { qWarning() << "Unable to open autostart file: `" EXECUTABLE_NAME ".desktop`."; return; } const QString binPath(applicationFilePath()); // Check if installation is done via Flatpak, AppImage, or classic package // in order to rewrite a correct exec path for autostart QString exec; qDebug() << "binpath=" << binPath; if (binPath.startsWith("/app")) { //Flatpak exec = QStringLiteral("flatpak run " APPLICATION_ID); qDebug() << "exec path autostart set flatpak=" << exec; } else if (binPath.startsWith("/tmp/.mount")) { //Appimage exec = QProcessEnvironment::systemEnvironment().value(QStringLiteral("APPIMAGE")); qDebug() << "exec path autostart set appimage=" << exec; } else { //classic package exec = binPath; qDebug() << "exec path autostart set classic package=" << exec; } QTextStream(&file) << QString( "[Desktop Entry]\n" "Name=" APPLICATION_NAME "\n" "GenericName=SIP Phone\n" "Comment=" APPLICATION_DESCRIPTION "\n" "Type=Application\n" "Exec=" + exec + " --iconified\n" "Terminal=false\n" "Categories=Network;Telephony;\n" "MimeType=x-scheme-handler/sip-" EXECUTABLE_NAME ";x-scheme-handler/sip;x-scheme-handler/sips-" EXECUTABLE_NAME ";x-scheme-handler/sips;x-scheme-handler/tel;x-scheme-handler/callto;\n" ); mAutoStart = enabled; emit autoStartChanged(enabled); } #elif defined(Q_OS_MACOS) void App::setAutoStart (bool enabled) { if (enabled == mAutoStart) return; if (getMacOsBundlePath().isEmpty()) { qWarning() << QStringLiteral("Application is not installed. Unable to change autostart state."); return; } if (enabled) QProcess::execute(OsascriptExecutable, { "-e", "tell application \"System Events\" to make login item at end with properties" "{ path: \"" + getMacOsBundlePath() + "\", hidden: false }" }); else QProcess::execute(OsascriptExecutable, { "-e", "tell application \"System Events\" to delete login item \"" + getMacOsBundleName() + "\"" }); mAutoStart = enabled; emit autoStartChanged(enabled); } #else void App::setAutoStart (bool enabled) { if (enabled == mAutoStart) return; QSettings settings(AutoStartSettingsFilePath, QSettings::NativeFormat); if (enabled) settings.setValue(EXECUTABLE_NAME, QDir::toNativeSeparators(applicationFilePath())); else settings.remove(EXECUTABLE_NAME); mAutoStart = enabled; emit autoStartChanged(enabled); } #endif // ifdef Q_OS_LINUX // ----------------------------------------------------------------------------- void App::openAppAfterInit (bool mustBeIconified) { qInfo() << QStringLiteral("Open " APPLICATION_NAME " app."); auto coreManager = CoreManager::getInstance(); coreManager->getSettingsModel()->updateCameraMode(); // Create other windows. mCallsWindow = createSubWindow(mEngine, Constants::QmlViewCallsWindow); mSettingsWindow = createSubWindow(mEngine, Constants::QmlViewSettingsWindow); QObject::connect(mSettingsWindow, &QWindow::visibilityChanged, this, [coreManager](QWindow::Visibility visibility) { if (visibility == QWindow::Hidden) { qInfo() << QStringLiteral("Update nat policy."); shared_ptr core = coreManager->getCore(); core->setNatPolicy(core->getNatPolicy()); } }); QQuickWindow *mainWindow = getMainWindow(); #ifndef __APPLE__ // Enable TrayIconSystem. if (!QSystemTrayIcon::isSystemTrayAvailable()) qWarning("System tray not found on this system."); else setTrayIcon(); #endif // ifndef __APPLE__ // Display Assistant if it does not exist proxy config. if (coreManager->getAccountList().empty()) QMetaObject::invokeMethod(mainWindow, "setView", Q_ARG(QVariant, Constants::AssistantViewName), Q_ARG(QVariant, QString("")), Q_ARG(QVariant, QString(""))); #ifdef ENABLE_UPDATE_CHECK QTimer *timer = new QTimer(mEngine); timer->setInterval(Constants::VersionUpdateCheckInterval); QObject::connect(timer, &QTimer::timeout, this, &App::checkForUpdate); timer->start(); checkForUpdates(); #endif // ifdef ENABLE_UPDATE_CHECK if(setFetchConfig(mParser)) restart(); else{ // Launch call if wanted and clean parser if( mParser->isSet("call") && coreManager->isLastRemoteProvisioningGood()){ QString sipAddress = mParser->value("call"); mParser->parse(cleanParserKeys(mParser, QStringList("call")));// Clean call from parser if(coreManager->started()){ coreManager->getCallsListModel()->launchAudioCall(sipAddress); }else{ QObject * context = new QObject(); QObject::connect(CoreManager::getInstance(), &CoreManager::coreManagerInitialized,context, [sipAddress,coreManager, context]() mutable { if(context){ delete context; context = nullptr; coreManager->getCallsListModel()->launchAudioCall(sipAddress); } }); } } #ifndef __APPLE__ if (!mustBeIconified) smartShowWindow(mainWindow); #else Q_UNUSED(mustBeIconified); smartShowWindow(mainWindow); #endif setOpened(true); } } // ----------------------------------------------------------------------------- QString App::getStrippedApplicationVersion(){// x.y.z but if 'z-*' then x.y.z-1 QString currentVersion = applicationVersion(); QStringList versions = currentVersion.split('.'); if(versions.size() >=3){ currentVersion = versions[0]+"."+versions[1]+"."; QStringList patchVersions = versions[2].split('-'); if( patchVersions.size() > 1 ) { bool ok; patchVersions[1].toInt(&ok); if( !ok) // Second part of patch is not a number (ie: alpha, beta, pre). Reduce version. currentVersion += QString::number(patchVersions[0].toInt()-1); else currentVersion += patchVersions[0]; } else currentVersion += patchVersions[0]; } return currentVersion; } void App::checkForUpdate() { checkForUpdates(false); } void App::checkForUpdates(bool force) { if(force || CoreManager::getInstance()->getSettingsModel()->isCheckForUpdateEnabled()) CoreManager::getInstance()->getCore()->checkForUpdate( Utils::appStringToCoreString(applicationVersion()) ); } linphone-desktop-5.0.2/linphone-app/src/app/App.hpp000066400000000000000000000114211434616504300222000ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef APP_H_ #define APP_H_ #include #include "single-application/SingleApplication.hpp" // ============================================================================= class QCommandLineParser; class QQmlApplicationEngine; class QQuickWindow; class QSystemTrayIcon; namespace linphone { class Config; } class ColorListModel; class DefaultTranslator; class ImageListModel; class Notifier; class App : public SingleApplication { Q_OBJECT Q_PROPERTY(QString configLocale READ getConfigLocale WRITE setConfigLocale NOTIFY configLocaleChanged) Q_PROPERTY(QString locale READ getLocale CONSTANT) Q_PROPERTY(QVariantList availableLocales READ getAvailableLocales CONSTANT) Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT) Q_PROPERTY(bool autoStart READ getAutoStart WRITE setAutoStart NOTIFY autoStartChanged) public: App (int &argc, char *argv[]); ~App (); void stop(); void initContentApp (); QStringList cleanParserKeys(QCommandLineParser * parser, QStringList keys);// Get all options from parser and remove the selected keys. Return the result that can be passed to parser process. void processArguments(QHash args); QString getCommandArgument (); bool setFetchConfig (QCommandLineParser *parser); #ifdef Q_OS_MACOS bool event (QEvent *event) override; #endif // ifdef Q_OS_MACOS QQmlApplicationEngine *getEngine () { return mEngine; } Notifier *getNotifier () const { return mNotifier; } ColorListModel *getColorListModel () const { return mColorListModel; } ImageListModel *getImageListModel () const { return mImageListModel; } //static ColorListModel *getColorListModel () const { //return App::getInstance()-getColorListModel(); //} QSystemTrayIcon *getSystemTrayIcon () const { return mSystemTrayIcon; } QQuickWindow *getMainWindow () const; bool hasFocus () const; bool isOpened () const { return mIsOpened; } static App *getInstance () { return static_cast(QApplication::instance()); } static constexpr int RestartCode = 1000; Q_INVOKABLE void restart () { exit(RestartCode); } Q_INVOKABLE QQuickWindow *getCallsWindow () const; Q_INVOKABLE QQuickWindow *getSettingsWindow () const; Q_INVOKABLE static void smartShowWindow (QQuickWindow *window); Q_INVOKABLE static void checkForUpdates(bool force = false); public slots: void stateChanged(Qt::ApplicationState); signals: void configLocaleChanged (const QString &locale); void autoStartChanged (bool enabled); void opened (bool status); private: void createParser (); void registerTypes (); void registerSharedTypes (); void registerToolTypes (); void registerSharedToolTypes (); void setTrayIcon (); void createNotifier (); void initLocale (const std::shared_ptr &config); QString getConfigLocale () const; void setConfigLocale (const QString &locale); QString getLocale () const; QVariantList getAvailableLocales () const { return mAvailableLocales; } bool getAutoStart () const { return mAutoStart; } void setAutoStart (bool enabled); void openAppAfterInit (bool mustBeIconified = false); void setOpened (bool status) { if (mIsOpened != status) { mIsOpened = status; emit opened(mIsOpened); } } static QString getStrippedApplicationVersion();// x.y.z but if 'z-*' then x.y.z-1 static void checkForUpdate (); static QString getQtVersion () { return qVersion(); } QVariantList mAvailableLocales; QString mLocale; bool mAutoStart = false; QCommandLineParser *mParser = nullptr; QQmlApplicationEngine *mEngine = nullptr; DefaultTranslator *mTranslator = nullptr; DefaultTranslator *mDefaultTranslator = nullptr; Notifier *mNotifier = nullptr; QQuickWindow *mCallsWindow = nullptr; QQuickWindow *mSettingsWindow = nullptr; ColorListModel * mColorListModel; ImageListModel * mImageListModel; QSystemTrayIcon *mSystemTrayIcon = nullptr; bool mIsOpened = false; }; #endif // APP_H_ linphone-desktop-5.0.2/linphone-app/src/app/AppController.cpp000066400000000000000000000072341434616504300242460ustar00rootroot00000000000000/* * Copyright (c) 2010-2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "config.h" #include #include #include #include #include #ifdef ENABLE_APP_WEBVIEW #include #endif #include "AppController.hpp" #include "components/other/desktop-tools/DesktopTools.hpp" #include "utils/Constants.hpp" // ============================================================================= using namespace std; AppController::AppController (int &argc, char *argv[]) { DesktopTools::init(); QT_REQUIRE_VERSION(argc, argv, Constants::ApplicationMinimalQtVersion) Q_ASSERT(!mApp); // Disable QML cache. Avoid malformed cache. qputenv("QML_DISABLE_DISK_CACHE", "true"); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // Useful to share camera on Fullscreen (other context) QApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // Do not use APPLICATION_NAME here. // The EXECUTABLE_NAME will be used in qt standard paths. It's our goal. QCoreApplication::setApplicationName(EXECUTABLE_NAME); QApplication::setOrganizationDomain(EXECUTABLE_NAME); QCoreApplication::setApplicationVersion(APPLICATION_SEMVER); #ifdef ENABLE_APP_WEBVIEW #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) mApp = new App(argc, argv); QtWebView::initialize(); #else QtWebView::initialize(); mApp = new App(argc, argv); #endif #else mApp = new App(argc, argv); #endif // --------------------------------------------------------------------------- // App creation. // --------------------------------------------------------------------------- QQuickStyle::setStyle("Default"); if (mApp->isSecondary()) { #ifdef Q_OS_MACOS mApp->processEvents(); #endif // ifdef Q_OS_MACOS QString command = mApp->getCommandArgument(); if( command.isEmpty()){ command = "show"; QStringList parametersList; for(int i = 1 ; i < argc ; ++i){ QString a = argv[i]; if(a.startsWith("--"))// show is a command : remove <-->-style parameters a.remove(0,2); command += " "+a; } } mApp->sendMessage(command.toLocal8Bit(), -1); return; } // --------------------------------------------------------------------------- // Fonts. // --------------------------------------------------------------------------- QDirIterator it(":", QDirIterator::Subdirectories); while (it.hasNext()) { QFileInfo info(it.next()); if (info.suffix() == QLatin1String("ttf") || info.suffix() == QLatin1String("otf")) { QString path = info.absoluteFilePath(); if (path.startsWith(":/assets/fonts/")) if(QFontDatabase::addApplicationFont(path)<0) qWarning() << "Font cannot load : " << path; } } qInfo() << "Available fonts : " << QFontDatabase().families(); mApp->setFont(QFont(Constants::DefaultFont)); } AppController::~AppController () { try{ if(mApp) delete mApp; } catch(...){ } } void AppController::stopApp(){ try{ mApp->stop(); delete mApp; mApp = nullptr; } catch(...){ } }linphone-desktop-5.0.2/linphone-app/src/app/AppController.hpp000066400000000000000000000022531434616504300242470ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef APP_CONTROLLER_H_ #define APP_CONTROLLER_H_ #include "App.hpp" // ============================================================================= class AppController { public: AppController (int &argc, char *argv[]); ~AppController (); App *getApp () const { Q_CHECK_PTR(mApp); return mApp; } void stopApp(); private: App *mApp = nullptr; }; #endif // APP_CONTROLLER_H_ linphone-desktop-5.0.2/linphone-app/src/app/cli/000077500000000000000000000000001434616504300215175ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/app/cli/Cli.cpp000066400000000000000000000500161434616504300227340ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "config.h" #include "app/App.hpp" #include "components/calls/CallsListModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/Utils.hpp" #include "Cli.hpp" // ============================================================================= using namespace std; // ============================================================================= // API. // ============================================================================= static void cliShow (QHash &args) { App *app = App::getInstance(); if( args.size() > 0){ app->processArguments(args); app->initContentApp(); } app->smartShowWindow(app->getMainWindow()); } static void cliCall (QHash &args) { QString addressToCall = args["sip-address"]; if(args.size() > 1){// Call with options App *app = App::getInstance(); args["call"] = args["sip-address"];// Swap cli def to parser args.remove("sip-address"); app->processArguments(args); app->initContentApp(); }else CoreManager::getInstance()->getCallsListModel()->launchAudioCall(args["sip-address"], ""); } static void cliBye (QHash &args) { auto currentCall = CoreManager::getInstance()->getCore()->getCurrentCall(); if(args.size() > 0) { if( args["sip-address"] == "*")// Call with options CoreManager::getInstance()->getCallsListModel()->terminateAllCalls(); else if( args["sip-address"] == ""){ if(currentCall) currentCall->terminate(); }else CoreManager::getInstance()->getCallsListModel()->terminateCall(args["sip-address"]); }else if(currentCall){ currentCall->terminate(); } } static void cliJoinConference (QHash &args) { const QString sipAddress = args.take("sip-address"); CoreManager *coreManager = CoreManager::getInstance(); const shared_ptr core = coreManager->getCore(); { shared_ptr address = core->createPrimaryContactParsed(); address->setDisplayName(Utils::appStringToCoreString(args.take("display-name"))); core->setPrimaryContact(address->asString()); } args["method"] = QStringLiteral("join-conference"); coreManager->getCallsListModel()->launchAudioCall(sipAddress, "", args); } static void cliJoinConferenceAs (QHash &args) { const QString fromSipAddress = args.take("guest-sip-address"); const QString toSipAddress = args.take("sip-address"); CoreManager *coreManager = CoreManager::getInstance(); /*shared_ptr proxyConfig = coreManager->getCore()->getDefaultProxyConfig(); if (!proxyConfig) { qWarning() << QStringLiteral("You have no proxy config."); return; } const shared_ptr currentSipAddress = proxyConfig->getIdentityAddress(); */ shared_ptr account = coreManager->getCore()->getDefaultAccount(); if (!account) { qWarning() << QStringLiteral("You have no account."); return; } const shared_ptr currentSipAddress = account->getParams()->getIdentityAddress(); const shared_ptr askedSipAddress = linphone::Factory::get()->createAddress( Utils::appStringToCoreString(fromSipAddress) ); if (!currentSipAddress->weakEqual(askedSipAddress)) { qWarning() << QStringLiteral("Guest sip address `%1` doesn't match with default account.") .arg(fromSipAddress); return; } args["method"] = QStringLiteral("join-conference"); coreManager->getCallsListModel()->launchAudioCall(toSipAddress, "", args); } static void cliInitiateConference (QHash &args) { shared_ptr core = CoreManager::getInstance()->getCore(); // Check identity. { shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(args["sip-address"])); if (!address || address->getUsername().empty()) { qWarning() << QStringLiteral("Unable to parse invalid sip address."); return; } address->clean(); shared_ptr account = core->getDefaultAccount(); if (!account) { qWarning() << QStringLiteral("Not connected to an account"); return; } if (!account->getParams()->getIdentityAddress()->weakEqual(address)) { qWarning() << QStringLiteral("Received different sip address from identity : `%1 != %2`.") .arg(Utils::coreStringToAppString(account->getParams()->getIdentityAddress()->asString())) .arg(Utils::coreStringToAppString(address->asString())); return; } } shared_ptr conference = core->getConference(); const QString id = args["conference-id"]; auto updateCallsWindow = []() { QQuickWindow *callsWindow = App::getInstance()->getCallsWindow(); if (!callsWindow) return; // TODO: Set the view to the "waiting call view". if (CoreManager::getInstance()->getSettingsModel()->getKeepCallsWindowInBackground()) { if (!callsWindow->isVisible()) callsWindow->showMinimized(); } else App::smartShowWindow(callsWindow); }; if (conference) { if (conference->getId() == Utils::appStringToCoreString(id)) { qInfo() << QStringLiteral("Conference `%1` already exists.").arg(id); updateCallsWindow(); return; } qInfo() << QStringLiteral("Remove existing conference with id: `%1`.") .arg(Utils::coreStringToAppString(conference->getId())); core->terminateConference(); } qInfo() << QStringLiteral("Create conference with id: `%1`.").arg(id); auto confParameters = core->createConferenceParams(conference); confParameters->enableVideo(false);// Video is not yet fully supported by the application in conference conference = core->createConferenceWithParams(confParameters); conference->setId(Utils::appStringToCoreString(id)); if (core->enterConference() == -1) { qWarning() << QStringLiteral("Unable to join created conference: `%1`.").arg(id); return; } updateCallsWindow(); } // ============================================================================= // Helpers. // ============================================================================= static QString splitWord (QString word, int &curPos, const int lineLength, const QString &padding) { QString out; out += word.mid(0, lineLength - curPos) + "\n" + padding; curPos = padding.length(); word = word.mid(lineLength - curPos); while (word.length() > lineLength - curPos) { out += word.mid(0, lineLength - curPos); word = word.mid(lineLength - curPos); out += "\n" + padding; } out += word; curPos = word.length() + padding.length(); return out; } static QString indentedWord (QString word, int &curPos, const int lineLength, const QString &padding) { QString out; if (curPos + word.length() > lineLength) { if (padding.length() + word.length() > lineLength) { out += splitWord(word, curPos, lineLength, padding); } else { out += QStringLiteral("\n"); out += padding + word; curPos = padding.length(); } } else { out += word; curPos += word.length(); } return out; } static string multilineIndent (const QString &str, int indentationNumber = 0) { constexpr int lineLength(80); static const QRegExp spaceRegexp("(\\s)"); const QString padding(indentationNumber * 2, ' '); QString out = padding; int indentedTextCurPos = padding.length(); int spacePos = 0; int wordPos = spacePos; QString word; while ((spacePos = spaceRegexp.indexIn(str, spacePos)) != -1) { word = str.mid(wordPos, spacePos - wordPos); out += indentedWord(word, indentedTextCurPos, lineLength, padding); switch (str[spacePos].unicode()) { case '\n': out += "\n" + padding; indentedTextCurPos = padding.length(); break; case '\t': // TAB as space. case ' ': if (indentedTextCurPos == lineLength) { out += "\n" + padding; indentedTextCurPos = padding.length(); } else { out += " "; indentedTextCurPos += 1; } break; default: break; } spacePos += 1; wordPos = spacePos; } word = str.mid(wordPos); out += indentedWord(word, indentedTextCurPos, lineLength, padding); out += "\n"; return Utils::appStringToCoreString(out); } // ============================================================================= Cli::Command::Command ( const QString &functionName, const char *functionDescription, Cli::Function function, const QHash &argsScheme, const bool &genericArguments ) : mFunctionName(functionName), mFunctionDescription(functionDescription), mFunction(function), mArgsScheme(argsScheme), mGenericArguments(genericArguments) {} void Cli::Command::execute (QHash &args) const { if(!mGenericArguments){// Check arguments validity. for (const auto &argName : args.keys()) { if (!mArgsScheme.contains(argName)) { qWarning() << QStringLiteral("Command with invalid argument: `%1 (%2)`.") .arg(mFunctionName).arg(argName); return; } } } // Check missing arguments. for (const auto &argName : mArgsScheme.keys()) { if (!mArgsScheme[argName].isOptional && (!args.contains(argName) || args[argName].isEmpty())) { qWarning() << QStringLiteral("Missing argument for command: `%1 (%2)`.") .arg(mFunctionName).arg(argName); return; } } // Execute! App *app = App::getInstance(); if (app->isOpened()) { qInfo() << QStringLiteral("Execute command:") << args; (*mFunction)(args); } else { Function f = mFunction; QObject * context = new QObject(); QObject::connect(app, &App::opened, [f, args, context]()mutable { if(context){ delete context; context = nullptr; qInfo() << QStringLiteral("Execute deferred command:") << args; QHash fuckConst = args; (*f)(fuckConst); } } ); } } void Cli::Command::executeUri (const shared_ptr &address) const { QHash args; QString qAddress = Utils::coreStringToAppString(address->asString()); QUrl url(qAddress); QString query = url.query(); QStringList parameters = query.split('&'); for(int i = 0 ; i < parameters.size() ; ++i){ QStringList parameter = parameters[i].split('='); if( parameter[0]!="" && parameter[0] != "method"){ if(parameter.size() > 1) args[parameter[0]] = QByteArray::fromBase64(parameter[1].toUtf8() ); else args[parameter[0]] = ""; } } // TODO: check if there is too much headers. for (const auto &argName : mArgsScheme.keys()) { const string header = address->getHeader(Utils::appStringToCoreString(argName)); args[argName] = QByteArray::fromBase64(QByteArray(header.c_str(), int(header.length()))); } address->clean(); args["sip-address"] = Utils::coreStringToAppString(address->asStringUriOnly()); execute(args); } // pUrl can be `anytoken?p1=x&p2=y` or `p1=x&p2=y`. It will only use p1 and p2 void Cli::Command::executeUrl (const QString &pUrl) const { QHash args; QStringList urlParts = pUrl.split('?'); QString query = (urlParts.size()>1?urlParts[1]:urlParts[0]); QString authority = (urlParts.size()>1 && urlParts[0].contains(':')?urlParts[0].split(':')[1]:""); QStringList parameters = query.split('&'); for(int i = 0 ; i < parameters.size() ; ++i){ QStringList parameter = parameters[i].split('='); if( parameter[0] != "method"){ if(parameter.size() > 1) args[parameter[0]] = QByteArray::fromBase64(parameter[1].toUtf8() ); else args[parameter[0]] = ""; } } if(!authority.isEmpty()) args["sip-address"] = authority; execute(args); } QString Cli::Command::getFunctionSyntax () const { QString functionSyntax; functionSyntax += QStringLiteral("\""); functionSyntax += mFunctionName; for (auto &argName : mArgsScheme.keys()){ functionSyntax += QStringLiteral(" "); functionSyntax += mArgsScheme[argName].isOptional ? QStringLiteral("[") : QStringLiteral(""); functionSyntax += argName; functionSyntax += QStringLiteral("=<"); switch (mArgsScheme[argName].type) { case String: functionSyntax += QStringLiteral("str"); break; default: functionSyntax += QStringLiteral("value"); break; } functionSyntax += QString(">"); functionSyntax += mArgsScheme[argName].isOptional ? QStringLiteral("]") : QStringLiteral(""); } functionSyntax += QStringLiteral("\""); return functionSyntax; } // ============================================================================= // FIXME: Do not accept args without value like: cmd toto. // In the future `toto` could be a boolean argument. QRegExp Cli::mRegExpArgs("(?:(?:([\\w-]+)\\s*)=\\s*(?:\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"|([^\\s]+)\\s*))"); QRegExp Cli::mRegExpFunctionName("^\\s*([a-z-]+)\\s*"); QMap Cli::mCommands = { createCommand("show", QT_TR_NOOP("showFunctionDescription"), cliShow, QHash(), true), createCommand("call", QT_TR_NOOP("callFunctionDescription"), cliCall, { { "sip-address", {} } }, true), createCommand("initiate-conference", QT_TR_NOOP("initiateConferenceFunctionDescription"), cliInitiateConference, { { "sip-address", {} }, { "conference-id", {} } }), createCommand("join-conference", QT_TR_NOOP("joinConferenceFunctionDescription"), cliJoinConference, { { "sip-address", {} }, { "conference-id", {} }, { "display-name", {} } }), createCommand("join-conference-as", QT_TR_NOOP("joinConferenceAsFunctionDescription"), cliJoinConferenceAs, { { "sip-address", {} }, { "conference-id", {} }, { "guest-sip-address", {} } }), createCommand("bye", QT_TR_NOOP("byeFunctionDescription"), cliBye, QHash(), true), }; // ----------------------------------------------------------------------------- /* string Cli::getScheme(const QString& address){ QStringList tempSipAddress = address->split(':'); if( tempSipAddress.size() > 0) return tempSipAddress[0].toStdString(); else return ""; } bool Cli::changeScheme(QString * address){ QStringList tempSipAddress = address->split(':'); string scheme; bool ok = false; if(tempSipAddress.size() > 1) { scheme = tempSipAddress[0].toStdString(); for (const string &validScheme : { string("sip"), "sip-"+string(EXECUTABLE_NAME), string("sips"), "sips-"+string(EXECUTABLE_NAME), string("tel"), string("callto"), string(EXECUTABLE_NAME)+ "-config" }) if (scheme == validScheme) ok = true; if( !ok){ qWarning() << QStringLiteral("Not a valid uri: `%1` Unsupported scheme: `%2`.").arg(*address).arg(Utils::coreStringToAppString(scheme)); }else{ tempSipAddress[0] = "sip";// In order to pass bellesip parsing. *address = tempSipAddress.join(':'); } } return ok; } */ void Cli::executeCommand (const QString &command, CommandFormat *format) { // Detect if command is a CLI by testing commands const QString &functionName = parseFunctionName(command); if(!functionName.isEmpty()){// It is a CLI qInfo() << QStringLiteral("Detecting cli command: `%1`...").arg(command); QHash args = parseArgs(command); mCommands[functionName].execute(args); if (format) *format = CliFormat; return; }else{// It is a URI QStringList tempSipAddress = command.split(':'); string scheme="sip"; QString transformedCommand; // In order to pass bellesip parsing, set scheme to 'sip:'. if( tempSipAddress.size() == 1){ transformedCommand = "sip:"+command; }else{ scheme = tempSipAddress[0].toStdString(); bool ok = false; for (const string &validScheme : { string("sip"), "sip-"+string(EXECUTABLE_NAME), string("sips"), "sips-"+string(EXECUTABLE_NAME), string("tel"), string("callto"), string(EXECUTABLE_NAME)+ "-config" }) if (scheme == validScheme) ok = true; if( !ok){ qWarning() << QStringLiteral("Not a valid URI: `%1` Unsupported scheme: `%2`.").arg(command).arg(Utils::coreStringToAppString(scheme)); return; } tempSipAddress[0] = "sip"; transformedCommand = tempSipAddress.join(':'); } if( scheme == string(EXECUTABLE_NAME)+"-config" ){ QHash args = parseArgs(command); if(args.contains("fetch-config")) args["fetch-config"] = QByteArray::fromBase64(args["fetch-config"].toUtf8() ); else { QUrl url(command); url.setScheme("https"); args["fetch-config"] = url.toString(); } if (format) *format = CliFormat; mCommands["show"].execute(args); }else{ shared_ptr address = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(transformedCommand));// Test if command is an address if (format) *format = UriFormat; qInfo() << QStringLiteral("Detecting URI command: `%1`...").arg(command); QString functionName; if( address) { functionName = Utils::coreStringToAppString(address->getHeader("method")).isEmpty() ? QStringLiteral("call") : Utils::coreStringToAppString(address->getHeader("method")); }else{ QStringList fields = command.split('?'); if(fields.size() >1){ fields = fields[1].split('&'); for(int i = 0 ; i < fields.size() && functionName.isEmpty(); ++i){ QStringList data = fields[i].split('='); if( data[0] == "method" && data.size() >1) functionName = data[1]; } if(functionName.isEmpty()) functionName = "call"; } } functionName = functionName.toLower(); if( functionName.isEmpty()){ qWarning() << QStringLiteral("There is no method set in `%1`.").arg(command); return; }else if( !mCommands.contains(functionName)) { qWarning() << QStringLiteral("This command doesn't exist: `%1`.").arg(functionName); return; } if(address) mCommands[functionName].executeUri(address); else mCommands[functionName].executeUrl(command); } } } void Cli::showHelp () { cout << multilineIndent(tr("appCliDescription").arg(APPLICATION_NAME), 0) << endl << "Usage: " << endl << multilineIndent(tr("uriCommandLineSyntax").arg(EXECUTABLE_NAME), 0) << multilineIndent(tr("cliCommandLineSyntax").arg(EXECUTABLE_NAME), 0) << endl << multilineIndent(tr("commandsName")) << endl; for (const auto &method : mCommands.keys()) cout << multilineIndent(mCommands[method].getFunctionSyntax(), 1) << multilineIndent(tr(mCommands[method].getFunctionDescription()), 2) << endl; } // ----------------------------------------------------------------------------- pair Cli::createCommand ( const QString &functionName, const char *functionDescription, Function function, const QHash &argsScheme, const bool &genericArguments ) { return { functionName.toLower(), Cli::Command(functionName.toLower(), functionDescription, function, argsScheme, genericArguments) }; } // ----------------------------------------------------------------------------- QString Cli::parseFunctionName (const QString &command) { mRegExpFunctionName.indexIn(command.toLower()); if (mRegExpFunctionName.pos(1) == -1) { qWarning() << QStringLiteral("Unable to parse function name of command: `%1`.").arg(command); return QString(""); } const QStringList texts = mRegExpFunctionName.capturedTexts(); const QString functionName = texts[1]; if (!mCommands.contains(functionName)) { qWarning() << QStringLiteral("This command doesn't exist: `%1`.").arg(functionName); return QString(""); } return functionName; } QHash Cli::parseArgs (const QString &command) { QHash args; int pos = 0; while ((pos = mRegExpArgs.indexIn(command.toLower(), pos)) != -1) { pos += mRegExpArgs.matchedLength(); args[mRegExpArgs.cap(1)] = (mRegExpArgs.cap(2).isEmpty() ? mRegExpArgs.cap(3) : mRegExpArgs.cap(2)); } return args; } linphone-desktop-5.0.2/linphone-app/src/app/cli/Cli.hpp000066400000000000000000000056401434616504300227440ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CLI_H_ #define CLI_H_ #include #include #include #include // ============================================================================= namespace linphone { class Address; } class Cli : public QObject { Q_OBJECT; typedef void (*Function)(QHash &); enum ArgumentType { String }; struct Argument { Argument (ArgumentType type = String, bool isOptional = false) { this->type = type; this->isOptional = isOptional; } ArgumentType type; bool isOptional; }; class Command { public: Command () = default; Command ( const QString &functionName, const char *functionDescription, Function function, const QHash &argsScheme, const bool &genericArguments=false ); void execute (QHash &args) const; void executeUri (const std::shared_ptr &address) const; void executeUrl (const QString &url) const; const char *getFunctionDescription () const { return mFunctionDescription; } QString getFunctionSyntax () const ; private: QString mFunctionName; const char *mFunctionDescription; Function mFunction = nullptr; QHash mArgsScheme; bool mGenericArguments=false;// Used to avoid check on arguments }; public: enum CommandFormat { UnknownFormat, CliFormat, UriFormat, // Parameters are in base64 UrlFormat }; static void executeCommand (const QString &command, CommandFormat *format = nullptr); static void showHelp (); private: Cli (); static std::pair createCommand ( const QString &functionName, const char *functionDescription, Function function, const QHash &argsScheme = QHash(), const bool &genericArguments=false ); static QString parseFunctionName (const QString &command); static QHash parseArgs (const QString &command); static QMap mCommands; static QRegExp mRegExpArgs; static QRegExp mRegExpFunctionName; }; #endif // CLI_H_ linphone-desktop-5.0.2/linphone-app/src/app/logger/000077500000000000000000000000001434616504300222275ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/app/logger/Logger.cpp000066400000000000000000000154121434616504300241550ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include #include "config.h" #include "components/settings/SettingsModel.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "Logger.hpp" // ============================================================================= #if defined(__linux__) || defined(__APPLE__) #define BLUE "\x1B[1;34m" #define YELLOW "\x1B[1;33m" #define GREEN "\x1B[1;32m" #define PURPLE "\x1B[1;35m" #define RED "\x1B[1;31m" #define RESET "\x1B[0m" #else #define BLUE "" #define YELLOW "" #define GREEN "" #define PURPLE "" #define RED "" #define RESET "" #endif // if defined(__linux__) || defined(__APPLE__) using namespace std; QMutex Logger::mMutex; Logger *Logger::mInstance; // ----------------------------------------------------------------------------- static inline QByteArray getFormattedCurrentTime () { return QDateTime::currentDateTime().toString("HH:mm:ss:zzz").toLocal8Bit(); } // ----------------------------------------------------------------------------- class LinphoneLogger : public linphone::LoggingServiceListener { public: LinphoneLogger (const Logger *logger) : mLogger(logger) {} private: void onLogMessageWritten ( const shared_ptr &, const string &domain, linphone::LogLevel level, const string &message ) override { if (!mLogger->isVerbose()) return; using LogLevel = linphone::LogLevel; const char *format; switch (level) { case LogLevel::Debug: format = GREEN "[%s][Debug]" YELLOW "Core:%s: " RESET "%s\n"; break; case LogLevel::Trace: format = BLUE "[%s][Trace]" YELLOW "Core:%s: " RESET "%s\n"; break; case LogLevel::Message: format = BLUE "[%s][Info]" YELLOW "Core:%s: " RESET "%s\n"; break; case LogLevel::Warning: format = RED "[%s][Warning]" YELLOW "Core:%s: " RESET "%s\n"; break; case LogLevel::Error: format = RED "[%s][Error]" YELLOW "Core:%s: " RESET "%s\n"; break; case LogLevel::Fatal: format = RED "[%s][Fatal]" YELLOW "Core:%s: " RESET "%s\n"; break; } fprintf( stderr, format, getFormattedCurrentTime().constData(), domain.empty() ? domain.c_str() : EXECUTABLE_NAME, message.c_str() ); if (level == LogLevel::Fatal) terminate(); }; const Logger *mLogger; }; // ----------------------------------------------------------------------------- void Logger::log (QtMsgType type, const QMessageLogContext &context, const QString &msg) { const char *format; BctbxLogLevel level; if (type == QtDebugMsg) { format = GREEN "[%s][%p][Debug]" PURPLE "%s" RESET "%s\n"; level = BCTBX_LOG_DEBUG; } else if (type == QtInfoMsg) { format = BLUE "[%s][%p][Info]" PURPLE "%s" RESET "%s\n"; level = BCTBX_LOG_MESSAGE; } else if (type == QtWarningMsg) { format = RED "[%s][%p][Warning]" PURPLE "%s" RESET "%s\n"; level = BCTBX_LOG_WARNING; } else if (type == QtCriticalMsg) { format = RED "[%s][%p][Critical]" PURPLE "%s" RESET "%s\n"; level = BCTBX_LOG_ERROR; } else if (type == QtFatalMsg) { format = RED "[%s][%p][Fatal]" PURPLE "%s" RESET "%s\n"; level = BCTBX_LOG_FATAL; } else return; const char *contextStr = ""; #ifdef QT_MESSAGELOGCONTEXT QByteArray contextArr; { const char *file = context.file; const char *pos = file ? Utils::rstrstr(file, Constants::SrcPattern) : file; contextArr = QStringLiteral("%1:%2: ") .arg(pos ? pos + sizeof(Constants::SrcPattern) - 1 : file) .arg(context.line) .toLocal8Bit(); contextStr = contextArr.constData(); } #else Q_UNUSED(context); #endif // ifdef QT_MESSAGELOGCONTEXT QByteArray localMsg = msg.toLocal8Bit(); QByteArray dateTime = getFormattedCurrentTime(); mMutex.lock(); fprintf(stdout, format, dateTime.constData(), QThread::currentThread(), contextStr, localMsg.constData()); fflush(stdout); if( level == BCTBX_LOG_FATAL) QMessageBox::critical(nullptr, "Linphone will crash", msg); // Print an error message before sending msg to bctoolbox bctbx_log(Constants::QtDomain, level, "QT: %s%s", contextStr, localMsg.constData()); mMutex.unlock(); if (type == QtFatalMsg) terminate(); } // ----------------------------------------------------------------------------- void Logger::enable (bool status) { linphone::Core::enableLogCollection( status ? linphone::LogCollectionState::Enabled : linphone::LogCollectionState::Disabled ); } void Logger::init (const shared_ptr &config) { if (mInstance) return; QLoggingCategory::setFilterRules("qt.qml.connections.warning=false"); const QString folder = SettingsModel::getLogsFolder(config); Q_ASSERT(!folder.isEmpty()); mInstance = new Logger(); qInstallMessageHandler(Logger::log); { shared_ptr loggingService = mInstance->mLoggingService = linphone::LoggingService::get(); loggingService->setDomain(Constants::QtDomain); loggingService->setLogLevel(linphone::LogLevel::Message); loggingService->addListener(make_shared(mInstance)); #ifdef _WIN32 loggingService->enableStackTraceDumps(true); #endif } linphone::Core::setLogCollectionPrefix(EXECUTABLE_NAME); linphone::Core::setLogCollectionPath(Utils::appStringToCoreString(folder)); linphone::Core::setLogCollectionMaxFileSize(Constants::MaxLogsCollectionSize); mInstance->enable(SettingsModel::getLogsEnabled(config)); } QString Logger::getLogText()const{ QDir path = QString::fromStdString(linphone::Core::getLogCollectionPath()); QString prefix = QString::fromStdString(linphone::Core::getLogCollectionPrefix()); auto files = path.entryInfoList(QStringList(prefix+"*.log"), QDir::Files | QDir::NoSymLinks | QDir::Readable, QDir::Time | QDir::Reversed); QString result; for(auto fileInfo : files){ QFile file(fileInfo.filePath()); if (file.open(QIODevice::ReadOnly)) { QByteArray arr = file.readAll(); result += QString::fromLatin1(arr); file.close(); } } return result; } linphone-desktop-5.0.2/linphone-app/src/app/logger/Logger.hpp000066400000000000000000000031461434616504300241630ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef LOGGER_H_ #define LOGGER_H_ #include #include // ============================================================================= namespace linphone { class Config; class LoggingService; } class Logger { public: bool isVerbose () const { return mVerbose; } void setVerbose (bool verbose) { mVerbose = verbose; } void enable (bool status); QString getLogText()const; static void init (const std::shared_ptr &config); static Logger *getInstance () { return mInstance; } private: Logger () = default; static void log (QtMsgType type, const QMessageLogContext &context, const QString &msg); bool mVerbose = false; static QMutex mMutex; static Logger *mInstance; std::shared_ptr mLoggingService; }; #endif // LOGGER_H_ linphone-desktop-5.0.2/linphone-app/src/app/main.cpp000066400000000000000000000046641434616504300224120ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "AppController.hpp" #include #ifdef QT_QML_DEBUG #include #endif #include #ifdef _WIN32 #include FILE * gStream = NULL; #endif #include "components/core/CoreManager.hpp" // ============================================================================= void cleanStream(){ #ifdef _WIN32 if(gStream) { fflush(stdout); fflush(stderr); fclose(gStream); } #endif } int main (int argc, char *argv[]) { #ifdef __APPLE__ qputenv("QT_ENABLE_GLYPH_CACHE_WORKAROUND", "1"); // On Mac, set this workaround to avoid glitches on M1, because of https://bugreports.qt.io/browse/QTBUG-89379 #elif defined _WIN32 // log in console only if launched from console if (AttachConsole(ATTACH_PARENT_PROCESS)) { freopen_s(&gStream, "CONOUT$", "w", stdout); freopen_s(&gStream, "CONOUT$", "w", stderr); } #endif AppController controller(argc, argv); #ifdef QT_QML_DEBUG QQmlDebuggingEnabler enabler; #endif //QLoggingCategory::setFilterRules("*.debug=true;qml=false"); App *app = controller.getApp(); if (app->isSecondary()) { qInfo() << QStringLiteral("Running secondary app success. Kill it now."); cleanStream(); return EXIT_SUCCESS; } qInfo() << QStringLiteral("Running app..."); int ret; do { app->initContentApp(); ret = app->exec(); } while (ret == App::RestartCode); controller.stopApp(); // Stopping app before core to let time to GUI to process needed items from linphone. if( CoreManager::getInstance()){ auto core = CoreManager::getInstance()->getCore(); if(core && core->getGlobalState() == linphone::GlobalState::On) core->stop(); } cleanStream(); return ret; } linphone-desktop-5.0.2/linphone-app/src/app/paths/000077500000000000000000000000001434616504300220675ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/app/paths/Paths.cpp000066400000000000000000000314761434616504300236650ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include "config.h" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "Paths.hpp" // ============================================================================= using namespace std; static inline bool dirPathExists (const QString &path) { QDir dir(path); return dir.exists(); } static inline bool filePathExists (const QString &path, const bool& isWritable) { QFileInfo info(path); if (!dirPathExists(info.path())) return false; if( isWritable && !info.isWritable()) return false; QFile file(path); return file.exists(); } static inline void ensureDirPathExists (const QString &path) { QDir dir(path); if (!dir.exists() && !dir.mkpath(path)) qFatal("Unable to access at directory: `%s`", Utils::appStringToCoreString(path).c_str()); } static inline void ensureFilePathExists (const QString &path) { QFileInfo info(path); ensureDirPathExists(info.path()); QFile file(path); if (!file.exists() && !file.open(QIODevice::ReadWrite)) qFatal("Unable to access at path: `%s`", Utils::appStringToCoreString(path).c_str()); } static inline string getReadableDirPath (const QString &dirname) { return Utils::appStringToCoreString(QDir::toNativeSeparators(dirname)); } static inline string getWritableDirPath (const QString &dirname) { ensureDirPathExists(dirname); return getReadableDirPath(dirname); } static inline string getReadableFilePath (const QString &filename) { return Utils::appStringToCoreString(QDir::toNativeSeparators(filename)); } static inline string getWritableFilePath (const QString &filename) { ensureFilePathExists(filename); return getReadableFilePath(filename); } // ----------------------------------------------------------------------------- // On Windows or Linux, the folders of the application are : // bin/linphone // lib/ // lib64/ // plugins/ // share/ // But in some cases, it can be : // /linphone // lib/ // lib64/ // share/ // On Mac, we have : // Contents/ // Frameworks/ // MacOs/linphone // Plugins/ // Resources/ // share/ static inline QDir getAppPackageDir () { QDir dir(QCoreApplication::applicationDirPath()); if (dir.dirName() == QLatin1String("MacOS")) { dir.cdUp(); } else if( !dir.exists("lib") && !dir.exists("lib64")){// Check if these folders are in the current path dir.cdUp(); if(!dir.exists("lib") && !dir.exists("lib64") && !dir.exists("plugins")) qWarning() <<"The application's location is not correct: You have to put your 'bin/' folder next to 'lib/' or 'plugins/' folder."; } return dir; } static inline QString getAppPackageDataDirPath() { QDir dir = getAppPackageDir(); #ifdef __APPLE__ if (!dir.cd("Resources")) { dir.mkdir("Resources"); dir.cd("Resources"); } #endif if (!dir.cd("share")) { dir.mkdir("share"); dir.cd("share"); } return dir.absolutePath(); } static inline QString getAppPackageMsPluginsDirPath () { QDir dir = getAppPackageDir(); dir.cd(MSPLUGINS_DIR); return dir.absolutePath(); } static inline QString getAppPackagePluginsDirPath () { return getAppPackageDir().absolutePath() + Constants::PathPlugins; } static inline QString getAppAssistantConfigDirPath () { return getAppPackageDataDirPath() + Constants::PathAssistantConfig; } static inline QString getAppConfigFilePath () { return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + Constants::PathConfig; } static inline QString getAppCallHistoryFilePath () { return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathCallHistoryList; } static inline QString getAppFactoryConfigFilePath () { return getAppPackageDataDirPath() + Constants::PathFactoryConfig; } static inline QString getAppRootCaFilePath () { QString rootca = getAppPackageDataDirPath() + Constants::PathRootCa; if(Paths::filePathExists(rootca.toStdString())){// Packaged return rootca; } return ""; } static inline QString getAppFriendsFilePath () { return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathFriendsList; } static inline QString getAppMessageHistoryFilePath () { return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathMessageHistoryList; } static inline QString getAppPluginsDirPath () { return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)+ Constants::PathPlugins; } // ----------------------------------------------------------------------------- bool Paths::filePathExists (const string &path, const bool isWritable) { return filePathExists(Utils::coreStringToAppString(path), isWritable); } // ----------------------------------------------------------------------------- string Paths::getAppLocalDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) +"/"); } string Paths::getAssistantConfigDirPath () { return getReadableDirPath(getAppAssistantConfigDirPath()); } string Paths::getAvatarsDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathAvatars); } string Paths::getCallHistoryFilePath () { return getWritableFilePath(getAppCallHistoryFilePath()); } string Paths::getCapturesDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + Constants::PathCaptures); } string Paths::getCodecsDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathCodecs); } string Paths::getConfigDirPath (bool writable) { return writable ? getWritableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)+QDir::separator()) : getReadableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)+QDir::separator()); } string Paths::getConfigFilePath (const QString &configPath, bool writable) { QString path; if( !configPath.isEmpty()){ QFileInfo file(configPath); if( !writable && (!file.exists() || !file.isFile())){// This file cannot be found. Check if it exists in standard folder QString defaultConfigPath = Utils::coreStringToAppString(getConfigDirPath(false)); file = QFileInfo(defaultConfigPath+QDir::separator()+configPath); if( !file.exists() || !file.isFile()) path = ""; else path = file.absoluteFilePath(); }else path = file.absoluteFilePath(); }else path = getAppConfigFilePath(); return writable ? getWritableFilePath(path) : getReadableFilePath(path); } std::string Paths::getDatabaseFilePath (){ return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) + Constants::PathDatabase; } string Paths::getFactoryConfigFilePath () { return getReadableFilePath(getAppFactoryConfigFilePath()); } string Paths::getFriendsListFilePath () { return getWritableFilePath(getAppFriendsFilePath()); } string Paths::getDownloadDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QDir::separator()); } std::string Paths::getLimeDatabasePath (){ return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) + Constants::PathLimeDatabase; } string Paths::getLogsDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathLogs); } string Paths::getMessageHistoryFilePath () { return getReadableFilePath(getAppMessageHistoryFilePath());// No need to ensure that the file exists as this DB is deprecated } string Paths::getPackageDataDirPath () { return getReadableDirPath(getAppPackageDataDirPath() + Constants::PathData); } string Paths::getPackageMsPluginsDirPath () { return getReadableDirPath(getAppPackageMsPluginsDirPath()); } string Paths::getPackagePluginsAppDirPath () { return getReadableDirPath(getAppPackagePluginsDirPath() + Constants::PathPluginsApp); } std::string Paths::getPackageSoundsResourcesDirPath (){ return getReadableDirPath(getAppPackageDataDirPath() + Constants::PathSounds); } std::string Paths::getPackageTopDirPath (){ return getReadableDirPath(getAppPackageDataDirPath()); } string Paths::getPluginsAppDirPath () { return getWritableDirPath(getAppPluginsDirPath() + Constants::PathPluginsApp); } QStringList Paths::getPluginsAppFolders() { QStringList pluginPaths; pluginPaths << Utils::coreStringToAppString(Paths::getPluginsAppDirPath()); pluginPaths << Utils::coreStringToAppString(Paths::getPackagePluginsAppDirPath()); return pluginPaths; } string Paths::getRootCaFilePath () { return getReadableFilePath(getAppRootCaFilePath()); } string Paths::getThumbnailsDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathThumbnails); } string Paths::getToolsDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathTools); } string Paths::getUserCertificatesDirPath () { return getWritableDirPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathUserCertificates); } string Paths::getZrtpSecretsFilePath () { return getWritableFilePath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + Constants::PathZrtpSecrets); } // ----------------------------------------------------------------------------- static void migrateFile (const QString &oldPath, const QString &newPath) { QFileInfo info(newPath); ensureDirPathExists(info.path()); if (QFile::copy(oldPath, newPath)) { QFile::remove(oldPath); qInfo() << "Migrated" << oldPath << "to" << newPath; } else { qWarning() << "Failed migration of" << oldPath << "to" << newPath; } } static void migrateConfigurationFile (const QString &oldPath, const QString &newPath) { QFileInfo info(newPath); ensureDirPathExists(info.path()); if (QFile::copy(oldPath, newPath)) { QFile oldFile(oldPath); if (oldFile.open(QIODevice::WriteOnly)) { QTextStream stream(&oldFile); stream << "This file has been migrated to " << newPath; } QFile::setPermissions(oldPath, QFileDevice::ReadOwner); qInfo() << "Migrated" << oldPath << "to" << newPath; } else { qWarning() << "Failed migration of" << oldPath << "to" << newPath; } } void migrateFlatpakVersionFiles(){ #ifdef Q_OS_LINUX if(!filePathExists(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) +Constants::PathDatabase, true)){ // Copy all files if linphone.db doesn't exist QString flatpakPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation)+"/.var/app/" APPLICATION_ID "/data/" EXECUTABLE_NAME; if( QDir().exists(flatpakPath)){ qInfo() << "Migrating data from Flatpak."; Utils::copyDir(flatpakPath, QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); } } #endif } void migrateGTKVersionFiles(){ if( EXECUTABLE_NAME == "linphone"){ QString newPath = getAppConfigFilePath(); QString oldBaseDir = QSysInfo::productType() == QLatin1String("windows") ? QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) : QStandardPaths::writableLocation(QStandardPaths::HomeLocation); QString oldPath = oldBaseDir + "/.linphonerc"; if (!filePathExists(newPath, false) && filePathExists(oldPath, false)) migrateConfigurationFile(oldPath, newPath); newPath = getAppCallHistoryFilePath(); oldPath = oldBaseDir + "/.linphone-call-history.db"; if (!filePathExists(newPath, false) && filePathExists(oldPath, false)) migrateFile(oldPath, newPath); newPath = getAppFriendsFilePath(); oldPath = oldBaseDir + "/.linphone-friends.db"; if (!filePathExists(newPath, false) && filePathExists(oldPath, false)) migrateFile(oldPath, newPath); newPath = getAppMessageHistoryFilePath(); oldPath = oldBaseDir + "/.linphone-history.db"; if (!filePathExists(newPath, false) && filePathExists(oldPath, false)) migrateFile(oldPath, newPath); } } void Paths::migrate () { migrateFlatpakVersionFiles(); // First, check Flatpak version as it is the earlier version migrateGTKVersionFiles();// Then check old version for migration } linphone-desktop-5.0.2/linphone-app/src/app/paths/Paths.hpp000066400000000000000000000042651434616504300236660ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PATHS_H_ #define PATHS_H_ #include // ============================================================================= namespace Paths { bool filePathExists (const std::string &path, const bool isWritable = false); std::string getAppLocalDirPath (); std::string getAssistantConfigDirPath (); std::string getAvatarsDirPath (); std::string getCallHistoryFilePath (); std::string getCapturesDirPath (); std::string getCodecsDirPath (); std::string getConfigDirPath (bool writable = true); std::string getConfigFilePath (const QString &configPath = QString(), bool writable = true); std::string getDatabaseFilePath (); std::string getDownloadDirPath (); std::string getFactoryConfigFilePath (); std::string getFriendsListFilePath (); std::string getLimeDatabasePath (); std::string getLogsDirPath (); std::string getMessageHistoryFilePath (); std::string getPackageDataDirPath (); std::string getPackageMsPluginsDirPath (); std::string getPackagePluginsAppDirPath (); std::string getPackageSoundsResourcesDirPath (); std::string getPackageTopDirPath (); std::string getPluginsAppDirPath (); QStringList getPluginsAppFolders(); std::string getRootCaFilePath (); std::string getThumbnailsDirPath (); std::string getToolsDirPath (); std::string getUserCertificatesDirPath (); std::string getZrtpDataFilePath (); std::string getZrtpSecretsFilePath (); void migrate (); } #endif // PATHS_H_ linphone-desktop-5.0.2/linphone-app/src/app/providers/000077500000000000000000000000001434616504300227655ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/app/providers/AvatarProvider.cpp000066400000000000000000000026011434616504300264210ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "app/paths/Paths.hpp" #include "utils/Utils.hpp" #include "AvatarProvider.hpp" // ============================================================================= const QString AvatarProvider::ProviderId = "avatar"; AvatarProvider::AvatarProvider () : QQuickImageProvider( QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading ) { mAvatarsPath = Utils::coreStringToAppString(Paths::getAvatarsDirPath()); } QImage AvatarProvider::requestImage (const QString &id, QSize *size, const QSize &) { QImage image(mAvatarsPath + id); *size = image.size(); return image; } linphone-desktop-5.0.2/linphone-app/src/app/providers/AvatarProvider.hpp000066400000000000000000000023311434616504300264260ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef AVATAR_PROVIDER_H_ #define AVATAR_PROVIDER_H_ #include // ============================================================================= class AvatarProvider : public QQuickImageProvider { public: AvatarProvider (); QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override; static const QString ProviderId; private: QString mAvatarsPath; }; #endif // AVATAR_PROVIDER_H_ linphone-desktop-5.0.2/linphone-app/src/app/providers/ExternalImageProvider.cpp000066400000000000000000000025661434616504300277420ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "app/paths/Paths.hpp" #include "utils/Utils.hpp" #include "ExternalImageProvider.hpp" #include // ============================================================================= const QString ExternalImageProvider::ProviderId = "external"; ExternalImageProvider::ExternalImageProvider () : QQuickImageProvider( QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading ) { } QImage ExternalImageProvider::requestImage (const QString &id, QSize *size, const QSize &) { QImage image(Utils::getImage(id)); *size = image.size(); return image; } linphone-desktop-5.0.2/linphone-app/src/app/providers/ExternalImageProvider.hpp000066400000000000000000000023351434616504300277410ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef EXTERNAL_IMAGE_PROVIDER_H_ #define EXTERNAL_IMAGE_PROVIDER_H_ #include // ============================================================================= class ExternalImageProvider : public QQuickImageProvider { public: ExternalImageProvider (); QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override; static const QString ProviderId; }; #endif // EXTERNAL_IMAGE_PROVIDER_H_ linphone-desktop-5.0.2/linphone-app/src/app/providers/ImageProvider.cpp000066400000000000000000000235561434616504300262410ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include #include "app/App.hpp" #include "ImageProvider.hpp" #include "components/other/colors/ColorListModel.hpp" #include "components/other/colors/ColorModel.hpp" #include "components/other/images/ImageListModel.hpp" #include "components/other/images/ImageModel.hpp" #include "utils/Constants.hpp" // ============================================================================= using namespace std; static void removeAttribute (QXmlStreamAttributes &readerAttributes, const QString &name) { auto it = find_if(readerAttributes.cbegin(), readerAttributes.cend(), [&name](const QXmlStreamAttribute &attribute) { return name == attribute.name() && !attribute.prefix().length(); }); if (it != readerAttributes.cend()) readerAttributes.remove(int(distance(readerAttributes.cbegin(), it))); } static QByteArray buildByteArrayAttribute (const QByteArray &name, const QByteArray &value) { QByteArray attribute = name; attribute.append("=\""); attribute.append(value); attribute.append("\" "); return attribute; } static QByteArray parseFillAndStroke (QXmlStreamAttributes &readerAttributes, const ColorListModel *colors) { static QRegExp regex("^color-([^-]+)-(fill|stroke)$"); QByteArray attributes; for (const auto &classValue : readerAttributes.value("class").toLatin1().split(' ')) { regex.indexIn(classValue.trimmed()); if (Q_LIKELY(regex.pos() == -1)) continue; const QStringList list = regex.capturedTexts(); const QVariant colorValue = colors->getQmlData()->value(list[1]); if (Q_UNLIKELY(!colorValue.isValid())) { qWarning() << QStringLiteral("Color name `%1` does not exist.").arg(list[1]); continue; } removeAttribute(readerAttributes, list[2]); attributes.append(buildByteArrayAttribute(list[2].toLatin1(), colorValue.value()->getColor().name().toLatin1())); } return attributes; } static QByteArray parseStyle (QXmlStreamAttributes &readerAttributes, const ColorListModel *colors) { static QRegExp regex("^color-([^-]+)-style-(fill|stroke)$"); QByteArray attribute; QSet overrode; for (const auto &classValue : readerAttributes.value("class").toLatin1().split(' ')) { regex.indexIn(classValue.trimmed()); if (Q_LIKELY(regex.pos() == -1)) continue; const QStringList list = regex.capturedTexts(); overrode.insert(list[2]); const QVariant colorValue = colors->getQmlData()->value(list[1]); if (Q_UNLIKELY(!colorValue.isValid())) { qWarning() << QStringLiteral("Color name `%1` does not exist.").arg(list[1]); continue; } attribute.append(list[2].toLatin1()); attribute.append(":"); attribute.append(colorValue.value()->getColor().name().toLatin1()); attribute.append(";"); } const QByteArrayList styleValues = readerAttributes.value("style").toLatin1().split(';'); for (const auto &styleValue : styleValues) { const QByteArrayList list = styleValue.split(':'); if (Q_UNLIKELY(list.length() > 0 && !overrode.contains(list[0]))) { attribute.append(styleValue); attribute.append(";"); } } removeAttribute(readerAttributes, "style"); if (attribute.length() > 0) { attribute.prepend("style=\""); attribute.append("\" "); } return attribute; } static QByteArray parseAttributes (const QXmlStreamReader &reader, const ColorListModel *colors) { QXmlStreamAttributes readerAttributes = reader.attributes(); QByteArray attributes = parseFillAndStroke(readerAttributes, colors); attributes.append(parseStyle(readerAttributes, colors)); for (const auto &attribute : readerAttributes) { const QByteArray prefix = attribute.prefix().toLatin1(); if (Q_UNLIKELY(prefix.length() > 0)) { attributes.append(prefix); attributes.append(":"); } attributes.append( buildByteArrayAttribute(attribute.name().toLatin1(), attribute.value().toLatin1()) ); } return attributes; } static QByteArray parseDeclarations (const QXmlStreamReader &reader) { QByteArray declarations; for (const auto &declaration : reader.namespaceDeclarations()) { const QByteArray prefix = declaration.prefix().toLatin1(); if (Q_UNLIKELY(prefix.length() > 0)) { declarations.append("xmlns:"); declarations.append(prefix); } else declarations.append("xmlns"); declarations.append("=\""); declarations.append(declaration.namespaceUri().toLatin1()); declarations.append("\" "); } return declarations; } static QByteArray parseStartDocument (const QXmlStreamReader &reader) { QByteArray startDocument = ""); return startDocument; } static QByteArray parseStartElement (const QXmlStreamReader &reader, const ColorListModel *colors) { QByteArray startElement = "<"; startElement.append(reader.name().toLatin1()); startElement.append(" "); startElement.append(parseAttributes(reader, colors)); startElement.append(" "); startElement.append(parseDeclarations(reader)); startElement.append(">"); return startElement; } static QByteArray parseEndElement (const QXmlStreamReader &reader) { QByteArray endElement = ""); return endElement; } // ----------------------------------------------------------------------------- static QByteArray computeContent (QFile &file) { const ColorListModel *colors = App::getInstance()->getColorListModel(); QByteArray content; QXmlStreamReader reader(&file); while (!reader.atEnd()) switch (reader.readNext()) { case QXmlStreamReader::Comment: case QXmlStreamReader::DTD: case QXmlStreamReader::EndDocument: case QXmlStreamReader::Invalid: case QXmlStreamReader::NoToken: case QXmlStreamReader::ProcessingInstruction: break; case QXmlStreamReader::StartDocument: content.append(parseStartDocument(reader)); break; case QXmlStreamReader::StartElement: content.append(parseStartElement(reader, colors)); break; case QXmlStreamReader::EndElement: content.append(parseEndElement(reader)); break; case QXmlStreamReader::Characters: content.append(reader.text().toLatin1()); break; case QXmlStreamReader::EntityReference: content.append(reader.name().toLatin1()); break; } return reader.hasError() ? QByteArray() : content; } // ----------------------------------------------------------------------------- const QString ImageProvider::ProviderId = "internal"; ImageProvider::ImageProvider () : QQuickImageProvider( QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading ) {} // ----------------------------------------------------------------------------- QImage ImageProvider::requestImage (const QString &id, QSize *size, const QSize &requestedSize) { ImageModel * model = App::getInstance()->getImageListModel()->getImageModel(id); if(!model) return QImage(); const QString path = model->getPath(); //qDebug() << QStringLiteral("Image `%1` requested with size: (%2, %3).") // .arg(path).arg(requestedSize.width()).arg(requestedSize.height()); QElapsedTimer timer; timer.start(); // 1. Read and update XML content. *size = QSize(); QFile file(path); if(!file.exists()){ qWarning() << QStringLiteral("File doesn't exist: `%1`.").arg(path); return QImage(); } if (Q_UNLIKELY(QFileInfo(file).size() > Constants::MaxImageSize)) { qWarning() << QStringLiteral("Unable to open large file: `%1`.").arg(path); return QImage(); } if (Q_UNLIKELY(!file.open(QIODevice::ReadOnly))) { qWarning() << QStringLiteral("Unable to open file: `%1`.").arg(path); return QImage(); } const QByteArray content = computeContent(file); if (Q_UNLIKELY(!content.length())) { qWarning() << QStringLiteral("Unable to parse file: `%1`.").arg(path); return QImage(); } // 2. Build svg renderer. QSvgRenderer renderer(content); if (Q_UNLIKELY(!renderer.isValid())) { qWarning() << QStringLiteral("Invalid svg file: `%1`.").arg(path); return QImage(); } #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) renderer.setAspectRatioMode(Qt::KeepAspectRatio); #endif QSize askedSize = !requestedSize.isEmpty() ? requestedSize : renderer.defaultSize() * QGuiApplication::primaryScreen()->devicePixelRatio(); // 3. Create image. QImage image(askedSize, QImage::Format_ARGB32_Premultiplied); if (Q_UNLIKELY(image.isNull())) { qWarning() << QStringLiteral("Unable to create image from path: `%1`.") .arg(path); return QImage(); // Memory cannot be allocated. } image.fill(Qt::transparent);// Fill with transparent to set alpha channel *size = image.size(); // 4. Paint! QPainter painter(&image); renderer.render(&painter); // qDebug() << QStringLiteral("Image `%1` loaded in %2 milliseconds.").arg(path).arg(timer.elapsed()); return image; } QPixmap ImageProvider::requestPixmap (const QString &id, QSize *size, const QSize &requestedSize) { return QPixmap::fromImage(requestImage(id, size, requestedSize)); } linphone-desktop-5.0.2/linphone-app/src/app/providers/ImageProvider.hpp000066400000000000000000000024211434616504300262320ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef IMAGE_PROVIDER_H_ #define IMAGE_PROVIDER_H_ #include // ============================================================================= class ImageProvider : public QQuickImageProvider { public: ImageProvider (); QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override; QPixmap requestPixmap (const QString &id, QSize *size, const QSize &requestedSize) override; static const QString ProviderId; }; #endif // IMAGE_PROVIDER_H_ linphone-desktop-5.0.2/linphone-app/src/app/providers/QRCodeProvider.cpp000066400000000000000000000047661434616504300263360ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include #include #include #include #include "app/App.hpp" #include "QRCodeProvider.hpp" #include "components/other/colors/ColorListModel.hpp" #include "components/other/colors/ColorModel.hpp" #include "components/other/images/ImageListModel.hpp" #include "components/other/images/ImageModel.hpp" #include "utils/Constants.hpp" // ============================================================================= using namespace std; const QString QRCodeProvider::ProviderId = "qrcode"; QRCodeProvider::QRCodeProvider () : QQuickImageProvider( QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading ) {} // ----------------------------------------------------------------------------- QImage QRCodeProvider::requestImage (const QString &id, QSize *size, const QSize &requestedSize) { unsigned int w = requestedSize.width()>0?requestedSize.width() : 100; unsigned int h = requestedSize.height()>0 ? requestedSize.height() : 100; auto content = linphone::Factory::get()->createQrcode(id.toStdString(), w, h, 0); if( !content) return QImage(); QImage image(w, h, QImage::Format_Indexed8); for (int y = 0;ygetBuffer() + y*w, w); QVector colorTable(256); for(int i=0;i<256;i++) colorTable[i] = qRgb(i,i,i); image.setColorTable(colorTable); *size = image.size(); return image; } QPixmap QRCodeProvider::requestPixmap (const QString &id, QSize *size, const QSize &requestedSize) { return QPixmap::fromImage(requestImage(id, size, requestedSize)); } linphone-desktop-5.0.2/linphone-app/src/app/providers/QRCodeProvider.hpp000066400000000000000000000024271434616504300263330ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef QRCODE_PROVIDER_H_ #define QRCODE_PROVIDER_H_ #include // ============================================================================= class QRCodeProvider : public QQuickImageProvider { public: QRCodeProvider (); QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override; QPixmap requestPixmap (const QString &id, QSize *size, const QSize &requestedSize) override; static const QString ProviderId; }; #endif // AVATAR_PROVIDER_H_ linphone-desktop-5.0.2/linphone-app/src/app/providers/ThumbnailProvider.cpp000066400000000000000000000026341434616504300271340ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "app/paths/Paths.hpp" #include "utils/Utils.hpp" #include "ThumbnailProvider.hpp" // ============================================================================= const QString ThumbnailProvider::ProviderId = "thumbnail"; ThumbnailProvider::ThumbnailProvider () : QQuickImageProvider( QQmlImageProviderBase::Image, QQmlImageProviderBase::ForceAsynchronousImageLoading ) { mThumbnailsPath = Utils::coreStringToAppString(Paths::getThumbnailsDirPath()); } QImage ThumbnailProvider::requestImage (const QString &id, QSize *size, const QSize &) { QImage image(mThumbnailsPath + id); *size = image.size(); return image; } linphone-desktop-5.0.2/linphone-app/src/app/providers/ThumbnailProvider.hpp000066400000000000000000000023531434616504300271370ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef THUMBNAIL_PROVIDER_H_ #define THUMBNAIL_PROVIDER_H_ #include // ============================================================================= class ThumbnailProvider : public QQuickImageProvider { public: ThumbnailProvider (); QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override; static const QString ProviderId; private: QString mThumbnailsPath; }; #endif // THUMBNAIL_PROVIDER_H_ linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/000077500000000000000000000000001434616504300231125ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/ProxyAbstractListModel.hpp000066400000000000000000000057451434616504300302600ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PROXY_ABSTRACT_LIST_MODEL_H_ #define PROXY_ABSTRACT_LIST_MODEL_H_ #include #include #include "ProxyAbstractObject.hpp" template class ProxyAbstractListModel : public ProxyAbstractObject { public: ProxyAbstractListModel (QObject *parent = Q_NULLPTR) : ProxyAbstractObject(parent) {} virtual ~ProxyAbstractListModel(){ clearData(); } virtual int rowCount (const QModelIndex &index = QModelIndex()) const override{ return mList.count(); } virtual QHash roleNames () const override { QHash roles; roles[Qt::DisplayRole] = "$modelData"; return roles; } virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override{ int row = index.row(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); if (role == Qt::DisplayRole) return QVariant::fromValue(mList[row]); return QVariant(); } virtual T getAt(const int& index) const{ return mList[index]; } // Add functions virtual void add(T item){ int row = mList.count(); beginInsertRows(QModelIndex(), row, row); mList << item; endInsertRows(); } virtual void add(QList items){ beginInsertRows(QModelIndex(), mList.size(), mList.size() + items.size()-1); mList << items; endInsertRows(); } virtual void prepend(T item){ beginInsertRows(QModelIndex(), 0, 0); mList.prepend(item); endInsertRows(); } virtual void prepend(QList items){ beginInsertRows(QModelIndex(), 0, items.size()-1); items << mList; mList = items; endInsertRows(); } // Remove functions virtual bool removeRow (int row, const QModelIndex &parent = QModelIndex()){ return removeRows(row, 1, parent); } virtual bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override{ int limit = row + count - 1; if (row < 0 || count < 0 || limit >= mList.count()) return false; beginRemoveRows(parent, row, limit); for (int i = 0; i < count; ++i) mList.takeAt(row); endRemoveRows(); return true; } virtual void clearData(){ mList.clear(); } virtual void resetData(){ beginResetModel(); clearData(); endResetModel(); } protected: QList mList; }; #endif linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/ProxyAbstractMapModel.hpp000066400000000000000000000035741434616504300300600ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PROXY_ABSTRACT_MAP_MODEL_H_ #define PROXY_ABSTRACT_MAP_MODEL_H_ #include #include "ProxyAbstractObject.hpp" template class ProxyAbstractMapModel : public ProxyAbstractObject { public: ProxyAbstractMapModel (QObject *parent = Q_NULLPTR) : ProxyAbstractObject(parent) {} virtual ~ProxyAbstractMapModel(){ clearData(); } virtual int rowCount (const QModelIndex &index = QModelIndex()) const override{ return mMappedList.count(); } virtual QHash roleNames () const override { QHash roles; roles[Qt::DisplayRole] = "$modelData"; roles[Qt::DisplayRole+1] = "$modelKey"; return roles; } virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override{ int row = index.row(); auto it = mMappedList.begin() + row; if (role == Qt::DisplayRole) return QVariant::fromValue(*it); else if( role == Qt::DisplayRole+1) return QVariant::fromValue(it.key()); return QVariant(); } virtual void clearData() override{ mMappedList.clear(); } protected: QMap mMappedList; }; #endif linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/ProxyAbstractObject.hpp000066400000000000000000000031331434616504300275570ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PROXY_ABSTRACT_OBJECT_H_ #define PROXY_ABSTRACT_OBJECT_H_ #include #include // Use a regular declaration for Qt signal/slots handling class ProxyAbstractObject : public QAbstractListModel{ Q_OBJECT public: Q_PROPERTY(int count READ getCount NOTIFY countChanged) ProxyAbstractObject(QObject * parent = nullptr) : QAbstractListModel(parent){ connect(this, &ProxyAbstractObject::rowsInserted, this, &ProxyAbstractObject::countChanged); connect(this, &ProxyAbstractObject::rowsRemoved, this, &ProxyAbstractObject::countChanged); } Q_INVOKABLE virtual int getCount() const{ return rowCount(); } Q_INVOKABLE virtual bool remove(QObject *itemToRemove){return false;} Q_INVOKABLE virtual void clearData(){} Q_INVOKABLE virtual void resetData(){} signals: void countChanged(); }; #endif linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/ProxyListModel.cpp000066400000000000000000000025311434616504300265550ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ProxyListModel.hpp" #include // ============================================================================= ProxyListModel::ProxyListModel (QObject *parent) : ProxyAbstractListModel(parent) { } ProxyListModel::~ProxyListModel(){ } QSharedPointer ProxyListModel::get(QObject * itemToGet, int * index) const{ int row = 0; for(auto item : mList) if( item.get() == itemToGet){ if( index ) *index = row; return item; }else ++row; return nullptr; } // ----------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/ProxyListModel.hpp000066400000000000000000000064051434616504300265660ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef _PROXY_LIST_MODEL_H_ #define _PROXY_LIST_MODEL_H_ #include "ProxyAbstractListModel.hpp" #include // ============================================================================= class ProxyListModel : public ProxyAbstractListModel> { Q_OBJECT public: Q_PROPERTY(int count READ getCount NOTIFY countChanged) ProxyListModel (QObject *parent = Q_NULLPTR); virtual ~ProxyListModel(); template QSharedPointer getAt(const int& index) const{ return ProxyAbstractListModel>::getAt(index).objectCast(); } QSharedPointer get(QObject * itemToGet, int * index = nullptr) const; template QList> getSharedList(){ QList> newList; for(auto item : mList) newList << item.objectCast(); return newList; } // Add functions virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override{ int row = index.row(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); if (role == Qt::DisplayRole) return QVariant::fromValue(mList[row].get()); return QVariant(); } template void add(QSharedPointer item){ ProxyAbstractListModel>::add(item.template objectCast()); } template void add(QList> items){ beginInsertRows(QModelIndex(), mList.size(), mList.size() + items.size() - 1); for(auto i : items) mList << i.template objectCast(); endInsertRows(); } template void prepend(QSharedPointer item){ ProxyAbstractListModel>::prepend(item.template objectCast()); } template void prepend(QList> items){ beginInsertRows(QModelIndex(), 0, items.size()-1); items << mList; mList = items; endInsertRows(); } virtual bool remove(QObject *itemToRemove) override{ bool removed = false; qInfo() << QStringLiteral("Removing ") << itemToRemove->metaObject()->className() << QStringLiteral(" : ") << itemToRemove; int index = 0; for(auto item : mList) if( item == itemToRemove) { removed = removeRow(index); break; }else ++index; if( !removed) qWarning() << QStringLiteral("Unable to remove ") << itemToRemove->metaObject()->className() << QStringLiteral(" : ") << itemToRemove; return removed; } virtual bool remove(QSharedPointer itemToRemove){ return remove(itemToRemove.get()); } }; #endif linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/ProxyModel.cpp000066400000000000000000000045421434616504300257250ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ProxyModel.hpp" // ============================================================================= using namespace std; ProxyModel::ProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { mFilterMode = 0; sort(0, Qt::DescendingOrder); } ProxyModel::ProxyModel (QAbstractItemModel * model, const int& defaultFilterMode, QObject *parent) : QSortFilterProxyModel(parent) { mFilterMode = defaultFilterMode; setSourceModel(model); sort(0, Qt::DescendingOrder); } ProxyModel::~ProxyModel(){ if(mDeleteSourceModel) deleteSourceModel(); } void ProxyModel::deleteSourceModel(){ auto oldSourceModel = sourceModel(); if(oldSourceModel) { oldSourceModel->deleteLater(); setSourceModel(nullptr); } } int ProxyModel::getFilterMode () const { return mFilterMode; } void ProxyModel::setFilterMode (int filterMode) { if (getFilterMode() != filterMode) { mFilterMode = filterMode; invalidate(); emit filterModeChanged(filterMode); } } bool ProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { return true; } bool ProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { Q_UNUSED(left) Q_UNUSED(right) return true; } QVariant ProxyModel::getAt(int row){ QModelIndex sourceIndex = mapToSource(this->index(row, 0)); return sourceModel()->data(sourceIndex); } QAbstractItemModel *ProxyModel::getModel(){ return sourceModel(); } void ProxyModel::setModel(QAbstractItemModel * model){ setSourceModel(model); emit modelChanged(); } void ProxyModel::add(std::shared_ptr model){ emit added(model); }linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/ProxyModel.hpp000066400000000000000000000040211434616504300257220ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PROXY_MODEL_H_ #define PROXY_MODEL_H_ #include #include // ============================================================================= class ProxyModel : public QSortFilterProxyModel { Q_OBJECT public: Q_PROPERTY(int filterMode READ getFilterMode WRITE setFilterMode SIGNAL filterModeChanged) Q_PROPERTY(QAbstractItemModel * model READ getModel WRITE setModel SIGNAL modelChanged) ProxyModel (QObject *parent = Q_NULLPTR); ProxyModel (QAbstractItemModel * list, const int& defaultFilterMode, QObject *parent = Q_NULLPTR); virtual ~ProxyModel(); virtual void deleteSourceModel(); int getFilterMode () const; void setFilterMode (int filterMode); Q_INVOKABLE QVariant getAt(int row); QAbstractItemModel *getModel(); void setModel(QAbstractItemModel * model); public slots: void add(std::shared_ptr model); signals: void filterModeChanged(int); void modelChanged(); void added(std::shared_ptr model); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; bool mDeleteSourceModel = false; private: int mFilterMode; }; #endif linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/SortFilterAbstractProxyModel.hpp000066400000000000000000000027661434616504300314420ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SORT_FILTER_ABSTRACT_PROXY_MODEL_H_ #define SORT_FILTER_ABSTRACT_PROXY_MODEL_H_ #include "SortFilterProxyModel.hpp" template class SortFilterAbstractProxyModel : public SortFilterProxyModel { public: SortFilterAbstractProxyModel(T * model, QObject * parent = nullptr) : SortFilterProxyModel(parent){ update(model); } void update(T* model){ setSourceModel(model); sort(0, Qt::DescendingOrder); } template void add(QSharedPointer x){ qobject_cast(sourceModel())->add(x); } template void removeShared(QSharedPointer x){ qobject_cast(sourceModel())->remove(x); } template void remove(X x){ qobject_cast(sourceModel())->remove(x); } }; #endif linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/SortFilterProxyModel.cpp000066400000000000000000000040521434616504300277370ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "SortFilterProxyModel.hpp" SortFilterProxyModel::SortFilterProxyModel(QObject * parent) : QSortFilterProxyModel(parent){ mFilterType = 0; connect(this, &SortFilterProxyModel::rowsInserted, this, &SortFilterProxyModel::countChanged); connect(this, &SortFilterProxyModel::rowsRemoved, this, &SortFilterProxyModel::countChanged); } SortFilterProxyModel::~SortFilterProxyModel(){ if(mDeleteSourceModel) deleteSourceModel(); } void SortFilterProxyModel::deleteSourceModel(){ auto oldSourceModel = sourceModel(); if(oldSourceModel) { oldSourceModel->deleteLater(); setSourceModel(nullptr); } } int SortFilterProxyModel::getCount() const{ return rowCount(); } int SortFilterProxyModel::getFilterType () const{ return mFilterType; } QVariant SortFilterProxyModel::getAt(const int& atIndex) const { auto modelIndex = index(atIndex,0); return sourceModel()->data(mapToSource(modelIndex), 0); } void SortFilterProxyModel::setSortOrder(const Qt::SortOrder& order){ sort(0, order); } void SortFilterProxyModel::setFilterType (int filterType) { if (getFilterType() != filterType) { mFilterType = filterType; emit filterTypeChanged(filterType); invalidate(); } } void SortFilterProxyModel::remove(int index, int count){ QSortFilterProxyModel::removeRows(index, count); } linphone-desktop-5.0.2/linphone-app/src/app/proxyModel/SortFilterProxyModel.hpp000066400000000000000000000032171434616504300277460ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SORT_FILTER_PROXY_MODEL_H_ #define SORT_FILTER_PROXY_MODEL_H_ #include class SortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: Q_PROPERTY(int count READ getCount NOTIFY countChanged) Q_PROPERTY(int filterType READ getFilterType WRITE setFilterType NOTIFY filterTypeChanged) SortFilterProxyModel(QObject * parent = nullptr); virtual ~SortFilterProxyModel(); virtual void deleteSourceModel(); virtual int getCount() const; virtual int getFilterType () const; Q_INVOKABLE QVariant getAt(const int& index) const; Q_INVOKABLE void setSortOrder(const Qt::SortOrder& order); virtual void setFilterType (int filterType); Q_INVOKABLE void remove(int index, int count = 1); signals: void countChanged(); void filterTypeChanged(int filterType); protected: int mFilterType; bool mDeleteSourceModel = false; }; #endif linphone-desktop-5.0.2/linphone-app/src/app/single-application/000077500000000000000000000000001434616504300245325ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/app/single-application/SingleApplication.cpp000066400000000000000000000312641434616504300306510ustar00rootroot00000000000000// The MIT License (MIT) // // Copyright (c) Itay Grudev 2015 - 2016 // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_UNIX #include #endif // ifdef Q_OS_UNIX #include "utils/Utils.hpp" #include "SingleApplication.hpp" #include "SingleApplicationPrivate.hpp" #ifdef Q_OS_WIN #include #include #endif // ifdef Q_OS_WIN // ============================================================================= using namespace std; namespace { constexpr char NewInstance = 'N'; constexpr char SecondaryInstance = 'S'; constexpr char Reconnect = 'R'; constexpr char InvalidConnection = '\0'; } // ----------------------------------------------------------------------------- SingleApplicationPrivate::SingleApplicationPrivate (SingleApplication *p_ptr) : q_ptr(p_ptr) { server = nullptr; socket = nullptr; } SingleApplicationPrivate::~SingleApplicationPrivate () { if (socket != nullptr) { socket->close(); socket->deleteLater(); } memory->lock(); InstancesInfo *inst = static_cast(memory->data()); if (server != nullptr) { server->close(); server->deleteLater(); inst->primary = false; } memory->unlock(); memory->deleteLater(); } void SingleApplicationPrivate::genBlockServerName (int timeout) { QCryptographicHash appData(QCryptographicHash::Sha256); appData.addData("SingleApplication", 17); appData.addData(QApplication::applicationName().toUtf8()); appData.addData(QApplication::organizationName().toUtf8()); appData.addData(QApplication::organizationDomain().toUtf8()); if (!(options & SingleApplication::Mode::ExcludeAppVersion)) { appData.addData(QApplication::applicationVersion().toUtf8()); } if (!(options & SingleApplication::Mode::ExcludeAppPath)) { #ifdef Q_OS_WIN appData.addData(QApplication::applicationFilePath().toLower().toUtf8()); #else appData.addData(QApplication::applicationFilePath().toUtf8()); #endif // ifdef Q_OS_WIN } // User level block requires a user specific data in the hash if (options & SingleApplication::Mode::User) { #ifdef Q_OS_WIN Q_UNUSED(timeout) wchar_t username[UNLEN + 1]; // Specifies size of the buffer on input DWORD usernameLength = UNLEN + 1; if (GetUserNameW(username, &usernameLength)) { appData.addData(QString::fromWCharArray(username).toUtf8()); } else { appData.addData(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).join("").toUtf8()); } #endif // ifdef Q_OS_WIN #ifdef Q_OS_UNIX QProcess process; process.start("whoami"); if (process.waitForFinished(timeout) && process.exitCode() == QProcess::NormalExit) { appData.addData(process.readLine()); } else { appData.addData( QDir( QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first() ).absolutePath().toUtf8() ); } #endif // ifdef Q_OS_UNIX } // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with // server naming requirements. blockServerName = appData.result().toBase64().replace("/", "_"); } void SingleApplicationPrivate::startPrimary (bool resetMemory) { #ifdef Q_OS_UNIX signal(SIGINT, SingleApplicationPrivate::terminate); #endif // ifdef Q_OS_UNIX // Successful creation means that no main process exists // So we start a QLocalServer to listen for connections QLocalServer::removeServer(blockServerName); server = new QLocalServer(); // Restrict access to the socket according to the // SingleApplication::Mode::User flag on User level or no restrictions if (options & SingleApplication::Mode::User) { server->setSocketOptions(QLocalServer::UserAccessOption); } else { server->setSocketOptions(QLocalServer::WorldAccessOption); } server->listen(blockServerName); QObject::connect( server, &QLocalServer::newConnection, this, &SingleApplicationPrivate::slotConnectionEstablished ); // Reset the number of connections memory->lock(); InstancesInfo *inst = static_cast(memory->data()); if (resetMemory) { inst->primary = true; inst->secondary = 0; inst->primaryId = q_ptr->applicationPid(); } else { inst->primary = true; inst->primaryId = q_ptr->applicationPid(); } memory->unlock(); instanceNumber = 0; } void SingleApplicationPrivate::startSecondary () { #ifdef Q_OS_UNIX signal(SIGINT, SingleApplicationPrivate::terminate); #endif // ifdef Q_OS_UNIX } void SingleApplicationPrivate::connectToPrimary (int msecs, char connectionType) { // Connect to the Local Server of the Primary Instance if not already // connected. if (socket == nullptr) { socket = new QLocalSocket(); } // If already connected - we are done; if (socket->state() == QLocalSocket::ConnectedState) return; // If not connect if (socket->state() == QLocalSocket::UnconnectedState || socket->state() == QLocalSocket::ClosingState) { socket->connectToServer(blockServerName); } // Wait for being connected if (socket->state() == QLocalSocket::ConnectingState) { socket->waitForConnected(msecs); } // Initialisation message according to the SingleApplication protocol if (socket->state() == QLocalSocket::ConnectedState) { // Notify the parent that a new instance had been started; QByteArray initMsg = blockServerName.toLatin1(); initMsg.append(connectionType); initMsg.append(reinterpret_cast(&instanceNumber), sizeof(quint32)); initMsg.append(QByteArray::number(qChecksum(initMsg.constData(), uint(initMsg.length())), 256)); socket->write(initMsg); socket->flush(); socket->waitForBytesWritten(msecs); } } #ifdef Q_OS_UNIX void SingleApplicationPrivate::terminate (int signum) { Q_UNUSED(signum); SingleApplication::instance()->quit(); } #endif // ifdef Q_OS_UNIX /** * @brief Executed when a connection has been made to the LocalServer */ void SingleApplicationPrivate::slotConnectionEstablished () { Q_Q(SingleApplication); QLocalSocket *nextConnSocket = server->nextPendingConnection(); // Verify that the new connection follows the SingleApplication protocol char connectionType = InvalidConnection; quint32 instanceId; QByteArray initMsg, tmp; if (nextConnSocket->waitForReadyRead(100)) { tmp = nextConnSocket->read(blockServerName.length()); // Verify that the socket data start with blockServerName if (tmp == blockServerName.toLatin1()) { initMsg = tmp; connectionType = nextConnSocket->read(1)[0]; switch (connectionType) { case NewInstance: case SecondaryInstance: case Reconnect: { initMsg += connectionType; tmp = nextConnSocket->read(sizeof(quint32)); const char *data = tmp.constData(); instanceId = quint32(*data); initMsg += tmp; // Verify the checksum of the initMsg QByteArray checksum = QByteArray::number( qChecksum(initMsg.constData(), uint(initMsg.length())), 256 ); tmp = nextConnSocket->read(checksum.length()); if (checksum == tmp) break; // Otherwise set to invalid connection (next line) connectionType = InvalidConnection; break; } default: connectionType = InvalidConnection; } } } if (connectionType == InvalidConnection) { nextConnSocket->close(); nextConnSocket->deleteLater(); return; } QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this, [nextConnSocket, instanceId, this]() { emit this->slotClientConnectionClosed(nextConnSocket, instanceId); }); QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this, [nextConnSocket, instanceId, this]() { emit this->slotDataAvailable(nextConnSocket, instanceId); }); if (connectionType == NewInstance || ( connectionType == SecondaryInstance && options & SingleApplication::Mode::SecondaryNotification ) ) { emit q->instanceStarted(); } if (nextConnSocket->bytesAvailable() > 0) { emit this->slotDataAvailable(nextConnSocket, instanceId); } } void SingleApplicationPrivate::slotDataAvailable (QLocalSocket *dataSocket, quint32 instanceId) { Q_Q(SingleApplication); emit q->receivedMessage(instanceId, dataSocket->readAll()); } void SingleApplicationPrivate::slotClientConnectionClosed (QLocalSocket *closedSocket, quint32 instanceId) { if (closedSocket->bytesAvailable() > 0) emit slotDataAvailable(closedSocket, instanceId); closedSocket->deleteLater(); } /** * @brief Constructor. Checks and fires up LocalServer or closes the program * if another instance already exists * @param argc * @param argv * @param {bool} allowSecondaryInstances */ SingleApplication::SingleApplication (int &argc, char *argv[], bool allowSecondary, Options options, int timeout) : QApplication(argc, argv), d_ptr(new SingleApplicationPrivate(this)) { Q_D(SingleApplication); // Store the current mode of the program d->options = options; // Generating an application ID used for identifying the shared memory // block and QLocalServer d->genBlockServerName(timeout); // Guarantee thread safe behaviour with a shared memory block. Also by // explicitly attaching it and then deleting it we make sure that the // memory is deleted even if the process had crashed on Unix. #ifdef Q_OS_UNIX d->memory = new QSharedMemory(d->blockServerName); d->memory->attach(); delete d->memory; #endif // ifdef Q_OS_UNIX d->memory = new QSharedMemory(d->blockServerName); // Create a shared memory block if (d->memory->create(sizeof(InstancesInfo))) { d->startPrimary(true); return; } // Attempt to attach to the memory segment if (d->memory->attach()) { d->memory->lock(); InstancesInfo *inst = static_cast(d->memory->data()); if (!inst->primary || !Utils::processExists(inst->primaryId)) { // Check if there is not a primary instance and if there is, is it still running? d->startPrimary(false); d->memory->unlock(); return; } // Check if another instance can be started if (allowSecondary) { inst->secondary += 1; d->instanceNumber = inst->secondary; d->startSecondary(); if (d->options & Mode::SecondaryNotification) d->connectToPrimary(timeout, SecondaryInstance); d->memory->unlock(); return; } d->memory->unlock(); } d->connectToPrimary(timeout, NewInstance); d->deleteLater(); ::exit(EXIT_SUCCESS); } /** * @brief Destructor */ SingleApplication::~SingleApplication () { Q_D(SingleApplication); d->deleteLater(); } bool SingleApplication::isPrimary () { Q_D(SingleApplication); return d->server != nullptr; } bool SingleApplication::isSecondary () { Q_D(SingleApplication); return d->server == nullptr; } quint32 SingleApplication::instanceId () { Q_D(SingleApplication); return d->instanceNumber; } bool SingleApplication::sendMessage (QByteArray message, int timeout) { Q_D(SingleApplication); // Nobody to connect to if (isPrimary()) return false; // Make sure the socket is connected d->connectToPrimary(timeout, Reconnect); d->socket->write(message); bool dataWritten = d->socket->flush(); d->socket->waitForBytesWritten(timeout); return dataWritten; } void SingleApplication::quit () { QCoreApplication::quit(); } linphone-desktop-5.0.2/linphone-app/src/app/single-application/SingleApplication.hpp000066400000000000000000000106121434616504300306500ustar00rootroot00000000000000// The MIT License (MIT) // // Copyright (c) Itay Grudev 2015 - 2016 // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // See: https://github.com/itay-grudev/SingleApplication/ #ifndef SINGLE_APPLICATION_H_ #define SINGLE_APPLICATION_H_ #include #include #include // ============================================================================= class SingleApplicationPrivate; /** * @brief The SingleApplication class handles multipe instances of the same * Application * @see QCoreApplication */ class SingleApplication : public QApplication { Q_OBJECT; public: /** * @brief Mode of operation of SingleApplication. * Whether the block should be user-wide or system-wide and whether the * primary instance should be notified when a secondary instance had been * started. * @note Operating system can restrict the shared memory blocks to the same * user, in which case the User/System modes will have no effect and the * block will be user wide. * @enum */ enum Mode { User = 1 << 0, System = 1 << 1, SecondaryNotification = 1 << 2, ExcludeAppVersion = 1 << 3, ExcludeAppPath = 1 << 4 }; Q_DECLARE_FLAGS(Options, Mode) /** * @brief Intitializes a SingleApplication instance with argc command line * arguments in argv * @arg {int &} argc - Number of arguments in argv * @arg {const char *[]} argv - Supplied command line arguments * @arg {bool} allowSecondary - Whether to start the instance as secondary * if there is already a primary instance. * @arg {Mode} mode - Whether for the SingleApplication block to be applied * User wide or System wide. * @arg {int} timeout - Timeout to wait in milliseconds. * @note argc and argv may be changed as Qt removes arguments that it * recognizes * @note Mode::SecondaryNotification only works if set on both the primary * instance and the secondary instance. * @note The timeout is just a hint for the maximum time of blocking * operations. It does not guarantee that the SingleApplication * initialisation will be completed in given time, though is a good hint. * Usually 4*timeout would be the worst case (fail) scenario. * @see See the corresponding QAPPLICATION_CLASS constructor for reference */ explicit SingleApplication (int &argc, char *argv[], bool allowSecondary = false, Options options = Mode::User, int timeout = 100); virtual ~SingleApplication (); /** * @brief Returns if the instance is the primary instance * @returns {bool} */ bool isPrimary (); /** * @brief Returns if the instance is a secondary instance * @returns {bool} */ bool isSecondary (); /** * @brief Returns a unique identifier for the current instance * @returns {int} */ quint32 instanceId (); /** * @brief Sends a message to the primary instance. Returns true on success. * @param {int} timeout - Timeout for connecting * @returns {bool} * @note sendMessage() will return false if invoked from the primary * instance. */ bool sendMessage (QByteArray message, int timeout = 100); virtual void quit (); signals: void instanceStarted (); void receivedMessage (quint32 instanceId, QByteArray message); private: SingleApplicationPrivate *d_ptr; Q_DECLARE_PRIVATE(SingleApplication) }; Q_DECLARE_OPERATORS_FOR_FLAGS(SingleApplication::Options) #endif // SINGLE_APPLICATION_H_ linphone-desktop-5.0.2/linphone-app/src/app/single-application/SingleApplicationDBus.cpp000066400000000000000000000070241434616504300314240ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "config.h" #include "SingleApplication.hpp" #include "SingleApplicationDBusPrivate.hpp" // ============================================================================= namespace { constexpr char ServiceName[] = "org." EXECUTABLE_NAME ".SingleApplication"; } SingleApplicationPrivate::SingleApplicationPrivate (SingleApplication *q_ptr) : QDBusAbstractAdaptor(q_ptr), q_ptr(q_ptr) {} QDBusConnection SingleApplicationPrivate::getBus () const { if (options & SingleApplication::Mode::User) return QDBusConnection::sessionBus(); return QDBusConnection::systemBus(); } void SingleApplicationPrivate::startPrimary () { signal(SIGINT, SingleApplicationPrivate::terminate); if (!getBus().registerObject("/", this, QDBusConnection::ExportAllSlots)) qWarning() << QStringLiteral("Failed to register single application object on DBus."); instanceNumber = 0; } void SingleApplicationPrivate::startSecondary () { signal(SIGINT, SingleApplicationPrivate::terminate); instanceNumber = 1; } void SingleApplicationPrivate::terminate (int signum) { Q_UNUSED(signum) SingleApplication::instance()->quit(); } SingleApplication::SingleApplication (int &argc, char *argv[], bool allowSecondary, Options options, int) : QApplication(argc, argv), d_ptr(new SingleApplicationPrivate(this)) { Q_D(SingleApplication); // Store the current mode of the program. d->options = options; if (!d->getBus().isConnected()) { qWarning() << QStringLiteral("Cannot connect to the D-Bus session bus."); delete d; ::exit(EXIT_FAILURE); } if (d->getBus().registerService(ServiceName)) { d->startPrimary(); return; } if (allowSecondary) { d->startSecondary(); return; } delete d; ::exit(EXIT_SUCCESS); } SingleApplication::~SingleApplication () { Q_D(SingleApplication); delete d; } bool SingleApplication::isPrimary () { Q_D(SingleApplication); return d->instanceNumber == 0; } bool SingleApplication::isSecondary () { Q_D(SingleApplication); return d->instanceNumber != 0; } quint32 SingleApplication::instanceId () { Q_D(SingleApplication); return d->instanceNumber; } bool SingleApplication::sendMessage (QByteArray message, int timeout) { Q_D(SingleApplication); if (isPrimary()) return false; QDBusInterface iface(ServiceName, "/", "", d->getBus()); if (iface.isValid()) { iface.setTimeout(timeout); iface.call(QDBus::Block, "handleMessageReceived", instanceId(), message); return true; } return false; } void SingleApplicationPrivate::handleMessageReceived (quint32 instanceId, QByteArray message) { Q_Q(SingleApplication); emit q->receivedMessage(instanceId, message); } void SingleApplication::quit () { QCoreApplication::quit(); } linphone-desktop-5.0.2/linphone-app/src/app/single-application/SingleApplicationDBusPrivate.hpp000066400000000000000000000034341434616504300327650ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SINGLE_APPLICATION_DBUS_PRIVATE_H_ #define SINGLE_APPLICATION_DBUS_PRIVATE_H_ #include #include #include "SingleApplication.hpp" // ============================================================================= struct InstancesInfo { bool primary; quint32 secondary; }; class SingleApplicationPrivate : public QDBusAbstractAdaptor { Q_OBJECT; Q_CLASSINFO("D-Bus Interface", "org.linphone.DBus.SingleApplication"); public: SingleApplicationPrivate (SingleApplication *q_ptr); QDBusConnection getBus () const; void startPrimary (); void startSecondary (); static void terminate (int signum); SingleApplication *q_ptr; SingleApplication::Options options; quint32 instanceNumber; // Explicit public slot. Cannot be private, must be exported as a method via D-Bus. public slots: void handleMessageReceived (quint32 instanceId, QByteArray message); private: Q_DECLARE_PUBLIC(SingleApplication); }; #endif // SINGLE_APPLICATION_DBUS_PRIVATE_H_ linphone-desktop-5.0.2/linphone-app/src/app/single-application/SingleApplicationPrivate.hpp000066400000000000000000000065621434616504300322140ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // The MIT License (MIT) // // Copyright (c) Itay Grudev 2015 - 2016 // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // // W A R N I N G !!! // ----------------- // // This file is not part of the SingleApplication API. It is used purely as an // implementation detail. This header file may change from version to // version without notice, or may even be removed. // #ifndef SINGLE_APPLICATION_PRIVATE_H_ #define SINGLE_APPLICATION_PRIVATE_H_ #include #include #include #include #include "SingleApplication.hpp" // ============================================================================= struct InstancesInfo { bool primary; quint32 secondary; qint64 primaryId; }; class SingleApplicationPrivate : public QObject { Q_OBJECT public: Q_DECLARE_PUBLIC(SingleApplication) SingleApplicationPrivate (SingleApplication *q_ptr); ~SingleApplicationPrivate (); void genBlockServerName (int msecs); void startPrimary (bool resetMemory); void startSecondary (); void connectToPrimary (int msecs, char connectionType); #ifdef Q_OS_UNIX static void terminate (int signum); #endif // ifdef Q_OS_UNIX QSharedMemory *memory; SingleApplication *q_ptr; QLocalSocket *socket; QLocalServer *server; quint32 instanceNumber; QString blockServerName; SingleApplication::Options options; public Q_SLOTS: void slotConnectionEstablished (); void slotDataAvailable (QLocalSocket *, quint32); void slotClientConnectionClosed (QLocalSocket *, quint32); }; #endif // SINGLE_APPLICATION_PRIVATE_H_ linphone-desktop-5.0.2/linphone-app/src/app/translator/000077500000000000000000000000001434616504300231415ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/app/translator/DefaultTranslator.cpp000066400000000000000000000042541434616504300273100ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "DefaultTranslator.hpp" // ============================================================================= DefaultTranslator::DefaultTranslator (QObject *parent) : QTranslator(parent) { QDirIterator it(":", QDirIterator::Subdirectories); while (it.hasNext()) { QFileInfo info(it.next()); if (info.suffix() == QLatin1String("qml")) { QString dir = info.absoluteDir().absolutePath(); // Ignore extra selectors. // TODO: Remove 5.9 support in July 2019. for (const auto &selector : { "+linux", "+mac", "+windows", "+custom", "+5.9" }) if (dir.contains(selector)) goto end; // Ignore default imports. if (dir.startsWith(":/QtQuick")) continue; QString basename = info.baseName(); if (!mContexts.contains(basename)) mContexts << basename; } end:; } } QString DefaultTranslator::translate ( const char *context, const char *sourceText, const char *disambiguation, int n ) const { if (!context) return QString(""); QString translation = QTranslator::translate(context, sourceText, disambiguation, n); if (translation.length() == 0 && mContexts.contains(context)) qDebug() << QStringLiteral("Unable to find a translation. (context=%1, label=%2, disambiguation=%3)") .arg(context).arg(sourceText).arg(disambiguation); return translation; } linphone-desktop-5.0.2/linphone-app/src/app/translator/DefaultTranslator.hpp000066400000000000000000000041051434616504300273100ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef DEFAULT_TRANSLATOR_H_ #define DEFAULT_TRANSLATOR_H_ #include #include // ============================================================================= class DefaultTranslator : public QTranslator { public: DefaultTranslator (QObject *parent = Q_NULLPTR); QString translate ( const char *context, const char *sourceText, const char *disambiguation = Q_NULLPTR, int n = -1 ) const override; private: QSet mContexts; }; // Workaround for bad Application Menu translation on Mac: // Overwrite Qt source by our translations : //static const char *application_menu_strings[] = //{ // QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","About %1"), // QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Preferences..."), // QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Services"), // QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Hide %1"), // QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Hide Others"), // QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Show All"), // QT_TRANSLATE_NOOP("MAC_APPLICATION_MENU","Quit %1") //}; class MAC_APPLICATION_MENU : public QObject{ QString forcedTranslation(){ return tr("About %1") + tr("Preferences...") + tr("Services") + tr("Hide %1") + tr("Hide Others") + tr("Show All") + tr("Quit %1"); } }; #endif // DEFAULT_TRANSLATOR_H_ linphone-desktop-5.0.2/linphone-app/src/components/000077500000000000000000000000001434616504300223555ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/Components.hpp000066400000000000000000000105001434616504300252070ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef COMPONENTS_H_ #define COMPONENTS_H_ #include "assistant/AssistantModel.hpp" #include "authentication/AuthenticationNotifier.hpp" #include "call/CallModel.hpp" #include "calls/CallsListModel.hpp" #include "calls/CallsListProxyModel.hpp" #include "camera/Camera.hpp" #include "components/chat-events/ChatCallModel.hpp" #include "components/chat-events/ChatMessageModel.hpp" #include "components/chat-events/ChatNoticeModel.hpp" #include "chat-room/ChatRoomProxyModel.hpp" #include "codecs/AudioCodecsModel.hpp" #include "codecs/VideoCodecsModel.hpp" #include "conference/ConferenceAddModel.hpp" #include "conference/ConferenceModel.hpp" #include "conference/ConferenceProxyModel.hpp" #include "conferenceInfo/ConferenceInfoModel.hpp" #include "conferenceInfo/ConferenceInfoProxyModel.hpp" #include "conferenceScheduler/ConferenceScheduler.hpp" #include "contact/ContactModel.hpp" #include "contact/VcardModel.hpp" #include "contacts/ContactsListModel.hpp" #include "contacts/ContactsListProxyModel.hpp" #include "contacts/ContactsImporterModel.hpp" #include "contacts/ContactsImporterPluginsManager.hpp" #include "contacts/ContactsImporterListModel.hpp" #include "contacts/ContactsImporterListProxyModel.hpp" #include "content/ContentListModel.hpp" #include "content/ContentModel.hpp" #include "content/ContentProxyModel.hpp" #include "core/CoreHandlers.hpp" #include "core/CoreManager.hpp" #include "file/FileDownloader.hpp" #include "file/FileExtractor.hpp" #include "history/HistoryProxyModel.hpp" #include "ldap/LdapModel.hpp" #include "ldap/LdapListModel.hpp" #include "ldap/LdapProxyModel.hpp" #include "notifier/Notifier.hpp" #include "participant/ParticipantListModel.hpp" #include "participant/ParticipantModel.hpp" #include "participant/ParticipantProxyModel.hpp" #include "participant/ParticipantDeviceListModel.hpp" #include "participant/ParticipantDeviceModel.hpp" #include "participant/ParticipantDeviceProxyModel.hpp" #include "participant-imdn/ParticipantImdnStateModel.hpp" #include "participant-imdn/ParticipantImdnStateListModel.hpp" #include "participant-imdn/ParticipantImdnStateProxyModel.hpp" #include "presence/OwnPresenceModel.hpp" #include "recorder/RecorderModel.hpp" #include "recorder/RecorderManager.hpp" #include "settings/AccountSettingsModel.hpp" #include "settings/SettingsModel.hpp" #include "search/SearchResultModel.hpp" #include "sip-addresses/SipAddressesModel.hpp" #include "sip-addresses/SipAddressesProxyModel.hpp" #include "search/SearchSipAddressesModel.hpp" #include "search/SearchSipAddressesProxyModel.hpp" #include "sound-player/SoundPlayer.hpp" #include "telephone-numbers/TelephoneNumbersModel.hpp" #include "timeline/TimelineModel.hpp" #include "timeline/TimelineProxyModel.hpp" #include "timeline/TimelineListModel.hpp" #include "tunnel/TunnelModel.hpp" #include "tunnel/TunnelConfigModel.hpp" #include "tunnel/TunnelConfigListModel.hpp" #include "tunnel/TunnelConfigProxyModel.hpp" #include "url-handlers/UrlHandlers.hpp" #include "other/colors/ColorModel.hpp" #include "other/colors/ColorListModel.hpp" #include "other/colors/ColorProxyModel.hpp" #include "other/colors/ImageColorsProxyModel.hpp" #include "other/clipboard/Clipboard.hpp" #include "other/desktop-tools/DesktopTools.hpp" #include "other/images/ImageModel.hpp" #include "other/images/ImageListModel.hpp" #include "other/images/ImageProxyModel.hpp" #include "other/text-to-speech/TextToSpeech.hpp" #include "other/timeZone/TimeZoneModel.hpp" #include "other/timeZone/TimeZoneListModel.hpp" #include "other/timeZone/TimeZoneProxyModel.hpp" #include "other/units/Units.hpp" #endif // COMPONENTS_H_ linphone-desktop-5.0.2/linphone-app/src/components/assistant/000077500000000000000000000000001434616504300243665ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/assistant/AssistantModel.cpp000066400000000000000000000473721434616504300300410ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "app/paths/Paths.hpp" #include "components/core/CoreManager.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "AssistantModel.hpp" #include #include #include // ============================================================================= using namespace std; class AssistantModel::Handlers : public linphone::AccountCreatorListener { public: Handlers (AssistantModel *assistant) { mAssistant = assistant; } private: void createAccount (const shared_ptr &creator) { shared_ptr proxyConfig = creator->createProxyConfig(); auto account = CoreManager::getInstance()->getCore()->getAccountByIdkey(proxyConfig->getIdkey()); if(account){ CoreManager::getInstance()->addingAccount(account->getParams()); CoreManager::getInstance()->getSettingsModel()->configureRlsUri(account); CoreManager::getInstance()->getAccountSettingsModel()->setDefaultAccount(account); } } void onCreateAccount ( const shared_ptr & accountCreator, linphone::AccountCreator::Status status, const string & ) override { if (status == linphone::AccountCreator::Status::AccountCreated){ emit mAssistant->createStatusChanged(QString("")); }else { if (status == linphone::AccountCreator::Status::RequestFailed) emit mAssistant->createStatusChanged(tr("requestFailed")); else if (status == linphone::AccountCreator::Status::ServerError) emit mAssistant->createStatusChanged(tr("cannotSendSms")); else emit mAssistant->createStatusChanged(tr("accountAlreadyExists")); } } void onIsAccountExist ( const shared_ptr &creator, linphone::AccountCreator::Status status, const string & ) override { if (status == linphone::AccountCreator::Status::AccountExist || status == linphone::AccountCreator::Status::AccountExistWithAlias) { createAccount(creator); CoreManager::getInstance()->getSipAddressesModel()->reset(); emit mAssistant->loginStatusChanged(QString("")); } else { if (status == linphone::AccountCreator::Status::RequestFailed) emit mAssistant->loginStatusChanged(tr("requestFailed")); else emit mAssistant->loginStatusChanged(tr("loginWithUsernameFailed")); } } void onActivateAccount ( const shared_ptr &creator, linphone::AccountCreator::Status status, const string & ) override { if ( status == linphone::AccountCreator::Status::AccountActivated || status == linphone::AccountCreator::Status::AccountAlreadyActivated ) { if (creator->getEmail().empty()) createAccount(creator); CoreManager::getInstance()->getSipAddressesModel()->reset(); emit mAssistant->activateStatusChanged(QString("")); } else { if (status == linphone::AccountCreator::Status::RequestFailed) emit mAssistant->activateStatusChanged(tr("requestFailed")); else emit mAssistant->activateStatusChanged(tr("smsActivationFailed")); } } void onIsAccountActivated ( const shared_ptr &creator, linphone::AccountCreator::Status status, const string & ) override { if (status == linphone::AccountCreator::Status::AccountActivated) { createAccount(creator); CoreManager::getInstance()->getSipAddressesModel()->reset(); emit mAssistant->activateStatusChanged(QString("")); } else { if (status == linphone::AccountCreator::Status::RequestFailed) emit mAssistant->activateStatusChanged(tr("requestFailed")); else emit mAssistant->activateStatusChanged(tr("emailActivationFailed")); } } void onRecoverAccount ( const shared_ptr &, linphone::AccountCreator::Status status, const string & ) override { if (status == linphone::AccountCreator::Status::RequestOk) { CoreManager::getInstance()->getSipAddressesModel()->reset(); emit mAssistant->recoverStatusChanged(QString("")); } else { if (status == linphone::AccountCreator::Status::RequestFailed) emit mAssistant->recoverStatusChanged(tr("requestFailed")); else if (status == linphone::AccountCreator::Status::ServerError) emit mAssistant->recoverStatusChanged(tr("cannotSendSms")); else emit mAssistant->recoverStatusChanged(tr("loginWithPhoneNumberFailed")); } } private: AssistantModel *mAssistant; }; // ----------------------------------------------------------------------------- AssistantModel::AssistantModel (QObject *parent) : QObject(parent) { mHandlers = make_shared(this); shared_ptr core = CoreManager::getInstance()->getCore(); connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::foundQRCode, this, &AssistantModel::onQRCodeFound); mIsReadingQRCode = false; mAccountCreator = core->createAccountCreator( core->getConfig()->getString("assistant", "xmlrpc_url", Constants::DefaultXmlrpcUri) ); mAccountCreator->addListener(mHandlers); connect(this, &AssistantModel::apiReceived, this, &AssistantModel::onApiReceived); } AssistantModel::~AssistantModel(){ setIsReadingQRCode(false); } // ----------------------------------------------------------------------------- void AssistantModel::activate () { if (mAccountCreator->getEmail().empty()) mAccountCreator->activateAccount(); else mAccountCreator->isAccountActivated(); } void AssistantModel::create () { mAccountCreator->createAccount(); } void AssistantModel::login () { if (!mCountryCode.isEmpty()) { mAccountCreator->recoverAccount(); return; } shared_ptr config(CoreManager::getInstance()->getCore()->getConfig()); if (!config->getString("assistant", "xmlrpc_url", "").empty()) { mAccountCreator->isAccountExist(); return; } // No verification if no xmlrpc url. Use addOtherSipAccount directly. QVariantMap map; map["sipDomain"] = Utils::coreStringToAppString(config->getString("assistant", "domain", "")); map["username"] = getUsername(); map["password"] = getPassword(); emit loginStatusChanged(addOtherSipAccount(map) ? QString("") : tr("unableToAddAccount")); } void AssistantModel::reset () { mCountryCode = QString(""); mAccountCreator->reset(); emit emailChanged(QString(""), QString("")); emit passwordChanged(QString(""), QString("")); emit phoneNumberChanged(QString(""), QString("")); emit usernameChanged(QString(""), QString("")); } // ----------------------------------------------------------------------------- bool AssistantModel::addOtherSipAccount (const QVariantMap &map) { CoreManager *coreManager = CoreManager::getInstance(); shared_ptr factory = linphone::Factory::get(); shared_ptr core = coreManager->getCore(); std::shared_ptr account; std::string accountIdKey = map["accountIdKey"].toString().toStdString(); if( accountIdKey != "") account = core->getAccountByIdkey(accountIdKey); shared_ptr accountParams = core->createAccountParams(); const QString domain = map["sipDomain"].toString(); QString sipAddress = QStringLiteral("sip:%1@%2") .arg(map["username"].toString()).arg(domain); { // Server address. shared_ptr address = factory->createAddress( Utils::appStringToCoreString(QStringLiteral("sip:%1").arg(domain)) ); if(!address) { qWarning() << QStringLiteral("Unable to create address from domain `%1`.") .arg(domain); return false; } const QString &transport(map["transport"].toString()); if (!transport.isEmpty()) { LinphoneEnums::TransportType transportType; LinphoneEnums::fromString(transport, &transportType); address->setTransport(LinphoneEnums::toLinphone(transportType)); } if (accountParams->setServerAddress(address)) { qWarning() << QStringLiteral("Unable to add server address: `%1`.") .arg(QString::fromStdString(address->asString())); return false; } } // Sip Address. shared_ptr address = factory->createAddress(Utils::appStringToCoreString(sipAddress)); if (!address) { qWarning() << QStringLiteral("Unable to create sip address object from: `%1`.").arg(sipAddress); return false; } address->setDisplayName(Utils::appStringToCoreString(map["displayName"].toString())); accountParams->setIdentityAddress(address); // AuthInfo. core->addAuthInfo( factory->createAuthInfo( address->getUsername(), // Username. "", // User ID. Utils::appStringToCoreString(map["password"].toString()), // Password. "", // HA1. "", // Realm. address->getDomain() // Domain. ) ); AccountSettingsModel *accountSettingsModel = coreManager->getAccountSettingsModel(); if (accountSettingsModel->addOrUpdateAccount(account, accountParams)) { accountSettingsModel->setDefaultAccount(account); return true; } return false; } void AssistantModel::createTestAccount(){ } void AssistantModel::generateQRCode(){ auto flexiAPIClient = make_shared(CoreManager::getInstance()->getCore()->cPtr()); flexiAPIClient ->accountProvision() ->then([this](FlexiAPIClient::Response response){ emit newQRCodeReceived(response.json()["provisioning_token"].asCString()); }) ->error([this](FlexiAPIClient::Response response){ emit newQRCodeNotReceived(Utils::coreStringToAppString(response.body), response.code); }); } void AssistantModel::requestQRCode(){ auto flexiAPIClient = make_shared(CoreManager::getInstance()->getCore()->cPtr()); flexiAPIClient ->accountAuthTokenCreate() ->then([this](FlexiAPIClient::Response response) { mToken = response.json()["token"].asCString(); emit newQRCodeReceived(mToken); QTimer::singleShot(5000, this, &AssistantModel::checkLinkingAccount); })->error([this](FlexiAPIClient::Response response){ qWarning() << response.code << " => " << response.body.c_str(); emit newQRCodeNotReceived(Utils::coreStringToAppString(response.body), response.code); }); } void AssistantModel::readQRCode(){ setIsReadingQRCode(!mIsReadingQRCode); } void AssistantModel::newQRCodeNotReceivedTest(){ emit newQRCodeNotReceived("Cannot generate a provisioning key",0); } void AssistantModel::checkLinkingAccount(){ auto flexiAPIClient = make_shared(CoreManager::getInstance()->getCore()->cPtr()); flexiAPIClient ->accountApiKeyFromAuthTokenGenerate(mToken.toStdString()) ->then([this](FlexiAPIClient::Response response)mutable{ emit apiReceived(Utils::coreStringToAppString(response.json()["api_key"].asCString())); })->error([this](FlexiAPIClient::Response){ QTimer::singleShot(5000, this, &AssistantModel::checkLinkingAccount); }); } void AssistantModel::onApiReceived(QString apiKey){ auto flexiAPIClient = make_shared(CoreManager::getInstance()->getCore()->cPtr()); flexiAPIClient->setApiKey(Utils::appStringToCoreString(apiKey).c_str()) ->accountProvision() ->then([this](FlexiAPIClient::Response response){ emit provisioningTokenReceived(response.json()["provisioning_token"].asCString()); })->error([this](FlexiAPIClient::Response response){ //it provisioningTokenReceived("token"); emit this->newQRCodeNotReceived("Cannot generate a provisioning key"+(response.body.empty() ? "" : " : " +Utils::coreStringToAppString(response.body)), response.code); }); } void AssistantModel::onQRCodeFound(const std::string & result){ setIsReadingQRCode(false); emit qRCodeFound(Utils::coreStringToAppString(result)); } void AssistantModel::attachAccount(const QString& token){ auto flexiAPIClient = make_shared(CoreManager::getInstance()->getCore()->cPtr()); flexiAPIClient-> accountAuthTokenAttach(Utils::appStringToCoreString(token)) ->then([this](FlexiAPIClient::Response response){ qWarning() << "Attached"; emit qRCodeAttached(); }) ->error([this](FlexiAPIClient::Response response){ emit qRCodeNotAttached("Cannot attach"+ (response.body.empty() ? "" : " : " +Utils::coreStringToAppString(response.body)), response.code); }); } // ----------------------------------------------------------------------------- QString AssistantModel::getEmail () const { return Utils::coreStringToAppString(mAccountCreator->getEmail()); } void AssistantModel::setEmail (const QString &email) { shared_ptr config = CoreManager::getInstance()->getCore()->getConfig(); QString error; switch (mAccountCreator->setEmail(Utils::appStringToCoreString(email))) { case linphone::AccountCreator::EmailStatus::Ok: break; case linphone::AccountCreator::EmailStatus::Malformed: error = tr("emailStatusMalformed"); break; case linphone::AccountCreator::EmailStatus::InvalidCharacters: error = tr("emailStatusMalformedInvalidCharacters"); break; } emit emailChanged(email, error); } // ----------------------------------------------------------------------------- QString AssistantModel::getPassword () const { return Utils::coreStringToAppString(mAccountCreator->getPassword()); } void AssistantModel::setPassword (const QString &password) { shared_ptr config = CoreManager::getInstance()->getCore()->getConfig(); QString error; switch (mAccountCreator->setPassword(Utils::appStringToCoreString(password))) { case linphone::AccountCreator::PasswordStatus::Ok: break; case linphone::AccountCreator::PasswordStatus::TooShort: error = tr("passwordStatusTooShort").arg(config->getInt("assistant", "password_min_length", 1)); break; case linphone::AccountCreator::PasswordStatus::TooLong: error = tr("passwordStatusTooLong").arg(config->getInt("assistant", "password_max_length", -1)); break; case linphone::AccountCreator::PasswordStatus::InvalidCharacters: error = tr("passwordStatusInvalidCharacters") .arg(Utils::coreStringToAppString(config->getString("assistant", "password_regex", ""))); break; case linphone::AccountCreator::PasswordStatus::MissingCharacters: error = tr("passwordStatusMissingCharacters") .arg(Utils::coreStringToAppString(config->getString("assistant", "missing_characters", ""))); break; } emit passwordChanged(password, error); } // ----------------------------------------------------------------------------- QString AssistantModel::getCountryCode () const { return mCountryCode; } void AssistantModel::setCountryCode (const QString &countryCode) { mCountryCode = countryCode; emit countryCodeChanged(countryCode); } // ----------------------------------------------------------------------------- QString AssistantModel::getPhoneNumber () const { return Utils::coreStringToAppString(mAccountCreator->getPhoneNumber()); } void AssistantModel::setPhoneNumber (const QString &phoneNumber) { shared_ptr config = CoreManager::getInstance()->getCore()->getConfig(); QString error; switch (static_cast( mAccountCreator->setPhoneNumber(Utils::appStringToCoreString(phoneNumber), Utils::appStringToCoreString(mCountryCode)) )) { case linphone::AccountCreator::PhoneNumberStatus::Ok: break; case linphone::AccountCreator::PhoneNumberStatus::Invalid: error = tr("phoneNumberStatusInvalid"); break; case linphone::AccountCreator::PhoneNumberStatus::TooShort: error = tr("phoneNumberStatusTooShort"); break; case linphone::AccountCreator::PhoneNumberStatus::TooLong: error = tr("phoneNumberStatusTooLong"); break; case linphone::AccountCreator::PhoneNumberStatus::InvalidCountryCode: error = tr("phoneNumberStatusInvalidCountryCode"); break; default: break; } emit phoneNumberChanged(phoneNumber, error); } // ----------------------------------------------------------------------------- QString AssistantModel::getUsername () const { return Utils::coreStringToAppString(mAccountCreator->getUsername()); } void AssistantModel::setUsername (const QString &username) { emit usernameChanged( username, mapAccountCreatorUsernameStatusToString( mAccountCreator->setUsername(Utils::appStringToCoreString(username)) ) ); } // ----------------------------------------------------------------------------- QString AssistantModel::getDisplayName () const { return Utils::coreStringToAppString(mAccountCreator->getDisplayName()); } void AssistantModel::setDisplayName (const QString &displayName) { emit displayNameChanged( displayName, mapAccountCreatorUsernameStatusToString( mAccountCreator->setDisplayName(Utils::appStringToCoreString(displayName)) ) ); } // ----------------------------------------------------------------------------- QString AssistantModel::getActivationCode () const { return Utils::coreStringToAppString(mAccountCreator->getActivationCode()); } void AssistantModel::setActivationCode (const QString &activationCode) { mAccountCreator->setActivationCode(Utils::appStringToCoreString(activationCode)); emit activationCodeChanged(activationCode); } // ----------------------------------------------------------------------------- QString AssistantModel::getConfigFilename () const { return mConfigFilename; } void AssistantModel::setConfigFilename (const QString &configFilename) { mConfigFilename = configFilename; QString configPath = Utils::coreStringToAppString(Paths::getAssistantConfigDirPath()) + configFilename; qInfo() << QStringLiteral("Set config on assistant: `%1`.").arg(configPath); CoreManager::getInstance()->getCore()->getConfig()->loadFromXmlFile( Utils::appStringToCoreString(configPath) ); emit configFilenameChanged(configFilename); } bool AssistantModel::getIsReadingQRCode() const{ return mIsReadingQRCode; } void AssistantModel::setIsReadingQRCode(bool isReading){ if( mIsReadingQRCode != isReading){ if( CoreManager::getInstance()->getCore()->qrcodeVideoPreviewEnabled() != isReading){ CoreManager::getInstance()->getCore()->enableQrcodeVideoPreview(isReading); //CoreManager::getInstance()->getCore()->enableVideoPreview(isReading); } mIsReadingQRCode = isReading; emit isReadingQRCodeChanged(); } } // ----------------------------------------------------------------------------- QString AssistantModel::mapAccountCreatorUsernameStatusToString (linphone::AccountCreator::UsernameStatus status) const { shared_ptr config = CoreManager::getInstance()->getCore()->getConfig(); QString error; switch (status) { case linphone::AccountCreator::UsernameStatus::Ok: break; case linphone::AccountCreator::UsernameStatus::TooShort: error = tr("usernameStatusTooShort").arg(config->getInt("assistant", "username_min_length", 1)); break; case linphone::AccountCreator::UsernameStatus::TooLong: error = tr("usernameStatusTooLong").arg(config->getInt("assistant", "username_max_length", -1)); break; case linphone::AccountCreator::UsernameStatus::InvalidCharacters: error = tr("usernameStatusInvalidCharacters") .arg(Utils::coreStringToAppString(config->getString("assistant", "username_regex", ""))); break; case linphone::AccountCreator::UsernameStatus::Invalid: error = tr("usernameStatusInvalid"); break; } return error; } linphone-desktop-5.0.2/linphone-app/src/components/assistant/AssistantModel.hpp000066400000000000000000000114451434616504300300360ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef ASSISTANT_MODEL_H_ #define ASSISTANT_MODEL_H_ #include #include // ============================================================================= class AssistantModel : public QObject { class Handlers; Q_OBJECT; Q_PROPERTY(QString email READ getEmail WRITE setEmail NOTIFY emailChanged); Q_PROPERTY(QString password READ getPassword WRITE setPassword NOTIFY passwordChanged); Q_PROPERTY(QString countryCode READ getCountryCode WRITE setCountryCode NOTIFY countryCodeChanged); Q_PROPERTY(QString phoneNumber READ getPhoneNumber WRITE setPhoneNumber NOTIFY phoneNumberChanged); Q_PROPERTY(QString username READ getUsername WRITE setUsername NOTIFY usernameChanged); Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName NOTIFY displayNameChanged); Q_PROPERTY(QString activationCode READ getActivationCode WRITE setActivationCode NOTIFY activationCodeChanged); Q_PROPERTY(QString configFilename READ getConfigFilename WRITE setConfigFilename NOTIFY configFilenameChanged); Q_PROPERTY(bool isReadingQRCode READ getIsReadingQRCode WRITE setIsReadingQRCode NOTIFY isReadingQRCodeChanged); public: AssistantModel (QObject *parent = Q_NULLPTR); virtual ~AssistantModel(); Q_INVOKABLE void activate (); Q_INVOKABLE void create (); Q_INVOKABLE void login (); Q_INVOKABLE void reset (); Q_INVOKABLE bool addOtherSipAccount (const QVariantMap &map); Q_INVOKABLE void createTestAccount(); Q_INVOKABLE void generateQRCode(); Q_INVOKABLE void requestQRCode(); Q_INVOKABLE void readQRCode(); Q_INVOKABLE void attachAccount(const QString& token); void checkLinkingAccount(); public slots: void onQRCodeFound(const std::string & result); void onApiReceived(QString apiKey); void newQRCodeNotReceivedTest(); signals: void emailChanged (const QString &email, const QString &error); void passwordChanged (const QString &password, const QString &error); void countryCodeChanged (const QString &countryCode); void phoneNumberChanged (const QString &phoneNumber, const QString &error); void usernameChanged (const QString &username, const QString &error); void displayNameChanged (const QString &displayName, const QString &error); void activationCodeChanged (const QString &activationCode); void activateStatusChanged (const QString &error); void createStatusChanged (const QString &error); void loginStatusChanged (const QString &error); void recoverStatusChanged (const QString &error); void configFilenameChanged (const QString &configFilename); void newQRCodeReceived(QString code);// code for QRCode generation. void newQRCodeNotReceived(QString message, int errorCode);// The QRCode couldn't be generated. Return HTTP error code. void provisioningTokenReceived(QString token);// Provisioning token to use void isReadingQRCodeChanged(); void qRCodeFound(QString token); void qRCodeAttached(); void qRCodeNotAttached(QString message, int errorCode); void apiReceived(QString apiKey); private: QString getEmail () const; void setEmail (const QString &email); QString getPassword () const; void setPassword (const QString &password); QString getCountryCode () const; void setCountryCode (const QString &countryCode); QString getPhoneNumber () const; void setPhoneNumber (const QString &phoneNumber); QString getUsername () const; void setUsername (const QString &username); QString getDisplayName () const; void setDisplayName (const QString &displayName); QString getActivationCode () const; void setActivationCode (const QString &activationCode); QString getConfigFilename () const; void setConfigFilename (const QString &configFilename); bool getIsReadingQRCode() const; void setIsReadingQRCode(bool isReading); QString mapAccountCreatorUsernameStatusToString (linphone::AccountCreator::UsernameStatus status) const; QString mCountryCode; QString mConfigFilename; QString mToken; bool mIsReadingQRCode; std::shared_ptr mAccountCreator; std::shared_ptr mHandlers; }; #endif // ASSISTANT_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/authentication/000077500000000000000000000000001434616504300253745ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/authentication/AuthenticationNotifier.cpp000066400000000000000000000033661434616504300325670ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "utils/Utils.hpp" #include "AuthenticationNotifier.hpp" // ============================================================================= using namespace std; AuthenticationNotifier::AuthenticationNotifier (QObject *parent) : QObject(parent) { QObject::connect( CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::authenticationRequested, this, &AuthenticationNotifier::handleAuthenticationRequested ); } void AuthenticationNotifier::handleAuthenticationRequested (const shared_ptr &authInfo) { emit authenticationRequested( QVariant::fromValue(authInfo), Utils::coreStringToAppString(authInfo->getRealm()), QStringLiteral("%1@%2").arg( QString::fromStdString(authInfo->getUsername()) ).arg( Utils::coreStringToAppString(authInfo->getDomain()) ), Utils::coreStringToAppString(authInfo->getUserid()) ); } linphone-desktop-5.0.2/linphone-app/src/components/authentication/AuthenticationNotifier.hpp000066400000000000000000000027271434616504300325740ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef AUTHENTICATION_NOTIFIER_H_ #define AUTHENTICATION_NOTIFIER_H_ #include #include // ============================================================================= namespace linphone { class AuthInfo; } class AuthenticationNotifier : public QObject { Q_OBJECT; public: AuthenticationNotifier (QObject *parent = Q_NULLPTR); signals: void authenticationRequested (const QVariant &authInfo, const QString &realm, const QString &sipAddress, const QString &userId); private: void handleAuthenticationRequested (const std::shared_ptr &authInfo); }; Q_DECLARE_METATYPE(std::shared_ptr); #endif // AUTHENTICATION_NOTIFIER_H_ linphone-desktop-5.0.2/linphone-app/src/components/call/000077500000000000000000000000001434616504300232705ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/call/CallListener.cpp000066400000000000000000000024721434616504300263620ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "CallListener.hpp" #include #include #include #include "CallModel.hpp" // ============================================================================= // ============================================================================= CallListener::CallListener(QObject* parent) : QObject(parent){ } void CallListener::onRemoteRecording(const std::shared_ptr & call, bool recording){ qDebug() << "onRemoteRecording: " << recording; emit remoteRecording(call, recording); }linphone-desktop-5.0.2/linphone-app/src/components/call/CallListener.hpp000066400000000000000000000025731434616504300263710ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CALL_LISTENER_H #define CALL_LISTENER_H #include "utils/LinphoneEnums.hpp" #include // ============================================================================= class CallModel; class CallListener : public QObject, public linphone::CallListener { Q_OBJECT public: CallListener(QObject * parent = nullptr); virtual ~CallListener(){} virtual void onRemoteRecording(const std::shared_ptr & call, bool recording) override; signals: void remoteRecording(const std::shared_ptr & call, bool recording); }; Q_DECLARE_METATYPE(CallListener*) #endif linphone-desktop-5.0.2/linphone-app/src/components/call/CallModel.cpp000066400000000000000000001331041434616504300256320ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "CallModel.hpp" #include #include #include #include #include "app/App.hpp" #include "CallListener.hpp" #include "components/calls/CallsListModel.hpp" #include "components/chat-room/ChatRoomInitializer.hpp" #include "components/chat-room/ChatRoomListener.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "components/conference/ConferenceModel.hpp" #include "components/conferenceInfo/ConferenceInfoModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/notifier/Notifier.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "utils/MediastreamerUtils.hpp" #include "utils/Utils.hpp" #include "linphone/api/c-search-result.h" // ============================================================================= using namespace std; namespace { constexpr char AutoAnswerObjectName[] = "auto-answer-timer"; } void CallModel::connectTo(CallListener * listener){ connect(listener, &CallListener::remoteRecording, this, &CallModel::onRemoteRecording); } CallModel::CallModel (shared_ptr call){ CoreManager *coreManager = CoreManager::getInstance(); SettingsModel *settings = coreManager->getSettingsModel(); connect(this, &CallModel::callIdChanged, this, &CallModel::chatRoomModelChanged);// When the call Id change, the chat room change. connect(this, &CallModel::encryptionChanged, this, &CallModel::securityUpdated); connect(this, &CallModel::isPQZrtpChanged, this, &CallModel::securityUpdated); mCall = call; if(mCall) mCall->setData("call-model", *this); updateIsInConference(); if(mCall) { mCallListener = std::make_shared(); connectTo(mCallListener.get()); mCall->addListener(mCallListener); auto callParams = mCall->getParams(); auto currentParams = mCall->getCurrentParams(); if(currentParams) mEncryption = static_cast(currentParams->getMediaEncryption()); mConferenceVideoLayout = LinphoneEnums::fromLinphone(callParams->getConferenceVideoLayout()); if(mConferenceVideoLayout == LinphoneEnums::ConferenceLayoutGrid && !callParams->videoEnabled()) mConferenceVideoLayout = LinphoneEnums::ConferenceLayoutAudioOnly; if(mCall->getConference()){ if( mConferenceVideoLayout == LinphoneEnums::ConferenceLayoutGrid) settings->setCameraMode(settings->getGridCameraMode()); else settings->setCameraMode(settings->getActiveSpeakerCameraMode()); }else settings->setCameraMode(settings->getCallCameraMode()); } // Deal with auto-answer. if (!isOutgoing()) { if (settings->getAutoAnswerStatus()) { QTimer *timer = new QTimer(this); timer->setInterval(settings->getAutoAnswerDelay()); timer->setSingleShot(true); timer->setObjectName(AutoAnswerObjectName); QObject::connect(timer, &QTimer::timeout, this, &CallModel::acceptWithAutoAnswerDelay); timer->start(); } } CoreHandlers *coreHandlers = coreManager->getHandlers().get(); QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &CallModel::handleCallStateChanged ); QObject::connect(coreHandlers, &CoreHandlers::callEncryptionChanged, this, &CallModel::handleCallEncryptionChanged ); // Update fields and make a search to know to who the call belong mMagicSearch = CoreManager::getInstance()->getCore()->createMagicSearch(); mSearch = std::make_shared(); QObject::connect(mSearch.get(), SIGNAL(searchReceived(std::list> )), this, SLOT(searchReceived(std::list>))); mMagicSearch->addListener(mSearch); if(mCall) { mRemoteAddress = mCall->getRemoteAddress()->clone(); if(mCall->getConference()) { mConferenceModel = ConferenceModel::create(mCall->getConference()); connect(mConferenceModel.get(), &ConferenceModel::participantAdminStatusChanged, this, &CallModel::onParticipantAdminStatusChanged); } auto conferenceInfo = CoreManager::getInstance()->getCore()->findConferenceInformationFromUri(getConferenceAddress()); if( conferenceInfo ){ mConferenceInfoModel = ConferenceInfoModel::create(conferenceInfo); } mMagicSearch->getContactsListAsync(mRemoteAddress->getUsername(),mRemoteAddress->getDomain(), (int)linphone::MagicSearchSource::LdapServers | (int)linphone::MagicSearchSource::Friends, linphone::MagicSearchAggregation::Friend); } } CallModel::~CallModel () { mMagicSearch->removeListener(mSearch); if(mCall){ mCall->removeListener(mCallListener); mConferenceModel = nullptr;// Ordering deletion. mConferenceInfoModel = nullptr; mCall->unsetData("call-model"); mCall = nullptr; } } // ----------------------------------------------------------------------------- QString CallModel::getPeerAddress () const { return Utils::coreStringToAppString(mRemoteAddress->asStringUriOnly()); } QString CallModel::getLocalAddress () const { return mCall ? Utils::coreStringToAppString(mCall->getCallLog()->getLocalAddress()->asStringUriOnly()) : ""; } QString CallModel::getFullPeerAddress () const { return Utils::coreStringToAppString(mRemoteAddress->asString()); } QString CallModel::getFullLocalAddress () const { return mCall ? Utils::coreStringToAppString(mCall->getCallLog()->getLocalAddress()->asString()) : ""; } std::shared_ptr CallModel::getConferenceAddress () const{ std::shared_ptr conferenceAddress; if(mCall){ auto remoteContact = mCall->getRemoteContact(); if (mCall->getDir() == linphone::Call::Dir::Incoming){ if( remoteContact != "" ) conferenceAddress = CoreManager::getInstance()->getCore()->interpretUrl(remoteContact); }else conferenceAddress = mCall->getRemoteAddress()->clone(); } return conferenceAddress; } // ----------------------------------------------------------------------------- ContactModel *CallModel::getContactModel() const{ QString cleanedAddress = mCall ? Utils::cleanSipAddress(Utils::coreStringToAppString(mCall->getRemoteAddress()->asString())) : ""; return CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(cleanedAddress).get(); } ChatRoomModel * CallModel::getChatRoomModel(){ if(mCall && mCall->getCallLog()->getCallId() != "" && !isConference()){// No chat rooms for conference (TODO) auto currentParams = mCall->getCurrentParams(); bool isEncrypted = currentParams->getMediaEncryption() != linphone::MediaEncryption::None; SettingsModel * settingsModel = CoreManager::getInstance()->getSettingsModel(); if(mChatRoom){// We already created a chat room. if( mChatRoom->getState() == linphone::ChatRoom::State::Created) return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mChatRoom, true).get(); else// Chat room is not yet created. return nullptr; } if( (settingsModel->getSecureChatEnabled() && (!settingsModel->getStandardChatEnabled() || (settingsModel->getStandardChatEnabled() && isEncrypted)) )){// Make a secure chat std::shared_ptr core = CoreManager::getInstance()->getCore(); std::shared_ptr params = core->createDefaultChatRoomParams(); auto callLog = mCall->getCallLog(); auto callLocalAddress = callLog->getLocalAddress(); std::list> participants; // Copy parameters params->enableEncryption(true); auto conference = mCall->getConference(); if(conference){// This is a group params->enableGroup(true); params->setSubject(conference->getSubject()); auto conferenceParaticipants = conference->getParticipantList(); for(auto p : conferenceParaticipants){ participants.push_back(p->getAddress()->clone()); } }else{ params->enableGroup(false); participants.push_back(mCall->getRemoteAddress()->clone()); } if( params->getSubject() == "") // A linphone::ChatRoomBackend::FlexisipChat need a subject. params->setSubject("Dummy Subject"); mChatRoom = core->searchChatRoom(params, callLocalAddress , nullptr , participants); if(mChatRoom) return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mChatRoom, true).get(); else{// Wait for creation. Secure chat rooms cannot be used before being created. mChatRoom = CoreManager::getInstance()->getCore()->createChatRoom(params, callLocalAddress, participants); auto initializer = ChatRoomInitializer::create(mChatRoom); connect(initializer.get(), &ChatRoomInitializer::finished, this, &CallModel::onChatRoomInitialized, Qt::DirectConnection); ChatRoomInitializer::start(initializer); return nullptr; } }else return CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(mCall->getChatRoom(), true).get(); }else return nullptr; } ConferenceModel * CallModel::getConferenceModel(){ return mConferenceModel.get(); } ConferenceInfoModel * CallModel::getConferenceInfoModel(){ return mConferenceInfoModel.get(); } QSharedPointer CallModel::getConferenceSharedModel(){ if(mCall->getConference() && !mConferenceModel){ mConferenceModel = ConferenceModel::create(mCall->getConference()); connect(mConferenceModel.get(), &ConferenceModel::participantAdminStatusChanged, this, &CallModel::onParticipantAdminStatusChanged); emit conferenceModelChanged(); } return mConferenceModel; } bool CallModel::isConference () const{ // Check status to avoid crash when requesting a conference on an ended call. bool isConf = false; if(mCall){ // Do not call getConference on Ended status. isConf = (getStatus() != CallStatusEnded && mCall->getConference() != nullptr) || mConferenceInfoModel != nullptr; if(!isConf){// Check special cases for Linphone. Having conf-id for a conference URI is not standard. auto remoteAddress = mCall->getRemoteAddress(); if( remoteAddress->getDomain() == Constants::LinphoneDomain){ isConf = remoteAddress->hasUriParam("conf-id"); } } } return isConf; } bool CallModel::isOneToOne() const{ return !isConference(); } // ----------------------------------------------------------------------------- void CallModel::setRecordFile (const shared_ptr &callParams) { callParams->setRecordFile(Utils::appStringToCoreString( CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder() .append(generateSavedFilename()) .append(".mkv") )); } void CallModel::setRecordFile (const shared_ptr &callParams, const QString &to) { const QString from( Utils::coreStringToAppString( CoreManager::getInstance()->getAccountSettingsModel()->getUsedSipAddress()->getUsername() ) ); callParams->setRecordFile(Utils::appStringToCoreString( CoreManager::getInstance()->getSettingsModel()->getSavedCallsFolder() .append(generateSavedFilename(from, to)) .append(".mkv") )); } // ----------------------------------------------------------------------------- void CallModel::updateStats (const shared_ptr &callStats) { switch (callStats->getType()) { case linphone::StreamType::Text: case linphone::StreamType::Unknown: break; case linphone::StreamType::Audio: if( callStats) isPQZrtp(callStats->isZrtpKeyAgreementAlgoPostQuantum() ? CallPQStateOn : CallPQStateOff); updateStats(callStats, mAudioStats); updateEncrypionStats(callStats, mEncryptionStats); break; case linphone::StreamType::Video: updateStats(callStats, mVideoStats); break; } emit statsUpdated(); } // ----------------------------------------------------------------------------- float CallModel::getSpeakerVolumeGain () const { float gain = mCall ? mCall->getSpeakerVolumeGain() : 0; if( gain < 0) gain = CoreManager::getInstance()->getSettingsModel()->getPlaybackGain(); return gain; } void CallModel::setSpeakerVolumeGain (float volume) { Q_ASSERT(volume >= 0.0f && volume <= 1.0f); float oldGain = getSpeakerVolumeGain(); if( mCall && mCall->getSpeakerVolumeGain() >= 0) mCall->setSpeakerVolumeGain(volume); else CoreManager::getInstance()->getSettingsModel()->setPlaybackGain(volume); if( (int)(oldGain * 1000) != (int)(volume*1000)) emit speakerVolumeGainChanged(getSpeakerVolumeGain()); } float CallModel::getMicroVolumeGain () const { float gain = mCall ? mCall->getMicrophoneVolumeGain() : 0.0; if( gain < 0) gain = CoreManager::getInstance()->getSettingsModel()->getCaptureGain(); return gain; } void CallModel::setMicroVolumeGain (float volume) { Q_ASSERT(volume >= 0.0f && volume <= 1.0f); float oldGain = getMicroVolumeGain(); if(mCall && mCall->getMicrophoneVolumeGain() >= 0) mCall->setMicrophoneVolumeGain(volume); else CoreManager::getInstance()->getSettingsModel()->setCaptureGain(volume); if( (int)(oldGain * 1000) != (int)(volume*1000)) emit microVolumeGainChanged(getMicroVolumeGain()); } // ----------------------------------------------------------------------------- void CallModel::notifyCameraFirstFrameReceived (unsigned int width, unsigned int height) { if (mNotifyCameraFirstFrameReceived) { mNotifyCameraFirstFrameReceived = false; emit cameraFirstFrameReceived(width, height); } } // ----------------------------------------------------------------------------- void CallModel::accept () { accept(false); } void CallModel::acceptWithVideo () { accept(true); } void CallModel::terminate () { mEndByUser = true; CoreManager *core = CoreManager::getInstance(); core->lockVideoRender(); if(mCall) mCall->terminate(); core->unlockVideoRender(); } // ----------------------------------------------------------------------------- void CallModel::askForTransfer () { CoreManager::getInstance()->getCallsListModel()->askForTransfer(this); } void CallModel::askForAttendedTransfer () { CoreManager::getInstance()->getCallsListModel()->askForAttendedTransfer(this); } bool CallModel::transferTo (const QString &sipAddress) { bool failure = mCall ? !!mCall->transferTo(Utils::interpretUrl(sipAddress)) : false; if (failure) qWarning() << QStringLiteral("Unable to transfer: `%1`.").arg(sipAddress); return !failure; } bool CallModel::transferToAnother (const QString &peerAddress) { qInfo() << QStringLiteral("Transferring to another: `%1`.").arg(peerAddress); CallModel *transferCallModel = CoreManager::getInstance()->getCallsListModel()->findCallModelFromPeerAddress(peerAddress); if (transferCallModel == nullptr) { qWarning() << QStringLiteral("Unable to transfer to another: `%1` (peer not found)").arg(peerAddress); return false; } bool failure = mCall ? !!transferCallModel->mCall->transferToAnother(mCall) : false; if (failure) qWarning() << QStringLiteral("Unable to transfer to another: `%1` (transfer failed)").arg(peerAddress); return !failure; } // ----------------------------------------------------------------------------- void CallModel::acceptVideoRequest () { if(mCall) { shared_ptr params = CoreManager::getInstance()->getCore()->createCallParams(mCall); params->enableVideo(true); mCall->acceptUpdate(params); } } void CallModel::rejectVideoRequest () { if(mCall) { shared_ptr params = CoreManager::getInstance()->getCore()->createCallParams(mCall); params->enableVideo(false); mCall->acceptUpdate(params); } } void CallModel::takeSnapshot () { static QString oldName; QString newName(generateSavedFilename().append(".jpg")); if (newName == oldName) { qWarning() << QStringLiteral("Unable to take snapshot. Wait one second."); return; } oldName = newName; qInfo() << QStringLiteral("Take snapshot of call:") << this; const QString filePath(CoreManager::getInstance()->getSettingsModel()->getSavedScreenshotsFolder().append(newName)); if(mCall) mCall->takeVideoSnapshot(Utils::appStringToCoreString(filePath)); App::getInstance()->getNotifier()->notifySnapshotWasTaken(filePath); } void CallModel::startRecording () { if (mRecording) return; qInfo() << QStringLiteral("Start recording call:") << this; if(mCall) mCall->startRecording(); mRecording = true; emit recordingChanged(true); } void CallModel::stopRecording () { if (!mRecording) return; qInfo() << QStringLiteral("Stop recording call:") << this; mRecording = false; if(mCall) { mCall->stopRecording(); App::getInstance()->getNotifier()->notifyRecordingCompleted( Utils::coreStringToAppString(mCall->getParams()->getRecordFile()) ); } emit recordingChanged(false); } // ----------------------------------------------------------------------------- void CallModel::handleCallEncryptionChanged (const shared_ptr &call) { if (call == mCall){ if(!setEncryption(static_cast(mCall->getCurrentParams()->getMediaEncryption()))) emit securityUpdated(); } } void CallModel::handleCallStateChanged (const shared_ptr &call, linphone::Call::State state) { if (call != mCall) return; updateIsInConference(); if(!mConferenceInfoModel){// Check if conferenceInfo has been set. auto conferenceInfo = CoreManager::getInstance()->getCore()->findConferenceInformationFromUri(getConferenceAddress()); if( conferenceInfo ){ mConferenceInfoModel = ConferenceInfoModel::create(conferenceInfo); emit conferenceInfoModelChanged(); } } switch (state) { case linphone::Call::State::Error: case linphone::Call::State::End: setCallErrorFromReason(call->getReason()); stopAutoAnswerTimer(); stopRecording(); mPausedByRemote = false; break; case linphone::Call::State::StreamsRunning: { if (!mWasConnected && CoreManager::getInstance()->getSettingsModel()->getAutomaticallyRecordCalls()) { startRecording(); mWasConnected = true; } mPausedByRemote = false; updateConferenceVideoLayout(); setCallId(QString::fromStdString(mCall->getCallLog()->getCallId())); updateEncryption(); break; } case linphone::Call::State::Connected: getConferenceSharedModel(); case linphone::Call::State::Referred: case linphone::Call::State::Released: mPausedByRemote = false; break; case linphone::Call::State::PausedByRemote: mNotifyCameraFirstFrameReceived = true; mPausedByRemote = true; break; case linphone::Call::State::Pausing: mNotifyCameraFirstFrameReceived = true; mPausedByUser = true; break; case linphone::Call::State::Resuming: mPausedByUser = false; break; case linphone::Call::State::UpdatedByRemote: qDebug() << "UpdatedByRemote : " << (mCall ? QString( "Video enabled ? CurrentParams:") + mCall->getCurrentParams()->videoEnabled() + QString(", RemoteParams:")+mCall->getRemoteParams()->videoEnabled() : " call NULL"); if (mCall && !mCall->getCurrentParams()->videoEnabled() && mCall->getRemoteParams()->videoEnabled()) { mCall->deferUpdate(); emit videoRequested(); } break; case linphone::Call::State::Idle: case linphone::Call::State::IncomingReceived: case linphone::Call::State::OutgoingInit: case linphone::Call::State::OutgoingProgress: case linphone::Call::State::OutgoingRinging: case linphone::Call::State::OutgoingEarlyMedia: case linphone::Call::State::Paused: case linphone::Call::State::IncomingEarlyMedia: case linphone::Call::State::Updating: case linphone::Call::State::EarlyUpdatedByRemote: case linphone::Call::State::EarlyUpdating: case linphone::Call::State::PushIncomingReceived: break; } emit statusChanged(getStatus()); } // ----------------------------------------------------------------------------- void CallModel::accept (bool withVideo) { stopAutoAnswerTimer(); CoreManager *coreManager = CoreManager::getInstance(); QQuickWindow *callsWindow = App::getInstance()->getCallsWindow(); if (callsWindow) { if (coreManager->getSettingsModel()->getKeepCallsWindowInBackground()) { if (!callsWindow->isVisible()) callsWindow->showMinimized(); } else App::smartShowWindow(callsWindow); } qApp->processEvents(); // Process GUI events before accepting in order to be synchronized with Call objects and be ready to get SDK events shared_ptr core = coreManager->getCore(); if(mCall) { shared_ptr params = core->createCallParams(mCall); params->enableVideo(withVideo); setRecordFile(params); auto localAddress = mCall->getCallLog()->getLocalAddress(); for(auto account : coreManager->getAccountList()){ if( account->getParams()->getIdentityAddress()->weakEqual(localAddress)){ params->setAccount(account); break; } } mCall->acceptWithParams(params); } } // ----------------------------------------------------------------------------- void CallModel::updateIsInConference () { if (mIsInConference != (mCall && mCall->getCurrentParams()->getLocalConferenceMode() )) { mIsInConference = !mIsInConference; } emit isInConferenceChanged(mIsInConference); } // ----------------------------------------------------------------------------- void CallModel::stopAutoAnswerTimer () const { QTimer *timer = findChild(AutoAnswerObjectName, Qt::FindDirectChildrenOnly); if (timer) { timer->stop(); timer->deleteLater(); } } // ----------------------------------------------------------------------------- CallModel::CallStatus CallModel::getStatus () const { if(mCall){ switch (mCall->getState()) { case linphone::Call::State::Connected: case linphone::Call::State::StreamsRunning: return CallStatusConnected; case linphone::Call::State::End: case linphone::Call::State::Error: case linphone::Call::State::Referred: case linphone::Call::State::Released: return CallStatusEnded; case linphone::Call::State::Paused: case linphone::Call::State::PausedByRemote: case linphone::Call::State::Pausing: case linphone::Call::State::Resuming: return CallStatusPaused; case linphone::Call::State::Updating: case linphone::Call::State::UpdatedByRemote: return mPausedByRemote ? CallStatusPaused : CallStatusConnected; case linphone::Call::State::EarlyUpdatedByRemote: case linphone::Call::State::EarlyUpdating: case linphone::Call::State::Idle: case linphone::Call::State::IncomingEarlyMedia: case linphone::Call::State::IncomingReceived: case linphone::Call::State::OutgoingEarlyMedia: case linphone::Call::State::OutgoingInit: case linphone::Call::State::OutgoingProgress: case linphone::Call::State::OutgoingRinging: break; } return mCall->getDir() == linphone::Call::Dir::Incoming ? CallStatusIncoming : CallStatusOutgoing; }else return CallStatusIdle; } // ----------------------------------------------------------------------------- void CallModel::acceptWithAutoAnswerDelay () { CoreManager *coreManager = CoreManager::getInstance(); SettingsModel *settingsModel = coreManager->getSettingsModel(); // Use auto-answer if activated and it's the only call. if (settingsModel->getAutoAnswerStatus() && coreManager->getCore()->getCallsNb() == 1) { if (mCall && mCall->getRemoteParams()->videoEnabled() && settingsModel->getAutoAnswerVideoStatus() && settingsModel->getVideoSupported()) acceptWithVideo(); else accept(); } } // ----------------------------------------------------------------------------- QString CallModel::getCallError () const { return mCallError; } void CallModel::setCallErrorFromReason (linphone::Reason reason) { switch (reason) { case linphone::Reason::Declined: mCallError = tr("callErrorDeclined"); break; case linphone::Reason::NotFound: mCallError = tr("callErrorNotFound"); break; case linphone::Reason::Busy: mCallError = tr("callErrorBusy"); break; case linphone::Reason::NotAcceptable: mCallError = tr("callErrorNotAcceptable"); break; case linphone::Reason::None: if(!mEndByUser) mCallError = tr("callErrorHangUp"); break; default: break; } if (!mCallError.isEmpty()) qInfo() << QStringLiteral("Call terminated with error (%1):").arg(mCallError) << this; emit callErrorChanged(mCallError); } void CallModel::setCallId(const QString& callId){ if(callId != mCallId){ mCallId = callId; emit callIdChanged(); } } // ----------------------------------------------------------------------------- int CallModel::getDuration () const { return mCall ? mCall->getDuration() : 0; } float CallModel::getQuality () const { return mCall ? mCall->getCurrentQuality() : 0.0; } // ----------------------------------------------------------------------------- float CallModel::getSpeakerVu () const { if (mCall && mCall->getState() == linphone::Call::State::StreamsRunning) return MediastreamerUtils::computeVu(mCall->getPlayVolume()); return 0.0; } float CallModel::getMicroVu () const { if (mCall && mCall->getState() == linphone::Call::State::StreamsRunning) return MediastreamerUtils::computeVu(mCall->getRecordVolume()); return 0.0; } // ----------------------------------------------------------------------------- bool CallModel::getSpeakerMuted () const { return mCall && mCall->getSpeakerMuted(); } void CallModel::setSpeakerMuted (bool status) { if (status == getSpeakerMuted()) return; if(mCall) mCall->setSpeakerMuted(status); emit speakerMutedChanged(getSpeakerMuted()); } // ----------------------------------------------------------------------------- bool CallModel::getMicroMuted () const { return mCall && mCall->getMicrophoneMuted(); } void CallModel::setMicroMuted (bool status) { if (status == getMicroMuted()) return; if(mCall) mCall->setMicrophoneMuted(status); emit microMutedChanged(getMicroMuted()); } // ----------------------------------------------------------------------------- bool CallModel::getCameraEnabled () const{ return mCall && (((int)mCall->getCurrentParams()->getVideoDirection() & (int)linphone::MediaDirection::SendOnly) == (int)linphone::MediaDirection::SendOnly); } void CallModel::setCameraEnabled (bool status){ shared_ptr core = CoreManager::getInstance()->getCore(); if (!core->videoSupported()) { qWarning() << QStringLiteral("Unable to update video call property. (Video not supported.)"); return; } if(mCall) { switch (mCall->getState()) { case linphone::Call::State::Connected: case linphone::Call::State::StreamsRunning: break; default: { qWarning() << "Cannot set Camera mode because of call status : " << (int)mCall->getState() << " is not in {" <<(int)linphone::Call::State::Connected << ", " <<(int)linphone::Call::State::StreamsRunning << "}"; return; } } if (status == getCameraEnabled()) return; shared_ptr params = core->createCallParams(mCall); params->enableVideo(true); params->setVideoDirection(status ? linphone::MediaDirection::SendRecv : linphone::MediaDirection::RecvOnly); mCall->update(params); } } // ----------------------------------------------------------------------------- bool CallModel::getPausedByUser () const { return mPausedByUser; } void CallModel::setPausedByUser (bool status) { if(mCall){ switch (mCall->getState()) { case linphone::Call::State::Connected: case linphone::Call::State::StreamsRunning: case linphone::Call::State::Paused: case linphone::Call::State::PausedByRemote: break; default: return; } if (status) { if (!mPausedByUser) mCall->pause(); return; } if (mPausedByUser) mCall->resume(); } } // ----------------------------------------------------------------------------- bool CallModel::getRemoteVideoEnabled () const { shared_ptr params = mCall->getRemoteParams(); return params && params->videoEnabled(); } bool CallModel::getLocalVideoEnabled () const { if(mCall){ shared_ptr params = mCall->getParams(); return params && params->videoEnabled(); }else return true; } bool CallModel::getVideoEnabled () const { if(mCall){ shared_ptr params = mCall->getCurrentParams(); return params && params->videoEnabled(); }else return true; } void CallModel::setVideoEnabled (bool status) { shared_ptr core = CoreManager::getInstance()->getCore(); if (!core->videoSupported()) { qWarning() << QStringLiteral("Unable to update video call property. (Video not supported.)"); return; } if(mCall) { switch (mCall->getState()) { case linphone::Call::State::Connected: case linphone::Call::State::StreamsRunning: break; default: { qWarning() << "Cannot set Video mode because of call status : " << (int)mCall->getState() << " is not in {" <<(int)linphone::Call::State::Connected << ", " <<(int)linphone::Call::State::StreamsRunning << "}"; return; } } if (status == getVideoEnabled()) return; shared_ptr params = core->createCallParams(mCall); params->enableVideo(status); mCall->update(params); } } // ----------------------------------------------------------------------------- bool CallModel::getUpdating () const { if(mCall) { switch (mCall->getState()) { case linphone::Call::State::Connected: case linphone::Call::State::StreamsRunning: case linphone::Call::State::Paused: case linphone::Call::State::PausedByRemote: return false; default: break; } } return true; } bool CallModel::getRecording () const { return mRecording; } bool CallModel::getSnapshotEnabled() const{ return getVideoEnabled() && getConferenceVideoLayout() != LinphoneEnums::ConferenceLayout::ConferenceLayoutGrid; } // ----------------------------------------------------------------------------- void CallModel::sendDtmf (const QString &dtmf) { const char key = dtmf.constData()[0].toLatin1(); qInfo() << QStringLiteral("Send dtmf: `%1`.").arg(key); if(mCall) mCall->sendDtmf(key); CoreManager::getInstance()->getCore()->playDtmf(key, DtmfSoundDelay); } // ----------------------------------------------------------------------------- void CallModel::verifyAuthenticationToken (bool verify) { if(mCall) mCall->setAuthenticationTokenVerified(verify); emit securityUpdated(); } // ----------------------------------------------------------------------------- void CallModel::updateStreams () { if(mCall) mCall->update(nullptr); } void CallModel::toggleSpeakerMute(){ setSpeakerMuted(!getSpeakerMuted()); } // ----------------------------------------------------------------------------- // Set remote display name when a search has been done // Local Friend > LDAP friend > Address > others void CallModel::searchReceived(std::list> results){ bool found = false; for(auto it = results.begin() ; it != results.end() && !found ; ++it){ if((*it)->getFriend()){// Local Friend if((*it)->getFriend()->getAddress()->weakEqual(mRemoteAddress)){ setRemoteDisplayName((*it)->getFriend()->getName()); found = true; } }else{ if((*it)->getAddress()->weakEqual(mRemoteAddress)){ std::string newDisplayName = (*it)->getAddress()->getDisplayName(); if(!newDisplayName.empty()){ // LDAP friend if( ((*it)->getSourceFlags() & (int) linphone::MagicSearchSource::LdapServers) == (int) linphone::MagicSearchSource::LdapServers){ setRemoteDisplayName(newDisplayName); found = true; }else if( Utils::coreStringToAppString(mRemoteAddress->getDisplayName()).isEmpty()){ setRemoteDisplayName(newDisplayName); found = true; } } } } } } void CallModel::endCall(){ if(mCall){ ChatRoomModel * model = getChatRoomModel(); if(model){ model->onCallEnded(mCall); }else{// No chat rooms have been associated for this call. Search one in current chat room list shared_ptr core = CoreManager::getInstance()->getCore(); std::shared_ptr params = core->createDefaultChatRoomParams(); std::list> participants; auto chatRoom = core->searchChatRoom(params, mCall->getCallLog()->getLocalAddress() , mCall->getRemoteAddress() , participants); QSharedPointer chatRoomModel= CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(chatRoom, false); if(chatRoomModel) chatRoomModel->onCallEnded(mCall); } } } bool CallModel::getRemoteRecording() const{ return mCall && mCall->getRemoteParams() && mCall->getRemoteParams()->isRecording(); } void CallModel::onRemoteRecording(const std::shared_ptr & call, bool recording){ emit remoteRecordingChanged(recording); } void CallModel::onChatRoomInitialized(int state){ qInfo() << "[CallModel] Chat room initialized with state : " << state; emit chatRoomModelChanged(); } void CallModel::onParticipantAdminStatusChanged(const std::shared_ptr & participant){ if(mConferenceModel && participant == mConferenceModel->getConference()->getMe()) { emit meAdminChanged(); } } void CallModel::setRemoteDisplayName(const std::string& name){ mRemoteAddress->setDisplayName(name); if(mCall) { auto callLog = mCall->getCallLog(); if(name!= "") { auto core = CoreManager::getInstance()->getCore(); auto address = Utils::interpretUrl(getFullPeerAddress()); callLog->setRemoteAddress(address); } } emit fullPeerAddressChanged(); ChatRoomModel * model = getChatRoomModel(); if(model) model->emitFullPeerAddressChanged(); } QString CallModel::getTransferAddress () const { return mTransferAddress; } void CallModel::setTransferAddress (const QString &transferAddress) { mTransferAddress = transferAddress; emit transferAddressChanged(mTransferAddress); } void CallModel::prepareTransfert(shared_ptr call, const QString& transfertAddress){ if( call && transfertAddress != ""){ CallModel * model = &call->getData("call-model"); model->setTransferAddress(transfertAddress); } } std::shared_ptr CallModel::getRemoteAddress()const{ return mRemoteAddress; } LinphoneEnums::ConferenceLayout CallModel::getConferenceVideoLayout() const{ return mConferenceVideoLayout; // return mCall ? LinphoneEnums::fromLinphone(mCall->getParams()->getConferenceVideoLayout()) : LinphoneEnums::ConferenceLayoutGrid; } void CallModel::changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout){ auto coreManager = CoreManager::getInstance(); if( layout == LinphoneEnums::ConferenceLayoutGrid) coreManager->getSettingsModel()->setCameraMode(coreManager->getSettingsModel()->getGridCameraMode()); else coreManager->getSettingsModel()->setCameraMode(coreManager->getSettingsModel()->getActiveSpeakerCameraMode()); shared_ptr params = coreManager->getCore()->createCallParams(mCall); params->setConferenceVideoLayout(LinphoneEnums::toLinphone(layout)); params->enableVideo(layout != LinphoneEnums::ConferenceLayoutAudioOnly); mCall->update(params); } void CallModel::updateConferenceVideoLayout(){ auto callParams = mCall->getParams(); auto settings = CoreManager::getInstance()->getSettingsModel(); auto newLayout = LinphoneEnums::fromLinphone(callParams->getConferenceVideoLayout()); if( !callParams->videoEnabled()) newLayout = LinphoneEnums::ConferenceLayoutAudioOnly; if( mConferenceVideoLayout != newLayout && !getPausedByUser()){// Only update if not in pause. if(mCall->getConference()){ if( callParams->getConferenceVideoLayout() == linphone::ConferenceLayout::Grid) settings->setCameraMode(settings->getGridCameraMode()); else settings->setCameraMode(settings->getActiveSpeakerCameraMode()); }else settings->setCameraMode(settings->getCallCameraMode()); qDebug() << "Changing layout from " << mConferenceVideoLayout << " into " << newLayout; mConferenceVideoLayout = newLayout; emit conferenceVideoLayoutChanged(); emit snapshotEnabledChanged(); } } // ----------------------------------------------------------------------------- CallModel::CallEncryption CallModel::getEncryption () const { return mEncryption; } bool CallModel::setEncryption(const CallModel::CallEncryption& encryption){ if( encryption != mEncryption){ mEncryption = encryption; emit encryptionChanged(); return true; }else return false; } void CallModel::updateEncryption(){ if(mCall){ auto currentParams = mCall->getCurrentParams(); if( currentParams){ setEncryption(static_cast(currentParams->getMediaEncryption())); } } } bool CallModel::isSecured () const { if(mCall){ auto encryption = getEncryption(); return (encryption == CallEncryptionZrtp && mCall->getAuthenticationTokenVerified()) || encryption == CallEncryptionSrtp || encryption == CallEncryptionDtls; }else return false; } // ----------------------------------------------------------------------------- QString CallModel::getLocalSas () const { if(mCall){ QString token = Utils::coreStringToAppString(mCall->getAuthenticationToken()); if(token.isEmpty()) return ""; else return mCall->getDir() == linphone::Call::Dir::Incoming ? token.left(2).toUpper() : token.right(2).toUpper(); }else return ""; } QString CallModel::getRemoteSas () const { if(mCall){ QString token = Utils::coreStringToAppString(mCall->getAuthenticationToken()); if(token.isEmpty()) return ""; else return mCall->getDir() != linphone::Call::Dir::Incoming ? token.left(2).toUpper() : token.right(2).toUpper(); }else return ""; } // ----------------------------------------------------------------------------- QString CallModel::getSecuredString (const shared_ptr &callStats) const { if(mCall){ switch (getEncryption()) { case CallEncryptionSrtp: return QStringLiteral("SRTP"); case CallEncryptionZrtp: return (callStats && callStats->isZrtpKeyAgreementAlgoPostQuantum() || (!callStats && mIsPQZrtp == CallPQStateOn) ) ? QStringLiteral("Post Quantum ZRTP") : QStringLiteral("ZRTP"); case CallEncryptionDtls: return QStringLiteral("DTLS"); case CallEncryptionNone: break; } } return QString(""); } // ----------------------------------------------------------------------------- QVariantList CallModel::getAudioStats () const { return mAudioStats; } QVariantList CallModel::getVideoStats () const { return mVideoStats; } QVariantList CallModel::getEncryptionStats () const { return mEncryptionStats; } // ----------------------------------------------------------------------------- static inline QVariantMap createStat (const QString &key, const QString &value) { QVariantMap m; m["key"] = key; m["value"] = value; return m; } void CallModel::updateStats (const shared_ptr &callStats, QVariantList &statsList) { if(mCall){ shared_ptr params = mCall->getCurrentParams(); shared_ptr payloadType; switch (callStats->getType()) { case linphone::StreamType::Audio: payloadType = params->getUsedAudioPayloadType(); break; case linphone::StreamType::Video: payloadType = params->getUsedVideoPayloadType(); break; default: return; } QString family; switch (callStats->getIpFamilyOfRemote()) { case linphone::AddressFamily::Inet: family = QStringLiteral("IPv4"); break; case linphone::AddressFamily::Inet6: family = QStringLiteral("IPv6"); break; default: family = QStringLiteral("Unknown"); break; } statsList.clear(); statsList << createStat(tr("callStatsCodec"), payloadType ? QStringLiteral("%1 / %2kHz").arg(Utils::coreStringToAppString(payloadType->getMimeType())).arg(payloadType->getClockRate() / 1000) : QString("")); statsList << createStat(tr("callStatsUploadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getUploadBandwidth()))); statsList << createStat(tr("callStatsDownloadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getDownloadBandwidth()))); statsList << createStat(tr("callStatsIceState"), iceStateToString(callStats->getIceState())); statsList << createStat(tr("callStatsIpFamily"), family); statsList << createStat(tr("callStatsSenderLossRate"), QStringLiteral("%1 %").arg(static_cast(callStats->getSenderLossRate()))); statsList << createStat(tr("callStatsReceiverLossRate"), QStringLiteral("%1 %").arg(static_cast(callStats->getReceiverLossRate()))); switch (callStats->getType()) { case linphone::StreamType::Audio: statsList << createStat(tr("callStatsJitterBuffer"), QStringLiteral("%1 ms").arg(callStats->getJitterBufferSizeMs())); break; case linphone::StreamType::Video: { statsList << createStat(tr("callStatsEstimatedDownloadBandwidth"), QStringLiteral("%1 kbits/s").arg(int(callStats->getEstimatedDownloadBandwidth()))); const QString sentVideoDefinitionName = Utils::coreStringToAppString(params->getSentVideoDefinition()->getName()); const QString sentVideoDefinition = QStringLiteral("%1x%2") .arg(params->getSentVideoDefinition()->getWidth()) .arg(params->getSentVideoDefinition()->getHeight()); statsList << createStat(tr("callStatsSentVideoDefinition"), sentVideoDefinition == sentVideoDefinitionName ? sentVideoDefinition : QStringLiteral("%1 (%2)").arg(sentVideoDefinition).arg(sentVideoDefinitionName)); const QString receivedVideoDefinitionName = Utils::coreStringToAppString(params->getReceivedVideoDefinition()->getName()); const QString receivedVideoDefinition = QString("%1x%2") .arg(params->getReceivedVideoDefinition()->getWidth()) .arg(params->getReceivedVideoDefinition()->getHeight()); statsList << createStat(tr("callStatsReceivedVideoDefinition"), receivedVideoDefinition == receivedVideoDefinitionName ? receivedVideoDefinition : QString("%1 (%2)").arg(receivedVideoDefinition).arg(receivedVideoDefinitionName)); statsList << createStat(tr("callStatsReceivedFramerate"), QStringLiteral("%1 FPS").arg(static_cast(params->getReceivedFramerate()))); statsList << createStat(tr("callStatsSentFramerate"), QStringLiteral("%1 FPS").arg(static_cast(params->getSentFramerate()))); } break; default: break; } } } void CallModel::updateEncrypionStats (const shared_ptr &callStats, QVariantList &statsList) { if( callStats->getType() == linphone::StreamType::Audio) {// just in case statsList.clear(); if(isSecured()) { //: 'Media encryption' : label in encryption section of call statistics statsList << createStat(tr("callStatsMediaEncryption"), getSecuredString(callStats)); if(mCall->getCurrentParams()->getMediaEncryption() == linphone::MediaEncryption::ZRTP){ //: 'Cipher algorithm' : label in encryption section of call statistics statsList << createStat(tr("callStatsCipherAlgo"), Utils::coreStringToAppString(callStats->getZrtpCipherAlgo())); //: 'Key agreement algorithm' : label in encryption section of call statistics statsList << createStat(tr("callStatsKeyAgreementAlgo"), Utils::coreStringToAppString(callStats->getZrtpKeyAgreementAlgo())); //: 'Hash algorithm' : label in encryption section of call statistics statsList << createStat(tr("callStatsHashAlgo"), Utils::coreStringToAppString(callStats->getZrtpHashAlgo())); //: 'Authentication algorithm' : label in encryption section of call statistics statsList << createStat(tr("callStatsAuthAlgo"), Utils::coreStringToAppString(callStats->getZrtpAuthTagAlgo())); //: 'SAS algorithm' : label in encryption section of call statistics statsList << createStat(tr("callStatsSasAlgo"), Utils::coreStringToAppString(callStats->getZrtpSasAlgo())); } } } } void CallModel::isPQZrtp(const CallPQState& isPQ){ if(mIsPQZrtp != isPQ){ mIsPQZrtp = isPQ; emit isPQZrtpChanged(); } } // ----------------------------------------------------------------------------- QString CallModel::iceStateToString (linphone::IceState state) const { switch (state) { case linphone::IceState::NotActivated: return tr("iceStateNotActivated"); case linphone::IceState::Failed: return tr("iceStateFailed"); case linphone::IceState::InProgress: return tr("iceStateInProgress"); case linphone::IceState::ReflexiveConnection: return tr("iceStateReflexiveConnection"); case linphone::IceState::HostConnection: return tr("iceStateHostConnection"); case linphone::IceState::RelayConnection: return tr("iceStateRelayConnection"); } return tr("iceStateInvalid"); } // ----------------------------------------------------------------------------- QString CallModel::generateSavedFilename () const { const shared_ptr callLog(mCall->getCallLog()); return generateSavedFilename( Utils::coreStringToAppString(callLog->getFromAddress()->getUsername()), Utils::coreStringToAppString(callLog->getToAddress()->getUsername()) ); } QString CallModel::generateSavedFilename (const QString &from, const QString &to) { auto escape = [](const QString &str) { constexpr char ReservedCharacters[] = "[<|>|:|\"|/|\\\\|\\?|\\*|\\+|\\|]+"; static QRegularExpression regexp(ReservedCharacters); return QString(str).replace(regexp, ""); }; return QStringLiteral("%1_%2_%3") .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss")) .arg(escape(from)) .arg(escape(to)); } linphone-desktop-5.0.2/linphone-app/src/components/call/CallModel.hpp000066400000000000000000000306011434616504300256350ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CALL_MODEL_H_ #define CALL_MODEL_H_ #include #include #include #include "../search/SearchListener.hpp" #include "utils/LinphoneEnums.hpp" // ============================================================================= class CallListener; class ConferenceInfoModel; class ConferenceModel; class ContactModel; class ChatRoomModel; class CallModel : public QObject { Q_OBJECT Q_PROPERTY(QString peerAddress READ getPeerAddress CONSTANT) Q_PROPERTY(QString localAddress READ getLocalAddress CONSTANT) Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress CONSTANT) Q_PROPERTY(ContactModel *contactModel READ getContactModel CONSTANT ) Q_PROPERTY(ChatRoomModel * chatRoomModel READ getChatRoomModel NOTIFY chatRoomModelChanged) Q_PROPERTY(ConferenceModel * conferenceModel READ getConferenceModel NOTIFY conferenceModelChanged) Q_PROPERTY(ConferenceInfoModel * conferenceInfoModel READ getConferenceInfoModel NOTIFY conferenceInfoModelChanged) Q_PROPERTY(CallStatus status READ getStatus NOTIFY statusChanged) Q_PROPERTY(QString callError READ getCallError NOTIFY callErrorChanged) Q_PROPERTY(QString callId MEMBER mCallId WRITE setCallId NOTIFY callIdChanged) Q_PROPERTY(bool isOutgoing READ isOutgoing CONSTANT) Q_PROPERTY(bool isInConference READ isInConference NOTIFY isInConferenceChanged) Q_PROPERTY(bool isConference READ isConference NOTIFY conferenceInfoModelChanged) Q_PROPERTY(bool isOneToOne READ isOneToOne NOTIFY conferenceInfoModelChanged) Q_PROPERTY(int duration READ getDuration CONSTANT) // Constants but called with a timer in qml. Q_PROPERTY(float quality READ getQuality CONSTANT) Q_PROPERTY(float speakerVu READ getSpeakerVu CONSTANT) Q_PROPERTY(float microVu READ getMicroVu CONSTANT) Q_PROPERTY(bool speakerMuted READ getSpeakerMuted WRITE setSpeakerMuted NOTIFY speakerMutedChanged) Q_PROPERTY(bool microMuted READ getMicroMuted WRITE setMicroMuted NOTIFY microMutedChanged) Q_PROPERTY(float speakerVolumeGain READ getSpeakerVolumeGain WRITE setSpeakerVolumeGain NOTIFY speakerVolumeGainChanged) Q_PROPERTY(float microVolumeGain READ getMicroVolumeGain WRITE setMicroVolumeGain NOTIFY microVolumeGainChanged) Q_PROPERTY(bool pausedByUser READ getPausedByUser WRITE setPausedByUser NOTIFY statusChanged) Q_PROPERTY(bool videoEnabled READ getVideoEnabled WRITE setVideoEnabled NOTIFY statusChanged) Q_PROPERTY(bool localVideoEnabled READ getLocalVideoEnabled WRITE setVideoEnabled NOTIFY statusChanged) Q_PROPERTY(bool cameraEnabled READ getCameraEnabled WRITE setCameraEnabled NOTIFY statusChanged) Q_PROPERTY(bool updating READ getUpdating NOTIFY statusChanged) Q_PROPERTY(bool recording READ getRecording NOTIFY recordingChanged) Q_PROPERTY(bool remoteRecording READ getRemoteRecording NOTIFY remoteRecordingChanged) Q_PROPERTY(bool snapshotEnabled READ getSnapshotEnabled NOTIFY snapshotEnabledChanged) // Grid doesn't enable snapshot Q_PROPERTY(QVariantList audioStats READ getAudioStats NOTIFY statsUpdated) Q_PROPERTY(QVariantList videoStats READ getVideoStats NOTIFY statsUpdated) Q_PROPERTY(QVariantList encryptionStats READ getEncryptionStats NOTIFY statsUpdated) Q_PROPERTY(CallEncryption encryption READ getEncryption WRITE setEncryption NOTIFY encryptionChanged) Q_PROPERTY(bool isSecured READ isSecured NOTIFY securityUpdated) Q_PROPERTY(QString localSas READ getLocalSas NOTIFY securityUpdated) Q_PROPERTY(QString remoteSas READ getRemoteSas NOTIFY securityUpdated) Q_PROPERTY(QString securedString READ getSecuredString NOTIFY securityUpdated) Q_PROPERTY(CallPQState isPQZrtp MEMBER mIsPQZrtp WRITE isPQZrtp NOTIFY isPQZrtpChanged) Q_PROPERTY(QString transferAddress READ getTransferAddress WRITE setTransferAddress NOTIFY transferAddressChanged) Q_PROPERTY(LinphoneEnums::ConferenceLayout conferenceVideoLayout READ getConferenceVideoLayout WRITE changeConferenceVideoLayout NOTIFY conferenceVideoLayoutChanged) public: enum CallStatus { CallStatusConnected, CallStatusEnded, CallStatusIdle, CallStatusIncoming, CallStatusOutgoing, CallStatusPaused }; Q_ENUM(CallStatus); enum CallEncryption { CallEncryptionNone = int(linphone::MediaEncryption::None), CallEncryptionDtls = int(linphone::MediaEncryption::DTLS), CallEncryptionSrtp = int(linphone::MediaEncryption::SRTP), CallEncryptionZrtp = int(linphone::MediaEncryption::ZRTP) }; Q_ENUM(CallEncryption); enum CallPQState { CallPQStateNone = 0, CallPQStateOn, CallPQStateOff, }; Q_ENUM(CallPQState); CallModel (std::shared_ptr call); ~CallModel (); std::shared_ptr getCall () const { return mCall; } QString getPeerAddress () const; QString getLocalAddress () const; QString getFullPeerAddress () const; QString getFullLocalAddress () const; std::shared_ptr getConferenceAddress () const; ContactModel *getContactModel() const; ChatRoomModel * getChatRoomModel(); ConferenceModel* getConferenceModel(); ConferenceInfoModel* getConferenceInfoModel(); QSharedPointer getConferenceSharedModel(); bool isInConference () const { return mIsInConference; } bool isConference () const; bool isOneToOne() const; void setRecordFile (const std::shared_ptr &callParams); static void setRecordFile (const std::shared_ptr &callParams, const QString &to); void updateStats (const std::shared_ptr &callStats); void notifyCameraFirstFrameReceived (unsigned int width, unsigned int height); Q_INVOKABLE void accept (); Q_INVOKABLE void acceptWithVideo (); Q_INVOKABLE void terminate (); Q_INVOKABLE void askForTransfer (); Q_INVOKABLE void askForAttendedTransfer (); Q_INVOKABLE bool transferTo (const QString &sipAddress); Q_INVOKABLE bool transferToAnother (const QString &peerAddress); Q_INVOKABLE bool getRemoteVideoEnabled () const; Q_INVOKABLE void acceptVideoRequest (); Q_INVOKABLE void rejectVideoRequest (); Q_INVOKABLE void takeSnapshot (); Q_INVOKABLE void startRecording (); Q_INVOKABLE void stopRecording (); Q_INVOKABLE void sendDtmf (const QString &dtmf); Q_INVOKABLE void verifyAuthenticationToken (bool verify); Q_INVOKABLE void updateStreams (); Q_INVOKABLE void toggleSpeakerMute(); void setRemoteDisplayName(const std::string& name); QString getTransferAddress () const; void setTransferAddress (const QString &transferAddress); static void prepareTransfert(std::shared_ptr call, const QString& transfertAddress); std::shared_ptr getRemoteAddress()const; LinphoneEnums::ConferenceLayout getConferenceVideoLayout() const; void changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout); // Make a call request void updateConferenceVideoLayout(); // Called from call state changed ater the new layout has been set. static constexpr int DtmfSoundDelay = 200; std::shared_ptr mCall; std::shared_ptr mCallListener; // This is passed to linpĥone object and must be in shared_ptr std::shared_ptr mChatRoom; // Used chat room for the call. std::shared_ptr mRemoteAddress; std::shared_ptr mMagicSearch; public slots: // Set remote display name when a search has been done void searchReceived(std::list> results); void endCall(); void onRemoteRecording(const std::shared_ptr & call, bool recording); void onChatRoomInitialized(int state); void onParticipantAdminStatusChanged(const std::shared_ptr & participant); signals: void meAdminChanged(); void callErrorChanged (const QString &callError); void callIdChanged(); void isInConferenceChanged (bool status); void conferenceModelChanged(); void conferenceInfoModelChanged(); void chatRoomModelChanged(); void speakerMutedChanged (bool status); void microMutedChanged (bool status); void cameraEnabledChanged(); void recordingChanged (bool status); void remoteRecordingChanged(bool status); void snapshotEnabledChanged(); void statsUpdated (); void statusChanged (CallStatus status); void videoRequested (); void securityUpdated (); void encryptionChanged(); void isPQZrtpChanged(); void speakerVolumeGainChanged (float volume); void microVolumeGainChanged (float volume); void cameraFirstFrameReceived (unsigned int width, unsigned int height); void fullPeerAddressChanged(); void transferAddressChanged (const QString &transferAddress); void conferenceVideoLayoutChanged(); public: void handleCallEncryptionChanged (const std::shared_ptr &call); void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); void accept (bool withVideo); void stopAutoAnswerTimer () const; CallStatus getStatus () const; bool isOutgoing () const { return mCall && mCall->getDir() == linphone::Call::Dir::Outgoing; } void updateIsInConference (); void acceptWithAutoAnswerDelay (); QString getCallError () const; void setCallErrorFromReason (linphone::Reason reason); void setCallId(const QString& callId); int getDuration () const; float getQuality () const; float getMicroVu () const; float getSpeakerVu () const; bool getSpeakerMuted () const; void setSpeakerMuted (bool status); bool getMicroMuted () const; void setMicroMuted (bool status); bool getCameraEnabled () const; void setCameraEnabled (bool status); bool getPausedByUser () const; void setPausedByUser (bool status); bool getLocalVideoEnabled () const; bool getVideoEnabled () const; void setVideoEnabled (bool status); bool getUpdating () const; bool getRecording () const; bool getRemoteRecording() const; bool getSnapshotEnabled() const; CallEncryption getEncryption () const; bool setEncryption(const CallModel::CallEncryption& encryption);// true if changed void updateEncryption(); bool isSecured () const; QString getLocalSas () const; QString getRemoteSas () const; QString getSecuredString (const std::shared_ptr &callStats = nullptr) const; QVariantList getAudioStats () const; QVariantList getVideoStats () const; QVariantList getEncryptionStats () const; void updateStats (const std::shared_ptr &callStats, QVariantList &statsList); void updateEncrypionStats (const std::shared_ptr &callStats, QVariantList &statsList); void isPQZrtp(const CallPQState& isPQ); QString iceStateToString (linphone::IceState state) const; float getSpeakerVolumeGain () const; void setSpeakerVolumeGain (float volume); float getMicroVolumeGain () const; void setMicroVolumeGain (float volume); QString generateSavedFilename () const; static QString generateSavedFilename (const QString &from, const QString &to); private: void connectTo(CallListener * listener); bool mIsInConference = false; CallPQState mIsPQZrtp = CallPQStateNone; CallEncryption mEncryption = CallEncryptionNone; bool mEndByUser = false; bool mPausedByRemote = false; bool mPausedByUser = false; bool mRecording = false; LinphoneEnums::ConferenceLayout mConferenceVideoLayout; bool mWasConnected = false; bool mNotifyCameraFirstFrameReceived = true; QString mCallError; QString mCallId; QVariantList mAudioStats; QVariantList mVideoStats; QVariantList mEncryptionStats; std::shared_ptr mSearch; QString mTransferAddress; QSharedPointer mConferenceModel; QSharedPointer mConferenceInfoModel; }; #endif // CALL_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/calls/000077500000000000000000000000001434616504300234535ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/calls/CallsListModel.cpp000066400000000000000000000627151434616504300270450ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include "app/App.hpp" #include "components/call/CallModel.hpp" #include "components/chat-room/ChatRoomInitializer.hpp" #include "components/conference/ConferenceAddModel.hpp" #include "components/conference/ConferenceHelperModel.hpp" #include "components/conference/ConferenceModel.hpp" #include "components/conferenceInfo/ConferenceInfoModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/participant/ParticipantModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "components/timeline/TimelineModel.hpp" #include "utils/Utils.hpp" #include "CallsListModel.hpp" // ============================================================================= using namespace std; namespace { // Delay before removing call in ms. constexpr int DelayBeforeRemoveCall = 6000; } static inline int findCallIndex (QList> &list, const shared_ptr &call) { auto it = find_if(list.begin(), list.end(), [call](QSharedPointer callModel) { return call == callModel.objectCast()->getCall(); }); return it == list.end() ? -1 : int(distance(list.begin(), it)); } static inline int findCallIndex (QList> &list, const CallModel &callModel) { return ::findCallIndex(list, callModel.getCall()); } // ----------------------------------------------------------------------------- CallsListModel::CallsListModel (QObject *parent) : ProxyListModel(parent) { mCoreHandlers = CoreManager::getInstance()->getHandlers(); QObject::connect( mCoreHandlers.get(), &CoreHandlers::callStateChanged, this, &CallsListModel::handleCallStateChanged ); connect(this, &CallsListModel::countChanged, this, &CallsListModel::canMergeCallsChanged); } CallModel *CallsListModel::findCallModelFromPeerAddress (const QString &peerAddress) const { std::shared_ptr address = Utils::interpretUrl(peerAddress); auto it = find_if(mList.begin(), mList.end(), [address](QSharedPointer callModel) { return callModel.objectCast()->getRemoteAddress()->weakEqual(address); }); return it != mList.end() ? it->objectCast().get() : nullptr; } // ----------------------------------------------------------------------------- void CallsListModel::askForTransfer (CallModel *callModel) { emit callTransferAsked(callModel); } void CallsListModel::askForAttendedTransfer (CallModel *callModel) { emit callAttendedTransferAsked(callModel); } // ----------------------------------------------------------------------------- void CallsListModel::launchAudioCall (const QString &sipAddress, const QString& prepareTransfertAddress, const QHash &headers) const { CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = true; shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr address = Utils::interpretUrl(sipAddress); if (!address){ qCritical() << "The calling address is not an interpretable SIP address: " << sipAddress; return; } shared_ptr params = core->createCallParams(nullptr); params->enableVideo(false); QHashIterator iterator(headers); while (iterator.hasNext()) { iterator.next(); params->addCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value())); } if(core->getDefaultAccount()) params->setAccount(core->getDefaultAccount()); CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername())); shared_ptr currentAccount = core->getDefaultAccount(); if(currentAccount){ if(!CoreManager::getInstance()->getSettingsModel()->getWaitRegistrationForCall() || currentAccount->getState() == linphone::RegistrationState::Ok) CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); else{ QObject * context = new QObject(); QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context, [address,core,params,currentAccount,prepareTransfertAddress, context](const std::shared_ptr &account, linphone::RegistrationState state) mutable { if(context && account==currentAccount && state==linphone::RegistrationState::Ok){ CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); context->deleteLater(); context = nullptr; } }); } }else CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); } void CallsListModel::launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers, const QString& prepareTransfertAddress) const { CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = true; shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress)); if (!address) return; shared_ptr params = core->createCallParams(nullptr); params->enableVideo(false); QHashIterator iterator(headers); while (iterator.hasNext()) { iterator.next(); params->addCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value())); } if(core->getDefaultAccount()) params->setAccount(core->getDefaultAccount()); CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername())); shared_ptr currentAccount = core->getDefaultAccount(); params->setMediaEncryption(LinphoneEnums::toLinphone(encryption)); if(currentAccount){ if(!CoreManager::getInstance()->getSettingsModel()->getWaitRegistrationForCall() || currentAccount->getState() == linphone::RegistrationState::Ok) CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); else{ QObject * context = new QObject(); QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context, [address,core,params,currentAccount,prepareTransfertAddress, context](const std::shared_ptr &account, linphone::RegistrationState state) mutable { if(context && account==currentAccount && state==linphone::RegistrationState::Ok){ CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); context->deleteLater(); context = nullptr; } }); } }else CallModel::prepareTransfert(core->inviteAddressWithParams(address, params), prepareTransfertAddress); } void CallsListModel::launchVideoCall (const QString &sipAddress, const QString& prepareTransfertAddress, const bool& autoSelectAfterCreation, QVariantMap options) const { CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = autoSelectAfterCreation; shared_ptr core = CoreManager::getInstance()->getCore(); if (!core->videoSupported()) { qWarning() << QStringLiteral("Unable to launch video call. (Video not supported.) Launching audio call..."); launchAudioCall(sipAddress, prepareTransfertAddress, {}); return; } shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress)); if (!address) return; shared_ptr params = core->createCallParams(nullptr); auto layout = options.contains("layout") ? LinphoneEnums::toLinphone((LinphoneEnums::ConferenceLayout)options["layout"].toInt()) : linphone::ConferenceLayout::Grid; bool enableMicro =options.contains("micro") ? options["micro"].toBool() : true; bool enableVideo = options.contains("video") ? options["video"].toBool() : true; bool enableCamera = options.contains("camera") ? options["camera"].toBool() : true; bool enableSpeaker = options.contains("audio") ? options["audio"].toBool() : true; params->setConferenceVideoLayout(layout); params->enableMic(enableMicro); params->enableVideo(enableVideo); params->setVideoDirection(enableCamera ? linphone::MediaDirection::SendRecv : linphone::MediaDirection::RecvOnly); if(core->getDefaultAccount()) params->setAccount(core->getDefaultAccount()); CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername())); auto call = core->inviteAddressWithParams(address, params); call->setSpeakerMuted(!enableSpeaker); qInfo() << "Launch " << (enableVideo ? "video" : "audio") << " call; camera: " << enableCamera<< " speaker:" << enableSpeaker << ", micro:" << params->micEnabled() << ", layout:" << (int)layout; CallModel::prepareTransfert(call, prepareTransfertAddress); } QVariantMap CallsListModel::launchChat(const QString &sipAddress, const int& securityLevel) const{ QVariantList participants; participants << sipAddress; return createChatRoom("", securityLevel, participants, true); } ChatRoomModel* CallsListModel::createChat (const QString &participantAddress) const{ CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = true; shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(participantAddress)); if (!address) return nullptr; std::shared_ptr params = core->createDefaultChatRoomParams(); std::list > participants; std::shared_ptr localAddress; participants.push_back(address); params->setBackend(linphone::ChatRoomBackend::Basic); qInfo() << "Create ChatRoom with " < chatRoom = core->createChatRoom(params, localAddress, participants); if( chatRoom != nullptr){ auto timelineList = CoreManager::getInstance()->getTimelineListModel(); auto timeline = timelineList->getTimeline(chatRoom, true); return timeline->getChatRoomModel(); } return nullptr; } ChatRoomModel* CallsListModel::createChat (CallModel * model){ if(model){ return model->getChatRoomModel(); } return nullptr; } bool CallsListModel::createSecureChat (const QString& subject, const QString &participantAddress) const{ CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = true; shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(participantAddress)); if (!address) return false; std::shared_ptr params = core->createDefaultChatRoomParams(); std::list > participants; std::shared_ptr localAddress; participants.push_back(address); params->enableEncryption(true); params->setSubject(Utils::appStringToCoreString(subject)); params->enableEncryption(true); params->enableGroup(true); qInfo() << "Create secure ChatRoom: " << subject << ", from " << QString::fromStdString(localAddress->asString()) << " and with " < chatRoom = core->createChatRoom(params, localAddress, participants); // Still needed? // if( chatRoom != nullptr){ // auto timelineList = CoreManager::getInstance()->getTimelineListModel(); // timelineList->update(); // auto timeline = timelineList->getTimeline(chatRoom, false); // if(!timeline){ // timeline = timelineList->getTimeline(chatRoom, true); // timelineList->add(timeline); // } // return timeline->getChatRoomModel(); // } return chatRoom != nullptr; } // Created, timeline that can be used QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants, const bool& selectAfterCreation) const{ return createChatRoom(subject, securityLevel, nullptr, participants, selectAfterCreation); } QVariantMap CallsListModel::createChatRoom(const QString& subject, const int& securityLevel, std::shared_ptr localAddress, const QVariantList& participants, const bool& selectAfterCreation) const{ CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = selectAfterCreation; QVariantMap result; shared_ptr core = CoreManager::getInstance()->getCore(); std::shared_ptr chatRoom; QList< std::shared_ptr> admins; QSharedPointer timeline; auto timelineList = CoreManager::getInstance()->getTimelineListModel(); QString localAddressStr = (localAddress ? Utils::coreStringToAppString(localAddress->asStringUriOnly()) : "local"); qInfo() << "Create ChatRoom: " << subject << " at " << securityLevel << " security, from " << localAddressStr << " and with " << participants; std::shared_ptr params = core->createDefaultChatRoomParams(); std::list > chatRoomParticipants; for(auto p : participants){ ParticipantModel* participant = p.value(); std::shared_ptr address; if(participant) { address = Utils::interpretUrl(participant->getSipAddress()); if(participant->getAdminStatus()) admins << address; }else{ QString participant = p.toString(); if( participant != "") address = Utils::interpretUrl(participant); } if( address) chatRoomParticipants.push_back( address ); else qWarning() << "Failed to add participant to conference, bad address : " << (participant ? participant->getSipAddress() : p.toString()); } params->enableEncryption(securityLevel>0); if( securityLevel<=0) params->setBackend(linphone::ChatRoomBackend::Basic); params->enableGroup( subject!="" ); if(chatRoomParticipants.size() > 0) { if(!params->groupEnabled()) {// Chat room is one-one : check if it is already exist with empty or dummy subject chatRoom = core->searchChatRoom(params, localAddress , nullptr , chatRoomParticipants); if(chatRoom && ChatRoomModel::isTerminated(chatRoom)) chatRoom = nullptr; params->setSubject(subject != ""?Utils::appStringToCoreString(subject):"Dummy Subject"); if(!chatRoom) chatRoom = core->searchChatRoom(params, localAddress , nullptr , chatRoomParticipants); if(chatRoom && ChatRoomModel::isTerminated(chatRoom)) chatRoom = nullptr; }else params->setSubject(subject != ""?Utils::appStringToCoreString(subject):"Dummy Subject"); if( !chatRoom) { chatRoom = core->createChatRoom(params, localAddress, chatRoomParticipants); if(chatRoom != nullptr && admins.size() > 0){ auto initializer = ChatRoomInitializer::create(chatRoom); initializer->setAdminsData(admins); ChatRoomInitializer::start(initializer); } timeline = timelineList->getTimeline(chatRoom, true); }else{ if(admins.size() > 0){ ChatRoomInitializer::create(chatRoom)->setAdmins(admins); } timeline = timelineList->getTimeline(chatRoom, true); } if(timeline){ CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = false; result["chatRoomModel"] = QVariant::fromValue(timeline->getChatRoomModel()); if(selectAfterCreation) {// The timeline here will not receive the first creation event. Set Selected if needed timeline->delaySelected(); } } } if( !chatRoom) qWarning() << "Chat room cannot be created"; result["created"] = (chatRoom != nullptr); return result; } void CallsListModel::prepareConferenceCall(ConferenceInfoModel * model){ if(model->getConferenceInfoState() != LinphoneEnums::ConferenceInfoStateCancelled) { auto app = App::getInstance(); app->smartShowWindow(app->getCallsWindow()); emit callConferenceAsked(model); } } int CallsListModel::addAllToConference(){ return CoreManager::getInstance()->getCore()->addAllToConference(); } void CallsListModel::mergeAll(){ auto core = CoreManager::getInstance()->getCore(); auto currentCalls = CoreManager::getInstance()->getCore()->getCalls(); shared_ptr conference = core->getConference(); // Search a managable conference from calls if(!conference){ for(auto call : currentCalls){ auto dbConference = call->getConference(); if(dbConference && dbConference->getMe()->isAdmin()){ conference = dbConference; break; } } } auto currentCall = CoreManager::getInstance()->getCore()->getCurrentCall(); bool enablingVideo = false; if( currentCall ) enablingVideo = currentCall->getCurrentParams()->videoEnabled(); if(!conference){ auto parameters = core->createConferenceParams(conference); if(!CoreManager::getInstance()->getSettingsModel()->getVideoConferenceEnabled()) { parameters->enableVideo(false); parameters->setConferenceFactoryAddress(nullptr);// Do a local conference parameters->setSubject("Local meeting"); }else{ parameters->enableVideo(enablingVideo); parameters->setSubject("Meeting"); } conference = core->createConferenceWithParams(parameters); } list> allLinphoneAddresses; list> newCalls; list> runningCallsToAdd; for(auto call : currentCalls){ if(!call->getConference()){ runningCallsToAdd.push_back(call); } } // 1) Add running calls if( runningCallsToAdd.size() > 0){ conference->addParticipants(runningCallsToAdd); } /* // 2) Put in pause and remove all calls that are not in the conference list for(const auto &call : CoreManager::getInstance()->getCore()->getCalls()){ const std::string callAddress = call->getRemoteAddress()->asStringUriOnly(); auto address = allLinphoneAddresses.begin(); while(address != allLinphoneAddresses.end() && (*address)->asStringUriOnly() != callAddress) ++address; if(address == allLinphoneAddresses.end()){// Not in conference list : put in pause and remove it from conference if it's the case if( call->getParams()->getLocalConferenceMode() ){// Remove conference if it is not yet requested CoreManager::getInstance()->getCore()->removeFromConference(call); }else call->pause(); } }*/ } // ----------------------------------------------------------------------------- int CallsListModel::getRunningCallsNumber () const { return CoreManager::getInstance()->getCore()->getCallsNb(); } bool CallsListModel::canMergeCalls()const{ auto calls = CoreManager::getInstance()->getCore()->getCalls(); bool mergableConference = false; int mergableCalls = 0; bool mergable = false; for(auto itCall = calls.begin(); !mergable && itCall != calls.end() ; ++itCall ) { auto conference = (*itCall)->getConference(); if(conference){ if( !mergableConference ) mergableConference = (conference && conference->getMe()->isAdmin()); }else{ ++mergableCalls; } mergable = (mergableConference && mergableCalls>0) // A call can be merged into the conference || mergableCalls>1;// 2 calls can be merged } return mergable; } void CallsListModel::terminateAllCalls () const { CoreManager::getInstance()->getCore()->terminateAllCalls(); } void CallsListModel::terminateCall (const QString& sipAddress) const{ auto coreManager = CoreManager::getInstance(); shared_ptr address = coreManager->getCore()->interpretUrl(Utils::appStringToCoreString(sipAddress)); if (!address) qWarning() << "Cannot terminate Call. The address cannot be parsed : " << sipAddress; else{ std::shared_ptr call = coreManager->getCore()->getCallByRemoteAddress2(address); if( call){ coreManager->lockVideoRender(); call->terminate(); coreManager->unlockVideoRender(); }else{ qWarning() << "Cannot terminate call as it doesn't exist : " << sipAddress; } } } std::list> CallsListModel::getCallHistory(const QString& peerAddress, const QString& localAddress){ std::shared_ptr cleanedPeerAddress = Utils::interpretUrl(Utils::cleanSipAddress(peerAddress)); std::shared_ptr cleanedLocalAddress = Utils::interpretUrl(Utils::cleanSipAddress(localAddress)); return CoreManager::getInstance()->getCore()->getCallHistory(cleanedPeerAddress, cleanedLocalAddress); } // ----------------------------------------------------------------------------- static void joinConference (const shared_ptr &call) { if (call->getToHeader("method") != "join-conference") return; shared_ptr core = CoreManager::getInstance()->getCore(); if (!core->getConference()) { qWarning() << QStringLiteral("Not in a conference. => Responding to `join-conference` as a simple call..."); return; } shared_ptr conference = core->getConference(); const QString conferenceId = Utils::coreStringToAppString(call->getToHeader("conference-id")); if (conference->getId() != Utils::appStringToCoreString(conferenceId)) { qWarning() << QStringLiteral("Trying to join conference with an invalid conference id: `%1`. Responding as a simple call...") .arg(conferenceId); return; } qInfo() << QStringLiteral("Join conference: `%1`.").arg(conferenceId); ConferenceHelperModel helperModel; ConferenceHelperModel::ConferenceAddModel *addModel = helperModel.getConferenceAddModel(); CallModel *callModel = &call->getData("call-model"); callModel->accept(); addModel->addToConference(call->getRemoteAddress()); addModel->update(); } void CallsListModel::handleCallStateChanged (const shared_ptr &call, linphone::Call::State state) { switch (state) { case linphone::Call::State::IncomingReceived: addCall(call); joinConference(call); break; case linphone::Call::State::OutgoingInit: addCall(call); break; case linphone::Call::State::End: case linphone::Call::State::Error:{ if(call->dataExists("call-model")) { CallModel * model = &call->getData("call-model"); model->endCall(); } } break; case linphone::Call::State::Released: removeCall(call); break; case linphone::Call::State::StreamsRunning: { int index = findCallIndex(mList, call); emit callRunning(index, &call->getData("call-model")); } break; default: break; } } // ----------------------------------------------------------------------------- void CallsListModel::addCall (const shared_ptr &call) { int index = findCallIndex(mList, call); if( index < 0){ QSharedPointer callModel = QSharedPointer::create(call); qInfo() << QStringLiteral("Add call:") << callModel->getFullLocalAddress() << callModel->getFullPeerAddress(); App::getInstance()->getEngine()->setObjectOwnership(callModel.get(), QQmlEngine::CppOwnership); connect(callModel.get(), &CallModel::meAdminChanged, this, &CallsListModel::canMergeCallsChanged); add(callModel); emit layoutChanged(); if (call->getDir() == linphone::Call::Dir::Outgoing) { QQuickWindow *callsWindow = App::getInstance()->getCallsWindow(); if (callsWindow) { if (CoreManager::getInstance()->getSettingsModel()->getKeepCallsWindowInBackground()) { if (!callsWindow->isVisible()) callsWindow->showMinimized(); } else App::smartShowWindow(callsWindow); } } } } void CallsListModel::addDummyCall () { QQuickWindow *callsWindow = App::getInstance()->getCallsWindow(); if (callsWindow) { App::smartShowWindow(callsWindow); } QSharedPointer callModel = QSharedPointer::create(nullptr); qInfo() << QStringLiteral("Add call:") << callModel->getFullLocalAddress() << callModel->getFullPeerAddress(); App::getInstance()->getEngine()->setObjectOwnership(callModel.get(), QQmlEngine::CppOwnership); // This connection is (only) useful for `CallsListProxyModel`. QObject::connect(callModel.get(), &CallModel::isInConferenceChanged, this, [this, callModel](bool) { int id = findCallIndex(mList, *callModel); emit dataChanged(index(id, 0), index(id, 0)); }); connect(callModel.get(), &CallModel::meAdminChanged, this, &CallsListModel::canMergeCallsChanged); add(callModel); emit layoutChanged(); } void CallsListModel::removeCall (const shared_ptr &call) { CallModel *callModel = nullptr; try { callModel = &call->getData("call-model"); } catch (const out_of_range &) { // The call model not exists because the linphone call state // `CallStateIncomingReceived`/`CallStateOutgoingInit` was not notified. qWarning() << QStringLiteral("Unable to find call:") << call.get(); return; } if( callModel && callModel->getCallError() != ""){ // Wait some time to display an error on ending call. QTimer::singleShot( DelayBeforeRemoveCall , this, [this, callModel] { removeCallCb(callModel); }); }else remove(callModel); } void CallsListModel::removeCallCb (CallModel *callModel) { remove(callModel); } linphone-desktop-5.0.2/linphone-app/src/components/calls/CallsListModel.hpp000066400000000000000000000076041434616504300270460ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CALLS_LIST_MODEL_H_ #define CALLS_LIST_MODEL_H_ #include #include "components/call/CallModel.hpp" #include "utils/LinphoneEnums.hpp" #include "app/proxyModel/ProxyListModel.hpp" // ============================================================================= class ChatRoomModel; class CoreHandlers; class ConferenceInfoModel; class CallsListModel : public ProxyListModel { Q_OBJECT public: Q_PROPERTY(bool canMergeCalls READ canMergeCalls NOTIFY canMergeCallsChanged) CallsListModel (QObject *parent = Q_NULLPTR); CallModel *findCallModelFromPeerAddress (const QString &peerAddress) const; void askForTransfer (CallModel *callModel); void askForAttendedTransfer (CallModel *callModel); Q_INVOKABLE void launchAudioCall (const QString &sipAddress, const QString& prepareTransfertAddress = "", const QHash &headers = {}) const; Q_INVOKABLE void launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers = {}, const QString& prepareTransfertAddress = "") const; Q_INVOKABLE void launchVideoCall (const QString &sipAddress, const QString& prepareTransfertAddress = "", const bool& autoSelectAfterCreation = true, QVariantMap options = QVariantMap()) const; Q_INVOKABLE QVariantMap launchChat(const QString &sipAddress, const int& securityLevel) const; Q_INVOKABLE ChatRoomModel* createChat (const QString &participantAddress) const; Q_INVOKABLE ChatRoomModel* createChat (CallModel * ); Q_INVOKABLE bool createSecureChat (const QString& subject, const QString &participantAddress) const; QVariantMap createChatRoom(const QString& subject, const int& securityLevel, std::shared_ptr localAddress, const QVariantList& participants, const bool& selectAfterCreation) const; Q_INVOKABLE QVariantMap createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants, const bool& selectAfterCreation) const; Q_INVOKABLE void prepareConferenceCall(ConferenceInfoModel * model); Q_INVOKABLE int addAllToConference(); Q_INVOKABLE void mergeAll(); Q_INVOKABLE int getRunningCallsNumber () const; bool canMergeCalls()const; Q_INVOKABLE void terminateAllCalls () const; Q_INVOKABLE void terminateCall (const QString& sipAddress) const; static std::list> getCallHistory(const QString& peerAddress, const QString& localAddress); signals: void callRunning (int index, CallModel *callModel); void callTransferAsked (CallModel *callModel); void callAttendedTransferAsked (CallModel *callModel); void callConferenceAsked(ConferenceInfoModel * conferenceInfoModel); void callMissed (CallModel *callModel); void canMergeCallsChanged(); private: void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); void addCall (const std::shared_ptr &call); void addDummyCall (); void removeCall (const std::shared_ptr &call); void removeCallCb (CallModel *callModel); std::shared_ptr mCoreHandlers; }; #endif // CALLS_LIST_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/calls/CallsListProxyModel.cpp000066400000000000000000000032161434616504300300760ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/call/CallModel.hpp" #include "components/core/CoreManager.hpp" #include "CallsListModel.hpp" #include "CallsListProxyModel.hpp" // ============================================================================= using namespace std; CallsListProxyModel::CallsListProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { CallsListModel *callsListModel = CoreManager::getInstance()->getCallsListModel(); QObject::connect(callsListModel, &CallsListModel::callRunning, this, [this](int index, CallModel *callModel) { emit callRunning(index, callModel); }); setSourceModel(callsListModel); sort(0); } bool CallsListProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); return !index.data().value()->isInConference(); } linphone-desktop-5.0.2/linphone-app/src/components/calls/CallsListProxyModel.hpp000066400000000000000000000024651434616504300301100ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CALLS_LIST_PROXY_MODEL_H_ #define CALLS_LIST_PROXY_MODEL_H_ #include // ============================================================================= class CallModel; class CallsListProxyModel : public QSortFilterProxyModel { Q_OBJECT; public: CallsListProxyModel (QObject *parent = Q_NULLPTR); signals: void callRunning (int index, CallModel *callModel); private: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; }; #endif // CALLS_LIST_PROXY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/camera/000077500000000000000000000000001434616504300236055ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/camera/Camera.cpp000066400000000000000000000233201434616504300255010ustar00rootroot00000000000000/* * Copyright (c) 2010-2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "components/call/CallModel.hpp" #include "components/core/CoreManager.hpp" #include "components/participant/ParticipantDeviceModel.hpp" #include "components/settings/SettingsModel.hpp" #include "Camera.hpp" #include "CameraDummy.hpp" // ============================================================================= using namespace std; namespace { constexpr int MaxFps = 30; } QMutex Camera::mPreviewCounterMutex; int Camera::mPreviewCounter; // ============================================================================= Camera::Camera (QQuickItem *parent) : QQuickFramebufferObject(parent) { qDebug() << "[Camera] Camera constructor" << this; updateWindowIdLocation(); setTextureFollowsItemSize(true); // The fbo content must be y-mirrored because the ms rendering is y-inverted. setMirrorVertically(true); mRefreshTimer = new QTimer(this); mRefreshTimer->setInterval(1000 / MaxFps); QObject::connect( mRefreshTimer, &QTimer::timeout, this, &QQuickFramebufferObject::update, Qt::QueuedConnection ); mRefreshTimer->start(); mLastVideoDefinitionChecker.setInterval(500); QObject::connect(&mLastVideoDefinitionChecker, &QTimer::timeout, this, &Camera::checkVideoDefinition, Qt::QueuedConnection); } Camera::~Camera(){ qDebug() << "[Camera] Camera destructor" << this; mRefreshTimer->stop(); if(mIsPreview) deactivatePreview(); setWindowIdLocation(None);// We need to remove the Qt Buffer from SDK ot avoid to reuse it. } void Camera::resetWindowId() const{ auto core = CoreManager::getInstance()->getCore(); if(core && mIsWindowIdSet){ QQuickFramebufferObject::Renderer * oldRenderer = NULL; if(mWindowIdLocation == CorePreview){ oldRenderer = (QQuickFramebufferObject::Renderer *)core->getNativePreviewWindowId(); if(oldRenderer) core->setNativePreviewWindowId(NULL); }else if( mWindowIdLocation == Call){ if(mCallModel){ auto call = mCallModel->getCall(); if( call && call->getState() != linphone::Call::State::End && call->getState() != linphone::Call::State::Released){ oldRenderer = (QQuickFramebufferObject::Renderer *) call->getNativeVideoWindowId(); if(oldRenderer) call->setNativeVideoWindowId(NULL); } } }else if(mWindowIdLocation == Device){ if(mParticipantDeviceModel){ auto device = mParticipantDeviceModel->getDevice(); if( device ){ oldRenderer = (QQuickFramebufferObject::Renderer *)device->getNativeVideoWindowId(); if(oldRenderer) mParticipantDeviceModel->getDevice()->setNativeVideoWindowId(NULL); } } }else if( mWindowIdLocation == Core){ oldRenderer = (QQuickFramebufferObject::Renderer *)core->getNativeVideoWindowId(); if(oldRenderer) core->setNativeVideoWindowId(NULL); } qDebug() << "[Camera] Removed " << oldRenderer << " at " << mWindowIdLocation << " for " << this; mIsWindowIdSet = false; } } void Camera::checkVideoDefinition(){ if( mWindowIdLocation == WindowIdLocation::CorePreview){ auto videoDefinition = CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); if(videoDefinition["width"] != mLastVideoDefinition["width"] || videoDefinition["height"] != mLastVideoDefinition["height"]){ mLastVideoDefinition = videoDefinition; emit videoDefinitionChanged(); } } } void Camera::setWindowIdLocation(const WindowIdLocation& location){ if( mWindowIdLocation != location){ resetWindowId();// Location change: Reset old window ID. mWindowIdLocation = location; if( mWindowIdLocation == WindowIdLocation::CorePreview){ mLastVideoDefinition = CoreManager::getInstance()->getSettingsModel()->getCurrentPreviewVideoDefinition(); emit videoDefinitionChanged(); mLastVideoDefinitionChecker.start(); }else mLastVideoDefinitionChecker.stop(); } } void Camera::updateWindowIdLocation(){ bool useDefaultWindow = true; if(mIsPreview) setWindowIdLocation( WindowIdLocation::CorePreview); else{ if(mCallModel){ auto call = mCallModel->getCall(); if(call){ setWindowIdLocation( WindowIdLocation::Call); useDefaultWindow = false; } }else if( mParticipantDeviceModel){ auto participantDevice = mParticipantDeviceModel->getDevice(); if(participantDevice){ setWindowIdLocation(WindowIdLocation::Device); useDefaultWindow = false; } } if(useDefaultWindow){ setWindowIdLocation(WindowIdLocation::Core); } } } void Camera::removeParticipantDeviceModel(){ mParticipantDeviceModel = nullptr; } QQuickFramebufferObject::Renderer *Camera::createRenderer () const { QQuickFramebufferObject::Renderer * renderer = NULL; if(mWindowIdLocation == CorePreview){ qDebug() << "[Camera] Setting Camera to Preview"; renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->createNativePreviewWindowId(); if(renderer) CoreManager::getInstance()->getCore()->setNativePreviewWindowId(renderer); }else if(mWindowIdLocation == Call){ if(mCallModel){ auto call = mCallModel->getCall(); if(call){ qDebug() << "[Camera] Setting Camera to CallModel"; renderer = (QQuickFramebufferObject::Renderer *) call->createNativeVideoWindowId(); if(renderer) call->setNativeVideoWindowId(renderer); } } }else if( mWindowIdLocation == Device) { auto participantDevice = mParticipantDeviceModel->getDevice(); if(participantDevice){ qDebug() << "[Camera] Setting Camera to Participant Device"; qDebug() << "[Camera] Trying to create new window ID for " << participantDevice->getName().c_str() << ", addr=" << participantDevice->getAddress()->asString().c_str(); renderer = (QQuickFramebufferObject::Renderer *) participantDevice->createNativeVideoWindowId(); if(renderer) participantDevice->setNativeVideoWindowId(renderer); } }else if( mWindowIdLocation == Core){ qDebug() << "[Camera] Setting Camera to Default Window"; renderer = (QQuickFramebufferObject::Renderer *) CoreManager::getInstance()->getCore()->createNativeVideoWindowId(); if(renderer) CoreManager::getInstance()->getCore()->setNativeVideoWindowId(renderer); } if( !renderer){ QTimer::singleShot(1, this, &Camera::isNotReady);// Workaround for const createRenderer qWarning() << "[Camera] Stream couldn't start for Rendering. Retrying in 1s"; renderer = new CameraDummy(); QTimer::singleShot(1000, this, &Camera::requestNewRenderer); }else{ mIsWindowIdSet = true; qDebug() << "[Camera] Added " << renderer << " at " << mWindowIdLocation << " for " << this; QTimer::singleShot(1, this, &Camera::isReady);// Workaround for const createRenderer. } return renderer; } // ----------------------------------------------------------------------------- CallModel *Camera::getCallModel () const { return mCallModel; } bool Camera::getIsPreview () const { return mIsPreview; } bool Camera::getIsReady () const { return mIsReady; } ParticipantDeviceModel * Camera::getParticipantDeviceModel() const{ return mParticipantDeviceModel; } void Camera::setCallModel (CallModel *callModel) { if (mCallModel != callModel) { if( mCallModel) disconnect(mCallModel, &CallModel::statusChanged, this, &Camera::onCallStateChanged); mCallModel = callModel; connect(mCallModel, &CallModel::statusChanged, this, &Camera::onCallStateChanged); updateWindowIdLocation(); update(); emit callChanged(mCallModel); } } void Camera::setIsPreview (bool status) { if (mIsPreview != status) { mIsPreview = status; if(mIsPreview) activatePreview(); else deactivatePreview(); updateWindowIdLocation(); update(); emit isPreviewChanged(status); } } void Camera::setIsReady(bool status) { if (mIsReady != status) { mIsReady = status; emit isReadyChanged(); } } void Camera::setParticipantDeviceModel(ParticipantDeviceModel * participantDeviceModel){ if (mParticipantDeviceModel != participantDeviceModel) { if( mParticipantDeviceModel) disconnect(mParticipantDeviceModel, &QObject::destroyed, this, &Camera::removeParticipantDeviceModel); mParticipantDeviceModel = participantDeviceModel; connect(mParticipantDeviceModel, &QObject::destroyed, this, &Camera::removeParticipantDeviceModel); updateWindowIdLocation(); update(); emit participantDeviceModelChanged(mParticipantDeviceModel); } } void Camera::isReady(){ setIsReady(true); } void Camera::isNotReady(){ setIsReady(false); } void Camera::activatePreview(){ mPreviewCounterMutex.lock(); if (++mPreviewCounter == 1) CoreManager::getInstance()->getCore()->enableVideoPreview(true); mPreviewCounterMutex.unlock(); } void Camera::deactivatePreview(){ auto core = CoreManager::getInstance()->getCore(); if(core){ mPreviewCounterMutex.lock(); if (--mPreviewCounter == 0) core->enableVideoPreview(false); mPreviewCounterMutex.unlock(); } } void Camera::onCallStateChanged(){ if( mCallModel && mCallModel->getStatus() == CallModel::CallStatusEnded){ resetWindowId(); disconnect(mCallModel, &CallModel::statusChanged, this, &Camera::onCallStateChanged); mCallModel = nullptr; } }linphone-desktop-5.0.2/linphone-app/src/components/camera/Camera.hpp000066400000000000000000000064271434616504300255170ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CAMERA_H_ #define CAMERA_H_ #include #include #include #include #include // ============================================================================= namespace linphone { class Call; } class CallModel; class ParticipantDeviceModel; // ----------------------------------------------------------------------------- class Camera : public QQuickFramebufferObject { Q_OBJECT Q_PROPERTY(CallModel * call READ getCallModel WRITE setCallModel NOTIFY callChanged); Q_PROPERTY(ParticipantDeviceModel * participantDeviceModel READ getParticipantDeviceModel WRITE setParticipantDeviceModel NOTIFY participantDeviceModelChanged) Q_PROPERTY(bool isPreview READ getIsPreview WRITE setIsPreview NOTIFY isPreviewChanged); Q_PROPERTY(bool isReady READ getIsReady WRITE setIsReady NOTIFY isReadyChanged); typedef enum{ None = -1, CorePreview = 0, Call, Device, Core }WindowIdLocation; public: Camera (QQuickItem *parent = Q_NULLPTR); virtual ~Camera(); QQuickFramebufferObject::Renderer *createRenderer () const override; Q_INVOKABLE void resetWindowId() const; // const to be used from createRenderer() void checkVideoDefinition(); static QMutex mPreviewCounterMutex; static int mPreviewCounter; void isReady(); void isNotReady(); public slots: void onCallStateChanged(); signals: void callChanged (CallModel *callModel); void isPreviewChanged (bool isPreview); void isReadyChanged(); void participantDeviceModelChanged(ParticipantDeviceModel *participantDeviceModel); void requestNewRenderer(); void videoDefinitionChanged(); private: CallModel *getCallModel () const; bool getIsPreview () const; bool getIsReady () const; ParticipantDeviceModel * getParticipantDeviceModel() const; void setCallModel (CallModel *callModel); void setIsPreview (bool status); void setIsReady(bool status); void setParticipantDeviceModel(ParticipantDeviceModel * participantDeviceModel); void setWindowIdLocation(const WindowIdLocation& location); void activatePreview(); void deactivatePreview(); void updateWindowIdLocation(); void removeParticipantDeviceModel(); QVariantMap mLastVideoDefinition; QTimer mLastVideoDefinitionChecker; bool mIsPreview = false; bool mIsReady = false; CallModel *mCallModel = nullptr; ParticipantDeviceModel *mParticipantDeviceModel = nullptr; WindowIdLocation mWindowIdLocation = None; mutable bool mIsWindowIdSet = false; QTimer *mRefreshTimer = nullptr; }; #endif // CAMERA_H_ linphone-desktop-5.0.2/linphone-app/src/components/camera/CameraDummy.cpp000066400000000000000000000024311434616504300265150ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "components/core/CoreManager.hpp" #include "CameraDummy.hpp" // ============================================================================= CameraDummy::CameraDummy(){ } QOpenGLFramebufferObject *CameraDummy::createFramebufferObject (const QSize &size){ return new QOpenGLFramebufferObject(size); } void CameraDummy::render (){ } void CameraDummy::synchronize (QQuickFramebufferObject *item){ }linphone-desktop-5.0.2/linphone-app/src/components/camera/CameraDummy.hpp000066400000000000000000000023261434616504300265250ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CAMERA_DUMMY_H_ #define CAMERA_DUMMY_H_ #include #include // ============================================================================= class CameraDummy : public QQuickFramebufferObject::Renderer{ public: CameraDummy(); QOpenGLFramebufferObject *createFramebufferObject (const QSize &size) override; void render () override; void synchronize (QQuickFramebufferObject *item) override; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/chat-events/000077500000000000000000000000001434616504300245765ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatCallModel.cpp000066400000000000000000000056361434616504300277500ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "components/core/CoreManager.hpp" #include "ChatCallModel.hpp" // ============================================================================= ChatCallModel::ChatCallModel ( std::shared_ptr callLog, const bool& isStart, QObject * parent) : ChatEvent(ChatRoomModel::EntryType::CallEntry, parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mCallLog = callLog; mIsStart = isStart; if(isStart){ mTimestamp = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000); }else{ mTimestamp = QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000); } mIsOutgoing = (mCallLog->getDir() == linphone::Call::Dir::Outgoing); mStatus = (LinphoneEnums::fromLinphone(mCallLog->getStatus())); } ChatCallModel::~ChatCallModel(){ } QSharedPointer ChatCallModel::create(std::shared_ptr callLog, const bool& isStart, QObject * parent){ auto model = QSharedPointer::create(callLog, isStart, parent); if(model ){ model->update(); return model; }else return nullptr; } std::shared_ptr ChatCallModel::getCallLog(){ return mCallLog; } //-------------------------------------------------------------------------------------------------------------------------- void ChatCallModel::setIsStart(const bool& data){ if(data != mIsStart) { mIsStart = data; emit isStartChanged(); } } void ChatCallModel::setStatus(const LinphoneEnums::CallStatus& data){ if(data != mStatus) { mStatus = data; emit statusChanged(); } } void ChatCallModel::setIsOutgoing(const bool& data){ if(data != mIsOutgoing) { mIsOutgoing = data; emit isOutgoingChanged(); } } void ChatCallModel::update(){ setIsOutgoing(mCallLog->getDir() == linphone::Call::Dir::Outgoing); setStatus(LinphoneEnums::fromLinphone(mCallLog->getStatus())); } void ChatCallModel::deleteEvent(){ CoreManager::getInstance()->getCore()->removeCallLog(mCallLog); emit CoreManager::getInstance()->callLogsCountChanged(); }linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatCallModel.hpp000066400000000000000000000044161434616504300277500ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CHAT_CALL_MODEL_H #define CHAT_CALL_MODEL_H #include "utils/LinphoneEnums.hpp" #include "ChatEvent.hpp" // ============================================================================= class ChatCallModel : public ChatEvent { Q_OBJECT public: static QSharedPointer create(std::shared_ptr chatLog, const bool& isStart, QObject * parent = nullptr);// Call it instead constructor ChatCallModel (std::shared_ptr eventLog, const bool& isStart, QObject * parent = nullptr); virtual ~ChatCallModel(); Q_PROPERTY(ChatRoomModel::EntryType type MEMBER mType CONSTANT) Q_PROPERTY(QDateTime timestamp MEMBER mTimestamp CONSTANT) Q_PROPERTY(bool isStart MEMBER mIsStart WRITE setIsStart NOTIFY isStartChanged) Q_PROPERTY(LinphoneEnums::CallStatus status MEMBER mStatus WRITE setStatus NOTIFY statusChanged) Q_PROPERTY(bool isOutgoing MEMBER mIsOutgoing WRITE setIsOutgoing NOTIFY isOutgoingChanged) std::shared_ptr getCallLog(); void setIsStart(const bool& isStart); void setStatus(const LinphoneEnums::CallStatus& status); void setIsOutgoing(const bool& isOutgoing); void update(); virtual void deleteEvent() override; bool mIsStart; LinphoneEnums::CallStatus mStatus; bool mIsOutgoing; signals: void isStartChanged(); void statusChanged(); void isOutgoingChanged(); private: std::shared_ptr mCallLog; }; Q_DECLARE_METATYPE(QSharedPointer) Q_DECLARE_METATYPE(ChatCallModel*) #endif linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatEvent.cpp000066400000000000000000000023741434616504300271710ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "ChatEvent.hpp" // ============================================================================= ChatEvent::ChatEvent (ChatRoomModel::EntryType type, QObject * parent) : QObject(parent){ mType = type; } ChatEvent::~ChatEvent(){ } QDateTime ChatEvent::getTimestamp() const{ return mTimestamp; } void ChatEvent::setTimestamp(const QDateTime& timestamp){ mTimestamp = timestamp; } void ChatEvent::deleteEvent(){ }linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatEvent.hpp000066400000000000000000000025521434616504300271740ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CHAT_EVENT_H #define CHAT_EVENT_H #include "components/chat-room/ChatRoomModel.hpp" // ============================================================================= class ChatEvent : public QObject{ Q_OBJECT public: ChatEvent (ChatRoomModel::EntryType type, QObject * parent = nullptr); virtual ~ChatEvent(); ChatRoomModel::EntryType mType; virtual QDateTime getTimestamp() const; virtual void setTimestamp(const QDateTime& timestamp = QDateTime::currentDateTime()); virtual void deleteEvent(); protected: QDateTime mTimestamp; }; Q_DECLARE_METATYPE(ChatEvent*) #endif linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatMessageListener.cpp000066400000000000000000000057021434616504300312000ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ChatMessageListener.hpp" #include #include #include "ChatMessageModel.hpp" // ============================================================================= // ============================================================================= ChatMessageListener::ChatMessageListener(QObject* parent) : QObject(parent){ } void ChatMessageListener::onFileTransferRecv(const std::shared_ptr & message, const std::shared_ptr & content, const std::shared_ptr & buffer){ emit fileTransferRecv(message, content, buffer); } void ChatMessageListener::onFileTransferSendChunk(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size, const std::shared_ptr & buffer){ emit fileTransferSendChunk(message, content, offset, size, buffer); } std::shared_ptr ChatMessageListener::onFileTransferSend(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size) { emit fileTransferSend(message, content, offset, size); return nullptr; } void ChatMessageListener::onFileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr & content, size_t offset, size_t total){ emit fileTransferProgressIndication(message, content, offset, total); } void ChatMessageListener::onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state){ emit msgStateChanged(message, state); } void ChatMessageListener::onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state){ emit participantImdnStateChanged(message, state); } void ChatMessageListener::onEphemeralMessageTimerStarted(const std::shared_ptr & message){ emit ephemeralMessageTimerStarted(message); } void ChatMessageListener::onEphemeralMessageDeleted(const std::shared_ptr & message){ emit ephemeralMessageDeleted(message); } linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatMessageListener.hpp000066400000000000000000000073361434616504300312120ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CHAT_MESSAGE_LISTENER_H #define CHAT_MESSAGE_LISTENER_H #include "utils/LinphoneEnums.hpp" #include // ============================================================================= class ChatMessageModel; class ChatMessageListener : public QObject, public linphone::ChatMessageListener { Q_OBJECT public: ChatMessageListener(QObject * parent = nullptr); virtual ~ChatMessageListener(){} virtual void onFileTransferRecv(const std::shared_ptr & message, const std::shared_ptr & content, const std::shared_ptr & buffer) override; virtual void onFileTransferSendChunk(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size, const std::shared_ptr & buffer) override; virtual std::shared_ptr onFileTransferSend(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size) override; virtual void onFileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr &, size_t offset, size_t) override; virtual void onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state) override; virtual void onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state) override; virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & message) override; virtual void onEphemeralMessageDeleted(const std::shared_ptr & message) override; signals: void fileTransferRecv(const std::shared_ptr & message, const std::shared_ptr & content, const std::shared_ptr & buffer); void fileTransferSendChunk(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size, const std::shared_ptr & buffer); std::shared_ptr fileTransferSend (const std::shared_ptr &,const std::shared_ptr &,size_t,size_t); void fileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr &, size_t offset, size_t); void msgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state); void participantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state); void ephemeralMessageTimerStarted(const std::shared_ptr & message); void ephemeralMessageDeleted(const std::shared_ptr & message); }; Q_DECLARE_METATYPE(ChatMessageListener*) #endif linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatMessageModel.cpp000066400000000000000000000313311434616504300304500ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ChatMessageModel.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "ChatMessageListener.hpp" #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "components/contact/ContactModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/content/ContentListModel.hpp" #include "components/content/ContentModel.hpp" #include "components/content/ContentProxyModel.hpp" #include "components/core/CoreManager.hpp" #include "app/providers/ThumbnailProvider.hpp" #include "components/notifier/Notifier.hpp" #include "components/participant-imdn/ParticipantImdnStateListModel.hpp" #include "components/participant-imdn/ParticipantImdnStateProxyModel.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" void ChatMessageModel::connectTo(ChatMessageListener * listener){ connect(listener, &ChatMessageListener::fileTransferRecv, this, &ChatMessageModel::onFileTransferRecv); connect(listener, &ChatMessageListener::fileTransferSendChunk, this, &ChatMessageModel::onFileTransferSendChunk); connect(listener, &ChatMessageListener::fileTransferSend, this, &ChatMessageModel::onFileTransferSend); connect(listener, &ChatMessageListener::fileTransferProgressIndication, this, &ChatMessageModel::onFileTransferProgressIndication); connect(listener, &ChatMessageListener::msgStateChanged, this, &ChatMessageModel::onMsgStateChanged); connect(listener, &ChatMessageListener::participantImdnStateChanged, this, &ChatMessageModel::onParticipantImdnStateChanged); connect(listener, &ChatMessageListener::ephemeralMessageTimerStarted, this, &ChatMessageModel::onEphemeralMessageTimerStarted); connect(listener, &ChatMessageListener::ephemeralMessageDeleted, this, &ChatMessageModel::onEphemeralMessageDeleted); connect(listener, &ChatMessageListener::participantImdnStateChanged, this->getParticipantImdnStates().get(), &ParticipantImdnStateListModel::onParticipantImdnStateChanged); } // ============================================================================= ChatMessageModel::AppDataManager::AppDataManager(const QString& appdata){ if(!appdata.isEmpty()){ for(QString pair : appdata.split(';')){ QStringList fields = pair.split(':'); if(fields.size() > 1) mData[fields[1]] = fields[0]; else qWarning() << "Bad or too old appdata. It need a compatibility parsing : " << appdata; } } } QString ChatMessageModel::AppDataManager::toString(){ QStringList pairs; for(QMap::iterator it = mData.begin() ; it != mData.end() ; ++it){ pairs << it.value() + ":" + it.key(); } return pairs.join(';'); } ChatMessageModel::ChatMessageModel ( std::shared_ptr chatMessage, QObject * parent) : ChatEvent(ChatRoomModel::EntryType::MessageEntry, parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it if(chatMessage){ mParticipantImdnStateListModel = QSharedPointer::create(chatMessage); mChatMessageListener = std::make_shared(); connectTo(mChatMessageListener.get()); mChatMessage = chatMessage; mChatMessage->addListener(mChatMessageListener); if( mChatMessage->isReply()){ auto replyMessage = mChatMessage->getReplyMessage(); if( replyMessage)// Reply message could be inexistant (for example : when locally deleted) mReplyChatMessageModel = create(replyMessage, parent); } std::list> contents = chatMessage->getContents(); QString txt; for(auto content : contents){ if(content->isText()) txt += content->getUtf8Text().c_str(); } mContent = txt; mTimestamp = QDateTime::fromMSecsSinceEpoch(chatMessage->getTime() * 1000); } mWasDownloaded = false; mContentListModel = QSharedPointer::create(this); } ChatMessageModel::~ChatMessageModel(){ if(mChatMessage) mChatMessage->removeListener(mChatMessageListener); } QSharedPointer ChatMessageModel::create(std::shared_ptr chatMessage, QObject * parent){ auto model = QSharedPointer::create(chatMessage, parent); return model; } std::shared_ptr ChatMessageModel::getChatMessage(){ return mChatMessage; } QSharedPointer ChatMessageModel::getContentModel(std::shared_ptr content){ return mContentListModel->getContentModel(content); } //----------------------------------------------------------------------------------------------------------------------- QString ChatMessageModel::getFromDisplayName() const{ return mChatMessage ? Utils::getDisplayName(mChatMessage->getFromAddress()) : ""; } QString ChatMessageModel::getFromDisplayNameReplyMessage() const{ if( isReply()) return Utils::getDisplayName(mChatMessage->getReplyMessageSenderAddress()); else return ""; } QString ChatMessageModel::getFromSipAddress() const{ return mChatMessage ? Utils::cleanSipAddress(Utils::coreStringToAppString(mChatMessage->getFromAddress()->asStringUriOnly())) : ""; } QString ChatMessageModel::getToDisplayName() const{ return mChatMessage ? Utils::getDisplayName(mChatMessage->getToAddress()) : ""; } QString ChatMessageModel::getToSipAddress() const{ return mChatMessage ? Utils::cleanSipAddress(Utils::coreStringToAppString(mChatMessage->getToAddress()->asStringUriOnly())) : ""; } ContactModel * ChatMessageModel::getContactModel() const{ return mChatMessage ? CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(Utils::coreStringToAppString(mChatMessage->getFromAddress()->asString())).get() : nullptr; } bool ChatMessageModel::isEphemeral() const{ return mChatMessage && mChatMessage->isEphemeral(); } qint64 ChatMessageModel::getEphemeralExpireTime() const{ time_t t = mChatMessage ? mChatMessage->getEphemeralExpireTime() : 0; return t >0 ? t - QDateTime::currentSecsSinceEpoch() : 0; } long ChatMessageModel::getEphemeralLifetime() const{ return mChatMessage ? mChatMessage->getEphemeralLifetime() : 0; } LinphoneEnums::ChatMessageState ChatMessageModel::getState() const{ return mChatMessage ? LinphoneEnums::fromLinphone(mChatMessage->getState()) : LinphoneEnums::ChatMessageStateIdle; } bool ChatMessageModel::isOutgoing() const{ return mChatMessage && mChatMessage->isOutgoing(); } ParticipantImdnStateProxyModel * ChatMessageModel::getProxyImdnStates(){ ParticipantImdnStateProxyModel * proxy = new ParticipantImdnStateProxyModel(); proxy->setChatMessageModel(this); return proxy; } QSharedPointer ChatMessageModel::getParticipantImdnStates() const{ return mParticipantImdnStateListModel; } QSharedPointer ChatMessageModel::getContents() const{ return mContentListModel; } bool ChatMessageModel::isReply() const{ return mChatMessage && mChatMessage->isReply(); } ChatMessageModel * ChatMessageModel::getReplyChatMessageModel() const{ return mReplyChatMessageModel.get(); } bool ChatMessageModel::isForward() const{ return mChatMessage && mChatMessage->isForward(); } QString ChatMessageModel::getForwardInfo() const{ return mChatMessage ? Utils::coreStringToAppString(mChatMessage->getForwardInfo()) : ""; } QString ChatMessageModel::getForwardInfoDisplayName() const{ QString forwardInfo = getForwardInfo(); auto forwardAddress = Utils::interpretUrl(forwardInfo); if(!forwardAddress || Utils::isMe(forwardAddress)) return "";// myself else return Utils::getDisplayName(forwardInfo); } //----------------------------------------------------------------------------------------------------------------------- void ChatMessageModel::setWasDownloaded(bool wasDownloaded){ if( mWasDownloaded != wasDownloaded) { mWasDownloaded = wasDownloaded; emit wasDownloadedChanged(); } } void ChatMessageModel::setTimestamp(const QDateTime& timestamp) { mTimestamp = timestamp; } //----------------------------------------------------------------------------------------------------------------------- void ChatMessageModel::resendMessage (){ switch (getState()) { case LinphoneEnums::ChatMessageStateFileTransferError: case LinphoneEnums::ChatMessageStateNotDelivered: { mChatMessage->send(); emit stateChanged(); break; } default: qWarning() << QStringLiteral("Unable to resend message: %1. Bad state.").arg(getState()); } } void ChatMessageModel::deleteEvent(){ if (mChatMessage && mChatMessage->getFileTransferInformation()) {// Remove thumbnail mChatMessage->cancelFileTransfer(); QString appdata = QString::fromStdString(mChatMessage->getAppdata()); QStringList fields = appdata.split(':'); if(fields[0].size() > 0) { QString thumbnailPath = QString::fromStdString(Paths::getThumbnailsDirPath()) + fields[0]; if (!QFile::remove(thumbnailPath)) qWarning() << QStringLiteral("Unable to remove `%1`.").arg(thumbnailPath); } mChatMessage->setAppdata("");// Remove completely Thumbnail from the message } if(mChatMessage) mChatMessage->getChatRoom()->deleteMessage(mChatMessage); } void ChatMessageModel::updateFileTransferInformation(){ mContentListModel->updateContents(this); } void ChatMessageModel::onFileTransferRecv(const std::shared_ptr & message, const std::shared_ptr & content, const std::shared_ptr & buffer){ } void ChatMessageModel::onFileTransferSendChunk(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size, const std::shared_ptr & buffer) { } std::shared_ptr ChatMessageModel::onFileTransferSend (const std::shared_ptr &,const std::shared_ptr &content,size_t,size_t) { return nullptr; } void ChatMessageModel::onFileTransferProgressIndication (const std::shared_ptr &message,const std::shared_ptr &content,size_t offset,size_t total) { auto contentModel = mContentListModel->getContentModel(content); if(contentModel) { contentModel->setFileOffset(offset); if (total == offset && mChatMessage && !mChatMessage->isOutgoing()) { mContentListModel->downloaded(); bool allAreDownloaded = true; for(auto content : mContentListModel->getSharedList()) allAreDownloaded &= content->mWasDownloaded; setWasDownloaded(allAreDownloaded); App::getInstance()->getNotifier()->notifyReceivedFileMessage(message, content); } } } void ChatMessageModel::onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state) { updateFileTransferInformation();// On message state, file transfert information Content can be changed if( state == linphone::ChatMessage::State::FileTransferDone) { mContentListModel->updateContents(this);// Avoid having leak contents if( !mWasDownloaded){// Update states bool allAreDownloaded = true; for(auto content : mContentListModel->getSharedList()) allAreDownloaded &= content->mWasDownloaded; setWasDownloaded(allAreDownloaded); } } emit stateChanged(); } void ChatMessageModel::onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state){ } void ChatMessageModel::onEphemeralMessageTimerStarted(const std::shared_ptr & message) { emit ephemeralExpireTimeChanged(); } void ChatMessageModel::onEphemeralMessageDeleted(const std::shared_ptr & message) { //emit remove(mSelf.lock()); if(!isOutgoing()) mContentListModel->removeDownloadedFiles(); emit remove(this); } //------------------------------------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatMessageModel.hpp000066400000000000000000000163611434616504300304630ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CHAT_MESSAGE_MODEL_H #define CHAT_MESSAGE_MODEL_H #include "utils/LinphoneEnums.hpp" #include // ============================================================================= /* class Thumbnail{ public: Thumbnail(); QString mId; QString mPath; QString toString()const; void fromString(const QString& ); static QString toString(const QVector& ); static QVector fromListString(const QString& ); }; */ #include "components/chat-room/ChatRoomModel.hpp" #include "ChatEvent.hpp" #include "components/participant-imdn/ParticipantImdnStateListModel.hpp" class ChatMessageModel; class ChatMessageListener; class ParticipantImdnStateProxyModel; class ParticipantImdnStateListModel; class ContentModel; class ContentListModel; class ContentProxyModel; class ChatMessageModel : public ChatEvent { Q_OBJECT public: static QSharedPointer create(std::shared_ptr chatMessage, QObject * parent = nullptr);// Call it instead constructor ChatMessageModel (std::shared_ptr chatMessage, QObject * parent = nullptr); virtual ~ChatMessageModel(); class AppDataManager{// Used to manage appdata to store persistant data like created thumbnails public: AppDataManager(const QString&); QMap mData;// Path / ID QString toString(); }; Q_PROPERTY(QString fromDisplayName READ getFromDisplayName CONSTANT) Q_PROPERTY(QString fromDisplayNameReplyMessage READ getFromDisplayNameReplyMessage CONSTANT) Q_PROPERTY(QString fromSipAddress READ getFromSipAddress CONSTANT) Q_PROPERTY(QString toDisplayName READ getToDisplayName CONSTANT) Q_PROPERTY(QString toSipAddress READ getToSipAddress CONSTANT) Q_PROPERTY(ContactModel * contactModel READ getContactModel CONSTANT) Q_PROPERTY(bool isEphemeral READ isEphemeral NOTIFY isEphemeralChanged) Q_PROPERTY(qint64 ephemeralExpireTime READ getEphemeralExpireTime NOTIFY ephemeralExpireTimeChanged) Q_PROPERTY(long ephemeralLifetime READ getEphemeralLifetime CONSTANT) Q_PROPERTY(LinphoneEnums::ChatMessageState state READ getState NOTIFY stateChanged) Q_PROPERTY(bool isOutgoing READ isOutgoing NOTIFY isOutgoingChanged) Q_PROPERTY(bool wasDownloaded MEMBER mWasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged) Q_PROPERTY(ChatRoomModel::EntryType type MEMBER mType CONSTANT) Q_PROPERTY(QDateTime timestamp MEMBER mTimestamp CONSTANT) Q_PROPERTY(QString content MEMBER mContent NOTIFY contentChanged) Q_PROPERTY(bool isReply READ isReply CONSTANT) Q_PROPERTY(ChatMessageModel* replyChatMessageModel READ getReplyChatMessageModel CONSTANT) Q_PROPERTY(bool isForward READ isForward CONSTANT) Q_PROPERTY(QString getForwardInfo READ getForwardInfo CONSTANT) Q_PROPERTY(QString getForwardInfoDisplayName READ getForwardInfoDisplayName CONSTANT) std::shared_ptr getChatMessage(); QSharedPointer getContentModel(std::shared_ptr content); //---------------------------------------------------------------------------- QString getFromDisplayName() const; QString getFromDisplayNameReplyMessage() const; QString getFromSipAddress() const; QString getToDisplayName() const; QString getToSipAddress() const; ContactModel * getContactModel() const; bool isEphemeral() const; Q_INVOKABLE qint64 getEphemeralExpireTime() const; Q_INVOKABLE long getEphemeralLifetime() const; LinphoneEnums::ChatMessageState getState() const; bool isOutgoing() const; Q_INVOKABLE ParticipantImdnStateProxyModel * getProxyImdnStates(); QSharedPointer getParticipantImdnStates() const; QSharedPointer getContents() const; bool isReply() const; ChatMessageModel * getReplyChatMessageModel() const; bool isForward() const; QString getForwardInfo() const; QString getForwardInfoDisplayName() const; //---------------------------------------------------------------------------- void setWasDownloaded(bool wasDownloaded); virtual void setTimestamp(const QDateTime& timestamp = QDateTime::currentDateTime()) override; //---------------------------------------------------------------------------- Q_INVOKABLE void resendMessage (); virtual void deleteEvent() override; void updateFileTransferInformation(); // Linphone callbacks void onFileTransferRecv(const std::shared_ptr & message, const std::shared_ptr & content, const std::shared_ptr & buffer) ; void onFileTransferSendChunk(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size, const std::shared_ptr & buffer) ; std::shared_ptr onFileTransferSend (const std::shared_ptr &,const std::shared_ptr &,size_t,size_t); void onFileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr &, size_t offset, size_t); void onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state); void onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state); void onEphemeralMessageTimerStarted(const std::shared_ptr & message); void onEphemeralMessageDeleted(const std::shared_ptr & message); //---------------------------------------------------------------------------- bool mWasDownloaded; QString mContent; QString mIsOutgoing; //---------------------------------------------------------------------------- signals: void isEphemeralChanged(); void ephemeralExpireTimeChanged(); void stateChanged(); void wasDownloadedChanged(); void contentChanged(); void isOutgoingChanged(); void fileContentChanged(); void remove(ChatMessageModel* model); private: void connectTo(ChatMessageListener * listener); std::shared_ptr mChatMessage; std::shared_ptr mChatMessageListener; // This is passed to linpĥone object and must be in shared_ptr QSharedPointer mContentListModel; QSharedPointer mFileTransfertContent; QSharedPointer mParticipantImdnStateListModel; QSharedPointer mReplyChatMessageModel; }; Q_DECLARE_METATYPE(ChatMessageModel*) Q_DECLARE_METATYPE(QSharedPointer) #endif linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatNoticeModel.cpp000066400000000000000000000141521434616504300303070ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "ChatNoticeModel.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "utils/Utils.hpp" // ============================================================================= ChatNoticeModel::ChatNoticeModel ( std::shared_ptr eventLog, QObject * parent) : ChatEvent(ChatRoomModel::EntryType::NoticeEntry, parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mEventLog = eventLog; mEventLogType = LinphoneEnums::EventLogType::EventLogTypeNone; setEventLogType(LinphoneEnums::fromLinphone(mEventLog->getType())); mTimestamp = QDateTime::fromMSecsSinceEpoch(eventLog->getCreationTime() * 1000); } ChatNoticeModel::ChatNoticeModel ( NoticeType noticeType, const QDateTime& timestamp, const QString& txt, QObject * parent) : ChatEvent(ChatRoomModel::EntryType::NoticeEntry, parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mEventLogType = LinphoneEnums::EventLogType::EventLogTypeNone; setStatus(noticeType); setName(txt); mTimestamp = timestamp; } ChatNoticeModel::~ChatNoticeModel(){ } QSharedPointer ChatNoticeModel::create(std::shared_ptr eventLog, QObject * parent){ auto model = QSharedPointer::create(eventLog, parent); if(model && model->update()){ return model; }else return nullptr; } QSharedPointer ChatNoticeModel::create(NoticeType noticeType, const QDateTime& timestamp, const QString& txt, QObject * parent){ auto model = QSharedPointer::create(noticeType, timestamp, txt, parent); if(model ){ return model; }else return nullptr; } std::shared_ptr ChatNoticeModel::getEventLog(){ return mEventLog; } //--------------------------------------------------------------------------------------------- bool ChatNoticeModel::update(){ bool handledEvent = true; if(!mEventLog) return false; auto participantAddress = mEventLog->getParticipantAddress(); switch(mEventLog->getType()){ case linphone::EventLog::Type::ConferenceCreated: setName(""); setStatus(NoticeType::NoticeMessage); //dest["message"] = "You have joined the group"; break; case linphone::EventLog::Type::ConferenceTerminated: setName(""); setStatus(NoticeType::NoticeMessage); // dest["message"] = "You have left the group"; break; case linphone::EventLog::Type::ConferenceParticipantAdded: setName(Utils::getDisplayName(participantAddress)); setStatus(NoticeType::NoticeMessage); //dest["message"] = "%1 has joined"; break; case linphone::EventLog::Type::ConferenceParticipantRemoved: setName(Utils::getDisplayName(participantAddress)); setStatus(NoticeType::NoticeMessage); //dest["message"] = "%1 has left"; break; case linphone::EventLog::Type::ConferenceSecurityEvent: { if(mEventLog->getSecurityEventType() == linphone::EventLog::SecurityEventType::SecurityLevelDowngraded ){ auto faultyParticipant = mEventLog->getSecurityEventFaultyDeviceAddress(); if(faultyParticipant) setName(Utils::getDisplayName(faultyParticipant)); else if(participantAddress) setName(Utils::getDisplayName(participantAddress)); setStatus(NoticeType::NoticeError); //dest["message"] = "Security level degraded by %1"; }else// No callback from SDK on upgraded security event yet handledEvent = false; break; } case linphone::EventLog::Type::ConferenceEphemeralMessageEnabled : case linphone::EventLog::Type::ConferenceEphemeralMessageLifetimeChanged : { int selectedTime = mEventLog->getEphemeralMessageLifetime(); if(selectedTime == 60) setName( tr("nMinute", "", 1).arg(1) ); else if(selectedTime == 3600) setName( tr("nHour", "", 1).arg(1)); else if(selectedTime == 86400) setName(tr("nDay", "", 1).arg(1) ); else if(selectedTime == 259200) setName( tr("nDay", "", 3).arg(3) ); else if(selectedTime == 604800) setName( tr("nWeek", "", 1).arg(1) ); setStatus(NoticeType::NoticeMessage); break; } case linphone::EventLog::Type::ConferenceEphemeralMessageDisabled :{ setName(""); setStatus(NoticeType::NoticeMessage); break; } case linphone::EventLog::Type::ConferenceSubjectChanged : { setName(QString::fromStdString(mEventLog->getSubject())); setStatus(NoticeType::NoticeMessage); break; } case linphone::EventLog::Type::ConferenceParticipantSetAdmin : case linphone::EventLog::Type::ConferenceParticipantUnsetAdmin : { setName(Utils::getDisplayName(participantAddress)); setStatus(NoticeType::NoticeMessage); break; } default:{ handledEvent = false; } } setEventLogType(LinphoneEnums::fromLinphone(mEventLog->getType())); return handledEvent; } void ChatNoticeModel::setName(const QString& data){ if(data != mName) { mName = data; emit nameChanged(); } } void ChatNoticeModel::setStatus(NoticeType data){ if(data != mStatus) { mStatus = data; emit statusChanged(); } } void ChatNoticeModel::setEventLogType(const LinphoneEnums::EventLogType& data){ if(data != mEventLogType) { mEventLogType = data; emit eventLogTypeChanged(); } } void ChatNoticeModel::deleteEvent(){ if(mEventLog) mEventLog->deleteFromDatabase(); }linphone-desktop-5.0.2/linphone-app/src/components/chat-events/ChatNoticeModel.hpp000066400000000000000000000053301434616504300303120ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CHAT_NOTICE_MODEL_H #define CHAT_NOTICE_MODEL_H #include "utils/LinphoneEnums.hpp" #include "ChatEvent.hpp" // ============================================================================= class ChatNoticeModel : public ChatEvent { Q_OBJECT public: enum NoticeType { NoticeMessage, // This is a Linphone message NoticeError, // This is a Linphone error NoticeUnreadMessages }; Q_ENUM(NoticeType); static QSharedPointer create(std::shared_ptr eventLog, QObject * parent = nullptr);// Call it instead constructor static QSharedPointer create(NoticeType noticeType, const QDateTime& timestamp,const QString& txt, QObject * parent = nullptr); ChatNoticeModel (std::shared_ptr eventLog, QObject * parent = nullptr); ChatNoticeModel (NoticeType noticeType, const QDateTime& timestamp, const QString& txt, QObject * parent = nullptr); virtual ~ChatNoticeModel(); Q_PROPERTY(ChatRoomModel::EntryType type MEMBER mType CONSTANT)// NoticeEntry Q_PROPERTY(QDateTime timestamp MEMBER mTimestamp CONSTANT) Q_PROPERTY(QString name MEMBER mName WRITE setName NOTIFY nameChanged) Q_PROPERTY(NoticeType status MEMBER mStatus WRITE setStatus NOTIFY statusChanged) Q_PROPERTY(LinphoneEnums::EventLogType eventLogType MEMBER mEventLogType WRITE setEventLogType NOTIFY eventLogTypeChanged) std::shared_ptr getEventLog(); void setName(const QString& data); void setStatus(NoticeType data); void setEventLogType(const LinphoneEnums::EventLogType& data); bool update(); // Update data from eventLog virtual void deleteEvent() override; QString mName; NoticeType mStatus; LinphoneEnums::EventLogType mEventLogType; signals: void nameChanged(); void statusChanged(); void eventLogTypeChanged(); private: std::shared_ptr mEventLog; }; Q_DECLARE_METATYPE(QSharedPointer) Q_DECLARE_METATYPE(ChatNoticeModel*) #endif linphone-desktop-5.0.2/linphone-app/src/components/chat-room/000077500000000000000000000000001434616504300242465ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/chat-room/ChatRoomInitializer.cpp000066400000000000000000000077641434616504300307100ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ChatRoomInitializer.hpp" #include #include #include "ChatRoomListener.hpp" #include "components/core/CoreManager.hpp" #include "components/core/CoreHandlers.hpp" // ============================================================================= void ChatRoomInitializer::connectTo(ChatRoomListener * listener){ connect(listener, &ChatRoomListener::conferenceJoined, this, &ChatRoomInitializer::onConferenceJoined); connect(listener, &ChatRoomListener::stateChanged, this, &ChatRoomInitializer::onStateChanged); } // ============================================================================= ChatRoomInitializer::ChatRoomInitializer(std::shared_ptr chatRoom){ mChatRoomListener = std::make_shared(); connectTo(mChatRoomListener.get()); if( chatRoom){ mChatRoom = chatRoom; mChatRoom->addListener(mChatRoomListener); } } ChatRoomInitializer::~ChatRoomInitializer(){ if(mChatRoom) mChatRoom->removeListener(mChatRoomListener); } QSharedPointer ChatRoomInitializer::create(std::shared_ptr chatRoom){ QSharedPointer initializer = QSharedPointer::create(chatRoom); return initializer; } void ChatRoomInitializer::setAdminsData(QList< std::shared_ptr> admins){ mAdmins = admins; mAdminsSet = false; } void ChatRoomInitializer::setAdmins(QList< std::shared_ptr> admins){ if( admins.size() > 0) { std::list> participants = mChatRoom->getParticipants(); int count = 0; for(auto participant : participants){ auto address = participant->getAddress(); auto isAdmin = std::find_if(admins.begin(), admins.end(), [address](std::shared_ptr addr){ return addr->weakEqual(address); }); if( isAdmin != admins.end()){ ++count; mChatRoom->setParticipantAdminStatus(participant, true); } } mAdminsSet = true; qInfo() << "[ChatRoomInitializer] '" << admins.size() << "' admin(s) specified in addition of Me, " << count << " set."; checkInitialization(); } } void ChatRoomInitializer::start(QSharedPointer initializer){ QObject * context = new QObject(); QObject::connect(initializer.get(), &ChatRoomInitializer::finished, context, [context, initializer](LinphoneEnums::ChatRoomState state){ qDebug() << "[ChatRoomInitializer] initialized"; context->deleteLater();// This will destroy context and initializer }); } void ChatRoomInitializer::checkInitialization(){ if( mAdmins.size() > 0 && !mAdminsSet) return; emit finished(LinphoneEnums::fromLinphone(mChatRoom->getState())); } void ChatRoomInitializer::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { qDebug() << "[ChatRoomInitializer] Conference has been set"; setAdmins(mAdmins); } void ChatRoomInitializer::onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState) { qDebug() << "[ChatRoomInitializer] State : " << (int)newState; if( newState >= linphone::ChatRoom::State::Created || newState == linphone::ChatRoom::State::Instantiated) { checkInitialization(); } }linphone-desktop-5.0.2/linphone-app/src/components/chat-room/ChatRoomInitializer.hpp000066400000000000000000000051671434616504300307100ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CHAT_ROOM_INITIALIZER_H_ #define CHAT_ROOM_INITIALIZER_H_ #include #include "ChatRoomInitializer.hpp" #include "utils/LinphoneEnums.hpp" #include #include // ============================================================================= class ChatRoomListener; class ChatRoomInitializer : public QObject{ Q_OBJECT public: ChatRoomInitializer(std::shared_ptr chatRoom); ~ChatRoomInitializer(); // DATA to set QList< std::shared_ptr> mAdmins; bool mAdminsSet = false; static QSharedPointer create(std::shared_ptr chatRoom); // Return a shared pointer to pass to start function (if delayed creation is needed) void setAdminsData(QList< std::shared_ptr> admins);// Call it to initialize admins for delayed creation. void setAdmins(QList< std::shared_ptr> admins);// set admins directly in chat room from list static void start(QSharedPointer initializer);// Keep a ref and remove it when initialization is finished. // Linphone callbacks virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState); signals: void finished(LinphoneEnums::ChatRoomState state); // this signal is emit before deletion and give the current linphone::ChatRoom:State of the chat room. private: void connectTo(ChatRoomListener * listener); void checkInitialization();// Will send finished() if all are done std::shared_ptr mChatRoom; std::shared_ptr mChatRoomListener; // This need to be a shared_ptr because of adding it to linphone }; #endif linphone-desktop-5.0.2/linphone-app/src/components/chat-room/ChatRoomListener.cpp000066400000000000000000000155111434616504300301770ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ChatRoomListener.hpp" #include "../calls/CallsListModel.hpp" #include #include #include ChatRoomListener::ChatRoomListener(QObject * parent): QObject(parent){ } ChatRoomListener::~ChatRoomListener(){ } //--------------------------------------------------------------------------------------------------- void ChatRoomListener::onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing){ emit isComposingReceived(chatRoom, remoteAddress, isComposing); } void ChatRoomListener::onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){ emit messageReceived(chatRoom, message); } void ChatRoomListener::onMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & messages){ emit messagesReceived(chatRoom, messages); } void ChatRoomListener::onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit newEvent(chatRoom, eventLog); } void ChatRoomListener::onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit chatMessageReceived(chatRoom, eventLog); } void ChatRoomListener::onChatMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & eventLogs){ emit chatMessagesReceived(chatRoom, eventLogs); } void ChatRoomListener::onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit chatMessageSending(chatRoom, eventLog); } void ChatRoomListener::onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit chatMessageSent(chatRoom, eventLog); } void ChatRoomListener::onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit participantAdded(chatRoom, eventLog); } void ChatRoomListener::onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit participantRemoved(chatRoom, eventLog); } void ChatRoomListener::onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit participantAdminStatusChanged(chatRoom, eventLog); } void ChatRoomListener::onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState){ emit stateChanged(chatRoom, newState); } void ChatRoomListener::onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit securityEvent(chatRoom, eventLog); } void ChatRoomListener::onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit subjectChanged(chatRoom, eventLog); } void ChatRoomListener::onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){ emit undecryptableMessageReceived(chatRoom, message); } void ChatRoomListener::onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit participantDeviceAdded(chatRoom, eventLog); } void ChatRoomListener::onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit participantDeviceRemoved(chatRoom, eventLog); } void ChatRoomListener::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit conferenceJoined(chatRoom, eventLog); } void ChatRoomListener::onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit conferenceLeft(chatRoom, eventLog); } void ChatRoomListener::onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit ephemeralEvent(chatRoom, eventLog); } void ChatRoomListener::onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit ephemeralMessageTimerStarted(chatRoom, eventLog); } void ChatRoomListener::onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ emit ephemeralMessageDeleted(chatRoom, eventLog); } void ChatRoomListener::onConferenceAddressGeneration(const std::shared_ptr & chatRoom){ emit conferenceAddressGeneration(chatRoom); } void ChatRoomListener::onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){ emit participantRegistrationSubscriptionRequested(chatRoom, participantAddress); } void ChatRoomListener::onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){ emit participantRegistrationUnsubscriptionRequested(chatRoom, participantAddress); } void ChatRoomListener::onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message){ emit chatMessageShouldBeStored(chatRoom, message); } void ChatRoomListener::onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state){ emit chatMessageParticipantImdnStateChanged(chatRoom, message, state); } linphone-desktop-5.0.2/linphone-app/src/components/chat-room/ChatRoomListener.hpp000066400000000000000000000225531434616504300302100ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CHATROOM_LISTENER_H_ #define CHATROOM_LISTENER_H_ // ============================================================================= #include #include #include class ChatRoomModel; class TimelineModel; class ChatRoomListener : public QObject, public linphone::ChatRoomListener { Q_OBJECT public: ChatRoomListener(QObject * parent = nullptr); virtual ~ChatRoomListener(); virtual void onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing) override; virtual void onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; virtual void onMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & messages) override; virtual void onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onChatMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & eventLogs) override; virtual void onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState) override; virtual void onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; virtual void onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; virtual void onConferenceAddressGeneration(const std::shared_ptr & chatRoom) override; virtual void onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; virtual void onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; virtual void onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; virtual void onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state) override; signals: void isComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing); void messageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message); void messagesReceived(const std::shared_ptr & chatRoom, const std::list> & messages); void newEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void chatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void chatMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & eventLogs); void chatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void chatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void participantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void participantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void participantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void stateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState); void securityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void subjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void undecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message); void participantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void participantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void conferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void conferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void ephemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void ephemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void ephemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); void conferenceAddressGeneration(const std::shared_ptr & chatRoom); void participantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); void participantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); void chatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message); void chatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state); }; #endif linphone-desktop-5.0.2/linphone-app/src/components/chat-room/ChatRoomModel.cpp000066400000000000000000001554421434616504300274620ustar00rootroot00000000000000/* * Copyright (c) 2010-2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ChatRoomModel.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "ChatRoomListener.hpp" #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" #include "components/calls/CallsListModel.hpp" #include "components/chat/ChatModel.hpp" #include "components/chat-events/ChatCallModel.hpp" #include "components/chat-events/ChatEvent.hpp" #include "components/chat-events/ChatMessageModel.hpp" #include "components/chat-events/ChatNoticeModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/content/ContentListModel.hpp" #include "components/content/ContentModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/notifier/Notifier.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/participant/ParticipantModel.hpp" #include "components/participant/ParticipantListModel.hpp" #include "components/presence/Presence.hpp" #include "components/recorder/RecorderManager.hpp" #include "components/recorder/RecorderModel.hpp" #include "components/timeline/TimelineModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "components/core/event-count-notifier/AbstractEventCountNotifier.hpp" #include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "utils/LinphoneEnums.hpp" // ============================================================================= using namespace std; // ----------------------------------------------------------------------------- void ChatRoomModel::connectTo(ChatRoomListener * listener){ connect(listener, &ChatRoomListener::isComposingReceived, this, &ChatRoomModel::onIsComposingReceived); connect(listener, &ChatRoomListener::messageReceived, this, &ChatRoomModel::onMessageReceived); connect(listener, &ChatRoomListener::messagesReceived, this, &ChatRoomModel::onMessagesReceived); connect(listener, &ChatRoomListener::newEvent, this, &ChatRoomModel::onNewEvent); connect(listener, &ChatRoomListener::chatMessageReceived, this, &ChatRoomModel::onChatMessageReceived); connect(listener, &ChatRoomListener::chatMessagesReceived, this, &ChatRoomModel::onChatMessagesReceived); connect(listener, &ChatRoomListener::chatMessageSending, this, &ChatRoomModel::onChatMessageSending); connect(listener, &ChatRoomListener::chatMessageSent, this, &ChatRoomModel::onChatMessageSent); connect(listener, &ChatRoomListener::participantAdded, this, &ChatRoomModel::onParticipantAdded); connect(listener, &ChatRoomListener::participantRemoved, this, &ChatRoomModel::onParticipantRemoved); connect(listener, &ChatRoomListener::participantAdminStatusChanged, this, &ChatRoomModel::onParticipantAdminStatusChanged); connect(listener, &ChatRoomListener::stateChanged, this, &ChatRoomModel::onStateChanged); connect(listener, &ChatRoomListener::securityEvent, this, &ChatRoomModel::onSecurityEvent); connect(listener, &ChatRoomListener::subjectChanged, this, &ChatRoomModel::onSubjectChanged); connect(listener, &ChatRoomListener::undecryptableMessageReceived, this, &ChatRoomModel::onUndecryptableMessageReceived); connect(listener, &ChatRoomListener::participantDeviceAdded, this, &ChatRoomModel::onParticipantDeviceAdded); connect(listener, &ChatRoomListener::participantDeviceRemoved, this, &ChatRoomModel::onParticipantDeviceRemoved); connect(listener, &ChatRoomListener::conferenceJoined, this, &ChatRoomModel::onConferenceJoined); connect(listener, &ChatRoomListener::conferenceLeft, this, &ChatRoomModel::onConferenceLeft); connect(listener, &ChatRoomListener::ephemeralEvent, this, &ChatRoomModel::onEphemeralEvent); connect(listener, &ChatRoomListener::ephemeralMessageTimerStarted, this, &ChatRoomModel::onEphemeralMessageTimerStarted); connect(listener, &ChatRoomListener::ephemeralMessageDeleted, this, &ChatRoomModel::onEphemeralMessageDeleted); connect(listener, &ChatRoomListener::conferenceAddressGeneration, this, &ChatRoomModel::onConferenceAddressGeneration); connect(listener, &ChatRoomListener::participantRegistrationSubscriptionRequested, this, &ChatRoomModel::onParticipantRegistrationSubscriptionRequested); connect(listener, &ChatRoomListener::participantRegistrationUnsubscriptionRequested, this, &ChatRoomModel::onParticipantRegistrationUnsubscriptionRequested); connect(listener, &ChatRoomListener::chatMessageShouldBeStored, this, &ChatRoomModel::onChatMessageShouldBeStored); connect(listener, &ChatRoomListener::chatMessageParticipantImdnStateChanged, this, &ChatRoomModel::onChatMessageParticipantImdnStateChanged); } // ----------------------------------------------------------------------------- QSharedPointer ChatRoomModel::create(const std::shared_ptr& chatRoom, const std::list>& callLogs){ QSharedPointer model = QSharedPointer::create(chatRoom, callLogs); if(model){ model->mSelf = model; //chatRoom->addListener(model); return model; }else return nullptr; } ChatRoomModel::ChatRoomModel (const std::shared_ptr& chatRoom, const std::list>& callLogs, QObject * parent) : ProxyListModel(parent){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE CoreManager *coreManager = CoreManager::getInstance(); mCoreHandlers = coreManager->getHandlers(); mChatRoom = chatRoom; mChatRoomListener = std::make_shared(); connectTo(mChatRoomListener.get()); mChatRoom->addListener(mChatRoomListener); // Get messages. mList.clear(); setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); setMissedCallsCount(0); QElapsedTimer timer; timer.start(); CoreHandlers *coreHandlers = mCoreHandlers.get(); QObject::connect(this, &ChatRoomModel::messageSent, this, &ChatRoomModel::resetMessageCount); QObject::connect(coreHandlers, &CoreHandlers::callCreated, this, &ChatRoomModel::handleCallCreated); QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatRoomModel::handleCallStateChanged); QObject::connect(coreHandlers, &CoreHandlers::presenceStatusReceived, this, &ChatRoomModel::handlePresenceStatusReceived); QObject::connect(coreManager->getContactsListModel(), &ContactsListModel::contactAdded, this, &ChatRoomModel::fullPeerAddressChanged); QObject::connect(coreManager->getContactsListModel(), &ContactsListModel::contactAdded, this, &ChatRoomModel::avatarChanged); QObject::connect(coreManager->getContactsListModel(), &ContactsListModel::contactRemoved, this, &ChatRoomModel::fullPeerAddressChanged); QObject::connect(coreManager->getContactsListModel(), &ContactsListModel::contactRemoved, this, &ChatRoomModel::avatarChanged); QObject::connect(coreManager->getContactsListModel(), &ContactsListModel::contactUpdated, this, &ChatRoomModel::fullPeerAddressChanged); QObject::connect(coreManager->getContactsListModel(), &ContactsListModel::contactUpdated, this, &ChatRoomModel::avatarChanged); connect(this, &ChatRoomModel::fullPeerAddressChanged, this, &ChatRoomModel::usernameChanged); connect(this, &ChatRoomModel::stateChanged, this, &ChatRoomModel::updatingChanged); if(mChatRoom){ mParticipantListModel = QSharedPointer::create(this); connect(mParticipantListModel.get(), &ParticipantListModel::participantsChanged, this, &ChatRoomModel::fullPeerAddressChanged); auto participants = getParticipants(false); for(auto participant : participants){ auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(Utils::coreStringToAppString((participant)->getAddress()->asString())); if(contact) { connect(contact.get(), &ContactModel::contactUpdated, this, &ChatRoomModel::fullPeerAddressChanged); } } std::shared_ptr lastCall = nullptr; QString peerAddress = getParticipantAddress(); std::shared_ptr lLocalAddress = mChatRoom->getLocalAddress(); QString localAddress = Utils::coreStringToAppString(lLocalAddress->asStringUriOnly()); if(callLogs.size() == 0) { auto callHistory = CallsListModel::getCallHistory(peerAddress, localAddress); if(callHistory.size() > 0) lastCall = callHistory.front(); }else{// Find the last call in list std::shared_ptr lPeerAddress = Utils::interpretUrl(peerAddress); if( lPeerAddress && lLocalAddress){ auto itCallLog = std::find_if(callLogs.begin(), callLogs.end(), [lPeerAddress, lLocalAddress](std::shared_ptr c){ return c->getLocalAddress()->weakEqual(lLocalAddress) && c->getRemoteAddress()->weakEqual(lPeerAddress); }); if( itCallLog != callLogs.end()) lastCall = *itCallLog; } } if(lastCall){ auto callDate = lastCall->getStartDate(); if( lastCall->getStatus() == linphone::Call::Status::Success ) callDate += lastCall->getDuration(); setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(std::max(mChatRoom->getLastUpdateTime(), callDate )*1000)); }else setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(mChatRoom->getLastUpdateTime()*1000)); }else mParticipantListModel = nullptr; } ChatRoomModel::~ChatRoomModel () { mParticipantListModel = nullptr; if(mChatRoom ){ mChatRoom->removeListener(mChatRoomListener); if(mDeleteChatRoom){ mDeleteChatRoom = false; if(CoreManager::getInstance()->getCore() ){ auto participants = getParticipants(); std::list> participantsAddress; for(auto p : participants) participantsAddress.push_back(p->getAddress()->clone()); auto internalChatRoom = CoreManager::getInstance()->getCore()->searchChatRoom(mChatRoom->getCurrentParams(), mChatRoom->getLocalAddress(), mChatRoom->getPeerAddress(), participantsAddress); if( internalChatRoom) { qInfo() << "Deleting ChatRoom : " << getSubject() << ", address=" << getFullPeerAddress(); CoreManager::getInstance()->getCore()->deleteChatRoom(internalChatRoom); } } } } mChatRoom = nullptr; } QHash ChatRoomModel::roleNames () const { QHash roles; roles[Roles::ChatEntry] = "$chatEntry"; roles[Roles::SectionDate] = "$sectionDate"; return roles; } QVariant ChatRoomModel::data (const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); switch (role) { case Roles::ChatEntry: return QVariant::fromValue(mList[row].get()); case Roles::SectionDate: return QVariant::fromValue(mList[row].objectCast()->getTimestamp().date()); } return QVariant(); } bool ChatRoomModel::removeRows (int row, int count, const QModelIndex &parent) { int limit = row + count - 1; if (row < 0 || count < 0 || limit >= mList.count()) return false; beginRemoveRows(parent, row, limit); for (int i = 0; i < count; ++i) { mList[row].objectCast()->deleteEvent(); mList.removeAt(row); } endRemoveRows(); if (mList.count() == 0) emit allEntriesRemoved(mSelf.lock()); else if (limit == mList.count()) emit lastEntryRemoved(); emit focused();// Removing rows is like having focus. Don't wait asynchronous events. return true; } void ChatRoomModel::removeAllEntries () { qInfo() << QStringLiteral("Removing all entries of: (%1, %2).") .arg(getPeerAddress()).arg(getLocalAddress()); auto core = CoreManager::getInstance()->getCore(); bool standardChatEnabled = CoreManager::getInstance()->getSettingsModel()->getStandardChatEnabled(); beginResetModel(); mList.clear(); mChatRoom->deleteHistory(); if( isOneToOne() && // Remove calls only if chat room is one-one and not secure (if available) ( !standardChatEnabled || !isSecure()) ) { auto callLogs = CallsListModel::getCallHistory(getParticipantAddress(), Utils::coreStringToAppString(mChatRoom->getLocalAddress()->asStringUriOnly())); bool haveLogs = callLogs.size() > 0; for(auto callLog : callLogs) core->removeCallLog(callLog); if(haveLogs) emit CoreManager::getInstance()->callLogsCountChanged(); } if( mChatRoom->isReadOnly())// = hasBeenLeft() deleteChatRoom(); endResetModel(); emit allEntriesRemoved(mSelf.lock()); emit focused();// Removing all entries is like having focus. Don't wait asynchronous events. } void ChatRoomModel::removeEntry(ChatEvent* entry){ remove(entry); } void ChatRoomModel::emitFullPeerAddressChanged(){ emit fullPeerAddressChanged(); } //-------------------------------------------------------------------------------------------- QString ChatRoomModel::getPeerAddress () const { return mChatRoom && mChatRoom->getPeerAddress() ? Utils::coreStringToAppString(mChatRoom->getPeerAddress()->asStringUriOnly()) : ""; } QString ChatRoomModel::getLocalAddress () const { if(!mChatRoom) return ""; else { auto localAddress = mChatRoom->getLocalAddress()->clone(); localAddress->clean(); return Utils::coreStringToAppString( localAddress->asStringUriOnly() ); } } QString ChatRoomModel::getFullPeerAddress () const { return mChatRoom && mChatRoom->getPeerAddress() ? Utils::coreStringToAppString(mChatRoom->getPeerAddress()->asString()) : ""; } QString ChatRoomModel::getFullLocalAddress () const { return mChatRoom && mChatRoom->getLocalAddress()? Utils::coreStringToAppString(mChatRoom->getLocalAddress()->asString()) : ""; } QString ChatRoomModel::getConferenceAddress () const { if(!mChatRoom || mChatRoom->hasCapability((int)linphone::ChatRoomCapabilities::Basic)) return ""; else { auto address = mChatRoom->getConferenceAddress(); return address?Utils::coreStringToAppString(address->asString()):""; } } QString ChatRoomModel::getSubject () const { return mChatRoom ? Utils::coreStringToAppString(mChatRoom->getSubject()) : ""; // in UTF8 } QString ChatRoomModel::getUsername () const { QString username; if( !mChatRoom) return ""; if( !isOneToOne()) username = getSubject(); if(username != "") return username; if( mChatRoom->getNbParticipants() == 1 ) { auto call = mChatRoom->getCall(); if(call) username = Utils::getDisplayName(call->getRemoteAddress()); if(username != "") return username; } if( mChatRoom->getNbParticipants() >= 1) username = mParticipantListModel->displayNamesToString(); if(username != "") return username; if(haveEncryption() || isGroupEnabled()) return "";// Wait for more info username = Utils::getDisplayName(mChatRoom->getPeerAddress()); if(username != "") return username; auto addr = mChatRoom->getPeerAddress(); if( addr) return Utils::coreStringToAppString(addr->asStringUriOnly()); else { qWarning() << "ChatRoom has no peer address or address is invalid : Subject=" << getSubject() << ", created at " << QDateTime::fromSecsSinceEpoch(mChatRoom->getCreationTime()) << " (" << mChatRoom.get() << ")"; return ""; } } QString ChatRoomModel::getAvatar () const { if( mChatRoom && mChatRoom->getNbParticipants() == 1){ auto participants = getParticipants(false); auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(Utils::coreStringToAppString((*participants.begin())->getAddress()->asString())); if(contact) return contact->getVcardModel()->getAvatar(); } return ""; } int ChatRoomModel::getPresenceStatus() const { if( mChatRoom && mChatRoom->getNbParticipants() == 1 && !isGroupEnabled()){ auto participants = getParticipants(false); auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(Utils::coreStringToAppString((*participants.begin())->getAddress()->asString())); if(contact) { return contact->getPresenceLevel(); } else return -1; }else return -1; } ParticipantListModel* ChatRoomModel::getParticipantListModel() const{ return mParticipantListModel.get(); } std::list> ChatRoomModel::getParticipants(const bool& withMe) const{ auto participantList = mChatRoom->getParticipants(); if(withMe) { auto me = mChatRoom->getMe(); if( me ) participantList.push_front(me); } return participantList; } LinphoneEnums::ChatRoomState ChatRoomModel::getState() const { return mChatRoom ? LinphoneEnums::fromLinphone(mChatRoom->getState()) : LinphoneEnums::ChatRoomStateNone; } bool ChatRoomModel::isReadOnly() const{ return mChatRoom && mChatRoom->isReadOnly(); } bool ChatRoomModel::isEphemeralEnabled() const{ return mChatRoom && mChatRoom->ephemeralEnabled(); } long ChatRoomModel::getEphemeralLifetime() const{ return mChatRoom ? mChatRoom->getEphemeralLifetime() : 0; } bool ChatRoomModel::canBeEphemeral(){ return isConference(); } bool ChatRoomModel::haveEncryption() const{ return mChatRoom && mChatRoom->getCurrentParams()->getEncryptionBackend() != linphone::ChatRoomEncryptionBackend::None; } bool ChatRoomModel::haveConferenceAddress() const{ return mChatRoom && getFullPeerAddress().toLower().contains("conf-id"); } bool ChatRoomModel::markAsReadEnabled() const{ return mMarkAsReadEnabled; } bool ChatRoomModel::isSecure() const{ return mChatRoom && (mChatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Encrypted || mChatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Safe); } int ChatRoomModel::getSecurityLevel() const{ return mChatRoom ? (int)mChatRoom->getSecurityLevel() : 0; } bool ChatRoomModel::isGroupEnabled() const{ return mChatRoom && mChatRoom->getCurrentParams()->groupEnabled(); } bool ChatRoomModel::isConference() const{ return mChatRoom && mChatRoom->hasCapability((int)linphone::ChatRoomCapabilities::Conference); } bool ChatRoomModel::isOneToOne() const{ return mChatRoom && mChatRoom->hasCapability((int)linphone::ChatRoomCapabilities::OneToOne); } bool ChatRoomModel::isMeAdmin() const{ return mChatRoom && mChatRoom->getMe()->isAdmin(); } bool ChatRoomModel::isCurrentAccount() const{ return mChatRoom && Utils::isMe(mChatRoom->getLocalAddress()); } bool ChatRoomModel::canHandleParticipants() const{ return mChatRoom && mChatRoom->canHandleParticipants(); } bool ChatRoomModel::getIsRemoteComposing () const { return mComposers.size() > 0; } bool ChatRoomModel::isEntriesLoading() const{ return mEntriesLoading; } bool ChatRoomModel::isBasic() const{ return mChatRoom && mChatRoom->hasCapability((int)linphone::ChatRoomCapabilities::Basic); } bool ChatRoomModel::isUpdating() const{ return getState() == LinphoneEnums::ChatRoomStateCreationPending || getState() == LinphoneEnums::ChatRoomStateTerminationPending; } std::shared_ptr ChatRoomModel::getChatRoom(){ return mChatRoom; } QList ChatRoomModel::getComposers(){ return mComposers.values(); } QString ChatRoomModel::getParticipantAddress(){ if(!isSecure()){ auto peerAddress = mChatRoom->getPeerAddress(); if( peerAddress) return Utils::coreStringToAppString(peerAddress->asString()); else if(isConference()){ auto conferenceAddress = mChatRoom->getConferenceAddress(); if( conferenceAddress) return Utils::coreStringToAppString(conferenceAddress->asString()); else{ qWarning() << "ConferenceAddress is NULL when requesting it from not secure and conference ChatRoomModel. Subject=" << getSubject() << ", created at " << QDateTime::fromSecsSinceEpoch(mChatRoom->getCreationTime()) << " (" << mChatRoom.get() << ")"; return ""; } }else { qWarning() << "PeerAddress is NULL when requesting it from not secure ChatRoomModel. Subject=" << getSubject() << ", created at " << QDateTime::fromSecsSinceEpoch(mChatRoom->getCreationTime()) << " (" << mChatRoom.get() << ")"; return ""; } }else{ auto participants = getParticipantListModel(); if(participants->getCount() > 1) return participants->getAt(1)->getSipAddress(); else return ""; } } int ChatRoomModel::getAllUnreadCount(){ return mUnreadMessagesCount + mMissedCallsCount; } //------------------------------------------------------------------------------------------------ void ChatRoomModel::setSubject(QString& subject){ if(mChatRoom && getSubject() != subject){ mChatRoom->setSubject(Utils::appStringToCoreString(subject)); // in UTF8 emit subjectChanged(subject); } } void ChatRoomModel::setLastUpdateTime(const QDateTime& lastUpdateDate) { if(mLastUpdateTime != lastUpdateDate ) { mLastUpdateTime = lastUpdateDate; emit lastUpdateTimeChanged(); } } void ChatRoomModel::updateLastUpdateTime(){ if( mChatRoom ){ QDateTime lastDateTime = QDateTime::fromMSecsSinceEpoch(mChatRoom->getLastUpdateTime()*1000); QDateTime lastCallTime = lastDateTime; for(auto e : mList){ auto chatEvent = e.objectCast(); if(chatEvent->mType == CallEntry && chatEvent->getTimestamp() > lastCallTime) lastCallTime = chatEvent->getTimestamp(); } setLastUpdateTime(lastCallTime); } } void ChatRoomModel::setUnreadMessagesCount(const int& count){ updateNewMessageNotice(count); if(count != mUnreadMessagesCount){ mUnreadMessagesCount = count; emit unreadMessagesCountChanged(); } } void ChatRoomModel::setMissedCallsCount(const int& count){ if(count != mMissedCallsCount){ mMissedCallsCount = count; emit missedCallsCountChanged(); } } void ChatRoomModel::addMissedCallsCount(std::shared_ptr call){ insertCall(call->getCallLog()); auto timeline = CoreManager::getInstance()->getTimelineListModel()->getTimeline(mChatRoom, false); if(!timeline || !timeline->mSelected){ setMissedCallsCount(mMissedCallsCount+1); CoreManager::getInstance()->getEventCountNotifier()->handleCallMissed(&call->getData("call-model")); } } void ChatRoomModel::setEphemeralEnabled(bool enabled){ if(isEphemeralEnabled() != enabled){ mChatRoom->enableEphemeral(enabled); emit ephemeralEnabledChanged(); } } void ChatRoomModel::setEphemeralLifetime(long lifetime){ if(getEphemeralLifetime() != lifetime){ mChatRoom->setEphemeralLifetime(lifetime); emit ephemeralLifetimeChanged(); } } void ChatRoomModel::enableMarkAsRead(const bool& enable){ if( mMarkAsReadEnabled != enable){ mMarkAsReadEnabled = enable; emit markAsReadEnabledChanged(); } } void ChatRoomModel::setReply(ChatMessageModel * model){ if(model != mReplyModel.get()){ if( model && model->getChatMessage() ) mReplyModel = ChatMessageModel::create(model->getChatMessage()); else mReplyModel = nullptr; emit replyChanged(); } } ChatMessageModel * ChatRoomModel::getReply()const{ return mReplyModel.get(); } //------------------------------------------------------------------------------------------------ void ChatRoomModel::markAsToDelete(){ mDeleteChatRoom = true; } void ChatRoomModel::deleteChatRoom(){ qInfo() << "Deleting ChatRoom : " << getSubject() << ", address=" << getFullPeerAddress(); if(mChatRoom){ CoreManager::getInstance()->getCore()->deleteChatRoom(mChatRoom); } } void ChatRoomModel::leaveChatRoom (){ if(mChatRoom){ if(!isReadOnly()) mChatRoom->leave(); if( mChatRoom->getHistorySize() == 0 && mChatRoom->getHistoryEventsSize() == 0) deleteChatRoom(); } } void ChatRoomModel::updateParticipants(const QVariantList& participants){ /* std::shared_ptr params = core->createDefaultChatRoomParams(); std::list > chatRoomParticipants; std::shared_ptr localAddress; for(auto p : participants){ ParticipantModel* participant = p.value(); auto address = Utils::interpretUrl(participant->getSipAddress()); if( address) chatRoomParticipants.push_back( address ); } if(mChatRoom->canHandleParticipants()) { mChatRoom->addParticipants(newParticipants); mChatRoom->removeParticipants(removeParticipants); } linphone::ChatRoom;*/ } // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- void ChatRoomModel::sendMessage (const QString &message) { std::list > _messages; bool isBasicChatRoom = isBasic(); if(mReplyModel && mReplyModel->getChatMessage()) { _messages.push_back(mChatRoom->createReplyMessage(mReplyModel->getChatMessage())); }else _messages.push_back(mChatRoom->createEmptyMessage()); auto recorder = CoreManager::getInstance()->getRecorderManager(); if(recorder->haveVocalRecorder()) { recorder->getVocalRecorder()->stop(); auto content = recorder->getVocalRecorder()->getRecorder()->createContent(); if(content) { _messages.back()->addContent(content); } } auto fileContents = CoreManager::getInstance()->getChatModel()->getContentListModel()->getSharedList(); for(auto content : fileContents){ if(isBasicChatRoom && _messages.back()->getContents().size() > 0) // Basic chat rooms don't support multipart _messages.push_back(mChatRoom->createEmptyMessage()); _messages.back()->addFileContent(content->getContent()); } if(!message.isEmpty()) { if(isBasicChatRoom && _messages.back()->getContents().size() > 0) // Basic chat rooms don't support multipart _messages.push_back(mChatRoom->createEmptyMessage()); _messages.back()->addUtf8TextContent(message.toUtf8().toStdString()); } bool sent = false; for(auto itMessage = _messages.begin() ; itMessage != _messages.end() ; ++itMessage) { if((*itMessage)->getContents().size() > 0){// Have something to send (*itMessage)->send(); emit messageSent((*itMessage)); sent = true; } } if(sent){ setReply(nullptr); if(recorder->haveVocalRecorder()) recorder->clearVocalRecorder(); CoreManager::getInstance()->getChatModel()->clear(); } } void ChatRoomModel::forwardMessage(ChatMessageModel * model){ if(model){ shared_ptr _message; _message = mChatRoom->createForwardMessage(model->getChatMessage()); auto recorder = CoreManager::getInstance()->getRecorderManager(); if(recorder->haveVocalRecorder()) { auto content = recorder->getVocalRecorder()->getRecorder()->createContent(); if(content) _message->addContent(content); } _message->send(); emit messageSent(_message); } } // ----------------------------------------------------------------------------- void ChatRoomModel::compose () { if( mChatRoom) mChatRoom->compose(); } void ChatRoomModel::resetMessageCount () { if(mChatRoom && !mDeleteChatRoom && markAsReadEnabled()){ if( mChatRoom->getState() != linphone::ChatRoom::State::Deleted){ if (mChatRoom->getUnreadMessagesCount() > 0){ mChatRoom->markAsRead();// Marking as read is only for messages. Not for calls. } setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); }else setUnreadMessagesCount(0); setMissedCallsCount(0); emit messageCountReset(); CoreManager::getInstance()->updateUnreadMessageCount(); } } //------------------------------------------------- // Entries Loading managment //------------------------------------------------- // For each type of events, a part of entries are loaded with a minimal count (=mLastEntriesStep). Like that, we have from 0 to 3*mLastEntriesStep events. // We store them in a list that will be sorted from oldest to newest. // From the oldest, we loop till having at least one type of event or if we hit the minimum limit. // As it was a request for each events, we ensure to get all available events after it. // Notations : M0 is the first Message event; N0, the first EventLog; C0, the first Call event. After '|', there are mLastEntriesStep events. // Available cases examples : // 'M0...N0....|...C0....' => '|...C0....' // 'M0C0N0|...' == 'C0N0|...' == '|N0....C0....' == '|N0....C0....' == '|.......' We suppose that we got all available events for the current scope. // 'N0...M0....C0...|...' => '|C0...' // // ------------------- // // When requesting more entries, we count the number of events we got. Each numbers represent the index from what we can retrieve next events from linphone database. // Like that, we avoid to load all database. A bad point is about loading call events : There are no range to retrieve and we don't want to load the entire database. So for this case, this is not fully optimized (optimization is only about GUI and connections) // // Request more entries are coming from GUI. Like that, we don't have to manage if events are filtered or not (only messages, call, events). class EntrySorterHelper{ public: EntrySorterHelper(time_t pTime, ChatRoomModel::EntryType pType,std::shared_ptr obj) : mTime(pTime), mType(pType), mObject(obj) {} time_t mTime; ChatRoomModel::EntryType mType; std::shared_ptr mObject; static void getLimitedSelection(QList > *resultEntries, QList& entries, const int& minEntries, ChatRoomModel * chatRoomModel) {// Sort and return a selection with at least 'minEntries' // Sort list std::sort(entries.begin(), entries.end(), [](const EntrySorterHelper& a, const EntrySorterHelper& b) { return a.mTime < b.mTime; }); // Keep max( minEntries, last(messages, events, calls) ) QList::iterator itEntries = entries.begin(); int spotted = 0; auto lastEntry = itEntries; while(itEntries != entries.end() && (spotted != 7 && (entries.end()-itEntries > minEntries)) ) { if( itEntries->mType == ChatRoomModel::EntryType::MessageEntry) { if( (spotted & 1) == 0) { lastEntry = itEntries; spotted |= 1; } }else if( itEntries->mType == ChatRoomModel::EntryType::CallEntry){ if( (spotted & 2) == 0){ lastEntry = itEntries; spotted |= 2; } }else { if( (spotted & 4) == 0){ lastEntry = itEntries; spotted |= 4; } } ++itEntries; } itEntries = lastEntry; if(itEntries - entries.begin() < 3) itEntries = entries.begin(); for(; itEntries != entries.end() ; ++itEntries){ if( (*itEntries).mType== ChatRoomModel::EntryType::MessageEntry) *resultEntries << ChatMessageModel::create(std::dynamic_pointer_cast(itEntries->mObject)); else if( (*itEntries).mType == ChatRoomModel::EntryType::CallEntry) { auto entry = ChatCallModel::create(std::dynamic_pointer_cast(itEntries->mObject), true); if(entry) { *resultEntries << entry; if (entry->mStatus == LinphoneEnums::CallStatusSuccess) { entry = ChatCallModel::create(entry->getCallLog(), false); if(entry) *resultEntries << entry; } } }else{ auto entry = ChatNoticeModel::create(std::dynamic_pointer_cast(itEntries->mObject)); if(entry) { *resultEntries << entry; } } } } }; void ChatRoomModel::updateNewMessageNotice(const int& count){ if( mChatRoom ) { if(mUnreadMessageNotice ) { removeEntry(mUnreadMessageNotice.get()); mUnreadMessageNotice = nullptr; } if(count > 0){ QDateTime lastUnreadMessage = QDateTime::currentDateTime(); enableMarkAsRead(false); // Get chat messages for (auto &message : mChatRoom->getHistory(mLastEntriesStep)) { if( !message->isRead()) { lastUnreadMessage = min(lastUnreadMessage, QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000 - 1 )); //-1 to be sure that event will be before the message } } mUnreadMessageNotice = ChatNoticeModel::create(ChatNoticeModel::NoticeType::NoticeUnreadMessages, lastUnreadMessage, QString::number(count)); beginInsertRows(QModelIndex(), 0, 0); mList.prepend(mUnreadMessageNotice); endInsertRows(); qDebug() << "New message notice timestamp to :" << lastUnreadMessage.toString(); } //emit layoutChanged(); } } int ChatRoomModel::loadTillMessage(ChatMessageModel * message){ if( message){ qDebug() << "Load history till message : " << message->getChatMessage()->getMessageId().c_str(); auto linphoneMessage = message->getChatMessage(); // First find on current list auto entry = std::find_if(mList.begin(), mList.end(), [linphoneMessage](const QSharedPointer& entry ){ auto chatEventEntry = entry.objectCast(); return chatEventEntry->mType == ChatRoomModel::EntryType::MessageEntry && chatEventEntry.objectCast()->getChatMessage() == linphoneMessage; }); // if not find, load more entries and find it in new entries. if( entry == mList.end()){ mPostModelChangedEvents = false; beginResetModel(); int newEntries = loadMoreEntries(); while( newEntries > 0){// no more new entries int entryCount = 0; entry = mList.begin(); auto chatEventEntry = entry->objectCast(); while(entryCount < newEntries && (chatEventEntry->mType != ChatRoomModel::EntryType::MessageEntry || chatEventEntry.objectCast()->getChatMessage() != linphoneMessage) ){ ++entryCount; ++entry; if( entry != mList.end()) chatEventEntry = entry->objectCast(); } if( entryCount < newEntries){// We got it qDebug() << "Find message at " << entryCount << " after loading new entries"; mPostModelChangedEvents = true; endResetModel(); return entryCount; }else newEntries = loadMoreEntries();// continue } mPostModelChangedEvents = true; endResetModel(); }else{ int entryCount = entry - mList.begin(); qDebug() << "Find message at " << entryCount; return entryCount; } qWarning() << "Message has not been found in history"; } return -1; } bool ChatRoomModel::isTerminated(const std::shared_ptr& chatRoom){ return chatRoom->getState() == linphone::ChatRoom::State::Terminated || chatRoom->getState() == linphone::ChatRoom::State::Deleted; } bool ChatRoomModel::exists(const std::shared_ptr message) const{ auto entry = std::find_if(mList.begin(), mList.end(), [message](const QSharedPointer& entry ){ auto chatEventEntry = entry.objectCast(); return chatEventEntry->mType == ChatRoomModel::EntryType::MessageEntry && chatEventEntry.objectCast()->getChatMessage() == message; }); // if not find, load more entries and find it in new entries. return entry != mList.end(); } void ChatRoomModel::addBindingCall(){ // If a call is binding to this chat room, we avoid cleaning data (Add=+1, remove=-1) ++mBindingCalls; } void ChatRoomModel::removeBindingCall(){ --mBindingCalls; } void ChatRoomModel::resetData(){ if( mBindingCalls == 0) ProxyListModel::resetData(); } void ChatRoomModel::initEntries(){ if( mList.size() > mLastEntriesStep) resetData(); if(mList.size() == 0) { qDebug() << "Internal Entries : Init"; // On call : reinitialize all entries. This allow to free up memory QList > entries; QList prepareEntries; // Get chat messages for (auto &message : mChatRoom->getHistory(mFirstLastEntriesStep)) { prepareEntries << EntrySorterHelper(message->getTime() ,MessageEntry, message); } // Get events for(auto &eventLog : mChatRoom->getHistoryEvents(mFirstLastEntriesStep)) prepareEntries << EntrySorterHelper(eventLog->getCreationTime() , NoticeEntry, eventLog); // Get calls. bool secureChatEnabled = CoreManager::getInstance()->getSettingsModel()->getSecureChatEnabled(); bool standardChatEnabled = CoreManager::getInstance()->getSettingsModel()->getStandardChatEnabled(); if( isOneToOne() && (secureChatEnabled && !standardChatEnabled && isSecure() || standardChatEnabled && !isSecure()) ) { auto callHistory = CallsListModel::getCallHistory(getParticipantAddress(), Utils::coreStringToAppString(mChatRoom->getLocalAddress()->asStringUriOnly())); // callhistory is sorted from newest to oldest int count = 0; for (auto callLog = callHistory.begin() ; count < mFirstLastEntriesStep && callLog != callHistory.end() ; ++callLog, ++count ){ if(!(*callLog)->wasConference()) prepareEntries << EntrySorterHelper((*callLog)->getStartDate(), CallEntry, *callLog); } } EntrySorterHelper::getLimitedSelection(&entries, prepareEntries, mFirstLastEntriesStep, this); qDebug() << "Internal Entries : Built"; if(entries.size() >0){ beginInsertRows(QModelIndex(),0, entries.size()-1); for(auto e : entries) mList.push_back(e); endInsertRows(); updateNewMessageNotice(mChatRoom->getUnreadMessagesCount()); } qDebug() << "Internal Entries : End"; } mIsInitialized = true; } void ChatRoomModel::setEntriesLoading(const bool& loading){ if( mEntriesLoading != loading){ mEntriesLoading = loading; emit entriesLoadingChanged(mEntriesLoading); qApp->processEvents(); } } int ChatRoomModel::loadMoreEntries(){ setEntriesLoading(true); int currentRowCount = rowCount(); int newEntries = 0; do{ QList > entries; QList prepareEntries; // Get current event count for each type QVector entriesCounts; entriesCounts.resize(3); for(auto itEntries = mList.begin() ; itEntries != mList.end() ; ++itEntries){ auto chatEvent = itEntries->objectCast(); if( chatEvent->mType == MessageEntry) ++entriesCounts[0]; else if( chatEvent->mType == CallEntry){ if(chatEvent.objectCast()->mIsStart) ++entriesCounts[1]; } else ++entriesCounts[2]; } // Messages for (auto &message : mChatRoom->getHistoryRange(entriesCounts[0], entriesCounts[0]+mLastEntriesStep)){ auto itEntries = mList.begin(); bool haveEntry = false; while(!haveEntry && itEntries != mList.end()){ auto entry = itEntries->objectCast(); haveEntry = (entry && entry->getChatMessage() == message); ++itEntries; } if(!haveEntry) prepareEntries << EntrySorterHelper(message->getTime() ,MessageEntry, message); } // Calls bool secureChatEnabled = CoreManager::getInstance()->getSettingsModel()->getSecureChatEnabled(); bool standardChatEnabled = CoreManager::getInstance()->getSettingsModel()->getStandardChatEnabled(); if( isOneToOne() && (secureChatEnabled && !standardChatEnabled && isSecure() || standardChatEnabled && !isSecure()) ) { auto callHistory = CallsListModel::getCallHistory(getParticipantAddress(), Utils::coreStringToAppString(mChatRoom->getLocalAddress()->asStringUriOnly())); int count = 0; auto itCallHistory = callHistory.begin(); while(count < entriesCounts[1] && itCallHistory != callHistory.end()){ ++itCallHistory; ++count; } count = 0; while( count < mLastEntriesStep && itCallHistory != callHistory.end()){ prepareEntries << EntrySorterHelper((*itCallHistory)->getStartDate(), CallEntry, *itCallHistory); ++itCallHistory; } } // Notices for (auto &eventLog : mChatRoom->getHistoryRangeEvents(entriesCounts[2], entriesCounts[2]+mLastEntriesStep)){ auto itEntries = mList.begin(); bool haveEntry = false; while(!haveEntry && itEntries != mList.end()){ auto entry = itEntries->objectCast(); haveEntry = (entry && entry->getEventLog() && entry->getEventLog() == eventLog); ++itEntries; } if(!haveEntry) prepareEntries << EntrySorterHelper(eventLog->getCreationTime() , NoticeEntry, eventLog); } EntrySorterHelper::getLimitedSelection(&entries, prepareEntries, mLastEntriesStep, this); if(entries.size() >0){ if(mPostModelChangedEvents) beginInsertRows(QModelIndex(), 0, entries.size()-1); for(auto entry : entries) mList.prepend(entry); if(mPostModelChangedEvents) endInsertRows(); //emit layoutChanged(); updateLastUpdateTime(); } newEntries = entries.size(); }while( newEntries>0 && currentRowCount == rowCount()); currentRowCount = rowCount() - currentRowCount; setEntriesLoading(false); if(mPostModelChangedEvents) emit moreEntriesLoaded(currentRowCount); return currentRowCount; } //------------------------------------------------- //------------------------------------------------- void ChatRoomModel::onCallEnded(std::shared_ptr call){ if( call->getCallLog()->getStatus() == linphone::Call::Status::Missed) addMissedCallsCount(call); else{ insertCall(call->getCallLog()); } // When a call is end, a new log WILL be written in database. It may have information on display name. QTimer::singleShot(100, this, &ChatRoomModel::fullPeerAddressChanged); } // ----------------------------------------------------------------------------- void ChatRoomModel::insertCall (const std::shared_ptr &callLog) { if(mIsInitialized){ QSharedPointer model = ChatCallModel::create(callLog, true); if(model){ int row = mList.count(); beginInsertRows(QModelIndex(), row, row); mList << model; endInsertRows(); if (callLog->getStatus() == linphone::Call::Status::Success) { model = ChatCallModel::create(callLog, false); if(model) add(model); } updateLastUpdateTime(); } } } void ChatRoomModel::insertCalls (const QList > &calls) { if(mIsInitialized){ QList > entries; for(auto callLog : calls) { QSharedPointer model = ChatCallModel::create(callLog, true); if(model){ entries << model; if (callLog->getStatus() == linphone::Call::Status::Success) { model = ChatCallModel::create(callLog, false); if(model){ entries << model; } } } } if(entries.size() > 0){ prepend(entries); emit layoutChanged(); } } } QSharedPointer ChatRoomModel::insertMessageAtEnd (const std::shared_ptr &message) { QSharedPointer model; if(mIsInitialized && !exists(message)){ model = ChatMessageModel::create(message); if(model){ connect(model.get(), &ChatMessageModel::remove, this, &ChatRoomModel::removeEntry); setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); add(model); } } return model; } void ChatRoomModel::insertMessages (const QList > &messages) { if(mIsInitialized){ QList > entries; for(auto message : messages) { QSharedPointer model = ChatMessageModel::create(message); if(model){ connect(model.get(), &ChatMessageModel::remove, this, &ChatRoomModel::removeEntry); entries << model; } } if(entries.size() > 0){ prepend(entries); setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); emit layoutChanged(); } } } void ChatRoomModel::insertNotice (const std::shared_ptr &eventLog) { if(mIsInitialized){ QSharedPointer model = ChatNoticeModel::create(eventLog); if(model) add(model); } } void ChatRoomModel::insertNotices (const QList> &eventLogs) { if(mIsInitialized){ QList > entries; for(auto eventLog : eventLogs) { QSharedPointer model = ChatNoticeModel::create(eventLog); if(model) { entries << model; } } if(entries.size() > 0){ prepend(entries); emit layoutChanged(); } } } // ----------------------------------------------------------------------------- /* void ChatRoomModel::removeUnreadMessagesNotice() { }*/ // ----------------------------------------------------------------------------- void ChatRoomModel::handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state) { /* if (state == linphone::Call::State::End || state == linphone::Call::State::Error){ shared_ptr core = CoreManager::getInstance()->getCore(); std::shared_ptr params = core->createDefaultChatRoomParams(); std::list> participants; auto chatRoom = core->searchChatRoom(params, mChatRoom->getLocalAddress() , call->getRemoteAddress() , participants); if( mChatRoom == chatRoom){ insertCall(call->getCallLog()); setMissedCallsCount(mMissedCallsCount+1); } } */ } void ChatRoomModel::handleCallCreated(const shared_ptr &call){ } void ChatRoomModel::handlePresenceStatusReceived(std::shared_ptr contact){ if(!mDeleteChatRoom && contact){ bool canUpdatePresence = false; auto contactAddresses = contact->getAddresses(); for( auto itContactAddress = contactAddresses.begin() ; !canUpdatePresence && itContactAddress != contactAddresses.end() ; ++itContactAddress){ //auto cleanContactAddress = (*itContactAddress)->clone(); //cleanContactAddress->clean(); canUpdatePresence = mChatRoom->getLocalAddress()->weakEqual(*itContactAddress); if(!canUpdatePresence && !isGroupEnabled() && mChatRoom->getNbParticipants() == 1){ auto participants = getParticipants(false); auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(Utils::coreStringToAppString((*participants.begin())->getAddress()->asString())); if(contact){ auto friendsAddresses = contact->getVcardModel()->getSipAddresses(); for(auto friendAddress = friendsAddresses.begin() ; !canUpdatePresence && friendAddress != friendsAddresses.end() ; ++friendAddress){ shared_ptr lAddress = CoreManager::getInstance()->getCore()->interpretUrl( Utils::appStringToCoreString(friendAddress->toString()) ); canUpdatePresence = lAddress->weakEqual(*itContactAddress); } } } } if(canUpdatePresence) { //emit presenceStatusChanged((int)contact->getPresenceModel()->getConsolidatedPresence()); emit presenceStatusChanged(); } } } //---------------------------------------------------------- //------ CHAT ROOM HANDLERS //---------------------------------------------------------- void ChatRoomModel::onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing){ auto it = mComposers.begin(); while(it != mComposers.end() && !it.key()->weakEqual(remoteAddress)) ++it; if(it != mComposers.end()) mComposers.erase(it); if(isComposing) mComposers[remoteAddress] = Utils::getDisplayName(remoteAddress); emit isRemoteComposingChanged(); } void ChatRoomModel::onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){ setUnreadMessagesCount(chatRoom->getUnreadMessagesCount()); updateLastUpdateTime(); } void ChatRoomModel::onMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & messages){ setUnreadMessagesCount(chatRoom->getUnreadMessagesCount()); updateLastUpdateTime(); } void ChatRoomModel::onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ if( eventLog->getType() == linphone::EventLog::Type::ConferenceCallEnded ){ setMissedCallsCount(mMissedCallsCount+1); }else if( eventLog->getType() == linphone::EventLog::Type::ConferenceCreated ){ emit fullPeerAddressChanged(); } updateLastUpdateTime(); } void ChatRoomModel::onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { auto message = eventLog->getChatMessage(); if(message){ insertMessageAtEnd(message); updateLastUpdateTime(); emit messageReceived(message); } } void ChatRoomModel::onChatMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & eventLogs){ for(auto eventLog : eventLogs){ auto message = eventLog->getChatMessage(); if(message){ insertMessageAtEnd(message); updateLastUpdateTime(); emit messageReceived(message); } } } void ChatRoomModel::onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ auto message = eventLog->getChatMessage(); if(message){ insertMessageAtEnd(message); updateLastUpdateTime(); emit messageReceived(message); } } void ChatRoomModel::onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ updateLastUpdateTime(); } // Called when the core have the participant (= exists) void ChatRoomModel::onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ auto events = chatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if( e != events.end() ) insertNotice(*e); updateLastUpdateTime(); emit participantAdded(eventLog); emit fullPeerAddressChanged(); } void ChatRoomModel::onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ auto events = chatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if( e != events.end() ) insertNotice(*e); updateLastUpdateTime(); emit participantRemoved(eventLog); emit fullPeerAddressChanged(); } void ChatRoomModel::onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ auto events = chatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if( e != events.end() ) insertNotice(*e); updateLastUpdateTime(); emit participantAdminStatusChanged(eventLog); emit isMeAdminChanged(); // It is not the case all the time but calling getters is not a heavy request } void ChatRoomModel::onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState){ updateLastUpdateTime(); emit stateChanged(getState()); if(newState == linphone::ChatRoom::State::Deleted){ mChatRoom->removeListener(mChatRoomListener); mChatRoom = nullptr; emit chatRoomDeleted(); } } void ChatRoomModel::onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ auto events = chatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if( e != events.end() ) insertNotice(*e); updateLastUpdateTime(); emit securityLevelChanged((int)chatRoom->getSecurityLevel()); } void ChatRoomModel::onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { auto events = chatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if( e != events.end() ) insertNotice(*e); updateLastUpdateTime(); emit subjectChanged(getSubject()); emit usernameChanged(); } void ChatRoomModel::onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){ updateLastUpdateTime(); } void ChatRoomModel::onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ updateLastUpdateTime(); emit participantDeviceAdded(eventLog); } void ChatRoomModel::onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ updateLastUpdateTime(); emit participantDeviceRemoved(eventLog); } void ChatRoomModel::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ auto events = chatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if(e != events.end() ) insertNotice(*e); else{ events = mChatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if(e != events.end() ) insertNotice(*e); } setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); // Update message count. In the case of joining conference, the conference id was not valid thus, the missing count was not about the chat room but a global one. updateLastUpdateTime(); emit usernameChanged(); emit conferenceJoined(eventLog); emit isReadOnlyChanged(); } void ChatRoomModel::onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ if( chatRoom->getState() != linphone::ChatRoom::State::Deleted) { auto events = chatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if( e != events.end()) insertNotice(*e); else{ events = mChatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if(e != events.end() ) insertNotice(*e); } updateLastUpdateTime(); emit conferenceLeft(eventLog); emit isReadOnlyChanged(); } } void ChatRoomModel::onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ auto events = chatRoom->getHistoryEvents(0); auto e = std::find(events.begin(), events.end(), eventLog); if(e != events.end() ) insertNotice(*e); updateLastUpdateTime(); } void ChatRoomModel::onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ updateLastUpdateTime(); } void ChatRoomModel::onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ updateLastUpdateTime(); } void ChatRoomModel::onConferenceAddressGeneration(const std::shared_ptr & chatRoom){ updateLastUpdateTime(); } void ChatRoomModel::onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){ updateLastUpdateTime(); emit participantRegistrationSubscriptionRequested(participantAddress); } void ChatRoomModel::onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){ emit participantRegistrationUnsubscriptionRequested(participantAddress); } void ChatRoomModel::onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message){ } void ChatRoomModel::onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state){ } linphone-desktop-5.0.2/linphone-app/src/components/chat-room/ChatRoomModel.hpp000066400000000000000000000403101434616504300274520ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CHAT_ROOM_MODEL_H_ #define CHAT_ROOM_MODEL_H_ #include #include "app/proxyModel/ProxyListModel.hpp" #include #include "utils/LinphoneEnums.hpp" // ============================================================================= // Fetch all N messages of a ChatRoom. // ============================================================================= class CoreHandlers; class ParticipantModel; class ParticipantListModel; class ChatEvent; class ContactModel; class ChatRoomModel; class ChatMessageModel; class ChatNoticeModel; class ChatRoomListener; class ChatRoomModel : public ProxyListModel { Q_OBJECT public: enum Roles { ChatEntry = Qt::DisplayRole, SectionDate }; enum EntryType { GenericEntry, MessageEntry, CallEntry, NoticeEntry }; Q_ENUM(EntryType) Q_PROPERTY(QString subject READ getSubject WRITE setSubject NOTIFY subjectChanged) Q_PROPERTY(QDateTime lastUpdateTime MEMBER mLastUpdateTime WRITE setLastUpdateTime NOTIFY lastUpdateTimeChanged) Q_PROPERTY(int unreadMessagesCount MEMBER mUnreadMessagesCount WRITE setUnreadMessagesCount NOTIFY unreadMessagesCountChanged) Q_PROPERTY(int missedCallsCount MEMBER mMissedCallsCount WRITE setMissedCallsCount NOTIFY missedCallsCountChanged) Q_PROPERTY(int securityLevel READ getSecurityLevel NOTIFY securityLevelChanged) Q_PROPERTY(bool groupEnabled READ isGroupEnabled NOTIFY groupEnabledChanged) Q_PROPERTY(bool isConference READ isConference CONSTANT) Q_PROPERTY(bool isOneToOne READ isOneToOne CONSTANT) Q_PROPERTY(bool haveEncryption READ haveEncryption CONSTANT) Q_PROPERTY(bool isMeAdmin READ isMeAdmin NOTIFY isMeAdminChanged) Q_PROPERTY(bool canHandleParticipants READ canHandleParticipants CONSTANT) Q_PROPERTY(bool isComposing READ getIsRemoteComposing NOTIFY isRemoteComposingChanged) Q_PROPERTY(QList composers READ getComposers NOTIFY isRemoteComposingChanged) Q_PROPERTY(bool isReadOnly READ isReadOnly NOTIFY isReadOnlyChanged) Q_PROPERTY(bool updating READ isUpdating NOTIFY updatingChanged) Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) Q_PROPERTY(QString sipAddressUriOnly READ getPeerAddress NOTIFY fullPeerAddressChanged) Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged) Q_PROPERTY(int presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged) Q_PROPERTY(LinphoneEnums::ChatRoomState state READ getState NOTIFY stateChanged) Q_PROPERTY(long ephemeralLifetime READ getEphemeralLifetime WRITE setEphemeralLifetime NOTIFY ephemeralLifetimeChanged) Q_PROPERTY(bool ephemeralEnabled READ isEphemeralEnabled WRITE setEphemeralEnabled NOTIFY ephemeralEnabledChanged) Q_PROPERTY(bool canBeEphemeral READ canBeEphemeral NOTIFY canBeEphemeralChanged) Q_PROPERTY(bool markAsReadEnabled READ markAsReadEnabled WRITE enableMarkAsRead NOTIFY markAsReadEnabledChanged) Q_PROPERTY(ParticipantListModel* participants READ getParticipantListModel CONSTANT) Q_PROPERTY(ChatMessageModel * reply READ getReply WRITE setReply NOTIFY replyChanged) Q_PROPERTY(bool entriesLoading READ isEntriesLoading WRITE setEntriesLoading NOTIFY entriesLoadingChanged) static QSharedPointer create(const std::shared_ptr& chatRoom, const std::list>& callLogs = std::list>()); ChatRoomModel (const std::shared_ptr& chatRoom, const std::list>& callLogs = std::list>(), QObject * parent = nullptr); ~ChatRoomModel (); QHash roleNames () const override; QVariant data (const QModelIndex &index, int role) const override; bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; void removeAllEntries (); //---- Getters Q_INVOKABLE QString getPeerAddress () const; Q_INVOKABLE QString getLocalAddress () const; Q_INVOKABLE QString getFullPeerAddress () const; Q_INVOKABLE QString getFullLocalAddress () const; Q_INVOKABLE QString getConferenceAddress () const; QString getSubject () const; QString getUsername () const; QString getAvatar () const; int getPresenceStatus() const; LinphoneEnums::ChatRoomState getState() const; bool isReadOnly() const; bool isEphemeralEnabled() const; long getEphemeralLifetime() const; bool canBeEphemeral(); bool haveEncryption() const; bool haveConferenceAddress() const; bool markAsReadEnabled() const; Q_INVOKABLE bool isSecure() const; int getSecurityLevel() const; bool isGroupEnabled() const; bool isConference() const; bool isOneToOne() const; bool isMeAdmin() const; bool isCurrentAccount() const; // Return true if this chat room is Me() is the current account bool canHandleParticipants() const; bool getIsRemoteComposing () const; bool isEntriesLoading() const; bool isBasic() const; bool isUpdating() const; ParticipantListModel* getParticipantListModel() const; std::list> getParticipants(const bool& withMe = true) const; std::shared_ptr getChatRoom(); QList getComposers(); QString getParticipantAddress(); // return peerAddress if not secure else return the first participant SIP address. int getAllUnreadCount(); // Return unread messages and missed call. //---- Setters void setSubject(QString& subject); void setLastUpdateTime(const QDateTime& lastUpdateDate); void updateLastUpdateTime(); void setEntriesLoading(const bool& loading); void setUnreadMessagesCount(const int& count); void setMissedCallsCount(const int& count); void addMissedCallsCount(std::shared_ptr call); void setEphemeralEnabled(bool enabled); void setEphemeralLifetime(long lifetime); void enableMarkAsRead(const bool& enable); void setReply(ChatMessageModel * model); ChatMessageModel * getReply()const; void clearReply(); void clearFilesToSend(); // Tools void markAsToDelete(); Q_INVOKABLE void deleteChatRoom(); Q_INVOKABLE void leaveChatRoom (); Q_INVOKABLE void updateParticipants(const QVariantList& participants); void sendMessage (const QString &message); Q_INVOKABLE void forwardMessage(ChatMessageModel * model); void compose (); Q_INVOKABLE void resetMessageCount (); void initEntries(); Q_INVOKABLE int loadMoreEntries(); // return new entries count void onCallEnded(std::shared_ptr call); void updateNewMessageNotice(const int& count); Q_INVOKABLE int loadTillMessage(ChatMessageModel * message);// Load all entries till message and return its index. -1 if not found. static bool isTerminated(const std::shared_ptr& chatRoom); bool exists(const std::shared_ptr message) const; void addBindingCall(); // If a call is binding to this chat room, we avoid cleaning data (Add=+1, remove=-1) void removeBindingCall(); virtual void resetData() override; QDateTime mLastUpdateTime; int mUnreadMessagesCount = 0; int mMissedCallsCount = 0; bool mIsInitialized = false; bool mDeleteChatRoom = false; // Use as workaround because of core->deleteChatRoom() that call destructor without takking account of count ref : call it in ChatRoomModel destructor int mLastEntriesStep = 50; // Retrieve a part of the history to avoid too much processing int mFirstLastEntriesStep = 10; // Retrieve a part of the history to avoid too much processing at the init bool mMarkAsReadEnabled = true; bool mEntriesLoading = false; void insertCall (const std::shared_ptr &callLog); void insertCalls (const QList > &calls); QSharedPointer insertMessageAtEnd (const std::shared_ptr &message); void insertMessages (const QList > &messages); void insertNotice (const std::shared_ptr &enventLog); void insertNotices (const QList> &eventLogs); //-------------------- CHAT ROOM HANDLER public slots: virtual void onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing); virtual void onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message); virtual void onMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & messages); virtual void onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onChatMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & eventLogs); virtual void onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState); virtual void onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message); virtual void onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onConferenceAddressGeneration(const std::shared_ptr & chatRoom); virtual void onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); virtual void onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); virtual void onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message); virtual void onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state) ; void removeEntry(ChatEvent* entry); void emitFullPeerAddressChanged(); // Use to call signal when changing data that are not managed by the chat room (like data coming from call) signals: bool isRemoteComposingChanged (); void entriesLoadingChanged(const bool& loading); void moreEntriesLoaded(const int& count); void allEntriesRemoved (QSharedPointer model); void lastEntryRemoved (); void messageSent (const std::shared_ptr &message); void messageReceived (const std::shared_ptr &message); void messageCountReset (); void focused (); void fullPeerAddressChanged(); void participantsChanged(); void subjectChanged(QString subject); void usernameChanged(); void avatarChanged(); void presenceStatusChanged(); void lastUpdateTimeChanged(); void unreadMessagesCountChanged(); void missedCallsCountChanged(); void securityLevelChanged(int securityLevel); void groupEnabledChanged(bool groupEnabled); void isMeAdminChanged(); void stateChanged(int state); void isReadOnlyChanged(); void ephemeralEnabledChanged(); void ephemeralLifetimeChanged(); void canBeEphemeralChanged(); void markAsReadEnabledChanged(); void chatRoomDeleted();// Must be connected with DirectConnection mode void replyChanged(); void updatingChanged(); // Chat Room listener callbacks void securityEvent(const std::shared_ptr & eventLog); void participantAdded(const std::shared_ptr & eventLog); void participantRemoved(const std::shared_ptr & eventLog); void participantDeviceAdded(const std::shared_ptr & eventLog); void participantDeviceRemoved(const std::shared_ptr & eventLog); void participantAdminStatusChanged(const std::shared_ptr & eventLog); void participantRegistrationSubscriptionRequested(const std::shared_ptr & participantAddress); void participantRegistrationUnsubscriptionRequested(const std::shared_ptr & participantAddress); void conferenceJoined(const std::shared_ptr & eventLog); void conferenceLeft(const std::shared_ptr & eventLog); private: void connectTo(ChatRoomListener * listener); void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); void handleCallCreated(const std::shared_ptr &call);// Count an event call void handlePresenceStatusReceived(std::shared_ptr contact); std::shared_ptr mChatRoom; std::shared_ptr mChatRoomListener; // This need to be a shared_ptr because of adding it to linphone std::shared_ptr mCoreHandlers; // This need to be a shared_ptr because of adding it to linphone QMap, QString> mComposers; // Store all addresses that are composing with its username QSharedPointer mParticipantListModel; QSharedPointer mReplyModel; QSharedPointer mUnreadMessageNotice; int mBindingCalls = 0; bool mPostModelChangedEvents = true; QWeakPointer mSelf; }; Q_DECLARE_METATYPE(QSharedPointer) #endif // CHAT_ROOM_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp000066400000000000000000000311711434616504300305140ustar00rootroot00000000000000/* * Copyright (c) 2010-2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "app/App.hpp" #include "components/core/CoreManager.hpp" #include "ChatRoomProxyModel.hpp" #include "components/chat-events/ChatEvent.hpp" #include "components/chat-events/ChatMessageModel.hpp" #include "components/chat-events/ChatNoticeModel.hpp" #include "components/chat-events/ChatCallModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "components/timeline/TimelineModel.hpp" // ============================================================================= using namespace std; QString ChatRoomProxyModel::gCachedText; // ============================================================================= ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { mMarkAsReadEnabled = true; App *app = App::getInstance(); QObject::connect(app->getMainWindow(), &QWindow::activeChanged, this, [this]() { handleIsActiveChanged(App::getInstance()->getMainWindow()); }); QQuickWindow *callsWindow = app->getCallsWindow(); if (callsWindow) QObject::connect(callsWindow, &QWindow::activeChanged, this, [this, callsWindow]() { handleIsActiveChanged(callsWindow); }); sort(0); } ChatRoomProxyModel::~ChatRoomProxyModel(){ setSourceModel(nullptr); setChatRoomModel(nullptr); // Do remove process like setting haveCall if is Call. } // ----------------------------------------------------------------------------- #define GET_CHAT_MODEL() \ if (!mChatRoomModel) \ return; \ mChatRoomModel #define CREATE_PARENT_MODEL_FUNCTION(METHOD) \ void ChatRoomProxyModel::METHOD () { \ GET_CHAT_MODEL()->METHOD(); \ } #define CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(METHOD, ARG_TYPE) \ void ChatRoomProxyModel::METHOD (ARG_TYPE value) { \ GET_CHAT_MODEL()->METHOD(value); \ } #define CREATE_PARENT_MODEL_FUNCTION_WITH_ID(METHOD) \ void ChatRoomProxyModel::METHOD (int id) { \ GET_CHAT_MODEL()->METHOD( \ mapFromSource(static_cast(sourceModel())->index(id, 0)).row() \ ); \ } CREATE_PARENT_MODEL_FUNCTION(removeAllEntries) CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(sendMessage, const QString &) CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(forwardMessage, ChatMessageModel *) CREATE_PARENT_MODEL_FUNCTION_WITH_ID(removeRow) CREATE_PARENT_MODEL_FUNCTION(deleteChatRoom) #undef GET_CHAT_MODEL #undef CREATE_PARENT_MODEL_FUNCTION #undef CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM #undef CREATE_PARENT_MODEL_FUNCTION_WITH_ID void ChatRoomProxyModel::compose (const QString& text) { if (mChatRoomModel) mChatRoomModel->compose(); gCachedText = text; } int ChatRoomProxyModel::getEntryTypeFilter () { return mEntryTypeFilter; } // ----------------------------------------------------------------------------- void ChatRoomProxyModel::loadMoreEntriesAsync(){ QTimer::singleShot(10, this, &ChatRoomProxyModel::loadMoreEntries); } void ChatRoomProxyModel::onMoreEntriesLoaded(const int& count){ emit moreEntriesLoaded(count); } void ChatRoomProxyModel::loadMoreEntries() { if(mChatRoomModel ) { mChatRoomModel->loadMoreEntries(); } } void ChatRoomProxyModel::setEntryTypeFilter (int type) { if (getEntryTypeFilter() != type) { mEntryTypeFilter = type; invalidate(); emit entryTypeFilterChanged(type); } } // ----------------------------------------------------------------------------- bool ChatRoomProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { bool show = false; if (mEntryTypeFilter == ChatRoomModel::EntryType::GenericEntry) show = true; else{ QModelIndex index = sourceModel()->index(sourceRow, 0, QModelIndex()); auto eventModel = sourceModel()->data(index); if( mEntryTypeFilter == ChatRoomModel::EntryType::CallEntry && eventModel.value() != nullptr) show = true; else if( mEntryTypeFilter == ChatRoomModel::EntryType::MessageEntry && eventModel.value() != nullptr) show = true; else if( mEntryTypeFilter == ChatRoomModel::EntryType::NoticeEntry && eventModel.value() != nullptr) show = true; } if( show && mFilterText != ""){ QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); auto eventModel = sourceModel()->data(index); ChatMessageModel * chatModel = eventModel.value(); if( chatModel){ QRegularExpression search(QRegularExpression::escape(mFilterText), QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); show = chatModel->mContent.contains(search); } } return show; } bool ChatRoomProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { auto l = sourceModel()->data(left); auto r = sourceModel()->data(right); ChatEvent * a = l.value();// l.value() cannot be used if(!a) a = l.value(); if(!a) a = l.value(); ChatEvent * b = r.value(); if(!b) b = r.value(); if(!b) b = r.value(); if(!b) return true; if(!a) return false; return a->getTimestamp() < b->getTimestamp(); } // ----------------------------------------------------------------------------- QString ChatRoomProxyModel::getPeerAddress () const { return mChatRoomModel ? mChatRoomModel->getPeerAddress() : mPeerAddress; } void ChatRoomProxyModel::setPeerAddress (const QString &peerAddress) { mPeerAddress = peerAddress; emit peerAddressChanged(mPeerAddress); } QString ChatRoomProxyModel::getLocalAddress () const { return mChatRoomModel ? mChatRoomModel->getLocalAddress() : mLocalAddress; } void ChatRoomProxyModel::setLocalAddress (const QString &localAddress) { mLocalAddress = localAddress; emit localAddressChanged(mLocalAddress); } QString ChatRoomProxyModel::getFullPeerAddress () const { return mChatRoomModel ? mChatRoomModel->getFullPeerAddress() : mFullPeerAddress; } void ChatRoomProxyModel::setFullPeerAddress (const QString &peerAddress) { mFullPeerAddress = peerAddress; emit fullPeerAddressChanged(mFullPeerAddress); } QString ChatRoomProxyModel::getFullLocalAddress () const { return mChatRoomModel ? mChatRoomModel->getFullLocalAddress() : mFullLocalAddress; } void ChatRoomProxyModel::setFullLocalAddress (const QString &localAddress) { mFullLocalAddress = localAddress; emit fullLocalAddressChanged(mFullLocalAddress); } bool ChatRoomProxyModel::markAsReadEnabled() const{ return (mChatRoomModel ? mChatRoomModel->markAsReadEnabled() : false); } void ChatRoomProxyModel::enableMarkAsRead(const bool& enable){ if(mChatRoomModel) mChatRoomModel->enableMarkAsRead(enable); } QList ChatRoomProxyModel::getComposers() const{ return (mChatRoomModel?mChatRoomModel->getComposers():QList()); } QString ChatRoomProxyModel::getDisplayNameComposers()const{ return getComposers().join(", "); } QVariant ChatRoomProxyModel::getAt(int row){ QModelIndex sourceIndex = mapToSource(this->index(row, 0)); return sourceModel()->data(sourceIndex); } QString ChatRoomProxyModel::getCachedText() const{ return gCachedText; } void ChatRoomProxyModel::setIsCall(const bool& isCall){ if(mIsCall != isCall) { if(mChatRoomModel){ if(isCall){ mChatRoomModel->addBindingCall(); }else mChatRoomModel->removeBindingCall(); } mIsCall = isCall; emit isCallChanged(); } } // ----------------------------------------------------------------------------- void ChatRoomProxyModel::reload (ChatRoomModel *chatRoomModel) { if(chatRoomModel != mChatRoomModel.get()) { if (mChatRoomModel) { ChatRoomModel *ChatRoomModel = mChatRoomModel.get(); QObject::disconnect(ChatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); QObject::disconnect(ChatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); QObject::disconnect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); QObject::disconnect(ChatRoomModel, &ChatRoomModel::markAsReadEnabledChanged, this, &ChatRoomProxyModel::markAsReadEnabledChanged); QObject::disconnect(ChatRoomModel, &ChatRoomModel::moreEntriesLoaded, this, &ChatRoomProxyModel::onMoreEntriesLoaded); QObject::disconnect(ChatRoomModel, &ChatRoomModel::chatRoomDeleted, this, &ChatRoomProxyModel::chatRoomDeleted); if(mIsCall) mChatRoomModel->removeBindingCall(); } if( mIsCall && chatRoomModel){ chatRoomModel->addBindingCall(); } mChatRoomModel = CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(chatRoomModel); setSourceModel(mChatRoomModel.get()); if (mChatRoomModel) { ChatRoomModel *ChatRoomModel = mChatRoomModel.get(); QObject::connect(ChatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); QObject::connect(ChatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); QObject::connect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); QObject::connect(ChatRoomModel, &ChatRoomModel::markAsReadEnabledChanged, this, &ChatRoomProxyModel::markAsReadEnabledChanged); QObject::connect(ChatRoomModel, &ChatRoomModel::moreEntriesLoaded, this, &ChatRoomProxyModel::onMoreEntriesLoaded); QObject::connect(ChatRoomModel, &ChatRoomModel::chatRoomDeleted, this, &ChatRoomProxyModel::chatRoomDeleted); mChatRoomModel->initEntries();// This way, we don't load huge chat rooms (that lead to freeze GUI) } } } void ChatRoomProxyModel::resetMessageCount(){ if( mChatRoomModel){ mChatRoomModel->resetMessageCount(); } } void ChatRoomProxyModel::setFilterText(const QString& text){ if( mFilterText != text && mChatRoomModel){ mFilterText = text; int currentRowCount = rowCount(); int newEntries = 0; do{ newEntries = mChatRoomModel->loadMoreEntries(); invalidate(); emit filterTextChanged(); }while( newEntries>0 && currentRowCount == rowCount()); } } int ChatRoomProxyModel::loadTillMessage(ChatMessageModel * message){ int messageIndex = mChatRoomModel->loadTillMessage(message); if( messageIndex>= 0 ) { messageIndex = mapFromSource(static_cast(sourceModel())->index(messageIndex, 0)).row(); } qDebug() << "Message index from chat room proxy : " << messageIndex; return messageIndex; } ChatRoomModel *ChatRoomProxyModel::getChatRoomModel () const{ return mChatRoomModel.get(); } void ChatRoomProxyModel::setChatRoomModel (ChatRoomModel *chatRoomModel){ if(chatRoomModel){ reload(chatRoomModel); emit chatRoomModelChanged(); emit isRemoteComposingChanged(); }else{ if(mIsCall && mChatRoomModel) mChatRoomModel->removeBindingCall(); mChatRoomModel = nullptr; } } // ----------------------------------------------------------------------------- static inline QWindow *getParentWindow (QObject *object) { App *app = App::getInstance(); const QWindow *mainWindow = app->getMainWindow(); const QWindow *callsWindow = app->getCallsWindow(); for (QObject *parent = object->parent(); parent; parent = parent->parent()) if (parent == mainWindow || parent == callsWindow) return static_cast(parent); return nullptr; } void ChatRoomProxyModel::handleIsActiveChanged (QWindow *window) { if (markAsReadEnabled() && mChatRoomModel && window->isActive() && getParentWindow(this) == window) { auto timeline = CoreManager::getInstance()->getTimelineListModel()->getTimeline(mChatRoomModel->getChatRoom(), false); if(timeline && timeline->mSelected){ mChatRoomModel->resetMessageCount(); mChatRoomModel->focused(); } } } void ChatRoomProxyModel::handleIsRemoteComposingChanged () { emit isRemoteComposingChanged(); } void ChatRoomProxyModel::handleMessageReceived (const shared_ptr &message) { QWindow *window = getParentWindow(this); if (mChatRoomModel){ if(window && window->isActive()) mChatRoomModel->resetMessageCount(); } } void ChatRoomProxyModel::handleMessageSent (const shared_ptr &) { } linphone-desktop-5.0.2/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp000066400000000000000000000122271434616504300305220ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CHAT_ROOM_PROXY_MODEL_H_ #define CHAT_ROOM_PROXY_MODEL_H_ #include #include "ChatRoomModel.hpp" // ============================================================================= class QWindow; class ChatRoomProxyModel : public QSortFilterProxyModel { class ChatRoomModelFilter; Q_OBJECT Q_PROPERTY(QString peerAddress READ getPeerAddress WRITE setPeerAddress NOTIFY peerAddressChanged) Q_PROPERTY(QString localAddress READ getLocalAddress WRITE setLocalAddress NOTIFY localAddressChanged) Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress WRITE setFullPeerAddress NOTIFY fullPeerAddressChanged) Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress WRITE setFullLocalAddress NOTIFY fullLocalAddressChanged) Q_PROPERTY(ChatRoomModel *chatRoomModel READ getChatRoomModel WRITE setChatRoomModel NOTIFY chatRoomModelChanged) Q_PROPERTY(QList composers READ getComposers NOTIFY isRemoteComposingChanged) Q_PROPERTY(QString cachedText READ getCachedText) Q_PROPERTY(QString filterText MEMBER mFilterText WRITE setFilterText NOTIFY filterTextChanged) Q_PROPERTY(bool markAsReadEnabled READ markAsReadEnabled WRITE enableMarkAsRead NOTIFY markAsReadEnabledChanged)// Focus is at end of the list. Used to reset message count if not at end Q_PROPERTY(bool isCall MEMBER mIsCall WRITE setIsCall NOTIFY isCallChanged) public: ChatRoomProxyModel (QObject *parent = Q_NULLPTR); ~ChatRoomProxyModel(); int getEntryTypeFilter (); Q_INVOKABLE void setEntryTypeFilter (int type); Q_INVOKABLE void setFilterText(const QString& text); Q_INVOKABLE QString getDisplayNameComposers()const; Q_INVOKABLE QVariant getAt(int row); void setIsCall(const bool& isCall); Q_INVOKABLE void loadMoreEntriesAsync (); Q_INVOKABLE void loadMoreEntries (); Q_INVOKABLE void removeAllEntries (); Q_INVOKABLE void removeRow (int index); Q_INVOKABLE void deleteChatRoom(); Q_INVOKABLE void sendMessage (const QString &message); Q_INVOKABLE void forwardMessage(ChatMessageModel * model); Q_INVOKABLE void compose (const QString& text); Q_INVOKABLE void resetMessageCount(); Q_INVOKABLE int loadTillMessage(ChatMessageModel * message);// Load all entries till message and return its index in displayed list (-1 if not found) public slots: void onMoreEntriesLoaded(const int& count); signals: void peerAddressChanged (const QString &peerAddress); void localAddressChanged (const QString &localAddress); void fullPeerAddressChanged (const QString &fullPeerAddress); void fullLocalAddressChanged (const QString &fullLocalAddress); bool isRemoteComposingChanged (); void markAsReadEnabledChanged(); //bool isSecureChanged(bool secure); void chatRoomModelChanged(); void chatRoomDeleted(); void moreEntriesLoaded (int n); void entryTypeFilterChanged (int type); void filterTextChanged(); void isCallChanged(); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; private: QString getPeerAddress () const; void setPeerAddress (const QString &peerAddress); QString getLocalAddress () const; void setLocalAddress (const QString &localAddress); QString getFullPeerAddress () const; void setFullPeerAddress (const QString &peerAddress); QString getFullLocalAddress () const; void setFullLocalAddress (const QString &localAddress); bool markAsReadEnabled() const; void enableMarkAsRead(const bool& enable); ChatRoomModel *getChatRoomModel() const; void setChatRoomModel (ChatRoomModel *chatRoomModel); QList getComposers () const; QString getCachedText() const; void reload (ChatRoomModel *chatRoomModel); void handleIsActiveChanged (QWindow *window); void handleIsRemoteComposingChanged (); void handleMessageReceived (const std::shared_ptr &message); void handleMessageSent (const std::shared_ptr &message); int mMaxDisplayedEntries = EntriesChunkSize; int mEntryTypeFilter = ChatRoomModel::EntryType::GenericEntry; QString mPeerAddress; QString mLocalAddress; QString mFullPeerAddress; QString mFullLocalAddress; static QString gCachedText; bool mMarkAsReadEnabled; bool mIsCall = false; QString mFilterText; QSharedPointer mChatRoomModel; static constexpr int EntriesChunkSize = 50; }; #endif // CHAT_ROOM_PROXY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/chat/000077500000000000000000000000001434616504300232745ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/chat/ChatModel.cpp000066400000000000000000000032541434616504300256440ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ChatModel.hpp" #include #include #include #include #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" #include "components/chat-events/ChatMessageModel.hpp" #include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "components/Components.hpp" // ============================================================================= ChatModel::ChatModel(QObject * parent ) : QObject(parent){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mContents = QSharedPointer::create(nullptr); } QSharedPointer ChatModel::getContentListModel() const { return mContents; } void ChatModel::clear() { mContents->clear(); }linphone-desktop-5.0.2/linphone-app/src/components/chat/ChatModel.hpp000066400000000000000000000025321434616504300256470ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // Used to store data between chats #ifndef CHAT_MODEL_H_ #define CHAT_MODEL_H_ #include // ============================================================================= #include #include #include #include class ContentListModel; class ChatModel : public QObject{ Q_OBJECT public: ChatModel(QObject * parent = nullptr); // Getters QSharedPointer getContentListModel() const; // Tools Q_INVOKABLE void clear(); private: QSharedPointer mContents; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/codecs/000077500000000000000000000000001434616504300236155ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/codecs/AbstractCodecsModel.cpp000066400000000000000000000131341434616504300301700ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "app/paths/Paths.hpp" #include "components/core/CoreManager.hpp" #include "utils/Utils.hpp" #include "AbstractCodecsModel.hpp" // ============================================================================= using namespace std; static inline shared_ptr getCodecFromMap (const QVariantMap &map) { return map.value("__codec").value>(); } // ----------------------------------------------------------------------------- AbstractCodecsModel::AbstractCodecsModel (QObject *parent) : ProxyAbstractListModel(parent) {} // ----------------------------------------------------------------------------- void AbstractCodecsModel::enableCodec (int id, bool status) { Q_ASSERT(id >= 0 && id < mList.count()); QVariantMap &map = mList[id]; shared_ptr codec = getCodecFromMap(map); if (codec) { codec->enable(status); map["enabled"] = codec->enabled(); emit dataChanged(index(id, 0), index(id, 0)); } } void AbstractCodecsModel::moveCodec (int source, int destination) { moveRow(QModelIndex(), source, QModelIndex(), destination); } void AbstractCodecsModel::setBitrate (int id, int bitrate) { Q_ASSERT(id >= 0 && id < mList.count()); QVariantMap &map = mList[id]; shared_ptr codec = getCodecFromMap(map); if (codec) { codec->setNormalBitrate(bitrate); map["bitrate"] = codec->getNormalBitrate(); emit dataChanged(index(id, 0), index(id, 0)); } } void AbstractCodecsModel::setRecvFmtp (int id, const QString &recvFmtp) { Q_ASSERT(id >= 0 && id < mList.count()); QVariantMap &map = mList[id]; shared_ptr codec = getCodecFromMap(map); if (codec) { codec->setRecvFmtp(Utils::appStringToCoreString(recvFmtp)); map["recvFmtp"] = Utils::coreStringToAppString(codec->getRecvFmtp()); emit dataChanged(index(id, 0), index(id, 0)); } } // ----------------------------------------------------------------------------- bool AbstractCodecsModel::moveRows ( const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild ) { // TODO: Do not move downloadable codecs. int limit = sourceRow + count - 1; { int nCodecs = mList.count(); if ( sourceRow < 0 || destinationChild < 0 || count < 0 || destinationChild > nCodecs || limit >= nCodecs || (sourceRow <= destinationChild && sourceRow + count >= destinationChild) ) return false; } beginMoveRows(sourceParent, sourceRow, limit, destinationParent, destinationChild); // Update UI. if (destinationChild > sourceRow) { --destinationChild; for (int i = sourceRow; i <= limit; ++i) { mList.move(sourceRow, destinationChild + i - sourceRow); } } else { for (int i = sourceRow; i <= limit; ++i) mList.move(sourceRow + i - sourceRow, destinationChild + i - sourceRow); } // Update linphone codecs list. list> codecs; for (const auto &map : mList) { // Do not update downloadable codecs. shared_ptr codec = getCodecFromMap(map); if (codec) codecs.push_back(codec); } updateCodecs(codecs); endMoveRows(); return true; } // ----------------------------------------------------------------------------- void AbstractCodecsModel::addCodec (shared_ptr &codec) { QVariantMap map; map["bitrate"] = codec->getNormalBitrate(); map["channels"] = codec->getChannels(); map["clockRate"] = codec->getClockRate(); map["description"] = Utils::coreStringToAppString(codec->getDescription()); map["enabled"] = codec->enabled(); map["encoderDescription"] = Utils::coreStringToAppString(codec->getEncoderDescription()); map["isUsable"] = codec->isUsable(); // TODO: Notify in UI when unusable. map["isVbr"] = codec->isVbr(); map["mime"] = Utils::coreStringToAppString(codec->getMimeType()); map["number"] = codec->getNumber(); map["recvFmtp"] = Utils::coreStringToAppString(codec->getRecvFmtp()); map["__codec"] = QVariant::fromValue(codec); mList << map; } void AbstractCodecsModel::addDownloadableCodec ( const QString &mime, const QString &encoderDescription, const QString &downloadUrl, const QString &installName, const QString &checksum ) { QVariantMap map; map["downloadUrl"] = downloadUrl; map["encoderDescription"] = encoderDescription; map["installName"] = installName; map["mime"] = mime; map["checksum"] = checksum; mList << map; } QVariantMap AbstractCodecsModel::getCodecInfo (const QString &mime) const { for (const auto &codec : mList) if (codec.value("mime") == mime) return codec; return QVariantMap(); }; QString AbstractCodecsModel::getCodecsFolder () { return Utils::coreStringToAppString(Paths::getCodecsDirPath()); } linphone-desktop-5.0.2/linphone-app/src/components/codecs/AbstractCodecsModel.hpp000066400000000000000000000043631434616504300302010ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef ABSTRACT_CODECS_MODEL_H_ #define ABSTRACT_CODECS_MODEL_H_ #include #include "app/proxyModel/ProxyAbstractListModel.hpp" // ============================================================================= namespace linphone { class PayloadType; } class AbstractCodecsModel : public ProxyAbstractListModel { Q_OBJECT Q_PROPERTY(QString codecsFolder READ getCodecsFolder CONSTANT) public: AbstractCodecsModel (QObject *parent = Q_NULLPTR); Q_INVOKABLE void enableCodec (int id, bool status); Q_INVOKABLE void moveCodec (int source, int destination); Q_INVOKABLE void setBitrate (int id, int bitrate); Q_INVOKABLE void setRecvFmtp (int id, const QString &recvFmtp); Q_INVOKABLE virtual void reload () {}; Q_INVOKABLE QVariantMap getCodecInfo (const QString &mime) const; protected: bool moveRows ( const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild ) override; void addCodec (std::shared_ptr &codec); void addDownloadableCodec ( const QString &mime, const QString &encoderDescription, const QString &downloadUrl, const QString &installName, const QString &chekcksum ); virtual void updateCodecs (std::list> &codecs) = 0; static QString getCodecsFolder (); }; Q_DECLARE_METATYPE(std::shared_ptr); #endif // ABSTRACT_CODECS_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/codecs/AudioCodecsModel.cpp000066400000000000000000000024611434616504300274670ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/core/CoreManager.hpp" #include "AudioCodecsModel.hpp" // ============================================================================= using namespace std; AudioCodecsModel::AudioCodecsModel (QObject *parent) : AbstractCodecsModel(parent) { for (auto &codec : CoreManager::getInstance()->getCore()->getAudioPayloadTypes()) addCodec(codec); } void AudioCodecsModel::updateCodecs (list> &codecs) { CoreManager::getInstance()->getCore()->setAudioPayloadTypes(codecs); } linphone-desktop-5.0.2/linphone-app/src/components/codecs/AudioCodecsModel.hpp000066400000000000000000000023221434616504300274700ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef AUDIO_CODECS_MODEL_H_ #define AUDIO_CODECS_MODEL_H_ #include "AbstractCodecsModel.hpp" // ============================================================================= class AudioCodecsModel : public AbstractCodecsModel { Q_OBJECT; public: AudioCodecsModel (QObject *parent = Q_NULLPTR); private: void updateCodecs (std::list> &codecs) override; }; #endif // AUDIO_CODECS_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/codecs/VideoCodecsModel.cpp000066400000000000000000000147451434616504300275040ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include "app/paths/Paths.hpp" #include "components/core/CoreManager.hpp" #include "components/file/FileDownloader.hpp" #include "components/file/FileExtractor.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "VideoCodecsModel.hpp" #include // ============================================================================= using namespace std; VideoCodecsModel::VideoCodecsModel (QObject *parent) : AbstractCodecsModel(parent) { load(); } #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) static bool downloadUpdatableCodec ( QObject *parent, const QString &codecsFolder, const QString &mime, const QString &downloadUrl, const QString &installName, const QString &checksum ) { QString versionFilePath = codecsFolder + mime + ".txt"; QFile versionFile(versionFilePath); if (!versionFile.exists() && !QFileInfo::exists(codecsFolder + installName)) return false; // Must be downloaded one time before. if (!versionFile.open(QIODevice::ReadOnly)) qWarning() << QStringLiteral("Unable to read codec version from: `%1`.").arg(versionFilePath); else if (!QString::compare(QTextStream(&versionFile).readAll(), downloadUrl, Qt::CaseInsensitive)) return false; qInfo() << QStringLiteral("Updating `%1` codec...").arg(mime); FileDownloader *fileDownloader = new FileDownloader(parent); fileDownloader->setUrl(QUrl(downloadUrl)); fileDownloader->setDownloadFolder(codecsFolder); FileExtractor *fileExtractor = new FileExtractor(fileDownloader); fileExtractor->setExtractFolder(codecsFolder); fileExtractor->setExtractName(installName + ".in"); QObject::connect(fileDownloader, &FileDownloader::downloadFinished, [fileDownloader, fileExtractor, checksum](const QString &filePath) { fileExtractor->setFile(filePath); QString fileChecksum = Utils::getFileChecksum(filePath); if(checksum.isEmpty() || fileChecksum == checksum) fileExtractor->extract(); else{ qWarning() << "File cannot be downloaded : Bad checksum : " << fileChecksum; fileDownloader->remove(); fileDownloader->deleteLater(); } }); QObject::connect(fileDownloader, &FileDownloader::downloadFailed, [fileDownloader]() { fileDownloader->deleteLater(); }); QObject::connect(fileExtractor, &FileExtractor::extractFinished, [fileDownloader, fileExtractor, versionFilePath, downloadUrl]() { QFile versionFile(versionFilePath); if (!versionFile.open(QIODevice::WriteOnly)) { qWarning() << QStringLiteral("Unable to write codec version in: `%1`.").arg(versionFilePath); return; } if (versionFile.write(Utils::appStringToCoreString(downloadUrl).c_str(), downloadUrl.length()) == -1) { fileExtractor->remove(); versionFile.close(); versionFile.remove(); } fileDownloader->remove(); fileDownloader->deleteLater(); }); QObject::connect(fileExtractor, &FileExtractor::extractFailed, [fileDownloader]() { fileDownloader->remove(); fileDownloader->deleteLater(); }); fileDownloader->download(); return true; } #endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN) void VideoCodecsModel::updateCodecs () { #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) static const QString codecSuffix = QStringLiteral(".%1").arg(Constants::LibraryExtension); QDirIterator it(getCodecsFolder()); while (it.hasNext()) { QFileInfo info(it.next()); if (info.suffix() == QLatin1String("in")) { QString codecName = info.completeBaseName(); if (codecName.endsWith(codecSuffix)) { QString codecPath = info.dir().path() + QDir::separator() + codecName; QFile::remove(codecPath); QFile::rename(info.filePath(), codecPath); } } } #endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN) } void VideoCodecsModel::downloadUpdatableCodecs (QObject *parent) { #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) downloadUpdatableCodec(parent, getCodecsFolder(), "H264", Constants::PluginUrlH264, Constants::H264InstallName, Constants::PluginH264Check); #else Q_UNUSED(parent); #endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN) } void VideoCodecsModel::updateCodecs (list> &codecs) { CoreManager::getInstance()->getCore()->setVideoPayloadTypes(codecs); } void VideoCodecsModel::load () { resetData(); shared_ptr core = CoreManager::getInstance()->getCore(); // Load downloaded codecs like OpenH264. #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) QDirIterator it(getCodecsFolder()); while (it.hasNext()) { QFileInfo info(it.next()); const QString filename(info.fileName()); if ( QLibrary::isLibrary(filename) ) { qInfo() << QStringLiteral("Loading `%1` symbols...").arg(filename); if (!QLibrary(info.filePath()).load()) //lib.load()) qWarning() << QStringLiteral("Failed to load `%1` symbols.").arg(filename); } } core->reloadMsPlugins(""); #endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN) // Add codecs. auto codecs = core->getVideoPayloadTypes(); for (auto &codec : codecs) addCodec(codec); // Add downloadable codecs. // TODO: Add an API to check if the ms h264 plugin is available. #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) if (find_if(codecs.begin(), codecs.end(), [](const shared_ptr &codec) { return codec->getMimeType() == "H264"; }) == codecs.end()) addDownloadableCodec("H264", Constants::H264Description, Constants::PluginUrlH264, Constants::H264InstallName, Constants::PluginH264Check); #endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN) } void VideoCodecsModel::reload () { beginResetModel(); load(); endResetModel(); } linphone-desktop-5.0.2/linphone-app/src/components/codecs/VideoCodecsModel.hpp000066400000000000000000000025271434616504300275040ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef VIDEO_CODECS_MODEL_H_ #define VIDEO_CODECS_MODEL_H_ #include "AbstractCodecsModel.hpp" // ============================================================================= class VideoCodecsModel : public AbstractCodecsModel { Q_OBJECT; public: VideoCodecsModel (QObject *parent = Q_NULLPTR); static void updateCodecs (); static void downloadUpdatableCodecs (QObject *parent); private: void updateCodecs (std::list> &codecs) override; void load (); void reload () override; }; #endif // VIDEO_CODECS_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/conference/000077500000000000000000000000001434616504300244645ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceAddModel.cpp000066400000000000000000000207201434616504300306320ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "components/core/CoreManager.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/Utils.hpp" #include "ConferenceAddModel.hpp" // ============================================================================= using namespace std; ConferenceHelperModel::ConferenceAddModel::ConferenceAddModel (QObject *parent) : QAbstractListModel(parent) { mConferenceHelperModel = qobject_cast(parent); Q_CHECK_PTR(mConferenceHelperModel); CoreManager *coreManager = CoreManager::getInstance(); QObject::connect( coreManager->getSipAddressesModel(), &SipAddressesModel::dataChanged, this, &ConferenceAddModel::handleDataChanged ); for (const auto &call : coreManager->getCore()->getCalls()) { if (call->getCurrentParams()->getLocalConferenceMode()) addToConference(call->getRemoteAddress()); } } int ConferenceHelperModel::ConferenceAddModel::rowCount (const QModelIndex &) const { return mRefs.count(); } QHash ConferenceHelperModel::ConferenceAddModel::roleNames () const { QHash roles; roles[Qt::DisplayRole] = "$modelData"; return roles; } QVariant ConferenceHelperModel::ConferenceAddModel::data (const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mRefs.count()) return QVariant(); if (role == Qt::DisplayRole) return QVariant::fromValue(*mRefs[row]); return QVariant(); } // ----------------------------------------------------------------------------- bool ConferenceHelperModel::ConferenceAddModel::addToConference (const shared_ptr &linphoneAddress) { const QString sipAddress = Utils::coreStringToAppString(linphoneAddress->asStringUriOnly()); if (mSipAddresses.contains(sipAddress)) return false; int row = rowCount(); beginInsertRows(QModelIndex(), row, row); addToConferencePrivate(linphoneAddress->clone()); endInsertRows(); emit layoutChanged(); mConferenceHelperModel->invalidate(); return true; } bool ConferenceHelperModel::ConferenceAddModel::addToConference (const QString &sipAddress) { if (mSipAddresses.contains(sipAddress)) return false; shared_ptr address = CoreManager::getInstance()->getCore()->interpretUrl( Utils::appStringToCoreString(sipAddress) ); if (!address) return false; int row = rowCount(); beginInsertRows(QModelIndex(), row, row); qInfo() << QStringLiteral("Add sip address to conference: `%1`.").arg(sipAddress); addToConferencePrivate(address); endInsertRows(); emit layoutChanged(); mConferenceHelperModel->invalidate(); return true; } void ConferenceHelperModel::ConferenceAddModel::addParticipants(ChatRoomModel * model){ auto participants = model->getParticipants(); for( auto participant : participants){ if(participant){ addToConference(Utils::coreStringToAppString(participant->getAddress()->asString())); } } } bool ConferenceHelperModel::ConferenceAddModel::removeFromConference (const QString &sipAddress) { auto it = mSipAddresses.find(sipAddress); if (it == mSipAddresses.end()) return false; int row = mRefs.indexOf(&(*it)); beginRemoveRows(QModelIndex(), row, row); qInfo() << QStringLiteral("Remove sip address from conference: `%1`.").arg(sipAddress); mRefs.removeAt(row); mSipAddresses.remove(sipAddress); endRemoveRows(); mConferenceHelperModel->invalidate(); return true; } // ----------------------------------------------------------------------------- void ConferenceHelperModel::ConferenceAddModel::update () { shared_ptr conference = mConferenceHelperModel->mCore->getConference(); auto currentCall = CoreManager::getInstance()->getCore()->getCurrentCall(); bool enablingVideo = false; if( currentCall ) enablingVideo = currentCall->getCurrentParams()->videoEnabled(); if(!conference){ auto parameters = mConferenceHelperModel->mCore->createConferenceParams(conference); if(!CoreManager::getInstance()->getSettingsModel()->getVideoConferenceEnabled()) { parameters->enableVideo(false); parameters->setConferenceFactoryAddress(nullptr);// Do a local conference }else parameters->enableVideo(enablingVideo); conference = mConferenceHelperModel->mCore->createConferenceWithParams(parameters); } auto currentCalls = CoreManager::getInstance()->getCore()->getCalls(); list> allLinphoneAddresses; list> newCalls; list> runningCallsToAdd; for (const auto &map : mRefs) { shared_ptr linphoneAddress = map->value("__linphoneAddress").value>(); Q_CHECK_PTR(linphoneAddress); allLinphoneAddresses.push_back(linphoneAddress); auto haveCall = std::find_if(currentCalls.begin(), currentCalls.end(), [linphoneAddress](const std::shared_ptr& call){ return call->getRemoteAddress()->weakEqual(linphoneAddress); }); if( haveCall == currentCalls.end()) newCalls.push_back(linphoneAddress); else runningCallsToAdd.push_back(*haveCall); } // 1) Add running calls if( runningCallsToAdd.size() > 0){ conference->addParticipants(runningCallsToAdd); } //1) Invite participants if( newCalls.size() > 0){ auto parameters = CoreManager::getInstance()->getCore()->createCallParams(nullptr); parameters->enableVideo(enablingVideo); conference->inviteParticipants( newCalls, parameters ); } // 3) Put in pause and remove all calls that are not in the conference list for(const auto &call : CoreManager::getInstance()->getCore()->getCalls()){ const std::string callAddress = call->getRemoteAddress()->asStringUriOnly(); auto address = allLinphoneAddresses.begin(); while(address != allLinphoneAddresses.end() && (*address)->asStringUriOnly() != callAddress) ++address; if(address == allLinphoneAddresses.end()){// Not in conference list : put in pause and remove it from conference if it's the case if( call->getParams()->getLocalConferenceMode() ){// Remove conference if it is not yet requested CoreManager::getInstance()->getCore()->removeFromConference(call); }else call->pause(); } } } // ----------------------------------------------------------------------------- void ConferenceHelperModel::ConferenceAddModel::addToConferencePrivate (const shared_ptr &linphoneAddress) { QString sipAddress = Utils::coreStringToAppString(linphoneAddress->asStringUriOnly()); QVariantMap map = CoreManager::getInstance()->getSipAddressesModel()->find(sipAddress); map["sipAddress"] = sipAddress; map["__linphoneAddress"] = QVariant::fromValue(linphoneAddress); mSipAddresses[sipAddress] = map; mRefs << &mSipAddresses[sipAddress]; } // ----------------------------------------------------------------------------- void ConferenceHelperModel::ConferenceAddModel::handleDataChanged ( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector & ) { SipAddressesModel *sipAddressesModel = CoreManager::getInstance()->getSipAddressesModel(); int limit = bottomRight.row(); for (int row = topLeft.row(); row <= limit; ++row) { const QVariantMap map = sipAddressesModel->data(sipAddressesModel->index(row, 0)).toMap(); auto it = mSipAddresses.find(map["sipAddress"].toString()); if (it != mSipAddresses.end()) { (*it)["contact"] = map.value("contact"); int row = mRefs.indexOf(&(*it)); Q_ASSERT(row != -1); emit dataChanged(index(row, 0), index(row, 0)); } } } linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceAddModel.hpp000066400000000000000000000046061434616504300306440ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONFERENCE_ADD_MODEL_H_ #define CONFERENCE_ADD_MODEL_H_ #include "ConferenceHelperModel.hpp" // ============================================================================= // Sip addresses list to add to conference. // ============================================================================= namespace linphone { class Address; } class ChatRoomModel; class ConferenceHelperModel::ConferenceAddModel : public QAbstractListModel { Q_OBJECT; public: ConferenceAddModel (QObject *parent = Q_NULLPTR); int rowCount (const QModelIndex &index = QModelIndex()) const override; QHash roleNames () const override; QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; bool addToConference (const std::shared_ptr &linphoneAddress); Q_INVOKABLE bool addToConference (const QString &sipAddress); Q_INVOKABLE bool removeFromConference (const QString &sipAddress); Q_INVOKABLE void addParticipants(ChatRoomModel * model); Q_INVOKABLE void update (); bool contains (const QString &sipAddress) const { return mSipAddresses.contains(sipAddress); } private: void addToConferencePrivate (const std::shared_ptr &linphoneAddress); void handleDataChanged ( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles = QVector() ); QHash mSipAddresses; QList mRefs; ConferenceHelperModel *mConferenceHelperModel = nullptr; }; Q_DECLARE_METATYPE(std::shared_ptr); #endif // CONFERENCE_ADD_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceHelperModel.cpp000066400000000000000000000061571434616504300313710ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "components/calls/CallsListModel.hpp" #include "components/core/CoreManager.hpp" #include "components/sip-addresses/SipAddressesProxyModel.hpp" #include "utils/Utils.hpp" #include "ConferenceAddModel.hpp" #include "ConferenceHelperModel.hpp" #include "components/contact/ContactModel.hpp" // ============================================================================= using namespace std; ConferenceHelperModel::ConferenceHelperModel (QObject *parent) : QSortFilterProxyModel(parent) { mCore = CoreManager::getInstance()->getCore(); mConferenceAddModel = new ConferenceAddModel(this); App::getInstance()->getEngine()->setObjectOwnership(mConferenceAddModel, QQmlEngine::CppOwnership); QObject::connect(this, &CallsListModel::rowsRemoved, [this] { invalidate(); }); QObject::connect(this, &CallsListModel::rowsInserted, [this] { invalidate(); }); setSourceModel(new SipAddressesProxyModel(this)); sort(0); } QHash ConferenceHelperModel::roleNames () const { QHash roles; roles[Qt::DisplayRole] = "$modelData"; return roles; } // ----------------------------------------------------------------------------- void ConferenceHelperModel::setFilter (const QString &pattern) { static_cast(sourceModel())->setFilter(pattern); } // ----------------------------------------------------------------------------- bool ConferenceHelperModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); const QVariantMap data = index.data().toMap(); const ContactModel * contactModel = data["contactModel"].value(); return contactModel != nullptr && !mConferenceAddModel->contains(data["sipAddress"].toString()); } // ----------------------------------------------------------------------------- bool ConferenceHelperModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { shared_ptr callA = mCore->findCallFromUri( Utils::appStringToCoreString(left.data().toMap()["sipAddress"].toString()) ); shared_ptr callB = mCore->findCallFromUri( Utils::appStringToCoreString(right.data().toMap()["sipAddress"].toString()) ); return callA && !callB; } linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceHelperModel.hpp000066400000000000000000000037731434616504300313770ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONFERENCE_HELPER_MODEL_H_ #define CONFERENCE_HELPER_MODEL_H_ #include #include // ============================================================================= // Sip addresses not in conference. // Can filter the sip addresses with a pattern. // ============================================================================= namespace linphone { class Conference; class Core; } class CallModel; class ConferenceHelperModel : public QSortFilterProxyModel { Q_OBJECT; Q_PROPERTY(ConferenceHelperModel::ConferenceAddModel *toAdd READ getConferenceAddModel CONSTANT); public: class ConferenceAddModel; ConferenceHelperModel (QObject *parent = Q_NULLPTR); QHash roleNames () const override; ConferenceAddModel *getConferenceAddModel () const { return mConferenceAddModel; } Q_INVOKABLE void setFilter (const QString &pattern); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; private: ConferenceAddModel *mConferenceAddModel = nullptr; std::shared_ptr mCore; }; #endif // CONFERENCE_HELPER_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceListener.cpp000066400000000000000000000142161434616504300307510ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ConferenceListener.hpp" #include #include #include #include #include "app/App.hpp" #include "app/paths/Paths.hpp" // ============================================================================= ConferenceListener::ConferenceListener () : QObject(nullptr) { } ConferenceListener::~ConferenceListener(){ } //----------------------------------------------------------------------------------------------------------------------- // LINPHONE LISTENERS //----------------------------------------------------------------------------------------------------------------------- void ConferenceListener::onActiveSpeakerParticipantDevice(const std::shared_ptr & conference, const std::shared_ptr & participantDevice) { qDebug() << "onActiveSpeakerParticipantDevice: " << participantDevice->getAddress()->asString().c_str(); emit activeSpeakerParticipantDevice(participantDevice); } void ConferenceListener::onParticipantAdded(const std::shared_ptr & conference, const std::shared_ptr & participant){ qDebug() << "onParticipantAdded: " << participant->getAddress()->asString().c_str(); emit participantAdded(participant); } void ConferenceListener::onParticipantRemoved(const std::shared_ptr & conference, const std::shared_ptr & participant){ qDebug() << "onParticipantRemoved"; emit participantRemoved(participant); } void ConferenceListener::onParticipantDeviceAdded(const std::shared_ptr & conference, const std::shared_ptr & participantDevice){ qDebug() << "onParticipantDeviceAdded"; qDebug() << "Me devices : " << conference->getMe()->getDevices().size(); if( conference->getMe()->getDevices().size() > 1) for(auto d : conference->getMe()->getDevices()) qDebug() << "\t--> " << d->getAddress()->asString().c_str(); emit participantDeviceAdded(participantDevice); } void ConferenceListener::onParticipantDeviceRemoved(const std::shared_ptr & conference, const std::shared_ptr & participantDevice){ qDebug() << "onParticipantDeviceRemoved: " << participantDevice->getAddress()->asString().c_str() << " isInConf?[" << participantDevice->isInConference() << "]"; qDebug() << "Me devices : " << conference->getMe()->getDevices().size(); emit participantDeviceRemoved(participantDevice); } void ConferenceListener::onParticipantDeviceStateChanged(const std::shared_ptr & conference, const std::shared_ptr & device, linphone::ParticipantDeviceState state) { qDebug() << "onParticipantDeviceStateChanged: " << device->getAddress()->asString().c_str() << " isInConf?[" << device->isInConference() << "] " << (int)state; emit participantDeviceStateChanged(conference, device, state); } void ConferenceListener::onParticipantAdminStatusChanged(const std::shared_ptr & conference, const std::shared_ptr & participant){ qDebug() << "onParticipantAdminStatusChanged"; emit participantAdminStatusChanged(participant); } void ConferenceListener::onParticipantDeviceMediaCapabilityChanged(const std::shared_ptr & conference, const std::shared_ptr & participantDevice){ qDebug() << "onParticipantDeviceMediaCapabilityChanged: " << (int)participantDevice->getStreamCapability(linphone::StreamType::Video) << ". Device: " << participantDevice->getAddress()->asString().c_str(); emit participantDeviceMediaCapabilityChanged(participantDevice); } void ConferenceListener::onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr & conference, const std::shared_ptr & participantDevice){ qDebug() << "onParticipantDeviceMediaAvailabilityChanged: " << (int)participantDevice->getStreamAvailability(linphone::StreamType::Video) << ". Device: " << participantDevice->getAddress()->asString().c_str(); emit participantDeviceMediaAvailabilityChanged(participantDevice); } void ConferenceListener::onParticipantDeviceIsSpeakingChanged(const std::shared_ptr & conference, const std::shared_ptr & participantDevice, bool isSpeaking) { //qDebug() << "onParticipantDeviceIsSpeakingChanged: " << participantDevice->getAddress()->asString().c_str() << ". Speaking:" << isSpeaking; emit participantDeviceIsSpeakingChanged(participantDevice, isSpeaking); } void ConferenceListener::onStateChanged(const std::shared_ptr & conference, linphone::Conference::State newState){ qDebug() << "onStateChanged:" << (int)newState; emit conferenceStateChanged(newState); } void ConferenceListener::onSubjectChanged(const std::shared_ptr & conference, const std::string & subject){ qDebug() << "onSubjectChanged"; emit subjectChanged(subject); } void ConferenceListener::onAudioDeviceChanged(const std::shared_ptr & conference, const std::shared_ptr & audioDevice){ qDebug() << "onAudioDeviceChanged is not yet implemented."; } //----------------------------------------------------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceListener.hpp000066400000000000000000000115501434616504300307540ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONFERENCE_LISTENER_H_ #define CONFERENCE_LISTENER_H_ #include // ============================================================================= #include #include #include class ConferenceListener : public QObject, public linphone::ConferenceListener{ Q_OBJECT public: ConferenceListener(); virtual ~ConferenceListener(); // LINPHONE LISTENERS virtual void onActiveSpeakerParticipantDevice(const std::shared_ptr & conference, const std::shared_ptr & participantDevice) override; virtual void onParticipantAdded(const std::shared_ptr & conference, const std::shared_ptr & participant) override; virtual void onParticipantRemoved(const std::shared_ptr & conference, const std::shared_ptr & participant) override; virtual void onParticipantAdminStatusChanged(const std::shared_ptr & conference, const std::shared_ptr & participant) override; virtual void onParticipantDeviceAdded(const std::shared_ptr & conference, const std::shared_ptr & participantDevice) override; virtual void onParticipantDeviceRemoved(const std::shared_ptr & conference, const std::shared_ptr & participantDevice) override; virtual void onParticipantDeviceStateChanged(const std::shared_ptr & conference, const std::shared_ptr & device, linphone::ParticipantDeviceState state) override; virtual void onParticipantDeviceMediaCapabilityChanged(const std::shared_ptr & conference, const std::shared_ptr & device) override; virtual void onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr & conference, const std::shared_ptr & device) override; virtual void onParticipantDeviceIsSpeakingChanged(const std::shared_ptr & conference, const std::shared_ptr & participantDevice, bool isSpeaking) override; virtual void onStateChanged(const std::shared_ptr & conference, linphone::Conference::State newState) override; virtual void onSubjectChanged(const std::shared_ptr & conference, const std::string & subject) override; virtual void onAudioDeviceChanged(const std::shared_ptr & conference, const std::shared_ptr & audioDevice) override; //--------------------------------------------------------------------------- signals: void activeSpeakerParticipantDevice(const std::shared_ptr & participantDevice); void participantAdded(const std::shared_ptr & participant); void participantRemoved(const std::shared_ptr & participant); void participantAdminStatusChanged(const std::shared_ptr & participant); void participantDeviceAdded(const std::shared_ptr & participantDevice); void participantDeviceRemoved(const std::shared_ptr & participantDevice); void participantDeviceStateChanged(const std::shared_ptr & conference, const std::shared_ptr & device, linphone::ParticipantDeviceState state); void participantDeviceMediaCapabilityChanged(const std::shared_ptr & participantDevice); void participantDeviceMediaAvailabilityChanged(const std::shared_ptr & participantDevice); void participantDeviceIsSpeakingChanged(const std::shared_ptr & participantDevice, bool isSpeaking); void conferenceStateChanged(linphone::Conference::State newState); void subjectChanged(const std::string & subject); }; #endif linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceModel.cpp000066400000000000000000000236751434616504300302350ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ConferenceModel.hpp" #include "ConferenceListener.hpp" #include #include #include #include #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" #include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "components/Components.hpp" void ConferenceModel::connectTo(ConferenceListener * listener){ connect(listener, &ConferenceListener::activeSpeakerParticipantDevice, this, &ConferenceModel::onActiveSpeakerParticipantDevice); connect(listener, &ConferenceListener::participantAdded, this, &ConferenceModel::onParticipantAdded); connect(listener, &ConferenceListener::participantRemoved, this, &ConferenceModel::onParticipantRemoved); connect(listener, &ConferenceListener::participantAdminStatusChanged, this, &ConferenceModel::onParticipantAdminStatusChanged); connect(listener, &ConferenceListener::participantDeviceAdded, this, &ConferenceModel::onParticipantDeviceAdded); connect(listener, &ConferenceListener::participantDeviceRemoved, this, &ConferenceModel::onParticipantDeviceRemoved); connect(listener, &ConferenceListener::participantDeviceStateChanged, this, &ConferenceModel::onParticipantDeviceStateChanged); connect(listener, &ConferenceListener::participantDeviceMediaCapabilityChanged, this, &ConferenceModel::onParticipantDeviceMediaCapabilityChanged); connect(listener, &ConferenceListener::participantDeviceMediaAvailabilityChanged, this, &ConferenceModel::onParticipantDeviceMediaAvailabilityChanged); connect(listener, &ConferenceListener::participantDeviceIsSpeakingChanged, this, &ConferenceModel::onParticipantDeviceIsSpeakingChanged); connect(listener, &ConferenceListener::conferenceStateChanged, this, &ConferenceModel::onConferenceStateChanged); connect(listener, &ConferenceListener::subjectChanged, this, &ConferenceModel::onSubjectChanged); } // ============================================================================= QSharedPointer ConferenceModel::create(std::shared_ptr conference, QObject *parent){ return QSharedPointer::create(conference, parent); } ConferenceModel::ConferenceModel (std::shared_ptr conference, QObject *parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mConference = conference; mParticipantListModel = QSharedPointer::create(this); mConferenceListener = std::make_shared(); connectTo(mConferenceListener.get()); mConference->addListener(mConferenceListener); connect(this, &ConferenceModel::participantDeviceAdded, this, &ConferenceModel::participantDeviceCountChanged); connect(this, &ConferenceModel::participantDeviceRemoved, this, &ConferenceModel::participantDeviceCountChanged); connect(mParticipantListModel.get(), &ParticipantListModel::participantsChanged, this, &ConferenceModel::participantDeviceCountChanged); onConferenceStateChanged(mConference->getState());// Is it already Created like for local conference? } ConferenceModel::~ConferenceModel(){ mConference->removeListener(mConferenceListener); } bool ConferenceModel::updateLocalParticipant(){ bool changed = false; if(mConference && mConference->getCall()){ // First try to use findParticipant auto localParticipant = mConference->findParticipant(mConference->getCall()->getCallLog()->getLocalAddress()); // Me is not in participants, use Me(). if( !localParticipant) localParticipant = mConference->getMe(); if( localParticipant && (!mLocalParticipant || mLocalParticipant->getParticipant() != localParticipant) ) { mLocalParticipant = QSharedPointer::create(localParticipant); qDebug() << "Is Admin: " << localParticipant->isAdmin() << " " << mLocalParticipant->getAdminStatus(); changed = true; emit localParticipantChanged(); } } return changed; } std::shared_ptr ConferenceModel::getConference()const{ return mConference; } QString ConferenceModel::getSubject() const{ return Utils::coreStringToAppString(mConference->getSubject()); } QDateTime ConferenceModel::getStartDate() const{ return QDateTime::fromSecsSinceEpoch(mConference->getStartTime()); } qint64 ConferenceModel::getElapsedSeconds() const { return mConference->getDuration(); } ParticipantModel* ConferenceModel::getLocalParticipant() const{ if( mLocalParticipant) { qDebug() << "LocalParticipant admin : " << mLocalParticipant->getAdminStatus() << " " << (mLocalParticipant->getParticipant() ? mLocalParticipant->getParticipant()->isAdmin() : -1); }else qDebug() << "No LocalParticipant"; return mLocalParticipant.get(); } ParticipantListModel* ConferenceModel::getParticipantListModel() const{ return mParticipantListModel.get(); } std::list> ConferenceModel::getParticipantList()const{ auto participantList = mConference->getParticipantList(); auto me = mConference->getMe(); if( me ) participantList.push_front(me); return participantList; } int ConferenceModel::getParticipantDeviceCount() const{ return mConference->getParticipantDeviceList().size(); } void ConferenceModel::setIsReady(bool state){ if( mIsReady != state){ mIsReady = state; emit isReadyChanged(); } } //----------------------------------------------------------------------------------------------------------------------- // LINPHONE LISTENERS //----------------------------------------------------------------------------------------------------------------------- void ConferenceModel::onActiveSpeakerParticipantDevice(const std::shared_ptr & participantDevice){ emit activeSpeakerParticipantDevice(participantDevice); } void ConferenceModel::onParticipantAdded(const std::shared_ptr & participant){ qDebug() << "Added call, participant count: " << getParticipantList().size() << ". Me devices : " << mConference->getMe()->getDevices().size(); updateLocalParticipant(); emit participantAdded(participant); } void ConferenceModel::onParticipantRemoved(const std::shared_ptr & participant){ qDebug() << "Me devices : " << mConference->getMe()->getDevices().size(); emit participantRemoved(participant); } void ConferenceModel::onParticipantAdminStatusChanged(const std::shared_ptr & participant){ qDebug() << "onParticipantAdminStatusChanged: " << participant->getAddress()->asString().c_str(); if(mLocalParticipant && participant == mLocalParticipant->getParticipant()) emit mLocalParticipant->adminStatusChanged(); emit participantAdminStatusChanged(participant); } void ConferenceModel::onParticipantDeviceAdded(const std::shared_ptr & participantDevice){ qDebug() << "Me devices : " << mConference->getMe()->getDevices().size(); updateLocalParticipant(); emit participantDeviceAdded(participantDevice); } void ConferenceModel::onParticipantDeviceRemoved(const std::shared_ptr & participantDevice){ qDebug() << "Me devices : " << mConference->getMe()->getDevices().size(); emit participantDeviceRemoved(participantDevice); } void ConferenceModel::onParticipantDeviceStateChanged(const std::shared_ptr & conference, const std::shared_ptr & device, linphone::ParticipantDeviceState state){ qDebug() << "Me devices : " << mConference->getMe()->getDevices().size(); updateLocalParticipant(); emit participantDeviceStateChanged(device, state); } void ConferenceModel::onParticipantDeviceMediaCapabilityChanged(const std::shared_ptr & participantDevice){ qDebug() << "ConferenceModel::onParticipantDeviceMediaCapabilityChanged: " << (int)participantDevice->getStreamCapability(linphone::StreamType::Video) << ". Me devices : " << mConference->getMe()->getDevices().size(); emit participantDeviceMediaCapabilityChanged(participantDevice); } void ConferenceModel::onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr & participantDevice){ qDebug() << "ConferenceModel::onParticipantDeviceMediaAvailabilityChanged: " << (int)participantDevice->getStreamAvailability(linphone::StreamType::Video) << ". Me devices : " << mConference->getMe()->getDevices().size(); emit participantDeviceMediaAvailabilityChanged(participantDevice); } void ConferenceModel::onParticipantDeviceIsSpeakingChanged(const std::shared_ptr & participantDevice, bool isSpeaking){ emit participantDeviceIsSpeakingChanged(participantDevice, isSpeaking); } void ConferenceModel::onConferenceStateChanged(linphone::Conference::State newState){ if(newState == linphone::Conference::State::Created){ setIsReady(true); emit participantDeviceCountChanged(); } updateLocalParticipant(); emit conferenceStateChanged(newState); } void ConferenceModel::onSubjectChanged(const std::string& string){ emit subjectChanged(); } //----------------------------------------------------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceModel.hpp000066400000000000000000000131031434616504300302230ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONFERENCE_MODEL_H_ #define CONFERENCE_MODEL_H_ #include // ============================================================================= #include #include #include #include "components/participant/ParticipantModel.hpp" class ConferenceListener; class ParticipantListModel; class ConferenceModel : public QObject{ Q_OBJECT public: Q_PROPERTY(QString subject READ getSubject NOTIFY subjectChanged) Q_PROPERTY(QDateTime startDate READ getStartDate CONSTANT) Q_PROPERTY(ParticipantListModel* participants READ getParticipantListModel CONSTANT) Q_PROPERTY(ParticipantModel* localParticipant READ getLocalParticipant NOTIFY localParticipantChanged) Q_PROPERTY(bool isReady MEMBER mIsReady WRITE setIsReady NOTIFY isReadyChanged) Q_PROPERTY(int participantDeviceCount READ getParticipantDeviceCount NOTIFY participantDeviceCountChanged) static QSharedPointer create(std::shared_ptr chatRoom, QObject *parent = Q_NULLPTR); ConferenceModel(std::shared_ptr content, QObject *parent = Q_NULLPTR); virtual ~ConferenceModel(); bool updateLocalParticipant(); // true if changed std::shared_ptr getConference()const; QString getSubject() const; QDateTime getStartDate() const; Q_INVOKABLE qint64 getElapsedSeconds() const; Q_INVOKABLE ParticipantModel* getLocalParticipant() const; ParticipantListModel* getParticipantListModel() const; std::list> getParticipantList() const; // SDK exclude me. We want to get ALL participants. int getParticipantDeviceCount() const; void setIsReady(bool state); virtual void onActiveSpeakerParticipantDevice(const std::shared_ptr & participantDevice); virtual void onParticipantAdded(const std::shared_ptr & participant); virtual void onParticipantRemoved(const std::shared_ptr & participant); virtual void onParticipantAdminStatusChanged(const std::shared_ptr & participant); virtual void onParticipantDeviceAdded(const std::shared_ptr & participantDevice); virtual void onParticipantDeviceRemoved(const std::shared_ptr & participantDevice); virtual void onParticipantDeviceMediaCapabilityChanged(const std::shared_ptr & device); virtual void onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr & device); virtual void onParticipantDeviceIsSpeakingChanged(const std::shared_ptr & device, bool isSpeaking); virtual void onParticipantDeviceStateChanged(const std::shared_ptr & conference, const std::shared_ptr & device, linphone::ParticipantDeviceState state); virtual void onConferenceStateChanged(linphone::Conference::State newState); virtual void onSubjectChanged(const std::string& subject); //--------------------------------------------------------------------------- signals: void activeSpeakerParticipantDevice(const std::shared_ptr & participantDevice); void localParticipantChanged(); void participantAdded(const std::shared_ptr & participant); void participantRemoved(const std::shared_ptr & participant); void participantAdminStatusChanged(const std::shared_ptr & participant); void participantDeviceAdded(const std::shared_ptr & participantDevice); void participantDeviceRemoved(const std::shared_ptr & participantDevice); void participantDeviceMediaCapabilityChanged(const std::shared_ptr & participantDevice); void participantDeviceMediaAvailabilityChanged(const std::shared_ptr & participantDevice); void participantDeviceIsSpeakingChanged(const std::shared_ptr & device, bool isSpeaking); void participantDeviceStateChanged(const std::shared_ptr & device, linphone::ParticipantDeviceState state); void conferenceStateChanged(linphone::Conference::State newState); void subjectChanged(); void isReadyChanged(); void participantDeviceCountChanged(); private: void connectTo(ConferenceListener * listener); std::shared_ptr mConference; std::shared_ptr mConferenceListener; QSharedPointer mLocalParticipant; QSharedPointer mParticipantListModel; bool mIsReady = false; }; Q_DECLARE_METATYPE(QSharedPointer) #endif linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceProxyModel.cpp000066400000000000000000000115201434616504300312610ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "app/App.hpp" #include "components/call/CallModel.hpp" #include "components/calls/CallsListModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/notifier/Notifier.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/MediastreamerUtils.hpp" #include "utils/Utils.hpp" #include "ConferenceProxyModel.hpp" // ============================================================================= using namespace std; ConferenceProxyModel::ConferenceProxyModel (QObject *parent) : SortFilterProxyModel(parent) { mDeleteSourceModel = false; setSourceModel(CoreManager::getInstance()->getCallsListModel()); emit conferenceChanged(); QObject::connect( CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::callStateChanged, this, [this] { emit conferenceChanged(); }); } // Show all paraticpants thar should be, will be or are still in conference bool ConferenceProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); const CallModel *callModel = index.data().value(); return callModel->getCall() && (callModel->getCall()->getParams()->getLocalConferenceMode() || callModel->getCall()->getCurrentParams()->getLocalConferenceMode()); } // ----------------------------------------------------------------------------- void ConferenceProxyModel::terminate () { shared_ptr core = CoreManager::getInstance()->getCore(); core->terminateConference(); for (const auto &call : core->getCalls()) { if (call->getParams()->getLocalConferenceMode())// Terminate all call where participants are or will be in conference call->terminate(); } } // ----------------------------------------------------------------------------- void ConferenceProxyModel::startRecording () { if (mRecording) return; qInfo() << QStringLiteral("Start recording conference:") << this; CoreManager *coreManager = CoreManager::getInstance(); mLastRecordFile = QStringLiteral("%1%2.mkv") .arg(coreManager->getSettingsModel()->getSavedCallsFolder()) .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss")); coreManager->getCore()->startConferenceRecording(Utils::appStringToCoreString(mLastRecordFile) ); mRecording = true; emit recordingChanged(true); } void ConferenceProxyModel::stopRecording () { if (!mRecording) return; qInfo() << QStringLiteral("Stop recording conference:") << this; mRecording = false; CoreManager::getInstance()->getCore()->stopConferenceRecording(); App::getInstance()->getNotifier()->notifyRecordingCompleted(mLastRecordFile); emit recordingChanged(false); } // ----------------------------------------------------------------------------- bool ConferenceProxyModel::getMicroMuted () const { return !CoreManager::getInstance()->getCore()->micEnabled(); } void ConferenceProxyModel::setMicroMuted (bool status) { shared_ptr core = CoreManager::getInstance()->getCore(); if (status == core->micEnabled()) { core->enableMic(!status); emit microMutedChanged(status); } } // ----------------------------------------------------------------------------- bool ConferenceProxyModel::getRecording () const { return mRecording; } // ----------------------------------------------------------------------------- float ConferenceProxyModel::getMicroVu () const { return MediastreamerUtils::computeVu( CoreManager::getInstance()->getCore()->getConferenceLocalInputVolume() ); } // ----------------------------------------------------------------------------- void ConferenceProxyModel::leave () { CoreManager::getInstance()->getCore()->leaveConference(); emit conferenceChanged(); } void ConferenceProxyModel::join () { CoreManager::getInstance()->getCore()->enterConference(); emit conferenceChanged(); } bool ConferenceProxyModel::isInConference () const { return CoreManager::getInstance()->getCore()->isInConference(); } linphone-desktop-5.0.2/linphone-app/src/components/conference/ConferenceProxyModel.hpp000066400000000000000000000040411434616504300312660ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONFERENCE_PROXY_MODEL_H_ #define CONFERENCE_PROXY_MODEL_H_ #include "app/proxyModel/SortFilterProxyModel.hpp" // ============================================================================= class CallModel; class ConferenceProxyModel : public SortFilterProxyModel { Q_OBJECT Q_PROPERTY(bool microMuted READ getMicroMuted WRITE setMicroMuted NOTIFY microMutedChanged); Q_PROPERTY(float microVu READ getMicroVu CONSTANT); Q_PROPERTY(bool recording READ getRecording NOTIFY recordingChanged); Q_PROPERTY(bool isInConf READ isInConference NOTIFY conferenceChanged); public: ConferenceProxyModel (QObject *parent = Q_NULLPTR); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; Q_INVOKABLE void terminate (); Q_INVOKABLE void startRecording (); Q_INVOKABLE void stopRecording (); Q_INVOKABLE void join (); Q_INVOKABLE void leave (); signals: void microMutedChanged (bool status); void recordingChanged (bool status); void conferenceChanged (); private: bool getMicroMuted () const; void setMicroMuted (bool status); float getMicroVu () const; bool isInConference () const; bool getRecording () const; bool mRecording = false; QString mLastRecordFile; }; #endif // CONFERENCE_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/conferenceInfo/000077500000000000000000000000001434616504300253005ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/conferenceInfo/ConferenceInfoListModel.cpp000066400000000000000000000112621434616504300325060ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ConferenceInfoListModel.hpp" #include #include #include #include "app/App.hpp" #include "components/conference/ConferenceAddModel.hpp" #include "components/conference/ConferenceHelperModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/Utils.hpp" #include "ConferenceInfoProxyModel.hpp" #include "ConferenceInfoModel.hpp" // ============================================================================= ConferenceInfoListModel::ConferenceInfoListModel (QObject *parent) : ProxyListModel(parent) { auto coreManager = CoreManager::getInstance(); connect(coreManager->getHandlers().get(), &CoreHandlers::conferenceInfoReceived, this, &ConferenceInfoListModel::onConferenceInfoReceived); auto conferenceInfos = coreManager->getCore()->getConferenceInformationList(); QList > items; for(auto conferenceInfo : conferenceInfos){ auto item = build(conferenceInfo, mBuildAll); if(item) items << item; } if(items.size() > 0) ProxyListModel::add(items); } // ----------------------------------------------------------------------------- QSharedPointer ConferenceInfoListModel::build(const std::shared_ptr & conferenceInfo, const bool& buildAll) const{ auto me = CoreManager::getInstance()->getCore()->getDefaultAccount()->getParams()->getIdentityAddress(); std::list> participants = conferenceInfo->getParticipants(); bool haveMe = buildAll || conferenceInfo->getOrganizer()->weakEqual(me); if(!haveMe) haveMe = (std::find_if(participants.begin(), participants.end(), [me](const std::shared_ptr& address){ return me->weakEqual(address); }) != participants.end()); if(haveMe){ auto conferenceModel = ConferenceInfoModel::create( conferenceInfo ); connect(conferenceModel.get(), &ConferenceInfoModel::removed, this, &ConferenceInfoListModel::onRemoved); return conferenceModel; }else return nullptr; } void ConferenceInfoListModel::add(const std::shared_ptr & conferenceInfo, const bool& sendEvents){ auto item = build(conferenceInfo, mBuildAll); if( item) ProxyListModel::add(item); } QHash ConferenceInfoListModel::roleNames () const{ QHash roles; roles[Qt::DisplayRole] = "$modelData"; roles[Qt::DisplayRole+1] = "$sectionDate"; return roles; } QVariant ConferenceInfoListModel::data (const QModelIndex &index, int role ) const{ int row = index.row(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); if (role == Qt::DisplayRole) return QVariant::fromValue(mList[row].get()); else if (role == Qt::DisplayRole +1 ) return QVariant::fromValue(mList[row].objectCast()->getDateTimeSystem().date()); return QVariant(); } QSharedPointer ConferenceInfoListModel::get(std::shared_ptr conferenceInfo) const{ auto uri = conferenceInfo->getUri(); for(auto item : mList){ auto model = item.objectCast(); auto dbConferenceInfo = model->getConferenceInfo(); if(dbConferenceInfo == conferenceInfo || dbConferenceInfo->getUri()->equal(uri)){ return model; } } return nullptr; } void ConferenceInfoListModel::onConferenceInfoReceived(const std::shared_ptr & conferenceInfo){ auto realConferenceInfo = ConferenceInfoModel::findConferenceInfo(conferenceInfo); if( realConferenceInfo){ auto model = get(realConferenceInfo); if(model) model->setConferenceInfo(realConferenceInfo); else add(realConferenceInfo); }else qWarning() << "No ConferenceInfo have beend found for " << conferenceInfo->getUri()->asString().c_str(); } void ConferenceInfoListModel::onRemoved(bool byUser){ remove(sender()); } linphone-desktop-5.0.2/linphone-app/src/components/conferenceInfo/ConferenceInfoListModel.hpp000066400000000000000000000043241434616504300325140ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef _CONFERENCE_INFO_LIST_MODEL_H_ #define _CONFERENCE_INFO_LIST_MODEL_H_ #include #include #include "app/proxyModel/ProxyAbstractListModel.hpp" #include "app/proxyModel/ProxyListModel.hpp" #include "app/proxyModel/SortFilterAbstractProxyModel.hpp" // ============================================================================= class ConferenceInfoModel; class ConferenceInfoListModel : public ProxyListModel { Q_OBJECT public: ConferenceInfoListModel (QObject *parent = Q_NULLPTR); QSharedPointer build(const std::shared_ptr & conferenceInfo, const bool& buildAll) const; void add(const std::shared_ptr & conferenceInfo, const bool& sendEvents = true); QHash roleNames () const override; virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; QSharedPointer get(std::shared_ptr conferenceInfo) const; void onConferenceInfoReceived(const std::shared_ptr & conferenceInfo); public slots: void onRemoved(bool byUser); signals: void filterTypeChanged(int filterType); private: bool mBuildAll = true; // Short term design choice : display all. As of 5.2.0 SDK and on cancel, there are no more more links between conference info and current account. }; Q_DECLARE_METATYPE(ConferenceInfoListModel*) #endif linphone-desktop-5.0.2/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.cpp000066400000000000000000000406151434616504300316560ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ConferenceInfoModel.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" #include "components/calls/CallsListModel.hpp" #include "components/chat-events/ChatCallModel.hpp" #include "components/chat-events/ChatEvent.hpp" #include "components/chat-events/ChatMessageModel.hpp" #include "components/chat-events/ChatNoticeModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/conferenceScheduler/ConferenceScheduler.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/notifier/Notifier.hpp" #include "components/other/timeZone/TimeZoneModel.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/participant/ParticipantModel.hpp" #include "components/participant/ParticipantListModel.hpp" #include "components/presence/Presence.hpp" #include "components/recorder/RecorderManager.hpp" #include "components/recorder/RecorderModel.hpp" #include "components/timeline/TimelineModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "components/core/event-count-notifier/AbstractEventCountNotifier.hpp" #include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "utils/LinphoneEnums.hpp" // ============================================================================= using namespace std; // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- QSharedPointer ConferenceInfoModel::create(std::shared_ptr conferenceInfo){ return QSharedPointer::create(conferenceInfo); } // Callable from QML ConferenceInfoModel::ConferenceInfoModel (QObject * parent) : QObject(parent){ mTimeZone = QTimeZone::systemTimeZone(); mConferenceInfo = linphone::Factory::get()->createConferenceInfo(); QDateTime currentDateTime = QDateTime::currentDateTime(); QDateTime utc = currentDateTime.addSecs( -mTimeZone.offsetFromUtc(currentDateTime)); mConferenceInfo->setDateTime(0); mConferenceInfo->setDuration(0); mIsScheduled = false; auto accountAddress = CoreManager::getInstance()->getCore()->getDefaultAccount()->getContactAddress(); if(accountAddress){ auto cleanedClonedAddress = accountAddress->clone(); cleanedClonedAddress->clean(); mConferenceInfo->setOrganizer(cleanedClonedAddress); } connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::timeZoneModelChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::dateTimeChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::durationChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::organizerChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::subjectChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::descriptionChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::participantsChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::uriChanged);// Useless but just in case. connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::isScheduledChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::inviteModeChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::conferenceInfoStateChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::conferenceSchedulerStateChanged); } // Callable from C++ ConferenceInfoModel::ConferenceInfoModel (std::shared_ptr conferenceInfo, QObject * parent) : QObject(parent){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mTimeZone = QTimeZone::systemTimeZone(); mConferenceInfo = conferenceInfo; mIsScheduled = (mConferenceInfo->getDateTime() != 0 || mConferenceInfo->getDuration() != 0); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::timeZoneModelChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::dateTimeChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::durationChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::organizerChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::subjectChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::descriptionChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::participantsChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::uriChanged);// Useless but just in case. connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::isScheduledChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::inviteModeChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::conferenceInfoStateChanged); connect(this, &ConferenceInfoModel::conferenceInfoChanged, this, &ConferenceInfoModel::conferenceSchedulerStateChanged); } ConferenceInfoModel::~ConferenceInfoModel () { } std::shared_ptr ConferenceInfoModel::getConferenceInfo(){ return mConferenceInfo; } std::shared_ptr ConferenceInfoModel::findConferenceInfo(const std::shared_ptr & conferenceInfo){ return CoreManager::getInstance()->getCore()->findConferenceInformationFromUri(conferenceInfo->getUri()->clone()); } //------------------------------------------------------------------------------------------------ //Note conferenceInfo->getDateTime uses system timezone and fromMSecsSinceEpoch need a UTC QDateTime ConferenceInfoModel::getDateTimeSystem() const{ QDateTime reference(QDateTime::fromMSecsSinceEpoch(mConferenceInfo->getDateTime() * 1000));// Get a reference for timezone offset computing qint64 utcMs = (mConferenceInfo->getDateTime() - QTimeZone::systemTimeZone().offsetFromUtc(reference)) * 1000;// Remove system timezone offset to get UTC return QDateTime::fromMSecsSinceEpoch(utcMs, QTimeZone::systemTimeZone()); // Return a System Timezone datetime based } QDateTime ConferenceInfoModel::getDateTimeUtc() const{ return getDateTimeSystem().toUTC(); } int ConferenceInfoModel::getDuration() const{ return mConferenceInfo->getDuration(); } QDateTime ConferenceInfoModel::getEndDateTime() const{ return getDateTimeUtc().addSecs(getDuration()*60); } QString ConferenceInfoModel::getOrganizer() const{ return Utils::coreStringToAppString(mConferenceInfo->getOrganizer()->asString()); } QString ConferenceInfoModel::getSubject() const{ return Utils::coreStringToAppString(mConferenceInfo->getSubject()); } QString ConferenceInfoModel::getDescription() const{ return Utils::coreStringToAppString(mConferenceInfo->getDescription()); } QString ConferenceInfoModel::displayNamesToString()const{ QStringList txt; for(auto participant : mConferenceInfo->getParticipants()){ if(participant){ QString displayName = Utils::getDisplayName(participant); if(displayName != "") txt << displayName; } } return txt.join(", "); } QString ConferenceInfoModel::getUri() const{ auto address = mConferenceInfo->getUri(); return address->isValid() && !address->getDomain().empty() ? QString::fromStdString(address->asStringUriOnly()) : ""; } bool ConferenceInfoModel::isScheduled() const{ return mIsScheduled; } int ConferenceInfoModel::getInviteMode() const{ return mInviteMode; } QVariantList ConferenceInfoModel::getParticipants() const{ QVariantList addresses; for(auto item : mConferenceInfo->getParticipants()){ QVariantMap participant; participant["displayName"] = Utils::getDisplayName(item); participant["address"] = QString::fromStdString(item->asStringUriOnly()); addresses << participant; } return addresses; } QVariantList ConferenceInfoModel::getAllParticipants() const{ QVariantList addresses = getParticipants(); QString organizerAddress = QString::fromStdString(mConferenceInfo->getOrganizer()->asStringUriOnly()); for(auto item : addresses){ if( item.toMap()["address"] == organizerAddress) return addresses; } QVariantMap participant; participant["displayName"] = Utils::getDisplayName(mConferenceInfo->getOrganizer()); participant["address"] = organizerAddress; addresses << participant; return addresses; } int ConferenceInfoModel::getParticipantCount()const{ return mConferenceInfo->getParticipants().size(); } int ConferenceInfoModel::getAllParticipantCount()const{ return getAllParticipants().size(); } TimeZoneModel* ConferenceInfoModel::getTimeZoneModel() const{ TimeZoneModel * model = new TimeZoneModel(mTimeZone); App::getInstance()->getEngine()->setObjectOwnership(model, QQmlEngine::JavaScriptOwnership); return model; } QString ConferenceInfoModel::getIcalendarString() const{ return Utils::coreStringToAppString(mConferenceInfo->getIcalendarString()); } LinphoneEnums::ConferenceInfoState ConferenceInfoModel::getConferenceInfoState() const{ return LinphoneEnums::fromLinphone(mConferenceInfo->getState()); } LinphoneEnums::ConferenceSchedulerState ConferenceInfoModel::getConferenceSchedulerState() const{ return LinphoneEnums::fromLinphone(mLastConferenceSchedulerState); } //------------------------------------------------------------------------------------------------ // Datetime is in Custom (Locale/UTC/System). Convert into system timezone for conference info void ConferenceInfoModel::setDateTime(const QDateTime& dateTime){ QDateTime system = dateTime.toTimeZone(QTimeZone::systemTimeZone());//System int offset = QTimeZone::systemTimeZone().offsetFromUtc(system);//Get UTC offset in system coordinate system = system.addSecs( offset - mTimeZone.offsetFromUtc(dateTime));// Delta on offsets mConferenceInfo->setDateTime(system.toMSecsSinceEpoch() / 1000 + offset);// toMSecsSinceEpoch() is UTC, add system reference. emit dateTimeChanged(); } void ConferenceInfoModel::setDuration(const int& duration){ mConferenceInfo->setDuration(duration); emit durationChanged(); } void ConferenceInfoModel::setSubject(const QString& subject){ mConferenceInfo->setSubject(Utils::appStringToCoreString(subject)); emit subjectChanged(); } void ConferenceInfoModel::setOrganizer(const QString& organizerAddress){ mConferenceInfo->setOrganizer(Utils::interpretUrl(organizerAddress)); emit organizerChanged(); } void ConferenceInfoModel::setDescription(const QString& description){ mConferenceInfo->setDescription(Utils::appStringToCoreString(description)); emit descriptionChanged(); } void ConferenceInfoModel::setParticipants(ParticipantListModel * participants){ mConferenceInfo->setParticipants(participants->getParticipants()); emit participantsChanged(); } void ConferenceInfoModel::setTimeZoneModel(TimeZoneModel * model){ if( mTimeZone != model->getTimeZone()){ mTimeZone = model->getTimeZone(); emit timeZoneModelChanged(); } } void ConferenceInfoModel::setIsScheduled(const bool& on){ if( mIsScheduled != on){ mIsScheduled = on; if(!mIsScheduled){ mConferenceInfo->setDateTime(0); mConferenceInfo->setDuration(0); }else{ mTimeZone = QTimeZone::systemTimeZone(); QDateTime currentDateTime = QDateTime::currentDateTime(); QDateTime utc = currentDateTime.addSecs( -mTimeZone.offsetFromUtc(currentDateTime)); mConferenceInfo->setDateTime(utc.toMSecsSinceEpoch() / 1000); mConferenceInfo->setDuration(1200); } emit dateTimeChanged(); emit durationChanged(); emit isScheduledChanged(); } } void ConferenceInfoModel::setInviteMode(const int& mode){ if( mode != mInviteMode){ mInviteMode = mode; emit inviteModeChanged(); } } void ConferenceInfoModel::setConferenceInfo(std::shared_ptr conferenceInfo){ mConferenceInfo = conferenceInfo; mIsScheduled = (mConferenceInfo->getDateTime() != 0 || mConferenceInfo->getDuration() != 0); emit conferenceInfoChanged(); } //------------------------------------------------------------------------------------------------- void ConferenceInfoModel::resetConferenceInfo() { mConferenceInfo = linphone::Factory::get()->createConferenceInfo(); mConferenceInfo->setDateTime(0); mConferenceInfo->setDuration(0); mIsScheduled = false; auto accountAddress = CoreManager::getInstance()->getCore()->getDefaultAccount()->getContactAddress(); if(accountAddress){ auto cleanedClonedAddress = accountAddress->clone(); cleanedClonedAddress->clean(); mConferenceInfo->setOrganizer(cleanedClonedAddress); } } void ConferenceInfoModel::createConference(const int& securityLevel) { CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = false; shared_ptr core = CoreManager::getInstance()->getCore(); static std::shared_ptr conference; qInfo() << "Conference creation of " << getSubject() << " at " << securityLevel << " security, organized by " << getOrganizer() << " for " << getDateTimeSystem().toString(); qInfo() << "Participants:"; for(auto p : mConferenceInfo->getParticipants()) qInfo() << "\t" << p->asString().c_str(); mConferenceScheduler = ConferenceScheduler::create(); mConferenceScheduler->mSendInvite = mInviteMode; connect(mConferenceScheduler.get(), &ConferenceScheduler::invitationsSent, this, &ConferenceInfoModel::onInvitationsSent); connect(mConferenceScheduler.get(), &ConferenceScheduler::stateChanged, this, &ConferenceInfoModel::onConferenceSchedulerStateChanged); mConferenceScheduler->getConferenceScheduler()->setInfo(mConferenceInfo); } void ConferenceInfoModel::cancelConference(){ mConferenceScheduler = ConferenceScheduler::create(); connect(mConferenceScheduler.get(), &ConferenceScheduler::invitationsSent, this, &ConferenceInfoModel::onInvitationsSent); connect(mConferenceScheduler.get(), &ConferenceScheduler::stateChanged, this, &ConferenceInfoModel::onConferenceSchedulerStateChanged); mConferenceScheduler->getConferenceScheduler()->cancelConference(mConferenceInfo); } void ConferenceInfoModel::deleteConferenceInfo(){ if(mConferenceInfo) { CoreManager::getInstance()->getCore()->deleteConferenceInformation(mConferenceInfo); emit removed(true); } } //------------------------------------------------------------------------------------------------- void ConferenceInfoModel::onConferenceSchedulerStateChanged(linphone::ConferenceScheduler::State state){ qDebug() << "ConferenceInfoModel::onConferenceSchedulerStateChanged: " << (int) state; mLastConferenceSchedulerState = state; if( state == linphone::ConferenceScheduler::State::Ready) emit conferenceCreated(); else if( state == linphone::ConferenceScheduler::State::Error) emit conferenceCreationFailed(); emit conferenceInfoChanged(); } void ConferenceInfoModel::onInvitationsSent(const std::list> & failedInvitations) { qDebug() << "ConferenceInfoModel::onInvitationsSent"; emit invitationsSent(); } linphone-desktop-5.0.2/linphone-app/src/components/conferenceInfo/ConferenceInfoModel.hpp000066400000000000000000000137261434616504300316660ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONFERENCE_INFO_MODEL_H_ #define CONFERENCE_INFO_MODEL_H_ #include #include #include #include #include #include "utils/LinphoneEnums.hpp" class ParticipantListModel; class ConferenceScheduler; class TimeZoneModel; class ConferenceInfoModel : public QObject { Q_OBJECT public: Q_PROPERTY(TimeZoneModel * timeZoneModel READ getTimeZoneModel WRITE setTimeZoneModel NOTIFY timeZoneModelChanged) Q_PROPERTY(QDateTime dateTime READ getDateTimeSystem WRITE setDateTime NOTIFY dateTimeChanged) Q_PROPERTY(QDateTime dateTimeUtc READ getDateTimeUtc NOTIFY dateTimeChanged) Q_PROPERTY(int duration READ getDuration WRITE setDuration NOTIFY durationChanged) Q_PROPERTY(QDateTime endDateTime READ getEndDateTime NOTIFY dateTimeChanged) Q_PROPERTY(QString organizer READ getOrganizer WRITE setOrganizer NOTIFY organizerChanged) Q_PROPERTY(QString subject READ getSubject WRITE setSubject NOTIFY subjectChanged) Q_PROPERTY(QString description READ getDescription WRITE setDescription NOTIFY descriptionChanged) Q_PROPERTY(QString displayNamesToString READ displayNamesToString NOTIFY participantsChanged) Q_PROPERTY(QString uri READ getUri NOTIFY uriChanged) Q_PROPERTY(bool isScheduled READ isScheduled WRITE setIsScheduled NOTIFY isScheduledChanged) Q_PROPERTY(int inviteMode READ getInviteMode WRITE setInviteMode NOTIFY inviteModeChanged) Q_PROPERTY(int participantCount READ getParticipantCount NOTIFY participantsChanged) Q_PROPERTY(int allParticipantCount READ getAllParticipantCount NOTIFY participantsChanged) Q_PROPERTY(LinphoneEnums::ConferenceInfoState state READ getConferenceInfoState NOTIFY conferenceInfoStateChanged) Q_PROPERTY(LinphoneEnums::ConferenceSchedulerState conferenceSchedulerState READ getConferenceSchedulerState NOTIFY conferenceSchedulerStateChanged) static QSharedPointer create(std::shared_ptr conferenceInfo); ConferenceInfoModel (QObject * parent = nullptr); ConferenceInfoModel (std::shared_ptr conferenceInfo, QObject * parent = nullptr); ~ConferenceInfoModel (); std::shared_ptr getConferenceInfo(); static std::shared_ptr findConferenceInfo(const std::shared_ptr & conferenceInfo); //------------------------------- QDateTime getDateTimeUtc() const; QDateTime getDateTimeSystem() const; int getDuration() const; QDateTime getEndDateTime() const; QString getOrganizer() const; QString getSubject() const; QString getDescription() const; Q_INVOKABLE QString displayNamesToString()const; QString getUri() const; bool isScheduled() const; int getInviteMode() const; Q_INVOKABLE QVariantList getParticipants() const; Q_INVOKABLE QVariantList getAllParticipants() const; Q_INVOKABLE int getParticipantCount()const; Q_INVOKABLE int getAllParticipantCount()const; Q_INVOKABLE TimeZoneModel* getTimeZoneModel() const; Q_INVOKABLE QString getIcalendarString() const; LinphoneEnums::ConferenceInfoState getConferenceInfoState() const; LinphoneEnums::ConferenceSchedulerState getConferenceSchedulerState() const; void setDateTime(const QDateTime& dateTime); void setDuration(const int& duration); void setSubject(const QString& subject); void setOrganizer(const QString& organizerAddress); void setDescription(const QString& description); void setIsScheduled(const bool& on); void setInviteMode(const int& modes); Q_INVOKABLE void setParticipants(ParticipantListModel * participants); Q_INVOKABLE void setTimeZoneModel(TimeZoneModel * model); void setConferenceInfo(std::shared_ptr conferenceInfo); // Tools Q_INVOKABLE void resetConferenceInfo();// Recreate a new conference info from factory Q_INVOKABLE void createConference(const int& securityLevel); Q_INVOKABLE void cancelConference(); Q_INVOKABLE void deleteConferenceInfo();// Remove completly this conference info from DB // SCHEDULER virtual void onConferenceSchedulerStateChanged(linphone::ConferenceScheduler::State state); virtual void onInvitationsSent(const std::list> & failedInvitations); signals: void timeZoneModelChanged(); void dateTimeChanged(); void durationChanged(); void organizerChanged(); void subjectChanged(); void descriptionChanged(); void participantsChanged(); void uriChanged(); void isScheduledChanged(); void inviteModeChanged(); void conferenceInfoStateChanged(); void conferenceSchedulerStateChanged(); void conferenceCreated(); void conferenceCreationFailed(); void conferenceInfoChanged(); void invitationsSent(); void removed(bool byUser); private: std::shared_ptr mConferenceInfo; QSharedPointer mConferenceScheduler= nullptr; QTimeZone mTimeZone; bool mIsScheduled = true; int mInviteMode = 0; bool mRemoveRequested = false;// true if user has request its deletion from DB linphone::ConferenceScheduler::State mLastConferenceSchedulerState = linphone::ConferenceScheduler::State::Idle;// Workaround for missing getter in scheduler. }; Q_DECLARE_METATYPE(QSharedPointer) Q_DECLARE_METATYPE(ConferenceInfoModel*) #endif linphone-desktop-5.0.2/linphone-app/src/components/conferenceInfo/ConferenceInfoProxyModel.cpp000066400000000000000000000063411434616504300327160ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ConferenceInfoProxyModel.hpp" #include "components/call/CallModel.hpp" #include "components/core/CoreManager.hpp" #include "components/core/CoreHandlers.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "ConferenceInfoListModel.hpp" #include "utils/Utils.hpp" // ============================================================================= using namespace std; //--------------------------------------------------------------------------------------------- ConferenceInfoProxyModel::ConferenceInfoProxyModel (QObject *parent) : SortFilterAbstractProxyModel(new ConferenceInfoListModel(parent), parent) { connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::primarySipAddressChanged, this, &ConferenceInfoProxyModel::update); //connect(this, &ConferenceInfoProxyModel::filterTypeChanged, qobject_cast(sourceModel()), &ConferenceInfoListModel); setFilterType((int)Scheduled); } void ConferenceInfoProxyModel::update(){ int oldFilter = getFilterType(); SortFilterAbstractProxyModel::update(new ConferenceInfoListModel(parent())); setFilterType(oldFilter+1); //connect(this, &ConferenceInfoProxyModel::filterTypeChanged, qobject_cast(sourceModel()), &ConferenceInfoListModel::filterTypeChanged); setFilterType(oldFilter); } bool ConferenceInfoProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { auto listModel = qobject_cast(sourceModel()); if(listModel){ QModelIndex index = listModel->index(sourceRow, 0, QModelIndex()); const ConferenceInfoModel* ics = sourceModel()->data(index).value(); if(ics){ if(ics->getDuration() == 0) return false; QDateTime currentDateTime = QDateTime::currentDateTime(); if( mFilterType == 0){ return ics->getEndDateTime() < currentDateTime; }else if( mFilterType == 1){ return ics->getEndDateTime() >= currentDateTime; }else if( mFilterType == 2){ return !Utils::isMe(ics->getOrganizer()); }else return mFilterType == -1; } } return false; } bool ConferenceInfoProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const ConferenceInfoModel* a = sourceModel()->data(left).value(); const ConferenceInfoModel* b = sourceModel()->data(right).value(); return a->getDateTimeUtc() < b->getDateTimeUtc(); } linphone-desktop-5.0.2/linphone-app/src/components/conferenceInfo/ConferenceInfoProxyModel.hpp000066400000000000000000000035261434616504300327250ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONFERENCE_INFO_PROXY_MODEL_H_ #define CONFERENCE_INFO_PROXY_MODEL_H_ #include #include #include "ConferenceInfoModel.hpp" #include "app/proxyModel/SortFilterAbstractProxyModel.hpp" // ============================================================================= class QWindow; class ConferenceInfoListModel; class ConferenceInfoProxyModel : public SortFilterAbstractProxyModel { class ChatRoomModelFilter; Q_OBJECT public: enum ConferenceType { Ended, Scheduled, Invitations }; Q_ENUM(ConferenceType) ConferenceInfoProxyModel (QObject *parent = Q_NULLPTR); Q_INVOKABLE void update(); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; private: ConferenceInfoModel *getConferenceInfoModel() const; void setConferenceInfoModel (ConferenceInfoModel *conferenceInfoModel); QSharedPointer mConferenceInfoModel; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/conferenceScheduler/000077500000000000000000000000001434616504300263235ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/conferenceScheduler/ConferenceScheduler.cpp000066400000000000000000000070331434616504300327400ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ConferenceScheduler.hpp" #include "ConferenceSchedulerListener.hpp" #include #include "app/App.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/settings/SettingsModel.hpp" void ConferenceScheduler::connectTo(ConferenceSchedulerListener * listener){ connect(listener, &ConferenceSchedulerListener::stateChanged, this, &ConferenceScheduler::onStateChanged); connect(listener, &ConferenceSchedulerListener::invitationsSent, this, &ConferenceScheduler::onInvitationsSent); } // ============================================================================= QSharedPointer ConferenceScheduler::create( QObject *parent){ return QSharedPointer::create(parent); } ConferenceScheduler::ConferenceScheduler (QObject * parent) : QObject(parent){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mConferenceScheduler = CoreManager::getInstance()->getCore()->createConferenceScheduler(); qDebug() << "Create Scheduler with this account : " << CoreManager::getInstance()->getCore()->getDefaultAccount()->getContactAddress()->asString().c_str(); mConferenceScheduler->setAccount(CoreManager::getInstance()->getCore()->getDefaultAccount()); mConferenceSchedulerListener = std::make_shared(); connectTo(mConferenceSchedulerListener.get()); mConferenceScheduler->addListener(mConferenceSchedulerListener); } ConferenceScheduler::~ConferenceScheduler () { mConferenceScheduler->removeListener(mConferenceSchedulerListener); } std::shared_ptr ConferenceScheduler::getConferenceScheduler(){ return mConferenceScheduler; } void ConferenceScheduler::onStateChanged(linphone::ConferenceScheduler::State state) { qDebug() << "ConferenceScheduler::onStateChanged : " << (int)state; emit stateChanged(state); if( state == linphone::ConferenceScheduler::State::Ready) { auto conferenceInfo = mConferenceScheduler->getInfo(); emit CoreManager::getInstance()->getHandlers()->conferenceInfoReceived(conferenceInfo); if( (mSendInvite & 1) == 1){ std::shared_ptr params = CoreManager::getInstance()->getCore()->createDefaultChatRoomParams(); params->enableGroup(false); params->setSubject(conferenceInfo->getSubject()); params->enableEncryption(CoreManager::getInstance()->getSettingsModel()->getSecureChatEnabled()); mConferenceScheduler->sendInvitations(params); } } } void ConferenceScheduler::onInvitationsSent( const std::list> & failedInvitations) { qDebug() << "ConferenceScheduler::onInvitationsSent"; emit invitationsSent(failedInvitations); } linphone-desktop-5.0.2/linphone-app/src/components/conferenceScheduler/ConferenceScheduler.hpp000066400000000000000000000037021434616504300327440ustar00rootroot00000000000000/* * Copyright (c) 2021-2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONFERENCE_SCHEDULER_H_ #define CONFERENCE_SCHEDULER_H_ #include #include #include class ConferenceSchedulerListener; class ConferenceScheduler : public QObject { Q_OBJECT public: static QSharedPointer create(QObject *parent = Q_NULLPTR); ConferenceScheduler (QObject * parent = nullptr); virtual ~ConferenceScheduler (); std::shared_ptr getConferenceScheduler(); virtual void onStateChanged(linphone::ConferenceScheduler::State state); virtual void onInvitationsSent(const std::list> & failedInvitations); int mSendInvite = 1;// TODO : Enum for app = 1, email=2. Both = 3 signals: void stateChanged(linphone::ConferenceScheduler::State state); void invitationsSent(const std::list> & failedInvitations); private: void connectTo(ConferenceSchedulerListener * listener); std::shared_ptr mConferenceScheduler; std::shared_ptr mConferenceSchedulerListener; }; Q_DECLARE_METATYPE(QSharedPointer) #endif ConferenceSchedulerListener.cpp000066400000000000000000000033371434616504300343720ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/conferenceScheduler/* * Copyright (c) 2021-2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ConferenceSchedulerListener.hpp" #include #include "app/App.hpp" #include "components/core/CoreManager.hpp" // ============================================================================= ConferenceSchedulerListener::ConferenceSchedulerListener () : QObject(nullptr){ } ConferenceSchedulerListener::~ConferenceSchedulerListener () { } void ConferenceSchedulerListener::onStateChanged(const std::shared_ptr & conferenceScheduler, linphone::ConferenceScheduler::State state) { qDebug() << "ConferenceSchedulerListener::onStateChanged" << (int) state; emit stateChanged(state); } void ConferenceSchedulerListener::onInvitationsSent(const std::shared_ptr & conferenceScheduler, const std::list> & failedInvitations) { qDebug() << "ConferenceSchedulerListener::onInvitationsSent"; emit invitationsSent(failedInvitations); } ConferenceSchedulerListener.hpp000066400000000000000000000032201434616504300343660ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/conferenceScheduler/* * Copyright (c) 2021-2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONFERENCE_SCHEDULER_LISTENER_H_ #define CONFERENCE_SCHEDULER_LISTENER_H_ #include #include #include class ConferenceSchedulerListener : public QObject, public linphone::ConferenceSchedulerListener { Q_OBJECT public: ConferenceSchedulerListener(); virtual ~ConferenceSchedulerListener(); virtual void onStateChanged(const std::shared_ptr & conferenceScheduler, linphone::ConferenceScheduler::State state) override; virtual void onInvitationsSent(const std::shared_ptr & conferenceScheduler, const std::list> & failedInvitations) override; signals: void stateChanged(linphone::ConferenceScheduler::State state); void invitationsSent(const std::list> & failedInvitations); }; #endif linphone-desktop-5.0.2/linphone-app/src/components/contact/000077500000000000000000000000001434616504300240105ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/contact/ContactModel.cpp000066400000000000000000000154311434616504300270740ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "ContactModel.hpp" #include "VcardModel.hpp" // ============================================================================= using namespace std; ContactModel::ContactModel (shared_ptr linphoneFriend, QObject * parent) : QObject(parent) { Q_CHECK_PTR(linphoneFriend); mLinphoneFriend = linphoneFriend; mLinphoneFriend->setData("contact-model", *this); setVcardModelInternal(new VcardModel(linphoneFriend->getVcard())); } ContactModel::ContactModel (VcardModel *vcardModel, QObject * parent) : QObject(parent) { Q_CHECK_PTR(vcardModel); Q_CHECK_PTR(vcardModel->mVcard); Q_ASSERT(!vcardModel->mIsReadOnly); mLinphoneFriend = linphone::Friend::newFromVcard(vcardModel->mVcard); mLinphoneFriend->setData("contact-model", *this); qInfo() << QStringLiteral("Create contact from vcard:") << this << vcardModel; setVcardModelInternal(vcardModel); } ContactModel::~ContactModel(){ mVcardModel = nullptr; mLinphoneFriend = nullptr; } // ----------------------------------------------------------------------------- void ContactModel::refreshPresence () { Presence::PresenceStatus status = static_cast( mLinphoneFriend->getConsolidatedPresence() ); emit presenceStatusChanged(status); emit presenceLevelChanged(Presence::getPresenceLevel(status)); } // ----------------------------------------------------------------------------- VcardModel *ContactModel::getVcardModel () const { return mVcardModel; } // ----------------------------------------------------------------------------- void ContactModel::setVcardModel (VcardModel *vcardModel) { VcardModel *oldVcardModel = mVcardModel; qInfo() << QStringLiteral("Remove vcard on contact:") << this << oldVcardModel; oldVcardModel->mIsReadOnly = false; oldVcardModel->mAvatarIsReadOnly = vcardModel->getAvatar() == oldVcardModel->getAvatar(); oldVcardModel->deleteLater(); qInfo() << QStringLiteral("Set vcard on contact:") << this << vcardModel; setVcardModelInternal(vcardModel); // Flush vcard. mLinphoneFriend->done(); updateSipAddresses(oldVcardModel); } void ContactModel::setVcardModelInternal (VcardModel *vcardModel) { Q_CHECK_PTR(vcardModel); Q_ASSERT(vcardModel != mVcardModel); mVcardModel = vcardModel; mVcardModel->mAvatarIsReadOnly = false; mVcardModel->mIsReadOnly = true; App::getInstance()->getEngine()->setObjectOwnership(mVcardModel, QQmlEngine::CppOwnership); mVcardModel->setParent(this); if (mLinphoneFriend->getVcard() != vcardModel->mVcard) mLinphoneFriend->setVcard(vcardModel->mVcard); } void ContactModel::updateSipAddresses (VcardModel *oldVcardModel) { Q_CHECK_PTR(oldVcardModel); QVariantList oldSipAddresses = oldVcardModel->getSipAddresses(); QVariantList sipAddresses = mVcardModel->getSipAddresses(); QSet done; for (const auto &variantA : oldSipAddresses) { next: const QString sipAddress = variantA.toString(); if (done.contains(sipAddress)) continue; done.insert(sipAddress); // Check if old sip address exists in new set => No changes. for (const auto &variantB : sipAddresses) { if (sipAddress == variantB.toString()) goto next; } emit sipAddressRemoved(sipAddress); } oldSipAddresses.clear(); for (const auto &variant : sipAddresses) { const QString sipAddress = variant.toString(); if (done.contains(sipAddress)) continue; done.insert(sipAddress); emit sipAddressAdded(sipAddress); } emit contactUpdated(); } // ----------------------------------------------------------------------------- void ContactModel::mergeVcardModel (VcardModel *vcardModel) { Q_CHECK_PTR(vcardModel); qInfo() << QStringLiteral("Merge vcard into contact:") << this << vcardModel; // 1. Merge avatar. if (vcardModel->getAvatar().isEmpty()) vcardModel->setAvatar(mVcardModel->getAvatar()); // 2. Merge sip addresses, companies, emails and urls. for (const auto &sipAddress : mVcardModel->getSipAddresses()) vcardModel->addSipAddress(sipAddress.toString()); for (const auto &company : mVcardModel->getCompanies()) vcardModel->addCompany(company.toString()); for (const auto &email : mVcardModel->getEmails()) vcardModel->addEmail(email.toString()); for (const auto &url : mVcardModel->getUrls()) vcardModel->addUrl(url.toString()); // 3. Merge address. { const QVariantMap oldAddress = vcardModel->getAddress(); QVariantMap newAddress = vcardModel->getAddress(); constexpr const char *attributes[4] = { "street", "locality", "postalCode", "country" }; bool needMerge = true; for (const auto &attribute : attributes) if (!newAddress[attribute].toString().isEmpty()) { needMerge = false; break; } if (needMerge) { for (const auto &attribute : attributes) newAddress[attribute] = oldAddress[attribute]; } } setVcardModel(vcardModel); } // ----------------------------------------------------------------------------- VcardModel *ContactModel::cloneVcardModel () const { shared_ptr vcard = mVcardModel->mVcard->clone(); Q_CHECK_PTR(vcard); Q_CHECK_PTR(vcard->getVcard()); mLinphoneFriend->edit(); VcardModel *vcardModel = new VcardModel(vcard); vcardModel->mIsReadOnly = false; qInfo() << QStringLiteral("Clone vcard from contact:") << this << vcardModel; return vcardModel; } // ----------------------------------------------------------------------------- Presence::PresenceStatus ContactModel::getPresenceStatus () const { return static_cast(mLinphoneFriend->getConsolidatedPresence()); } Presence::PresenceLevel ContactModel::getPresenceLevel () const { return Presence::getPresenceLevel(getPresenceStatus()); } bool ContactModel::hasCapability(const LinphoneEnums::FriendCapability& capability){ return mLinphoneFriend->hasCapability(LinphoneEnums::toLinphone(capability)); } std::shared_ptr ContactModel::getFriend() const{ return mLinphoneFriend; }linphone-desktop-5.0.2/linphone-app/src/components/contact/ContactModel.hpp000066400000000000000000000053771434616504300271110ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTACT_MODEL_H_ #define CONTACT_MODEL_H_ #include "components/presence/Presence.hpp" #include "utils/LinphoneEnums.hpp" #include // ============================================================================= class VcardModel; class ContactModel : public QObject { // Grant access to `mLinphoneFriend`. friend class ContactsListModel; friend class ContactsListProxyModel; friend class SipAddressesProxyModel; friend class SipAddressesSorter; Q_OBJECT Q_PROPERTY(Presence::PresenceStatus presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged) Q_PROPERTY(Presence::PresenceLevel presenceLevel READ getPresenceLevel NOTIFY presenceLevelChanged) Q_PROPERTY(VcardModel * vcard READ getVcardModel WRITE setVcardModel NOTIFY contactUpdated) public: ContactModel ( std::shared_ptr linphoneFriend, QObject * parent = nullptr); ContactModel (VcardModel *vcardModel, QObject * parent = nullptr); virtual ~ContactModel(); void refreshPresence (); VcardModel *getVcardModel () const; void setVcardModel (VcardModel *vcardModel); void mergeVcardModel (VcardModel *vcardModel); Q_INVOKABLE VcardModel *cloneVcardModel () const; Presence::PresenceLevel getPresenceLevel () const; Q_INVOKABLE bool hasCapability(const LinphoneEnums::FriendCapability& capability); std::shared_ptr getFriend() const; signals: void contactUpdated (); void presenceStatusChanged (Presence::PresenceStatus status); void presenceLevelChanged (Presence::PresenceLevel level); void sipAddressAdded (const QString &sipAddress); void sipAddressRemoved (const QString &sipAddress); private: void setVcardModelInternal (VcardModel *vcardModel); void updateSipAddresses (VcardModel *oldVcardModel); Presence::PresenceStatus getPresenceStatus () const; VcardModel *mVcardModel = nullptr; std::shared_ptr mLinphoneFriend; }; Q_DECLARE_METATYPE(ContactModel *); #endif // CONTACT_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/contact/VcardModel.cpp000066400000000000000000000371641434616504300265470ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "app/providers/AvatarProvider.hpp" #include "components/core/CoreManager.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "VcardModel.hpp" // ============================================================================= using namespace std; #define CHECK_VCARD_IS_WRITABLE(VCARD) Q_ASSERT(VCARD->mIsReadOnly == false) template static inline shared_ptr findBelCardValue (const list> &list, const string &value) { auto it = find_if(list.cbegin(), list.cend(), [&value](const shared_ptr &entry) { return value == entry->getValue(); }); return it != list.cend() ? *it : nullptr; } template static inline shared_ptr findBelCardValue (const list> &list, const QString &value) { return findBelCardValue(list, value.toStdString()); } static inline bool isLinphoneDesktopPhoto (const shared_ptr &photo) { return !photo->getValue().compare(0, sizeof(Constants::VcardScheme) - 1, Constants::VcardScheme); } static shared_ptr findBelcardPhoto (const shared_ptr &belcard) { const list> &photos = belcard->getPhotos(); auto it = find_if(photos.cbegin(), photos.cend(), isLinphoneDesktopPhoto); if (it != photos.cend()) return *it; return nullptr; } static void removeBelcardPhoto (const shared_ptr &belcard, bool cleanPathsOnly = false) { list> photos; for (const auto photo : belcard->getPhotos()) { if (isLinphoneDesktopPhoto(photo)) photos.push_back(photo); } for (const auto photo : photos) { QString imagePath( QString::fromStdString( Paths::getAvatarsDirPath() + photo->getValue().substr(sizeof(Constants::VcardScheme) - 1) ) ); if (!cleanPathsOnly) { if (!QFile::remove(imagePath)) qWarning() << QStringLiteral("Unable to remove `%1`.").arg(imagePath); else qInfo() << QStringLiteral("Remove `%1`.").arg(imagePath); } belcard->removePhoto(photo); } } // ----------------------------------------------------------------------------- VcardModel::VcardModel (shared_ptr vcard, bool isReadOnly) : QObject(nullptr) { Q_CHECK_PTR(vcard); mVcard = vcard; mIsReadOnly = isReadOnly; } VcardModel::~VcardModel () { if (!mIsReadOnly) { qInfo() << QStringLiteral("Destroy detached vcard:") << this; if (!mAvatarIsReadOnly) removeBelcardPhoto(mVcard->getVcard()); } else qInfo() << QStringLiteral("Destroy attached vcard:") << this; } // ----------------------------------------------------------------------------- QString VcardModel::getAvatar () const { // Find desktop avatar. shared_ptr photo = findBelcardPhoto(mVcard->getVcard()); // No path found. if (!photo) return QString(""); // Returns right path. return QStringLiteral("image://%1/%2").arg(AvatarProvider::ProviderId).arg( QString::fromStdString(photo->getValue().substr(sizeof(Constants::VcardScheme) - 1)) ); } static inline QString getFileIdFromAppPath (const QString &path) { const static QString appPrefix = QStringLiteral("image://%1/").arg(AvatarProvider::ProviderId); return path.mid(appPrefix.length()); } bool VcardModel::setAvatar (const QString &path) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr belcard = mVcard->getVcard(); QString fileId; QFile file; // 1. Try to copy photo in avatars folder if it's a right path file and // not an application path like `image:`. if (!path.isEmpty()) { if (path.startsWith("image:")) fileId = getFileIdFromAppPath(path); else { file.setFileName(path); if (!file.exists() || QImageReader::imageFormat(path).size() == 0) return false; QFileInfo info(file); QString uuid = QUuid::createUuid().toString(); fileId = QStringLiteral("%1.%2") .arg(uuid.mid(1, uuid.length() - 2)) // Remove `{}`. .arg(info.suffix()); QString dest = QString::fromStdString(Paths::getAvatarsDirPath()) + fileId; if (!file.copy(dest)) return false; qInfo() << QStringLiteral("Update avatar of `%1`. (path=%2)").arg(getUsername()).arg(dest); } } // 2. Remove oldest photo. removeBelcardPhoto(belcard, mAvatarIsReadOnly); mAvatarIsReadOnly = false; // 3. Update new photo. if (!path.isEmpty()) { shared_ptr photo = belcard::BelCardGeneric::create(); photo->setValue(Constants::VcardScheme + fileId.toStdString()); if (!belcard->addPhoto(photo)) { file.remove(); return false; } } emit vcardUpdated(); return true; } // ----------------------------------------------------------------------------- QString VcardModel::getUsername () const { return decode(QString::fromStdString(mVcard->getFullName()));// Is in UTF8 } void VcardModel::setUsername (const QString &username) { CHECK_VCARD_IS_WRITABLE(this); if (username.length() == 0 || username == getUsername()) return; mVcard->setFullName(encode(username).toStdString()); emit vcardUpdated(); } // ----------------------------------------------------------------------------- static inline shared_ptr getOrCreateBelCardAddress (shared_ptr belcard) { list> addresses = belcard->getAddresses(); shared_ptr address; if (addresses.empty()) { address = belcard::BelCardGeneric::create(); if (!belcard->addAddress(address)) qWarning() << "Unable to create a new address on vcard."; } else address = addresses.front(); return address; } QVariantMap VcardModel::getAddress () const { list> addresses = mVcard->getVcard()->getAddresses(); QVariantMap map; if (addresses.empty()) return map; shared_ptr address = addresses.front(); map["street"] = QString::fromStdString(address->getStreet()); map["locality"] = QString::fromStdString(address->getLocality()); map["postalCode"] = QString::fromStdString(address->getPostalCode()); map["country"] = QString::fromStdString(address->getCountry()); return map; } void VcardModel::setStreet (const QString &street) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr address = getOrCreateBelCardAddress(mVcard->getVcard()); address->setStreet(street.toStdString()); emit vcardUpdated(); } void VcardModel::setLocality (const QString &locality) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr address = getOrCreateBelCardAddress(mVcard->getVcard()); address->setLocality(locality.toStdString()); emit vcardUpdated(); } void VcardModel::setPostalCode (const QString &postalCode) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr address = getOrCreateBelCardAddress(mVcard->getVcard()); address->setPostalCode(postalCode.toStdString()); emit vcardUpdated(); } void VcardModel::setCountry (const QString &country) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr address = getOrCreateBelCardAddress(mVcard->getVcard()); address->setCountry(country.toStdString()); emit vcardUpdated(); } // ----------------------------------------------------------------------------- QVariantList VcardModel::getSipAddresses () const { QVariantList list; if(mVcard->getVcard()){ shared_ptr core = CoreManager::getInstance()->getCore(); for (const auto &address : mVcard->getVcard()->getImpp()) { string value = address->getValue(); shared_ptr linphoneAddress = core->createAddress(value); if (linphoneAddress) list << Utils::coreStringToAppString(linphoneAddress->asStringUriOnly()); else qWarning() << QStringLiteral("Unable to parse sip address: `%1`") .arg(QString::fromStdString(value)); } } return list; } bool VcardModel::addSipAddress (const QString &sipAddress) { CHECK_VCARD_IS_WRITABLE(this); string interpretedSipAddress = SipAddressesModel::interpretSipAddress(sipAddress).toStdString(); if (interpretedSipAddress.empty()) return false; // Add sip address in belcard. shared_ptr belcard = mVcard->getVcard(); if (findBelCardValue(belcard->getImpp(), interpretedSipAddress)) return false; shared_ptr value = belcard::BelCardGeneric::create(); value->setValue(interpretedSipAddress); if (!belcard->addImpp(value)) { qWarning() << QStringLiteral("Unable to add sip address on vcard: `%1`.").arg(sipAddress); return false; } qInfo() << QStringLiteral("Add new sip address on vcard: `%1` from `%2`.").arg(QString::fromStdString(interpretedSipAddress)).arg(sipAddress); emit vcardUpdated(); return true; } void VcardModel::removeSipAddress (const QString &sipAddress) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr belcard = mVcard->getVcard(); list> addresses = belcard->getImpp(); shared_ptr value = findBelCardValue( addresses, SipAddressesModel::interpretSipAddress(sipAddress).toStdString() ); if (!value) { qWarning() << QStringLiteral("Unable to remove sip address on vcard: `%1`.").arg(sipAddress); return; } if (addresses.size() == 1) { qWarning() << QStringLiteral("Unable to remove the only existing sip address on vcard: `%1`.") .arg(sipAddress); return; } qInfo() << QStringLiteral("Remove sip address on vcard: `%1`.").arg(sipAddress); belcard->removeImpp(value); emit vcardUpdated(); } bool VcardModel::updateSipAddress (const QString &oldSipAddress, const QString &sipAddress) { bool soFarSoGood = addSipAddress(sipAddress); removeSipAddress(oldSipAddress); // Remove after. Avoid `Unable to remove the only sip address...` error. return soFarSoGood; } // ----------------------------------------------------------------------------- QVariantList VcardModel::getCompanies () const { QVariantList list; for (const auto &company : mVcard->getVcard()->getRoles()) list.append(QString::fromStdString(company->getValue())); return list; } bool VcardModel::addCompany (const QString &company) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr belcard = mVcard->getVcard(); if (findBelCardValue(belcard->getRoles(), company)) return false; shared_ptr value = belcard::BelCardGeneric::create(); value->setValue(company.toStdString()); if (!belcard->addRole(value)) { qWarning() << QStringLiteral("Unable to add company on vcard: `%1`.").arg(company); return false; } qInfo() << QStringLiteral("Add new company on vcard: `%1`.").arg(company); emit vcardUpdated(); return true; } void VcardModel::removeCompany (const QString &company) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr belcard = mVcard->getVcard(); shared_ptr value = findBelCardValue(belcard->getRoles(), company); if (!value) { qWarning() << QStringLiteral("Unable to remove company on vcard: `%1`.").arg(company); return; } qInfo() << QStringLiteral("Remove company on vcard: `%1`.").arg(company); belcard->removeRole(value); emit vcardUpdated(); } bool VcardModel::updateCompany (const QString &oldCompany, const QString &company) { removeCompany(oldCompany); return addCompany(company); } // ----------------------------------------------------------------------------- QVariantList VcardModel::getEmails () const { QVariantList list; for (const auto &email : mVcard->getVcard()->getEmails()) list.append(QString::fromStdString(email->getValue())); return list; } bool VcardModel::addEmail (const QString &email) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr belcard = mVcard->getVcard(); if (findBelCardValue(belcard->getEmails(), email)) return false; shared_ptr value = belcard::BelCardGeneric::create(); value->setValue(email.toStdString()); if (!belcard->addEmail(value)) { qWarning() << QStringLiteral("Unable to add email on vcard: `%1`.").arg(email); return false; } qInfo() << QStringLiteral("Add new email on vcard: `%1`.").arg(email); emit vcardUpdated(); return true; } void VcardModel::removeEmail (const QString &email) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr belcard = mVcard->getVcard(); shared_ptr value = findBelCardValue(belcard->getEmails(), email); if (!value) { qWarning() << QStringLiteral("Unable to remove email on vcard: `%1`.").arg(email); return; } qInfo() << QStringLiteral("Remove email on vcard: `%1`.").arg(email); belcard->removeEmail(value); emit vcardUpdated(); } bool VcardModel::updateEmail (const QString &oldEmail, const QString &email) { removeEmail(oldEmail); return addEmail(email); } // ----------------------------------------------------------------------------- QVariantList VcardModel::getUrls () const { QVariantList list; for (const auto &url : mVcard->getVcard()->getURLs()) list.append(QString::fromStdString(url->getValue())); return list; } bool VcardModel::addUrl (QString url) { CHECK_VCARD_IS_WRITABLE(this); QUrl urlParser(url); if( urlParser.scheme() == ""){ url = "https://"+url; } shared_ptr belcard = mVcard->getVcard(); if (findBelCardValue(belcard->getURLs(), url)) return false; shared_ptr value = belcard::BelCardGeneric::create(); value->setValue(url.toStdString()); if (!belcard->addURL(value)) { qWarning() << QStringLiteral("Unable to add url on vcard: `%1`.").arg(url); return false; } qInfo() << QStringLiteral("Add new url on vcard: `%1`.").arg(url); emit vcardUpdated(); return true; } void VcardModel::removeUrl (const QString &url) { CHECK_VCARD_IS_WRITABLE(this); shared_ptr belcard = mVcard->getVcard(); shared_ptr value = findBelCardValue(belcard->getURLs(), url); if (!value) { qWarning() << QStringLiteral("Unable to remove url on vcard: `%1`.").arg(url); return; } qInfo() << QStringLiteral("Remove url on vcard: `%1`.").arg(url); belcard->removeURL(value); emit vcardUpdated(); } bool VcardModel::updateUrl (const QString &oldUrl, const QString &url) { removeUrl(oldUrl); return addUrl(url); } QString VcardModel::encode(const QString& data)const{// Convert '\n', ',', '\' to "\n", "\,", "\\" QString encoded; for(int i = 0 ; i < data.length() ; ++i){ if(data[i] == ',') encoded += "\\,"; else if(data[i] == '\\') encoded += "\\\\"; else if(data[i] == '\n') encoded += "\\n"; else encoded += data[i]; } return encoded; } QString VcardModel::decode(const QString& data)const{// Convert "\n", "\,", "\\" to '\n', ',', '\' QString decoded = data; decoded.replace("\\,", ",").replace("\\\\", "\\").replace("\\n", "\n"); return decoded; } linphone-desktop-5.0.2/linphone-app/src/components/contact/VcardModel.hpp000066400000000000000000000100721434616504300265410ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef VCARD_MODEL_H_ #define VCARD_MODEL_H_ #include #include // ============================================================================= namespace linphone { class Vcard; } class VcardModel : public QObject { friend class ContactModel; // Grant access to `mVcard`. Q_OBJECT Q_PROPERTY(QString username READ getUsername WRITE setUsername NOTIFY vcardUpdated); Q_PROPERTY(QString avatar READ getAvatar WRITE setAvatar NOTIFY vcardUpdated); Q_PROPERTY(QVariantMap address READ getAddress NOTIFY vcardUpdated); //Q_PROPERTY(QString sipAddress Q_PROPERTY(QVariantList sipAddresses READ getSipAddresses NOTIFY vcardUpdated); Q_PROPERTY(QVariantList companies READ getCompanies NOTIFY vcardUpdated); Q_PROPERTY(QVariantList emails READ getEmails NOTIFY vcardUpdated); Q_PROPERTY(QVariantList urls READ getUrls NOTIFY vcardUpdated); // --------------------------------------------------------------------------- public: VcardModel (std::shared_ptr vcard, bool isReadOnly = true); ~VcardModel (); // --------------------------------------------------------------------------- bool getIsReadOnly () const { return mIsReadOnly; } // --------------------------------------------------------------------------- QString getAvatar () const; bool setAvatar (const QString &path); QString getUsername () const; void setUsername (const QString &username); // --------------------------------------------------------------------------- QVariantList getSipAddresses () const; QVariantMap getAddress () const; QVariantList getEmails () const; QVariantList getCompanies () const; QVariantList getUrls () const; // --------------------------------------------------------------------------- Q_INVOKABLE bool addSipAddress (const QString &sipAddress); Q_INVOKABLE void removeSipAddress (const QString &sipAddress); Q_INVOKABLE bool updateSipAddress (const QString &oldSipAddress, const QString &sipAddress); Q_INVOKABLE bool addCompany (const QString &company); Q_INVOKABLE void removeCompany (const QString &company); Q_INVOKABLE bool updateCompany (const QString &oldCompany, const QString &company); Q_INVOKABLE bool addEmail (const QString &email); Q_INVOKABLE void removeEmail (const QString &email); Q_INVOKABLE bool updateEmail (const QString &oldEmail, const QString &email); Q_INVOKABLE bool addUrl (QString url); Q_INVOKABLE void removeUrl (const QString &url); Q_INVOKABLE bool updateUrl (const QString &oldUrl, const QString &url); Q_INVOKABLE void setStreet (const QString &street); Q_INVOKABLE void setLocality (const QString &locality); Q_INVOKABLE void setPostalCode (const QString &postalCode); Q_INVOKABLE void setCountry (const QString &country); // --------------------------------------------------------------------------- QString encode(const QString& data)const;// Convert '\n', ',', '\' to "\n", "\,", "\\" QString decode(const QString& data)const;// Convert "\n", "\,", "\\" to '\n', ',', '\' signals: void vcardUpdated (); // --------------------------------------------------------------------------- private: bool mIsReadOnly = true; bool mAvatarIsReadOnly = true; std::shared_ptr mVcard; }; Q_DECLARE_METATYPE(VcardModel *); #endif // VCARD_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/contacts/000077500000000000000000000000001434616504300241735ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsImporterListModel.cpp000066400000000000000000000161341434616504300320210ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "ContactsImporterModel.hpp" #include "ContactsImporterListModel.hpp" #include "ContactsImporterPluginsManager.hpp" #include "components/core/CoreManager.hpp" #include "utils/Utils.hpp" // ============================================================================= using namespace std; ContactsImporterListModel::ContactsImporterListModel (QObject *parent) : ProxyListModel(parent) { // Init contacts with linphone friends list. mMaxContactsImporterId = -1; QQmlEngine *engine = App::getInstance()->getEngine(); auto config = CoreManager::getInstance()->getCore()->getConfig(); PluginsManager::getPlugins();// Initialize list // Read configuration file std::list sections = config->getSectionsNamesList(); for(auto section : sections){ QString qtSection = Utils::coreStringToAppString(section); QStringList parse = qtSection.split("_");// PluginsManager::gPluginsConfigSection_id_capability if( parse.size() > 2){ QVariantMap importData; if( parse[2].toInt() == PluginDataAPI::CONTACTS){// We only care about Contacts int id = parse[1].toInt(); mMaxContactsImporterId = qMax(id, mMaxContactsImporterId); std::list keys = config->getKeysNamesList(section); auto keyName = std::find(keys.begin(), keys.end(), "pluginID"); if( keyName != keys.end()){ QString pluginID = Utils::coreStringToAppString(config->getString(section, *keyName, "")); PluginDataAPI* data = static_cast(PluginsManager::createInstance(pluginID)); if(data) { auto model = QSharedPointer::create(data); // See: http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership // The returned value must have a explicit parent or a QQmlEngine::CppOwnership. engine->setObjectOwnership(model.get(), QQmlEngine::CppOwnership); model->setIdentity(id); model->loadConfiguration();// Read the configuration contacts inside the plugin addContactsImporter(model); } } } } } } // GUI methods bool ContactsImporterListModel::removeRows (int row, int count, const QModelIndex &parent) { int limit = row + count - 1; if (row < 0 || count < 0 || limit >= mList.count()) return false; beginRemoveRows(parent, row, limit); for (int i = 0; i < count; ++i) { emit contactsImporterRemoved(mList.takeAt(row).objectCast()); } endRemoveRows(); return true; } // ----------------------------------------------------------------------------- QSharedPointer ContactsImporterListModel::findContactsImporterModelFromId (const int &id) const { auto it = find_if(mList.begin(), mList.end(), [id](QSharedPointer contactsImporterModel) { return contactsImporterModel.objectCast()->getIdentity() == id; }); return it != mList.end() ? it->objectCast() : nullptr; } // ----------------------------------------------------------------------------- ContactsImporterModel *ContactsImporterListModel::createContactsImporter(QVariantMap data){ QSharedPointer contactsImporter = nullptr; if( data.contains("pluginID")){ PluginDataAPI * dataInstance = static_cast(PluginsManager::createInstance(data["pluginID"].toString())); if(dataInstance) { // get default values contactsImporter = QSharedPointer::create(dataInstance); App::getInstance()->getEngine()->setObjectOwnership(contactsImporter.get(), QQmlEngine::CppOwnership); QVariantMap newData = ContactsImporterPluginsManager::getDefaultValues(data["pluginID"].toString());// Start with defaults from plugin QVariantMap InstanceFields = contactsImporter->getFields(); for(auto field = InstanceFields.begin() ; field != InstanceFields.end() ; ++field)// Insert or Update with the defaults of an instance newData[field.key()] = field.value(); for(auto field = data.begin() ; field != data.end() ; ++field)// Insert or Update with Application data newData[field.key()] = field.value(); contactsImporter->setIdentity(++mMaxContactsImporterId); contactsImporter->setFields(newData); addContactsImporter(contactsImporter); emit layoutChanged(); emit contactsImporterAdded(contactsImporter); } } return contactsImporter.get(); } ContactsImporterModel* ContactsImporterListModel::addContactsImporter (QVariantMap data, int pId) { auto contactsImporter = findContactsImporterModelFromId(pId); if (contactsImporter) { contactsImporter->setFields(data); return contactsImporter.get(); }else return createContactsImporter(data); } void ContactsImporterListModel::removeContactsImporter (ContactsImporterModel *contactsImporter) { int index = 0; for(auto importer : mList){ if( importer.get() == contactsImporter){ if( contactsImporter->getIdentity() >=0 ){// Remove from configuration int id = contactsImporter->getIdentity(); string section = Utils::appStringToCoreString(PluginsManager::gPluginsConfigSection+"_"+QString::number(id)+"_"+QString::number(PluginDataAPI::CONTACTS)); CoreManager::getInstance()->getCore()->getConfig()->cleanSection(section); if( id == mMaxContactsImporterId)// Decrease mMaxContactsImporterId in a safe way --mMaxContactsImporterId; } removeRow(index); return; }else ++index; } } void ContactsImporterListModel::importContacts(const int &pId){ if( pId >=0) { auto contactsImporter = findContactsImporterModelFromId(pId); if( contactsImporter) contactsImporter->importContacts(); }else // Import from all current connectors for(auto importer : mList) qobject_cast(importer.get())->importContacts(); } // ----------------------------------------------------------------------------- void ContactsImporterListModel::addContactsImporter (QSharedPointer contactsImporter) { // Connect all update signals QObject::connect(contactsImporter.get(), &ContactsImporterModel::fieldsChanged, this, [this, contactsImporter]() { emit contactsImporterUpdated(contactsImporter); }); QObject::connect(contactsImporter.get(), &ContactsImporterModel::identityChanged, this, [this, contactsImporter]() { emit contactsImporterUpdated(contactsImporter); }); add(contactsImporter); } //----------------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsImporterListModel.hpp000066400000000000000000000044121434616504300320220ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTACTS_IMPORTER_LIST_MODEL_H_ #define CONTACTS_IMPORTER_LIST_MODEL_H_ #include #include "app/proxyModel/ProxyListModel.hpp" // ============================================================================= class ContactsImporterModel; class PluginsModel; // Store all connectors class ContactsImporterListModel : public ProxyListModel { Q_OBJECT public: ContactsImporterListModel (QObject *parent = Q_NULLPTR); virtual bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; QSharedPointer findContactsImporterModelFromId (const int &id) const; Q_INVOKABLE ContactsImporterModel *createContactsImporter(QVariantMap data); Q_INVOKABLE ContactsImporterModel *addContactsImporter (QVariantMap data, int id=-1); Q_INVOKABLE void removeContactsImporter (ContactsImporterModel *importer); Q_INVOKABLE void importContacts(const int &id = -1); // Import contacts for all enabled importer if -1 //----------------------------------------------------------------------------------- signals: void contactsImporterAdded (QSharedPointer); void contactsImporterRemoved (QSharedPointer); void contactsImporterUpdated (QSharedPointer); private: void addContactsImporter (QSharedPointer contactsImporter); int mMaxContactsImporterId; // Used to ensure unicity on ID when creating a connector }; #endif // CONTACTS_IMPORTER_LIST_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsImporterListProxyModel.cpp000066400000000000000000000041551434616504300330630ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "components/contacts/ContactsImporterModel.hpp" #include "components/core/CoreManager.hpp" #include "utils/Utils.hpp" #include "ContactsImporterListModel.hpp" #include "ContactsImporterListProxyModel.hpp" // ============================================================================= using namespace std; // ----------------------------------------------------------------------------- ContactsImporterListProxyModel::ContactsImporterListProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { setSourceModel(CoreManager::getInstance()->getContactsImporterListModel()); sort(0);// Sort by identity } // ----------------------------------------------------------------------------- bool ContactsImporterListProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent ) const { Q_UNUSED(sourceRow) Q_UNUSED(sourceParent) return true; } bool ContactsImporterListProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const ContactsImporterModel *contactA = sourceModel()->data(left).value(); const ContactsImporterModel *contactB = sourceModel()->data(right).value(); return contactA->getIdentity() < contactB->getIdentity(); } // ----------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsImporterListProxyModel.hpp000066400000000000000000000027221434616504300330660ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTACTS_IMPORTER_LIST_PROXY_MODEL_H_ #define CONTACTS_IMPORTER_LIST_PROXY_MODEL_H_ #include // ============================================================================= class ContactsImporterModel; class ContactsImporterListModel; // Manage the list of connectors class ContactsImporterListProxyModel : public QSortFilterProxyModel { Q_OBJECT; public: ContactsImporterListProxyModel (QObject *parent = Q_NULLPTR); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; }; #endif // CONTACTS_IMPORTER_LIST_PROXY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsImporterModel.cpp000066400000000000000000000103151434616504300311600ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ContactsImporterModel.hpp" #include "ContactsImporterPluginsManager.hpp" #include "../../utils/Utils.hpp" //#include #include "include/LinphoneApp/PluginDataAPI.hpp" #include #include // ============================================================================= using namespace std; ContactsImporterModel::ContactsImporterModel (PluginDataAPI * data, QObject *parent) : PluginsModel(parent) { mIdentity = -1; mData = nullptr; setDataAPI(data); } // ----------------------------------------------------------------------------- void ContactsImporterModel::setDataAPI(PluginDataAPI *data){ if(mData){// Unload the current plugin loader and delete it from memory QPluginLoader * loader = mData->getPluginLoader(); mData->deleteLater(); if(loader){ loader->unload(); loader->deleteLater(); } mData = data; }else mData = data; if( mData){ connect(mData, &PluginDataAPI::inputFieldsChanged, this, &ContactsImporterModel::fieldsChanged); connect(mData, &PluginDataAPI::message, this, &ContactsImporterModel::messageReceived); connect(mData, &PluginDataAPI::dataReceived, this, &ContactsImporterModel::parsedContacts); } } PluginDataAPI *ContactsImporterModel::getDataAPI(){ return mData; } bool ContactsImporterModel::isUsable(){ if( mData){ if( !mData->getPluginLoader()->isLoaded()) mData->getPluginLoader()->load(); return mData->getPluginLoader()->isLoaded(); }else return false; } QVariantMap ContactsImporterModel::getFields(){ return (isUsable()?mData->getInputFields(PluginDataAPI::CONTACTS)[PluginDataAPI::CONTACTS] :QVariantMap()); } void ContactsImporterModel::setFields(const QVariantMap &pFields){ if( isUsable()) mData->setInputFields(PluginDataAPI::CONTACTS, pFields); } int ContactsImporterModel::getIdentity()const{ return mIdentity; } void ContactsImporterModel::setIdentity(const int &pIdentity){ if( mIdentity != pIdentity){ mIdentity = pIdentity; if(mData && mData->getPluginLoader()->isLoaded()) mData->setSectionConfiguration(PluginsManager::gPluginsConfigSection+"_"+QString::number(mIdentity)); emit identityChanged(mIdentity); } } void ContactsImporterModel::loadConfiguration(){ if(isUsable()) mData->loadConfiguration(PluginDataAPI::CONTACTS); } void ContactsImporterModel::importContacts(){ if(isUsable()){ qInfo() << "Importing contacts with " << mData->getInputFields(PluginDataAPI::CONTACTS)[PluginDataAPI::CONTACTS]["pluginTitle"]; QPluginLoader * loader = mData->getPluginLoader(); if( !loader) qWarning() << "Loader is NULL"; else{ qWarning() << "Plugin loaded Status : " << loader->isLoaded() << " for " << loader->fileName(); } mData->run(PluginDataAPI::CONTACTS); }else qWarning() << "Cannot import contacts, mData is NULL or plugin cannot be loaded "; } void ContactsImporterModel::parsedContacts(const PluginDataAPI::PluginCapability& actionType, QVector > contacts){ if(actionType == PluginDataAPI::CONTACTS) ContactsImporterPluginsManager::importContacts(contacts); } void ContactsImporterModel::updateInputs(const PluginDataAPI::PluginCapability& capability, const QVariantMap &inputs){ if(capability == PluginDataAPI::CONTACTS) setFields(inputs); } void ContactsImporterModel::messageReceived(const QtMsgType& type, const QString &message){ if( type == QtMsgType::QtInfoMsg) emit statusMessage(message); else emit errorMessage(message); } linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsImporterModel.hpp000066400000000000000000000047641434616504300312000ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTACTS_IMPORTER_MODEL_H_ #define CONTACTS_IMPORTER_MODEL_H_ #include #include #include "utils/plugins/PluginsManager.hpp" #include "include/LinphoneApp/PluginDataAPI.hpp" // ============================================================================= class ContactsImporterModel : public PluginsModel { Q_OBJECT Q_PROPERTY(QVariantMap fields READ getFields WRITE setFields NOTIFY fieldsChanged) Q_PROPERTY(int identity READ getIdentity WRITE setIdentity NOTIFY identityChanged) public: ContactsImporterModel (PluginDataAPI * data, QObject *parent = nullptr); void setDataAPI(PluginDataAPI *data); PluginDataAPI *getDataAPI(); bool isUsable(); // Return true if the plugin can be load and has been loaded. QVariantMap getFields(); void setFields(const QVariantMap &pFields); int getIdentity()const; void setIdentity(const int &pIdentity); void loadConfiguration(); Q_INVOKABLE void importContacts(); public slots: void parsedContacts(const PluginDataAPI::PluginCapability& actionType, QVector > contacts); void updateInputs(const PluginDataAPI::PluginCapability&, const QVariantMap &inputs); void messageReceived(const QtMsgType& type, const QString &message); signals: void fieldsChanged (const PluginDataAPI::PluginCapability&, QVariantMap fields); void identityChanged(int identity); void errorMessage(const QString& message); void statusMessage(const QString& message); private: int mIdentity; // The identity of the model in configuration. It must be unique between all contact plugins. PluginDataAPI *mData; // The instance of the plugin with its plugin Loader. }; Q_DECLARE_METATYPE(ContactsImporterModel *); #endif // CONTACTS_IMPORTER_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsImporterPluginsManager.cpp000066400000000000000000000106211434616504300330340ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ContactsImporterPluginsManager.hpp" #include "ContactsImporterModel.hpp" #include "include/LinphoneApp/PluginNetworkHelper.hpp" #include "utils/Utils.hpp" #include "app/paths/Paths.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/contacts/ContactsImporterListModel.hpp" #include "components/core/CoreManager.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include #include #include #include #include #include // ============================================================================= ContactsImporterPluginsManager::ContactsImporterPluginsManager(QObject * parent) : PluginsManager(parent){ } QVariantMap ContactsImporterPluginsManager::getContactsImporterPluginDescription(const QString& pluginID) { QVariantMap description; QJsonDocument doc = getJson(pluginID); description = doc.toVariant().toMap(); if(description.contains("fields")){ auto fields = description["fields"].toList(); auto removedFields = std::remove_if(fields.begin(), fields.end(), [](const QVariant& f){ auto field = f.toMap(); return field.contains("capability") && ((field["capability"].toInt() & PluginDataAPI::CONTACTS) != PluginDataAPI::CONTACTS); }); fields.erase(removedFields, fields.end()); description["fields"] = fields; } return description; } void ContactsImporterPluginsManager::openNewPlugin(){ PluginsManager::openNewPlugin("Import Address Book Connector"); } QVariantList ContactsImporterPluginsManager::getPlugins(){ return PluginsManager::getPlugins(PluginDataAPI::CONTACTS); } void ContactsImporterPluginsManager::importContacts(ContactsImporterModel * model) { if(model){ QString pluginID = model->getFields()["pluginID"].toString(); if(!pluginID.isEmpty()){ if( !PluginsManager::gPluginsMap.contains(pluginID)) qInfo() << "Unknown " << pluginID; model->importContacts(); }else qWarning() << "Error : Cannot import contacts : pluginID is empty"; } } void ContactsImporterPluginsManager::importContacts(const QVector >& pContacts ){ for(int i = 0 ; i < pContacts.size() ; ++i){ VcardModel * card = CoreManager::getInstance()->createDetachedVcardModel(); SipAddressesModel * sipConvertion = CoreManager::getInstance()->getSipAddressesModel(); QString domain = pContacts[i].values("sipDomain").at(0); //if(pContacts[i].contains("phoneNumber")) // card->addSipAddress(sipConvertion->interpretSipAddress(pContacts[i].values("phoneNumber").at(0)+"@"+domain, false)); if(pContacts[i].contains("displayName") && pContacts[i].values("displayName").size() > 0) card->setUsername(pContacts[i].values("displayName").at(0)); if(pContacts[i].contains("sipUsername") && pContacts[i].values("sipUsername").size() > 0){ QString sipUsername = pContacts[i].values("sipUsername").at(0); QString convertedUsername = sipConvertion->interpretSipAddress(sipUsername, domain); if(!convertedUsername.contains(domain)){ convertedUsername = convertedUsername.replace('@',"%40")+"@"+domain; } card->addSipAddress(convertedUsername); if( sipUsername.contains('@')){ card->addEmail(sipUsername); } } if(pContacts[i].contains("email")) for(auto email : pContacts[i].values("email")) card->addEmail(email); if(pContacts[i].contains("organization")) for(auto company : pContacts[i].values("organization")) card->addCompany(company); if( card->getSipAddresses().size()>0){ CoreManager::getInstance()->getContactsListModel()->addContact(card); }else card->deleteLater(); } } linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsImporterPluginsManager.hpp000066400000000000000000000037621434616504300330510ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTACTS_IMPORTER_PLUGINS_MANAGER_MODEL_H_ #define CONTACTS_IMPORTER_PLUGINS_MANAGER_MODEL_H_ #include #include // ============================================================================= #include "utils/plugins/PluginsManager.hpp" class ContactsImporterModel; class PluginContactsDataAPI; class QPluginLoader; class ContactsImporterPluginsManager : public PluginsManager{ Q_OBJECT public: ContactsImporterPluginsManager (QObject *parent = Q_NULLPTR); Q_INVOKABLE static void openNewPlugin(); // Open a File Dialog. Test if the file can be load and have a matched version. Replace old plugins from custom paths and with the same plugin title. Q_INVOKABLE static QVariantList getPlugins(); // Get a list of all available plugins Q_INVOKABLE static QVariantMap getContactsImporterPluginDescription(const QString& pluginID); // Get the description of the plugin. It is used for GUI to create dynamically items Q_INVOKABLE static void importContacts(ContactsImporterModel * model); // Request the import of the model static void importContacts(const QVector >& contacts ); // Merge these data into contacts }; #endif // CONTACTS_IMPORTER_PLUGINS_MANAGER_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsListModel.cpp000066400000000000000000000177451434616504300303100ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/core/CoreManager.hpp" #include "components/friend/FriendListListener.hpp" #include "ContactsListModel.hpp" // ============================================================================= void ContactsListModel::connectTo(FriendListListener * listener){ connect(listener, &FriendListListener::contactCreated, this, &ContactsListModel::onContactCreated); connect(listener, &FriendListListener::contactDeleted, this, &ContactsListModel::onContactDeleted); connect(listener, &FriendListListener::contactUpdated, this, &ContactsListModel::onContactUpdated); connect(listener, &FriendListListener::syncStatusChanged, this, &ContactsListModel::onSyncStatusChanged); connect(listener, &FriendListListener::presenceReceived, this, &ContactsListModel::onPresenceReceived); } // ============================================================================= using namespace std; ContactsListModel::ContactsListModel (QObject *parent) : ProxyListModel(parent) { mFriendListListener = std::make_shared(); connectTo(mFriendListListener.get()); update(); } ContactsListModel::~ContactsListModel(){ if(rowCount()>0) { beginResetModel(); mOptimizedSearch.clear(); mList.clear(); mLinphoneFriends.clear(); endResetModel(); } } bool ContactsListModel::removeRows (int row, int count, const QModelIndex &parent) { int limit = row + count - 1; if (row < 0 || count < 0 || limit >= mList.count()) return false; auto friendsList = CoreManager::getInstance()->getCore()->getFriendsLists(); beginRemoveRows(parent, row, limit); for (int i = 0; i < count; ++i) { QSharedPointer contact = mList.takeAt(row).objectCast(); for(auto address : contact->getVcardModel()->getSipAddresses()){ mOptimizedSearch.remove(address.toString()); } for(auto l : friendsList) l->removeFriend(contact->mLinphoneFriend); emit contactRemoved(contact); } endRemoveRows(); return true; } // ----------------------------------------------------------------------------- QSharedPointer ContactsListModel::findContactModelFromSipAddress (const QString &sipAddress) const { if(mOptimizedSearch.contains(sipAddress)) return mOptimizedSearch[sipAddress]; else return nullptr; } QSharedPointer ContactsListModel::findContactModelFromUsername (const QString &username) const { auto it = find_if(mList.begin(), mList.end(), [&username](QSharedPointer contactModel) { return qobject_cast(contactModel.get())->getVcardModel()->getUsername() == username; }); return it != mList.end() ? it->objectCast() : nullptr; } // ----------------------------------------------------------------------------- ContactModel *ContactsListModel::getContactModelFromAddress (const QString& address) const{ auto contact = findContactModelFromSipAddress(address); return contact.get(); } ContactModel *ContactsListModel::addContact (VcardModel *vcardModel) { // Try to merge vcardModel to an existing contact. auto contact = findContactModelFromUsername(vcardModel->getUsername()); if (contact) { contact->mergeVcardModel(vcardModel); return contact.get(); } contact = QSharedPointer::create(vcardModel); App::getInstance()->getEngine()->setObjectOwnership(contact.get(), QQmlEngine::CppOwnership); if( mLinphoneFriends.size() == 0){ update();// Friends were not loaded correctly. Update them. } auto friendsList = CoreManager::getInstance()->getCore()->getDefaultFriendList(); if( !friendsList){ qWarning() << "There is no friends list available, cannot add a contact" ; return nullptr; } if (friendsList->addFriend(contact->mLinphoneFriend) != linphone::FriendList::Status::OK) { qWarning() << QStringLiteral("Unable to add contact from vcard:") << vcardModel; return nullptr; } qInfo() << QStringLiteral("Add contact from vcard:") << contact.get() << vcardModel; // Make sure new subscribe is issued. friendsList->updateSubscriptions(); emit layoutChanged(); emit contactAdded(contact); return contact.get(); } void ContactsListModel::removeContact (ContactModel *contact){ remove(contact); } // ----------------------------------------------------------------------------- void ContactsListModel::cleanAvatars () { qInfo() << QStringLiteral("Delete all avatars."); for (const auto &item : mList) { auto contact = item.objectCast(); VcardModel* vcardModel = contact->cloneVcardModel(); vcardModel->setAvatar(QString("")); contact->setVcardModel(vcardModel); } } // ----------------------------------------------------------------------------- void ContactsListModel::addContact (QSharedPointer contact) { QObject::connect(contact.get(), &ContactModel::contactUpdated, this, [this, contact]() { emit contactUpdated(contact); }); QObject::connect(contact.get(), &ContactModel::sipAddressAdded, this, [this, contact](const QString &sipAddress) { mOptimizedSearch[sipAddress] = contact; emit sipAddressAdded(contact, sipAddress); }); QObject::connect(contact.get(), &ContactModel::sipAddressRemoved, this, [this, contact](const QString &sipAddress) { mOptimizedSearch.remove(sipAddress); emit sipAddressRemoved(contact, sipAddress); }); add(contact); for(auto address : contact->getVcardModel()->getSipAddresses()){ mOptimizedSearch[address.toString()] = contact; } } void ContactsListModel::update(){ beginResetModel(); for(auto l : mLinphoneFriends) l->removeListener(mFriendListListener); mLinphoneFriends.clear(); mOptimizedSearch.clear(); mList.clear(); endResetModel(); mLinphoneFriends = CoreManager::getInstance()->getCore()->getFriendsLists(); for(auto l : mLinphoneFriends){ l->addListener(mFriendListListener); for (const auto &linphoneFriend : l->getFriends()) { onContactCreated(linphoneFriend); } } } //------------------------------------------------------------------------------------------------ void ContactsListModel::onContactCreated(const std::shared_ptr & linphoneFriend){ QQmlEngine *engine = App::getInstance()->getEngine(); auto haveContact = std::find_if(mList.begin(), mList.end(), [linphoneFriend] (const QSharedPointer& item){ return item.objectCast()->getFriend() == linphoneFriend; }); if(haveContact == mList.end()) { auto contact = QSharedPointer::create(linphoneFriend); // See: http://doc.qt.io/qt-5/qtqml-cppintegration-data.html#data-ownership // The returned value must have a explicit parent or a QQmlEngine::CppOwnership. engine->setObjectOwnership(contact.get(), QQmlEngine::CppOwnership); addContact(contact); } } void ContactsListModel::onContactDeleted(const std::shared_ptr & linphoneFriend){ } void ContactsListModel::onContactUpdated(const std::shared_ptr & newFriend, const std::shared_ptr & oldFriend){ } void ContactsListModel::onSyncStatusChanged(linphone::FriendList::SyncStatus status, const std::string & message){ } void ContactsListModel::onPresenceReceived(const std::list> & friends){ } linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsListModel.hpp000066400000000000000000000057301434616504300303040ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTACTS_LIST_MODEL_H_ #define CONTACTS_LIST_MODEL_H_ #include #include "app/proxyModel/ProxyListModel.hpp" // ============================================================================= namespace linphone { class FriendList; } class ContactModel; class VcardModel; class FriendListListener; class ContactsListModel : public ProxyListModel { friend class SipAddressesModel; Q_OBJECT; public: ContactsListModel (QObject *parent = Q_NULLPTR); virtual ~ContactsListModel(); bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; QSharedPointer findContactModelFromSipAddress (const QString &sipAddress) const; QSharedPointer findContactModelFromUsername (const QString &username) const; Q_INVOKABLE ContactModel *getContactModelFromAddress (const QString& address) const; Q_INVOKABLE ContactModel *addContact (VcardModel *vcardModel); Q_INVOKABLE void removeContact (ContactModel *contact); Q_INVOKABLE void cleanAvatars (); Q_INVOKABLE void update (); void connectTo(FriendListListener * listener); public slots: void onContactCreated(const std::shared_ptr & linphoneFriend); void onContactDeleted(const std::shared_ptr & linphoneFriend); void onContactUpdated(const std::shared_ptr & newFriend, const std::shared_ptr & oldFriend); void onSyncStatusChanged(linphone::FriendList::SyncStatus status, const std::string & message); void onPresenceReceived(const std::list> & friends); signals: void contactAdded (QSharedPointer); void contactRemoved (QSharedPointer); void contactUpdated (QSharedPointer); void sipAddressAdded (QSharedPointer, const QString &sipAddress); void sipAddressRemoved (QSharedPointer, const QString &sipAddress); private: void addContact (QSharedPointer contact); QMap> mOptimizedSearch; std::list> mLinphoneFriends; std::shared_ptr mFriendListListener; }; #endif // CONTACTS_LIST_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsListProxyModel.cpp000066400000000000000000000117771434616504300313510ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/core/CoreManager.hpp" #include "utils/Utils.hpp" #include "ContactsListModel.hpp" #include "ContactsListProxyModel.hpp" // ============================================================================= using namespace std; namespace { constexpr float UsernameWeight = 50.f; constexpr float SipAddressWeight = 50.f; constexpr float FactorPos0 = 1.0f; constexpr float FactorPos1 = 0.9f; constexpr float FactorPos2 = 0.8f; constexpr float FactorPos3 = 0.7f; constexpr float FactorPosOther = 0.6f; } // Notes: // // - First `^` is necessary to search two words with one separator // between them like `Claire Manning`. // // - [^_.-;@ ] is used to search patterns which starts with // a separator like ` word`. // // - [_.-;@ ] is the main pattern (a separator). const QRegExp ContactsListProxyModel::SearchSeparators("^[^_.-;@ ][_.-;@ ]"); // ----------------------------------------------------------------------------- ContactsListProxyModel::ContactsListProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { setSourceModel(CoreManager::getInstance()->getContactsListModel()); sort(0); } // ----------------------------------------------------------------------------- void ContactsListProxyModel::setFilter (const QString &pattern) { mFilter = pattern; invalidate(); } // ----------------------------------------------------------------------------- bool ContactsListProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent ) const { const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); const ContactModel *contact = index.data().value(); mWeights[contact] = uint(round(computeContactWeight(contact))); return mWeights[contact] > 0 && ( !mUseConnectedFilter || contact->getPresenceLevel() != Presence::PresenceLevel::White ); } bool ContactsListProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const ContactModel *contactA = sourceModel()->data(left).value(); const ContactModel *contactB = sourceModel()->data(right).value(); unsigned int weightA = mWeights[contactA]; unsigned int weightB = mWeights[contactB]; // Sort by weight and name. return weightA > weightB || ( weightA == weightB && QString::localeAwareCompare(Utils::coreStringToAppString(contactA->mLinphoneFriend->getName()), Utils::coreStringToAppString(contactB->mLinphoneFriend->getName())) <= 0 ); } // ----------------------------------------------------------------------------- float ContactsListProxyModel::computeStringWeight (const QString &string, float percentage) const { int index = -1; int offset = -1; // Search pattern. while ((index = string.indexOf(mFilter, index + 1, Qt::CaseInsensitive)) != -1) { // Search n chars between one separator and index. int tmpOffset = index - string.lastIndexOf(SearchSeparators, index) - 1; if ((tmpOffset != -1 && tmpOffset < offset) || offset == -1) if ((offset = tmpOffset) == 0) break; } switch (offset) { case -1: return 0; case 0: return percentage *FactorPos0; case 1: return percentage *FactorPos1; case 2: return percentage *FactorPos2; case 3: return percentage *FactorPos3; default: break; } return percentage *FactorPosOther; } float ContactsListProxyModel::computeContactWeight (const ContactModel *contact) const { float weight = computeStringWeight(contact->getVcardModel()->getUsername(), UsernameWeight); // Get all contact's addresses. const list> addresses = contact->mLinphoneFriend->getAddresses(); float size = float(addresses.size()); for (auto it = addresses.cbegin(); it != addresses.cend(); ++it) weight += computeStringWeight( Utils::coreStringToAppString((*it)->asStringUriOnly()), SipAddressWeight / size ); return weight; } // ----------------------------------------------------------------------------- void ContactsListProxyModel::setConnectedFilter (bool useConnectedFilter) { if (useConnectedFilter != mUseConnectedFilter) { mUseConnectedFilter = useConnectedFilter; invalidate(); } } linphone-desktop-5.0.2/linphone-app/src/components/contacts/ContactsListProxyModel.hpp000066400000000000000000000040731434616504300313450ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTACTS_LIST_PROXY_MODEL_H_ #define CONTACTS_LIST_PROXY_MODEL_H_ #include // ============================================================================= class ContactModel; class ContactsListModel; class ContactsListProxyModel : public QSortFilterProxyModel { Q_OBJECT; Q_PROPERTY( bool useConnectedFilter READ isConnectedFilterUsed WRITE setConnectedFilter ); public: ContactsListProxyModel (QObject *parent = Q_NULLPTR); Q_INVOKABLE void setFilter (const QString &pattern); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; private: float computeStringWeight (const QString &string, float percentage) const; float computeContactWeight (const ContactModel *contact) const; bool isConnectedFilterUsed () const { return mUseConnectedFilter; } void setConnectedFilter (bool useConnectedFilter); QString mFilter; bool mUseConnectedFilter = false; // It's just a cache to save values computed by `filterAcceptsRow` // and reused by `lessThan`. mutable QHash mWeights; static const QRegExp SearchSeparators; }; #endif // CONTACTS_LIST_PROXY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/content/000077500000000000000000000000001434616504300240275ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/content/ContentListModel.cpp000066400000000000000000000135051434616504300277660ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "ContentListModel.hpp" #include "ContentModel.hpp" #include "utils/Constants.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" // ============================================================================= ContentListModel::ContentListModel (ChatMessageModel * message, QObject* parent) : ProxyListModel(parent) { mParent = message; if(message){ std::list> contents = message->getChatMessage()->getContents() ; for(auto content : contents){ auto contentModel = QSharedPointer::create(content, message); connect(this, &ContentListModel::updateTransferDataRequested, contentModel.get(), &ContentModel::updateTransferData); mList << contentModel; } } } int ContentListModel::count(){ return mList.count(); } QSharedPointer ContentListModel::add(std::shared_ptr content){ auto contentModel = QSharedPointer::create(content, mParent); ProxyListModel::add(contentModel); emit contentsChanged(); return contentModel; } void ContentListModel::addFile(const QString& path){ QFile file(path); if (!file.exists()) return; qint64 fileSize = file.size(); if (fileSize > Constants::FileSizeLimit) { qWarning() << QStringLiteral("Unable to send file. (Size limit=%1)").arg(Constants::FileSizeLimit); return; } std::shared_ptr content = CoreManager::getInstance()->getCore()->createContent(); { QStringList mimeType = QMimeDatabase().mimeTypeForFile(path).name().split('/'); if (mimeType.length() != 2) { qWarning() << QStringLiteral("Unable to get supported mime type for: `%1`.").arg(path); return; } content->setType(Utils::appStringToCoreString(mimeType[0])); content->setSubtype(Utils::appStringToCoreString(mimeType[1])); } content->setSize(size_t(fileSize)); content->setName(QFileInfo(file).fileName().toStdString()); content->setFilePath(Utils::appStringToCoreString(path)); auto modelAdded = add(content); if(!content->isFile()) modelAdded->createThumbnail(true); // Was not created because linphone::Content is not considered as a file (yet) } void ContentListModel::remove(ContentModel * model){ int count = 0; for(auto it = mList.begin() ; it != mList.end() ; ++count, ++it) { if( it->get() == model) { model->removeThumbnail(); removeRow(count, QModelIndex()); return; } } } void ContentListModel::clear(){ // Delete thumbnails for(auto contentModel : mList){ contentModel.objectCast()->removeThumbnail(); } resetData(); } void ContentListModel::removeDownloadedFiles(){ for(auto model : mList){ auto contentModel = model.objectCast(); contentModel->removeDownloadedFile(); contentModel->removeThumbnail(); } } QSharedPointer ContentListModel::getContentModel(std::shared_ptr content){ for(auto item : mList){ auto c = item.objectCast(); if(c->getContent() == content) return c; } if(content->isFileTransfer() || content->isFile() || content->isFileEncrypted()){ for(auto item : mList){// Content object can be different for file (like while data transfer) auto c = item.objectCast(); if(c->getContent()->getFilePath() == content->getFilePath()) return c; } } return nullptr; } void ContentListModel::updateContent(std::shared_ptr oldContent, std::shared_ptr newContent){ int row = 0; for(auto content = mList.begin() ; content != mList.end() ; ++content, ++row){ auto contentModel = content->objectCast(); if( contentModel->getContent() == oldContent){ mList.replace(row, QSharedPointer::create(newContent, contentModel->getChatMessageModel())); emit dataChanged(index(row,0), index(row,0)); return; } } } void ContentListModel::updateContents(ChatMessageModel * messageModel){ std::list> contents = messageModel->getChatMessage()->getContents() ; int count = 0; beginResetModel(); for(auto content : contents){ if( count >= mList.size()){// New content mList.insert(count, QSharedPointer::create(content, messageModel)); }else if(mList.at(count).objectCast()->getContent() != content){ // This content is not at its place int c = count + 1; while( c < mList.size() && mList.at(c).objectCast()->getContent() != content) ++c; if( c < mList.size()){// Found => swap position #if QT_VERSION < QT_VERSION_CHECK(5, 13, 0) mList.swap(count, c); #else mList.swapItemsAt(count, c); #endif }else{// content is new mList.insert(count, QSharedPointer::create(content, messageModel)); } } ++count; } if(count < mList.size())// Remove all old contents mList.erase(mList.begin()+count, mList.end()); endResetModel(); } void ContentListModel::updateAllTransferData(){ emit updateTransferDataRequested(); } void ContentListModel::downloaded(){ for(auto content : mList) content.objectCast()->createThumbnail(); }linphone-desktop-5.0.2/linphone-app/src/components/content/ContentListModel.hpp000066400000000000000000000041061434616504300277700ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTENT_LIST_MODEL_H_ #define CONTENT_LIST_MODEL_H_ #include // ============================================================================= #include #include #include #include "app/proxyModel/ProxyListModel.hpp" class ContentModel; class ChatMessageModel; class ContentListModel : public ProxyListModel { Q_OBJECT public: ContentListModel (ChatMessageModel * message, QObject * parent = nullptr); int count(); QSharedPointer add(std::shared_ptr content); void addFile(const QString& path); Q_INVOKABLE void remove(ContentModel * model); void clear(); void removeDownloadedFiles(); QSharedPointer getContentModel(std::shared_ptr content);// Return the contentModel by checking Content, or if it is the same file. void updateContent(std::shared_ptr oldContent, std::shared_ptr newContent); void updateContents(ChatMessageModel * messageModel); void updateAllTransferData(); void downloaded(); // Contents have been downloaded, update all contents signals: void updateTransferDataRequested(); void contentsChanged(); private: ChatMessageModel * mParent; }; Q_DECLARE_METATYPE(std::shared_ptr) #endif linphone-desktop-5.0.2/linphone-app/src/components/content/ContentModel.cpp000066400000000000000000000236141434616504300271340ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ContentModel.hpp" #include #include #include #include #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" #include "components/chat-events/ChatMessageModel.hpp" #include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "components/Components.hpp" // ============================================================================= ContentModel::ContentModel(ChatMessageModel* chatModel) : mAppData(chatModel ? QString::fromStdString(chatModel->getChatMessage()->getAppdata()) : ""){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mChatMessageModel = chatModel; mWasDownloaded = false; mFileOffset = 0; } ContentModel::ContentModel(std::shared_ptr content, ChatMessageModel* chatModel) : mAppData(chatModel ? QString::fromStdString(chatModel->getChatMessage()->getAppdata()) : ""){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mChatMessageModel = chatModel; mWasDownloaded = false; mFileOffset = 0; setContent(content); } std::shared_ptr ContentModel::getContent()const{ return mContent; } ChatMessageModel * ContentModel::getChatMessageModel()const{ return mChatMessageModel; } quint64 ContentModel::getFileSize() const{ auto s = mContent->getFileSize(); return (quint64)s; } QString ContentModel::getName() const{ QString name = QString::fromStdString(mContent->getName()); if( name == "") { // Try to find the name from file Path QString fileName = QString::fromStdString(mContent->getFilePath()); if(fileName != ""){ name = QFileInfo(fileName).baseName(); } } return name; } QString ContentModel::getThumbnail() const{ return mThumbnail; } QString ContentModel::getFilePath() const{ return Utils::coreStringToAppString(mContent->getFilePath()); } QString ContentModel::getUtf8Text() const{ return QString::fromStdString(mContent->getUtf8Text()); } ConferenceInfoModel * ContentModel::getConferenceInfoModel(){ if( !mConferenceInfoModel && isIcalendar()){ auto conferenceInfo = linphone::Factory::get()->createConferenceInfoFromIcalendarContent(mContent); if(conferenceInfo) mConferenceInfoModel = ConferenceInfoModel::create(conferenceInfo); } return mConferenceInfoModel.get(); } void ContentModel::setFileOffset(quint64 fileOffset){ if( mFileOffset != fileOffset) { mFileOffset = fileOffset; emit fileOffsetChanged(); } } void ContentModel::setThumbnail(const QString& data){ if( mThumbnail != data) { mThumbnail = data; emit thumbnailChanged(); } } void ContentModel::setWasDownloaded(bool wasDownloaded){ if( mWasDownloaded != wasDownloaded) { mWasDownloaded = wasDownloaded; emit wasDownloadedChanged(); } } void ContentModel::setContent(std::shared_ptr content){ mContent = content; emit nameChanged(); mConferenceInfoModel = nullptr; if(isFile() || isFileEncrypted() || isFileTransfer() ){ QString path = Utils::coreStringToAppString(mContent->getFilePath()); if (!path.isEmpty()) createThumbnail(); } } bool ContentModel::isFile() const{ return mContent->isFile(); } bool ContentModel::isFileEncrypted() const{ return mContent->isFileEncrypted(); } bool ContentModel::isFileTransfer() const{ return mContent->isFileTransfer(); } bool ContentModel::isIcalendar() const{ return mContent->isIcalendar(); } bool ContentModel::isMultipart() const{ return mContent->isMultipart(); } bool ContentModel::isText() const{ return mContent->isText(); } bool ContentModel::isVoiceRecording()const{ return mContent->isVoiceRecording(); } int ContentModel::getFileDuration() const { return mContent->getFileDuration(); } // Create a thumbnail from the first content that have a file and store it in Appdata void ContentModel::createThumbnail (const bool& force) { if(force || isFile() || isFileEncrypted() || isFileTransfer()){ QString id; QString path = getFilePath(); auto appdata = ChatMessageModel::AppDataManager(mChatMessageModel ? QString::fromStdString(mChatMessageModel->getChatMessage()->getAppdata()) : ""); if(!appdata.mData.contains(path) || !QFileInfo(QString::fromStdString(Paths::getThumbnailsDirPath())+appdata.mData[path]).isFile()){ // File don't exist. Create the thumbnail QImage image(path); if( image.isNull()){// Try to determine format from headers QImageReader reader(path); reader.setDecideFormatFromContent(true); QByteArray format = reader.format(); if(!format.isEmpty()) image = QImage(path, format); } if (!image.isNull()){ int rotation = 0; QExifImageHeader exifImageHeader; if (exifImageHeader.loadFromJpeg(path)) rotation = int(exifImageHeader.value(QExifImageHeader::ImageTag::Orientation).toShort()); QImage thumbnail = image.scaled( Constants::ThumbnailImageFileWidth, Constants::ThumbnailImageFileHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation ); if (rotation != 0) { QTransform transform; if (rotation == 3 || rotation == 4) transform.rotate(180); else if (rotation == 5 || rotation == 6) transform.rotate(90); else if (rotation == 7 || rotation == 8) transform.rotate(-90); thumbnail = thumbnail.transformed(transform); if (rotation == 2 || rotation == 4 || rotation == 5 || rotation == 7) thumbnail = thumbnail.mirrored(true, false); } QString uuid = QUuid::createUuid().toString(); id = QStringLiteral("%1.jpg").arg(uuid.mid(1, uuid.length() - 2)); if (!thumbnail.save(QString::fromStdString(Paths::getThumbnailsDirPath()) + id , "jpg", 100)) { qWarning() << QStringLiteral("Unable to create thumbnail of: `%1`.").arg(path); }else{ appdata.mData[path] = id; mAppData.mData[path] = id; if(mChatMessageModel) mChatMessageModel->getChatMessage()->setAppdata(appdata.toString().toStdString()); } } } if( path != ""){ setWasDownloaded( !path.isEmpty() && QFileInfo(path).isFile()); if(appdata.mData.contains(path) && !appdata.mData[path].isEmpty()) setThumbnail(QStringLiteral("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(appdata.mData[path])); } } } void ContentModel::removeThumbnail(){ for(QMap::iterator itData = mAppData.mData.begin() ; itData != mAppData.mData.end() ; ++itData){ QString thumbnailPath = QString::fromStdString(Paths::getThumbnailsDirPath()) +itData.value(); if( QFileInfo(thumbnailPath).isFile()){ QFile(thumbnailPath).remove(); } } mAppData.mData.clear(); } void ContentModel::removeDownloadedFile(){ QString path = getFilePath(); if( path != ""){ QFile(path).remove(); } } void ContentModel::downloadFile(){ switch (mChatMessageModel->getState()) { case LinphoneEnums::ChatMessageStateDelivered: case LinphoneEnums::ChatMessageStateDeliveredToUser: case LinphoneEnums::ChatMessageStateDisplayed: case LinphoneEnums::ChatMessageStateFileTransferDone: break; case LinphoneEnums::ChatMessageStateFileTransferInProgress: return; default: qWarning() << QStringLiteral("Wrong message state when requesting downloading, state=%1.").arg(mChatMessageModel->getState()); } bool soFarSoGood; QString filename = getName();//mFileTransfertContent->getName(); const QString safeFilePath = Utils::getSafeFilePath( QStringLiteral("%1%2") .arg(CoreManager::getInstance()->getSettingsModel()->getDownloadFolder()) .arg(filename), &soFarSoGood ); if (!soFarSoGood) { qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(filename); return; } mContent->setFilePath(Utils::appStringToCoreString(safeFilePath)); if( !mContent->isFileTransfer()){ QMessageBox::warning(nullptr, "Download File", "This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it"); }else { if (!mChatMessageModel->getChatMessage()->downloadContent(mContent)) qWarning() << QStringLiteral("Unable to download file of entry %1.").arg(filename); } } void ContentModel::cancelDownloadFile(){ if(mChatMessageModel && mChatMessageModel->getChatMessage()) { if(mChatMessageModel->isOutgoing() ){ mChatMessageModel->deleteEvent();// Uploading is cancelling : Delete event to have clean history. emit mChatMessageModel->remove(mChatMessageModel); }else mChatMessageModel->getChatMessage()->cancelFileTransfer(); } } void ContentModel::openFile (bool showDirectory) { if (mChatMessageModel && ((!mWasDownloaded && !mChatMessageModel->isOutgoing()) || mContent->getFilePath() == "")) { downloadFile(); }else{ QFileInfo info( Utils::coreStringToAppString(mContent->getFilePath())); showDirectory = showDirectory || !info.exists(); if(!QDesktopServices::openUrl( QUrl(QStringLiteral("file:///%1").arg(showDirectory ? info.absolutePath() : info.absoluteFilePath())) ) && !showDirectory){ QDesktopServices::openUrl( QUrl(QStringLiteral("file:///%1").arg(info.absolutePath())) ); } } } void ContentModel::updateTransferData(){ }linphone-desktop-5.0.2/linphone-app/src/components/content/ContentModel.hpp000066400000000000000000000071541434616504300271420ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTENT_MODEL_H_ #define CONTENT_MODEL_H_ #include // ============================================================================= #include #include #include #include #include "components/chat-events/ChatMessageModel.hpp" class ChatMessageModel; class ConferenceInfoModel; class ContentModel : public QObject{ Q_OBJECT public: ContentModel(ChatMessageModel* chatModel); ContentModel(std::shared_ptr content, ChatMessageModel* chatModel); Q_PROPERTY(quint64 fileSize READ getFileSize NOTIFY fileSizeChanged) Q_PROPERTY(QString name READ getName NOTIFY nameChanged) Q_PROPERTY(quint64 fileOffset MEMBER mFileOffset WRITE setFileOffset NOTIFY fileOffsetChanged) Q_PROPERTY(QString thumbnail READ getThumbnail WRITE setThumbnail NOTIFY thumbnailChanged) Q_PROPERTY(bool wasDownloaded MEMBER mWasDownloaded WRITE setWasDownloaded NOTIFY wasDownloadedChanged) Q_PROPERTY(QString filePath READ getFilePath CONSTANT) Q_PROPERTY(ChatMessageModel * chatMessageModel READ getChatMessageModel CONSTANT) Q_PROPERTY(ConferenceInfoModel * conferenceInfoModel READ getConferenceInfoModel CONSTANT) Q_PROPERTY(QString text READ getUtf8Text CONSTANT) std::shared_ptr getContent()const; ChatMessageModel * getChatMessageModel()const; quint64 getFileSize() const; QString getName() const; QString getThumbnail() const; QString getFilePath() const; QString getUtf8Text() const; ConferenceInfoModel * getConferenceInfoModel();//Create a conference Info if not set void setFileOffset(quint64 fileOffset); void setThumbnail(const QString& data); void setWasDownloaded(bool wasDownloaded); void setContent(std::shared_ptr content); Q_INVOKABLE bool isFile() const; Q_INVOKABLE bool isFileEncrypted() const; Q_INVOKABLE bool isFileTransfer() const; Q_INVOKABLE bool isIcalendar() const; Q_INVOKABLE bool isMultipart() const; Q_INVOKABLE bool isText() const; Q_INVOKABLE bool isVoiceRecording()const; Q_INVOKABLE int getFileDuration() const; void createThumbnail (const bool& force = false); void removeThumbnail (); void removeDownloadedFile(); Q_INVOKABLE void downloadFile(); Q_INVOKABLE void cancelDownloadFile(); Q_INVOKABLE void openFile (bool showDirectory = false); QString mThumbnail; bool mWasDownloaded; quint64 mFileOffset; public slots: void updateTransferData(); signals: void fileSizeChanged(); void nameChanged(); void thumbnailChanged(); void fileOffsetChanged(); void wasDownloadedChanged(); private: std::shared_ptr mContent; ChatMessageModel* mChatMessageModel; ChatMessageModel::AppDataManager mAppData; // Used if there is no Chat Message model set. QSharedPointer mConferenceInfoModel; }; Q_DECLARE_METATYPE(QSharedPointer) #endif linphone-desktop-5.0.2/linphone-app/src/components/content/ContentProxyModel.cpp000066400000000000000000000067051434616504300302000ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "ContentProxyModel.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "components/chat/ChatModel.hpp" #include "components/chat-events/ChatMessageModel.hpp" #include "ContentListModel.hpp" // ============================================================================= ContentProxyModel::ContentProxyModel (QObject * parent) : QSortFilterProxyModel(parent){ setContentListModel(CoreManager::getInstance()->getChatModel()->getContentListModel().get()); } ChatMessageModel * ContentProxyModel::getChatMessageModel() const{ return nullptr; } void ContentProxyModel::setChatMessageModel(ChatMessageModel * message){ if(message){ setSourceModel(message->getContents().get()); sort(0); } emit chatMessageModelChanged(); } void ContentProxyModel::setContentListModel(ContentListModel * model){ setSourceModel(model); sort(0); emit chatMessageModelChanged(); } void ContentProxyModel::addFile(const QString& path){ ContentListModel* model = qobject_cast(sourceModel()); model->addFile(path); } bool ContentProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent ) const { Q_UNUSED(sourceRow) Q_UNUSED(sourceParent) return true; } bool ContentProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const ContentModel *contentA = sourceModel()->data(left).value(); const ContentModel *contentB = sourceModel()->data(right).value(); bool aIsForward = contentA->getChatMessageModel() && contentA->getChatMessageModel()->isForward(); bool aIsReply = contentA->getChatMessageModel() && contentA->getChatMessageModel()->isReply(); bool aIsVoiceRecording = contentA->isVoiceRecording(); bool aIsFile = contentA->isFile() || contentA->isFileEncrypted() || contentA->isFileTransfer(); bool aIsText = contentA->isText() ; bool bIsForward = contentB->getChatMessageModel() && contentB->getChatMessageModel()->isForward(); bool bIsReply = contentB->getChatMessageModel() && contentB->getChatMessageModel()->isReply(); bool bIsVoiceRecording = contentB->isVoiceRecording(); bool bIsFile = contentB->isFile() || contentB->isFileEncrypted() || contentB->isFileTransfer(); bool bIsText = contentB->isText() ; return !bIsForward && (aIsForward || !bIsReply && (aIsReply || !bIsVoiceRecording && (aIsVoiceRecording || !bIsFile && (aIsFile || aIsText && !bIsText ) ) ) ); } void ContentProxyModel::remove(ContentModel * model){ qobject_cast(sourceModel())->remove(model); } void ContentProxyModel::clear(){ qobject_cast(sourceModel())->clear(); }linphone-desktop-5.0.2/linphone-app/src/components/content/ContentProxyModel.hpp000066400000000000000000000037071434616504300302040ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONTENT_PROXY_MODEL_H_ #define CONTENT_PROXY_MODEL_H_ #include // ============================================================================= #include #include #include #include class ContentListModel; class ContentModel; class ChatMessageModel; class ContentProxyModel : public QSortFilterProxyModel { Q_OBJECT public: ContentProxyModel (QObject *parent = nullptr); Q_PROPERTY(ChatMessageModel * chatMessageModel READ getChatMessageModel WRITE setChatMessageModel NOTIFY chatMessageModelChanged) ChatMessageModel * getChatMessageModel() const; void setChatMessageModel(ChatMessageModel * message); Q_INVOKABLE void setContentListModel(ContentListModel * model); Q_INVOKABLE void addFile(const QString& path); Q_INVOKABLE void remove(ContentModel * model); Q_INVOKABLE void clear(); signals: void chatMessageModelChanged(); protected: virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; std::shared_ptr mContents; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/core/000077500000000000000000000000001434616504300233055ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/core/CoreHandlers.cpp000066400000000000000000000301631434616504300263650ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "app/App.hpp" #include "components/call/CallModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/notifier/Notifier.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "utils/Utils.hpp" #include "CoreHandlers.hpp" #include "CoreManager.hpp" // ============================================================================= using namespace std; // ----------------------------------------------------------------------------- CoreHandlers::CoreHandlers (CoreManager *coreManager) { Q_UNUSED(coreManager) } CoreHandlers::~CoreHandlers () { } // ----------------------------------------------------------------------------- void CoreHandlers::onAccountRegistrationStateChanged ( const shared_ptr &, const shared_ptr &account, linphone::RegistrationState state, const string & ) { emit registrationStateChanged(account, state); } void CoreHandlers::onAuthenticationRequested ( const shared_ptr & core, const shared_ptr &authInfo, linphone::AuthMethod method ) { Q_UNUSED(method) if( authInfo ) { auto accounts = CoreManager::getInstance()->getAccountList(); auto itAccount = accounts.begin() ; std::string username = authInfo->getUsername(); std::string domain = authInfo->getDomain(); while(itAccount != accounts.end()) { auto contact = (*itAccount)->getParams()->getIdentityAddress(); if( contact && contact->getUsername() == username && contact->getDomain() == domain) { emit authenticationRequested(authInfo);// Send authentification request only if an account still exists return; }else ++itAccount; } } } void CoreHandlers::onCallEncryptionChanged ( const shared_ptr &, const shared_ptr &call, bool, const string & ) { emit callEncryptionChanged(call); } void CoreHandlers::onCallLogUpdated(const std::shared_ptr & core, const std::shared_ptr & callLog){ emit callLogUpdated(callLog); } void CoreHandlers::onCallStateChanged ( const shared_ptr &, const shared_ptr &call, linphone::Call::State state, const string & ) { emit callStateChanged(call, state); SettingsModel *settingsModel = CoreManager::getInstance()->getSettingsModel(); if ( call->getState() == linphone::Call::State::IncomingReceived && ( !settingsModel->getAutoAnswerStatus() || settingsModel->getAutoAnswerDelay() > 0 ) ) App::getInstance()->getNotifier()->notifyReceivedCall(call); } void CoreHandlers::onCallStatsUpdated ( const shared_ptr &, const shared_ptr &call, const shared_ptr &stats ) { call->getData("call-model").updateStats(stats); } void CoreHandlers::onCallCreated(const shared_ptr &, const shared_ptr &call) { emit callCreated(call); } void CoreHandlers::onChatRoomRead(const std::shared_ptr & core, const std::shared_ptr & chatRoom){ emit chatRoomRead(chatRoom); } void CoreHandlers::onChatRoomStateChanged( const std::shared_ptr & core, const std::shared_ptr & chatRoom, linphone::ChatRoom::State state ) { if (core->getGlobalState() == linphone::GlobalState::On) emit chatRoomStateChanged(chatRoom, state); } void CoreHandlers::onConfiguringStatus( const std::shared_ptr & core, linphone::ConfiguringState status, const std::string & message){ Q_UNUSED(core) emit setLastRemoteProvisioningState(status); if(status == linphone::ConfiguringState::Failed){ qWarning() << "Remote provisioning has failed and was removed : "<< QString::fromStdString(message); core->setProvisioningUri(""); } } void CoreHandlers::onDtmfReceived( const std::shared_ptr & lc, const std::shared_ptr & call, int dtmf) { Q_UNUSED(lc) Q_UNUSED(call) CoreManager::getInstance()->getCore()->playDtmf((char)dtmf, CallModel::DtmfSoundDelay); } void CoreHandlers::onGlobalStateChanged ( const shared_ptr &core, linphone::GlobalState gstate, const string & message ) { Q_UNUSED(core) Q_UNUSED(message) switch(gstate){ case linphone::GlobalState::On : qInfo() << "Core is running " << QString::fromStdString(message); emit coreStarted(); break; case linphone::GlobalState::Off : qInfo() << "Core is stopped " << QString::fromStdString(message); emit coreStopped(); break; case linphone::GlobalState::Startup : // Usefull to start core iterations qInfo() << "Core is starting " << QString::fromStdString(message); emit coreStarting(); break; default:{} } } void CoreHandlers::onIsComposingReceived ( const shared_ptr &, const shared_ptr &room ) { emit isComposingChanged(room); } void CoreHandlers::onLogCollectionUploadStateChanged ( const shared_ptr &, linphone::Core::LogCollectionUploadState state, const string &info ) { emit logsUploadStateChanged(state, info); } void CoreHandlers::onLogCollectionUploadProgressIndication ( const shared_ptr &, size_t, size_t ) { // TODO; } void CoreHandlers::onMessageReceived ( const shared_ptr &core, const shared_ptr &chatRoom, const shared_ptr &message ) { onMessagesReceived(core, chatRoom, std::list>{message}); } void CoreHandlers::onMessagesReceived ( const shared_ptr &core, const shared_ptr &chatRoom, const std::list> &messages ) { std::list> messagesToSignal; std::list> messagesToNotify; CoreManager *coreManager = CoreManager::getInstance(); SettingsModel *settingsModel = coreManager->getSettingsModel(); const App *app = App::getInstance(); QStringList notNotifyReasons; for(auto message : messages){ if( !message || message->isOutgoing() ) continue; messagesToSignal.push_back(message); // 1. Do not notify if chat is not activated. if (chatRoom->getCurrentParams()->getEncryptionBackend() == linphone::ChatRoomEncryptionBackend::None && !settingsModel->getStandardChatEnabled() || chatRoom->getCurrentParams()->getEncryptionBackend() != linphone::ChatRoomEncryptionBackend::None && !settingsModel->getSecureChatEnabled()) continue; // 2. Notify with Notification popup. if (coreManager->getSettingsModel()->getChatNotificationsEnabled() && (!app->hasFocus() || !Utils::isMe(chatRoom->getLocalAddress())) && !message->isRead())// On aggregation, the list can contains already displayed messages. messagesToNotify.push_back(message); else{ notNotifyReasons.push_back( "NotifEnabled=" + QString::number(coreManager->getSettingsModel()->getChatNotificationsEnabled()) +" focus=" +QString::number(app->hasFocus()) +" isMe=" +QString::number(Utils::isMe(chatRoom->getLocalAddress())) +" isRead=" +QString::number(message->isRead()) ); } } if( messagesToSignal.size() > 0) emit messagesReceived(messagesToSignal); if( messagesToNotify.size() > 0) app->getNotifier()->notifyReceivedMessages(messagesToNotify); else if( notNotifyReasons.size() > 0) qInfo() << "Notification received but was not selected to popup. Reasons : \n" << notNotifyReasons.join("\n"); // 3. Notify with sound. if( messagesToNotify.size() > 0) { if (!coreManager->getSettingsModel()->getChatNotificationsEnabled() || !settingsModel->getChatNotificationSoundEnabled()) return; if ( !app->hasFocus() || !CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(chatRoom, false) ) core->playLocal(Utils::appStringToCoreString(settingsModel->getChatNotificationSoundPath())); } } void CoreHandlers::onNotifyPresenceReceivedForUriOrTel ( const shared_ptr &, const shared_ptr &, const string &uriOrTel, const shared_ptr &presenceModel ) { emit presenceReceived(Utils::coreStringToAppString(uriOrTel), presenceModel); } void CoreHandlers::onNotifyPresenceReceived ( const shared_ptr &, const shared_ptr &linphoneFriend ) { // Ignore friend without vcard because the `contact-model` data doesn't exist. if (linphoneFriend->getVcard() && linphoneFriend->dataExists("contact-model")) linphoneFriend->getData("contact-model").refreshPresence(); emit presenceStatusReceived(linphoneFriend); } void CoreHandlers::onQrcodeFound(const std::shared_ptr & core, const std::string & result){ emit foundQRCode(result); } void CoreHandlers::onTransferStateChanged ( const shared_ptr &, const shared_ptr &call, linphone::Call::State state ) { switch (state) { case linphone::Call::State::EarlyUpdatedByRemote: case linphone::Call::State::EarlyUpdating: case linphone::Call::State::Idle: case linphone::Call::State::IncomingEarlyMedia: case linphone::Call::State::IncomingReceived: case linphone::Call::State::OutgoingEarlyMedia: case linphone::Call::State::OutgoingRinging: case linphone::Call::State::Paused: case linphone::Call::State::PausedByRemote: case linphone::Call::State::Pausing: case linphone::Call::State::PushIncomingReceived: case linphone::Call::State::Referred: case linphone::Call::State::Released: case linphone::Call::State::Resuming: case linphone::Call::State::StreamsRunning: case linphone::Call::State::UpdatedByRemote: case linphone::Call::State::Updating: break; // Nothing. // 1. Init. case linphone::Call::State::OutgoingInit: qInfo() << QStringLiteral("Call transfer init."); break; // 2. In progress. case linphone::Call::State::OutgoingProgress: qInfo() << QStringLiteral("Call transfer in progress."); break; // 3. Done. case linphone::Call::State::Connected: qInfo() << QStringLiteral("Call transfer succeeded."); emit callTransferSucceeded(call); break; // 4. Error. case linphone::Call::State::End: case linphone::Call::State::Error: qWarning() << QStringLiteral("Call transfer failed."); emit callTransferFailed(call); break; } } void CoreHandlers::onVersionUpdateCheckResultReceived ( const shared_ptr &, linphone::VersionUpdateCheckResult result, const string &version, const string &url ) { if (result == linphone::VersionUpdateCheckResult::NewVersionAvailable) App::getInstance()->getNotifier()->notifyNewVersionAvailable( Utils::coreStringToAppString(version), Utils::coreStringToAppString(url) ); } void CoreHandlers::onEcCalibrationResult( const std::shared_ptr &, linphone::EcCalibratorStatus status, int delayMs ) { emit ecCalibrationResult(status, delayMs); } //------------------------------ CONFERENCE INFO void CoreHandlers::onConferenceInfoReceived(const std::shared_ptr & core, const std::shared_ptr & conferenceInfo) { qDebug() << "onConferenceInfoReceived: " << conferenceInfo->getUri()->asString().c_str(); emit conferenceInfoReceived(conferenceInfo); } linphone-desktop-5.0.2/linphone-app/src/components/core/CoreHandlers.hpp000066400000000000000000000162071434616504300263750ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CORE_HANDLERS_H_ #define CORE_HANDLERS_H_ #include #include // ============================================================================= class CoreManager; class QMutex; class CoreHandlers : public QObject, public linphone::CoreListener { Q_OBJECT public: CoreHandlers (CoreManager *coreManager); ~CoreHandlers (); signals: void authenticationRequested (const std::shared_ptr &authInfo); void callEncryptionChanged (const std::shared_ptr &call); void callLogUpdated(const std::shared_ptr &call); void callStateChanged (const std::shared_ptr &call, linphone::Call::State state); void callTransferFailed (const std::shared_ptr &call); void callTransferSucceeded (const std::shared_ptr &call); void callCreated(const std::shared_ptr & call); void chatRoomRead(const std::shared_ptr &chatRoom); void chatRoomStateChanged(const std::shared_ptr &chatRoom,linphone::ChatRoom::State state); void coreStarting(); void coreStarted (); void coreStopped (); void isComposingChanged (const std::shared_ptr &chatRoom); void logsUploadStateChanged (linphone::Core::LogCollectionUploadState state, const std::string &info); void messagesReceived (const std::list> &messages); void presenceReceived (const QString &sipAddress, const std::shared_ptr &presenceModel); void presenceStatusReceived(std::shared_ptr contact); void registrationStateChanged (const std::shared_ptr &account, linphone::RegistrationState state); void ecCalibrationResult(linphone::EcCalibratorStatus status, int delayMs); void setLastRemoteProvisioningState(const linphone::ConfiguringState &state); void conferenceInfoReceived(const std::shared_ptr & conferenceInfo); void foundQRCode(const std::string & result); private: // --------------------------------------------------------------------------- // Linphone callbacks. // --------------------------------------------------------------------------- void onAccountRegistrationStateChanged( const std::shared_ptr & core, const std::shared_ptr & account, linphone::RegistrationState state, const std::string & message) override; void onAuthenticationRequested ( const std::shared_ptr &core, const std::shared_ptr &authInfo, linphone::AuthMethod method ) override; void onCallEncryptionChanged ( const std::shared_ptr &core, const std::shared_ptr &call, bool on, const std::string &authenticationToken ) override; void onCallLogUpdated(const std::shared_ptr & core, const std::shared_ptr & callLog) override; void onCallStateChanged ( const std::shared_ptr &core, const std::shared_ptr &call, linphone::Call::State state, const std::string &message ) override; void onCallStatsUpdated ( const std::shared_ptr &core, const std::shared_ptr &call, const std::shared_ptr &stats ) override; void onCallCreated( const std::shared_ptr & lc, const std::shared_ptr & call ) override; void onChatRoomRead(const std::shared_ptr & core, const std::shared_ptr & chatRoom) override; void onChatRoomStateChanged( const std::shared_ptr & core, const std::shared_ptr & chatRoom, linphone::ChatRoom::State state ) override; void onConfiguringStatus( const std::shared_ptr & core, linphone::ConfiguringState status, const std::string & message) override; void onDtmfReceived( const std::shared_ptr & lc, const std::shared_ptr & call, int dtmf)override; void onGlobalStateChanged ( const std::shared_ptr &core, linphone::GlobalState gstate, const std::string &message ) override; void onIsComposingReceived ( const std::shared_ptr &core, const std::shared_ptr &room ) override; void onLogCollectionUploadStateChanged ( const std::shared_ptr &core, linphone::Core::LogCollectionUploadState state, const std::string &info ) override; void onLogCollectionUploadProgressIndication ( const std::shared_ptr &lc, size_t offset, size_t total ) override; void onMessageReceived ( const std::shared_ptr &core, const std::shared_ptr &room, const std::shared_ptr &message ) override; void onMessagesReceived ( const std::shared_ptr &core, const std::shared_ptr &room, const std::list> &messages ) override; void onNotifyPresenceReceivedForUriOrTel ( const std::shared_ptr &core, const std::shared_ptr &linphoneFriend, const std::string &uriOrTel, const std::shared_ptr &presenceModel ) override; void onNotifyPresenceReceived ( const std::shared_ptr &core, const std::shared_ptr &linphoneFriend ) override; void onQrcodeFound(const std::shared_ptr & core, const std::string & result) override; void onTransferStateChanged ( const std::shared_ptr &core, const std::shared_ptr &call, linphone::Call::State state ) override; void onVersionUpdateCheckResultReceived ( const std::shared_ptr & core, linphone::VersionUpdateCheckResult result, const std::string &version, const std::string &url ) override; void onEcCalibrationResult( const std::shared_ptr & core, linphone::EcCalibratorStatus status, int delayMs ) override; // Conference Info virtual void onConferenceInfoReceived(const std::shared_ptr & core, const std::shared_ptr & conferenceInfo) override; }; #endif // CORE_HANDLERS_H_ linphone-desktop-5.0.2/linphone-app/src/components/core/CoreManager.cpp000066400000000000000000000437621434616504300262100ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include #include #include "config.h" #include "app/paths/Paths.hpp" #include "components/calls/CallsListModel.hpp" #include "components/chat/ChatModel.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/contacts/ContactsImporterListModel.hpp" #include "components/history/HistoryModel.hpp" #include "components/ldap/LdapListModel.hpp" #include "components/recorder/RecorderManager.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #if defined(Q_OS_MACOS) #include "event-count-notifier/EventCountNotifierMacOs.hpp" #else #include "event-count-notifier/EventCountNotifierSystemTrayIcon.hpp" #endif // if defined(Q_OS_MACOS) #include "CoreHandlers.hpp" #include "CoreManager.hpp" #include #include // ============================================================================= using namespace std; // ----------------------------------------------------------------------------- CoreManager *CoreManager::mInstance=nullptr; CoreManager::CoreManager (QObject *parent, const QString &configPath) : QObject(parent), mHandlers(make_shared(this)) { mCore = nullptr; mLastRemoteProvisioningState = linphone::ConfiguringState::Skipped; CoreHandlers *coreHandlers = mHandlers.get(); QObject::connect(coreHandlers, &CoreHandlers::coreStarting, this, &CoreManager::startIterate, Qt::QueuedConnection); QObject::connect(coreHandlers, &CoreHandlers::setLastRemoteProvisioningState, this, &CoreManager::setLastRemoteProvisioningState); QObject::connect(coreHandlers, &CoreHandlers::coreStarted, this, &CoreManager::initCoreManager, Qt::QueuedConnection); QObject::connect(coreHandlers, &CoreHandlers::coreStopped, this, &CoreManager::stopIterate, Qt::QueuedConnection); QObject::connect(coreHandlers, &CoreHandlers::logsUploadStateChanged, this, &CoreManager::handleLogsUploadStateChanged); QObject::connect(coreHandlers, &CoreHandlers::callLogUpdated, this, &CoreManager::callLogsCountChanged); QTimer::singleShot(10, [this, configPath](){// Delay the creation in order to have the CoreManager instance set before createLinphoneCore(configPath); }); } CoreManager::~CoreManager(){ mCore->removeListener(mHandlers); mHandlers = nullptr;// Ordering Call destructor just to be sure (removeListener should be enough) mCore = nullptr; } // ----------------------------------------------------------------------------- void CoreManager::initCoreManager(){ qInfo() << "Init CoreManager"; mAccountSettingsModel = new AccountSettingsModel(this); mSettingsModel = new SettingsModel(this); mCallsListModel = new CallsListModel(this); mChatModel = new ChatModel(this); mContactsListModel = new ContactsListModel(this); mContactsImporterListModel = new ContactsImporterListModel(this); mLdapListModel = new LdapListModel(this); mSipAddressesModel = new SipAddressesModel(this); mEventCountNotifier = new EventCountNotifier(this); mTimelineListModel = new TimelineListModel(this); mEventCountNotifier->updateUnreadMessageCount(); QObject::connect(mEventCountNotifier, &EventCountNotifier::eventCountChanged,this, &CoreManager::eventCountChanged); migrate(); mStarted = true; qInfo() << QStringLiteral("CoreManager initialized"); emit coreManagerInitialized(); } AbstractEventCountNotifier * CoreManager::getEventCountNotifier(){ return mEventCountNotifier; } CoreManager *CoreManager::getInstance (){ return mInstance; } HistoryModel* CoreManager::getHistoryModel(){ if(!mHistoryModel){ mHistoryModel = new HistoryModel(this); emit historyModelCreated(mHistoryModel); } return mHistoryModel; } RecorderManager* CoreManager::getRecorderManager(){ if(!mRecorderManager){ mRecorderManager = new RecorderManager(this); emit recorderManagerCreated(mRecorderManager); } return mRecorderManager; } // ----------------------------------------------------------------------------- void CoreManager::init (QObject *parent, const QString &configPath) { if (mInstance) return; mInstance = new CoreManager(parent, configPath); } void CoreManager::uninit () { if (mInstance) { mInstance->stopIterate(); auto core = mInstance->mCore; mInstance->lockVideoRender();// Stop do iterations. We have to protect GUI. mInstance->unlockVideoRender(); delete mInstance; // This will also remove stored Linphone objects. mInstance = nullptr; core->stop(); if( core->getGlobalState() != linphone::GlobalState::Off) qWarning() << "Core is not off after stopping it. It may result to have multiple core instance."; } } // ----------------------------------------------------------------------------- VcardModel *CoreManager::createDetachedVcardModel () const { VcardModel *vcardModel = new VcardModel(linphone::Factory::get()->createVcard(), false); qInfo() << QStringLiteral("Create detached vcard:") << vcardModel; return vcardModel; } void CoreManager::forceRefreshRegisters () { Q_CHECK_PTR(mCore); qInfo() << QStringLiteral("Refresh registers."); mCore->refreshRegisters(); } void CoreManager::updateUnreadMessageCount(){ mEventCountNotifier->updateUnreadMessageCount(); } void CoreManager::stateChanged(Qt::ApplicationState pState){ if(mCbsTimer){ if(pState == Qt::ApplicationActive) mCbsTimer->setInterval( Constants::CbsCallInterval); else mCbsTimer->setInterval( Constants::CbsCallInterval * 2);// Reduce a little processes } } // ----------------------------------------------------------------------------- void CoreManager::sendLogs () const { Q_CHECK_PTR(mCore); qInfo() << QStringLiteral("Send logs to: `%1` from `%2`.") .arg(Utils::coreStringToAppString(mCore->getLogCollectionUploadServerUrl())) .arg(Utils::coreStringToAppString(mCore->getLogCollectionPath())); mCore->uploadLogCollection(); } void CoreManager::cleanLogs () const { Q_CHECK_PTR(mCore); mCore->resetLogCollection(); } // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- #define SET_DATABASE_PATH(DATABASE, PATH) \ do { \ qInfo() << QStringLiteral("Set `%1` path: `%2`") \ .arg( # DATABASE) \ .arg(Utils::coreStringToAppString(PATH)); \ mCore->set ## DATABASE ## DatabasePath(PATH); \ } while (0); void CoreManager::setDatabasesPaths () { SET_DATABASE_PATH(Friends, Paths::getFriendsListFilePath()); linphone_core_set_call_logs_database_path(mCore->cPtr(), Paths::getCallHistoryFilePath().c_str());// Setting the message database let SDK to migrate data if(QFile::exists(Utils::coreStringToAppString(Paths::getMessageHistoryFilePath()))){ linphone_core_set_chat_database_path(mCore->cPtr(), Paths::getMessageHistoryFilePath().c_str());// Setting the message database let SDK to migrate data QFile::remove(Utils::coreStringToAppString(Paths::getMessageHistoryFilePath())); } } #undef SET_DATABASE_PATH // ----------------------------------------------------------------------------- void CoreManager::setOtherPaths () { if (mCore->getZrtpSecretsFile().empty() || !Paths::filePathExists(mCore->getZrtpSecretsFile(), true)) mCore->setZrtpSecretsFile(Paths::getZrtpSecretsFilePath());// Use application path if Linphone default is not available qInfo() << "Using ZrtpSecrets path : " << QString::fromStdString(mCore->getZrtpSecretsFile()); if (mCore->getUserCertificatesPath().empty() || !Paths::filePathExists(mCore->getUserCertificatesPath(), true)) mCore->setUserCertificatesPath(Paths::getUserCertificatesDirPath());// Use application path if Linphone default is not available qInfo() << "Using UserCertificate path : " << QString::fromStdString(mCore->getUserCertificatesPath()); if (mCore->getRootCa().empty() || !Paths::filePathExists(mCore->getRootCa())) mCore->setRootCa(Paths::getRootCaFilePath());// Use application path if Linphone default is not available qInfo() << "Using RootCa path : " << QString::fromStdString(mCore->getRootCa()); } void CoreManager::setResourcesPaths () { shared_ptr factory = linphone::Factory::get(); factory->setMspluginsDir(Paths::getPackageMsPluginsDirPath()); factory->setTopResourcesDir(Paths::getPackageTopDirPath()); factory->setSoundResourcesDir(Paths::getPackageSoundsResourcesDirPath()); factory->setDataResourcesDir(Paths::getPackageDataDirPath()); factory->setDataDir(Paths::getAppLocalDirPath()); factory->setDownloadDir(Paths::getDownloadDirPath()); factory->setConfigDir(Paths::getConfigDirPath(true)); } // ----------------------------------------------------------------------------- void CoreManager::createLinphoneCore (const QString &configPath) { qInfo() << QStringLiteral("Launch async core creation."); // Migration of configuration and database files from GTK version of Linphone. Paths::migrate(); setResourcesPaths(); mCore = linphone::Factory::get()->createCore( Paths::getConfigFilePath(configPath), Paths::getFactoryConfigFilePath(), nullptr ); setDatabasesPaths(); // Enable LIME on your core to use encryption. mCore->enableLimeX3Dh(mCore->limeX3DhAvailable()); // Now see the CoreService.CreateGroupChatRoom to see how to create a secure chat room mCore->addListener(mHandlers); mCore->setVideoDisplayFilter("MSQOGL"); mCore->usePreviewWindow(true); mCore->enableVideoPreview(false); // Force capture/display. // Useful if the app was built without video support. // (The capture/display attributes are reset by the core in this case.) shared_ptr config = mCore->getConfig(); if (mCore->videoSupported()) { config->setInt("video", "capture", 1); config->setInt("video", "display", 1); } QString userAgent = Utils::computeUserAgent(config); mCore->setUserAgent(Utils::appStringToCoreString(userAgent), mCore->getVersion()); mCore->start(); setOtherPaths(); mCore->enableFriendListSubscription(true); mCore->enableRecordAware(true); if(mCore->getAccountCreatorUrl() == "") mCore->setAccountCreatorUrl(Constants::DefaultFlexiAPIURL); if( mCore->getAccountList().size() == 0) mCore->setLogCollectionUploadServerUrl(Constants::DefaultUploadLogsServer); } void CoreManager::updateUserAgent(){ mCore->setUserAgent(Utils::appStringToCoreString(Utils::computeUserAgent(mCore->getConfig())), mCore->getVersion()); forceRefreshRegisters(); // After setting a new device name, REGISTER need to take account it. } void CoreManager::addingAccount(const std::shared_ptr params) { if( params->getDomain() == Constants::LinphoneDomain) {// Special case for Linphone // It has been decided that if the core encryption is None, new Linphone accounts will reset it to SRTP. if( CoreManager::getInstance()->getSettingsModel()->getMediaEncryption() == SettingsModel::MediaEncryptionNone){ CoreManager::getInstance()->getSettingsModel()->setMediaEncryption(SettingsModel::MediaEncryptionSrtp); } } } void CoreManager::handleChatRoomCreated(const QSharedPointer &chatRoomModel){ emit chatRoomModelCreated(chatRoomModel); } void CoreManager::migrate () { shared_ptr config = mCore->getConfig(); auto oldLimeServerUrl = mCore->getLimeX3DhServerUrl();// core url is deprecated : If core url exists, it must be copied to all linphone accounts. int rcVersion = config->getInt(SettingsModel::UiSection, Constants::RcVersionName, 0); if (oldLimeServerUrl.empty() && rcVersion == Constants::RcVersionCurrent) return; if (rcVersion > Constants::RcVersionCurrent) { qWarning() << QStringLiteral("RC file version (%1) is more recent than app rc file version (%2)!!!") .arg(rcVersion).arg(Constants::RcVersionCurrent); return; } qInfo() << QStringLiteral("Migrate from old rc file (%1 to %2).") .arg(rcVersion).arg(Constants::RcVersionCurrent); bool setLimeServerUrl = false; for(const auto &account : getAccountList()){ auto params = account->getParams(); if( params->getDomain() == Constants::LinphoneDomain) { auto newParams = params->clone(); QString accountIdentity = (newParams->getIdentityAddress() ? newParams->getIdentityAddress()->asString().c_str() : "no-identity"); if( rcVersion < 1) { newParams->setContactParameters(Constants::DefaultContactParameters); newParams->setExpires(Constants::DefaultExpires); qInfo() << "Migrating" << accountIdentity << "for version 1. contact parameters =" << Constants::DefaultContactParameters << ", expires =" << Constants::DefaultExpires; } if( rcVersion < 2) { bool exists = newParams->getConferenceFactoryUri() != ""; setLimeServerUrl = true; if(!exists ) newParams->setConferenceFactoryUri(Constants::DefaultConferenceURI); qInfo() << "Migrating" << accountIdentity << "for version 2. Conference factory URI" << (exists ? std::string("unchanged") : std::string("= ") +Constants::DefaultConferenceURI).c_str(); // note: using std::string.c_str() to avoid having double quotes in qInfo() } if( rcVersion < 3){ newParams->enableCpimInBasicChatRoom(true); qInfo() << "Migrating" << accountIdentity << "for version 3. Enable Cpim in basic chat rooms"; } if( rcVersion < 4){ newParams->enableRtpBundle(true); qInfo() << "Migrating" << accountIdentity << "for version 4. Enable RTP bundle mode"; } if( rcVersion < 5) { bool exists = !!newParams->getAudioVideoConferenceFactoryAddress(); setLimeServerUrl = true; if( !exists) newParams->setAudioVideoConferenceFactoryAddress(Utils::interpretUrl(Constants::DefaultVideoConferenceURI)); qInfo() << "Migrating" << accountIdentity << "for version 5. Video conference factory URI" << (exists ? std::string("unchanged") : std::string("= ") +Constants::DefaultVideoConferenceURI).c_str(); // note: using std::string.c_str() to avoid having double quotes in qInfo() } if(!oldLimeServerUrl.empty()) newParams->setLimeServerUrl(oldLimeServerUrl); else if( setLimeServerUrl) newParams->setLimeServerUrl(Constants::DefaultLimeServerURL); account->setParams(newParams); } } if( !oldLimeServerUrl.empty()) { mCore->setLimeX3DhServerUrl(""); mCore->enableLimeX3Dh(true); }else if(setLimeServerUrl) { mCore->enableLimeX3Dh(true); } config->setInt(SettingsModel::UiSection, Constants::RcVersionName, Constants::RcVersionCurrent); } // ----------------------------------------------------------------------------- QString CoreManager::getVersion () const { return Utils::coreStringToAppString(mCore->getVersion()); } // ----------------------------------------------------------------------------- int CoreManager::getEventCount () const { return mEventCountNotifier ? mEventCountNotifier->getEventCount() : 0; } int CoreManager::getCallLogsCount() const{ return mCore->getCallLogs().size(); } int CoreManager::getMissedCallCount(const QString &peerAddress, const QString &localAddress)const{ return mEventCountNotifier ? mEventCountNotifier->getMissedCallCount(peerAddress, localAddress) : 0; } int CoreManager::getMissedCallCountFromLocal( const QString &localAddress)const{ return mEventCountNotifier ? mEventCountNotifier->getMissedCallCountFromLocal(localAddress) : 0; } std::list> CoreManager::getAccountList()const{ std::list> accounts; for(auto account : mCore->getAccountList()) if( account->getCustomParam("hidden") != "1") accounts.push_back(account); return accounts; } // ----------------------------------------------------------------------------- void CoreManager::startIterate(){ mCbsTimer = new QTimer(this); mCbsTimer->setInterval(Constants::CbsCallInterval); QObject::connect(mCbsTimer, &QTimer::timeout, this, &CoreManager::iterate); qInfo() << QStringLiteral("Start iterate"); mCbsTimer->start(); } void CoreManager::stopIterate(){ qInfo() << QStringLiteral("Stop iterate"); mCbsTimer->stop(); mCbsTimer->deleteLater();// allow the timer to continue its stuff mCbsTimer = nullptr; } void CoreManager::iterate () { lockVideoRender(); if(mCore) mCore->iterate(); unlockVideoRender(); } // ----------------------------------------------------------------------------- void CoreManager::handleLogsUploadStateChanged (linphone::Core::LogCollectionUploadState state, const string &info) { switch (state) { case linphone::Core::LogCollectionUploadState::InProgress: break; case linphone::Core::LogCollectionUploadState::Delivered: case linphone::Core::LogCollectionUploadState::NotDelivered: emit logsUploaded(Utils::coreStringToAppString(info)); break; } } // ----------------------------------------------------------------------------- QString CoreManager::getDownloadUrl () { return Constants::DownloadUrl; } void CoreManager::setLastRemoteProvisioningState(const linphone::ConfiguringState& state){ mLastRemoteProvisioningState = state; } bool CoreManager::isLastRemoteProvisioningGood(){ return mLastRemoteProvisioningState != linphone::ConfiguringState::Failed; } QString CoreManager::getUserAgent()const { if(mCore) return Utils::coreStringToAppString(mCore->getUserAgent()); else return EXECUTABLE_NAME " Desktop";// Just in case } linphone-desktop-5.0.2/linphone-app/src/components/core/CoreManager.hpp000066400000000000000000000156131434616504300262070ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CORE_MANAGER_H_ #define CORE_MANAGER_H_ #include #include #include #include #include // ============================================================================= class QTimer; class AbstractEventCountNotifier; class AccountSettingsModel; class CallsListModel; class ChatModel; class ChatRoomModel; class ContactsListModel; class ContactsImporterListModel; class CoreHandlers; class EventCountNotifier; class HistoryModel; class LdapListModel; class RecorderManager; class SettingsModel; class SipAddressesModel; class VcardModel; class TimelineListModel; class CoreManager : public QObject { Q_OBJECT; Q_PROPERTY(QString version READ getVersion CONSTANT) Q_PROPERTY(QString downloadUrl READ getDownloadUrl CONSTANT) Q_PROPERTY(int eventCount READ getEventCount NOTIFY eventCountChanged) Q_PROPERTY(int callLogsCount READ getCallLogsCount NOTIFY callLogsCountChanged) public: bool started () const { return mStarted; } std::shared_ptr getCore () { return mCore; } std::shared_ptr getHandlers () { Q_CHECK_PTR(mHandlers); return mHandlers; } HistoryModel* getHistoryModel(); RecorderManager* getRecorderManager(); // --------------------------------------------------------------------------- // Video render lock. // --------------------------------------------------------------------------- void lockVideoRender () { mMutexVideoRender.lock(); } void unlockVideoRender () { mMutexVideoRender.unlock(); } // --------------------------------------------------------------------------- // Singleton models. // --------------------------------------------------------------------------- CallsListModel *getCallsListModel () const { Q_CHECK_PTR(mCallsListModel); return mCallsListModel; } ContactsListModel *getContactsListModel () const { Q_CHECK_PTR(mContactsListModel); return mContactsListModel; } ContactsImporterListModel *getContactsImporterListModel () const { Q_CHECK_PTR(mContactsImporterListModel); return mContactsImporterListModel; } TimelineListModel *getTimelineListModel () const { return mTimelineListModel; } SipAddressesModel *getSipAddressesModel () const { Q_CHECK_PTR(mSipAddressesModel); return mSipAddressesModel; } SettingsModel *getSettingsModel () const { Q_CHECK_PTR(mSettingsModel); return mSettingsModel; } AccountSettingsModel *getAccountSettingsModel () const { Q_CHECK_PTR(mAccountSettingsModel); return mAccountSettingsModel; } LdapListModel *getLdapListModel() const{ return mLdapListModel; } AbstractEventCountNotifier * getEventCountNotifier(); ChatModel * getChatModel() const{ return mChatModel; } static CoreManager *getInstance (); // --------------------------------------------------------------------------- // Initialization. // --------------------------------------------------------------------------- static void init (QObject *parent, const QString &configPath); static void uninit (); // --------------------------------------------------------------------------- // Must be used in a qml scene. // Warning: The ownership of `VcardModel` is `QQmlEngine::JavaScriptOwnership` by default. Q_INVOKABLE VcardModel *createDetachedVcardModel () const; Q_INVOKABLE void forceRefreshRegisters (); void updateUnreadMessageCount(); void stateChanged(Qt::ApplicationState pState); Q_INVOKABLE void sendLogs () const; Q_INVOKABLE void cleanLogs () const; int getCallLogsCount() const; int getMissedCallCount(const QString &peerAddress, const QString &localAddress) const;// Get missed call count from a chat (useful for showing bubbles on Timelines) int getMissedCallCountFromLocal(const QString &localAddress) const;// Get missed call count from a chat (useful for showing bubbles on Timelines) std::list> getAccountList()const; static bool isInstanciated(){return mInstance!=nullptr;} Q_INVOKABLE bool isLastRemoteProvisioningGood(); Q_INVOKABLE QString getUserAgent()const; void updateUserAgent(); void addingAccount(const std::shared_ptr params); public slots: void initCoreManager(); void startIterate(); void stopIterate(); void setLastRemoteProvisioningState(const linphone::ConfiguringState& state); void createLinphoneCore (const QString &configPath);// In order to delay creation void handleChatRoomCreated(const QSharedPointer &chatRoomModel); signals: void coreManagerInitialized (); void chatRoomModelCreated (const QSharedPointer &chatRoomModel); void historyModelCreated (HistoryModel *historyModel); void recorderManagerCreated(RecorderManager *recorderModel); void logsUploaded (const QString &url); void eventCountChanged (); void callLogsCountChanged(); private: CoreManager (QObject *parent, const QString &configPath); ~CoreManager (); void setDatabasesPaths (); void setOtherPaths (); void setResourcesPaths (); void migrate (); QString getVersion () const; int getEventCount () const; void iterate (); void handleLogsUploadStateChanged (linphone::Core::LogCollectionUploadState state, const std::string &info); static QString getDownloadUrl (); std::shared_ptr mCore; std::shared_ptr mHandlers; // It is used for handling linphone. Keep it to shared_ptr. bool mStarted = false; linphone::ConfiguringState mLastRemoteProvisioningState; CallsListModel *mCallsListModel = nullptr; ContactsListModel *mContactsListModel = nullptr; ContactsImporterListModel *mContactsImporterListModel = nullptr; TimelineListModel *mTimelineListModel = nullptr; ChatModel *mChatModel = nullptr; SipAddressesModel *mSipAddressesModel = nullptr; SettingsModel *mSettingsModel = nullptr; AccountSettingsModel *mAccountSettingsModel = nullptr; EventCountNotifier *mEventCountNotifier = nullptr; HistoryModel * mHistoryModel = nullptr; LdapListModel *mLdapListModel = nullptr; RecorderManager* mRecorderManager = nullptr; QTimer *mCbsTimer = nullptr; QMutex mMutexVideoRender; static CoreManager *mInstance; }; #endif // CORE_MANAGER_H_ linphone-desktop-5.0.2/linphone-app/src/components/core/event-count-notifier/000077500000000000000000000000001434616504300273715ustar00rootroot00000000000000AbstractEventCountNotifier.cpp000066400000000000000000000131011434616504300352700ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/core/event-count-notifier/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "components/call/CallModel.hpp" #include "components/calls/CallsListModel.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/history/HistoryModel.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/Utils.hpp" #include "AbstractEventCountNotifier.hpp" // ============================================================================= using namespace std; AbstractEventCountNotifier::AbstractEventCountNotifier (QObject *parent) : QObject(parent) { CoreManager *coreManager = CoreManager::getInstance(); QObject::connect( coreManager, &CoreManager::chatRoomModelCreated, this, &AbstractEventCountNotifier::handleChatRoomModelCreated ); QObject::connect( coreManager, &CoreManager::historyModelCreated, this, &AbstractEventCountNotifier::handleHistoryModelCreated ); QObject::connect( coreManager->getHandlers().get(), &CoreHandlers::messagesReceived, this, &AbstractEventCountNotifier::updateUnreadMessageCount ); QObject::connect( coreManager->getSettingsModel(), &SettingsModel::standardChatEnabledChanged, this, &AbstractEventCountNotifier::internalnotifyEventCount ); QObject::connect( coreManager->getSettingsModel(), &SettingsModel::secureChatEnabledChanged, this, &AbstractEventCountNotifier::internalnotifyEventCount ); /* QObject::connect( coreManager->getCallsListModel(), &CallsListModel::callMissed, this, &AbstractEventCountNotifier::handleCallMissed );*/ } // ----------------------------------------------------------------------------- void AbstractEventCountNotifier::updateUnreadMessageCount () { mUnreadMessageCount = CoreManager::getInstance()->getCore()->getUnreadChatMessageCountFromActiveLocals(); internalnotifyEventCount(); } void AbstractEventCountNotifier::internalnotifyEventCount () { int n = mUnreadMessageCount + getMissedCallCount(); qInfo() << QStringLiteral("Notify event count: %1.").arg(n); n = n > 99 ? 99 : n; notifyEventCount(CoreManager::getInstance()->getSettingsModel()->getStandardChatEnabled() || CoreManager::getInstance()->getSettingsModel()->getSecureChatEnabled()? n : 0); emit eventCountChanged(); } // Get missed call from a chat (useful for showing bubbles on Timelines) int AbstractEventCountNotifier::getMissedCallCount(const QString &peerAddress, const QString &localAddress) const{ auto it = mMissedCalls.find({ Utils::cleanSipAddress(peerAddress), Utils::cleanSipAddress(localAddress) }); if (it != mMissedCalls.cend()) return *it; else return 0; } // Get missed call from a chat (useful for showing bubbles on Timelines) int AbstractEventCountNotifier::getMissedCallCountFromLocal(const QString &localAddress) const{ QString cleanAddress = Utils::cleanSipAddress(localAddress); int count = 0; for(auto it = mMissedCalls.cbegin() ; it != mMissedCalls.cend() ; ++it){ if(it.key().second == cleanAddress) count += *it; } return count; } // ----------------------------------------------------------------------------- void AbstractEventCountNotifier::handleChatRoomModelCreated (const QSharedPointer &chatRoomModel) { ChatRoomModel *chatRoomModelPtr = chatRoomModel.get(); QObject::connect( chatRoomModelPtr, &ChatRoomModel::messageCountReset, this, &AbstractEventCountNotifier::updateUnreadMessageCount ); QObject::connect( chatRoomModelPtr, &ChatRoomModel::focused, this, [this, chatRoomModelPtr]() { handleResetMissedCalls(chatRoomModelPtr); } ); QObject::connect( chatRoomModelPtr, &ChatRoomModel::messageCountReset, this, [this, chatRoomModelPtr]() { handleResetMissedCalls(chatRoomModelPtr); } ); } void AbstractEventCountNotifier::handleHistoryModelCreated (HistoryModel *historyModel) { QObject::connect(historyModel, &HistoryModel::callCountReset , this, &AbstractEventCountNotifier::handleResetAllMissedCalls); } void AbstractEventCountNotifier::handleResetAllMissedCalls () { mMissedCalls.clear(); internalnotifyEventCount(); } void AbstractEventCountNotifier::handleResetMissedCalls (ChatRoomModel *chatRoomModel) { auto it = mMissedCalls.find({ Utils::cleanSipAddress(chatRoomModel->getPeerAddress()), Utils::cleanSipAddress(chatRoomModel->getLocalAddress()) }); if (it != mMissedCalls.cend()) { mMissedCalls.erase(it); internalnotifyEventCount(); } } void AbstractEventCountNotifier::handleCallMissed (CallModel *callModel) { ++mMissedCalls[{ Utils::cleanSipAddress(callModel->getPeerAddress()), Utils::cleanSipAddress(callModel->getLocalAddress()) }]; internalnotifyEventCount(); } void AbstractEventCountNotifier::handleCallMissed (const QString& localAddress, const QString& peerAddress) { ++mMissedCalls[{ peerAddress, localAddress }]; internalnotifyEventCount(); } AbstractEventCountNotifier.hpp000066400000000000000000000050261434616504300353040ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/core/event-count-notifier/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef ABSTRACT_EVENT_COUNT_NOTIFIER_H_ #define ABSTRACT_EVENT_COUNT_NOTIFIER_H_ #include #include #include #include // ============================================================================= namespace linphone { class ChatMessage; } class CallModel; class ChatRoomModel; class HistoryModel; class AbstractEventCountNotifier : public QObject { Q_OBJECT public: AbstractEventCountNotifier (QObject *parent = Q_NULLPTR); void updateUnreadMessageCount (); int getUnreadMessageCount () const { return mUnreadMessageCount; } int getMissedCallCount () const { int t = 0; for (int n : mMissedCalls) t += n; return t; } int getEventCount () const { return mUnreadMessageCount + getMissedCallCount(); } int getMissedCallCount(const QString &peerAddress, const QString &localAddress) const;// Get missed call count from a chat (useful for showing bubbles on Timelines) int getMissedCallCountFromLocal(const QString &localAddress) const;// Get missed call count from a chat (useful for showing bubbles on Timelines) signals: void eventCountChanged (); public slots: void handleCallMissed (const QString& localAddress, const QString& peerAddress); void handleResetAllMissedCalls (); void handleResetMissedCalls (ChatRoomModel *chatRoomModel); void handleCallMissed (CallModel *callModel); protected: virtual void notifyEventCount (int n) = 0; private: using ConferenceId = QPair; void internalnotifyEventCount (); void handleChatRoomModelCreated (const QSharedPointer &chatRoomModel); void handleHistoryModelCreated (HistoryModel *historyModel); QHash mMissedCalls; int mUnreadMessageCount = 0; }; #endif // ABSTRACT_EVENT_COUNT_NOTIFIER_H_ EventCountNotifierMacOs.hpp000066400000000000000000000024631434616504300345450ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/core/event-count-notifier/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef EVENT_COUNT_NOTIFIER_MAC_OS_H_ #define EVENT_COUNT_NOTIFIER_MAC_OS_H_ #include "AbstractEventCountNotifier.hpp" // ============================================================================= extern "C" void notifyEventCountMacOs (int n); class EventCountNotifier : public AbstractEventCountNotifier { public: EventCountNotifier (QObject *parent = Q_NULLPTR) : AbstractEventCountNotifier(parent) {} void notifyEventCount (int n) override { notifyEventCountMacOs(n); } }; #endif // EVENT_COUNT_NOTIFIER_MAC_OS_H_ EventCountNotifierMacOs.m000066400000000000000000000022111434616504300342010ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/core/event-count-notifier/* * EventCountNotifierMacOs.m * Copyright (C) 2017-2018 Belledonne Communications, Grenoble, France * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Created on: June 30, 2017 * Author: Ghislain MARY */ #import // ============================================================================= void notifyEventCountMacOs (int n) { NSString *badgeStr = (n > 0) ? [NSString stringWithFormat:@"%d", n] : @""; [[NSApp dockTile] setBadgeLabel:badgeStr]; } EventCountNotifierSystemTrayIcon.cpp000066400000000000000000000070471434616504300364760ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/core/event-count-notifier/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "components/other/colors/ColorListModel.hpp" #include "EventCountNotifierSystemTrayIcon.hpp" // ============================================================================= namespace { constexpr int IconWidth = 256; constexpr int IconHeight = 256; constexpr int IconCounterBackgroundRadius = 100; constexpr int IconCounterBlinkInterval = 1000; constexpr int IconCounterTextPixelSize = 144; } EventCountNotifier::EventCountNotifier (QObject *parent) : AbstractEventCountNotifier(parent) { QSvgRenderer renderer((QString(Constants::WindowIconPath))); if (!renderer.isValid()) qFatal("Invalid SVG Image."); QPixmap buf(IconWidth, IconHeight); buf.fill(QColor(Qt::transparent)); QPainter painter(&buf); renderer.render(&painter); mBuf = new QPixmap(buf); mBufWithCounter = new QPixmap(); mBlinkTimer = new QTimer(this); mBlinkTimer->setInterval(IconCounterBlinkInterval); QObject::connect(mBlinkTimer, &QTimer::timeout, this, &EventCountNotifier::update); Utils::connectOnce( App::getInstance(), &App::focusWindowChanged, this, &EventCountNotifier::updateUnreadMessageCount ); } EventCountNotifier::~EventCountNotifier () { delete mBuf; delete mBufWithCounter; } void EventCountNotifier::notifyEventCount (int n) { QSystemTrayIcon *sysTrayIcon = App::getInstance()->getSystemTrayIcon(); if (!sysTrayIcon) return; if (!n) { mBlinkTimer->stop(); sysTrayIcon->setIcon(QIcon(*mBuf)); return; } *mBufWithCounter = *mBuf; QPainter p(mBufWithCounter); const int width = mBufWithCounter->width(); const int height = mBufWithCounter->height(); // Draw background. { p.setBrush(App::getInstance()->getColorListModel()->addImageColor("Logo_tray_blink_bg", Constants::WindowIconPath,"b")->getColor()); p.drawEllipse(QPointF(width / 2, height / 2), IconCounterBackgroundRadius, IconCounterBackgroundRadius); } // Draw text. { QFont font = p.font(); font.setPixelSize(IconCounterTextPixelSize); p.setFont(font); p.setPen(QPen(App::getInstance()->getColorListModel()->addImageColor("Logo_tray_blink_fg", Constants::WindowIconPath,"ai")->getColor(), 1)); p.drawText(QRect(0, 0, width, height), Qt::AlignCenter, QString::number(n)); } // Change counter. mBlinkTimer->stop(); mBlinkTimer->start(); mDisplayCounter = true; update(); } void EventCountNotifier::update () { QSystemTrayIcon *sysTrayIcon = App::getInstance()->getSystemTrayIcon(); if(sysTrayIcon) sysTrayIcon->setIcon(QIcon(mDisplayCounter ? *mBufWithCounter : *mBuf)); mDisplayCounter = !mDisplayCounter; } EventCountNotifierSystemTrayIcon.hpp000066400000000000000000000026601434616504300364770ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/core/event-count-notifier/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef EVENT_COUNT_NOTIFIER_SYSTEM_TRAY_ICON_H_ #define EVENT_COUNT_NOTIFIER_SYSTEM_TRAY_ICON_H_ #include "AbstractEventCountNotifier.hpp" // ============================================================================= class QTimer; class EventCountNotifier : public AbstractEventCountNotifier { public: EventCountNotifier (QObject *parent = Q_NULLPTR); ~EventCountNotifier (); protected: void notifyEventCount (int n) override; private: void update (); const QPixmap *mBuf = nullptr; QPixmap *mBufWithCounter = nullptr; QTimer *mBlinkTimer = nullptr; bool mDisplayCounter = false; }; #endif // EVENT_COUNT_NOTIFIER_SYSTEM_TRAY_ICON_H_ linphone-desktop-5.0.2/linphone-app/src/components/file/000077500000000000000000000000001434616504300232745ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/file/FileDownloader.cpp000066400000000000000000000223031434616504300266760ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "app/paths/Paths.hpp" #include "components/core/CoreManager.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/Utils.hpp" #include "FileDownloader.hpp" // ============================================================================= namespace { constexpr char cDefaultFileName[] = "download"; } static QString getDownloadFilePath (const QString &folder, const QUrl &url, const bool& overwrite) { QFileInfo fileInfo(url.path()); QString fileName = fileInfo.fileName(); if (fileName.isEmpty()) fileName = cDefaultFileName; fileName.prepend(folder); if( overwrite && QFile::exists(fileName)) QFile::remove(fileName); if (!QFile::exists(fileName)) return fileName; // Already exists, don't overwrite. QString baseName = fileInfo.completeBaseName(); if (baseName.isEmpty()) baseName = cDefaultFileName; QString suffix = fileInfo.suffix(); if (!suffix.isEmpty()) suffix.prepend("."); for (int i = 1; true; ++i) { fileName = folder + baseName + "(" + QString::number(i) + ")" + suffix; if (!QFile::exists(fileName)) break; } return fileName; } static bool isHttpRedirect (QNetworkReply *reply) { Q_CHECK_PTR(reply); int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); return statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305 || statusCode == 307 || statusCode == 308; } // ----------------------------------------------------------------------------- void FileDownloader::download () { if (mDownloading) { qWarning() << "Unable to download file. Already downloading!"; return; } setDownloading(true); QNetworkRequest request(mUrl); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); mNetworkReply = mManager.get(request); QNetworkReply *data = mNetworkReply.data(); QObject::connect(data, &QNetworkReply::readyRead, this, &FileDownloader::handleReadyData); QObject::connect(data, &QNetworkReply::finished, this, &FileDownloader::handleDownloadFinished); #if QT_VERSION > QT_VERSION_CHECK(5, 15, 0) QObject::connect(data, &QNetworkReply::errorOccurred, this, &FileDownloader::handleError); #else QObject::connect(data, QNonConstOverload::of(&QNetworkReply::error), this, &FileDownloader::handleError); #endif QObject::connect(data, &QNetworkReply::downloadProgress, this, &FileDownloader::handleDownloadProgress); #if QT_CONFIG(ssl) QObject::connect(data, &QNetworkReply::sslErrors, this, &FileDownloader::handleSslErrors); #endif if (mDownloadFolder.isEmpty()) { if(CoreManager::isInstanciated()) mDownloadFolder = CoreManager::getInstance()->getSettingsModel()->getDownloadFolder(); else mDownloadFolder = QDir::cleanPath(Utils::coreStringToAppString(Paths::getDownloadDirPath ()) + QDir::separator()); emit downloadFolderChanged(mDownloadFolder); } Q_ASSERT(!mDestinationFile.isOpen()); mDestinationFile.setFileName(getDownloadFilePath(QDir::cleanPath(mDownloadFolder) + QDir::separator(), mUrl, mOverwriteFile)); if (!mDestinationFile.open(QIODevice::WriteOnly)) emitOutputError(); else { mTimeoutReadBytes = 0; mTimeout.start(); } } bool FileDownloader::remove () { return mDestinationFile.exists() && !mDestinationFile.isOpen() && mDestinationFile.remove(); } void FileDownloader::emitOutputError () { qWarning() << QStringLiteral("Could not write into `%1` (%2).") .arg(mDestinationFile.fileName()).arg(mDestinationFile.errorString()); mNetworkReply->abort(); } void FileDownloader::cleanDownloadEnd () { mTimeout.stop(); mNetworkReply->deleteLater(); setDownloading(false); } void FileDownloader::handleReadyData () { QByteArray data = mNetworkReply->readAll(); if (mDestinationFile.write(data) == -1) emitOutputError(); } void FileDownloader::handleDownloadFinished() { if (mNetworkReply->error() != QNetworkReply::NoError) return; if (isHttpRedirect(mNetworkReply)) { qWarning() << QStringLiteral("Request was redirected."); mDestinationFile.remove(); cleanDownloadEnd(); emit downloadFailed(); } else { qInfo() << QStringLiteral("Download of %1 finished to %2").arg(mUrl.toString(), mDestinationFile.fileName()); mDestinationFile.close(); cleanDownloadEnd(); QString fileChecksum = Utils::getFileChecksum(mDestinationFile.fileName()); if( mChecksum.isEmpty() || fileChecksum == mChecksum) emit downloadFinished(mDestinationFile.fileName()); else{ qCritical() << "File cannot be downloaded : Bad checksum " << fileChecksum; mDestinationFile.remove(); emit downloadFailed(); } } } void FileDownloader::handleError (QNetworkReply::NetworkError code) { if (code != QNetworkReply::OperationCanceledError) qWarning() << QStringLiteral("Download of %1 failed: %2") .arg(mUrl.toString()).arg(mNetworkReply->errorString()); mDestinationFile.remove(); cleanDownloadEnd(); emit downloadFailed(); } void FileDownloader::handleSslErrors (const QList &sslErrors) { #if QT_CONFIG(ssl) for (const QSslError &error : sslErrors) qWarning() << QStringLiteral("SSL error: %1").arg(error.errorString()); #else Q_UNUSED(sslErrors); #endif } void FileDownloader::handleTimeout () { if (mReadBytes == mTimeoutReadBytes) { qWarning() << QStringLiteral("Download of %1 failed: timeout.").arg(mUrl.toString()); mNetworkReply->abort(); } else mTimeoutReadBytes = mReadBytes; } void FileDownloader::handleDownloadProgress (qint64 readBytes, qint64 totalBytes) { setReadBytes(readBytes); setTotalBytes(totalBytes); } // ----------------------------------------------------------------------------- QUrl FileDownloader::getUrl () const { return mUrl; } void FileDownloader::setUrl (const QUrl &url) { if (mDownloading) { qWarning() << QStringLiteral("Unable to set url, a file is downloading."); return; } if (mUrl != url) { mUrl = url; if(!QSslSocket::supportsSsl() && mUrl.scheme() == "https") { qWarning() << "Https has been requested but SSL is not supported. Fallback to http. Install manually OpenSSL libraries in your PATH."; mUrl.setScheme("http"); } emit urlChanged(mUrl); } } QString FileDownloader::getDownloadFolder () const { return mDownloadFolder; } void FileDownloader::setDownloadFolder (const QString &downloadFolder) { if (mDownloading) { qWarning() << QStringLiteral("Unable to set download folder, a file is downloading."); return; } if (mDownloadFolder != downloadFolder) { mDownloadFolder = downloadFolder; emit downloadFolderChanged(mDownloadFolder); } } QString FileDownloader::getDestinationFileName () const{ return mDestinationFile.fileName(); } void FileDownloader::setOverwriteFile(const bool &overwrite){ mOverwriteFile = overwrite; } QString FileDownloader::synchronousDownload(const QUrl &url, const QString &destinationFolder, const bool &overwriteFile){ QString filePath; FileDownloader downloader; if(url.isRelative()) qWarning() << "FileDownloader: The specified URL is not valid"; else{ bool isOver = false; bool * pIsOver = &isOver; downloader.setUrl(url); downloader.setOverwriteFile(overwriteFile); downloader.setDownloadFolder(destinationFolder); connect(&downloader, &FileDownloader::downloadFinished, [pIsOver]()mutable{ *pIsOver=true; }); connect(&downloader, &FileDownloader::downloadFailed, [pIsOver]()mutable{ *pIsOver=true; }); downloader.download(); if(QTest::qWaitFor([&]() {return isOver;}, DefaultTimeout)){ filePath = downloader.getDestinationFileName(); if(!QFile::exists(filePath)) { filePath = ""; qWarning() << "FileDownloader: Cannot download the specified file"; } } } return filePath; } QString FileDownloader::getChecksum() const{ return mChecksum; } void FileDownloader::setChecksum(const QString& code){ if(mChecksum != code) { mChecksum = code; emit checksumChanged(); } } qint64 FileDownloader::getReadBytes () const { return mReadBytes; } void FileDownloader::setReadBytes (qint64 readBytes) { if (mReadBytes != readBytes) { mReadBytes = readBytes; emit readBytesChanged(readBytes); } } qint64 FileDownloader::getTotalBytes () const { return mTotalBytes; } void FileDownloader::setTotalBytes (qint64 totalBytes) { if (mTotalBytes != totalBytes) { mTotalBytes = totalBytes; emit totalBytesChanged(totalBytes); } } bool FileDownloader::getDownloading () const { return mDownloading; } void FileDownloader::setDownloading (bool downloading) { if (mDownloading != downloading) { mDownloading = downloading; emit downloadingChanged(downloading); } } linphone-desktop-5.0.2/linphone-app/src/components/file/FileDownloader.hpp000066400000000000000000000073461434616504300267150ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef FILE_DOWNLOADER_H_ #define FILE_DOWNLOADER_H_ #include #include #include // ============================================================================= class QSslError; class FileDownloader : public QObject{ Q_OBJECT; // TODO: Add an error property to use in UI. Q_PROPERTY(QUrl url READ getUrl WRITE setUrl NOTIFY urlChanged); Q_PROPERTY(QString downloadFolder READ getDownloadFolder WRITE setDownloadFolder NOTIFY downloadFolderChanged); Q_PROPERTY(qint64 readBytes READ getReadBytes NOTIFY readBytesChanged); Q_PROPERTY(qint64 totalBytes READ getTotalBytes NOTIFY totalBytesChanged); Q_PROPERTY(bool downloading READ getDownloading NOTIFY downloadingChanged); Q_PROPERTY(QString checksum READ getChecksum WRITE setChecksum NOTIFY checksumChanged); public: FileDownloader (QObject *parent = Q_NULLPTR) : QObject(parent) { // See: https://bugreports.qt.io/browse/QTBUG-57390 mTimeout.setInterval(DefaultTimeout); QObject::connect(&mTimeout, &QTimer::timeout, this, &FileDownloader::handleTimeout); } ~FileDownloader () { if (mNetworkReply) mNetworkReply->abort(); } Q_INVOKABLE void download (); Q_INVOKABLE bool remove(); QUrl getUrl () const; void setUrl (const QUrl &url); QString getDownloadFolder () const; void setDownloadFolder (const QString &downloadFolder); QString getDestinationFileName () const; void setOverwriteFile(const bool &overwrite); static QString synchronousDownload(const QUrl &url, const QString &destinationFolder, const bool &overwriteFile);// Return the filpath. Empty if nof file could be downloaded QString getChecksum() const; void setChecksum(const QString& code); signals: void urlChanged (const QUrl &url); void downloadFolderChanged (const QString &downloadFolder); void readBytesChanged (qint64 readBytes); void totalBytesChanged (qint64 totalBytes); void downloadingChanged (bool downloading); void downloadFinished (const QString &filePath); void downloadFailed(); void checksumChanged(); private: qint64 getReadBytes () const; void setReadBytes (qint64 readBytes); qint64 getTotalBytes () const; void setTotalBytes (qint64 totalBytes); bool getDownloading () const; void setDownloading (bool downloading); void emitOutputError (); void cleanDownloadEnd (); void handleReadyData (); void handleDownloadFinished (); void handleError (QNetworkReply::NetworkError code); void handleSslErrors (const QList &errors); void handleTimeout (); void handleDownloadProgress (qint64 readBytes, qint64 totalBytes); QUrl mUrl; QString mDownloadFolder; QFile mDestinationFile; QString mChecksum; qint64 mReadBytes = 0; qint64 mTotalBytes = 0; bool mDownloading = false; bool mOverwriteFile = false; QPointer mNetworkReply; QNetworkAccessManager mManager; qint64 mTimeoutReadBytes; QTimer mTimeout; static constexpr int DefaultTimeout = 5000; }; #endif // FILE_DOWNLOADER_H_ linphone-desktop-5.0.2/linphone-app/src/components/file/FileExtractor.cpp000066400000000000000000000155661434616504300265700ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "FileExtractor.hpp" #include "FileDownloader.hpp" #include "app/paths/Paths.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" // ============================================================================= using namespace std; FileExtractor::FileExtractor (QObject *parent) : QObject(parent) {} FileExtractor::~FileExtractor () {} void FileExtractor::extract () { if (mExtracting) { qWarning() << "Unable to extract file. Already extracting!"; return; } setExtracting(true); QFileInfo fileInfo(mFile); if(!fileInfo.isReadable()){ emitExtractFailed(-1); return; } mDestinationFile = QDir::cleanPath(mExtractFolder) + QDir::separator() + (mExtractName.isEmpty() ? fileInfo.completeBaseName() : mExtractName); if(QFile::exists(mDestinationFile) && !QFile::remove(mDestinationFile)){ emitOutputError(); return; } if( mTimer == nullptr){ mTimer = new QTimer(this); QObject::connect(mTimer, &QTimer::timeout, this, &FileExtractor::handleExtraction); } #ifdef WIN32 // Test the presence of bzip2 in the system QProcess process; process.closeReadChannel(QProcess::StandardOutput); process.closeReadChannel(QProcess::StandardError); process.start("bzip2.exe",QStringList("--help") ); //int result = QProcess::execute("bzip2.exe", QStringList("--help")); if( process.error() != QProcess::FailedToStart || QProcess::execute(Utils::coreStringToAppString(Paths::getToolsDirPath())+"\\bzip2.exe", QStringList())!=-2){ mTimer->start(); }else{// Download bzip2 qWarning() << "bzip2 was not found. Downloading it."; QTimer * timer = mTimer; FileDownloader * fileDownloader = new FileDownloader(); int downloadStep = 0; fileDownloader->setUrl(QUrl(Constants::LinphoneBZip2_exe)); fileDownloader->setDownloadFolder(Utils::coreStringToAppString(Paths::getToolsDirPath())); QObject::connect(fileDownloader, &FileDownloader::totalBytesChanged, this, &FileExtractor::setTotalBytes); QObject::connect(fileDownloader, &FileDownloader::readBytesChanged, this, &FileExtractor::setReadBytes); QObject::connect(fileDownloader, &FileDownloader::downloadFinished, [fileDownloader, timer, downloadStep,this ]()mutable { if( downloadStep++ == 0){ fileDownloader->setUrl(QUrl(Constants::LinphoneBZip2_dll)); fileDownloader->download(); }else { fileDownloader->deleteLater(); QObject::disconnect(fileDownloader, &FileDownloader::totalBytesChanged, this, &FileExtractor::setTotalBytes); QObject::disconnect(fileDownloader, &FileDownloader::readBytesChanged, this, &FileExtractor::setReadBytes); timer->start(); } }); QObject::connect(fileDownloader, &FileDownloader::downloadFailed, [fileDownloader, this]() { fileDownloader->deleteLater(); emitExtractorFailed(); }); fileDownloader->download(); } #else mTimer->start(); #endif } bool FileExtractor::remove () { return QFile::exists(mDestinationFile) && QFile::remove(mDestinationFile); } QString FileExtractor::getFile () const { return mFile; } void FileExtractor::setFile (const QString &file) { if (mExtracting) { qWarning() << QStringLiteral("Unable to set file, a file is extracting."); return; } if (mFile != file) { mFile = file; emit fileChanged(mFile); } } QString FileExtractor::getExtractFolder () const { return mExtractFolder; } void FileExtractor::setExtractFolder (const QString &extractFolder) { if (mExtracting) { qWarning() << QStringLiteral("Unable to set extract folder, a file is extracting."); return; } if (mExtractFolder != extractFolder) { mExtractFolder = extractFolder; emit extractFolderChanged(mExtractFolder); } } QString FileExtractor::getExtractName () const { return mExtractName; } void FileExtractor::setExtractName (const QString &extractName) { if (mExtracting) { qWarning() << QStringLiteral("Unable to set extract name, a file is extracting."); return; } if (mExtractName != extractName) { mExtractName = extractName; emit extractNameChanged(mExtractName); } } bool FileExtractor::getExtracting () const { return mExtracting; } void FileExtractor::setExtracting (bool extracting) { if (mExtracting != extracting) { mExtracting = extracting; emit extractingChanged(extracting); } } qint64 FileExtractor::getReadBytes () const { return mReadBytes; } void FileExtractor::setReadBytes (qint64 readBytes) { mReadBytes = readBytes; emit readBytesChanged(readBytes); } qint64 FileExtractor::getTotalBytes () const { return mTotalBytes; } void FileExtractor::setTotalBytes (qint64 totalBytes) { mTotalBytes = totalBytes; emit totalBytesChanged(totalBytes); } void FileExtractor::clean () { if (mTimer) { mTimer->stop(); mTimer->deleteLater(); mTimer = nullptr; } setExtracting(false); } void FileExtractor::emitExtractorFailed () { qWarning() << QStringLiteral("Unable to extract file `%1`. bzip2 is unavailable, please install it.") .arg(mFile); clean(); emit extractFailed(); } void FileExtractor::emitExtractFailed (int error) { qWarning() << QStringLiteral("Unable to extract file with bzip2: `%1` (code: %2).") .arg(mFile).arg(error); clean(); emit extractFailed(); } void FileExtractor::emitExtractFinished () { clean(); emit extractFinished(); } void FileExtractor::emitOutputError () { qWarning() << QStringLiteral("Could not write into `%1`.") .arg(mDestinationFile); clean(); emit extractFailed(); } void FileExtractor::handleExtraction () { QString tempDestination = mDestinationFile+"."+QFileInfo(mFile).suffix(); QStringList args; args.push_back("-dq"); args.push_back(tempDestination); QFile::copy(mFile, tempDestination); #ifdef WIN32 int result = QProcess::execute("bzip2.exe", args); if( result == -2) result = QProcess::execute(Utils::coreStringToAppString(Paths::getToolsDirPath())+"\\bzip2.exe", args); #else int result = QProcess::execute("bzip2", args); #endif if(QFile::exists(tempDestination)) QFile::remove(tempDestination); if (result == 0){ setReadBytes(getTotalBytes()); emitExtractFinished(); }else if (result > 0) emitExtractFailed(result); else if(result == -2) emitExtractorFailed(); else emitOutputError(); } linphone-desktop-5.0.2/linphone-app/src/components/file/FileExtractor.hpp000066400000000000000000000057211434616504300265650ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef FILE_EXTRACTOR_H_ #define FILE_EXTRACTOR_H_ #include // ============================================================================= class QTimer; // Supports only bzip file. class FileExtractor : public QObject { Q_OBJECT; // TODO: Add an error property to use in UI. Q_PROPERTY(QString file READ getFile WRITE setFile NOTIFY fileChanged); Q_PROPERTY(QString extractFolder READ getExtractFolder WRITE setExtractFolder NOTIFY extractFolderChanged); Q_PROPERTY(QString extractName READ getExtractName WRITE setExtractName NOTIFY extractNameChanged); Q_PROPERTY(bool extracting READ getExtracting NOTIFY extractingChanged); Q_PROPERTY(qint64 readBytes READ getReadBytes NOTIFY readBytesChanged); Q_PROPERTY(qint64 totalBytes READ getTotalBytes NOTIFY totalBytesChanged); public: FileExtractor (QObject *parent = nullptr); ~FileExtractor (); Q_INVOKABLE void extract (); Q_INVOKABLE bool remove (); QString getFile () const; void setFile (const QString &file); QString getExtractFolder () const; void setExtractFolder (const QString &extractFolder); QString getExtractName () const; void setExtractName (const QString &extractName); signals: void fileChanged (const QString &file); void extractFolderChanged (const QString &extractFolder); void extractNameChanged (const QString &extractName); void readBytesChanged (qint64 readBytes); void totalBytesChanged (qint64 totalBytes); void extractingChanged (bool extracting); void extractFinished (); void extractFailed (); private: qint64 getReadBytes () const; void setReadBytes (qint64 readBytes); qint64 getTotalBytes () const; void setTotalBytes (qint64 totalBytes); bool getExtracting () const; void setExtracting (bool extracting); void clean (); void emitExtractFinished (); void emitExtractorFailed (); // Used when bzip2 cannot be used void emitExtractFailed (int error); void emitOutputError (); void handleExtraction (); QString mFile; QString mExtractFolder; QString mExtractName; QString mDestinationFile; bool mExtracting = false; qint64 mReadBytes = 0; qint64 mTotalBytes = 0; QTimer *mTimer = nullptr; }; #endif // FILE_EXTRACTOR_H_ linphone-desktop-5.0.2/linphone-app/src/components/friend/000077500000000000000000000000001434616504300236245ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/friend/FriendListListener.cpp000066400000000000000000000050651434616504300301070ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "FriendListListener.hpp" #include "../../utils/Utils.hpp" #include // ============================================================================= FriendListListener::FriendListListener(QObject *parent) : QObject(parent) { } //-------------------------------------------------------------------- void FriendListListener::onContactCreated(const std::shared_ptr & friendList, const std::shared_ptr & linphoneFriend) { qDebug() << "onContactCreated: " << Utils::coreStringToAppString(linphoneFriend->getName()); emit contactCreated(linphoneFriend); } void FriendListListener::onContactDeleted(const std::shared_ptr & friendList, const std::shared_ptr & linphoneFriend) { qDebug() << "onContactDeleted: " << Utils::coreStringToAppString(linphoneFriend->getName()); emit contactDeleted(linphoneFriend); } void FriendListListener::onContactUpdated(const std::shared_ptr & friendList, const std::shared_ptr & newFriend, const std::shared_ptr & oldFriend) { qDebug() << "onContactUpdated: " << Utils::coreStringToAppString(newFriend->getName()); emit contactUpdated(newFriend, oldFriend); } void FriendListListener::onSyncStatusChanged(const std::shared_ptr & friendList, linphone::FriendList::SyncStatus status, const std::string & message) { qDebug() << "onSyncStatusChanged: [" << (int)status<<"] " << Utils::coreStringToAppString(message); emit syncStatusChanged(status, message); } void FriendListListener::onPresenceReceived(const std::shared_ptr & friendList, const std::list> & friends) { qDebug() << "onPresenceReceived: " <. */ #ifndef FRIEND_LIST_LISTENER_H_ #define FRIEND_LIST_LISTENER_H_ #include #include // ============================================================================= class FriendListListener : public QObject, public linphone::FriendListListener { Q_OBJECT public: FriendListListener (QObject *parent = nullptr); virtual void onContactCreated(const std::shared_ptr & friendList, const std::shared_ptr & linphoneFriend) override; virtual void onContactDeleted(const std::shared_ptr & friendList, const std::shared_ptr & linphoneFriend) override; virtual void onContactUpdated(const std::shared_ptr & friendList, const std::shared_ptr & newFriend, const std::shared_ptr & oldFriend) override; virtual void onSyncStatusChanged(const std::shared_ptr & friendList, linphone::FriendList::SyncStatus status, const std::string & message) override; virtual void onPresenceReceived(const std::shared_ptr & friendList, const std::list> & friends) override; signals: void contactCreated(const std::shared_ptr & linphoneFriend); void contactDeleted(const std::shared_ptr & linphoneFriend); void contactUpdated(const std::shared_ptr & newFriend, const std::shared_ptr & oldFriend); void syncStatusChanged(linphone::FriendList::SyncStatus status, const std::string & message); void presenceReceived(const std::list> & friends); }; #endif linphone-desktop-5.0.2/linphone-app/src/components/history/000077500000000000000000000000001434616504300240565ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/history/HistoryModel.cpp000066400000000000000000000206771434616504300272200ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/notifier/Notifier.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" #include "HistoryModel.hpp" // ============================================================================= using namespace std; static inline void fillCallStartEntry (QVariantMap &dest, const shared_ptr &callLog) { dest["type"] = HistoryModel::CallEntry; dest["timestamp"] = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000); dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; dest["status"] = static_cast(callLog->getStatus()); dest["isStart"] = true; if(callLog->getConferenceInfo()) dest["title"] = Utils::coreStringToAppString(callLog->getConferenceInfo()->getSubject()); dest["sipAddress"] = Utils::coreStringToAppString(callLog->getRemoteAddress()->asString()); dest["callId"] = Utils::coreStringToAppString(callLog->getCallId()); dest["wasConference"] = callLog->wasConference(); } static inline void fillCallEndEntry (QVariantMap &dest, const shared_ptr &callLog) { dest["type"] = HistoryModel::CallEntry; dest["timestamp"] = QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000); dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; dest["status"] = static_cast(callLog->getStatus()); dest["isStart"] = false; if(callLog->getConferenceInfo()) dest["title"] = Utils::coreStringToAppString(callLog->getConferenceInfo()->getSubject()); dest["sipAddress"] = Utils::coreStringToAppString(callLog->getRemoteAddress()->asString()); dest["callId"] = Utils::coreStringToAppString(callLog->getCallId()); dest["wasConference"] = callLog->wasConference(); } // ----------------------------------------------------------------------------- HistoryModel::HistoryModel (QObject *parent) :QAbstractListModel(parent){ CoreManager *coreManager = CoreManager::getInstance(); mCoreHandlers = coreManager->getHandlers(); setSipAddresses(); CoreHandlers *coreHandlers = mCoreHandlers.get(); QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &HistoryModel::handleCallStateChanged); } HistoryModel::~HistoryModel () { } QHash HistoryModel::roleNames () const { QHash roles; roles[Roles::HistoryEntry] = "$historyEntry"; roles[Roles::SectionDate] = "$sectionDate"; return roles; } int HistoryModel::rowCount (const QModelIndex &) const { return mEntries.count(); } QVariant HistoryModel::data (const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mEntries.count()) return QVariant(); switch (role) { case Roles::HistoryEntry: { auto &data = mEntries[row].first; return QVariant::fromValue(data); } case Roles::SectionDate: return QVariant::fromValue(mEntries[row].first["timestamp"].toDate()); } return QVariant(); } bool HistoryModel::removeRow (int row, const QModelIndex &) { return removeRows(row, 1); } bool HistoryModel::removeRows (int row, int count, const QModelIndex &parent) { int limit = row + count - 1; if (row < 0 || count < 0 || limit >= mEntries.count()) return false; beginRemoveRows(parent, row, limit); for (int i = 0; i < count; ++i) { removeEntry(mEntries[row]); mEntries.removeAt(row); } endRemoveRows(); if (mEntries.count() == 0) emit allEntriesRemoved(); else if (limit == mEntries.count()) emit lastEntryRemoved(); emit CoreManager::getInstance()->callLogsCountChanged(); emit focused();// Removing rows is like having focus. Don't wait asynchronous events. return true; } void HistoryModel::setSipAddresses () { shared_ptr core = CoreManager::getInstance()->getCore(); mEntries.clear(); QElapsedTimer timer; timer.start(); // Get calls. for (auto &callLog : core->getCallLogs()) insertCall(callLog); qInfo() << QStringLiteral("HistoryModel loaded in %3 milliseconds.").arg(timer.elapsed()); } void HistoryModel::reload(){ beginResetModel(); setSipAddresses(); endResetModel(); } // ----------------------------------------------------------------------------- void HistoryModel::removeEntry (int id) { qInfo() << QStringLiteral("Removing call entry: %1.").arg(id); if (!removeRow(id)) qWarning() << QStringLiteral("Unable to remove call entry: %1").arg(id); } void HistoryModel::removeAllEntries () { qInfo() << QStringLiteral("Removing all call entries."); beginResetModel(); for (auto &entry : mEntries) removeEntry(entry); mEntries.clear(); endResetModel(); emit allEntriesRemoved(); emit CoreManager::getInstance()->callLogsCountChanged(); emit focused();// Removing all entries is like having focus. Don't wait asynchronous events. } // ----------------------------------------------------------------------------- void HistoryModel::removeEntry (HistoryEntryData &entry) { int type = entry.first["type"].toInt(); switch (type) { case HistoryModel::CallEntry: { if (entry.first["status"].toInt() == CallStatusSuccess) { // WARNING: Unable to remove symmetric call here. (start/end) // We are between `beginRemoveRows` and `endRemoveRows`. // A solution is to schedule a `removeEntry` call in the Qt main loop. shared_ptr linphonePtr = entry.second; QTimer::singleShot(0, this, [this, linphonePtr]() { auto it = find_if(mEntries.begin(), mEntries.end(), [linphonePtr](const HistoryEntryData &entry) { return entry.second == linphonePtr; }); if (it != mEntries.end()) removeEntry(int(distance(mEntries.begin(), it))); }); } CoreManager::getInstance()->getCore()->removeCallLog(static_pointer_cast(entry.second)); break; } default: qWarning() << QStringLiteral("Unknown history entry type: %1.").arg(type); } } void HistoryModel::insertCall (const shared_ptr &callLog) { linphone::Call::Status status = callLog->getStatus(); auto insertEntry = [this]( const HistoryEntryData &entry, const QList::iterator *start = nullptr ) { auto it = lower_bound(start ? *start : mEntries.begin(), mEntries.end(), entry, [](const HistoryEntryData &a, const HistoryEntryData &b) { return a.first["timestamp"] < b.first["timestamp"]; }); int row = int(distance(mEntries.begin(), it)); beginInsertRows(QModelIndex(), row, row); it = mEntries.insert(it, entry); endInsertRows(); emit layoutChanged(); return it; }; // Add start call. QVariantMap start; fillCallStartEntry(start, callLog); auto it = insertEntry(qMakePair(start, static_pointer_cast(callLog))); if (status == linphone::Call::Status::Success) { QVariantMap end; fillCallEndEntry(end, callLog); insertEntry(qMakePair(end, static_pointer_cast(callLog)), &it); } } // ----------------------------------------------------------------------------- void HistoryModel::resetMessageCount () { emit callCountReset(); } // ----------------------------------------------------------------------------- void HistoryModel::handleCallStateChanged (const shared_ptr &call, linphone::Call::State state) { if (state == linphone::Call::State::End || state == linphone::Call::State::Error) insertCall(call->getCallLog()); } linphone-desktop-5.0.2/linphone-app/src/components/history/HistoryModel.hpp000066400000000000000000000056161434616504300272210ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef HISTORY_MODEL_H_ #define HISTORY_MODEL_H_ #include #include // ============================================================================= // Fetch all N messages of the History. // ============================================================================= class CoreHandlers; class HistoryModel : public QAbstractListModel { Q_OBJECT public: enum Roles { HistoryEntry = Qt::DisplayRole, SectionDate }; enum EntryType { GenericEntry, CallEntry }; Q_ENUM(EntryType) enum CallStatus { CallStatusDeclined = int(linphone::Call::Status::Declined), CallStatusMissed = int(linphone::Call::Status::Missed), CallStatusSuccess = int(linphone::Call::Status::Success), CallStatusAborted = int(linphone::Call::Status::Aborted), CallStatusEarlyAborted = int(linphone::Call::Status::EarlyAborted), CallStatusAcceptedElsewhere = int(linphone::Call::Status::AcceptedElsewhere), CallStatusDeclinedElsewhere = int(linphone::Call::Status::DeclinedElsewhere) }; Q_ENUM(CallStatus) HistoryModel (QObject *parent = Q_NULLPTR); virtual ~HistoryModel (); int rowCount (const QModelIndex &index = QModelIndex()) const override; QHash roleNames () const override; QVariant data (const QModelIndex &index, int role) const override; bool removeRow (int row, const QModelIndex &parent = QModelIndex()); bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; void removeEntry (int id); void removeAllEntries (); void resetMessageCount (); Q_INVOKABLE void reload(); signals: void allEntriesRemoved (); void lastEntryRemoved (); void focused (); void callCountReset(); private: typedef QPair> HistoryEntryData; void setSipAddresses (); void removeEntry (HistoryEntryData &entry); void insertCall (const std::shared_ptr &callLog); void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); mutable QList mEntries; std::shared_ptr mCoreHandlers; }; #endif // HISTORY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/history/HistoryProxyModel.cpp000066400000000000000000000123641434616504300302540ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "components/core/CoreManager.hpp" #include "HistoryProxyModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" // ============================================================================= using namespace std; // Fetch the L last filtered history entries. class HistoryProxyModel::HistoryModelFilter : public QSortFilterProxyModel { public: HistoryModelFilter (QObject *parent) : QSortFilterProxyModel(parent) { } HistoryModel::EntryType getEntryTypeFilter () { return mEntryTypeFilter; } void setEntryTypeFilter (HistoryModel::EntryType type) { mEntryTypeFilter = type; invalidate(); } protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &) const override { if (mEntryTypeFilter == HistoryModel::EntryType::GenericEntry) return true; QModelIndex index = sourceModel()->index(sourceRow, 0, QModelIndex()); const QVariantMap data = index.data().toMap(); return data["type"].toInt() == mEntryTypeFilter; } private: HistoryModel::EntryType mEntryTypeFilter = HistoryModel::EntryType::GenericEntry; }; // ============================================================================= HistoryProxyModel::HistoryProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { setSourceModel(new HistoryModelFilter(this)); reload(); App *app = App::getInstance(); QObject::connect(app->getMainWindow(), &QWindow::activeChanged, this, [this]() { handleIsActiveChanged(App::getInstance()->getMainWindow()); }); QQuickWindow *callsWindow = app->getCallsWindow(); if (callsWindow) QObject::connect(callsWindow, &QWindow::activeChanged, this, [this, callsWindow]() { handleIsActiveChanged(callsWindow); }); } // ----------------------------------------------------------------------------- void HistoryProxyModel::removeAllEntries(){ auto model = CoreManager::getInstance()->getHistoryModel(); if (!model) return; model->removeAllEntries(); } void HistoryProxyModel::removeEntry (int id){ auto model = CoreManager::getInstance()->getHistoryModel(); if (!model) return; QModelIndex sourceIndex = mapToSource(index(id, 0)); model->removeEntry(static_cast(sourceModel())->mapToSource(sourceIndex).row() ); } // ----------------------------------------------------------------------------- void HistoryProxyModel::loadMoreEntries () { int count = rowCount(); int parentCount = sourceModel()->rowCount(); if (count < parentCount) { // Do not increase `mMaxDisplayedEntries` if it's not necessary... // Limit qml calls. if (count == mMaxDisplayedEntries) mMaxDisplayedEntries += EntriesChunkSize; invalidateFilter(); count = rowCount() - count; if (count > 0) emit moreEntriesLoaded(count); } } void HistoryProxyModel::setEntryTypeFilter (HistoryModel::EntryType type) { HistoryModelFilter *HistoryModelFilter = static_cast(sourceModel()); if (HistoryModelFilter->getEntryTypeFilter() != type) { HistoryModelFilter->setEntryTypeFilter(type); emit entryTypeFilterChanged(type); } } // ----------------------------------------------------------------------------- bool HistoryProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &) const { return sourceModel()->rowCount() - sourceRow <= mMaxDisplayedEntries; } // ----------------------------------------------------------------------------- void HistoryProxyModel::reload () { mMaxDisplayedEntries = EntriesChunkSize; auto model = CoreManager::getInstance()->getHistoryModel(); //model->reload(); static_cast(sourceModel())->setSourceModel(model); } void HistoryProxyModel::resetMessageCount(){ auto model = CoreManager::getInstance()->getHistoryModel(); if( model){ model->resetMessageCount(); } } // ----------------------------------------------------------------------------- static inline QWindow *getParentWindow (QObject *object) { App *app = App::getInstance(); const QWindow *mainWindow = app->getMainWindow(); const QWindow *callsWindow = app->getCallsWindow(); for (QObject *parent = object->parent(); parent; parent = parent->parent()) if (parent == mainWindow || parent == callsWindow) return static_cast(parent); return nullptr; } void HistoryProxyModel::handleIsActiveChanged (QWindow *window) { auto model = CoreManager::getInstance()->getHistoryModel(); if (model && window->isActive() && getParentWindow(this) == window) { model->focused(); model->resetMessageCount(); } } linphone-desktop-5.0.2/linphone-app/src/components/history/HistoryProxyModel.hpp000066400000000000000000000035221434616504300302550ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef HISTORY_PROXY_MODEL_H_ #define HISTORY_PROXY_MODEL_H_ #include #include "HistoryModel.hpp" // ============================================================================= class QWindow; class HistoryProxyModel : public QSortFilterProxyModel { class HistoryModelFilter; Q_OBJECT; public: HistoryProxyModel (QObject *parent = Q_NULLPTR); Q_INVOKABLE void loadMoreEntries (); Q_INVOKABLE void setEntryTypeFilter (HistoryModel::EntryType type = HistoryModel::EntryType::CallEntry); Q_INVOKABLE void removeEntry (int id); Q_INVOKABLE void removeAllEntries (); Q_INVOKABLE void resetMessageCount(); Q_INVOKABLE void reload(); signals: void moreEntriesLoaded (int n); void entryTypeFilterChanged (HistoryModel::EntryType type); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; private: void handleIsActiveChanged (QWindow *window); int mMaxDisplayedEntries = EntriesChunkSize; static constexpr int EntriesChunkSize = 50; }; #endif // HISTORY_PROXY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/ldap/000077500000000000000000000000001434616504300232755ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/ldap/LdapListModel.cpp000066400000000000000000000045701434616504300265040ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "utils/Utils.hpp" #include "LdapListModel.hpp" // ============================================================================= using namespace std; LdapListModel::LdapListModel (QObject *parent) : ProxyListModel(parent) { initLdap(); } // ----------------------------------------------------------------------------- void LdapListModel::reset(){ initLdap(); } // ----------------------------------------------------------------------------- void LdapListModel::initLdap () { CoreManager *coreManager = CoreManager::getInstance(); auto ldapList = coreManager->getCore()->getLdapList(); resetData(); for(auto ldap : ldapList){ ProxyListModel::add(QSharedPointer::create(ldap)); } } // Save if valid void LdapListModel::enable(int id, bool status){ auto item = getAt(id); if( item->isValid()){ QVariantMap config = item->getConfig(); config["enable"] = status; item->setConfig(config); item->save(); } emit dataChanged(index(id, 0), index(id, 0)); } // Create a new LdapModel and put it in the list void LdapListModel::add(){ auto ldap= QSharedPointer::create(nullptr); connect(ldap.get(), &LdapModel::indexChanged, this, &LdapListModel::indexChanged); ldap->init(); ProxyListModel::add(ldap); emit layoutChanged(); } void LdapListModel::remove (LdapModel *ldap) { int index; auto item = get(ldap, &index); if(item){ item.objectCast()->unsave(); removeRow(index); } } linphone-desktop-5.0.2/linphone-app/src/components/ldap/LdapListModel.hpp000066400000000000000000000030241434616504300265020ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef LDAP_LIST_MODEL_H_ #define LDAP_LIST_MODEL_H_ #include #include "LdapModel.hpp" #include "app/proxyModel/ProxyListModel.hpp" // ============================================================================= class CoreHandlers; class LdapListModel : public ProxyListModel { Q_OBJECT public: LdapListModel (QObject *parent = Q_NULLPTR); void reset(); // Enable the Server and save it if it is valid Q_INVOKABLE void enable(int id, bool status); // Create a Server Q_INVOKABLE void add(); // Remove a Server Q_INVOKABLE void remove (LdapModel *importer); signals: void indexChanged(); private: // --------------------------------------------------------------------------- void initLdap (); }; #endif // LDAP_LIST_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/ldap/LdapModel.cpp000066400000000000000000000213361434616504300256470ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "utils/Utils.hpp" #include "LdapModel.hpp" // ============================================================================= using namespace std; LdapModel::LdapModel (std::shared_ptr ldap, QObject *parent ) : QObject(parent){ mLdap = ldap; mIsValid = false; if(mLdap) mLdapParams = ldap->getParams()->clone(); else{ mLdapParams = CoreManager::getInstance()->getCore()->createLdapParams(); mLdapParams->setMaxResults(50); // Desktop default mLdapParams->setEnabled(false); } unset(); } void LdapModel::init(){ set(); unset(); } bool LdapModel::isValid(){ bool valid = mServerFieldError=="" && mMaxResultsFieldError=="" && mTimeoutFieldError=="" && mPasswordFieldError=="" && mBindDnFieldError=="" && mBaseObjectFieldError=="" && mFilterFieldError=="" && mNameAttributesFieldError=="" && mSipAttributesFieldError=="" && mSipDomainFieldError==""; if( valid != mIsValid){ mIsValid = valid; emit isValidChanged(); } return mIsValid; } void LdapModel::save(){ if(isValid()){ set(); if(!mLdap) { mLdap = CoreManager::getInstance()->getCore()->createLdapWithParams(mLdapParams); emit indexChanged(); }else{ int oldIndex = getIndex(); mLdap->setParams(mLdapParams); if( oldIndex != getIndex()) emit indexChanged(); } } } void LdapModel::unsave(){ if(mLdap) CoreManager::getInstance()->getCore()->removeLdap(mLdap); } QVariantMap LdapModel::getConfig(){ //return mConfig; return QVariantMap(); } void LdapModel::setConfig(const QVariantMap& config){ //mConfig = config; emit configChanged(); } void LdapModel::set(){ mLdapParams->setServer(mServer.toStdString()); mLdapParams->setCustomValue("display_name", mDisplayName.toStdString()); mLdapParams->enableSal(mUseSal); mLdapParams->enableTls(mUseTls); mLdapParams->setMaxResults(mMaxResults); mLdapParams->setTimeout(mTimeout); mLdapParams->setPassword(mPassword.toStdString()); mLdapParams->setBindDn(mBindDn.toStdString()); mLdapParams->setBaseObject(mBaseObject.toStdString()); mLdapParams->setFilter(mFilter.toStdString()); mLdapParams->setNameAttribute(mNameAttributes.toStdString()); mLdapParams->setSipAttribute(mSipAttributes.toStdString()); mLdapParams->setSipDomain(mSipDomain.toStdString()); mLdapParams->setDebugLevel( (linphone::LdapDebugLevel) mDebug); mLdapParams->setServerCertificatesVerificationMode((linphone::LdapCertVerificationMode)mVerifyServerCertificates); static int gCount = 0; mLdapParams->setAuthMethod((++gCount % 2) == 0 ? linphone::LdapAuthMethod::Anonymous : linphone::LdapAuthMethod::Simple); } void LdapModel::unset(){ mServer = QString::fromStdString(mLdapParams->getServer()); std::string t = mLdapParams->getCustomValue("display_name"); mDisplayName = QString::fromStdString(t); mUseTls = mLdapParams->tlsEnabled(); mUseSal = mLdapParams->salEnabled(); mMaxResults = mLdapParams->getMaxResults(); mTimeout = mLdapParams->getTimeout(); mPassword = QString::fromStdString(mLdapParams->getPassword()); mBindDn = QString::fromStdString(mLdapParams->getBindDn()); mBaseObject = QString::fromStdString(mLdapParams->getBaseObject()); mFilter = QString::fromStdString(mLdapParams->getFilter()); mNameAttributes = QString::fromStdString(mLdapParams->getNameAttribute()); mSipAttributes = QString::fromStdString(mLdapParams->getSipAttribute()); mSipDomain = QString::fromStdString(mLdapParams->getSipDomain()); mDebug = (int)mLdapParams->getDebugLevel(); mVerifyServerCertificates = (int)mLdapParams->getServerCertificatesVerificationMode(); testServerField(); testMaxResultsField(); testTimeoutField(); testPasswordField(); testBindDnField(); testBaseObjectField(); testFilterField(); testNameAttributesField(); testSipAttributesField(); testSipDomainField(); isValid(); } bool LdapModel::isEnabled(){ return mLdapParams->getEnabled(); } void LdapModel::setEnabled(const bool& data){ if(isValid()){ mLdapParams->setEnabled(data); save(); }else mLdapParams->setEnabled(false); emit enabledChanged(); } //------------------------------------------------------------------------------------ void LdapModel::setServer(const QString& server){ mServer = server; testServerField(); emit serverChanged(); } void LdapModel::testServerField(){ QString valid; if(mServer == "") valid = "Server must not be empty"; else{ QUrl url(mServer); if(!url.isValid()) valid = "Server is not an URL"; else if(url.scheme().left(4) != "ldap") valid = "URL must begin by a ldap scheme."; else if(url.scheme() == "ldaps") valid = "ldaps is not supported."; else valid = ""; } if( valid != mServerFieldError){ mServerFieldError = valid; emit serverFieldErrorChanged(); isValid(); } } void LdapModel::setDisplayName(const QString& displayName){ mDisplayName = displayName; emit displayNameChanged(); } void LdapModel::setMaxResults(const int& data){ mMaxResults = data; testMaxResultsField(); emit maxResultsChanged(); } void LdapModel::testMaxResultsField(){ QString valid; if(mMaxResults <= 0) valid = "Max Results must be greater than 0"; else valid = ""; if( valid != mMaxResultsFieldError){ mMaxResultsFieldError = valid; emit maxResultsFieldErrorChanged(); isValid(); } } void LdapModel::setTimeout(const int& data){ mTimeout = data; testTimeoutField(); emit timeoutChanged(); } void LdapModel::testTimeoutField(){ QString valid; if(mTimeout < 0) valid = "Timeout must be positive in seconds."; else valid = ""; if( valid != mTimeoutFieldError){ mTimeoutFieldError = valid; emit timeoutFieldErrorChanged(); isValid(); } } void LdapModel::setPassword(const QString& data){ mPassword = data; testPasswordField(); emit passwordChanged(); } void LdapModel::testPasswordField(){ QString valid = ""; if( valid != mPasswordFieldError){ mPasswordFieldError = valid; emit passwordFieldErrorChanged(); isValid(); } } void LdapModel::setBindDn(const QString& data){ mBindDn = data; testBindDnField(); emit bindDnChanged(); } void LdapModel::testBindDnField(){ QString valid; if( valid != mBindDnFieldError){ mBindDnFieldError = valid; emit bindDnFieldErrorChanged(); isValid(); } } void LdapModel::setBaseObject(const QString& data){ mBaseObject = data; testBaseObjectField(); emit baseObjectChanged(); } void LdapModel::testBaseObjectField(){ QString valid; if(mBaseObject == "") valid = "Search Base must not be empty"; else valid = ""; if( valid != mBaseObjectFieldError){ mBaseObjectFieldError = valid; emit baseObjectFieldErrorChanged(); isValid(); } } void LdapModel::setFilter(const QString& data){ mFilter = data; testFilterField(); emit filterChanged(); } void LdapModel::testFilterField(){ QString valid = ""; if( valid != mFilterFieldError){ mFilterFieldError = valid; emit filterFieldErrorChanged(); isValid(); } } void LdapModel::setNameAttributes(const QString& data){ mNameAttributes = data; testNameAttributesField(); emit nameAttributesChanged(); } void LdapModel::testNameAttributesField(){ QString valid = ""; if( valid != mNameAttributesFieldError){ mNameAttributesFieldError = valid; emit nameAttributesFieldErrorChanged(); isValid(); } } void LdapModel::setSipAttributes(const QString& data){ mSipAttributes = data; testSipAttributesField(); emit sipAttributesChanged(); } void LdapModel::testSipAttributesField(){ QString valid = ""; if( valid != mSipAttributesFieldError){ mSipAttributesFieldError = valid; emit sipAttributesFieldErrorChanged(); isValid(); } } void LdapModel::setSipDomain(const QString& data){ mSipDomain = data; testSipDomainField(); emit sipDomainChanged(); } void LdapModel::testSipDomainField(){ QString valid = ""; if( valid != mSipDomainFieldError){ mSipDomainFieldError = valid; emit sipDomainFieldErrorChanged(); isValid(); } } int LdapModel::getIndex() const{ if(mLdap) return mLdap->getIndex(); else return -2; } linphone-desktop-5.0.2/linphone-app/src/components/ldap/LdapModel.hpp000066400000000000000000000151131434616504300256500ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef LDAP_MODEL_H_ #define LDAP_MODEL_H_ #include #include #include // ============================================================================= class CoreHandlers; class LdapModel : public QObject { Q_OBJECT Q_PROPERTY(QVariantMap config READ getConfig WRITE setConfig NOTIFY configChanged) Q_PROPERTY(bool isValid MEMBER mIsValid NOTIFY isValidChanged) Q_PROPERTY(QString server MEMBER mServer WRITE setServer NOTIFY serverChanged) Q_PROPERTY(QString serverFieldError MEMBER mServerFieldError NOTIFY serverFieldErrorChanged) Q_PROPERTY(QString displayName MEMBER mDisplayName WRITE setDisplayName NOTIFY displayNameChanged) Q_PROPERTY(bool useTls MEMBER mUseTls NOTIFY useTlsChanged) Q_PROPERTY(bool useSal MEMBER mUseSal NOTIFY useSalChanged) Q_PROPERTY(int maxResults MEMBER mMaxResults WRITE setMaxResults NOTIFY maxResultsChanged) Q_PROPERTY(QString maxResultsFieldError MEMBER mMaxResultsFieldError NOTIFY maxResultsFieldErrorChanged) Q_PROPERTY(int timeout MEMBER mTimeout WRITE setTimeout NOTIFY timeoutChanged) Q_PROPERTY(QString timeoutFieldError MEMBER mTimeoutFieldError NOTIFY timeoutFieldErrorChanged) Q_PROPERTY(QString password MEMBER mPassword WRITE setPassword NOTIFY passwordChanged) Q_PROPERTY(QString passwordFieldError MEMBER mPasswordFieldError NOTIFY passwordFieldErrorChanged) Q_PROPERTY(QString bindDn MEMBER mBindDn WRITE setBindDn NOTIFY bindDnChanged) Q_PROPERTY(QString bindDnFieldError MEMBER mBindDnFieldError NOTIFY bindDnFieldErrorChanged) Q_PROPERTY(QString baseObject MEMBER mBaseObject WRITE setBaseObject NOTIFY baseObjectChanged) Q_PROPERTY(QString baseObjectFieldError MEMBER mBaseObjectFieldError NOTIFY baseObjectFieldErrorChanged) Q_PROPERTY(QString filter MEMBER mFilter WRITE setFilter NOTIFY filterChanged) Q_PROPERTY(QString filterFieldError MEMBER mFilterFieldError NOTIFY filterFieldErrorChanged) Q_PROPERTY(QString nameAttributes MEMBER mNameAttributes WRITE setNameAttributes NOTIFY nameAttributesChanged) Q_PROPERTY(QString nameAttributesFieldError MEMBER mNameAttributesFieldError NOTIFY nameAttributesFieldErrorChanged) Q_PROPERTY(QString sipAttributes MEMBER mSipAttributes WRITE setSipAttributes NOTIFY sipAttributesChanged) Q_PROPERTY(QString sipAttributesFieldError MEMBER mSipAttributesFieldError NOTIFY sipAttributesFieldErrorChanged) Q_PROPERTY(QString sipDomain MEMBER mSipDomain WRITE setSipDomain NOTIFY sipDomainChanged) Q_PROPERTY(QString sipDomainFieldError MEMBER mSipDomainFieldError NOTIFY sipDomainFieldErrorChanged) Q_PROPERTY(bool debug MEMBER mDebug NOTIFY debugChanged) Q_PROPERTY(int verifyServerCertificates MEMBER mVerifyServerCertificates NOTIFY verifyServerCertificatesChanged) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) public: LdapModel (std::shared_ptr ldap, QObject *parent = nullptr); //QVariantMap mConfig; bool mIsValid; //int mId; // "ldap_mId" from section name QString mServer; QString mServerFieldError; void setServer(const QString& server); void testServerField(); QString mDisplayName; void setDisplayName(const QString& displayName); bool mUseSal; bool mUseTls; int mMaxResults; QString mMaxResultsFieldError; void setMaxResults(const int& data); void testMaxResultsField(); int mTimeout; QString mTimeoutFieldError; void setTimeout(const int& data); void testTimeoutField(); QString mPassword; QString mPasswordFieldError; void setPassword(const QString& data); void testPasswordField(); QString mBindDn; QString mBindDnFieldError; void setBindDn(const QString& data); void testBindDnField(); QString mBaseObject; QString mBaseObjectFieldError; void setBaseObject(const QString& data); void testBaseObjectField(); QString mFilter; QString mFilterFieldError; void setFilter(const QString& data); void testFilterField(); QString mNameAttributes; QString mNameAttributesFieldError; void setNameAttributes(const QString& data); void testNameAttributesField(); QString mSipAttributes; QString mSipAttributesFieldError; void setSipAttributes(const QString& data); void testSipAttributesField(); QString mSipDomain; QString mSipDomainFieldError; void setSipDomain(const QString& data); void testSipDomainField(); int getIndex() const; int mDebug; int mVerifyServerCertificates; // Test if the configuration is valid bool isValid(); void init();// init configuration by default value Q_INVOKABLE void save(); // Save configuration to linphonerc void unsave(); // Remove configuration from linphonerc void set(); // Fix Configuration from variables Q_INVOKABLE void unset(); // Set variables from Configuration QVariantMap getConfig(); void setConfig(const QVariantMap& config); bool isEnabled(); void setEnabled(const bool& data); signals: void configChanged(); void isValidChanged(); void serverChanged(); void displayNameChanged(); void useTlsChanged(); void useSalChanged(); void isServerValidChanged(); void maxResultsChanged(); void timeoutChanged(); void passwordChanged(); void bindDnChanged(); void baseObjectChanged(); void filterChanged(); void nameAttributesChanged(); void sipAttributesChanged(); void sipDomainChanged(); void debugChanged(); void verifyServerCertificatesChanged(); void indexChanged(); void serverFieldErrorChanged(); void maxResultsFieldErrorChanged(); void timeoutFieldErrorChanged(); void passwordFieldErrorChanged(); void bindDnFieldErrorChanged(); void baseObjectFieldErrorChanged(); void filterFieldErrorChanged(); void nameAttributesFieldErrorChanged(); void sipAttributesFieldErrorChanged(); void sipDomainFieldErrorChanged(); void enabledChanged(); private: std::shared_ptr mLdap; std::shared_ptr mLdapParams; }; Q_DECLARE_METATYPE(LdapModel*); #endif // LDAP_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/ldap/LdapProxyModel.cpp000066400000000000000000000035701434616504300267110ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/core/CoreManager.hpp" #include "LdapModel.hpp" #include "LdapListModel.hpp" #include "LdapProxyModel.hpp" // ----------------------------------------------------------------------------- LdapProxyModel::LdapProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { auto list = CoreManager::getInstance()->getLdapListModel(); setSourceModel(list); connect(list, &LdapListModel::indexChanged, this, &LdapProxyModel::invalidate); sort(0); } // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- bool LdapProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); return true; } bool LdapProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const LdapModel* ldapA = sourceModel()->data(left).value(); const LdapModel* ldapB = sourceModel()->data(right).value(); return ldapA->getIndex() < ldapB->getIndex(); } linphone-desktop-5.0.2/linphone-app/src/components/ldap/LdapProxyModel.hpp000066400000000000000000000024231434616504300267120ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef LDAP_PROXY_MODEL_H_ #define LDAP_PROXY_MODEL_H_ #include // ============================================================================= class LdapProxyModel : public QSortFilterProxyModel { Q_OBJECT public: LdapProxyModel (QObject *parent = Q_NULLPTR); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; }; #endif // LDAP_PROXY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/notifier/000077500000000000000000000000001434616504300241745ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/notifier/Notifier.cpp000066400000000000000000000351661434616504300264720ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include #include #include "app/App.hpp" #include "components/call/CallModel.hpp" #include "components/core/CoreManager.hpp" #include "components/timeline/TimelineModel.hpp" #include "components/timeline/TimelineListModel.hpp" #include "utils/Utils.hpp" #include "Notifier.hpp" // ============================================================================= using namespace std; namespace { constexpr char NotificationsPath[] = "qrc:/ui/modules/Linphone/Notifications/"; // --------------------------------------------------------------------------- // Notifications QML properties/methods. // --------------------------------------------------------------------------- constexpr char NotificationShowMethodName[] = "open"; constexpr char NotificationPropertyData[] = "notificationData"; constexpr char NotificationPropertyX[] = "popupX"; constexpr char NotificationPropertyY[] = "popupY"; constexpr char NotificationPropertyWindow[] = "__internalWindow"; constexpr char NotificationPropertyTimer[] = "__timer"; // --------------------------------------------------------------------------- // Arbitrary hardcoded values. // --------------------------------------------------------------------------- constexpr int NotificationSpacing = 10; constexpr int MaxNotificationsNumber = 5; } // ============================================================================= template void setProperty (QObject &object, const char *property, const T &value) { if (!object.setProperty(property, QVariant(value))) { qWarning() << QStringLiteral("Unable to set property: `%1`.").arg(property); abort(); } } // ============================================================================= // Available notifications. // ============================================================================= const QHash Notifier::Notifications = { { Notifier::ReceivedMessage, { Notifier::ReceivedMessage, "NotificationReceivedMessage.qml", 10 } }, { Notifier::ReceivedFileMessage, { Notifier::ReceivedFileMessage, "NotificationReceivedFileMessage.qml", 10 } }, { Notifier::ReceivedCall, { Notifier::ReceivedCall, "NotificationReceivedCall.qml", 30 } }, { Notifier::NewVersionAvailable, { Notifier::NewVersionAvailable, "NotificationNewVersionAvailable.qml", 30 } }, { Notifier::SnapshotWasTaken, { Notifier::SnapshotWasTaken, "NotificationSnapshotWasTaken.qml", 10 } }, { Notifier::RecordingCompleted, { Notifier::RecordingCompleted, "NotificationRecordingCompleted.qml", 10 } } }; // ----------------------------------------------------------------------------- Notifier::Notifier (QObject *parent) : QObject(parent) { const int nComponents = Notifications.size(); mComponents = new QQmlComponent *[nComponents]; QQmlEngine *engine = App::getInstance()->getEngine(); for (const auto &key : Notifications.keys()) { QQmlComponent *component = new QQmlComponent(engine, QUrl(NotificationsPath + Notifier::Notifications[key].filename)); if (Q_UNLIKELY(component->isError())) { qWarning() << QStringLiteral("Errors found in `Notification` component %1:").arg(key) << component->errors(); abort(); } mComponents[key] = component; } mMutex = new QMutex(); } Notifier::~Notifier () { delete mMutex; const int nComponents = Notifications.size(); for (int i = 0; i < nComponents; ++i) mComponents[i]->deleteLater(); delete[] mComponents; } // ----------------------------------------------------------------------------- QObject *Notifier::createNotification (Notifier::NotificationType type, QVariantMap data) { QQuickItem *wrapperItem = nullptr; mMutex->lock(); Q_ASSERT(mInstancesNumber <= MaxNotificationsNumber); if (mInstancesNumber == MaxNotificationsNumber) { // Check existing instances. qWarning() << QStringLiteral("Unable to create another notification."); mMutex->unlock(); return nullptr; } QList allScreens = QGuiApplication::screens(); if(allScreens.size() > 0){ // Ensure to have a screen to avoid errors QQuickItem * previousWrapper = nullptr; ++mInstancesNumber; bool showAsTool = false; #ifdef Q_OS_MACOS for(auto w : QGuiApplication::topLevelWindows()){ if( (w->windowState()&Qt::WindowFullScreen)==Qt::WindowFullScreen){ showAsTool = true; w->raise();// Used to get focus on Mac (On Mac, A Tool is hidden if the app has not focus and the only way to rid it is to use Widget Attributes(Qt::WA_MacAlwaysShowToolWindow) that is not available) } } #endif for(int i = 0 ; i < allScreens.size() ; ++i){ QQuickView *view = new QQuickView(App::getInstance()->getEngine(), nullptr); // Use QQuickView to create a visual root object that is independant from current application Window QScreen *screen = allScreens[i]; QObject::connect(view, &QQuickView::statusChanged, [allScreens](QQuickView::Status status){ // Debug handler : show screens descriptions on Error if( status == QQuickView::Error){ QScreen * primaryScreen = QGuiApplication::primaryScreen(); qInfo() << "Primary screen : " << primaryScreen->geometry() << primaryScreen->availableGeometry() << primaryScreen->virtualGeometry() << primaryScreen->availableVirtualGeometry(); for(int i = 0 ; i < allScreens.size() ; ++i){ QScreen *screen = allScreens[i]; qInfo() << QString("Screen [")+QString::number(i)+"] (hdpi, Geometry, Available, Virtual, AvailableGeometry) :" << screen->devicePixelRatio() << screen->geometry() << screen->availableGeometry() << screen->virtualGeometry() << screen->availableVirtualGeometry(); } } }); view->setScreen(screen); // Bind the visual root object to the screen view->setProperty("flags", QVariant(Qt::BypassWindowManagerHint | Qt::WindowStaysOnBottomHint | Qt::CustomizeWindowHint | Qt::X11BypassWindowManagerHint)); // Set the visual ghost window view->setSource(QString(NotificationsPath)+Notifier::Notifications[type].filename); QQuickWindow *subWindow = view->findChild("__internalWindow"); QObject::connect(subWindow, &QObject::destroyed, view, &QObject::deleteLater); // When destroying window, detroy visual root object too int * screenHeightOffset = &mScreenHeightOffset[screen->name()]; // Access optimization QRect availableGeometry = screen->availableGeometry(); int heightOffset = availableGeometry.y() + (availableGeometry.height() - subWindow->height());//*screen->devicePixelRatio(); when using manual scaler if(showAsTool) subWindow->setProperty("showAsTool",true); subWindow->setX(availableGeometry.x()+ (availableGeometry.width()-subWindow->property("width").toInt()));//*screen->devicePixelRatio()); when using manual scaler subWindow->setY(heightOffset-(*screenHeightOffset % heightOffset)); *screenHeightOffset = (subWindow->height() + *screenHeightOffset) + NotificationSpacing; if (*screenHeightOffset - heightOffset + availableGeometry.y() >= 0) *screenHeightOffset = 0; // if(primaryScreen != screen){ //Useful when doing manual scaling jobs. Need to implement scaler in GUI objects // //subwindow->setProperty("xScale", (double)screen->availableVirtualGeometry().width()/availableGeometry.width() ); // //subwindow->setProperty("yScale", (double)screen->availableVirtualGeometry().height()/availableGeometry.height()); // } wrapperItem = view->findChild("__internalWrapper"); ::setProperty(*wrapperItem, NotificationPropertyData,data); view->setGeometry(subWindow->geometry()); // Ensure to have sufficient space to both let painter do job without error, and stay behind popup if(previousWrapper!=nullptr){ // Link objects in order to propagate events without having to store them QObject::connect(previousWrapper, SIGNAL(deleteNotification(QVariant)), wrapperItem,SLOT(deleteNotificationSlot())); QObject::connect(wrapperItem, SIGNAL(isOpened()), previousWrapper,SLOT(open())); QObject::connect(wrapperItem, SIGNAL(isClosed()), previousWrapper,SLOT(close())); QObject::connect(wrapperItem, &QObject::destroyed, previousWrapper, &QObject::deleteLater); } previousWrapper = wrapperItem; // The last one is used as a point of start when deleting and openning view->show(); } qInfo() << QStringLiteral("Create notifications:") << wrapperItem; } mMutex->unlock(); return wrapperItem; } // ----------------------------------------------------------------------------- void Notifier::showNotification (QObject *notification, int timeout) { // Display notification. QMetaObject::invokeMethod(notification, NotificationShowMethodName, Qt::DirectConnection); QTimer *timer = new QTimer(notification); timer->setInterval(timeout); timer->setSingleShot(true); notification->setProperty(NotificationPropertyTimer, QVariant::fromValue(timer)); // Destroy it after timeout. QObject::connect(timer, &QTimer::timeout, this, [this, notification]() { deleteNotificationOnTimeout(QVariant::fromValue(notification)); }); // Called explicitly (by a click on notification for example) QObject::connect(notification, SIGNAL(deleteNotification(QVariant)), this, SLOT(deleteNotification(QVariant))); timer->start(); } // ----------------------------------------------------------------------------- void Notifier::deleteNotificationOnTimeout(QVariant notification) { #ifdef Q_OS_MACOS for(auto w : QGuiApplication::topLevelWindows()){ if( (w->windowState()&Qt::WindowFullScreen)==Qt::WindowFullScreen){ w->requestActivate();// Used to get focus on fullscreens on Mac in order to avoid screen switching. } } #endif deleteNotification(notification); } void Notifier::deleteNotification (QVariant notification) { mMutex->lock(); QObject *instance = notification.value(); // Notification marked destroyed. if (instance->property("__valid").isValid()) { mMutex->unlock(); return; } qInfo() << QStringLiteral("Delete notification:") << instance; instance->setProperty("__valid", true); instance->property(NotificationPropertyTimer).value()->stop(); mInstancesNumber--; Q_ASSERT(mInstancesNumber >= 0); if (mInstancesNumber == 0) mScreenHeightOffset.clear(); mMutex->unlock(); instance->deleteLater(); } // ============================================================================= #define CREATE_NOTIFICATION(TYPE, DATA) \ QObject * notification = createNotification(TYPE, DATA); \ if (!notification) \ return; \ const int timeout = Notifications[TYPE].getTimeout() * 1000; \ showNotification(notification, timeout); // ----------------------------------------------------------------------------- // Notification functions. // ----------------------------------------------------------------------------- void Notifier::notifyReceivedMessages (const list> &messages) { QVariantMap map; QString txt; if( messages.size() > 0){ shared_ptr message = messages.front(); if( messages.size() == 1){ if(! message->getFileTransferInformation() ){ foreach(auto content, message->getContents()){ if(content->isText()) txt += content->getUtf8Text().c_str(); } }else txt = tr("newFileMessage"); }else //: 'New messages received!' Notification that warn the user of new messages. txt = tr("newChatRoomMessages"); map["message"] = txt; shared_ptr chatRoom(message->getChatRoom()); map["timelineModel"].setValue(CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true).get()); if( messages.size() == 1) {// Display only sender on mono message. map["peerAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asStringUriOnly()); map["fullPeerAddress"] = Utils::coreStringToAppString(message->getFromAddress()->asString()); } map["localAddress"] = Utils::coreStringToAppString(message->getToAddress()->asStringUriOnly()); map["fullLocalAddress"] = Utils::coreStringToAppString(message->getToAddress()->asString()); map["window"].setValue(App::getInstance()->getMainWindow()); CREATE_NOTIFICATION(Notifier::ReceivedMessage, map) } } void Notifier::notifyReceivedFileMessage (const shared_ptr &message, const shared_ptr &content) { QVariantMap map; shared_ptr chatRoom(message->getChatRoom()); map["timelineModel"].setValue(CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true).get()); map["fileUri"] = Utils::coreStringToAppString(content->getFilePath()); if( Utils::getImage(map["fileUri"].toString()).isNull()) map["imageUri"] = ""; else map["imageUri"] = map["fileUri"]; map["fileSize"] = quint64(content->getSize() + content->getFileSize()); CREATE_NOTIFICATION(Notifier::ReceivedFileMessage, map) } void Notifier::notifyReceivedCall (const shared_ptr &call) { CallModel *callModel = &call->getData("call-model"); QVariantMap map; map["call"].setValue(callModel); CREATE_NOTIFICATION(Notifier::ReceivedCall, map) QObject::connect(callModel, &CallModel::statusChanged, notification, [this, notification](CallModel::CallStatus status) { if (status == CallModel::CallStatusEnded || status == CallModel::CallStatusConnected) deleteNotification(QVariant::fromValue(notification)); }); } void Notifier::notifyNewVersionAvailable (const QString &version, const QString &url) { QVariantMap map; map["message"] = tr("newVersionAvailable").arg(version); map["url"] = url; CREATE_NOTIFICATION(Notifier::NewVersionAvailable, map) } void Notifier::notifySnapshotWasTaken (const QString &filePath) { QVariantMap map; map["filePath"] = filePath; CREATE_NOTIFICATION(Notifier::SnapshotWasTaken, map) } void Notifier::notifyRecordingCompleted (const QString &filePath) { QVariantMap map; map["filePath"] = filePath; CREATE_NOTIFICATION(Notifier::RecordingCompleted, map) } #undef SHOW_NOTIFICATION #undef CREATE_NOTIFICATION linphone-desktop-5.0.2/linphone-app/src/components/notifier/Notifier.hpp000066400000000000000000000054561434616504300264760ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef NOTIFIER_H_ #define NOTIFIER_H_ #include #include #include #include "components/core/CoreManager.hpp" #include "components/settings/SettingsModel.hpp" // ============================================================================= class QMutex; class QQmlComponent; namespace linphone { class Call; class ChatMessage; } class Notifier : public QObject { Q_OBJECT; public: Notifier (QObject *parent = Q_NULLPTR); ~Notifier (); enum NotificationType { ReceivedMessage, ReceivedFileMessage, ReceivedCall, NewVersionAvailable, SnapshotWasTaken, RecordingCompleted }; void notifyReceivedMessages (const std::list> &messages); void notifyReceivedFileMessage (const std::shared_ptr &message, const std::shared_ptr &content); void notifyReceivedCall (const std::shared_ptr &call); void notifyNewVersionAvailable (const QString &version, const QString &url); void notifySnapshotWasTaken (const QString &filePath); void notifyRecordingCompleted (const QString &filePath); public slots: void deleteNotificationOnTimeout(QVariant notification); void deleteNotification (QVariant notification); private: struct Notification { Notification (const int& type = 0, const QString &filename = QString(""), int timeout = 0) { this->type = type; this->filename = filename; this->timeout = timeout; } int getTimeout() const{ if( type == Notifier::ReceivedCall){ return CoreManager::getInstance()->getSettingsModel()->getIncomingCallTimeout(); }else return timeout; } QString filename; private: int timeout; int type; }; QObject *createNotification (NotificationType type, QVariantMap data); void showNotification (QObject *notification, int timeout); QHash mScreenHeightOffset; int mInstancesNumber = 0; QMutex *mMutex = nullptr; QQmlComponent **mComponents = nullptr; static const QHash Notifications; }; #endif // NOTIFIER_H_ linphone-desktop-5.0.2/linphone-app/src/components/other/000077500000000000000000000000001434616504300234765ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/clipboard/000077500000000000000000000000001434616504300254355ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/clipboard/Clipboard.cpp000066400000000000000000000025061434616504300300430ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "Clipboard.hpp" // ============================================================================= Clipboard::Clipboard (QObject *parent) : QObject(parent) { QObject::connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, this, &Clipboard::textChanged); } QString Clipboard::getText () const { return QGuiApplication::clipboard()->text(QClipboard::Clipboard); } void Clipboard::setText (const QString &text) { QGuiApplication::clipboard()->setText(text, QClipboard::Clipboard); } linphone-desktop-5.0.2/linphone-app/src/components/other/clipboard/Clipboard.hpp000066400000000000000000000023511434616504300300460ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CLIPBOARD_H_ #define CLIPBOARD_H_ #include // ============================================================================= class Clipboard : public QObject { Q_OBJECT; Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged); public: Clipboard (QObject *parent = Q_NULLPTR); signals: void textChanged (); private: QString getText () const; void setText (const QString &text); }; #endif // ifndef CLIPBOARD_H_ linphone-desktop-5.0.2/linphone-app/src/components/other/colors/000077500000000000000000000000001434616504300247775ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/colors/ColorListModel.cpp000066400000000000000000000245351434616504300304070ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ColorListModel.hpp" #include #include #include #if LINPHONE_FRIDAY #include #endif // if LINPHONE_FRIDAY #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" namespace { constexpr char ColorsSection[] = "ui_colors"; } // ============================================================================= ColorListModel::ColorListModel ( QObject *parent) : ProxyListModel(parent) { initKeywords(); init(); } void ColorListModel::initKeywords(){ mKeywordsMap["s"] = "standard"; mKeywordsMap["ma"] = "main"; mKeywordsMap["l"] = "list"; mKeywordsMap["sc"] = "screen"; mKeywordsMap["me"] = "menu"; mKeywordsMap["n"] = "normal"; mKeywordsMap["d"] = "disabled"; mKeywordsMap["h"] = "hovered"; mKeywordsMap["p"] = "pressed"; mKeywordsMap["u"] = "updating"; mKeywordsMap["c"] = "checked"; mKeywordsMap["b"] = "button"; mKeywordsMap["inv"] = "inverse"; mKeywordsMap["bg"] = "background"; mKeywordsMap["fg"] = "foreground"; } QHash ColorListModel::roleNames () const { QHash roles; roles[Qt::DisplayRole] = "$color"; roles[Qt::UserRole] = "id"; roles[Qt::UserRole+1] = "$modelData"; return roles; } QVariant ColorListModel::data (const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); if (role >= Qt::UserRole) return mList[row].objectCast()->getName(); return QVariant::fromValue(mList[row].get()); } void ColorListModel::add(QSharedPointer color){ connect(color.get(), &ColorModel::uiColorChanged, this, &ColorListModel::handleUiColorChanged); setProperty(color->getName().toStdString().c_str(), QVariant::fromValue(color.get())); mData.insert(color->getName(), QVariant::fromValue(color.get())); ProxyListModel::add(color); emit layoutChanged(); } QString ColorListModel::buildDescription(QString description){ QStringList tokens = description.split('_'); for(int index = 0 ; index < tokens.size() ; ++index) if(mKeywordsMap.contains(tokens[index])) tokens[index] = mKeywordsMap[tokens[index]]; description = tokens.join(' '); description[0] = description[0].toUpper(); return description; } ColorModel * ColorListModel::add(const QString& id, const QString& idLink, QString description, QString colorValue, const int& overrideAlpha, const ColorModel::ContextMode& context){ ColorModel * color = getColor(id); if( description == "") description = buildDescription(id); if(!color){ if(idLink != ""){ if( colorValue == ""){ auto linkColor = getColor(idLink); if(linkColor){ colorValue = linkColor->getColor().name(QColor::HexArgb); } } addLink(id, idLink); } auto colorShared = QSharedPointer::create(id, colorValue, description); if(overrideAlpha>=0) colorShared->setAlpha(overrideAlpha* 255 / 100); colorShared->setContext(context); add(colorShared); color = colorShared.get(); emit colorChanged(); }else if( description != color->getDescription()) { color->setDescription(description); if(overrideAlpha>=0) color->setAlpha(overrideAlpha* 255 / 100); emit colorChanged(); } return color; } ColorModel * ColorListModel::addImageColor(const QString& id, const QString& imageId, const QString& idLink, QString description, QString color){ ColorModel * model = add(id, idLink, description, color); model->setLinkedToImage(imageId); imageLinks[imageId].push_back(model); return model; } void ColorListModel::updateLinkIndexToColor(const QString& id, const int& index){ auto colorModel = getColor(id); if(colorModel) colorModel->setLinkIndex(index); } void ColorListModel::addLink(const QString& a, const QString& b){ int index = 0; if( mColorLinkIndexes.contains(b)){ index = mColorLinkIndexes[b]; }else { index = mColorLinks.size(); mColorLinks.push_back(QStringList(b)); mColorLinkIndexes[b] = index; updateLinkIndexToColor(b, index); } mColorLinks[index].push_back(a); mColorLinkIndexes[a] = index; updateLinkIndexToColor(a, index); } void ColorListModel::removeLink(const QString& a){ mColorLinks[mColorLinkIndexes[a]].removeOne(a); mColorLinkIndexes.remove(a); updateLinkIndexToColor(a, -1); } void ColorListModel::updateLink(const QString& id, const QString& newLink){ removeLink(id); if( newLink != "" ){ addLink(id, newLink); ColorModel * linkModel = getColor(newLink); ColorModel * idModel = getColor(id); idModel->setColor(linkModel->getColor()); } } void ColorListModel::useConfig (const std::shared_ptr &config) { #if LINPHONE_FRIDAY if (!isLinphoneFriday()) overrideColors(config); #else overrideColors(config); #endif // if LINPHONE_FRIDAY } QString ColorListModel::getNames(){ QStringList names; const QMetaObject *info = metaObject(); for (int i = info->propertyOffset(); i < info->propertyCount(); ++i) { const QMetaProperty metaProperty = info->property(i); const std::string colorName = metaProperty.name(); names << QString::fromStdString(colorName); } return names.join(", "); } ColorModel * ColorListModel::getColor(const QString& id){ if(mData.contains(id)){ return mData[id].value(); }else return nullptr; } QVector ColorListModel::getColorIdLinks(){ return mColorLinks; } QQmlPropertyMap * ColorListModel::getQmlData() { return &mData; } const QQmlPropertyMap * ColorListModel::getQmlData() const{ return &mData; } int ColorListModel::getLinkIndex(const QString& id){ if( mColorLinkIndexes.contains(id)) return mColorLinkIndexes[id]; else return -1; } void ColorListModel::overrideColors (const std::shared_ptr &colorsConfig) { if (!colorsConfig) return; std::list colorsIds = colorsConfig->getKeysNamesList(ColorsSection); for(auto configId : colorsIds){ if(configId == "ColorLinks"){ std::list colorString = colorsConfig->getStringList(ColorsSection, configId, std::list()); QVector colorLinks; QMap colorLinksIndexes; for(auto color : colorString){ QStringList pair = QString::fromStdString(color).split(";"); if(pair.size() == 2){ QString id = pair.front(); int index = pair.back().toInt(); colorLinksIndexes[id] = index; colorLinks.resize(index+1); colorLinks[index].append(id); } } mColorLinks = colorLinks; mColorLinkIndexes = colorLinksIndexes; for(auto it = mColorLinkIndexes.begin() ; it != mColorLinkIndexes.end() ; ++it){ updateLinkIndexToColor(it.key(), it.value()); } }else{ bool haveColor = false; QString qtConfigId = QString::fromStdString(configId); QString colorName = QString::fromStdString(colorsConfig->getString(ColorsSection, configId, "")); for(auto item : mList){ auto color = item.objectCast(); QString name = color->getName(); if( name == qtConfigId) { color->setColor(QColor(colorName)); haveColor = true; } } if(!haveColor){ add(qtConfigId, "", "Added from Configuration", colorName); } } } } std::shared_ptr ColorListModel::getConfigColors(const QString filename){ std::shared_ptr config = linphone::Factory::get()->createConfig(filename.toStdString()); // Colors links std::list links; for(auto link = mColorLinkIndexes.begin() ; link != mColorLinkIndexes.end() ; ++link) { links.push_back((link.key()+";"+QString::number(link.value())).toStdString()); } config->setStringList(ColorsSection, "ColorLinks", links); for(auto item : mList){ auto color = item.objectCast(); config->setString(ColorsSection, color->getName().toStdString(), color->getColor().name(QColor::HexArgb).toStdString()); } return config; } void ColorListModel::handleUiColorChanged(const QString& id, const QColor& color){ if( mColorLinkIndexes.contains(id)){ int index = mColorLinkIndexes[id]; for(int i = 0 ; i < mColorLinks[index].size() ; ++i){ auto colorToUpdate = getColor(mColorLinks[index][i]); if(colorToUpdate) colorToUpdate->setInternalColor(color); } } } //-------------------------------------------------------------------------------- /* Snippet for having 2 custom colors QPixmap ColorListModel::getLogoIcon(){ // Icon colors QPixmap foregroundPixmap = QPixmap(Constants::WindowIconPath); QPixmap backgroundPixmap = QPixmap(Constants::WindowIconPath); QBitmap inMask = backgroundPixmap.createMaskFromColor(QColor("black"), Qt::MaskInColor); QBitmap outMask = backgroundPixmap.createMaskFromColor(QColor("black"), Qt::MaskOutColor); backgroundPixmap.fill(App::getInstance()->getColorListModel()->addImageColor("Logo_bg", Constants::WindowIconPath, "i")->getColor()); backgroundPixmap.setMask(inMask); foregroundPixmap.fill(App::getInstance()->getColorListModel()->addImageColor("Logo_fg", Constants::WindowIconPath, "ai")->getColor()); foregroundPixmap.setMask(outMask); QPainter painter(&backgroundPixmap); painter.drawPixmap(0,backgroundPixmap.height()-foregroundPixmap.height(),foregroundPixmap); return backgroundPixmap; }*/ /* std::shared_ptr ColorListModel::getImdnState(const std::shared_ptr & state){ std::shared_ptr imdn; auto imdnAddress = state->getParticipant()->getAddress(); auto it = mList.begin(); while(it != mList.end() && !(*it)->getAddress()->equal(imdnAddress)) ++it; if(it != mList.end()) imdn = *it; else{// Create the new one imdn = std::make_shared(state); add(imdn); } return imdn; } */ //--------------------------------------------------------------------------------linphone-desktop-5.0.2/linphone-app/src/components/other/colors/ColorListModel.hpp000066400000000000000000000423641434616504300304140ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef COLOR_LIST_MODEL_H_ #define COLOR_LIST_MODEL_H_ // ============================================================================= #include #include #include #include #include #include #include "ColorModel.hpp" #include "app/proxyModel/ProxyListModel.hpp" #define ADD_COLOR(COLOR, VALUE, DESCRIPTION) \ color = QSharedPointer::create(COLOR, VALUE, DESCRIPTION); \ add(color); #define ADD_COLOR_WITH_LINK(COLOR, VALUE, DESCRIPTION, LINK) \ add(COLOR,LINK,DESCRIPTION,VALUE); #define ADD_COLOR_WITH_LINK_MODE(COLOR, VALUE, DESCRIPTION, LINK, MODE) \ add(COLOR,LINK,DESCRIPTION,VALUE, -1, MODE); // Alpha is in percent. #define ADD_COLOR_WITH_ALPHA(COLOR, ALPHA, DESCRIPTION) \ add(COLOR + QString::number(ALPHA), COLOR, DESCRIPTION, "", ALPHA); /* \ color = QSharedPointer::create(COLOR + QString::number(ALPHA), mData[COLOR].value()->getColor().name(), DESCRIPTION); \ color->setAlpha(ALPHA * 255 / 100); \ add(color); */ class ColorModel; class ColorListModel : public ProxyListModel { Q_OBJECT void init() { QSharedPointer color; ADD_COLOR("a", "transparent", "Generic transparent color.") ADD_COLOR("c", "#CBCBCB", "Button pressed, separatos, fields.") ADD_COLOR("d", "#5A585B", "Text (Ephemerals)") ADD_COLOR("e", "#F3F3F3", "Chat text area Background") ADD_COLOR("f", "#E8E8E8", "Border color") ADD_COLOR("g", "#6B7A86", "SIP Address; Text of Contact, question popup; Selected button.") ADD_COLOR("h", "#687680", "Others") // Primary color. ADD_COLOR("i", "#FF5E00", "Primary color.")//263D86 ADD_COLOR_WITH_LINK_MODE("primary_d", "", "Primary color for deactivated items.", "i", ColorModel::CONTEXT_DEACTIVATED) ADD_COLOR_WITH_LINK_MODE("m", "", "Primary color for clicked items.", "i", ColorModel::CONTEXT_PRESSED) ADD_COLOR_WITH_LINK_MODE("b", "", "Primary color for hovered items.", "i", ColorModel::CONTEXT_HOVERED) ADD_COLOR("secondary_h", "#4B5964", "Secondary color for hovered items.") ADD_COLOR("n", "#A1A1A1", "Primary color for pressed button") ADD_COLOR("o", "#D0D8DE", "Primary color for disabled button") ADD_COLOR("outgoing_bg","#F3F3F3","Outgoing message background") ADD_COLOR("incoming_bg","#D0D8DE","Incoming message background") ADD_COLOR("primary_accept", "#9ECD1D", "Primary color for accepting button") ADD_COLOR("j", "#4B5964", "Username, Background cancel button hovered.") // Popups, home, call, assistant and settings background. ADD_COLOR("k", "#FFFFFF", "Popups, home, call, assistant and settings background.") ADD_COLOR("l", "#000000", "Generic Black color") ADD_COLOR("p", "#17A81A", "Progress bar.") ADD_COLOR("q", "#FFFFFF", "Fields, backgrounds and text color on some items") ADD_COLOR("r", "#909fab", "Background button normal.") ADD_COLOR("s", "#96be64", "Security") ADD_COLOR("unsecure", "#FF0000", "Unsecure") ADD_COLOR("t", "#C2C2C2", "Title Header") ADD_COLOR("u", "#D2D2D2", "Menu border (message)") ADD_COLOR("v", "#E7E7E7", "Menu pressed (message)") ADD_COLOR("w", "#EDEDED", "Menu background (conversation)") ADD_COLOR("x", "#D0D8DE", "Background unselected round button") ADD_COLOR("y", "#FFFFFF", "Gradient dialog start") ADD_COLOR("z", "#E2E2E2", "Gradient dialog end") ADD_COLOR("aa", "#E1E1E1", "Chat text outside background") ADD_COLOR("ab", "#979797", "Chat heading section text") ADD_COLOR("ac", "#B1B1B1", "Chat bubble author/ text") ADD_COLOR_WITH_LINK("ad", "", "Ephemeral main color", "i") ADD_COLOR("ae", "#FF0000", "Important message") ADD_COLOR("af", "#9FA6AB", "Admin Status") ADD_COLOR("ag", "#EBEBEB", "Line between items in list") ADD_COLOR("ah", "#F5F5F5", "Main List item background") ADD_COLOR("ai", "#FFFFFF", "Foreground color on buttons") ADD_COLOR("slider_bg", "#bdbebf", "Slider background") ADD_COLOR("slider_low", "#21be2b", "Slider low value") ADD_COLOR("slider_high", "#ff0000", "Slider high value") ADD_COLOR("event_neutral", "#424242", "Event colors that are neutral") ADD_COLOR("event_in", "#96C11F", "Event colors that are incoming") ADD_COLOR("event_out", "#18A7AF", "Event colors that are outgoing") ADD_COLOR("avatar_initials_bg", "#AFAFAF", "Avatar : Background for initials") ADD_COLOR("avatar_initials_sticker_bg", "transparent", "Avatar : Sticker background for avatar initials") ADD_COLOR("conference_entry_bg", "#D0D8DE", "Conferences : Background entry") ADD_COLOR("conference_bg", "#798791", "Conferences: Background") ADD_COLOR("conference_avatar_sticker_bg", "#A1A1A1", "Conferences : Background for sticker avatar") ADD_COLOR("conference_avatar_preview_sticker_bg", "#475663", "Conferences : Background for sticker avatar of preview") ADD_COLOR("conference_avatar_initials_bg", "#798791", "Conferences : Background for avatar initials") ADD_COLOR("fullscreen_conference_bg", "black", "Conferences: Fullscreen background") ADD_COLOR("validation", "#96C11F", "Background for validation on buttons") ADD_COLOR("validation_h", "#7B9D1B", "Hovered background for validation on buttons") ADD_COLOR("readonly_fg", "#B1B1B1", "Chat text area Readonly foreground") ADD_COLOR("telkeypad_bg", "#4D5B66", "Background for phone keypad") ADD_COLOR("telkeypad_fg", "#E4E4E4", "Foreground for phone keypad") ADD_COLOR("telkeypad_h", "#B1B1B1", "Foreground for phone keypad") ADD_COLOR("progress_bg", "black", "Background of round progress bar") ADD_COLOR("progress_remaining_fg", "white", "Remaining progression color") ADD_COLOR("timeline_bg_1", "#EFF0F2", "Timeline background color 1") ADD_COLOR("timeline_bg_2", "#FFFFFF", "Timeline background color 2") ADD_COLOR_WITH_LINK("message_banner_bg", "", "Message banner background", "primary_accept") ADD_COLOR("message_banner_fg", "#FFFFFF", "Message banner foreground") ADD_COLOR("incall_message_banner_bg", "#FC4607", "Incall message banner background") ADD_COLOR("incall_message_banner_fg", "#FFFFFF", "Incall message banner foreground") ADD_COLOR_WITH_LINK("ics_fg", "", "Special text color for ICS", "j") ADD_COLOR("updated_ics_fg", "#EFAE00", "Special text color for updated ICS") ADD_COLOR("cancelled_ics_fg", "#FF0000", "Special text color for cancelled ICS") ADD_COLOR("cancelled_ics_bg", "#fff5fa", "Background color for cancelled ICS") // Keywords: 'mKeywordsMap' // s=standard, ma=main, l=list, sc=screen, me=menu // n=normal, d=disabled, h=hovered, p=pressed, u=updating, c=checked // b=button // inv=inverse // bg=background, fg=foreground // Standard actions : ADD_COLOR("s_n_b_bg", "#96A5B1", "[M] Standard normal button : background") ADD_COLOR("s_d_b_bg", "#D0D8DE", "[M] Standard disabled button : background") ADD_COLOR("s_h_b_bg", "#4B5964", "[M] Standard hovered button : background") ADD_COLOR_WITH_LINK("s_p_b_bg", "", "[M] Standard pressed button : background", "i") ADD_COLOR("s_n_b_fg", "white", "[M] Standard normal button : foreground") ADD_COLOR("s_d_b_fg", "white", "[M] Standard disabled button : foreground") ADD_COLOR("s_h_b_fg", "white", "[M] Standard hovered button : foreground") ADD_COLOR("s_p_b_fg", "white", "[M] Standard pressed button : foreground") //---------------------------- // Main Actions : like home button ADD_COLOR_WITH_LINK("ma_n_b_bg", "", "[M] Main normal button : background", "i") ADD_COLOR_WITH_LINK("ma_d_b_bg", "", "[M] Main disabled button : background", "primary_d") // "#FFCEB2" ADD_COLOR_WITH_LINK("ma_h_b_bg", "", "[M] Main hovered button : background", "b") ADD_COLOR_WITH_LINK("ma_p_b_bg", "", "[M] Main pressed button : background", "m") ADD_COLOR("ma_n_b_fg", "white", "[M] Main normal button : foreground") ADD_COLOR("ma_d_b_fg", "white", "[M] Main disabled button : foreground") ADD_COLOR("ma_h_b_fg", "white", "[M] Main hovered button : foreground") ADD_COLOR("ma_p_b_fg", "white", "[M] Main pressed button : foreground") //------------------------------------- // Accept Actions : like accepting a call ADD_COLOR_WITH_LINK("a_n_b_bg", "", "[M] Accept normal button : background", "primary_accept") ADD_COLOR_WITH_LINK_MODE("a_d_b_bg", "", "[M] Accept disabled button : background", "primary_accept", ColorModel::CONTEXT_DEACTIVATED) ADD_COLOR("a_h_b_bg", "#7D9F21", "[M] Accept hovered button : background") ADD_COLOR_WITH_LINK("a_p_b_bg", "", "[M] Accept pressed button : background", "a_n_b_bg") ADD_COLOR("a_n_b_fg", "white", "[M] Accept normal button : foreground") ADD_COLOR("a_d_b_fg", "white", "[M] Accept disabled button : foreground") ADD_COLOR("a_h_b_fg", "white", "[M] Accept hovered button : foreground") ADD_COLOR("a_p_b_fg", "white", "[M] Accept pressed button : foreground") //------------------------------------- // Reject Actions : like rejecting a call ADD_COLOR_WITH_LINK("r_n_b_bg", "", "[M] Reject normal button : background", "i") ADD_COLOR_WITH_LINK("r_d_b_bg", "", "[M] Reject disabled button : background", "primary_d") ADD_COLOR_WITH_LINK("r_h_b_bg", "", "[M] Reject hovered button : background", "b") ADD_COLOR_WITH_LINK("r_p_b_bg", "", "[M] Reject pressed button : background", "r_n_b_bg") ADD_COLOR("r_n_b_fg", "white", "[M] Reject normal button : foreground") ADD_COLOR("r_d_b_fg", "white", "[M] Reject disabled button : foreground") ADD_COLOR("r_h_b_fg", "white", "[M] Reject hovered button : foreground") ADD_COLOR("r_p_b_fg", "white", "[M] Reject pressed button : foreground") //------------------------------------- // List Actions : like dot menu in chat ADD_COLOR("l_n_b_bg", "transparent", "[M] List normal button : background") ADD_COLOR("l_d_b_bg", "transparent", "[M] List disabled button : background") ADD_COLOR("l_h_b_bg", "transparent", "[M] List hovered button : background") ADD_COLOR("l_p_b_bg", "transparent", "[M] List pressed button : background") ADD_COLOR_WITH_LINK("l_u_b_bg", "", "[M] List updating button : background", "l_p_b_bg") ADD_COLOR("l_n_b_fg", "#4B5964", "[M] List normal button : foreground") ADD_COLOR("l_d_b_fg", "#8096A5B1", "[M] List disabled button : foreground") ADD_COLOR("l_h_b_fg", "#96A5B1", "[M] List hovered button : foreground") ADD_COLOR_WITH_LINK("l_p_b_fg", "", "[M] List pressed button : foreground", "i") ADD_COLOR_WITH_LINK("l_u_b_fg", "", "[M] List updating button : foreground", "l_p_b_fg") //------------------------------------- // Menu Actions ADD_COLOR("me_n_b_bg", "transparent", "[M] Menu normal button : background") ADD_COLOR("me_d_b_bg", "transparent", "[M] Menu disabled button : background") ADD_COLOR("me_h_b_bg", "transparent", "[M] Menu hovered button : background") ADD_COLOR("me_p_b_bg", "transparent", "[M] Menu pressed button : background") ADD_COLOR_WITH_LINK("me_u_b_bg", "", "[M] Menu updating button : background", "me_p_b_bg") ADD_COLOR("me_n_b_fg", "#4B5964", "[M] Menu normal button : foreground") ADD_COLOR("me_h_b_fg", "#96A5B1", "[M] Menu hovered button : foreground") ADD_COLOR_WITH_LINK_MODE("me_d_b_fg", "", "[M] Menu disabled button : foreground", "me_h_b_fg", ColorModel::CONTEXT_DEACTIVATED) ADD_COLOR_WITH_LINK("me_p_b_fg", "", "[M] Menu pressed button : foreground", "i") ADD_COLOR_WITH_LINK("me_u_b_fg", "", "[M] Menu updating button : background", "me_p_b_fg") // Inverse ADD_COLOR("me_n_b_inv_bg", "transparent", "[M] Menu normal button : inverse background") ADD_COLOR("me_d_b_inv_bg", "transparent", "[M] Menu disabled button : inverse background") ADD_COLOR("me_h_b_inv_bg", "transparent", "[M] Menu hovered button : inverse background") ADD_COLOR("me_p_b_inv_bg", "transparent", "[M] Menu pressed button : inverse background") ADD_COLOR_WITH_LINK("me_c_b_inv_bg", "", "[M] Menu checked button : inverse foreground", "i") ADD_COLOR("me_n_b_inv_fg", "white", "[M] Menu normal button : inverse foreground") ADD_COLOR_WITH_LINK_MODE("me_d_b_inv_fg", "", "[M] Menu disabled button : inverse foreground", "me_n_b_inv_fg", ColorModel::CONTEXT_DEACTIVATED) ADD_COLOR_WITH_LINK_MODE("me_h_b_inv_fg", "", "[M] Menu hovered button : inverse foreground", "me_n_b_inv_fg", ColorModel::CONTEXT_DEACTIVATED) ADD_COLOR("me_p_b_inv_fg", "white", "[M] Menu pressed button : inverse foreground") ADD_COLOR("me_c_b_inv_fg", "white", "[M] Menu checked button : inverse foreground") //------------------------------------- // Wave Play ADD_COLOR_WITH_LINK("w_n_b_bg", "", "[M] Wave play normal button : background", "ma_n_b_bg") ADD_COLOR_WITH_LINK("w_d_b_bg", "", "[M] Wave play disabled button : background", "ma_d_b_bg") ADD_COLOR_WITH_LINK("w_h_b_bg", "", "[M] Wave play hovered button : background", "ma_h_b_bg") ADD_COLOR_WITH_LINK("w_p_b_bg", "", "[M] Wave play pressed button : background", "ma_p_b_bg") ADD_COLOR_WITH_LINK("w_n_b_fg", "", "[M] Wave play normal button : foreground", "ma_n_b_fg") ADD_COLOR_WITH_LINK("w_d_b_fg", "", "[M] Wave play disabled button : foreground", "ma_d_b_fg") ADD_COLOR_WITH_LINK("w_h_b_fg", "", "[M] Wave play hovered button : foreground", "ma_h_b_fg") ADD_COLOR_WITH_LINK("w_p_b_fg", "", "[M] Wave play pressed button : foreground", "ma_p_b_fg") // Wave Record ADD_COLOR("wr_n_b_bg", "transparent", "[M] Wave record normal button : background") ADD_COLOR("wr_d_b_bg", "transparent", "[M] Wave record disabled button : background") ADD_COLOR("wr_h_b_bg", "transparent", "[M] Wave record hovered button : background") ADD_COLOR("wr_p_b_bg", "transparent", "[M] Wave record pressed button : background") ADD_COLOR("wr_n_b_fg", "#96A5B1", "[M] Wave record normal button : foreground") ADD_COLOR("wr_d_b_fg", "#96A5B1", "[M] Wave record disabled button : foreground") ADD_COLOR("wr_h_b_fg", "#4B5964", "[M] Wave record hovered button : foreground") ADD_COLOR_WITH_LINK("wr_p_b_fg", "", "[M] Wave record pressed button : foreground", "i") //-------------------------------------------------------------------------------------------------------------------- ADD_COLOR("border", "black", "Borders") ADD_COLOR("border_light", "#A8A8A8", "Lighter borders") // Field error. ADD_COLOR("error", "#FF0000", "Error Generic button.") ADD_COLOR_WITH_ALPHA("g", 10, "") ADD_COLOR_WITH_ALPHA("g", 20, "") ADD_COLOR_WITH_ALPHA("g", 90, "") ADD_COLOR_WITH_ALPHA("i", 30, "") ADD_COLOR_WITH_ALPHA("j", 50, "") ADD_COLOR_WITH_ALPHA("j", 90, "") ADD_COLOR_WITH_ALPHA("l", 50, "") ADD_COLOR_WITH_ALPHA("l", 80, "") ADD_COLOR_WITH_ALPHA("q", 50, "") ADD_COLOR_WITH_LINK("event_bad", "", "Event colors that are bad", "error") } public: ColorListModel (QObject *parent = nullptr); void initKeywords(); virtual QHash roleNames () const override; virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; void useConfig (const std::shared_ptr &config); Q_INVOKABLE QString getNames(); ColorModel * getColor(const QString& id); QVector getColorIdLinks(); // id: set an ID. If the ID already exist, the funtion return the item instead of create one. // color : if empty, use the color from link // description : describe the color // idLink : link this color with another ID Q_INVOKABLE ColorModel * add(const QString& id, const QString& idLink, QString description = "", QString color = "", const int& overrideAlpha = -1, const ColorModel::ContextMode& context = ColorModel::CONTEXT_NORMAL); Q_INVOKABLE ColorModel * addImageColor(const QString& id, const QString& imageId, const QString& idLink, QString description = "", QString color = ""); void addLink(const QString& a, const QString& b); void removeLink(const QString& a); Q_INVOKABLE void updateLink(const QString& id, const QString& newLink); QQmlPropertyMap * getQmlData(); const QQmlPropertyMap * getQmlData() const; int getLinkIndex(const QString& id); void overrideColors (const std::shared_ptr &config); std::shared_ptr getConfigColors(const QString filename); public slots: void handleUiColorChanged(const QString& id, const QColor& color); signals: void colorChanged(); private: void add(QSharedPointer imdn); QString buildDescription(QString description); // return a description from id by splitting '_' void updateLinkIndexToColor(const QString& id, const int& index); QStringList getColorNames () const; QQmlPropertyMap mData; QVector mColorLinks; QMap mColorLinkIndexes;// Optimization for access QMap > imageLinks; QMap mKeywordsMap; // Convert keyword into description }; #undef ADD_COLOR Q_DECLARE_METATYPE(QSharedPointer) #endif linphone-desktop-5.0.2/linphone-app/src/components/other/colors/ColorModel.cpp000066400000000000000000000055531434616504300275520ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ColorModel.hpp" #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "components/core/CoreManager.hpp" // ============================================================================= ColorModel::ColorModel (const QString& name, const QColor& color, const QString& description, QObject * parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mName = name; setColor(color); setDescription(description) ; } // ----------------------------------------------------------------------------- QString ColorModel::getName() const{ return mName; } QColor ColorModel::getColor() const{ return mColor; } QString ColorModel::getDescription() const{ return mDescription; } QString ColorModel::getLinkedToImage() const{ return mLinkedToImage; } int ColorModel::getLinkIndex() const{ return mLinkIndex; } void ColorModel::setColor(const QColor& color){ if(color != mColor){ mColor = color; emit colorChanged(); emit uiColorChanged(mName, color); } } void ColorModel::setInternalColor(const QColor& color){ if(color != mColor){ mColor = color; updateContext(); emit colorChanged(); } } void ColorModel::setAlpha(const int& alpha){ mColor.setAlpha(alpha); emit colorChanged(); } void ColorModel::setDescription(const QString& description){ if(description != mDescription){ mDescription = description; emit descriptionChanged(); } } void ColorModel::setLinkedToImage(const QString& id){ mLinkedToImage = id; } void ColorModel::setLinkIndex(const int& index){ if(index != mLinkIndex){ mLinkIndex = index; emit linkIndexChanged(); } } void ColorModel::setContext(const ContextMode& context){ mContextMode = context; updateContext(); } void ColorModel::updateContext(){ switch(mContextMode){ case CONTEXT_NORMAL : break; case CONTEXT_PRESSED: mColor = mColor.lighter(140); break; case CONTEXT_HOVERED: mColor = mColor.darker(140); break; case CONTEXT_DEACTIVATED: mColor.setAlpha(60);break; } }linphone-desktop-5.0.2/linphone-app/src/components/other/colors/ColorModel.hpp000066400000000000000000000050151434616504300275500ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef COLOR_MODEL_H #define COLOR_MODEL_H // ============================================================================= #include #include #include #include #include "utils/LinphoneEnums.hpp" class ColorModel : public QObject { Q_OBJECT public: typedef enum{ CONTEXT_NORMAL = 0, CONTEXT_HOVERED,// More darker CONTEXT_PRESSED,// More lighter CONTEXT_DEACTIVATED// Alpha }ContextMode; ColorModel (const QString& name, const QColor& color, const QString& description, QObject * parent = nullptr); Q_PROPERTY(QColor color MEMBER mColor WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QString description MEMBER mDescription WRITE setDescription NOTIFY descriptionChanged) Q_PROPERTY(QString name MEMBER mName CONSTANT) Q_PROPERTY(int linkIndex MEMBER mLinkIndex WRITE setLinkIndex NOTIFY linkIndexChanged) QColor getColor() const; QString getDescription() const; QString getName() const; int getLinkIndex() const; Q_INVOKABLE QString toString(){return getName();} QString getLinkedToImage() const; void setColor(const QColor& color); void setInternalColor(const QColor& color); void setAlpha(const int& alpha); void setDescription(const QString& description); void setLinkIndex(const int& index); void setLinkedToImage(const QString& id); void setContext(const ContextMode& context); void updateContext(); signals: void colorChanged(); void uiColorChanged(const QString& id, const QColor& color); // UI request a change void descriptionChanged(); void linkIndexChanged(); private: QString mName; QColor mColor; QString mDescription; QString mLinkedToImage; int mLinkIndex = -1; ContextMode mContextMode = CONTEXT_NORMAL; }; Q_DECLARE_METATYPE(QSharedPointer); #endif linphone-desktop-5.0.2/linphone-app/src/components/other/colors/ColorProxyModel.cpp000066400000000000000000000063611434616504300306120ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ColorProxyModel.hpp" #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "ColorListModel.hpp" #include "ColorModel.hpp" // ============================================================================= ColorProxyModel::ColorProxyModel (QObject *parent) : QSortFilterProxyModel(parent){ setSourceModel(App::getInstance()->getColorListModel()); mSortMode = 0; sort(0); } int ColorProxyModel::getShowPageIndex()const{ return mShowPageIndex; } void ColorProxyModel::setShowPageIndex(const int& index){ if(mShowPageIndex != index){ mShowPageIndex = index; emit showPageIndexChanged(); invalidate(); } } bool ColorProxyModel::getShowAll()const{ return mShowAll; } void ColorProxyModel::setShowAll(const bool& show){ if(mShowAll != show){ mShowAll = show; emit showAllChanged(); invalidate(); } } void ColorProxyModel::updateLink(const QString& id, const QString& newLink){ App::getInstance()->getColorListModel()->updateLink(id, newLink); invalidate(); } void ColorProxyModel::changeSort(){ mSortMode = (mSortMode+1)%4; invalidate(); emit sortChanged(); } QString ColorProxyModel::getSortDescription() const{ switch(mSortMode){ case 0: return "Link name"; case 1: return "Name"; case 2: return "Description"; case 3: return "Color"; default:{ return "Name"; } } } bool ColorProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent ) const { Q_UNUSED(sourceRow) Q_UNUSED(sourceParent) const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); const ColorModel *model= index.data().value(); //return model->getLinkedToImage() == "";// Remove linked to image from list int currentPage = sourceRow / 50; return mShowAll || currentPage == mShowPageIndex; } bool ColorProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { ColorListModel * model = static_cast(sourceModel()); const ColorModel *a = model->data(left).value(); const ColorModel *b = model->data(right).value(); switch(mSortMode){ case 0 : return a->getLinkIndex() < b->getLinkIndex(); case 1: return a->getName() < b->getName(); case 2: return a->getDescription() < b->getDescription(); case 3: return a->getColor().name() < b->getColor().name(); default: return a->getName() < b->getName(); } } //--------------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/other/colors/ColorProxyModel.hpp000066400000000000000000000041561434616504300306170ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef COLOR_PROXY_MODEL_H_ #define COLOR_PROXY_MODEL_H_ #include // ============================================================================= #include #include #include #include class ColorListModel; class ChatMessageModel; class ColorProxyModel : public QSortFilterProxyModel { Q_OBJECT public: ColorProxyModel (QObject *parent = nullptr); Q_PROPERTY(QString sortDescription READ getSortDescription NOTIFY sortChanged) Q_PROPERTY(int showPageIndex READ getShowPageIndex WRITE setShowPageIndex NOTIFY showPageIndexChanged) Q_PROPERTY(bool showAll READ getShowAll WRITE setShowAll NOTIFY showAllChanged) Q_INVOKABLE void updateLink(const QString& id, const QString& newLink); Q_INVOKABLE void changeSort(); QString getSortDescription() const; int getShowPageIndex()const; void setShowPageIndex(const int& index); bool getShowAll()const; void setShowAll(const bool& showAll); signals: void sortChanged(); void showPageIndexChanged(); void showAllChanged(); protected: virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; private: int mSortMode; int mShowPageIndex = 0; bool mShowAll = false; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/other/colors/ImageColorsProxyModel.cpp000066400000000000000000000045361434616504300317420ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ImageColorsProxyModel.hpp" #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "ColorListModel.hpp" #include "ColorModel.hpp" // ============================================================================= ImageColorsProxyModel::ImageColorsProxyModel (QObject *parent) : QSortFilterProxyModel(parent){ setSourceModel(App::getInstance()->getColorListModel()); sort(0); } void ImageColorsProxyModel::setImageId(const QString& imageId){ if(mImageId != imageId){ mImageId = imageId; emit imageIdChanged(); invalidate(); } } //--------------------------------------------------------------------------------- bool ImageColorsProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent ) const { Q_UNUSED(sourceRow) Q_UNUSED(sourceParent) const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); const ColorModel *model= index.data().value(); return mImageId != "" && model->getLinkedToImage() == mImageId;// Remove linked to image from list } bool ImageColorsProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { ColorListModel * model = static_cast(sourceModel()); const ColorModel *a = model->data(left).value(); const ColorModel *b = model->data(right).value(); //return model->getLinkIndex(a->getName()) < model->getLinkIndex(b->getName()); return a->getName() < b->getName() ; } //--------------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/other/colors/ImageColorsProxyModel.hpp000066400000000000000000000031731434616504300317430ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef IMAGE_COLORS_PROXY_MODEL_H_ #define IMAGE_COLORS_PROXY_MODEL_H_ #include // ============================================================================= #include #include #include #include class ColorListModel; class ChatMessageModel; class ImageColorsProxyModel : public QSortFilterProxyModel { Q_OBJECT public: ImageColorsProxyModel (QObject *parent = nullptr); Q_PROPERTY(QString imageId MEMBER mImageId WRITE setImageId NOTIFY imageIdChanged) void setImageId(const QString& imageId); signals: void imageIdChanged(); protected: virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; private: QString mImageId; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/000077500000000000000000000000001434616504300263055ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/DesktopTools.hpp000066400000000000000000000022061434616504300314500ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef DESKTOP_TOOLS_H_ #define DESKTOP_TOOLS_H_ #include #ifdef Q_OS_LINUX #include "DesktopToolsLinux.hpp" #elif defined(Q_OS_WIN) #include "DesktopToolsWindows.hpp" #else #include "DesktopToolsMacOs.hpp" #endif // ifdef Q_OS_LINUX // ============================================================================= #endif // DESKTOP_TOOLS_H_ linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/DesktopToolsLinux.cpp000066400000000000000000000026511434616504300324670ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "DesktopToolsLinux.hpp" // ============================================================================= DesktopTools::~DesktopTools () { setScreenSaverStatus(true); } bool DesktopTools::getScreenSaverStatus () const { return mScreenSaverStatus; } void DesktopTools::setScreenSaverStatus (bool status) { screenSaverDBus.setScreenSaverStatus(status); screenSaverXdg.setScreenSaverStatus(status); bool newStatus = screenSaverDBus.getScreenSaverStatus() || screenSaverXdg.getScreenSaverStatus(); if (newStatus != mScreenSaverStatus) { mScreenSaverStatus = newStatus; emit screenSaverStatusChanged(mScreenSaverStatus); } } linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/DesktopToolsLinux.hpp000066400000000000000000000033061434616504300324720ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef DESKTOP_TOOLS_LINUX_H_ #define DESKTOP_TOOLS_LINUX_H_ #include "components/other/desktop-tools/screen-saver/ScreenSaverDBus.hpp" #include "components/other/desktop-tools/screen-saver/ScreenSaverXdg.hpp" // ============================================================================= class DesktopTools : public QObject { Q_OBJECT; Q_PROPERTY(bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged); public: DesktopTools (QObject *parent = Q_NULLPTR) : QObject(parent) {} ~DesktopTools (); bool getScreenSaverStatus () const; void setScreenSaverStatus (bool status); static void init(){} static void applicationStateChanged(Qt::ApplicationState){}; signals: void screenSaverStatusChanged (bool status); private: bool mScreenSaverStatus = true; ScreenSaverDBus screenSaverDBus; ScreenSaverXdg screenSaverXdg; }; #endif // DESKTOP_TOOLS_LINUX_H_ linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/DesktopToolsMacOs.cpp000066400000000000000000000026751434616504300324000ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "DesktopToolsMacOs.hpp" // ============================================================================= extern "C" bool enableScreenSaverMacOs (); extern "C" bool disableScreenSaverMacOs (); DesktopTools::DesktopTools (QObject *parent) : QObject(parent) {} DesktopTools::~DesktopTools () { setScreenSaverStatus(true); } bool DesktopTools::getScreenSaverStatus () const { return mScreenSaverStatus; } void DesktopTools::setScreenSaverStatus (bool status) { if (status != mScreenSaverStatus && (status ? enableScreenSaverMacOs() : disableScreenSaverMacOs())) { mScreenSaverStatus = status; emit screenSaverStatusChanged(mScreenSaverStatus); } } linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/DesktopToolsMacOs.hpp000066400000000000000000000030231434616504300323710ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef DESKTOP_TOOLS_MAC_OS_H_ #define DESKTOP_TOOLS_MAC_OS_H_ #include // ============================================================================= class DesktopTools : public QObject { Q_OBJECT; Q_PROPERTY(bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged); public: DesktopTools (QObject *parent = Q_NULLPTR); ~DesktopTools (); bool getScreenSaverStatus () const; void setScreenSaverStatus (bool status); static void init(); // Do first initialization static void applicationStateChanged(Qt::ApplicationState currentState); signals: void screenSaverStatusChanged (bool status); private: bool mScreenSaverStatus = true; }; #endif // DESKTOP_TOOLS_MAC_OS_H_ linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/DesktopToolsMacOsNative.mm000066400000000000000000000010751434616504300333670ustar00rootroot00000000000000#include "DesktopToolsMacOs.hpp" #import void DesktopTools::init(){ // Request permissions if( @available(macOS 10.14, *) ) { if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusNotDetermined) [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL) {}]; if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio] == AVAuthorizationStatusNotDetermined) [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL) {}]; } } linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/DesktopToolsWindows.cpp000066400000000000000000000026661434616504300330300ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "DesktopToolsWindows.hpp" #include // ============================================================================= DesktopTools::DesktopTools (QObject *parent) : QObject(parent) {} DesktopTools::~DesktopTools () { setScreenSaverStatus(true); } bool DesktopTools::getScreenSaverStatus () const { return mScreenSaverStatus; } void DesktopTools::setScreenSaverStatus (bool status) { if (status == mScreenSaverStatus) return; if (status) SetThreadExecutionState(ES_CONTINUOUS); else SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); mScreenSaverStatus = status; emit screenSaverStatusChanged(status); } linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/DesktopToolsWindows.hpp000066400000000000000000000027611434616504300330310ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef DESKTOP_TOOLS_WINDOWS_H_ #define DESKTOP_TOOLS_WINDOWS_H_ #include // ============================================================================= class DesktopTools : public QObject { Q_OBJECT; Q_PROPERTY(bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged); public: DesktopTools (QObject *parent = Q_NULLPTR); ~DesktopTools (); bool getScreenSaverStatus () const; void setScreenSaverStatus (bool status); static void init(){} static void applicationStateChanged(Qt::ApplicationState){}; signals: void screenSaverStatusChanged (bool status); private: bool mScreenSaverStatus = true; }; #endif // DESKTOP_TOOLS_WINDOWS_H_ linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/screen-saver/000077500000000000000000000000001434616504300307025ustar00rootroot00000000000000ScreenSaverDBus.cpp000066400000000000000000000050061434616504300343260ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/screen-saver/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "ScreenSaverDBus.hpp" // ============================================================================= namespace { constexpr char ServiceName[] = "org.freedesktop.ScreenSaver"; constexpr char ServicePath[] = "/ScreenSaver"; } ScreenSaverDBus::ScreenSaverDBus (QObject *parent) : QObject(parent), mBus(ServiceName, ServicePath, ServiceName) {} ScreenSaverDBus::~ScreenSaverDBus () { setScreenSaverStatus(true); } bool ScreenSaverDBus::getScreenSaverStatus () const { return mScreenSaverStatus; } void ScreenSaverDBus::setScreenSaverStatus (bool status) { if (status == mScreenSaverStatus) return; if (status) { QDBusMessage reply(mBus.call("UnInhibit", mToken)); if (reply.type() == QDBusMessage::ErrorMessage) { qWarning() << QStringLiteral("Uninhibit screen saver failed: `%1: %2`.") .arg(reply.errorName()).arg(reply.errorMessage()); return; } else qInfo("Uninhibit screen saver."); mToken = uint32_t(reply.arguments().first().toULongLong()); mScreenSaverStatus = status; emit screenSaverStatusChanged(mScreenSaverStatus); return; } QDBusMessage reply(mBus.call("Inhibit", QCoreApplication::applicationName(), "Inhibit asked for video stream")); if (reply.type() == QDBusMessage::ErrorMessage) { if (reply.errorName() != QLatin1String("org.freedesktop.DBus.Error.ServiceUnknown")) qWarning() << QStringLiteral("Inhibit screen saver failed: `%1: %2`.") .arg(reply.errorName()).arg(reply.errorMessage()); return; } else qInfo("Inhibit screen saver."); mScreenSaverStatus = status; emit screenSaverStatusChanged(mScreenSaverStatus); } ScreenSaverDBus.hpp000066400000000000000000000025551434616504300343410ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/screen-saver/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SCREEN_SAVER_DBUS_H_ #define SCREEN_SAVER_DBUS_H_ #include // ============================================================================= class QDBusPendingCallWatcher; class ScreenSaverDBus : public QObject { Q_OBJECT; public: ScreenSaverDBus (QObject *parent = Q_NULLPTR); ~ScreenSaverDBus (); bool getScreenSaverStatus () const; void setScreenSaverStatus (bool status); signals: void screenSaverStatusChanged (bool status); private: bool mScreenSaverStatus = true; QDBusInterface mBus; uint32_t mToken; }; #endif // SCREEN_SAVER_DBUS_H_ ScreenSaverMacOs.m000066400000000000000000000030561434616504300341500ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/screen-saver/* * ScreenSaverMacOS.m * Copyright (C) 2017-2018 Belledonne Communications, Grenoble, France * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Created on: August 3, 2018 * Author: Ronan Abhamon */ #import // ============================================================================= static bool ScreenSaverEnabled = true; static IOPMAssertionID AssertionID; bool enableScreenSaverMacOs () { if (ScreenSaverEnabled) return true; ScreenSaverEnabled = IOPMAssertionRelease(AssertionID) == kIOReturnSuccess; return ScreenSaverEnabled; } bool disableScreenSaverMacOs () { if (!ScreenSaverEnabled) return true; ScreenSaverEnabled = IOPMAssertionCreateWithName( kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, CFSTR("Inhibit asked for video stream"), &AssertionID ) != kIOReturnSuccess; return !ScreenSaverEnabled; } ScreenSaverXdg.cpp000066400000000000000000000031321434616504300342110ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/screen-saver/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "ScreenSaverXdg.hpp" // ============================================================================= namespace { constexpr char Program[] = "xdg-screensaver"; const QStringList Arguments{"reset"}; constexpr int Interval = 30000; } ScreenSaverXdg::ScreenSaverXdg (QObject *parent) : QObject(parent) { mTimer.setInterval(Interval); QObject::connect(&mTimer, &QTimer::timeout, []() { // Legacy for systems without DBus screensaver. QProcess::startDetached(Program, Arguments); }); } bool ScreenSaverXdg::getScreenSaverStatus () const { return !mTimer.isActive(); } void ScreenSaverXdg::setScreenSaverStatus (bool status) { if (status == !mTimer.isActive()) return; if (status) mTimer.stop(); else mTimer.start(); emit screenSaverStatusChanged(status); } ScreenSaverXdg.hpp000066400000000000000000000023551434616504300342240ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/screen-saver/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SCREEN_SAVER_XDG_H_ #define SCREEN_SAVER_XDG_H_ #include // ============================================================================= class ScreenSaverXdg : public QObject { Q_OBJECT; public: ScreenSaverXdg (QObject *parent = Q_NULLPTR); bool getScreenSaverStatus () const; void setScreenSaverStatus (bool status); signals: void screenSaverStatusChanged (bool status); private: QTimer mTimer; }; #endif // SCREEN_SAVER_XDG_H_ linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/state-process/000077500000000000000000000000001434616504300311015ustar00rootroot00000000000000StateProcessMacOs.mm000066400000000000000000000015501434616504300347200ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/desktop-tools/state-process#include "../DesktopToolsMacOs.hpp" #import #import // Store a unique global instance of Activity to avoid App Nap of MacOs static id g_backgroundActivity =0; void DesktopTools::applicationStateChanged(Qt::ApplicationState p_currentState) { if( p_currentState == Qt::ApplicationActive && g_backgroundActivity != 0 ) {// Entering Foreground [[NSProcessInfo processInfo] endActivity:g_backgroundActivity]; [g_backgroundActivity release]; g_backgroundActivity = 0; }else if( g_backgroundActivity == 0 ) {// Doesn't begin activity if it is already started g_backgroundActivity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityUserInitiatedAllowingIdleSystemSleep reason:@"Linphone : Continue to receive requests while in Background"]; [g_backgroundActivity retain]; } } linphone-desktop-5.0.2/linphone-app/src/components/other/images/000077500000000000000000000000001434616504300247435ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/images/ImageListModel.cpp000066400000000000000000000064501434616504300303130ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ImageListModel.hpp" #include #include #include #include #if LINPHONE_FRIDAY #include #endif // if LINPHONE_FRIDAY #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "ImageModel.hpp" namespace { constexpr char ImagesSection[] = "ui_images"; } // ============================================================================= ImageListModel::ImageListModel ( QObject *parent) : ProxyListModel(parent), mData(this) { // Get all internals QString path = ":/assets/images/"; QStringList filters; filters << "*.svg"; QFileInfoList files = QDir(path).entryInfoList(filters, QDir::Files , QDir::Name); for(QFileInfo file : files){ QSharedPointer model = QSharedPointer::create(file.completeBaseName(), path+file.fileName(), ""); add(model); } mData.insert("areReadOnlyImages", QVariant::fromValue(true)); } void ImageListModel::add(QSharedPointer image){ setProperty(image->getId().toStdString().c_str(), QVariant::fromValue(image.get())); mData.insert(image->getId(), QVariant::fromValue(image.get())); ProxyListModel::add(image); emit layoutChanged(); } void ImageListModel::useConfig (const std::shared_ptr &config) { #if LINPHONE_FRIDAY if (!isLinphoneFriday()) overrideImages(config); #else overrideImages(config); #endif // if LINPHONE_FRIDAY } QString ImageListModel::getIds(){ QStringList ids; const QMetaObject *info = metaObject(); for (int i = info->propertyOffset(); i < info->propertyCount(); ++i) { const QMetaProperty metaProperty = info->property(i); const std::string id = metaProperty.name(); ids << QString::fromStdString(id); } return ids.join(", "); } QQmlPropertyMap * ImageListModel::getQmlData() { return &mData; } const QQmlPropertyMap * ImageListModel::getQmlData() const{ return &mData; } ImageModel * ImageListModel::getImageModel(const QString& id){ for(auto item : mList) { auto image = item.objectCast(); if(image->getId() == id) return image.get(); } return nullptr; } void ImageListModel::overrideImages (const std::shared_ptr &config) { if (!config) return; for(auto item : mList){ auto image = item.objectCast(); QString id = image->getId(); const std::string pathValue = config->getString(ImagesSection, id.toStdString(), ""); if(!pathValue.empty()){ image->setPath(QString::fromStdString(pathValue)); } } } linphone-desktop-5.0.2/linphone-app/src/components/other/images/ImageListModel.hpp000066400000000000000000000033531434616504300303170ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef IMAGE_LIST_MODEL_H_ #define IMAGE_LIST_MODEL_H_ // ============================================================================= #include #include #include #include #include #include "ImageModel.hpp" #include "app/proxyModel/ProxyListModel.hpp" class ImageModel; class ImageListModel : public ProxyListModel { Q_OBJECT public: ImageListModel (QObject *parent = nullptr); void useConfig (const std::shared_ptr &config); Q_INVOKABLE QString getIds(); QQmlPropertyMap * getQmlData(); const QQmlPropertyMap * getQmlData() const; ImageModel * getImageModel(const QString& id); private: void add(QSharedPointer imdn); void overrideImages (const std::shared_ptr &config); QStringList getImagesIds () const; QQmlPropertyMap mData; bool mAreReadOnlyImages = true; }; Q_DECLARE_METATYPE(QSharedPointer) #endif linphone-desktop-5.0.2/linphone-app/src/components/other/images/ImageModel.cpp000066400000000000000000000040671434616504300274610ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ImageModel.hpp" #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "components/core/CoreManager.hpp" // ============================================================================= ImageModel::ImageModel (const QString& id, const QString& path, const QString& description, QObject * parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mId = id; //setPath(path); mPath = path; setDescription(description) ; } // ----------------------------------------------------------------------------- QString ImageModel::getId() const{ return mId; } QString ImageModel::getPath() const{ return mPath; } QString ImageModel::getDescription() const{ return mDescription; } void ImageModel::setPath(const QString& data){ if(data != mPath){ mPath = data; emit pathChanged(); QString old = mId; mId="";// Force change emit idChanged(); mId=old; emit idChanged(); } } void ImageModel::setDescription(const QString& data){ if(data != mDescription){ mDescription = data; emit descriptionChanged(); } } void ImageModel::setUrl(const QUrl& url){ setPath(url.toString(QUrl::RemoveScheme)); } linphone-desktop-5.0.2/linphone-app/src/components/other/images/ImageModel.hpp000066400000000000000000000034731434616504300274660ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef IMAGE_MODEL_H #define IMAGE_MODEL_H // ============================================================================= #include #include #include #include #include "utils/LinphoneEnums.hpp" class ImageModel : public QObject { Q_OBJECT public: ImageModel (const QString& id, const QString& path, const QString& description, QObject * parent = nullptr); Q_PROPERTY(QString path MEMBER mPath WRITE setPath NOTIFY pathChanged) Q_PROPERTY(QString description MEMBER mDescription WRITE setDescription NOTIFY descriptionChanged) Q_PROPERTY(QString id MEMBER mId NOTIFY idChanged) QString getPath() const; QString getDescription() const; QString getId() const; void setPath(const QString& path); void setDescription(const QString& description); Q_INVOKABLE void setUrl(const QUrl& url); signals: void pathChanged(); void descriptionChanged(); void idChanged(); private: QString mId; QString mPath; QString mDescription; }; Q_DECLARE_METATYPE(std::shared_ptr); #endif linphone-desktop-5.0.2/linphone-app/src/components/other/images/ImageProxyModel.cpp000066400000000000000000000033261434616504300305200ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ImageProxyModel.hpp" #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "ImageListModel.hpp" #include "ImageModel.hpp" // ============================================================================= ImageProxyModel::ImageProxyModel (QObject *parent) : QSortFilterProxyModel(parent){ setSourceModel(App::getInstance()->getImageListModel()); } bool ImageProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent ) const { Q_UNUSED(sourceRow) Q_UNUSED(sourceParent) return true; } bool ImageProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const ImageModel *a = sourceModel()->data(left).value(); const ImageModel *b = sourceModel()->data(right).value(); return a->getId() < b->getId() ; } //--------------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/other/images/ImageProxyModel.hpp000066400000000000000000000026131434616504300305230ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef IMAGE_PROXY_MODEL_H_ #define IMAGE_PROXY_MODEL_H_ #include // ============================================================================= #include #include #include #include class ImageListModel; class ImageProxyModel : public QSortFilterProxyModel { Q_OBJECT public: ImageProxyModel (QObject *parent = nullptr); protected: virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/other/text-to-speech/000077500000000000000000000000001434616504300263475ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/text-to-speech/TextToSpeech.cpp000066400000000000000000000041451434616504300314360ustar00rootroot00000000000000/* * Copyright (c) 2010-2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifdef TEXTTOSPEECH_ENABLED #include #include #endif // ifdef TEXTTOSPEECH_ENABLED #include "TextToSpeech.hpp" #include // ============================================================================= #ifdef TEXTTOSPEECH_ENABLED TextToSpeech::TextToSpeech (QObject *parent) : QObject(parent) { mQtTextToSpeech = new QTextToSpeech(this); connect(mQtTextToSpeech, &QTextToSpeech::stateChanged, this, &TextToSpeech::onStateChanged); } TextToSpeech::~TextToSpeech(){ mQtTextToSpeech->deleteLater(); } void TextToSpeech::say (const QString &text) { if(mQtTextToSpeech->volume() == 0.0) mQtTextToSpeech->setVolume(1.0); QStringList names; for(auto i : mQtTextToSpeech->availableVoices()) names << i.name(); qInfo() << "Speech request : Volume " << mQtTextToSpeech->volume() << "; voices: " << names.join(",") << "; Engines: " << QTextToSpeech::availableEngines(); mQtTextToSpeech->say(text); } bool TextToSpeech::available () const { return true; } void TextToSpeech::onStateChanged(QTextToSpeech::State state){ qInfo() << "Speech Status : " << (int)state; } #else TextToSpeech::TextToSpeech (QObject *parent) : QObject(parent) {} TextToSpeech::~TextToSpeech(){} void TextToSpeech::say (const QString &) {} bool TextToSpeech::available () const { return false; } #endif // ifdef TEXTTOSPEECH_ENABLED linphone-desktop-5.0.2/linphone-app/src/components/other/text-to-speech/TextToSpeech.hpp000066400000000000000000000027411434616504300314430ustar00rootroot00000000000000/* * Copyright (c) 2010-2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TEXT_TO_SPEECH_H_ #define TEXT_TO_SPEECH_H_ #include #ifdef TEXTTOSPEECH_ENABLED #include #endif // ifdef TEXTTOSPEECH_ENABLED // ============================================================================= class QTextToSpeech; class TextToSpeech : public QObject { Q_OBJECT; Q_PROPERTY(bool available READ available CONSTANT); public: TextToSpeech (QObject *parent = Q_NULLPTR); ~TextToSpeech(); Q_INVOKABLE void say (const QString &text); #ifdef TEXTTOSPEECH_ENABLED public slots: void onStateChanged(QTextToSpeech::State state); #endif private: bool available () const; QTextToSpeech *mQtTextToSpeech = nullptr; }; #endif // ifndef TEXT_TO_SPEECH_H_ linphone-desktop-5.0.2/linphone-app/src/components/other/timeZone/000077500000000000000000000000001434616504300252705ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/timeZone/TimeZoneListModel.cpp000066400000000000000000000050341434616504300313450ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "TimeZoneListModel.hpp" #include // ============================================================================= using namespace std; TimeZoneListModel::TimeZoneListModel (QObject *parent) : ProxyListModel(parent) { initTimeZones(); } // ----------------------------------------------------------------------------- void TimeZoneListModel::initTimeZones () { resetData(); for(auto id : QTimeZone::availableTimeZoneIds()){ auto model = QSharedPointer::create(QTimeZone(id)); if(model->getCountryName().toUpper() != "DEFAULT") ProxyListModel::add(model); } } QHash TimeZoneListModel::roleNames () const { QHash roles; roles[Qt::DisplayRole] = "$modelData"; roles[Qt::DisplayRole+1] = "displayText"; return roles; } QVariant TimeZoneListModel::data (const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); auto timeZoneModel = getAt(row); if (role == Qt::DisplayRole) { return QVariant::fromValue(timeZoneModel.get()); }else{ int offset = timeZoneModel->getStandardTimeOffset()/3600; int absOffset = std::abs(offset); return QStringLiteral("%1 (UTC%2%3%4) %5") .arg(timeZoneModel->getCountryName()) .arg(offset >=0 ? "+" : "-") .arg(absOffset <10 ? "0" : "") .arg(absOffset) .arg(timeZoneModel->getTimeZone().comment()); } return QVariant(); } int TimeZoneListModel::get (const QTimeZone& timeZone) const { const auto it = find_if( mList.cbegin(), mList.cend(), [&timeZone](QSharedPointer item) { return item.objectCast()->getTimeZone() == timeZone; } ); return it != mList.cend() ? int(distance(mList.cbegin(), it)) : 0; } linphone-desktop-5.0.2/linphone-app/src/components/other/timeZone/TimeZoneListModel.hpp000066400000000000000000000025471434616504300313600ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TIME_ZONE_LIST_MODEL_H_ #define TIME_ZONE_LIST_MODEL_H_ #include "TimeZoneModel.hpp" #include "app/proxyModel/ProxyListModel.hpp" // ============================================================================= class TimeZoneListModel : public ProxyListModel { Q_OBJECT public: TimeZoneListModel (QObject *parent = Q_NULLPTR); void initTimeZones(); int get(const QTimeZone& timeZone = QTimeZone::systemTimeZone())const; QHash roleNames () const override; QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/other/timeZone/TimeZoneModel.cpp000066400000000000000000000034071434616504300305130ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "TimeZoneModel.hpp" #include "app/App.hpp" #include "utils/Utils.hpp" #include // ============================================================================= TimeZoneModel::TimeZoneModel (const QTimeZone& timeZone, QObject *parent ) : QObject(parent){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mTimeZone = timeZone; } TimeZoneModel::~TimeZoneModel(){ } QTimeZone TimeZoneModel::getTimeZone()const{ return mTimeZone; } int TimeZoneModel::getOffsetFromUtc() const{ return mTimeZone.offsetFromUtc(QDateTime::currentDateTime()); } int TimeZoneModel::getStandardTimeOffset() const{ return mTimeZone.standardTimeOffset(QDateTime::currentDateTime()); } QString TimeZoneModel::getCountryName() const{ return Utils::getCountryName(mTimeZone.country()); } QString TimeZoneModel::getDisplayName() const{ return mTimeZone.displayName(QTimeZone::TimeType::GenericTime); }linphone-desktop-5.0.2/linphone-app/src/components/other/timeZone/TimeZoneModel.hpp000066400000000000000000000032721434616504300305200ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TIME_ZONE_MODEL_H_ #define TIME_ZONE_MODEL_H_ #include #include #include // ============================================================================= class TimeZoneModel : public QObject { Q_OBJECT Q_PROPERTY(QTimeZone timezone MEMBER mTimeZone CONSTANT) Q_PROPERTY(int offsetFromUtc READ getOffsetFromUtc CONSTANT) Q_PROPERTY(int standardTimeOffset READ getStandardTimeOffset CONSTANT) Q_PROPERTY(QString countryName READ getCountryName CONSTANT) Q_PROPERTY(QString displayName READ getDisplayName CONSTANT) public: TimeZoneModel (const QTimeZone& timeZone, QObject *parent = nullptr); virtual ~TimeZoneModel(); QTimeZone getTimeZone()const; int getOffsetFromUtc() const; int getStandardTimeOffset() const; QString getCountryName() const; QString getDisplayName() const; private: QTimeZone mTimeZone; }; Q_DECLARE_METATYPE(TimeZoneModel*); #endif linphone-desktop-5.0.2/linphone-app/src/components/other/timeZone/TimeZoneProxyModel.cpp000066400000000000000000000037761434616504300315660ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/core/CoreManager.hpp" #include "TimeZoneModel.hpp" #include "TimeZoneListModel.hpp" #include "TimeZoneProxyModel.hpp" // ----------------------------------------------------------------------------- TimeZoneProxyModel::TimeZoneProxyModel (QObject *parent) : SortFilterProxyModel(parent) { mDeleteSourceModel = true; setSourceModel(new TimeZoneListModel(parent)); sort(0); } // ----------------------------------------------------------------------------- bool TimeZoneProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { auto test = sourceModel()->data(left); const TimeZoneModel* a = sourceModel()->data(left).value(); const TimeZoneModel* b = sourceModel()->data(right).value(); auto timeA = a->getStandardTimeOffset(); auto timeB = b->getStandardTimeOffset(); return timeA < timeB || (timeA == timeB && a->getCountryName() < b->getCountryName()); } int TimeZoneProxyModel::getIndex(TimeZoneModel * model) const{ auto listModel = qobject_cast(sourceModel()); int index = 0; if(model) index = listModel->get(model->getTimeZone()); else index = listModel->get(); return mapFromSource(sourceModel()->index(index, 0)).row(); } linphone-desktop-5.0.2/linphone-app/src/components/other/timeZone/TimeZoneProxyModel.hpp000066400000000000000000000025261434616504300315630ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TIME_ZONE_PROXY_MODEL_H_ #define TIME_ZONE_PROXY_MODEL_H_ #include "app/proxyModel/SortFilterProxyModel.hpp" // ============================================================================= class TimeZoneModel; class TimeZoneProxyModel : public SortFilterProxyModel { Q_OBJECT public: TimeZoneProxyModel (QObject *parent = Q_NULLPTR); Q_PROPERTY(int defaultIndex READ getIndex CONSTANT) Q_INVOKABLE int getIndex(TimeZoneModel * model = nullptr) const; protected: bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/other/units/000077500000000000000000000000001434616504300246405ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/other/units/Units.cpp000066400000000000000000000020661434616504300264520ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "Units.hpp" // ============================================================================= Units::Units (QObject *parent) : QObject(parent) {} float Units::getDp () const { #ifdef Q_OS_MACOS return float(96.0 / 72.0); #endif // ifdef Q_OS_MACOS return 1.0; } linphone-desktop-5.0.2/linphone-app/src/components/other/units/Units.hpp000066400000000000000000000021441434616504300264540ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef UNITS_H_ #define UNITS_H_ #include // ============================================================================= class Units : public QObject { Q_OBJECT; Q_PROPERTY(float dp READ getDp CONSTANT); public: Units (QObject *parent = Q_NULLPTR); private: float getDp () const; }; #endif // UNITS_H_ linphone-desktop-5.0.2/linphone-app/src/components/participant-imdn/000077500000000000000000000000001434616504300256205ustar00rootroot00000000000000ParticipantImdnStateListModel.cpp000066400000000000000000000072661434616504300341640ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/participant-imdn/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ParticipantImdnStateListModel.hpp" #include "ParticipantImdnStateModel.hpp" #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" // ============================================================================= ParticipantImdnStateListModel::ParticipantImdnStateListModel (std::shared_ptr message, QObject *parent) : ProxyListModel(parent) { QVector states; states.push_back(linphone::ChatMessage::State::Delivered); states.push_back(linphone::ChatMessage::State::DeliveredToUser); states.push_back(linphone::ChatMessage::State::Displayed); states.push_back(linphone::ChatMessage::State::NotDelivered); states.push_back(linphone::ChatMessage::State::Idle); states.push_back(linphone::ChatMessage::State::InProgress); states.push_back(linphone::ChatMessage::State::FileTransferError); states.push_back(linphone::ChatMessage::State::FileTransferDone); states.push_back(linphone::ChatMessage::State::FileTransferInProgress); for(int i = 0 ; i < states.size() ; ++i){ std::list> imdns = message->getParticipantsByImdnState(states[i]); for(auto imdn : imdns){ if(imdn->getParticipant()){ auto deviceModel = QSharedPointer::create(imdn); mList << deviceModel; } } } } //-------------------------------------------------------------------------------- QSharedPointer ParticipantImdnStateListModel::getImdnState(const std::shared_ptr & state){ QSharedPointer imdn; auto participant = state->getParticipant(); auto it = mList.begin(); if( participant){ auto imdnAddress = state->getParticipant()->getAddress(); while(it != mList.end() && !it->objectCast()->getAddress()->equal(imdnAddress)) ++it; }else it = mList.end(); if(it != mList.end()) imdn = it->objectCast(); else{// Create the new one imdn = QSharedPointer::create(state); add(imdn); } return imdn; } //-------------------------------------------------------------------------------- void ParticipantImdnStateListModel::updateState(const std::shared_ptr & state){ if(state->getParticipant()) { auto imdn = getImdnState(state); auto oldState = imdn->getState(); getImdnState(state)->update(state); if( oldState == LinphoneEnums::ChatMessageState::ChatMessageStateIdle && oldState != imdn->getState()) emit stateChangedFromIdle(); } } //-------------------------------------------------------------------------------- void ParticipantImdnStateListModel::onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state){ updateState(state); }ParticipantImdnStateListModel.hpp000066400000000000000000000035141434616504300341610ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/participant-imdn/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_IMDN_STATE_LIST_MODEL_H_ #define PARTICIPANT_IMDN_STATE_LIST_MODEL_H_ #include // ============================================================================= #include #include #include #include "app/proxyModel/ProxyListModel.hpp" class ParticipantImdnStateModel; class ParticipantImdnStateListModel : public ProxyListModel { Q_OBJECT public: ParticipantImdnStateListModel (std::shared_ptr message, QObject *parent = nullptr); QSharedPointer getImdnState(const std::shared_ptr & state); void updateState(const std::shared_ptr & state); public slots: void onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state); signals: void imdnStateChanged(); void stateChangedFromIdle(); }; Q_DECLARE_METATYPE(QSharedPointer) #endif linphone-desktop-5.0.2/linphone-app/src/components/participant-imdn/ParticipantImdnStateModel.cpp000066400000000000000000000052311434616504300333750ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ParticipantImdnStateModel.hpp" #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "components/core/CoreManager.hpp" // ============================================================================= ParticipantImdnStateModel::ParticipantImdnStateModel (const std::shared_ptr imdn, QObject * parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mState = (LinphoneEnums::fromLinphone(imdn->getState())); mStateChangeTime = (QDateTime::fromSecsSinceEpoch(imdn->getStateChangeTime())) ; auto participant = imdn->getParticipant(); if(participant) mAddress = imdn->getParticipant()->getAddress()->clone(); } // ----------------------------------------------------------------------------- LinphoneEnums::ChatMessageState ParticipantImdnStateModel::getState() const{ return mState; } QDateTime ParticipantImdnStateModel::getStateChangeTime() const{ return mStateChangeTime; } QString ParticipantImdnStateModel::getDisplayName() const{ return Utils::getDisplayName(mAddress); } std::shared_ptr ParticipantImdnStateModel::getAddress() const{ return mAddress; } void ParticipantImdnStateModel::update(const std::shared_ptr imdn){ setState(LinphoneEnums::fromLinphone(imdn->getState())); setStateChangeTime(QDateTime::fromSecsSinceEpoch(imdn->getStateChangeTime())) ; } void ParticipantImdnStateModel::setState(LinphoneEnums::ChatMessageState state){ if(state != mState){ mState = state; emit stateChanged(); } } void ParticipantImdnStateModel::setStateChangeTime(const QDateTime& changeTime){ if(changeTime != mStateChangeTime){ mStateChangeTime = changeTime; emit stateChangeTimeChanged(); } } linphone-desktop-5.0.2/linphone-app/src/components/participant-imdn/ParticipantImdnStateModel.hpp000066400000000000000000000043611434616504300334050ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_IMDN_STATE_MODEL_H_ #define PARTICIPANT_IMDN_STATE_MODEL_H_ #include // ============================================================================= #include #include #include #include "utils/LinphoneEnums.hpp" class ParticipantModel; class ParticipantImdnStateModel : public QObject { Q_OBJECT public: ParticipantImdnStateModel (const std::shared_ptr imdn, QObject * parent = nullptr); Q_PROPERTY(LinphoneEnums::ChatMessageState state MEMBER mState WRITE setState NOTIFY stateChanged) Q_PROPERTY(QDateTime stateChangeTime MEMBER mStateChangeTime WRITE setStateChangeTime NOTIFY stateChangeTimeChanged) Q_PROPERTY(QString displayName READ getDisplayName NOTIFY displayNameChanged) LinphoneEnums::ChatMessageState getState() const; QDateTime getStateChangeTime() const; QString getDisplayName() const; std::shared_ptr getAddress() const; void update(const std::shared_ptr state); void setState(LinphoneEnums::ChatMessageState state); void setStateChangeTime(const QDateTime& changeTime); signals: void stateChanged(); void stateChangeTimeChanged(); void displayNameChanged(); private: std::shared_ptr mAddress; LinphoneEnums::ChatMessageState mState; QDateTime mStateChangeTime; }; Q_DECLARE_METATYPE(std::shared_ptr); #endif ParticipantImdnStateProxyModel.cpp000066400000000000000000000066201434616504300343630ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/participant-imdn/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ParticipantImdnStateProxyModel.hpp" #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "ParticipantImdnStateListModel.hpp" #include "ParticipantImdnStateModel.hpp" // ============================================================================= ParticipantImdnStateProxyModel::ParticipantImdnStateProxyModel (QObject *parent) : QSortFilterProxyModel(parent){ } bool ParticipantImdnStateProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent ) const { auto listModel = qobject_cast(sourceModel()); const QModelIndex index = listModel->index(sourceRow, 0, sourceParent); const ParticipantImdnStateModel *imdn = index.data().value(); return imdn->getState() != LinphoneEnums::ChatMessageState::ChatMessageStateIdle; } bool ParticipantImdnStateProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const ParticipantImdnStateModel *imdnA = sourceModel()->data(left).value(); const ParticipantImdnStateModel *imdnB = sourceModel()->data(right).value(); return imdnA->getState() < imdnB->getState() || (imdnA->getState() == imdnB->getState() && imdnA->getStateChangeTime() < imdnB->getStateChangeTime()); } //--------------------------------------------------------------------------------- int ParticipantImdnStateProxyModel::getCount(){ //return sourceModel() ? sourceModel()->rowCount() : 0; return rowCount(); } ChatMessageModel * ParticipantImdnStateProxyModel::getChatMessageModel(){ return mChatMessageModel; } void ParticipantImdnStateProxyModel::setChatMessageModel(ChatMessageModel * message){ mChatMessageModel = message; if(message){ ParticipantImdnStateListModel *model = static_cast(sourceModel()); ParticipantImdnStateListModel *messageModel = message->getParticipantImdnStates().get(); if( model != messageModel){ if(model) disconnect(model, &ParticipantImdnStateListModel::countChanged, this, &ParticipantImdnStateProxyModel::countChanged); setSourceModel(messageModel); connect(messageModel, &ParticipantImdnStateListModel::countChanged, this, &ParticipantImdnStateProxyModel::countChanged); connect(messageModel, &ParticipantImdnStateListModel::stateChangedFromIdle, this, &ParticipantImdnStateProxyModel::invalidate); connect(messageModel, &ParticipantImdnStateListModel::stateChangedFromIdle, this, &ParticipantImdnStateProxyModel::countChanged); sort(0); emit countChanged(); } } emit chatMessageModelChanged(); } ParticipantImdnStateProxyModel.hpp000066400000000000000000000037141434616504300343710ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/participant-imdn/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_IMDN_STATE_PROXY_MODEL_H_ #define PARTICIPANT_IMDN_STATE_PROXY_MODEL_H_ #include // ============================================================================= #include #include #include #include class ParticipantImdnStateListModel; class ChatMessageModel; class ParticipantImdnStateProxyModel : public QSortFilterProxyModel { Q_OBJECT public: Q_PROPERTY(ChatMessageModel * chatMessageModel READ getChatMessageModel WRITE setChatMessageModel NOTIFY chatMessageModelChanged) Q_PROPERTY(int count READ getCount NOTIFY countChanged) ParticipantImdnStateProxyModel (QObject *parent = nullptr); ChatMessageModel * getChatMessageModel(); void setChatMessageModel(ChatMessageModel* message); int getCount(); signals: void chatMessageModelChanged(); void countChanged(); protected: virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; std::shared_ptr mImdns; ChatMessageModel * mChatMessageModel = nullptr; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/participant/000077500000000000000000000000001434616504300246735ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantDeviceListModel.cpp000066400000000000000000000303631434616504300326170ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "app/App.hpp" #include "ParticipantDeviceListModel.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" // ============================================================================= ParticipantDeviceListModel::ParticipantDeviceListModel (std::shared_ptr participant, QObject *parent) : ProxyListModel(parent) { std::list> devices = participant->getDevices() ; mCallModel = nullptr; for(auto device : devices){ auto deviceModel = ParticipantDeviceModel::create(mCallModel, device, isMe(device)); connect(this, &ParticipantDeviceListModel::securityLevelChanged, deviceModel.get(), &ParticipantDeviceModel::onSecurityLevelChanged); connect(deviceModel.get(), &ParticipantDeviceModel::isSpeakingChanged, this, &ParticipantDeviceListModel::onParticipantDeviceSpeaking); mList << deviceModel; } mInitialized = true; } ParticipantDeviceListModel::ParticipantDeviceListModel (CallModel * callModel, QObject *parent) : ProxyListModel(parent) { if(callModel && callModel->isConference()) { mCallModel = callModel; connect(mCallModel, &CallModel::conferenceModelChanged, this, &ParticipantDeviceListModel::onConferenceModelChanged); initConferenceModel(); } } void ParticipantDeviceListModel::initConferenceModel(){ if(!mInitialized && mCallModel){ auto conferenceModel = mCallModel->getConferenceSharedModel(); if(conferenceModel){ updateDevices(conferenceModel->getConference()->getMe()->getDevices(), true); updateDevices(conferenceModel->getConference()->getParticipantDeviceList(), false); qDebug() << "Conference have " << mList.size() << " devices"; connect(conferenceModel.get(), &ConferenceModel::activeSpeakerParticipantDevice, this, &ParticipantDeviceListModel::onActiveSpeakerParticipantDevice); connect(conferenceModel.get(), &ConferenceModel::participantAdded, this, &ParticipantDeviceListModel::onParticipantAdded); connect(conferenceModel.get(), &ConferenceModel::participantRemoved, this, &ParticipantDeviceListModel::onParticipantRemoved); connect(conferenceModel.get(), &ConferenceModel::participantDeviceAdded, this, &ParticipantDeviceListModel::onParticipantDeviceAdded); connect(conferenceModel.get(), &ConferenceModel::participantDeviceRemoved, this, &ParticipantDeviceListModel::onParticipantDeviceRemoved); connect(conferenceModel.get(), &ConferenceModel::conferenceStateChanged, this, &ParticipantDeviceListModel::onConferenceStateChanged); connect(conferenceModel.get(), &ConferenceModel::participantDeviceMediaCapabilityChanged, this, &ParticipantDeviceListModel::onParticipantDeviceMediaCapabilityChanged); connect(conferenceModel.get(), &ConferenceModel::participantDeviceMediaAvailabilityChanged, this, &ParticipantDeviceListModel::onParticipantDeviceMediaAvailabilityChanged); connect(conferenceModel.get(), &ConferenceModel::participantDeviceIsSpeakingChanged, this, &ParticipantDeviceListModel::onParticipantDeviceIsSpeakingChanged); mInitialized = true; } } } void ParticipantDeviceListModel::updateDevices(std::shared_ptr participant){ std::list> devices = participant->getDevices() ; bool meAdded = false; beginResetModel(); qDebug() << "Update devices from participant"; mList.clear(); for(auto device : devices){ bool addMe = isMe(device); auto deviceModel = ParticipantDeviceModel::create(mCallModel, device, addMe); connect(this, &ParticipantDeviceListModel::securityLevelChanged, deviceModel.get(), &ParticipantDeviceModel::onSecurityLevelChanged); connect(deviceModel.get(), &ParticipantDeviceModel::isSpeakingChanged, this, &ParticipantDeviceListModel::onParticipantDeviceSpeaking); mList << deviceModel; if( addMe) meAdded = true; } endResetModel(); if( meAdded) emit meChanged(); } void ParticipantDeviceListModel::updateDevices(const std::list>& devices, const bool& isMe){ for(auto device : devices){ add(device); } } bool ParticipantDeviceListModel::add(std::shared_ptr deviceToAdd){ auto deviceToAddAddr = deviceToAdd->getAddress(); int row = 0; qDebug() << "Adding device " << deviceToAdd->getAddress()->asString().c_str(); for(auto item : mList) { auto deviceModel = item.objectCast(); if(deviceModel->getDevice() == deviceToAdd) { qDebug() << "Device already exist. Send video update event"; deviceModel->updateVideoEnabled(); return false; }else if(deviceToAddAddr->equal(deviceModel->getDevice()->getAddress())){// Address is the same (same device) but the model is using another linphone object. Replace it. deviceModel->updateVideoEnabled(); removeRow(row); break; } ++row; } bool addMe = isMe(deviceToAdd); auto deviceModel = ParticipantDeviceModel::create(mCallModel, deviceToAdd, addMe); connect(this, &ParticipantDeviceListModel::securityLevelChanged, deviceModel.get(), &ParticipantDeviceModel::onSecurityLevelChanged); connect(deviceModel.get(), &ParticipantDeviceModel::isSpeakingChanged, this, &ParticipantDeviceListModel::onParticipantDeviceSpeaking); ProxyListModel::add(deviceModel); qDebug() << "Device added. Count=" << mList.count(); QStringList debugDevices; for(auto i : mList){ auto item = i.objectCast(); debugDevices.push_back( item->getAddress()); } qDebug() << debugDevices.join("\n"); if( addMe){ qDebug() << "Added a me device"; emit meChanged(); }else if(mList.size() == 1 || (mList.size() == 2 && isMe(mList.front().objectCast()->getDevice()))){ mActiveSpeaker = mList.back().objectCast(); emit activeSpeakerChanged(); } return true; } bool ParticipantDeviceListModel::remove(std::shared_ptr deviceToRemove){ int row = 0; for(auto item : mList){ auto device = item.objectCast(); if( device->getDevice() == deviceToRemove){ device->updateVideoEnabled(); removeRow(row); return true; }else ++row; } return false; } QSharedPointer ParticipantDeviceListModel::get(std::shared_ptr deviceToGet, int * index){ int row = 0; for(auto item : mList){ auto device = item.objectCast(); if( device->getDevice() == deviceToGet){ if(index) *index = row; return device; }else ++row; } return nullptr; } QSharedPointer ParticipantDeviceListModel::getMe(int * index)const{ int row = 0; for(auto item : mList){ auto device = item.objectCast(); if( device->isMe()){ if(index) *index = row; return device; }else ++row; } return nullptr; } ParticipantDeviceModel* ParticipantDeviceListModel::getActiveSpeakerModel() const{ return mActiveSpeaker.get(); } bool ParticipantDeviceListModel::isMe(std::shared_ptr deviceToCheck)const{ if(mCallModel){ auto devices = mCallModel->getConferenceSharedModel()->getConference()->getMe()->getDevices(); auto deviceToCheckAddress = deviceToCheck->getAddress(); for(auto device : devices){ if(deviceToCheckAddress == device->getAddress()) return true; } } return false; } bool ParticipantDeviceListModel::isMeAlone() const{ for(auto item : mList){ auto device = item.objectCast(); if( !isMe(device->getDevice())) return false; } return true; } void ParticipantDeviceListModel::onConferenceModelChanged (){ if(!mInitialized){ initConferenceModel(); } } void ParticipantDeviceListModel::onSecurityLevelChanged(std::shared_ptr device){ emit securityLevelChanged(device); } //---------------------------------------------------------------------------------------------------------- void ParticipantDeviceListModel::onParticipantAdded(const std::shared_ptr & participant){ std::list> devices = participant->getDevices() ; if(devices.size() == 0) qDebug() << "Participant has no device. It will not be added : " << participant->getAddress()->asString().c_str(); else for(auto device : devices) add(device); } void ParticipantDeviceListModel::onParticipantRemoved(const std::shared_ptr & participant){ std::list> devices = participant->getDevices() ; for(auto device : devices) remove(device); } void ParticipantDeviceListModel::onParticipantDeviceAdded(const std::shared_ptr & participantDevice){ qDebug() << "Adding new device : " << mList.count(); auto conferenceModel = mCallModel->getConferenceSharedModel(); std::list> devices; for(int i = 0 ; i < 2 ; ++i){ if( i == 0) devices = conferenceModel->getConference()->getParticipantDeviceList();// Active devices. else devices = conferenceModel->getConference()->getMe()->getDevices(); for(auto realParticipantDevice : devices){ if( realParticipantDevice == participantDevice){ add(realParticipantDevice); return; } } } qDebug() << "No participant device found from linphone::ParticipantDevice at onParticipantDeviceAdded"; } void ParticipantDeviceListModel::onParticipantDeviceRemoved(const std::shared_ptr & participantDevice){ qDebug() << "Removing participant device : " << mList.count(); if(!remove(participantDevice)) qDebug() << "No participant device found from linphone::ParticipantDevice at onParticipantDeviceRemoved"; } void ParticipantDeviceListModel::onConferenceStateChanged(linphone::Conference::State newState){ if(newState == linphone::Conference::State::Created){ if(mCallModel && mCallModel->isConference()) { auto conferenceModel = mCallModel->getConferenceSharedModel(); updateDevices(conferenceModel->getConference()->getMe()->getDevices(), true); updateDevices(conferenceModel->getConference()->getParticipantDeviceList(), false); } emit conferenceCreated(); } } void ParticipantDeviceListModel::onParticipantDeviceMediaCapabilityChanged(const std::shared_ptr & participantDevice){ auto device = get(participantDevice); if(device) device->updateVideoEnabled(); else onParticipantDeviceAdded(participantDevice); device = get(participantDevice); if( device && device->isMe()){ // Capability change for me. Update all videos. for(auto item : mList) { auto device = item.objectCast(); device->updateVideoEnabled(); } } } void ParticipantDeviceListModel::onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr & participantDevice){ auto device = get(participantDevice); if(device) device->updateVideoEnabled(); else onParticipantDeviceAdded(participantDevice); } void ParticipantDeviceListModel::onActiveSpeakerParticipantDevice(const std::shared_ptr& participantDevice){ auto device = get(participantDevice); if( device){ mActiveSpeaker = device; emit activeSpeakerChanged(); } } void ParticipantDeviceListModel::onParticipantDeviceIsSpeakingChanged(const std::shared_ptr & participantDevice, bool isSpeaking){ auto device = get(participantDevice); if( device) emit participantSpeaking(device.get()); } void ParticipantDeviceListModel::onParticipantDeviceSpeaking(){ } linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantDeviceListModel.hpp000066400000000000000000000074051434616504300326250ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_DEVICE_LIST_MODEL_H_ #define PARTICIPANT_DEVICE_LIST_MODEL_H_ #include // ============================================================================= #include #include #include #include "app/proxyModel/ProxyListModel.hpp" #include "components/call/CallModel.hpp" class ParticipantDeviceModel; class ParticipantDeviceListModel : public ProxyListModel { Q_OBJECT public: ParticipantDeviceListModel (std::shared_ptr participant, QObject *parent = nullptr); ParticipantDeviceListModel (CallModel * callModel, QObject *parent = nullptr); void initConferenceModel(); void updateDevices(std::shared_ptr participant); void updateDevices(const std::list>& devices, const bool& isMe); bool add(std::shared_ptr deviceToAdd); bool remove(std::shared_ptr deviceToAdd); QSharedPointer get(std::shared_ptr deviceToGet, int * index = nullptr); QSharedPointer getMe(int * index = nullptr)const; ParticipantDeviceModel* getActiveSpeakerModel() const; bool isMe(std::shared_ptr device)const; bool isMeAlone() const; public slots: void onActiveSpeakerParticipantDevice(const std::shared_ptr& participantDevice); void onConferenceModelChanged (); void onSecurityLevelChanged(std::shared_ptr device); void onParticipantAdded(const std::shared_ptr & participant); void onParticipantRemoved(const std::shared_ptr & participant); void onParticipantDeviceAdded(const std::shared_ptr & participantDevice); void onParticipantDeviceRemoved(const std::shared_ptr & participantDevice); void onConferenceStateChanged(linphone::Conference::State newState); void onParticipantDeviceMediaCapabilityChanged(const std::shared_ptr & participantDevice); void onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr & participantDevice); void onParticipantDeviceIsSpeakingChanged(const std::shared_ptr & device, bool isSpeaking); void onParticipantDeviceSpeaking(); signals: void activeSpeakerChanged(); void securityLevelChanged(std::shared_ptr device); void participantSpeaking(ParticipantDeviceModel *speakingDevice); void conferenceCreated(); void meChanged(); private: CallModel * mCallModel = nullptr; QSharedPointer mActiveSpeaker; //QList mActiveSpeakers;// First item is last speaker bool mInitialized = false; }; Q_DECLARE_METATYPE(std::shared_ptr) #endif // PARTICIPANT_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantDeviceListener.cpp000066400000000000000000000055231434616504300325100ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ParticipantDeviceListener.hpp" #include // ============================================================================= ParticipantDeviceListener::ParticipantDeviceListener(QObject *parent) : QObject(parent) { } //-------------------------------------------------------------------- void ParticipantDeviceListener::onIsSpeakingChanged(const std::shared_ptr & participantDevice, bool isSpeaking) { qDebug() << "onIsSpeakingChanged " << participantDevice->getAddress()->asString().c_str() << " " << isSpeaking; emit isSpeakingChanged(participantDevice, isSpeaking); } void ParticipantDeviceListener::onIsMuted(const std::shared_ptr & participantDevice, bool isMutedVar) { qDebug() << "onIsMuted " << isMutedVar << " vs " << participantDevice->getIsMuted() << " for " << participantDevice->getAddress()->asString().c_str(); emit isMuted(participantDevice, isMutedVar); } void ParticipantDeviceListener::onStateChanged(const std::shared_ptr & participantDevice, linphone::ParticipantDeviceState state){ qDebug() << "onStateChanged: " << participantDevice->getAddress()->asString().c_str() << " " << (int)state; emit stateChanged(participantDevice, state); } void ParticipantDeviceListener::onStreamCapabilityChanged(const std::shared_ptr & participantDevice, linphone::MediaDirection direction, linphone::StreamType streamType) { qDebug() << "onStreamCapabilityChanged: " << participantDevice->getAddress()->asString().c_str() << " " << (int)direction << " / " << (int)streamType; emit streamCapabilityChanged(participantDevice, direction, streamType); } void ParticipantDeviceListener::onStreamAvailabilityChanged(const std::shared_ptr & participantDevice, bool available, linphone::StreamType streamType) { qDebug() << "onStreamAvailabilityChanged: " << participantDevice->getAddress()->asString().c_str() << " " << available<< " / " << (int)streamType; emit streamAvailabilityChanged(participantDevice, available, streamType); }linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantDeviceListener.hpp000066400000000000000000000052511434616504300325130ustar00rootroot00000000000000/* * Copyright (c) 2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_DEVICE_LISTENER_H_ #define PARTICIPANT_DEVICE_LISTENER_H_ #include // ============================================================================= #include #include #include class CallModel; class ParticipantDeviceListener : public QObject, public linphone::ParticipantDeviceListener { Q_OBJECT public: ParticipantDeviceListener (QObject *parent = nullptr); virtual void onIsSpeakingChanged(const std::shared_ptr & participantDevice, bool isSpeaking) override; virtual void onIsMuted(const std::shared_ptr & participantDevice, bool isMuted) override; virtual void onStateChanged(const std::shared_ptr & participantDevice, linphone::ParticipantDeviceState state) override; virtual void onStreamCapabilityChanged(const std::shared_ptr & participantDevice, linphone::MediaDirection direction, linphone::StreamType streamType) override; virtual void onStreamAvailabilityChanged(const std::shared_ptr & participantDevice, bool available, linphone::StreamType streamType) override; signals: void isSpeakingChanged(const std::shared_ptr & participantDevice, bool isSpeaking); void isMuted(const std::shared_ptr & participantDevice, bool isMuted); void stateChanged(const std::shared_ptr & participantDevice, linphone::ParticipantDeviceState state); void streamCapabilityChanged(const std::shared_ptr & participantDevice, linphone::MediaDirection direction, linphone::StreamType streamType); void streamAvailabilityChanged(const std::shared_ptr & participantDevice, bool available, linphone::StreamType streamType); }; #endif // PARTICIPANT_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantDeviceModel.cpp000066400000000000000000000175031434616504300317640ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ParticipantDeviceModel.hpp" #include "ParticipantDeviceListener.hpp" #include #include "app/App.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" void ParticipantDeviceModel::connectTo(ParticipantDeviceListener * listener){ connect(listener, &ParticipantDeviceListener::isSpeakingChanged, this, &ParticipantDeviceModel::onIsSpeakingChanged); connect(listener, &ParticipantDeviceListener::isMuted, this, &ParticipantDeviceModel::onIsMuted); connect(listener, &ParticipantDeviceListener::stateChanged, this, &ParticipantDeviceModel::onStateChanged); connect(listener, &ParticipantDeviceListener::streamCapabilityChanged, this, &ParticipantDeviceModel::onStreamCapabilityChanged); connect(listener, &ParticipantDeviceListener::streamAvailabilityChanged, this, &ParticipantDeviceModel::onStreamAvailabilityChanged); } // ============================================================================= ParticipantDeviceModel::ParticipantDeviceModel (CallModel * callModel, std::shared_ptr device, const bool& isMe, QObject *parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mIsMe = isMe; mParticipantDevice = device; if( device) { mParticipantDeviceListener = std::make_shared(); connectTo(mParticipantDeviceListener.get()); device->addListener(mParticipantDeviceListener); mState = device->getState(); } mCall = callModel; if(mCall) connect(mCall, &CallModel::statusChanged, this, &ParticipantDeviceModel::onCallStatusChanged); mIsVideoEnabled = false; updateVideoEnabled(); } ParticipantDeviceModel::~ParticipantDeviceModel(){ if( mParticipantDevice) mParticipantDevice->removeListener(mParticipantDeviceListener); } QSharedPointer ParticipantDeviceModel::create(CallModel * callModel, std::shared_ptr device, const bool& isMe, QObject *parent){ QSharedPointer model = QSharedPointer::create(callModel, device, isMe, parent); if(model){ model->mSelf = model; return model; } return nullptr; } // ----------------------------------------------------------------------------- QString ParticipantDeviceModel::getName() const{ return mParticipantDevice ? Utils::coreStringToAppString(mParticipantDevice->getName()) : "NoName"; } QString ParticipantDeviceModel::getDisplayName() const{ return mParticipantDevice ? Utils::getDisplayName(mParticipantDevice->getAddress()) : ""; } int ParticipantDeviceModel::getSecurityLevel() const{ if( mParticipantDevice) { int security = (int)mParticipantDevice->getSecurityLevel(); return security; }else return 0; } time_t ParticipantDeviceModel::getTimeOfJoining() const{ return mParticipantDevice ? mParticipantDevice->getTimeOfJoining() : 0; } QString ParticipantDeviceModel::getAddress() const{ return mParticipantDevice ? Utils::coreStringToAppString(mParticipantDevice->getAddress()->asStringUriOnly()) : ""; } bool ParticipantDeviceModel::getPaused() const{ return mIsPaused; } bool ParticipantDeviceModel::getIsSpeaking() const{ return mIsSpeaking; } bool ParticipantDeviceModel::getIsMuted() const{ return mParticipantDevice ? mParticipantDevice->getIsMuted() : false; } LinphoneEnums::ParticipantDeviceState ParticipantDeviceModel::getState() const{ return LinphoneEnums::fromLinphone(mState); } std::shared_ptr ParticipantDeviceModel::getDevice(){ return mParticipantDevice; } bool ParticipantDeviceModel::isVideoEnabled() const{ return mIsVideoEnabled; } void ParticipantDeviceModel::setPaused(bool paused){ if(mIsPaused != paused){ mIsPaused = paused; emit isPausedChanged(); } } void ParticipantDeviceModel::setIsSpeaking(bool speaking){ if(mIsSpeaking != speaking){ mIsSpeaking = speaking; emit isSpeakingChanged(); } } void ParticipantDeviceModel::setState(LinphoneEnums::ParticipantDeviceState state){ auto newState = LinphoneEnums::toLinphone(state); if(mState != newState){ mState = newState; emit stateChanged(); } } void ParticipantDeviceModel::updateVideoEnabled(){ bool enabled = (mParticipantDevice && mParticipantDevice->isInConference() && mParticipantDevice->getStreamAvailability(linphone::StreamType::Video) && ( mParticipantDevice->getStreamCapability(linphone::StreamType::Video) == linphone::MediaDirection::SendRecv || mParticipantDevice->getStreamCapability(linphone::StreamType::Video) == linphone::MediaDirection::SendOnly ) || isMe()) && !mIsPaused; if( mIsVideoEnabled != enabled && mCall && mCall->getCall()->getState() == linphone::Call::State::StreamsRunning) { qDebug() << "VideoEnabled: " << enabled << ", old=" << mIsVideoEnabled << (mParticipantDevice ? mParticipantDevice->getAddress()->asString().c_str() : "") << ", me=" << isMe() << ", CallState=" << (mCall ? (int)mCall->getCall()->getState() : -1); mIsVideoEnabled = enabled; emit videoEnabledChanged(); } } bool ParticipantDeviceModel::isMe() const{ return mIsMe; } void ParticipantDeviceModel::onSecurityLevelChanged(std::shared_ptr device){ if(!device || mParticipantDevice && mParticipantDevice->getAddress()->weakEqual(device)) emit securityLevelChanged(); } void ParticipantDeviceModel::onCallStatusChanged(){ if( mCall->getCall()->getState() == linphone::Call::State::StreamsRunning){ updateVideoEnabled(); } } //-------------------------------------------------------------------- void ParticipantDeviceModel::onIsSpeakingChanged(const std::shared_ptr & participantDevice, bool isSpeaking) { setIsSpeaking(isSpeaking); } void ParticipantDeviceModel::onIsMuted(const std::shared_ptr & participantDevice, bool isMuted) { emit isMutedChanged(); } void ParticipantDeviceModel::onStateChanged(const std::shared_ptr & participantDevice, linphone::ParticipantDeviceState state){ switch(state){ case linphone::ParticipantDeviceState::Joining: break; case linphone::ParticipantDeviceState::Present: setPaused(false);break; case linphone::ParticipantDeviceState::Leaving: break; case linphone::ParticipantDeviceState::Left: break; case linphone::ParticipantDeviceState::ScheduledForJoining: break; case linphone::ParticipantDeviceState::ScheduledForLeaving: break; case linphone::ParticipantDeviceState::OnHold: setPaused(true);break; case linphone::ParticipantDeviceState::Alerting: break; case linphone::ParticipantDeviceState::MutedByFocus: break; default:{} } setState(LinphoneEnums::fromLinphone(state)); updateVideoEnabled(); } void ParticipantDeviceModel::onStreamCapabilityChanged(const std::shared_ptr & participantDevice, linphone::MediaDirection direction, linphone::StreamType streamType) { updateVideoEnabled(); } void ParticipantDeviceModel::onStreamAvailabilityChanged(const std::shared_ptr & participantDevice, bool available, linphone::StreamType streamType) { updateVideoEnabled(); }linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantDeviceModel.hpp000066400000000000000000000107051434616504300317660ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_DEVICE_MODEL_H_ #define PARTICIPANT_DEVICE_MODEL_H_ #include #include "utils/LinphoneEnums.hpp" // ============================================================================= #include #include #include #include class CallModel; class ParticipantDeviceListener; class ParticipantDeviceModel : public QObject { Q_OBJECT public: ParticipantDeviceModel (CallModel * callModel, std::shared_ptr device, const bool& isMe = false, QObject *parent = nullptr); virtual ~ParticipantDeviceModel(); static QSharedPointer create(CallModel* callModel, std::shared_ptr device, const bool& isMe = false, QObject *parent = nullptr); Q_PROPERTY(QString displayName READ getDisplayName CONSTANT) Q_PROPERTY(QString name READ getName CONSTANT) Q_PROPERTY(QString address READ getAddress CONSTANT) Q_PROPERTY(int securityLevel READ getSecurityLevel NOTIFY securityLevelChanged) Q_PROPERTY(time_t timeOfJoining READ getTimeOfJoining CONSTANT) Q_PROPERTY(bool videoEnabled READ isVideoEnabled NOTIFY videoEnabledChanged) Q_PROPERTY(bool isMe READ isMe CONSTANT) Q_PROPERTY(bool isPaused READ getPaused WRITE setPaused NOTIFY isPausedChanged) Q_PROPERTY(bool isSpeaking READ getIsSpeaking WRITE setIsSpeaking NOTIFY isSpeakingChanged) Q_PROPERTY(bool isMuted READ getIsMuted NOTIFY isMutedChanged) Q_PROPERTY(LinphoneEnums::ParticipantDeviceState state READ getState WRITE setState NOTIFY stateChanged) QString getName() const; QString getDisplayName() const; QString getAddress() const; int getSecurityLevel() const; time_t getTimeOfJoining() const; bool isVideoEnabled() const; bool isMe() const; bool getPaused() const; bool getIsSpeaking() const; bool getIsMuted() const; LinphoneEnums::ParticipantDeviceState getState() const; std::shared_ptr getDevice(); void setPaused(bool paused); void setIsSpeaking(bool speaking); void setState(LinphoneEnums::ParticipantDeviceState state); virtual void onIsSpeakingChanged(const std::shared_ptr & participantDevice, bool isSpeaking); virtual void onIsMuted(const std::shared_ptr & participantDevice, bool isMuted); virtual void onStateChanged(const std::shared_ptr & participantDevice, linphone::ParticipantDeviceState state); virtual void onStreamCapabilityChanged(const std::shared_ptr & participantDevice, linphone::MediaDirection direction, linphone::StreamType streamType); virtual void onStreamAvailabilityChanged(const std::shared_ptr & participantDevice, bool available, linphone::StreamType streamType); void connectTo(ParticipantDeviceListener * listener); void updateVideoEnabled(); public slots: void onSecurityLevelChanged(std::shared_ptr device); void onCallStatusChanged(); signals: void securityLevelChanged(); void videoEnabledChanged(); void isPausedChanged(); void isSpeakingChanged(); void isMutedChanged(); void stateChanged(); private: bool mIsMe = false; bool mIsVideoEnabled; bool mIsPaused = false; bool mIsSpeaking = false; linphone::ParticipantDeviceState mState; std::shared_ptr mParticipantDevice; std::shared_ptr mParticipantDeviceListener; // This is passed to linpĥone object and must be in shared_ptr CallModel * mCall; QWeakPointer mSelf; }; Q_DECLARE_METATYPE(QSharedPointer) #endif // PARTICIPANT_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantDeviceProxyModel.cpp000066400000000000000000000110111434616504300330120ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "ParticipantDeviceProxyModel.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "ParticipantDeviceListModel.hpp" // ============================================================================= ParticipantDeviceProxyModel::ParticipantDeviceProxyModel (QObject *parent) : SortFilterProxyModel(parent){ mDeleteSourceModel = true; } ParticipantDeviceProxyModel::~ParticipantDeviceProxyModel(){ } bool ParticipantDeviceProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent ) const { auto listModel = qobject_cast(sourceModel()); const QModelIndex index = listModel->index(sourceRow, 0, sourceParent); const ParticipantDeviceModel *device = index.data().value(); return device && (isShowMe() || !device->isMe()); } bool ParticipantDeviceProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const ParticipantDeviceModel *deviceA = sourceModel()->data(left).value(); const ParticipantDeviceModel *deviceB = sourceModel()->data(right).value(); return deviceA->getTimeOfJoining() > deviceB->getTimeOfJoining(); } //--------------------------------------------------------------------------------- ParticipantDeviceModel *ParticipantDeviceProxyModel::getAt(int row){ QModelIndex sourceIndex = mapToSource(this->index(row, 0)); return sourceModel()->data(sourceIndex).value(); } ParticipantDeviceModel* ParticipantDeviceProxyModel::getActiveSpeakerModel(){ auto listModel = qobject_cast(sourceModel()); return listModel ? listModel->getActiveSpeakerModel() : nullptr; } CallModel * ParticipantDeviceProxyModel::getCallModel() const{ return mCallModel; } ParticipantDeviceModel * ParticipantDeviceProxyModel::getMe() const{ auto listModel = qobject_cast(sourceModel()); return listModel ? listModel->getMe().get() : nullptr; } bool ParticipantDeviceProxyModel::isShowMe() const{ return mShowMe; } void ParticipantDeviceProxyModel::connectTo(ParticipantDeviceListModel* model){ connect(model, &ParticipantDeviceListModel::countChanged, this, &ParticipantDeviceProxyModel::onCountChanged); connect(model, &ParticipantDeviceListModel::participantSpeaking, this, &ParticipantDeviceProxyModel::onParticipantSpeaking); connect(model, &ParticipantDeviceListModel::conferenceCreated, this, &ParticipantDeviceProxyModel::conferenceCreated); connect(model, &ParticipantDeviceListModel::meChanged, this, &ParticipantDeviceProxyModel::meChanged); connect(model, &ParticipantDeviceListModel::activeSpeakerChanged, this, &ParticipantDeviceProxyModel::activeSpeakerChanged); } void ParticipantDeviceProxyModel::setCallModel(CallModel * callModel){ setFilterType(1); mCallModel = callModel; deleteSourceModel(); auto newSourceModel = new ParticipantDeviceListModel(mCallModel); connectTo(newSourceModel); setSourceModel(newSourceModel); emit countChanged(); emit meChanged(); } void ParticipantDeviceProxyModel::setParticipant(ParticipantModel * participant){ setFilterType(0); deleteSourceModel(); auto newSourceModel = participant->getParticipantDevices().get(); connectTo(newSourceModel); setSourceModel(newSourceModel); emit countChanged(); emit meChanged(); } void ParticipantDeviceProxyModel::setShowMe(const bool& show){ if( mShowMe != show) { mShowMe = show; emit showMeChanged(); invalidate(); } } void ParticipantDeviceProxyModel::onCountChanged(){ qDebug() << "Count changed : " << getCount(); } void ParticipantDeviceProxyModel::onParticipantSpeaking(ParticipantDeviceModel * speakingDevice){ emit participantSpeaking(speakingDevice); }linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantDeviceProxyModel.hpp000066400000000000000000000052561434616504300330350ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_DEVICE_PROXY_MODEL_H_ #define PARTICIPANT_DEVICE_PROXY_MODEL_H_ #include // ============================================================================= #include #include #include #include #include "app/proxyModel/SortFilterProxyModel.hpp" class ParticipantDeviceListModel; class ParticipantDeviceModel; class ParticipantModel; class CallModel; class ParticipantDeviceProxyModel : public SortFilterProxyModel { Q_OBJECT public: Q_PROPERTY(CallModel * callModel READ getCallModel WRITE setCallModel NOTIFY callModelChanged) Q_PROPERTY(bool showMe READ isShowMe WRITE setShowMe NOTIFY showMeChanged) Q_PROPERTY(ParticipantDeviceModel * me READ getMe NOTIFY meChanged) Q_PROPERTY(ParticipantDeviceModel* activeSpeaker READ getActiveSpeakerModel NOTIFY activeSpeakerChanged) ParticipantDeviceProxyModel (QObject *parent = nullptr); ~ParticipantDeviceProxyModel(); Q_INVOKABLE ParticipantDeviceModel* getAt(int row); ParticipantDeviceModel* getActiveSpeakerModel(); ParticipantDeviceModel* getMe() const; CallModel * getCallModel() const; bool isShowMe() const; void setCallModel(CallModel * callModel); void setParticipant(ParticipantModel * participant); void setShowMe(const bool& show); void connectTo(ParticipantDeviceListModel* model); public slots: void onCountChanged(); void onParticipantSpeaking(ParticipantDeviceModel * speakingDevice); signals: void activeSpeakerChanged(); void callModelChanged(); void showMeChanged(); void meChanged(); void participantSpeaking(ParticipantDeviceModel * speakingDevice); void conferenceCreated(); protected: virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; CallModel * mCallModel; bool mShowMe = true; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantListModel.cpp000066400000000000000000000350121434616504300314730ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/core/CoreManager.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "components/conference/ConferenceModel.hpp" #include "utils/Utils.hpp" #include "ParticipantListModel.hpp" #include "ParticipantModel.hpp" #include // ============================================================================= ParticipantListModel::ParticipantListModel (ChatRoomModel * chatRoomModel, QObject *parent) : ProxyListModel(parent) { if( chatRoomModel) { mChatRoomModel = chatRoomModel; connect(mChatRoomModel, &ChatRoomModel::securityEvent, this, &ParticipantListModel::onSecurityEvent); connect(mChatRoomModel, &ChatRoomModel::conferenceJoined, this, &ParticipantListModel::onConferenceJoined); connect(mChatRoomModel, &ChatRoomModel::participantAdded, this, QOverload &>::of(&ParticipantListModel::onParticipantAdded)); connect(mChatRoomModel, &ChatRoomModel::participantRemoved, this, QOverload &>::of(&ParticipantListModel::onParticipantRemoved)); connect(mChatRoomModel, &ChatRoomModel::participantAdminStatusChanged, this, QOverload &>::of(&ParticipantListModel::onParticipantAdminStatusChanged)); connect(mChatRoomModel, &ChatRoomModel::participantDeviceAdded, this, &ParticipantListModel::onParticipantDeviceAdded); connect(mChatRoomModel, &ChatRoomModel::participantDeviceRemoved, this, &ParticipantListModel::onParticipantDeviceRemoved); connect(mChatRoomModel, &ChatRoomModel::participantRegistrationSubscriptionRequested, this, &ParticipantListModel::onParticipantRegistrationSubscriptionRequested); connect(mChatRoomModel, &ChatRoomModel::participantRegistrationUnsubscriptionRequested, this, &ParticipantListModel::onParticipantRegistrationUnsubscriptionRequested); updateParticipants(); } } ParticipantListModel::ParticipantListModel (ConferenceModel * conferenceModel, QObject *parent) : ProxyListModel(parent) { if( conferenceModel) { mConferenceModel = conferenceModel; connect(mConferenceModel, &ConferenceModel::participantAdded, this, QOverload &>::of(&ParticipantListModel::onParticipantAdded)); connect(mConferenceModel, &ConferenceModel::participantRemoved, this, QOverload &>::of(&ParticipantListModel::onParticipantRemoved)); connect(mConferenceModel, &ConferenceModel::participantAdminStatusChanged, this, QOverload &>::of(&ParticipantListModel::onParticipantAdminStatusChanged)); connect(mConferenceModel, &ConferenceModel::conferenceStateChanged, this, &ParticipantListModel::onStateChanged); updateParticipants(); } } ParticipantListModel::~ParticipantListModel(){ mList.clear(); mChatRoomModel = nullptr; mConferenceModel = nullptr; } // ----------------------------------------------------------------------------- ChatRoomModel *ParticipantListModel::getChatRoomModel() const{ return mChatRoomModel; } ConferenceModel *ParticipantListModel::getConferenceModel() const{ return mConferenceModel; } std::list> ParticipantListModel::getParticipants()const{ std::list> participants; for(auto participant : mList){ participants.push_back(Utils::interpretUrl(participant.objectCast()->getSipAddress())); } return participants; } QString ParticipantListModel::addressesToString()const{ QStringList txt; for(auto item : mList){ auto participant = item.objectCast(); if( participant->getParticipant())// is Participant. We test it because this participant is not accepted by chat room yet. txt << Utils::coreStringToAppString(participant->getParticipant()->getAddress()->asStringUriOnly()); } txt.removeFirst();// Remove me return txt.join(", "); } QString ParticipantListModel::displayNamesToString()const{ QStringList txt; for(auto participant : mList){ auto p = participant.objectCast()->getParticipant(); if(p){ QString displayName = Utils::getDisplayName(p->getAddress()); if(displayName != "") txt << displayName; } } txt.removeFirst();// Remove me return txt.join(", "); } QString ParticipantListModel::usernamesToString()const{ QStringList txt; for(auto item : mList){ auto participant = item.objectCast()->getParticipant(); std::string username = participant->getAddress()->getDisplayName(); if(username == "") username = participant->getAddress()->getUsername(); txt << Utils::coreStringToAppString(username); } txt.removeFirst();// Remove me return txt.join(", "); } bool ParticipantListModel::contains(const QString& address) const{ auto testAddress = Utils::interpretUrl(address); bool exists = false; for(auto itParticipant = mList.begin() ; !exists && itParticipant != mList.end() ; ++itParticipant) exists = testAddress->weakEqual(Utils::interpretUrl(itParticipant->objectCast()->getSipAddress() )); return exists; } // ----------------------------------------------------------------------------- void ParticipantListModel::updateParticipants () { if( mChatRoomModel || mConferenceModel) { bool changed = false; auto dbParticipants = (mChatRoomModel ? mChatRoomModel->getParticipants() : mConferenceModel->getParticipantList()); //Remove left participants auto itParticipant = mList.begin(); while(itParticipant != mList.end()) { auto itDbParticipant = dbParticipants.begin(); while(itDbParticipant != dbParticipants.end() && (itParticipant->objectCast()->getParticipant() && !(*itDbParticipant)->getAddress()->weakEqual(itParticipant->objectCast()->getParticipant()->getAddress()) || !itParticipant->objectCast()->getParticipant() && !(*itDbParticipant)->getAddress()->weakEqual(Utils::interpretUrl(itParticipant->objectCast()->getSipAddress())) ) ){ ++itDbParticipant; } if( itDbParticipant == dbParticipants.end()){ int row = itParticipant - mList.begin(); beginRemoveRows(QModelIndex(), row, row); itParticipant = mList.erase(itParticipant); endRemoveRows(); changed = true; }else ++itParticipant; } // Add new for(auto dbParticipant : dbParticipants){ auto itParticipant = mList.begin(); while(itParticipant != mList.end() && (( itParticipant->objectCast()->getParticipant() && !dbParticipant->getAddress()->weakEqual(itParticipant->objectCast()->getParticipant()->getAddress()) ) || (!itParticipant->objectCast()->getParticipant() && !dbParticipant->getAddress()->weakEqual(Utils::interpretUrl(itParticipant->objectCast()->getSipAddress())) )) ){ ++itParticipant; } if( itParticipant == mList.end()){ auto participant = QSharedPointer::create(dbParticipant); add(participant); changed = true; }else if(!itParticipant->objectCast()->getParticipant() || itParticipant->objectCast()->getParticipant() != dbParticipant){ itParticipant->objectCast()->setParticipant(dbParticipant); changed = true; } } if( changed){ emit layoutChanged(); emit participantsChanged(); emit countChanged(); } } } void ParticipantListModel::add (QSharedPointer participant){ int row = mList.count(); connect(this, &ParticipantListModel::deviceSecurityLevelChanged, participant.get(), &ParticipantModel::onDeviceSecurityLevelChanged); connect(this, &ParticipantListModel::securityLevelChanged, participant.get(), &ParticipantModel::onSecurityLevelChanged); connect(participant.get(),&ParticipantModel::updateAdminStatus, this, &ParticipantListModel::setAdminStatus); ProxyListModel::add(participant); emit layoutChanged(); emit participantsChanged(); } void ParticipantListModel::add(const std::shared_ptr & participant){ updateParticipants(); } void ParticipantListModel::add(const std::shared_ptr & participantAddress){ add((mChatRoomModel ? mChatRoomModel->getChatRoom()->findParticipant(participantAddress) : mConferenceModel->getConference()->findParticipant(participantAddress))); } void ParticipantListModel::remove (ParticipantModel *model) { QString address = model->getSipAddress(); int index = 0; bool found = false; auto itParticipant = mList.begin() ; while(!found && itParticipant != mList.end()){ if( itParticipant->objectCast()->getSipAddress() == address) found = true; else{ ++itParticipant; ++index; } } if(found) { removeRow(index); emit participantsChanged(); } } const QSharedPointer ParticipantListModel::getParticipant(const std::shared_ptr& address) const{ if(address){ auto itParticipant = std::find_if(mList.begin(), mList.end(), [address] (const QSharedPointer& participant){ return participant.objectCast()->getParticipant()->getAddress()->weakEqual(address); }); if( itParticipant == mList.end()) return nullptr; else return itParticipant->objectCast(); }else return nullptr; } const QSharedPointer ParticipantListModel::getParticipant(const std::shared_ptr& pParticipant) const{ if(pParticipant){ auto itParticipant = std::find_if(mList.begin(), mList.end(), [pParticipant] (const QSharedPointer& participant){ return participant.objectCast()->getParticipant() == pParticipant; }); if( itParticipant == mList.end()) return nullptr; else return itParticipant->objectCast(); }else return nullptr; } //------------------------------------------------------------- void ParticipantListModel::setAdminStatus(const std::shared_ptr participant, const bool& isAdmin){ if(mChatRoomModel) mChatRoomModel->getChatRoom()->setParticipantAdminStatus(participant, isAdmin); if(mConferenceModel) mConferenceModel->getConference()->setParticipantAdminStatus(participant, isAdmin); } void ParticipantListModel::onSecurityEvent(const std::shared_ptr & eventLog) { auto address = eventLog->getParticipantAddress(); if(address) { auto participant = getParticipant(address); if( participant){ emit participant->securityLevelChanged(); } }else{ address = eventLog->getDeviceAddress(); // Looping on all participant ensure to get all devices. Can be optimized if Device address is unique : Gain 2n operations. if(address) emit deviceSecurityLevelChanged(address); } } void ParticipantListModel::onConferenceJoined(){ updateParticipants(); } void ParticipantListModel::onParticipantAdded(const std::shared_ptr & eventLog){ qDebug() << "onParticipantAdded event: " << eventLog->getParticipantAddress()->asString().c_str(); add(eventLog->getParticipantAddress()); } void ParticipantListModel::onParticipantAdded(const std::shared_ptr & participant){ qDebug() << "onParticipantAdded part: " << participant->getAddress()->asString().c_str(); add(participant); } void ParticipantListModel::onParticipantAdded(const std::shared_ptr& address){ qDebug() << "onParticipantAdded addr: " << address->asString().c_str(); add(address); } void ParticipantListModel::onParticipantRemoved(const std::shared_ptr & eventLog){ onParticipantRemoved(eventLog->getParticipantAddress()); } void ParticipantListModel::onParticipantRemoved(const std::shared_ptr & participant){ auto p = getParticipant(participant); if(p) remove(p.get()); } void ParticipantListModel::onParticipantRemoved(const std::shared_ptr& address){ auto participant = getParticipant(address); if(participant) remove(participant.get()); } void ParticipantListModel::onParticipantAdminStatusChanged(const std::shared_ptr & eventLog){ onParticipantAdminStatusChanged(eventLog->getParticipantAddress()); } void ParticipantListModel::onParticipantAdminStatusChanged(const std::shared_ptr & participant){ auto p = getParticipant(participant); if( participant) emit p->adminStatusChanged();// Request to participant to update its status from its data } void ParticipantListModel::onParticipantAdminStatusChanged(const std::shared_ptr& address ){ auto participant = getParticipant(address); if( participant) emit participant->adminStatusChanged();// Request to participant to update its status from its data } void ParticipantListModel::onParticipantDeviceAdded(const std::shared_ptr & eventLog){ auto participant = getParticipant(eventLog->getParticipantAddress()); if( participant){ emit participant->deviceCountChanged(); } } void ParticipantListModel::onParticipantDeviceRemoved(const std::shared_ptr & eventLog){ auto participant = getParticipant(eventLog->getParticipantAddress()); if( participant){ emit participant->deviceCountChanged(); } } void ParticipantListModel::onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & participantAddress){ } void ParticipantListModel::onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & participantAddress){ } void ParticipantListModel::onStateChanged(){ if(mConferenceModel){ if(mConferenceModel->getConference()->getState() == linphone::Conference::State::Created){ updateParticipants(); } } } linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantListModel.hpp000066400000000000000000000111361434616504300315010ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_LIST_MODEL_H_ #define PARTICIPANT_LIST_MODEL_H_ #include #include "components/participant/ParticipantModel.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "app/proxyModel/ProxyListModel.hpp" class ConferenceModel; // ============================================================================= class ParticipantListModel : public ProxyListModel { Q_OBJECT public: ParticipantListModel (ChatRoomModel * chatRoomModel, QObject *parent = Q_NULLPTR); ParticipantListModel (ConferenceModel * conferenceModel, QObject *parent = Q_NULLPTR); virtual ~ParticipantListModel(); Q_PROPERTY(ChatRoomModel* chatRoomModel READ getChatRoomModel CONSTANT) Q_PROPERTY(QString addressesToString READ addressesToString NOTIFY participantsChanged) Q_PROPERTY(QString displayNamesToString READ displayNamesToString NOTIFY participantsChanged) Q_PROPERTY(QString usernamesToString READ usernamesToString NOTIFY participantsChanged) void reset(); void update(); void selectAll(const bool& selected); const QSharedPointer getParticipant(const std::shared_ptr& address) const; const QSharedPointer getParticipant(const std::shared_ptr& participant) const; void add (QSharedPointer participant); void add(const std::shared_ptr & participant); void add(const std::shared_ptr & participantAddress); void updateParticipants(); // Update list from Chat Room // Remove a chatroom Q_INVOKABLE void remove (ParticipantModel *importer); Q_INVOKABLE ChatRoomModel* getChatRoomModel() const; Q_INVOKABLE ConferenceModel* getConferenceModel() const; std::list> getParticipants()const; Q_INVOKABLE QString addressesToString()const; Q_INVOKABLE QString displayNamesToString()const; Q_INVOKABLE QString usernamesToString()const; bool contains(const QString& address) const; public slots: void setAdminStatus(const std::shared_ptr participant, const bool& isAdmin); void onSecurityEvent(const std::shared_ptr & eventLog); void onConferenceJoined(); void onParticipantAdded(const std::shared_ptr & participant); void onParticipantAdded(const std::shared_ptr & eventLog); void onParticipantAdded(const std::shared_ptr& address); void onParticipantRemoved(const std::shared_ptr & participant); void onParticipantRemoved(const std::shared_ptr & eventLog); void onParticipantRemoved(const std::shared_ptr& address); void onParticipantAdminStatusChanged(const std::shared_ptr & participant); void onParticipantAdminStatusChanged(const std::shared_ptr & eventLog); void onParticipantAdminStatusChanged(const std::shared_ptr& address ); void onParticipantDeviceAdded(const std::shared_ptr & eventLog); void onParticipantDeviceRemoved(const std::shared_ptr & eventLog); void onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & participantAddress); void onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & participantAddress); void onStateChanged(); signals: void securityLevelChanged(); void deviceSecurityLevelChanged(std::shared_ptr device); void participantsChanged(); private: ChatRoomModel* mChatRoomModel = nullptr; ConferenceModel *mConferenceModel = nullptr; }; Q_DECLARE_METATYPE(QSharedPointer); #endif // PARTICIPANT_LIST_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantModel.cpp000066400000000000000000000114661434616504300306460ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "ParticipantModel.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" // ============================================================================= using namespace std; ParticipantModel::ParticipantModel (shared_ptr linphoneParticipant, QObject *parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mParticipant = linphoneParticipant; mAdminStatus = false; if(mParticipant){ mAdminStatus = mParticipant->isAdmin(); mParticipantDevices = QSharedPointer::create(mParticipant); connect(this, &ParticipantModel::deviceSecurityLevelChanged, mParticipantDevices.get(), &ParticipantDeviceListModel::securityLevelChanged); } } // ----------------------------------------------------------------------------- ContactModel *ParticipantModel::getContactModel() const{ return CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(getSipAddress()).get(); } int ParticipantModel::getSecurityLevel() const{ return (mParticipant ? (int)mParticipant->getSecurityLevel() : 0); } int ParticipantModel::getDeviceCount(){ int count = (mParticipant ? mParticipant->getDevices().size() : 0); if(mParticipant && count != mParticipantDevices->getCount()){ mParticipantDevices->updateDevices(mParticipant); } return count; } bool ParticipantModel::getInviting() const{ return !mParticipant; } bool ParticipantModel::isMe() const{ return Utils::isMe(getSipAddress()); } QString ParticipantModel::getSipAddress() const{ return (mParticipant ? Utils::coreStringToAppString(mParticipant->getAddress()->asString()) : mSipAddress); } QDateTime ParticipantModel::getCreationTime() const{ return (mParticipant ? QDateTime::fromSecsSinceEpoch(mParticipant->getCreationTime()) : QDateTime::currentDateTime()); } bool ParticipantModel::getAdminStatus() const{ return (mParticipant ? mParticipant->isAdmin() : mAdminStatus); } bool ParticipantModel::isFocus() const{ return (mParticipant ? mParticipant->isFocus() : false); } //------------------------------------------------------------------------ void ParticipantModel::setSipAddress(const QString& address){ if(mSipAddress != address){ mSipAddress = address; emit sipAddressChanged(); } } void ParticipantModel::setAdminStatus(const bool& status){ if(status != mAdminStatus || mParticipant && status != mParticipant->isAdmin()){ mAdminStatus = status; if(mParticipant) emit updateAdminStatus(mParticipant, mAdminStatus); else emit adminStatusChanged(); } } void ParticipantModel::setParticipant(std::shared_ptr participant){ mParticipant = participant; if(mParticipant){ mAdminStatus = mParticipant->isAdmin(); mParticipantDevices = QSharedPointer::create(mParticipant); connect(this, &ParticipantModel::deviceSecurityLevelChanged, mParticipantDevices.get(), &ParticipantDeviceListModel::securityLevelChanged); } emit invitingChanged(); } //------------------------------------------------------------------------ void ParticipantModel::onSecurityLevelChanged(){ emit securityLevelChanged(); } void ParticipantModel::onDeviceSecurityLevelChanged(std::shared_ptr device){ emit deviceSecurityLevelChanged(device); } std::shared_ptr ParticipantModel::getParticipant(){ return mParticipant; } ParticipantDeviceProxyModel * ParticipantModel::getProxyDevices(){ ParticipantDeviceProxyModel * devices = new ParticipantDeviceProxyModel(); devices->setParticipant(this); return devices; } QSharedPointer ParticipantModel::getParticipantDevices(){ return mParticipantDevices; } void ParticipantModel::startInvitation(const int& secs){ QTimer::singleShot(secs * 1000, this, &ParticipantModel::onEndOfInvitation); } void ParticipantModel::onEndOfInvitation(){ if( getInviting()) emit invitationTimeout(this); } linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantModel.hpp000066400000000000000000000071531434616504300306510ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_MODEL_H_ #define PARTICIPANT_MODEL_H_ #include // ============================================================================= #include #include #include #include class ContactModel; class ParticipantDeviceProxyModel; class ParticipantDeviceListModel; class ParticipantModel : public QObject { Q_OBJECT public: ParticipantModel (std::shared_ptr linphoneParticipant, QObject *parent = nullptr); Q_PROPERTY(ContactModel *contactModel READ getContactModel CONSTANT) Q_PROPERTY(QString sipAddress READ getSipAddress WRITE setSipAddress NOTIFY sipAddressChanged) Q_PROPERTY(bool adminStatus READ getAdminStatus WRITE setAdminStatus NOTIFY adminStatusChanged) Q_PROPERTY(bool isMe READ isMe CONSTANT) Q_PROPERTY(QDateTime creationTime READ getCreationTime CONSTANT) Q_PROPERTY(bool focus READ isFocus CONSTANT) Q_PROPERTY(int securityLevel READ getSecurityLevel NOTIFY securityLevelChanged) Q_PROPERTY(int deviceCount READ getDeviceCount NOTIFY deviceCountChanged) Q_PROPERTY(bool inviting READ getInviting NOTIFY invitingChanged) ContactModel *getContactModel() const; QString getSipAddress() const; QDateTime getCreationTime() const; bool getAdminStatus() const; bool isFocus() const; int getSecurityLevel() const; int getDeviceCount(); bool getInviting() const; bool isMe() const; void setSipAddress(const QString& address); void setAdminStatus(const bool& status); void setParticipant(std::shared_ptr participant); std::shared_ptr getParticipant(); Q_INVOKABLE ParticipantDeviceProxyModel * getProxyDevices(); QSharedPointer getParticipantDevices(); void startInvitation(const int& secondes = 30); // Start a timer to remove the model if the invitation didn't ended after some time public slots: void onSecurityLevelChanged(); void onDeviceSecurityLevelChanged(std::shared_ptr device); void onEndOfInvitation(); signals: void securityLevelChanged(); void deviceSecurityLevelChanged(std::shared_ptr device); void sipAddressChanged(); void updateAdminStatus(const std::shared_ptr participant, const bool& isAdmin);// Split in two signals in order to sequancialize execution between SDK and GUI void adminStatusChanged(); void deviceCountChanged(); void invitingChanged(); void invitationTimeout(ParticipantModel* model); private: std::shared_ptr mParticipant; QSharedPointer mParticipantDevices; // Variables when Linphone Participant has not been created QString mSipAddress; bool mAdminStatus; }; Q_DECLARE_METATYPE(QSharedPointer); #endif // PARTICIPANT_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantProxyModel.cpp000066400000000000000000000213411434616504300317010ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "ParticipantProxyModel.hpp" #include "components/core/CoreManager.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "components/conference/ConferenceModel.hpp" #include "components/conferenceInfo/ConferenceInfoModel.hpp" #include "utils/Utils.hpp" #include "ParticipantListModel.hpp" #include "ParticipantModel.hpp" #include // ============================================================================= // ----------------------------------------------------------------------------- ParticipantProxyModel::ParticipantProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { setSourceModel(new ParticipantListModel((ConferenceModel*)nullptr, this)); connect(this, &ParticipantProxyModel::chatRoomModelChanged, this, &ParticipantProxyModel::countChanged); connect(this, &ParticipantProxyModel::conferenceModelChanged, this, &ParticipantProxyModel::countChanged); } // ----------------------------------------------------------------------------- ChatRoomModel *ParticipantProxyModel::getChatRoomModel() const{ return mChatRoomModel; } ConferenceModel *ParticipantProxyModel::getConferenceModel() const{ return mConferenceModel; } ParticipantListModel * ParticipantProxyModel::getParticipantListModel() const{ return qobject_cast(sourceModel()); } QStringList ParticipantProxyModel::getSipAddresses() const{ QStringList participants; ParticipantListModel * list = qobject_cast(sourceModel()); for(int i = 0 ; i < list->rowCount() ; ++i) participants << list->getAt(i)->getSipAddress(); return participants; } QVariantList ParticipantProxyModel::getParticipants() const{ QVariantList participants; ParticipantListModel * list = qobject_cast(sourceModel()); for(int i = 0 ; i < list->rowCount() ; ++i) participants << QVariant::fromValue(list->getAt(i).get()); return participants; } int ParticipantProxyModel::getCount() const{ auto model = getParticipantListModel(); return model ? model->rowCount() : 0; } bool ParticipantProxyModel::getShowMe() const{ return mShowMe; } // ----------------------------------------------------------------------------- void ParticipantProxyModel::setChatRoomModel(ChatRoomModel * chatRoomModel){ if(!mChatRoomModel || mChatRoomModel != chatRoomModel){ mChatRoomModel = chatRoomModel; if(mChatRoomModel) { auto participants = mChatRoomModel->getParticipantListModel(); connect(participants, &ParticipantListModel::countChanged, this, &ParticipantProxyModel::countChanged); setSourceModel(participants); emit participantListModelChanged(); for(int i = 0 ; i < participants->getCount() ; ++i) { auto participant = participants->getAt(i); connect(participant.get(), &ParticipantModel::invitationTimeout, this, &ParticipantProxyModel::removeModel); emit addressAdded(participant->getSipAddress()); } }else if(!sourceModel()){ auto model = new ParticipantListModel((ChatRoomModel*)nullptr, this); connect(model, &ParticipantListModel::countChanged, this, &ParticipantProxyModel::countChanged); setSourceModel(model); emit participantListModelChanged(); } sort(0); emit chatRoomModelChanged(); } } void ParticipantProxyModel::setConferenceModel(ConferenceModel * conferenceModel){ if(!mConferenceModel || mConferenceModel != conferenceModel){ mConferenceModel = conferenceModel; if(mConferenceModel) { auto participants = mConferenceModel->getParticipantListModel(); connect(participants, &ParticipantListModel::countChanged, this, &ParticipantProxyModel::countChanged); setSourceModel(participants); emit participantListModelChanged(); for(int i = 0 ; i < participants->getCount() ; ++i) { auto participant = participants->getAt(i); connect(participant.get(), &ParticipantModel::invitationTimeout, this, &ParticipantProxyModel::removeModel); emit addressAdded(participant->getSipAddress()); } }else if(!sourceModel()){ auto model = new ParticipantListModel((ConferenceModel*)nullptr, this); connect(model, &ParticipantListModel::countChanged, this, &ParticipantProxyModel::countChanged); setSourceModel(model); emit participantListModelChanged(); } sort(0); emit conferenceModelChanged(); } } void ParticipantProxyModel::setAddresses(ConferenceInfoModel * conferenceInfoModel){ if(conferenceInfoModel && conferenceInfoModel->getConferenceInfo()) for(auto address : conferenceInfoModel->getConferenceInfo()->getParticipants()) addAddress(QString::fromStdString(address->asString())); } void ParticipantProxyModel::setShowMe(const bool& show){ if(mShowMe != show){ mShowMe = show; emit showMeChanged(); invalidate(); } } void ParticipantProxyModel::addAddress(const QString& address){ ParticipantListModel * participantsModel = qobject_cast(sourceModel()); if(!participantsModel->contains(address)){ QSharedPointer participant = QSharedPointer::create(nullptr); connect(participant.get(), &ParticipantModel::invitationTimeout, this, &ParticipantProxyModel::removeModel); participant->setSipAddress(address); participantsModel->add(participant); if(mChatRoomModel && mChatRoomModel->getChatRoom()){// Invite and wait for its creation participant->startInvitation(); mChatRoomModel->getChatRoom()->addParticipant(Utils::interpretUrl(address)); } if( mConferenceModel && mConferenceModel->getConference()){ auto addressToInvite = Utils::interpretUrl(address); std::list> runningCallsToAdd; auto currentCalls = CoreManager::getInstance()->getCore()->getCalls(); auto haveCall = std::find_if(currentCalls.begin(), currentCalls.end(), [addressToInvite](const std::shared_ptr& call){ return call->getRemoteAddress()->weakEqual(addressToInvite); }); participant->startInvitation(); if( haveCall == currentCalls.end()) mConferenceModel->getConference()->addParticipant(addressToInvite); else{ runningCallsToAdd.push_back(*haveCall); mConferenceModel->getConference()->addParticipants(runningCallsToAdd); } /* std::list> addressesToInvite; addressesToInvite.push_back(addressToInvite); auto callParameters = CoreManager::getInstance()->getCore()->createCallParams(mConferenceModel->getConference()->getCall()); mConferenceModel->getConference()->inviteParticipants(addressesToInvite, callParameters);*/ } emit countChanged(); emit addressAdded(address); } } void ParticipantProxyModel::removeModel(ParticipantModel * participant){ if(participant) { QString sipAddress = participant->getSipAddress(); auto dbParticipant = participant->getParticipant(); if(mChatRoomModel && dbParticipant && mChatRoomModel->getChatRoom()) mChatRoomModel->getChatRoom()->removeParticipant(dbParticipant); // Remove already added if( mConferenceModel && dbParticipant && mConferenceModel->getConference()) mConferenceModel->getConference()->removeParticipant(dbParticipant ); ParticipantListModel * participantsModel = qobject_cast(sourceModel()); participantsModel->remove(participant); emit countChanged(); emit addressRemoved(sipAddress); } } // ----------------------------------------------------------------------------- bool ParticipantProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { if( mShowMe) return true; else{ const ParticipantModel* a = sourceModel()->data(sourceModel()->index(sourceRow, 0, sourceParent)).value(); return !a->isMe(); } } bool ParticipantProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const ParticipantModel* a = sourceModel()->data(left).value(); const ParticipantModel* b = sourceModel()->data(right).value(); return a->getCreationTime() > b->getCreationTime(); } linphone-desktop-5.0.2/linphone-app/src/components/participant/ParticipantProxyModel.hpp000066400000000000000000000056731434616504300317200ustar00rootroot00000000000000/* * Copyright (c) 2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PARTICIPANT_PROXY_MODEL_H_ #define PARTICIPANT_PROXY_MODEL_H_ #include #include class ParticipantModel; class ChatRoomModel; class ParticipantListModel; class ConferenceModel; class ConferenceInfoModel; // ============================================================================= class QWindow; class ParticipantProxyModel : public QSortFilterProxyModel { Q_OBJECT public: ParticipantProxyModel ( QObject *parent = Q_NULLPTR); Q_PROPERTY(ChatRoomModel* chatRoomModel READ getChatRoomModel WRITE setChatRoomModel NOTIFY chatRoomModelChanged) Q_PROPERTY(ConferenceModel* conferenceModel READ getConferenceModel WRITE setConferenceModel NOTIFY conferenceModelChanged) Q_PROPERTY(ParticipantListModel * participantListModel READ getParticipantListModel NOTIFY participantListModelChanged) Q_PROPERTY(int count READ getCount NOTIFY countChanged) Q_PROPERTY(bool showMe READ getShowMe WRITE setShowMe NOTIFY showMeChanged) bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; ChatRoomModel *getChatRoomModel() const; ConferenceModel *getConferenceModel() const; ParticipantListModel * getParticipantListModel() const; Q_INVOKABLE QStringList getSipAddresses() const; Q_INVOKABLE QVariantList getParticipants() const; Q_INVOKABLE int getCount() const; bool getShowMe() const; void setChatRoomModel(ChatRoomModel * chatRoomModel); void setConferenceModel(ConferenceModel * conferenceModel); void setShowMe(const bool& show); Q_INVOKABLE void addAddress(const QString& address); Q_INVOKABLE void removeModel(ParticipantModel * participant); Q_INVOKABLE void setAddresses(ConferenceInfoModel * conferenceInfoModel); signals: void chatRoomModelChanged(); void conferenceModelChanged(); void participantListModelChanged(); void countChanged(); void showMeChanged(); void addressAdded(QString sipAddress); void addressRemoved(QString sipAddress); private: ChatRoomModel *mChatRoomModel = nullptr; ConferenceModel *mConferenceModel = nullptr; bool mShowMe = true; }; #endif // PARTICIPANT_PROXY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/presence/000077500000000000000000000000001434616504300241615ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/presence/OwnPresenceModel.cpp000066400000000000000000000060341434616504300301010ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "components/core/CoreManager.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "OwnPresenceModel.hpp" // ============================================================================= using namespace std; OwnPresenceModel::OwnPresenceModel (QObject *parent) : QObject(parent) { AccountSettingsModel *accountSettingsModel = CoreManager::getInstance()->getAccountSettingsModel(); // Update status when changing the publish presence from settings QObject::connect(accountSettingsModel, &AccountSettingsModel::publishPresenceChanged, this, [this]() { Presence::PresenceStatus status = static_cast(CoreManager::getInstance()->getCore()->getConsolidatedPresence()); emit presenceStatusChanged(status); emit presenceLevelChanged(Presence::getPresenceLevel(status)); }); } Presence::PresenceLevel OwnPresenceModel::getPresenceLevel () const { return Presence::getPresenceLevel(getPresenceStatus()); } Presence::PresenceStatus OwnPresenceModel::getPresenceStatus () const { return static_cast(CoreManager::getInstance()->getCore()->getConsolidatedPresence()); } void OwnPresenceModel::setPresenceStatus (Presence::PresenceStatus status) { shared_ptr core = CoreManager::getInstance()->getCore(); core->setConsolidatedPresence(static_cast(status)); emit presenceStatusChanged(status); emit presenceLevelChanged(Presence::getPresenceLevel(status)); } // ----------------------------------------------------------------------------- static inline void addBuildStatus (QVariantList &list, Presence::PresenceStatus status) { Presence::PresenceLevel level = Presence::getPresenceLevel(status); QVariantMap map; map["presenceLevel"] = level; map["presenceStatus"] = status; map["presenceIcon"] = Presence::getPresenceLevelIconName(level); map["presenceLabel"] = Presence::getPresenceStatusAsString(status); list << map; } QVariantList OwnPresenceModel::getStatuses () const { QVariantList statuses; addBuildStatus(statuses, Presence::Online); addBuildStatus(statuses, Presence::Busy); addBuildStatus(statuses, Presence::DoNotDisturb); addBuildStatus(statuses, Presence::Offline); return statuses; } linphone-desktop-5.0.2/linphone-app/src/components/presence/OwnPresenceModel.hpp000066400000000000000000000036661434616504300301160ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef OWN_PRESENCE_MODEL_H_ #define OWN_PRESENCE_MODEL_H_ #include "Presence.hpp" // ============================================================================= // Gives the statuses list informations (icons, label, level, status). // Can set/get the presence status of the linphone user app. // ============================================================================= class OwnPresenceModel : public QObject { Q_OBJECT; Q_PROPERTY(QVariantList statuses READ getStatuses CONSTANT); Q_PROPERTY(Presence::PresenceLevel presenceLevel READ getPresenceLevel NOTIFY presenceLevelChanged); Q_PROPERTY(Presence::PresenceStatus presenceStatus READ getPresenceStatus WRITE setPresenceStatus NOTIFY presenceStatusChanged); public: OwnPresenceModel (QObject *parent = Q_NULLPTR); signals: void presenceLevelChanged (Presence::PresenceLevel level); void presenceStatusChanged (Presence::PresenceStatus status); private: Presence::PresenceLevel getPresenceLevel () const; Presence::PresenceStatus getPresenceStatus () const; void setPresenceStatus (Presence::PresenceStatus status); QVariantList getStatuses () const; }; #endif // OWN_PRESENCE_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/presence/Presence.cpp000066400000000000000000000054301434616504300264330ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "Presence.hpp" // ============================================================================= Presence::PresenceLevel Presence::getPresenceLevel (const PresenceStatus &status) { return getPresenceLevel(static_cast(status)); } Presence::PresenceLevel Presence::getPresenceLevel (const linphone::ConsolidatedPresence &status) { switch (status) { case linphone::ConsolidatedPresence::Online: return Green; case linphone::ConsolidatedPresence::Busy: return Orange; case linphone::ConsolidatedPresence::DoNotDisturb: return Red; default: break; } return White; } QString Presence::getPresenceStatusAsString (const PresenceStatus &status) { return getPresenceStatusAsString(static_cast(status)); } QString Presence::getPresenceStatusAsString (const linphone::ConsolidatedPresence &status) { switch (status) { case linphone::ConsolidatedPresence::Online: return tr("presenceOnline"); case linphone::ConsolidatedPresence::Busy: return tr("presenceBusy"); case linphone::ConsolidatedPresence::DoNotDisturb: return tr("presenceDoNotDisturb"); default: break; } return tr("presenceOffline"); } QString Presence::getBetterPresenceLevelIconName (const PresenceLevel &level) { switch (level) { case Green: return QStringLiteral("current_account_status_online"); case Orange: return QStringLiteral("current_account_status_busy"); case Red: return QStringLiteral("current_account_status_dnd"); case White: return QStringLiteral("current_account_status_offline"); } return QString(""); } QString Presence::getPresenceLevelIconName (const PresenceLevel &level) { switch (level) { case Green: return QStringLiteral("led_green"); case Orange: return QStringLiteral("led_orange"); case Red: return QStringLiteral("led_red"); case White: return QStringLiteral("led_white"); } return QString(""); } linphone-desktop-5.0.2/linphone-app/src/components/presence/Presence.hpp000066400000000000000000000043751434616504300264470ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef PRESENCE_H_ #define PRESENCE_H_ #include #include // ============================================================================= // A helper to get the presence level of a presence status and to get a // presence status as string. // ============================================================================= class Presence : public QObject { Q_OBJECT; public: enum PresenceStatus { Online = int(linphone::ConsolidatedPresence::Online), Busy = int(linphone::ConsolidatedPresence::Busy), DoNotDisturb = int(linphone::ConsolidatedPresence::DoNotDisturb), Offline = int(linphone::ConsolidatedPresence::Offline) }; Q_ENUM(PresenceStatus); enum PresenceLevel { Green, Orange, Red, White }; Q_ENUM(PresenceLevel); Presence (QObject *parent = Q_NULLPTR) : QObject(parent) {} Q_INVOKABLE static PresenceLevel getPresenceLevel (const PresenceStatus &status); static Presence::PresenceLevel getPresenceLevel (const linphone::ConsolidatedPresence &status); Q_INVOKABLE static QString getPresenceStatusAsString (const PresenceStatus &status); static QString getPresenceStatusAsString (const linphone::ConsolidatedPresence &status); Q_INVOKABLE static QString getBetterPresenceLevelIconName (const PresenceLevel &level);// Get a better set of icons Q_INVOKABLE static QString getPresenceLevelIconName (const PresenceLevel &level);// Get a "background" icons (not very visible) }; #endif // PRESENCE_H_ linphone-desktop-5.0.2/linphone-app/src/components/recorder/000077500000000000000000000000001434616504300241625ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/recorder/RecorderManager.cpp000066400000000000000000000043531434616504300277330ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "app/App.hpp" #include "components/core/CoreManager.hpp" #include "RecorderManager.hpp" #include "RecorderModel.hpp" // ============================================================================= RecorderManager::RecorderManager (QObject * parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE } RecorderManager::~RecorderManager(){ } bool RecorderManager::haveVocalRecorder() const{ return mVocalRecorder != nullptr; } RecorderModel* RecorderManager::getVocalRecorder(){ if( !mVocalRecorder) { auto core = CoreManager::getInstance()->getCore(); std::shared_ptr params = core->createRecorderParams(); params->setFileFormat(linphone::RecorderFileFormat::Mkv); params->setVideoCodec(""); auto recorder = core->createRecorder(params); if(recorder) mVocalRecorder = RecorderModel::create(recorder, nullptr); emit haveVocalRecorderChanged(); } return mVocalRecorder.get(); } RecorderModel* RecorderManager::resetVocalRecorder(){ if(mVocalRecorder) clearVocalRecorder(); return getVocalRecorder(); } void RecorderManager::clearVocalRecorder(){ if( mVocalRecorder){ mVocalRecorder = nullptr; emit haveVocalRecorderChanged(); } } //-------------------------------------------------------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/recorder/RecorderManager.hpp000066400000000000000000000027701434616504300277410ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef RECORDER_MANAGER_MODEL_H #define RECORDER_MANAGER_MODEL_H #include #include // ============================================================================= class RecorderModel; class RecorderManager : public QObject { Q_OBJECT public: RecorderManager (QObject * parent = nullptr); virtual ~RecorderManager(); Q_PROPERTY(bool haveVocalRecorder READ haveVocalRecorder NOTIFY haveVocalRecorderChanged) bool haveVocalRecorder() const; Q_INVOKABLE RecorderModel* getVocalRecorder(); Q_INVOKABLE RecorderModel* resetVocalRecorder(); Q_INVOKABLE void clearVocalRecorder(); signals: void haveVocalRecorderChanged(); private: std::shared_ptr mVocalRecorder; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/recorder/RecorderModel.cpp000066400000000000000000000064631434616504300274250ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "components/core/CoreManager.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/Utils.hpp" #include "RecorderModel.hpp" // ============================================================================= RecorderModel::RecorderModel ( std::shared_ptr recorder, QObject * parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mRecorder= recorder; } RecorderModel::~RecorderModel(){ } std::shared_ptr RecorderModel::create(std::shared_ptr recorder, QObject * parent){ return std::make_shared(recorder, parent); } std::shared_ptr RecorderModel::getRecorder(){ return mRecorder; } int RecorderModel::getDuration()const{ return mRecorder->getDuration(); } float RecorderModel::getCaptureVolume()const{ return mRecorder->getCaptureVolume(); } LinphoneEnums::RecorderState RecorderModel::getState() const{ return LinphoneEnums::fromLinphone(mRecorder->getState()); } QString RecorderModel::getFile()const{ return Utils::coreStringToAppString(mRecorder->getFile()); } void RecorderModel::start(){ bool soFarSoGood; QString filename = QStringLiteral("vocal_%1.mkv") .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss-zzz")); const QString safeFilePath = Utils::getSafeFilePath( QStringLiteral("%1%2") .arg(Utils::coreStringToAppString(Paths::getCapturesDirPath())) .arg(filename), &soFarSoGood ); if (!soFarSoGood) { qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(filename); }else if(mRecorder->open(Utils::appStringToCoreString(safeFilePath)) < 0) qWarning() << QStringLiteral("Unable to open safe file path for: %1.").arg(filename); else if( mRecorder->start() < 0) qWarning() << QStringLiteral("Unable to start recording to : %1.").arg(filename); emit stateChanged(); emit fileChanged(); } void RecorderModel::pause(){ mRecorder->pause(); emit stateChanged(); } void RecorderModel::stop(){ if(mRecorder->getState() == linphone::RecorderState::Running) // Remove these tests when the SDK do them. mRecorder->pause(); if(mRecorder->getState() == linphone::RecorderState::Paused) mRecorder->close(); emit stateChanged(); } //-------------------------------------------------------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/src/components/recorder/RecorderModel.hpp000066400000000000000000000036241434616504300274260ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef RECORDER_MODEL_H #define RECORDER_MODEL_H #include "utils/LinphoneEnums.hpp" // ============================================================================= class RecorderModel : public QObject { Q_OBJECT public: static std::shared_ptr create(std::shared_ptr recorder,QObject * parent = nullptr);// Call it instead constructor RecorderModel (std::shared_ptr recorder,QObject * parent = nullptr); virtual ~RecorderModel(); Q_PROPERTY(LinphoneEnums::RecorderState state READ getState NOTIFY stateChanged) Q_PROPERTY(QString file READ getFile NOTIFY fileChanged) std::shared_ptr getRecorder(); Q_INVOKABLE int getDuration()const; Q_INVOKABLE float getCaptureVolume()const; LinphoneEnums::RecorderState getState() const; Q_INVOKABLE QString getFile()const; Q_INVOKABLE void start(); Q_INVOKABLE void pause(); Q_INVOKABLE void stop(); signals: void stateChanged(); void fileChanged(); private: std::shared_ptr mRecorder; }; Q_DECLARE_METATYPE(std::shared_ptr) Q_DECLARE_METATYPE(RecorderModel*) #endif linphone-desktop-5.0.2/linphone-app/src/components/search/000077500000000000000000000000001434616504300236225ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/search/SearchListener.cpp000066400000000000000000000022271434616504300272440ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "SearchListener.hpp" #include "linphone/api/c-search-result.h" // ============================================================================= SearchListener::SearchListener(QObject * parent) : QObject(parent){ } void SearchListener::onSearchResultsReceived(const std::shared_ptr & magicSearch){ emit searchReceived(magicSearch->getLastSearch()); } linphone-desktop-5.0.2/linphone-app/src/components/search/SearchListener.hpp000066400000000000000000000025611434616504300272520ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SEARCH_LISTENER_H_ #define SEARCH_LISTENER_H_ #include #include #include // ============================================================================= class SearchListener : public QObject, public linphone::MagicSearchListener{ Q_OBJECT public: SearchListener(QObject * parent = nullptr); virtual void onSearchResultsReceived(const std::shared_ptr & magicSearch); signals: void searchReceived(std::list> ); }; Q_DECLARE_METATYPE(std::shared_ptr); #endif linphone-desktop-5.0.2/linphone-app/src/components/search/SearchResultModel.cpp000066400000000000000000000036621434616504300277220ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "SearchResultModel.hpp" #include "components/core/CoreManager.hpp" #include "components/contact/ContactModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "utils/Utils.hpp" // ============================================================================= SearchResultModel::SearchResultModel(std::shared_ptr linphoneFriend, std::shared_ptr address, QObject * parent) : QObject(parent){ mFriend = linphoneFriend; if( address) mAddress = address->clone(); else if(linphoneFriend && linphoneFriend->getAddress()) mAddress = linphoneFriend->getAddress()->clone(); } QString SearchResultModel::getAddressString() const{ return Utils::coreStringToAppString(mAddress->asString()); } QString SearchResultModel::getAddressStringUriOnly() const{ return Utils::coreStringToAppString(mAddress->asStringUriOnly()); } std::shared_ptr SearchResultModel::getAddress() const{ return mAddress; } ContactModel * SearchResultModel::getContactModel() const{ return CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(getAddressStringUriOnly()).get(); } linphone-desktop-5.0.2/linphone-app/src/components/search/SearchResultModel.hpp000066400000000000000000000033151434616504300277220ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SEARCH_RESULT_MODEL_H_ #define SEARCH_RESULT_MODEL_H_ #include #include #include // ============================================================================= class ContactModel; class SearchResultModel : public QObject{ Q_OBJECT public: SearchResultModel(std::shared_ptr linphoneFriend, std::shared_ptr address, QObject * parent = nullptr); Q_PROPERTY(ContactModel * contactModel READ getContactModel CONSTANT) Q_PROPERTY(QString sipAddress READ getAddressString CONSTANT) Q_INVOKABLE QString getAddressString() const; Q_INVOKABLE QString getAddressStringUriOnly() const; std::shared_ptr getAddress()const; ContactModel * getContactModel() const; std::shared_ptr mAddress; std::shared_ptr mFriend; }; Q_DECLARE_METATYPE(std::shared_ptr) #endif linphone-desktop-5.0.2/linphone-app/src/components/search/SearchSipAddressesModel.cpp000066400000000000000000000057661434616504300310440ustar00rootroot00000000000000/* * Copyright (c) 2010-2022 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "SearchSipAddressesModel.hpp" #include #include #include #include #include "components/call/CallModel.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/history/HistoryModel.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "utils/Utils.hpp" #include "SearchResultModel.hpp" // ============================================================================= using namespace std; // ----------------------------------------------------------------------------- SearchSipAddressesModel::SearchSipAddressesModel (QObject *parent) : ProxyListModel(parent) { mMagicSearch = CoreManager::getInstance()->getCore()->createMagicSearch(); mSearch = std::make_shared(); QObject::connect(mSearch.get(), &SearchListener::searchReceived, this, &SearchSipAddressesModel::searchReceived, Qt::QueuedConnection); mMagicSearch->addListener(mSearch); } SearchSipAddressesModel::~SearchSipAddressesModel(){ mMagicSearch->removeListener(mSearch); } // ----------------------------------------------------------------------------- void SearchSipAddressesModel::setFilter(const QString& filter){ mMagicSearch->getContactsListAsync(filter.toStdString(),"", (int)linphone::MagicSearchSource::All, linphone::MagicSearchAggregation::None); //searchReceived(mMagicSearch->getContactListFromFilter(Utils::appStringToCoreString(filter),"")); // Just to show how to use sync method } void SearchSipAddressesModel::searchReceived(std::list> results){ QList > addresses; for(auto it = results.begin() ; it != results.end() ; ++it){ auto linphoneFriend = (*it)->getFriend(); auto address = (*it)->getAddress(); if( linphoneFriend || address) addresses << QSharedPointer::create(linphoneFriend,address ); } beginResetModel(); mList.clear(); mList = addresses; if(mList.size() > 0 )// remove self mList.pop_back(); endResetModel(); } linphone-desktop-5.0.2/linphone-app/src/components/search/SearchSipAddressesModel.hpp000066400000000000000000000032271434616504300310370ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SEARCH_SIP_ADDRESSES_MODEL_H_ #define SEARCH_SIP_ADDRESSES_MODEL_H_ #include #include #include #include "SearchListener.hpp" #include "app/proxyModel/ProxyListModel.hpp" // ============================================================================= class SearchResultModel; class SearchSipAddressesModel : public ProxyListModel { Q_OBJECT public: SearchSipAddressesModel (QObject *parent = Q_NULLPTR); ~SearchSipAddressesModel(); Q_INVOKABLE void setFilter (const QString &pattern); // And instance of Magic search std::shared_ptr mMagicSearch; // Callback when searching std::shared_ptr mSearch; public slots: void searchReceived(std::list> results); }; Q_DECLARE_METATYPE(SearchSipAddressesModel *); #endif // SIP_ADDRESSES_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/search/SearchSipAddressesProxyModel.cpp000066400000000000000000000075671434616504300321070ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "SearchSipAddressesProxyModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/core/CoreManager.hpp" #include "components/participant/ParticipantListModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "components/sip-addresses/SipAddressesSorter.hpp" #include "SearchSipAddressesModel.hpp" #include "SearchResultModel.hpp" #include "utils/Utils.hpp" #include // ----------------------------------------------------------------------------- SearchSipAddressesProxyModel::SearchSipAddressesProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { mParticipantListModel = nullptr; setSourceModel(new SearchSipAddressesModel(this)); sort(0); } // ----------------------------------------------------------------------------- SearchSipAddressesModel * SearchSipAddressesProxyModel::getModel(){ return qobject_cast(sourceModel()); } ParticipantListModel * SearchSipAddressesProxyModel::getParticipantListModel() const{ return mParticipantListModel; } void SearchSipAddressesProxyModel::setFilter (const QString &pattern){ mFilter = pattern; getModel()->setFilter(pattern); } void SearchSipAddressesProxyModel::setParticipantListModel(ParticipantListModel * model){ if(mParticipantListModel != model){ mParticipantListModel = model; emit participantListModelChanged(); } } void SearchSipAddressesProxyModel::addAddressToIgnore(const QString& address){ std::shared_ptr a = Utils::interpretUrl(address); if(a) { mResultsToIgnore[Utils::coreStringToAppString(a->asStringUriOnly())] = true; invalidate(); } } void SearchSipAddressesProxyModel::removeAddressToIgnore(const QString& address){ std::shared_ptr a = Utils::interpretUrl(address); if( a){ mResultsToIgnore.remove(Utils::coreStringToAppString(a->asStringUriOnly())); invalidate(); } } bool SearchSipAddressesProxyModel::isIgnored(const QString& address) const{ if(address != ""){ std::shared_ptr a = Utils::interpretUrl(address); return a ? mResultsToIgnore.contains(Utils::coreStringToAppString(a->asStringUriOnly())) : false; } return false; } bool SearchSipAddressesProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); const SearchResultModel * model = sourceModel()->data(index).value(); if(!model) return false; else if(mParticipantListModel){ return !mParticipantListModel->contains(Utils::coreStringToAppString(model->getAddress()->asStringUriOnly())); }else return !mResultsToIgnore.contains(Utils::coreStringToAppString(model->getAddress()->asStringUriOnly())); } bool SearchSipAddressesProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const SearchResultModel * modelA = sourceModel()->data(left).value(); const SearchResultModel * modelB = sourceModel()->data(right).value(); return SipAddressesSorter::lessThan(mFilter, modelA, modelB); } linphone-desktop-5.0.2/linphone-app/src/components/search/SearchSipAddressesProxyModel.hpp000066400000000000000000000043631434616504300321030ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SEARCH_SIP_ADDRESSES_PROXY_MODEL_H_ #define SEARCH_SIP_ADDRESSES_PROXY_MODEL_H_ #include class ParticipantListModel; class SearchSipAddressesModel; // ============================================================================= class SearchSipAddressesProxyModel : public QSortFilterProxyModel { Q_OBJECT public: SearchSipAddressesProxyModel (QObject *parent = Q_NULLPTR); Q_PROPERTY(SearchSipAddressesModel * model READ getModel CONSTANT) Q_PROPERTY(ParticipantListModel *participantListModel READ getParticipantListModel WRITE setParticipantListModel NOTIFY participantListModelChanged) Q_INVOKABLE void addAddressToIgnore(const QString& address); Q_INVOKABLE void removeAddressToIgnore(const QString& address); Q_INVOKABLE bool isIgnored(const QString& address) const; SearchSipAddressesModel * getModel(); ParticipantListModel * getParticipantListModel() const; Q_INVOKABLE void setFilter (const QString &pattern); void setResultExceptions(QAbstractListModel* exceptionList); void setParticipantListModel( ParticipantListModel *model); signals: void participantListModelChanged(); void resultExceptionsChanged(); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; private: QMap mResultsToIgnore; QString mFilter; ParticipantListModel *mParticipantListModel = nullptr; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/settings/000077500000000000000000000000001434616504300242155ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/settings/AccountSettingsModel.cpp000066400000000000000000000574141434616504300310320ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "config.h" #include "app/paths/Paths.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "AccountSettingsModel.hpp" #include "SettingsModel.hpp" // ============================================================================= using namespace std; static inline AccountSettingsModel::RegistrationState mapLinphoneRegistrationStateToUi (linphone::RegistrationState state) { switch (state) { case linphone::RegistrationState::None: case linphone::RegistrationState::Cleared: case linphone::RegistrationState::Failed: return AccountSettingsModel::RegistrationStateNotRegistered; case linphone::RegistrationState::Progress: return AccountSettingsModel::RegistrationStateInProgress; case linphone::RegistrationState::Ok: break; } return AccountSettingsModel::RegistrationStateRegistered; } // ----------------------------------------------------------------------------- AccountSettingsModel::AccountSettingsModel (QObject *parent) : QObject(parent) { CoreManager *coreManager = CoreManager::getInstance(); QObject::connect( coreManager->getHandlers().get(), &CoreHandlers::registrationStateChanged, this, &AccountSettingsModel::handleRegistrationStateChanged ); //QObject::connect(coreManager, &CoreManager::eventCountChanged, this, [this]() { emit accountSettingsUpdated(); }); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::usernameChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::sipAddressChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::fullSipAddressChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::registrationStateChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::conferenceUriChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::videoConferenceUriChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::limeServerUrlChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::primaryDisplayNameChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::primaryUsernameChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::primarySipAddressChanged); QObject::connect(this, &AccountSettingsModel::accountSettingsUpdated, this, &AccountSettingsModel::accountsChanged); } // ----------------------------------------------------------------------------- shared_ptr AccountSettingsModel::getUsedSipAddress () const { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); return account ? account->getParams()->getIdentityAddress() : core->createPrimaryContactParsed(); } void AccountSettingsModel::setUsedSipAddress (const shared_ptr &address) { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); if( account){ auto params = account->getParams()->clone(); if(!params->setIdentityAddress(address)) { account->setParams(params); emit sipAddressChanged(); } return; } core->setPrimaryContact(address->asString()); emit sipAddressChanged(); } QString AccountSettingsModel::getUsedSipAddressAsStringUriOnly () const { return Utils::coreStringToAppString(getUsedSipAddress()->asStringUriOnly()); } QString AccountSettingsModel::getUsedSipAddressAsString () const { return Utils::coreStringToAppString(getUsedSipAddress()->asString()); } // ----------------------------------------------------------------------------- bool AccountSettingsModel::addOrUpdateAccount (std::shared_ptr account, const std::shared_ptr& accountParams) { CoreManager *coreManager = CoreManager::getInstance(); shared_ptr core = coreManager->getCore(); list> accounts = coreManager->getAccountList(); if(!account) account = core->createAccount(accountParams); if (account->setParams(accountParams) == -1) { qWarning() << QStringLiteral("Unable to update account: `%1`.") .arg(QString::fromStdString(account->getParams()->getIdentityAddress()->asString())); return false; } if (find(accounts.cbegin(), accounts.cend(), account) == accounts.cend()) { if (core->addAccount(account) == -1) { qWarning() << QStringLiteral("Unable to add account: `%1`.") .arg(QString::fromStdString(account->getParams()->getIdentityAddress()->asString())); return false; } coreManager->addingAccount(account->getParams()); coreManager->getSettingsModel()->configureRlsUri(account); }else coreManager->getSettingsModel()->configureRlsUri(); emit accountSettingsUpdated(); return true; } QVariantMap AccountSettingsModel::getAccountDescription (const shared_ptr &account) { QVariantMap map; auto accountParams = account->getParams(); { const shared_ptr address = accountParams->getIdentityAddress(); map["sipAddress"] = address ? Utils::coreStringToAppString(accountParams->getIdentityAddress()->asString()) : QString(""); } map["serverAddress"] = Utils::coreStringToAppString(accountParams->getServerAddress()->asString()); map["registrationDuration"] = accountParams->getPublishExpires(); if( map["serverAddress"].toString().toUpper().contains("TRANSPORT="))// transport has been specified : let the RFC select the transport map["transport"] = LinphoneEnums::toString(LinphoneEnums::fromLinphone(accountParams->getTransport())); else// Set to TLS as default map["transport"] = "TLS"; auto routes = accountParams->getRoutesAddresses(); if( routes.size() > 0) map["route"] = Utils::coreStringToAppString(routes.front()->asString()); else map["route"] = ""; map["conferenceUri"] = Utils::coreStringToAppString(accountParams->getConferenceFactoryUri()); auto address = accountParams->getAudioVideoConferenceFactoryAddress(); map["videoConferenceUri"] = address ? Utils::coreStringToAppString(address->asString()) : ""; map["limeServerUrl"] = Utils::coreStringToAppString(accountParams->getLimeServerUrl()); map["videoConferenceUri"] = address ? Utils::coreStringToAppString(address->asString()) : ""; map["contactParams"] = Utils::coreStringToAppString(accountParams->getContactParameters()); map["avpfInterval"] = accountParams->getAvpfRrInterval(); map["registerEnabled"] = accountParams->registerEnabled(); map["publishPresence"] = accountParams->publishEnabled(); map["avpfEnabled"] = accountParams->getAvpfMode() == linphone::AVPFMode::Enabled; map["registrationState"] = mapLinphoneRegistrationStateToUi(account->getState()); shared_ptr natPolicy = accountParams->getNatPolicy(); bool createdNat = !natPolicy; if (createdNat) natPolicy = CoreManager::getInstance()->getCore()->createNatPolicy(); map["iceEnabled"] = natPolicy->iceEnabled(); map["turnEnabled"] = natPolicy->turnEnabled(); const string &turnUser(natPolicy->getStunServerUsername()); const string &stunServer(natPolicy->getStunServer()); map["turnUser"] = Utils::coreStringToAppString(turnUser); map["stunServer"] = Utils::coreStringToAppString(stunServer); if (createdNat){ auto accountParamsUpdated = accountParams->clone(); accountParamsUpdated->setNatPolicy(natPolicy); account->setParams(accountParamsUpdated); } shared_ptr authInfo = CoreManager::getInstance()->getCore()->findAuthInfo( "", turnUser, stunServer ); map["turnPassword"] = authInfo ? Utils::coreStringToAppString(authInfo->getPassword()) : QString(""); return map; } QString AccountSettingsModel::getConferenceUri() const{ shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); return account ? Utils::coreStringToAppString(account->getParams()->getConferenceFactoryUri()) : ""; } QString AccountSettingsModel::getVideoConferenceUri() const{ shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); if(account) { auto address = account->getParams()->getAudioVideoConferenceFactoryAddress(); return address ? Utils::coreStringToAppString(address->asString()) : ""; }else return ""; } QString AccountSettingsModel::getLimeServerUrl() const{ shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr account = core->getDefaultAccount(); return account ? Utils::coreStringToAppString(account->getParams()->getLimeServerUrl()) : ""; } void AccountSettingsModel::setDefaultAccount (const shared_ptr &account) { shared_ptr core = CoreManager::getInstance()->getCore(); if (mSelectedAccount != account) { core->setDefaultAccount(account); mSelectedAccount = account; emit accountSettingsUpdated(); emit defaultAccountChanged(); } } void AccountSettingsModel::setDefaultAccountFromSipAddress (const QString &sipAddress) { shared_ptr core = CoreManager::getInstance()->getCore(); auto address = Utils::interpretUrl(sipAddress); if ( core->createPrimaryContactParsed()->weakEqual(address)) { setDefaultAccount(nullptr); return; } for (const auto &account : CoreManager::getInstance()->getAccountList()) if (account->getParams()->getIdentityAddress()->weakEqual(address)) { setDefaultAccount(account); return; } qWarning() << "Unable to set default account from:" << sipAddress; } void AccountSettingsModel::removeAccount (const shared_ptr &account) { CoreManager *coreManager = CoreManager::getInstance(); std::shared_ptr newAccount = nullptr; std::list> allAccounts = coreManager->getAccountList(); if( account == coreManager->getCore()->getDefaultAccount()){ for(auto nextAccount : allAccounts){ if( nextAccount != account){ newAccount = nextAccount; break; } } setDefaultAccount(newAccount); } // "message-expires" is used to keep contact for messages. Setting to 0 will remove the contact for messages too. // Check if a "message-expires" exists and set it to 0 QStringList parameters = Utils::coreStringToAppString(account->getParams()->getContactParameters()).split(";"); for(int i = 0 ; i < parameters.size() ; ++i){ QStringList fields = parameters[i].split("="); if( fields.size() > 1 && fields[0].simplified() == "message-expires"){ parameters[i] = Constants::DefaultContactParametersOnRemove; } } auto accountParams = account->getParams()->clone(); accountParams->setContactParameters(Utils::appStringToCoreString(parameters.join(";"))); if (account->setParams(accountParams) == -1) { qWarning() << QStringLiteral("Unable to reset message-expiry property before removing account: `%1`.") .arg(QString::fromStdString(account->getParams()->getIdentityAddress()->asString())); }else if(account->getParams()->registerEnabled()) { // Wait for update mRemovingAccounts.push_back(account); }else{// Registration is not enabled : Removing without wait. CoreManager::getInstance()->getCore()->removeAccount(account); } emit accountSettingsUpdated(); } bool AccountSettingsModel::addOrUpdateAccount( const shared_ptr &account, const QVariantMap &data ) { bool newPublishPresence = false; auto accountParams = account->getParams()->clone(); QString literal = data["sipAddress"].toString(); // Sip address. { shared_ptr address = Utils::interpretUrl(literal); if (!address) { qWarning() << QStringLiteral("Unable to create sip address object from: `%1`.").arg(literal); return false; } if (accountParams->setIdentityAddress(address)) { qWarning() << QStringLiteral("Unable to set identity address: `%1`.") .arg(Utils::coreStringToAppString(address->asStringUriOnly())); return false; } } // Server address. { auto serverAddress = Utils::interpretUrl(data["serverAddress"].toString()); if (accountParams->setServerAddress(serverAddress)) { qWarning() << QStringLiteral("Unable to add server address: `%1`.").arg(serverAddress->asString().c_str()); return false; } } if(data.contains("registrationDuration")) accountParams->setPublishExpires(data["registrationDuration"].toInt()); if(data.contains("route")) { std::list> routes; routes.push_back(Utils::interpretUrl(data["route"].toString())); accountParams->setRoutesAddresses(routes); } QString txt = data["conferenceUri"].toString();// Var is used for debug accountParams->setConferenceFactoryUri(Utils::appStringToCoreString(txt)); txt = data["videoConferenceUri"].toString(); accountParams->setAudioVideoConferenceFactoryAddress(Utils::interpretUrl(txt)); accountParams->setLimeServerUrl(Utils::appStringToCoreString(data["limeServerUrl"].toString())); if(data.contains("contactParams")) accountParams->setContactParameters(Utils::appStringToCoreString(data["contactParams"].toString())); if(data.contains("avpfInterval")) accountParams->setAvpfRrInterval(uint8_t(data["avpfInterval"].toInt())); if(data.contains("registerEnabled")) accountParams->enableRegister(data.contains("registerEnabled") ? data["registerEnabled"].toBool() : true); if(data.contains("publishPresence")) { newPublishPresence = accountParams->publishEnabled() != data["publishPresence"].toBool(); accountParams->enablePublish(data["publishPresence"].toBool()); }else newPublishPresence = accountParams->publishEnabled(); if(data.contains("avpfEnabled")) accountParams->setAvpfMode(data["avpfEnabled"].toBool() ? linphone::AVPFMode::Enabled : linphone::AVPFMode::Default ); shared_ptr natPolicy = accountParams->getNatPolicy(); bool createdNat = !natPolicy; if (createdNat) natPolicy = CoreManager::getInstance()->getCore()->createNatPolicy(); if(data.contains("iceEnabled")) natPolicy->enableIce(data["iceEnabled"].toBool()); if(data.contains("iceEnabled")) natPolicy->enableStun(data["iceEnabled"].toBool()); string turnUser, stunServer; if(data.contains("turnUser")) turnUser = Utils::appStringToCoreString(data["turnUser"].toString()); if(data.contains("stunServer")) stunServer = Utils::appStringToCoreString(data["stunServer"].toString()); if(data.contains("turnEnabled")) natPolicy->enableTurn(data["turnEnabled"].toBool()); natPolicy->setStunServerUsername(turnUser); natPolicy->setStunServer(stunServer); if( createdNat) accountParams->setNatPolicy(natPolicy); shared_ptr core(CoreManager::getInstance()->getCore()); shared_ptr authInfo(core->findAuthInfo("", turnUser, stunServer)); if (authInfo) { shared_ptr clonedAuthInfo(authInfo->clone()); clonedAuthInfo->setUserid(turnUser); clonedAuthInfo->setUsername(turnUser); clonedAuthInfo->setPassword(Utils::appStringToCoreString(data["turnPassword"].toString())); core->addAuthInfo(clonedAuthInfo); core->removeAuthInfo(authInfo); } else core->addAuthInfo(linphone::Factory::get()->createAuthInfo( turnUser, turnUser, Utils::appStringToCoreString(data["turnPassword"].toString()), "", stunServer, "" )); if( newPublishPresence) emit publishPresenceChanged(); return addOrUpdateAccount(account, accountParams); } bool AccountSettingsModel::addOrUpdateAccount ( const QVariantMap &data ) { shared_ptr account; QString sipAddress = data["sipAddress"].toString(); shared_ptr address = CoreManager::getInstance()->getCore()->interpretUrl(sipAddress.toStdString()); for (const auto &databaseAccount : CoreManager::getInstance()->getAccountList()) if (databaseAccount->getParams()->getIdentityAddress()->weakEqual(address)) { account = databaseAccount; } if(!account) account = createAccount(data.contains("configFilename") ? data["configFilename"].toString() : "create-app-sip-account.rc" ); return addOrUpdateAccount(account, data); } shared_ptr AccountSettingsModel::createAccount(const QString& assistantFile) { shared_ptr core = CoreManager::getInstance()->getCore(); qInfo() << QStringLiteral("Set config on assistant: `%1`.").arg(assistantFile); core->getConfig()->loadFromXmlFile(Paths::getAssistantConfigDirPath() + assistantFile.toStdString()); return core->createAccount(core->createAccountParams()); } void AccountSettingsModel::addAuthInfo ( const shared_ptr &authInfo, const QString &password, const QString &userId ) { authInfo->setPassword(Utils::appStringToCoreString(password)); authInfo->setUserid(Utils::appStringToCoreString(userId)); CoreManager::getInstance()->getCore()->addAuthInfo(authInfo); } void AccountSettingsModel::eraseAllPasswords () { CoreManager::getInstance()->getCore()->clearAllAuthInfo(); } // ----------------------------------------------------------------------------- QString AccountSettingsModel::getUsername () const { shared_ptr address = getUsedSipAddress(); const string displayName = address->getDisplayName(); return Utils::coreStringToAppString( displayName.empty() ? address->getUsername() : displayName ); } void AccountSettingsModel::setUsername (const QString &username) { shared_ptr address = getUsedSipAddress(); shared_ptr newAddress = address->clone(); QString oldUsername = Utils::coreStringToAppString(newAddress->getUsername()); if( oldUsername != username) { if (newAddress->setDisplayName(Utils::appStringToCoreString(username))) { qWarning() << QStringLiteral("Unable to set displayName on sip address: `%1`.") .arg(Utils::coreStringToAppString(newAddress->asStringUriOnly())); } else { setUsedSipAddress(newAddress); emit usernameChanged(); } } } AccountSettingsModel::RegistrationState AccountSettingsModel::getRegistrationState () const { shared_ptr account = CoreManager::getInstance()->getCore()->getDefaultAccount(); return account ? mapLinphoneRegistrationStateToUi(account->getState()) : RegistrationStateNoAccount; } // ----------------------------------------------------------------------------- QString AccountSettingsModel::getPrimaryUsername () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->createPrimaryContactParsed()->getUsername() ); } void AccountSettingsModel::setPrimaryUsername (const QString &username) { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr primary = core->createPrimaryContactParsed(); QString oldUsername = Utils::coreStringToAppString(primary->getUsername()); if(oldUsername != username){ primary->setUsername(Utils::appStringToCoreString( username.isEmpty() ? APPLICATION_NAME : username )); core->setPrimaryContact(primary->asString()); emit primaryUsernameChanged(); } } QString AccountSettingsModel::getPrimaryDisplayName () const { return Utils::coreStringToAppString(CoreManager::getInstance()->getCore()->createPrimaryContactParsed()->getDisplayName()); } void AccountSettingsModel::setPrimaryDisplayName (const QString &displayName) { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr primary = core->createPrimaryContactParsed(); QString oldDisplayName = Utils::coreStringToAppString(primary->getDisplayName()); if(oldDisplayName != displayName){ primary->setDisplayName(Utils::appStringToCoreString(displayName)); core->setPrimaryContact(primary->asString()); emit primaryDisplayNameChanged(); } } QString AccountSettingsModel::getPrimarySipAddress () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->createPrimaryContactParsed()->asString() ); } QString AccountSettingsModel::getDefaultAccountDomain() const{ auto account = CoreManager::getInstance()->getCore()->getDefaultAccount(); if(account) return Utils::coreStringToAppString(account->getParams()->getDomain()); else return ""; } // ----------------------------------------------------------------------------- QVariantList AccountSettingsModel::getAccounts () const { shared_ptr core = CoreManager::getInstance()->getCore(); QVariantList accounts; if(CoreManager::getInstance()->getSettingsModel()->getShowLocalSipAccount()) { QVariantMap account; account["sipAddress"] = Utils::coreStringToAppString(core->createPrimaryContactParsed()->asStringUriOnly()); account["fullSipAddress"] = Utils::coreStringToAppString(core->createPrimaryContactParsed()->asString()); account["unreadMessageCount"] = core->getUnreadChatMessageCountFromLocal(core->createPrimaryContactParsed()); account["missedCallCount"] = CoreManager::getInstance()->getMissedCallCountFromLocal(account["sipAddress"].toString()); account["account"].setValue(nullptr); accounts << account; } for (const auto &account : CoreManager::getInstance()->getAccountList()) { QVariantMap accountMap; accountMap["sipAddress"] = Utils::coreStringToAppString(account->getParams()->getIdentityAddress()->asStringUriOnly()); accountMap["fullSipAddress"] = Utils::coreStringToAppString(account->getParams()->getIdentityAddress()->asString()); accountMap["account"].setValue(account); accountMap["unreadMessageCount"] = account->getUnreadChatMessageCount(); accountMap["missedCallCount"] = CoreManager::getInstance()->getMissedCallCountFromLocal(accountMap["sipAddress"].toString()); accounts << accountMap; } return accounts; } // ----------------------------------------------------------------------------- void AccountSettingsModel::handleRegistrationStateChanged ( const shared_ptr & account, linphone::RegistrationState state ) { Q_UNUSED(state) auto coreManager = CoreManager::getInstance(); shared_ptr defaultAccount = coreManager->getCore()->getDefaultAccount(); if( state == linphone::RegistrationState::Cleared){ auto authInfo = account->findAuthInfo(); if(authInfo) QTimer::singleShot(60000, [authInfo](){// 60s is just to be sure. account_update remove deleted account only after 32s CoreManager::getInstance()->getCore()->removeAuthInfo(authInfo); }); coreManager->getSettingsModel()->configureRlsUri(); }else if(mRemovingAccounts.contains(account)){ mRemovingAccounts.removeAll(account); QTimer::singleShot(100, [account, this](){// removeAccount cannot be called from callback CoreManager::getInstance()->getCore()->removeAccount(account); emit accountsChanged(); }); } if(defaultAccount == account) emit defaultRegistrationChanged(); emit registrationStateChanged(); } linphone-desktop-5.0.2/linphone-app/src/components/settings/AccountSettingsModel.hpp000066400000000000000000000127311434616504300310300ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef ACCOUNT_SETTINGS_MODEL_H_ #define ACCOUNT_SETTINGS_MODEL_H_ #include #include #include #include #include #include // ============================================================================= class AccountSettingsModel : public QObject { Q_OBJECT // Selected account. Q_PROPERTY(QString username READ getUsername WRITE setUsername NOTIFY usernameChanged) Q_PROPERTY(QString sipAddress READ getUsedSipAddressAsStringUriOnly NOTIFY sipAddressChanged) Q_PROPERTY(QString fullSipAddress READ getUsedSipAddressAsString NOTIFY fullSipAddressChanged) Q_PROPERTY(RegistrationState registrationState READ getRegistrationState NOTIFY registrationStateChanged) Q_PROPERTY(QString conferenceUri READ getConferenceUri NOTIFY conferenceUriChanged) Q_PROPERTY(QString videoConferenceUri READ getVideoConferenceUri NOTIFY videoConferenceUriChanged) Q_PROPERTY(QString limeServerUrl READ getLimeServerUrl NOTIFY limeServerUrlChanged) // Default info. Q_PROPERTY(QString primaryDisplayName READ getPrimaryDisplayName WRITE setPrimaryDisplayName NOTIFY primaryDisplayNameChanged) Q_PROPERTY(QString primaryUsername READ getPrimaryUsername WRITE setPrimaryUsername NOTIFY primaryUsernameChanged) Q_PROPERTY(QString primarySipAddress READ getPrimarySipAddress NOTIFY primarySipAddressChanged) Q_PROPERTY(QString defaultAccountDomain READ getDefaultAccountDomain NOTIFY defaultAccountChanged) Q_PROPERTY(QVariantList accounts READ getAccounts NOTIFY accountsChanged) public: enum RegistrationState { RegistrationStateRegistered, RegistrationStateNotRegistered, RegistrationStateInProgress, RegistrationStateNoAccount, }; Q_ENUM(RegistrationState); AccountSettingsModel (QObject *parent = Q_NULLPTR); std::shared_ptr getUsedSipAddress () const; void setUsedSipAddress (const std::shared_ptr &address); QString getUsedSipAddressAsStringUriOnly () const; QString getUsedSipAddressAsString () const; // Update account with parameters or add a new one in core. bool addOrUpdateAccount (std::shared_ptr account, const std::shared_ptr& accountParams); Q_INVOKABLE QVariantMap getAccountDescription (const std::shared_ptr &account); QString getConferenceUri() const; QString getVideoConferenceUri() const; QString getLimeServerUrl() const; Q_INVOKABLE void setDefaultAccount (const std::shared_ptr &account = nullptr); Q_INVOKABLE void setDefaultAccountFromSipAddress (const QString &sipAddress); Q_INVOKABLE bool addOrUpdateAccount (const std::shared_ptr &account, const QVariantMap &data); Q_INVOKABLE bool addOrUpdateAccount (const QVariantMap &data);// Create default account and apply data Q_INVOKABLE void removeAccount (const std::shared_ptr &account); Q_INVOKABLE std::shared_ptr createAccount (const QString& assistantFile); Q_INVOKABLE void addAuthInfo ( const std::shared_ptr &authInfo, const QString &password, const QString &userId ); Q_INVOKABLE void eraseAllPasswords (); signals: void usernameChanged(); void sipAddressChanged(); void fullSipAddressChanged(); void registrationStateChanged(); void conferenceUriChanged(); void videoConferenceUriChanged(); void limeServerUrlChanged(); void primaryDisplayNameChanged(); void primaryUsernameChanged(); void primarySipAddressChanged(); void accountsChanged(); void accountSettingsUpdated (); void defaultAccountChanged(); void publishPresenceChanged(); void defaultRegistrationChanged(); private: QString getUsername () const; void setUsername (const QString &username); RegistrationState getRegistrationState () const; // --------------------------------------------------------------------------- QString getPrimaryUsername () const; void setPrimaryUsername (const QString &username); QString getPrimaryDisplayName () const; void setPrimaryDisplayName (const QString &displayName); QString getPrimarySipAddress () const; QString getDefaultAccountDomain () const; // --------------------------------------------------------------------------- QVariantList getAccounts () const; // --------------------------------------------------------------------------- void handleRegistrationStateChanged ( const std::shared_ptr &account, linphone::RegistrationState state ); QVector > mRemovingAccounts; std::shared_ptr mSelectedAccount; }; Q_DECLARE_METATYPE(std::shared_ptr); #endif // ACCOUNT_SETTINGS_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/settings/SettingsModel.cpp000066400000000000000000001673721434616504300275220ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include #include "config.h" #include "app/App.hpp" #include "app/logger/Logger.hpp" #include "app/paths/Paths.hpp" #include "components/core/CoreManager.hpp" #include "components/tunnel/TunnelModel.hpp" #include "include/LinphoneApp/PluginNetworkHelper.hpp" #include "utils/Utils.hpp" #include "utils/Constants.hpp" #include "utils/MediastreamerUtils.hpp" #include "AccountSettingsModel.hpp" #include "SettingsModel.hpp" // ============================================================================= using namespace std; const string SettingsModel::UiSection("ui"); const string SettingsModel::ContactsSection("contacts_import"); SettingsModel::SettingsModel (QObject *parent) : QObject(parent) { CoreManager *coreManager = CoreManager::getInstance(); mConfig = coreManager->getCore()->getConfig(); QObject::connect(coreManager->getHandlers().get(), &CoreHandlers::callCreated, this, &SettingsModel::handleCallCreated); QObject::connect(coreManager->getHandlers().get(), &CoreHandlers::callStateChanged, this, &SettingsModel::handleCallStateChanged); QObject::connect(coreManager->getHandlers().get(), &CoreHandlers::ecCalibrationResult, this, &SettingsModel::handleEcCalibrationResult); // Readonly state that can change from default account connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &SettingsModel::groupChatEnabledChanged); connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &SettingsModel::videoConferenceEnabledChanged); connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &SettingsModel::secureChatEnabledChanged); connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::accountSettingsUpdated, this, &SettingsModel::groupChatEnabledChanged); connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::accountSettingsUpdated, this, &SettingsModel::videoConferenceEnabledChanged); connect(coreManager->getAccountSettingsModel(), &AccountSettingsModel::accountSettingsUpdated, this, &SettingsModel::secureChatEnabledChanged); configureRlsUri(); } SettingsModel::~SettingsModel() { if(mSimpleCaptureGraph ) { delete mSimpleCaptureGraph; mSimpleCaptureGraph = nullptr; } } void SettingsModel::settingsWindowClosing(void) { onSettingsTabChanged(-1); } void SettingsModel::reloadDevices(){ CoreManager::getInstance()->getCore()->reloadSoundDevices(); emit captureDevicesChanged(getCaptureDevices()); emit playbackDevicesChanged(getPlaybackDevices()); emit playbackDeviceChanged(getPlaybackDevice()); emit captureDeviceChanged(getCaptureDevice()); emit ringerDeviceChanged(getRingerDevice()); CoreManager::getInstance()->getCore()->reloadVideoDevices(); emit videoDevicesChanged(getVideoDevices()); } //Provides tabbar per-tab setup/teardown mechanism for specific settings views void SettingsModel::onSettingsTabChanged(int idx) { int prevIdx = mCurrentSettingsTab; mCurrentSettingsTab = idx; switch (prevIdx) { case 0://sip break; case 1://audio closeAudioSettings(); break; case 2://video break; case 3://call break; case 4://ui break; case 5://advanced break; default: break; } switch (idx) { case 0://sip break; case 1://audio accessAudioSettings(); break; case 2://video accessVideoSettings(); break; case 3://call break; case 4://ui break; case 5://advanced accessAdvancedSettings(); break; default: break; } } // ============================================================================= // Assistant. // ============================================================================= bool SettingsModel::getUseAppSipAccountEnabled () const { return !!mConfig->getInt(UiSection, "use_app_sip_account_enabled", 1); } void SettingsModel::setUseAppSipAccountEnabled (bool status) { mConfig->setInt(UiSection, "use_app_sip_account_enabled", status); emit useAppSipAccountEnabledChanged(status); } bool SettingsModel::getUseOtherSipAccountEnabled () const { return !!mConfig->getInt(UiSection, "use_other_sip_account_enabled", 1); } void SettingsModel::setUseOtherSipAccountEnabled (bool status) { mConfig->setInt(UiSection, "use_other_sip_account_enabled", status); emit useOtherSipAccountEnabledChanged(status); } bool SettingsModel::getCreateAppSipAccountEnabled () const { return !!mConfig->getInt(UiSection, "create_app_sip_account_enabled", 1); } void SettingsModel::setCreateAppSipAccountEnabled (bool status) { mConfig->setInt(UiSection, "create_app_sip_account_enabled", status); emit createAppSipAccountEnabledChanged(status); } bool SettingsModel::getFetchRemoteConfigurationEnabled () const { return !!mConfig->getInt(UiSection, "fetch_remote_configuration_enabled", 1); } void SettingsModel::setFetchRemoteConfigurationEnabled (bool status) { mConfig->setInt(UiSection, "fetch_remote_configuration_enabled", status); emit fetchRemoteConfigurationEnabledChanged(status); } // --------------------------------------------------------------------------- bool SettingsModel::getAssistantSupportsPhoneNumbers () const { return !!mConfig->getInt(UiSection, "assistant_supports_phone_numbers", 1); } void SettingsModel::setAssistantSupportsPhoneNumbers (bool status) { mConfig->setInt(UiSection, "assistant_supports_phone_numbers", status); emit assistantSupportsPhoneNumbersChanged(status); } bool SettingsModel::useWebview() const{ #ifdef ENABLE_APP_WEBVIEW return true; #else return false; #endif } QString SettingsModel::getAssistantRegistrationUrl () const { return Utils::coreStringToAppString(mConfig->getString(UiSection, "assistant_registration_url", Constants::DefaultAssistantRegistrationUrl)); } void SettingsModel::setAssistantRegistrationUrl (QString url) { mConfig->setString(UiSection, "assistant_registration_url", Utils::appStringToCoreString(url)); emit assistantRegistrationUrlChanged(url); } QString SettingsModel::getAssistantLoginUrl () const { return Utils::coreStringToAppString(mConfig->getString(UiSection, "assistant_login_url", Constants::DefaultAssistantLoginUrl)); } void SettingsModel::setAssistantLoginUrl (QString url) { mConfig->setString(UiSection, "assistant_login_url", Utils::appStringToCoreString(url)); emit assistantLoginUrlChanged(url); } QString SettingsModel::getAssistantLogoutUrl () const { return Utils::coreStringToAppString(mConfig->getString(UiSection, "assistant_logout_url", Constants::DefaultAssistantLogoutUrl)); } void SettingsModel::setAssistantLogoutUrl (QString url) { mConfig->setString(UiSection, "assistant_logout_url", Utils::appStringToCoreString(url)); emit assistantLogoutUrlChanged(url); } bool SettingsModel::isCguAccepted () const{ return !!mConfig->getInt(UiSection, "read_and_agree_terms_and_privacy", 0); } void SettingsModel::acceptCgu(const bool accept){ bool oldAccept = isCguAccepted(); if( oldAccept != accept){ mConfig->setInt(UiSection, "read_and_agree_terms_and_privacy", accept); emit cguAcceptedChanged(accept); } } // ============================================================================= // SIP Accounts. // ============================================================================= QString SettingsModel::getDeviceName(const std::shared_ptr& config){ return Utils::coreStringToAppString(config->getString(UiSection, "device_name", Utils::appStringToCoreString(QSysInfo::machineHostName()))); } QString SettingsModel::getDeviceName() const{ return getDeviceName(mConfig); } void SettingsModel::setDeviceName(const QString& deviceName){ mConfig->setString(UiSection, "device_name", Utils::appStringToCoreString(deviceName)); emit deviceNameChanged(); CoreManager::getInstance()->updateUserAgent(); } // ============================================================================= // Audio. // ============================================================================= void SettingsModel::resetCaptureGraph() { deleteCaptureGraph(); createCaptureGraph(); } void SettingsModel::createCaptureGraph() { mSimpleCaptureGraph = new MediastreamerUtils::SimpleCaptureGraph(Utils::appStringToCoreString(getCaptureDevice()), Utils::appStringToCoreString(getPlaybackDevice())); mSimpleCaptureGraph->start(); emit captureGraphRunningChanged(getCaptureGraphRunning()); } void SettingsModel::startCaptureGraph(){ if(!mSimpleCaptureGraph) createCaptureGraph(); ++mCaptureGraphListenerCount; } void SettingsModel::stopCaptureGraph(){ if(mCaptureGraphListenerCount > 0 ){ if(--mCaptureGraphListenerCount == 0) deleteCaptureGraph(); } } void SettingsModel::deleteCaptureGraph(){ if (mSimpleCaptureGraph) { if (mSimpleCaptureGraph->isRunning()) { mSimpleCaptureGraph->stop(); } delete mSimpleCaptureGraph; mSimpleCaptureGraph = nullptr; } } //Force a call on the 'detect' method of all audio filters, updating new or removed devices void SettingsModel::accessAudioSettings() { CoreManager::getInstance()->getCore()->reloadSoundDevices(); emit captureDevicesChanged(getCaptureDevices()); emit playbackDevicesChanged(getPlaybackDevices()); emit playbackDeviceChanged(getPlaybackDevice()); emit captureDeviceChanged(getCaptureDevice()); emit ringerDeviceChanged(getRingerDevice()); emit playbackGainChanged(getPlaybackGain()); emit captureGainChanged(getCaptureGain()); //if (!getIsInCall()) { startCaptureGraph(); //} } void SettingsModel::closeAudioSettings() { stopCaptureGraph(); emit captureGraphRunningChanged(getCaptureGraphRunning()); } bool SettingsModel::getCaptureGraphRunning() { return mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning() && !getIsInCall(); } float SettingsModel::getMicVolume() { float v = 0.0; if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { v = mSimpleCaptureGraph->getCaptureVolume(); } return v; } float SettingsModel::getPlaybackGain() const { float dbGain = CoreManager::getInstance()->getCore()->getPlaybackGainDb(); return MediastreamerUtils::dbToLinear(dbGain); } void SettingsModel::setPlaybackGain(float gain) { float oldGain = getPlaybackGain(); CoreManager::getInstance()->getCore()->setPlaybackGainDb(MediastreamerUtils::linearToDb(gain)); if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { mSimpleCaptureGraph->setPlaybackGain(gain); } if((int)(oldGain*1000) != (int)(gain*1000)) emit playbackGainChanged(gain); } float SettingsModel::getCaptureGain() const { float dbGain = CoreManager::getInstance()->getCore()->getMicGainDb(); return MediastreamerUtils::dbToLinear(dbGain); } void SettingsModel::setCaptureGain(float gain) { float oldGain = getCaptureGain(); CoreManager::getInstance()->getCore()->setMicGainDb(MediastreamerUtils::linearToDb(gain)); if (mSimpleCaptureGraph && mSimpleCaptureGraph->isRunning()) { mSimpleCaptureGraph->setCaptureGain(gain); } if((int)(oldGain *1000) != (int)(gain *1000)) emit captureGainChanged(gain); } QStringList SettingsModel::getCaptureDevices () const { shared_ptr core = CoreManager::getInstance()->getCore(); QStringList list; for (const auto &device : core->getSoundDevicesList()) { if (core->soundDeviceCanCapture(device)) list << Utils::coreStringToAppString(device); } return list; } QStringList SettingsModel::getPlaybackDevices () const { shared_ptr core = CoreManager::getInstance()->getCore(); QStringList list; for (const auto &device : core->getSoundDevicesList()) { if (core->soundDeviceCanPlayback(device)) { list << Utils::coreStringToAppString(device); } } return list; } // ----------------------------------------------------------------------------- QString SettingsModel::getCaptureDevice () const { auto audioDevice = CoreManager::getInstance()->getCore()->getInputAudioDevice(); return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreManager::getInstance()->getCore()->getCaptureDevice()); } void SettingsModel::setCaptureDevice (const QString &device) { std::string devId = Utils::appStringToCoreString(device); auto list = CoreManager::getInstance()->getCore()->getExtendedAudioDevices(); auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) { return audioItem->getId() == devId; }); if(audioDevice != list.cend()){ CoreManager::getInstance()->getCore()->setCaptureDevice(devId); CoreManager::getInstance()->getCore()->setInputAudioDevice(*audioDevice); emit captureDeviceChanged(device); resetCaptureGraph(); }else qWarning() << "Cannot set Capture device. The ID cannot be matched with an existant device : " << device; } // ----------------------------------------------------------------------------- QString SettingsModel::getPlaybackDevice () const { auto audioDevice = CoreManager::getInstance()->getCore()->getOutputAudioDevice(); return Utils::coreStringToAppString(audioDevice? audioDevice->getId() : CoreManager::getInstance()->getCore()->getPlaybackDevice()); } void SettingsModel::setPlaybackDevice (const QString &device) { std::string devId = Utils::appStringToCoreString(device); auto list = CoreManager::getInstance()->getCore()->getExtendedAudioDevices(); auto audioDevice = find_if(list.cbegin(), list.cend(), [&] ( const std::shared_ptr & audioItem) { return audioItem->getId() == devId; }); if(audioDevice != list.cend()){ CoreManager::getInstance()->getCore()->setPlaybackDevice(devId); CoreManager::getInstance()->getCore()->setOutputAudioDevice(*audioDevice); emit playbackDeviceChanged(device); resetCaptureGraph(); }else qWarning() << "Cannot set Playback device. The ID cannot be matched with an existant device : " << device; } // ----------------------------------------------------------------------------- QString SettingsModel::getRingerDevice () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->getRingerDevice() ); } void SettingsModel::setRingerDevice (const QString &device) { CoreManager::getInstance()->getCore()->setRingerDevice( Utils::appStringToCoreString(device) ); emit ringerDeviceChanged(device); } // ----------------------------------------------------------------------------- QString SettingsModel::getRingPath () const { return Utils::coreStringToAppString(CoreManager::getInstance()->getCore()->getRing()); } void SettingsModel::setRingPath (const QString &path) { QString cleanedPath = QDir::cleanPath(path); CoreManager::getInstance()->getCore()->setRing( Utils::appStringToCoreString(cleanedPath) ); emit ringPathChanged(cleanedPath); } // ----------------------------------------------------------------------------- bool SettingsModel::getEchoCancellationEnabled () const { return CoreManager::getInstance()->getCore()->echoCancellationEnabled(); } void SettingsModel::setEchoCancellationEnabled (bool status) { CoreManager::getInstance()->getCore()->enableEchoCancellation(status); emit echoCancellationEnabledChanged(status); } void SettingsModel::startEchoCancellerCalibration(){ CoreManager::getInstance()->getCore()->startEchoCancellerCalibration(); } // ----------------------------------------------------------------------------- bool SettingsModel::getShowAudioCodecs () const { return !!mConfig->getInt(UiSection, "show_audio_codecs", 1); } void SettingsModel::setShowAudioCodecs (bool status) { mConfig->setInt(UiSection, "show_audio_codecs", status); emit showAudioCodecsChanged(status); } // ============================================================================= // Video. // ============================================================================= //Force a call on the 'detect' method of all video filters, updating new or removed devices void SettingsModel::accessVideoSettings() { //if(!getIsInCall())// TODO : This is a workaround to a crash when reloading video devices while in call. Spotted on Macos. CoreManager::getInstance()->getCore()->reloadVideoDevices(); emit videoDevicesChanged(getVideoDevices()); } QStringList SettingsModel::getVideoDevices () const { QStringList list; for (const auto &device : CoreManager::getInstance()->getCore()->getVideoDevicesList()) list << Utils::coreStringToAppString(device); return list; } // ----------------------------------------------------------------------------- QString SettingsModel::getVideoDevice () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->getVideoDevice() ); } void SettingsModel::setVideoDevice (const QString &device) { CoreManager::getInstance()->getCore()->setVideoDevice( Utils::appStringToCoreString(device) ); emit videoDeviceChanged(device); } // ----------------------------------------------------------------------------- QString SettingsModel::getVideoPreset () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->getVideoPreset() ); } void SettingsModel::setVideoPreset (const QString &preset) { CoreManager::getInstance()->getCore()->setVideoPreset( Utils::appStringToCoreString(preset) ); emit videoPresetChanged(preset); } // ----------------------------------------------------------------------------- int SettingsModel::getVideoFramerate () const { return int(CoreManager::getInstance()->getCore()->getPreferredFramerate()); } void SettingsModel::setVideoFramerate (int framerate) { CoreManager::getInstance()->getCore()->setPreferredFramerate(float(framerate)); emit videoFramerateChanged(framerate); } // ----------------------------------------------------------------------------- static inline QVariantMap createMapFromVideoDefinition (const shared_ptr &definition) { QVariantMap map; if (!definition) { map["name"] = QStringLiteral("Bad EGG"); map["width"] = QStringLiteral("?????"); map["height"] = QStringLiteral("?????"); return map; } map["name"] = Utils::coreStringToAppString(definition->getName()); map["width"] = definition->getWidth(); map["height"] = definition->getHeight(); map["__definition"] = QVariant::fromValue(definition); return map; } QVariantList SettingsModel::getSupportedVideoDefinitions () const { QVariantList list; for (const auto &definition : linphone::Factory::get()->getSupportedVideoDefinitions()) list << createMapFromVideoDefinition(definition); return list; } QVariantMap SettingsModel::getVideoDefinition () const { return createMapFromVideoDefinition(CoreManager::getInstance()->getCore()->getPreferredVideoDefinition()); } QVariantMap SettingsModel::getCurrentPreviewVideoDefinition () const { auto definition = CoreManager::getInstance()->getCore()->getCurrentPreviewVideoDefinition(); if(definition) return createMapFromVideoDefinition(definition); else { QVariantMap map; map["width"] = 0; map["height"] = 0; return map; } } void SettingsModel::setVideoDefinition (const QVariantMap &definition) { CoreManager::getInstance()->getCore()->setPreferredVideoDefinition( definition.value("__definition").value>()->clone() ); emit videoDefinitionChanged(definition); } bool SettingsModel::getVideoSupported () const { return CoreManager::getInstance()->getCore()->videoSupported(); } void SettingsModel::setHighMosaicQuality(){ mConfig->setString("video", "max_mosaic_size", ""); } void SettingsModel::setLimitedMosaicQuality(){ mConfig->setString("video", "max_mosaic_size", "vga"); } // ----------------------------------------------------------------------------- bool SettingsModel::getShowVideoCodecs () const { return !!mConfig->getInt(UiSection, "show_video_codecs", 1); } void SettingsModel::setShowVideoCodecs (bool status) { mConfig->setInt(UiSection, "show_video_codecs", status); emit showVideoCodecsChanged(status); } // ============================================================================= void SettingsModel::updateCameraMode(){ auto mode = mConfig->getString("video", "main_display_mode", "OccupyAllSpace"); mConfig->setString("video", "main_display_mode", mode); mConfig->setString("video", "other_display_mode", mode); } SettingsModel::CameraMode SettingsModel::cameraModefromString(const std::string& mode){ if( mode == "Hybrid") return CameraMode::CameraMode_Hybrid; else if( mode == "BlackBars") return CameraMode::CameraMode_BlackBars; else return CameraMode::CameraMode_OccupyAllSpace; } std::string SettingsModel::toString(const CameraMode& mode){ std::string modeStr; switch(mode){ case CameraMode::CameraMode_Hybrid : modeStr = "Hybrid";break; case CameraMode::CameraMode_BlackBars: modeStr = "BlackBars";break; default: modeStr = "OccupyAllSpace"; } return modeStr; } SettingsModel::CameraMode SettingsModel::getCameraMode() const{ return cameraModefromString(mConfig->getString("video", "main_display_mode", "OccupyAllSpace")); } void SettingsModel::setCameraMode(CameraMode mode){ std::string modeToSet; switch(mode){ case CameraMode::CameraMode_Hybrid : modeToSet = "Hybrid";break; case CameraMode::CameraMode_BlackBars: modeToSet = "BlackBars";break; default: modeToSet = "OccupyAllSpace"; } mConfig->setString("video", "main_display_mode", modeToSet); mConfig->setString("video", "other_display_mode", modeToSet); emit cameraModeChanged(); } SettingsModel::CameraMode SettingsModel::getGridCameraMode() const{ return cameraModefromString(mConfig->getString(UiSection, "main_grid_display_mode", "OccupyAllSpace")); } void SettingsModel::setGridCameraMode(CameraMode mode){ auto modeStd = toString(mode); mConfig->setString(UiSection, "main_grid_display_mode", modeStd); emit gridCameraModeChanged(); } SettingsModel::CameraMode SettingsModel::getActiveSpeakerCameraMode() const{ return cameraModefromString(mConfig->getString(UiSection, "main_active_speaker_display_mode", "Hybrid")); } void SettingsModel::setActiveSpeakerCameraMode(CameraMode mode){ auto modeStd = toString(mode); mConfig->setString(UiSection, "main_active_speaker_display_mode", modeStd); emit activeSpeakerCameraModeChanged(); } SettingsModel::CameraMode SettingsModel::getCallCameraMode() const{ return cameraModefromString(mConfig->getString(UiSection, "main_call_display_mode", "Hybrid")); } void SettingsModel::setCallCameraMode(CameraMode mode){ auto modeStd = toString(mode); mConfig->setString(UiSection, "main_call_display_mode", modeStd); emit callCameraModeChanged(); } LinphoneEnums::ConferenceLayout SettingsModel::getVideoConferenceLayout() const{ return (LinphoneEnums::ConferenceLayout) mConfig->getInt(UiSection, "video_conference_layout", (int)LinphoneEnums::ConferenceLayoutActiveSpeaker); } void SettingsModel::setVideoConferenceLayout(LinphoneEnums::ConferenceLayout layout){ mConfig->setInt(UiSection, "video_conference_layout", (int)layout); emit videoConferenceLayoutChanged(); } // ============================================================================= // Chat & calls. // ============================================================================= bool SettingsModel::getAutoAnswerStatus () const { return !!mConfig->getInt(UiSection, "auto_answer", 0); } void SettingsModel::setAutoAnswerStatus (bool status) { mConfig->setInt(UiSection, "auto_answer", status); emit autoAnswerStatusChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getAutoAnswerVideoStatus () const { return !!mConfig->getInt(UiSection, "auto_answer_with_video", 0); } void SettingsModel::setAutoAnswerVideoStatus (bool status) { mConfig->setInt(UiSection, "auto_answer_with_video", status); emit autoAnswerVideoStatusChanged(status); } // ----------------------------------------------------------------------------- int SettingsModel::getAutoAnswerDelay () const { return mConfig->getInt(UiSection, "auto_answer_delay", 0); } void SettingsModel::setAutoAnswerDelay (int delay) { mConfig->setInt(UiSection, "auto_answer_delay", delay); emit autoAnswerDelayChanged(delay); } // ----------------------------------------------------------------------------- bool SettingsModel::getShowTelKeypadAutomatically () const { return !!mConfig->getInt(UiSection, "show_tel_keypad_automatically", 0); } void SettingsModel::setShowTelKeypadAutomatically (bool status) { mConfig->setInt(UiSection, "show_tel_keypad_automatically", status); emit showTelKeypadAutomaticallyChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getKeepCallsWindowInBackground () const { return !!mConfig->getInt(UiSection, "keep_calls_window_in_background", 0); } void SettingsModel::setKeepCallsWindowInBackground (bool status) { mConfig->setInt(UiSection, "keep_calls_window_in_background", status); emit keepCallsWindowInBackgroundChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getOutgoingCallsEnabled () const { return !!mConfig->getInt(UiSection, "outgoing_calls_enabled", 1); } void SettingsModel::setOutgoingCallsEnabled (bool status) { mConfig->setInt(UiSection, "outgoing_calls_enabled", status); emit outgoingCallsEnabledChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getCallRecorderEnabled () const { return !!mConfig->getInt(UiSection, "call_recorder_enabled", 1); } void SettingsModel::setCallRecorderEnabled (bool status) { mConfig->setInt(UiSection, "call_recorder_enabled", status); emit callRecorderEnabledChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getAutomaticallyRecordCalls () const { return !!mConfig->getInt(UiSection, "automatically_record_calls", 0); } void SettingsModel::setAutomaticallyRecordCalls (bool status) { mConfig->setInt(UiSection, "automatically_record_calls", status); emit automaticallyRecordCallsChanged(status); } int SettingsModel::getAutoDownloadMaxSize() const{ return CoreManager::getInstance()->getCore()->getMaxSizeForAutoDownloadIncomingFiles(); } void SettingsModel::setAutoDownloadMaxSize(int maxSize){ if(maxSize != getAutoDownloadMaxSize()){ CoreManager::getInstance()->getCore()->setMaxSizeForAutoDownloadIncomingFiles(maxSize); emit autoDownloadMaxSizeChanged(maxSize); } } // ----------------------------------------------------------------------------- bool SettingsModel::getCallPauseEnabled () const { return !!mConfig->getInt(UiSection, "call_pause_enabled", 1); } void SettingsModel::setCallPauseEnabled (bool status) { mConfig->setInt(UiSection, "call_pause_enabled", status); emit callPauseEnabledChanged(status); } bool SettingsModel::getMuteMicrophoneEnabled () const { return !!mConfig->getInt(UiSection, "mute_microphone_enabled", 1); } void SettingsModel::setMuteMicrophoneEnabled (bool status) { mConfig->setInt(UiSection, "mute_microphone_enabled", status); emit muteMicrophoneEnabledChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getStandardChatEnabled () const { return !!mConfig->getInt(UiSection, getEntryFullName(UiSection,"standard_chat_enabled"), 1); } void SettingsModel::setStandardChatEnabled (bool status) { if(!isReadOnly(UiSection, "standard_chat_enabled")) mConfig->setInt(UiSection, "standard_chat_enabled", status); emit standardChatEnabledChanged(getStandardChatEnabled ()); } bool SettingsModel::getSecureChatEnabled () const { return !!mConfig->getInt(UiSection, getEntryFullName(UiSection, "secure_chat_enabled"), 1) && getLimeIsSupported() && CoreManager::getInstance()->getCore()->getDefaultAccount() && !CoreManager::getInstance()->getCore()->getDefaultAccount()->getParams()->getLimeServerUrl().empty() //&& !CoreManager::getInstance()->getCore()->getLimeX3DhServerUrl().empty() && getGroupChatEnabled(); ; } void SettingsModel::setSecureChatEnabled (bool status) { if(!isReadOnly(UiSection, "secure_chat_enabled")) mConfig->setInt(UiSection, "secure_chat_enabled", status); emit secureChatEnabledChanged(); } bool SettingsModel::getGroupChatEnabled() const{ return CoreManager::getInstance()->getCore()->getDefaultAccount() && !CoreManager::getInstance()->getCore()->getDefaultAccount()->getParams()->getConferenceFactoryUri().empty(); } // ----------------------------------------------------------------------------- bool SettingsModel::getHideEmptyChatRooms() const{ int defaultValue = 0; if(!mConfig->hasEntry("misc", "hide_empty_chat_rooms"))// This step should be removed when this option comes from API and not directly from config file mConfig->setInt("misc", "hide_empty_chat_rooms", defaultValue); return !!mConfig->getInt("misc", "hide_empty_chat_rooms", defaultValue); } void SettingsModel::setHideEmptyChatRooms(const bool& status){ mConfig->setInt("misc", "hide_empty_chat_rooms", status); emit hideEmptyChatRoomsChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getWaitRegistrationForCall() const{ return !!mConfig->getInt(UiSection, "call_wait_registration", 0); } void SettingsModel::setWaitRegistrationForCall(const bool& status){ mConfig->setInt(UiSection, "call_wait_registration", status); emit waitRegistrationForCallChanged(status); } bool SettingsModel::getIncallScreenshotEnabled() const{ return !!mConfig->getInt(UiSection, "show_take_screenshot_button_in_call", 0); } void SettingsModel::setIncallScreenshotEnabled(const bool& status){ mConfig->setInt(UiSection, "show_take_screenshot_button_in_call", status); emit incallScreenshotEnabledChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getConferenceEnabled () const { return !!mConfig->getInt(UiSection, "conference_enabled", 1); } void SettingsModel::setConferenceEnabled (bool status) { mConfig->setInt(UiSection, "conference_enabled", status); emit conferenceEnabledChanged(status); } bool SettingsModel::getVideoConferenceEnabled() const{ return CoreManager::getInstance()->getCore()->getDefaultAccount() && !!CoreManager::getInstance()->getCore()->getDefaultAccount()->getParams()->getAudioVideoConferenceFactoryAddress(); } // ----------------------------------------------------------------------------- bool SettingsModel::getChatNotificationsEnabled () const { return !!mConfig->getInt(UiSection, "chat_notifications_enabled", 1); } void SettingsModel::setChatNotificationsEnabled (bool status) { mConfig->setInt(UiSection, "chat_notifications_enabled", status); emit chatNotificationsEnabledChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getChatNotificationSoundEnabled () const { return !!mConfig->getInt(UiSection, "chat_sound_notification_enabled", 1); } void SettingsModel::setChatNotificationSoundEnabled (bool status) { mConfig->setInt(UiSection, "chat_sound_notification_enabled", status); emit chatNotificationSoundEnabledChanged(status); } // ----------------------------------------------------------------------------- QString SettingsModel::getChatNotificationSoundPath () const { static const string defaultFile = linphone::Factory::get()->getSoundResourcesDir() + "/incoming_chat.wav"; return Utils::coreStringToAppString(mConfig->getString(UiSection, "chat_sound_notification_file", defaultFile)); } void SettingsModel::setChatNotificationSoundPath (const QString &path) { QString cleanedPath = QDir::cleanPath(path); mConfig->setString(UiSection, "chat_sound_notification_file", Utils::appStringToCoreString(cleanedPath)); emit chatNotificationSoundPathChanged(cleanedPath); } // ----------------------------------------------------------------------------- QString SettingsModel::getFileTransferUrl () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->getFileTransferServer() ); } void SettingsModel::setFileTransferUrl (const QString &url) { CoreManager::getInstance()->getCore()->setFileTransferServer( Utils::appStringToCoreString(url) ); emit fileTransferUrlChanged(url); } // ----------------------------------------------------------------------------- bool SettingsModel::getLimeIsSupported () const { return CoreManager::getInstance()->getCore()->limeX3DhAvailable(); } // ----------------------------------------------------------------------------- static inline QVariant buildEncryptionDescription (SettingsModel::MediaEncryption encryption, const char *description) { QVariantMap m; m["key"] = description; m["value"] = encryption; return m; } QVariantList SettingsModel::getSupportedMediaEncryptions () const { shared_ptr core = CoreManager::getInstance()->getCore(); QVariantList list; if (core->mediaEncryptionSupported(linphone::MediaEncryption::SRTP)) list << buildEncryptionDescription(MediaEncryptionSrtp, "SRTP"); if (core->mediaEncryptionSupported(linphone::MediaEncryption::ZRTP)){ if( core->getPostQuantumAvailable()) list << buildEncryptionDescription(MediaEncryptionZrtp, "Post Quantum ZRTP"); else list << buildEncryptionDescription(MediaEncryptionZrtp, "ZRTP"); } if (core->mediaEncryptionSupported(linphone::MediaEncryption::DTLS)) list << buildEncryptionDescription(MediaEncryptionDtls, "DTLS"); return list; } // ----------------------------------------------------------------------------- SettingsModel::MediaEncryption SettingsModel::getMediaEncryption () const { return static_cast( CoreManager::getInstance()->getCore()->getMediaEncryption() ); } void SettingsModel::setMediaEncryption (MediaEncryption encryption) { if (encryption == getMediaEncryption()) return; CoreManager::getInstance()->getCore()->setMediaEncryption( static_cast(encryption) ); if (mandatoryMediaEncryptionEnabled() && encryption == SettingsModel::MediaEncryptionNone) { //Disable mandatory encryption if none is selected enableMandatoryMediaEncryption(false); } emit mediaEncryptionChanged(encryption); } bool SettingsModel::mandatoryMediaEncryptionEnabled () const { return CoreManager::getInstance()->getCore()->isMediaEncryptionMandatory(); } void SettingsModel::enableMandatoryMediaEncryption(bool mandatory) { if (mandatoryMediaEncryptionEnabled() == mandatory) { return; } CoreManager::getInstance()->getCore()->setMediaEncryptionMandatory(mandatory); if (mandatory && getMediaEncryption() == SettingsModel::MediaEncryptionNone) { //Force to SRTP if mandatory but 'none' was selected setMediaEncryption(SettingsModel::MediaEncryptionSrtp); } else { emit mediaEncryptionChanged(getMediaEncryption()); } } bool SettingsModel::getPostQuantumAvailable() const{ return CoreManager::getInstance()->getCore() && CoreManager::getInstance()->getCore()->getPostQuantumAvailable(); } // ----------------------------------------------------------------------------- bool SettingsModel::getLimeState () const { return CoreManager::getInstance()->getCore()->limeX3DhEnabled(); } void SettingsModel::setLimeState (const bool& state) { if (state == getLimeState()) return; CoreManager::getInstance()->getCore()->enableLimeX3Dh(state); emit limeStateChanged(state); } // ----------------------------------------------------------------------------- bool SettingsModel::getContactsEnabled () const { return !!mConfig->getInt(UiSection, "contacts_enabled", 1); } void SettingsModel::setContactsEnabled (bool status) { if(!isReadOnly(UiSection, "contacts_enabled")) mConfig->setInt(UiSection, "contacts_enabled", status); emit contactsEnabledChanged(getContactsEnabled ()); } int SettingsModel::getIncomingCallTimeout() const { return CoreManager::getInstance()->getCore()->getIncTimeout(); } // ============================================================================= // Network. // ============================================================================= bool SettingsModel::getShowNetworkSettings () const { return !!mConfig->getInt(UiSection, "show_network_settings", 1); } void SettingsModel::setShowNetworkSettings (bool status) { mConfig->setInt(UiSection, "show_network_settings", status); emit showNetworkSettingsChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getUseSipInfoForDtmfs () const { return CoreManager::getInstance()->getCore()->getUseInfoForDtmf(); } void SettingsModel::setUseSipInfoForDtmfs (bool status) { shared_ptr core = CoreManager::getInstance()->getCore(); if (status) { core->setUseRfc2833ForDtmf(false); core->setUseInfoForDtmf(true); } else { core->setUseInfoForDtmf(false); core->setUseRfc2833ForDtmf(true); } emit dtmfsProtocolChanged(); } // ----------------------------------------------------------------------------- bool SettingsModel::getUseRfc2833ForDtmfs () const { return CoreManager::getInstance()->getCore()->getUseRfc2833ForDtmf(); } void SettingsModel::setUseRfc2833ForDtmfs (bool status) { shared_ptr core = CoreManager::getInstance()->getCore(); if (status) { core->setUseInfoForDtmf(false); core->setUseRfc2833ForDtmf(true); } else { core->setUseRfc2833ForDtmf(false); core->setUseInfoForDtmf(true); } emit dtmfsProtocolChanged(); } // ----------------------------------------------------------------------------- bool SettingsModel::getIpv6Enabled () const { return CoreManager::getInstance()->getCore()->ipv6Enabled(); } void SettingsModel::setIpv6Enabled (bool status) { CoreManager::getInstance()->getCore()->enableIpv6(status); emit ipv6EnabledChanged(status); } // ----------------------------------------------------------------------------- int SettingsModel::getDownloadBandwidth () const { return CoreManager::getInstance()->getCore()->getDownloadBandwidth(); } void SettingsModel::setDownloadBandwidth (int bandwidth) { CoreManager::getInstance()->getCore()->setDownloadBandwidth(bandwidth); emit downloadBandWidthChanged(getDownloadBandwidth()); } // ----------------------------------------------------------------------------- int SettingsModel::getUploadBandwidth () const { return CoreManager::getInstance()->getCore()->getUploadBandwidth(); } void SettingsModel::setUploadBandwidth (int bandwidth) { CoreManager::getInstance()->getCore()->setUploadBandwidth(bandwidth); emit uploadBandWidthChanged(getUploadBandwidth()); } // ----------------------------------------------------------------------------- bool SettingsModel::getAdaptiveRateControlEnabled () const { return CoreManager::getInstance()->getCore()->adaptiveRateControlEnabled(); } void SettingsModel::setAdaptiveRateControlEnabled (bool status) { CoreManager::getInstance()->getCore()->enableAdaptiveRateControl(status); emit adaptiveRateControlEnabledChanged(status); } // ----------------------------------------------------------------------------- int SettingsModel::getTcpPort () const { return CoreManager::getInstance()->getCore()->getTransports()->getTcpPort(); } void SettingsModel::setTcpPort (int port) { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr transports = core->getTransports(); transports->setTcpPort(port); core->setTransports(transports); emit tcpPortChanged(port); } // ----------------------------------------------------------------------------- int SettingsModel::getUdpPort () const { return CoreManager::getInstance()->getCore()->getTransports()->getUdpPort(); } void SettingsModel::setUdpPort (int port) { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr transports = core->getTransports(); transports->setUdpPort(port); core->setTransports(transports); emit udpPortChanged(port); } // ----------------------------------------------------------------------------- QList SettingsModel::getAudioPortRange () const { shared_ptr range = CoreManager::getInstance()->getCore()->getAudioPortsRange(); return QList() << range->getMin() << range->getMax(); } void SettingsModel::setAudioPortRange (const QList &range) { shared_ptr core = CoreManager::getInstance()->getCore(); int a = range[0]; int b = range[1]; if (b == -1) core->setAudioPort(a); else core->setAudioPortRange(a, b); emit audioPortRangeChanged(a, b); } // ----------------------------------------------------------------------------- QList SettingsModel::getVideoPortRange () const { shared_ptr range = CoreManager::getInstance()->getCore()->getVideoPortsRange(); return QList() << range->getMin() << range->getMax(); } void SettingsModel::setVideoPortRange (const QList &range) { shared_ptr core = CoreManager::getInstance()->getCore(); int a = range[0]; int b = range[1]; if (b == -1) core->setVideoPort(a); else core->setVideoPortRange(a, b); emit videoPortRangeChanged(a, b); } // ----------------------------------------------------------------------------- bool SettingsModel::getIceEnabled () const { return CoreManager::getInstance()->getCore()->getNatPolicy()->iceEnabled(); } void SettingsModel::setIceEnabled (bool status) { shared_ptr natPolicy = CoreManager::getInstance()->getCore()->getNatPolicy(); natPolicy->enableIce(status); natPolicy->enableStun(status); emit iceEnabledChanged(status); } // ----------------------------------------------------------------------------- bool SettingsModel::getTurnEnabled () const { return CoreManager::getInstance()->getCore()->getNatPolicy()->turnEnabled(); } void SettingsModel::setTurnEnabled (bool status) { CoreManager::getInstance()->getCore()->getNatPolicy()->enableTurn(status); emit turnEnabledChanged(status); } // ----------------------------------------------------------------------------- QString SettingsModel::getStunServer () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->getNatPolicy()->getStunServer() ); } void SettingsModel::setStunServer (const QString &stunServer) { CoreManager::getInstance()->getCore()->getNatPolicy()->setStunServer( Utils::appStringToCoreString(stunServer) ); } // ----------------------------------------------------------------------------- QString SettingsModel::getTurnUser () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->getNatPolicy()->getStunServerUsername() ); } void SettingsModel::setTurnUser (const QString &user) { CoreManager::getInstance()->getCore()->getNatPolicy()->setStunServerUsername( Utils::appStringToCoreString(user) ); emit turnUserChanged(user); } // ----------------------------------------------------------------------------- QString SettingsModel::getTurnPassword () const { shared_ptr core(CoreManager::getInstance()->getCore()); shared_ptr natPolicy(core->getNatPolicy()); shared_ptr authInfo(core->findAuthInfo( "", natPolicy->getStunServerUsername(), natPolicy->getStunServer() )); return authInfo ? Utils::coreStringToAppString(authInfo->getPassword()) : QString(""); } void SettingsModel::setTurnPassword (const QString &password) { shared_ptr core(CoreManager::getInstance()->getCore()); shared_ptr natPolicy(core->getNatPolicy()); const string &turnUser(natPolicy->getStunServerUsername()); shared_ptr authInfo(core->findAuthInfo("", turnUser, natPolicy->getStunServer())); if (authInfo) { shared_ptr clonedAuthInfo(authInfo->clone()); clonedAuthInfo->setPassword(Utils::appStringToCoreString(password)); core->addAuthInfo(clonedAuthInfo); core->removeAuthInfo(authInfo); } else core->addAuthInfo(linphone::Factory::get()->createAuthInfo( turnUser, turnUser, Utils::appStringToCoreString(password), "", "", "" )); emit turnPasswordChanged(password); } // ----------------------------------------------------------------------------- int SettingsModel::getDscpSip () const { return CoreManager::getInstance()->getCore()->getSipDscp(); } void SettingsModel::setDscpSip (int dscp) { CoreManager::getInstance()->getCore()->setSipDscp(dscp); emit dscpSipChanged(dscp); } int SettingsModel::getDscpAudio () const { return CoreManager::getInstance()->getCore()->getAudioDscp(); } void SettingsModel::setDscpAudio (int dscp) { CoreManager::getInstance()->getCore()->setAudioDscp(dscp); emit dscpAudioChanged(dscp); } int SettingsModel::getDscpVideo () const { return CoreManager::getInstance()->getCore()->getVideoDscp(); } void SettingsModel::setDscpVideo (int dscp) { CoreManager::getInstance()->getCore()->setVideoDscp(dscp); emit dscpVideoChanged(dscp); } // ----------------------------------------------------------------------------- bool SettingsModel::getRlsUriEnabled () const { return !!mConfig->getInt(UiSection, "rls_uri_enabled", true); } void SettingsModel::setRlsUriEnabled (bool status) { mConfig->setInt(UiSection, "rls_uri_enabled", status); mConfig->setString("sip", "rls_uri", status ? Constants::DefaultRlsUri : ""); emit rlsUriEnabledChanged(status); } static string getRlsUriDomain () { static string domain; if (!domain.empty()) return domain; shared_ptr linphoneAddress = CoreManager::getInstance()->getCore()->createAddress(Constants::DefaultRlsUri); Q_CHECK_PTR(linphoneAddress); domain = linphoneAddress->getDomain(); return domain; } void SettingsModel::configureRlsUri () { // Ensure rls uri is empty. if (!getRlsUriEnabled()) { mConfig->setString("sip", "rls_uri", ""); return; } // Set rls uri if necessary. const string domain = getRlsUriDomain(); for (const auto &account : CoreManager::getInstance()->getAccountList()) if (account->getParams()->getDomain() == domain) { mConfig->setString("sip", "rls_uri", Constants::DefaultRlsUri); return; } mConfig->setString("sip", "rls_uri", ""); } void SettingsModel::configureRlsUri (const std::string& domain) { if (!getRlsUriEnabled()) { mConfig->setString("sip", "rls_uri", ""); return; } const string currentDomain = getRlsUriDomain(); if (domain == currentDomain) { mConfig->setString("sip", "rls_uri", Constants::DefaultRlsUri); return; } mConfig->setString("sip", "rls_uri", ""); } void SettingsModel::configureRlsUri (const shared_ptr &account) { configureRlsUri(account->getParams()->getDomain()); } //------------------------------------------------------------------------------ bool SettingsModel::tunnelAvailable() const{ return CoreManager::getInstance()->getCore()->tunnelAvailable(); } TunnelModel* SettingsModel::getTunnel() const{ return new TunnelModel(CoreManager::getInstance()->getCore()->getTunnel()); } // ============================================================================= // UI. // ============================================================================= QFont SettingsModel::getTextMessageFont() const{ QString family = Utils::coreStringToAppString(mConfig->getString(UiSection, "text_message_font", Utils::appStringToCoreString(App::getInstance()->font().family()))); int pointSize = getTextMessageFontSize(); return QFont(family,pointSize); } void SettingsModel::setTextMessageFont(const QFont& font){ mConfig->setString(UiSection, "text_message_font", Utils::appStringToCoreString(font.family())); setTextMessageFontSize(font.pointSize()); emit textMessageFontChanged(font); } int SettingsModel::getTextMessageFontSize() const{ return mConfig->getInt(UiSection, "text_message_font_size", 10); } void SettingsModel::setTextMessageFontSize(const int& size){ mConfig->setInt(UiSection, "text_message_font_size", size); emit textMessageFontSizeChanged(size); } QString SettingsModel::getSavedScreenshotsFolder () const { return QDir::cleanPath( Utils::coreStringToAppString( mConfig->getString(UiSection, "saved_screenshots_folder", Paths::getCapturesDirPath()) ) ) + QDir::separator(); } void SettingsModel::setSavedScreenshotsFolder (const QString &folder) { QString cleanedFolder = QDir::cleanPath(folder) + QDir::separator(); mConfig->setString(UiSection, "saved_screenshots_folder", Utils::appStringToCoreString(cleanedFolder)); emit savedScreenshotsFolderChanged(cleanedFolder); } // ----------------------------------------------------------------------------- static inline string getLegacySavedCallsFolder (const shared_ptr &config) { return config->getString(SettingsModel::UiSection, "saved_videos_folder", Paths::getCapturesDirPath()); } QString SettingsModel::getSavedCallsFolder () const { return QDir::cleanPath( Utils::coreStringToAppString( mConfig->getString(UiSection, "saved_calls_folder", getLegacySavedCallsFolder(mConfig)) ) ) + QDir::separator(); } void SettingsModel::setSavedCallsFolder (const QString &folder) { QString cleanedFolder = QDir::cleanPath(folder) + QDir::separator(); mConfig->setString(UiSection, "saved_calls_folder", Utils::appStringToCoreString(cleanedFolder)); emit savedCallsFolderChanged(cleanedFolder); } // ----------------------------------------------------------------------------- QString SettingsModel::getDownloadFolder () const { return QDir::cleanPath( Utils::coreStringToAppString( mConfig->getString(UiSection, "download_folder", Paths::getDownloadDirPath()) ) ) + QDir::separator(); } void SettingsModel::setDownloadFolder (const QString &folder) { QString cleanedFolder = QDir::cleanPath(folder) + QDir::separator(); mConfig->setString(UiSection, "download_folder", Utils::appStringToCoreString(cleanedFolder)); emit downloadFolderChanged(cleanedFolder); } // ----------------------------------------------------------------------------- QString SettingsModel::getRemoteProvisioningRootUrl() const{ return Utils::coreStringToAppString(mConfig->getString(UiSection, "remote_provisioning_root", Constants::RemoteProvisioningURL)); } QString SettingsModel::getRemoteProvisioning () const { return Utils::coreStringToAppString(CoreManager::getInstance()->getCore()->getProvisioningUri()); } void SettingsModel::setRemoteProvisioning (const QString &remoteProvisioning) { QString urlRemoteProvisioning = remoteProvisioning; if( QUrl(urlRemoteProvisioning).isRelative()) { urlRemoteProvisioning = getRemoteProvisioningRootUrl() +"/"+ remoteProvisioning; } if (!CoreManager::getInstance()->getCore()->setProvisioningUri(Utils::appStringToCoreString(urlRemoteProvisioning))) emit remoteProvisioningChanged(urlRemoteProvisioning); else emit remoteProvisioningNotChanged(urlRemoteProvisioning); } bool SettingsModel::isQRCodeAvailable() const{ return linphone::Factory::get()->isQrcodeAvailable() && !!mConfig->getInt(UiSection, "use_qrcode", 1); } QString SettingsModel::getFlexiAPIUrl() const{ return Utils::coreStringToAppString(CoreManager::getInstance()->getCore()->getAccountCreatorUrl()); } void SettingsModel::setFlexiAPIUrl (const QString &url){ CoreManager::getInstance()->getCore()->setAccountCreatorUrl(Utils::appStringToCoreString(url)); emit flexiAPIUrlChanged(url); } // ----------------------------------------------------------------------------- bool SettingsModel::getExitOnClose () const { return !!mConfig->getInt(UiSection, "exit_on_close", 0); } void SettingsModel::setExitOnClose (bool value) { mConfig->setInt(UiSection, "exit_on_close", value); emit exitOnCloseChanged(value); } bool SettingsModel::isCheckForUpdateEnabled() const{ return !!mConfig->getInt(UiSection, "check_for_update_enabled", 1); } void SettingsModel::setCheckForUpdateEnabled(bool enable){ mConfig->setInt(UiSection, "check_for_update_enabled", enable); emit checkForUpdateEnabledChanged(); } QString SettingsModel::getVersionCheckUrl() const{ return Utils::coreStringToAppString(mConfig->getString("misc", "version_check_url_root", Constants::VersionCheckReleaseUrl)); } void SettingsModel::setVersionCheckUrl(const QString& url){ if( url != getVersionCheckUrl()){ // Do not trim the url before because we want to update GUI from potential auto fix. mConfig->setString("misc", "version_check_url_root", Utils::appStringToCoreString(url.trimmed())); if( url == Constants::VersionCheckReleaseUrl) setVersionCheckType(VersionCheckType_Release); else if( url == Constants::VersionCheckNightlyUrl) setVersionCheckType(VersionCheckType_Nightly); else setVersionCheckType(VersionCheckType_Custom); emit versionCheckUrlChanged(); } } SettingsModel::VersionCheckType SettingsModel::getVersionCheckType() const{ return (SettingsModel::VersionCheckType) mConfig->getInt(UiSection, "version_check_type", (int)VersionCheckType_Release); } void SettingsModel::setVersionCheckType(const VersionCheckType& type){ if( type != getVersionCheckType()){ mConfig->setInt(UiSection, "version_check_type", (int)type); switch(type){ case VersionCheckType_Release : setVersionCheckUrl(Constants::VersionCheckReleaseUrl); break; case VersionCheckType_Nightly : setVersionCheckUrl(Constants::VersionCheckNightlyUrl);break; case VersionCheckType_Custom : break;// Do not override URL } emit versionCheckTypeChanged(); } } bool SettingsModel::haveVersionNightlyUrl()const{ return QString(Constants::VersionCheckNightlyUrl) != ""; } // ----------------------------------------------------------------------------- bool SettingsModel::getShowLocalSipAccount()const{ return !!mConfig->getInt(UiSection, "show_local_sip_account", 1); } bool SettingsModel::getShowStartChatButton ()const{ return !!mConfig->getInt(UiSection, "show_start_chat_button", 1); } bool SettingsModel::getShowStartVideoCallButton ()const{ return !!mConfig->getInt(UiSection, "show_start_video_button", 1); } bool SettingsModel::isMipmapEnabled() const{ return !!mConfig->getInt(UiSection, "mipmap_enabled", 0); } void SettingsModel::setMipmapEnabled(const bool& enabled){ mConfig->setInt(UiSection, "mipmap_enabled", enabled); emit mipmapEnabledChanged(); } bool SettingsModel::useMinimalTimelineFilter() const{ return !!mConfig->getInt(UiSection, "use_minimal_timeline_filter", 1); } void SettingsModel::setUseMinimalTimelineFilter(const bool& useMinimal) { mConfig->setInt(UiSection, "use_minimal_timeline_filter", useMinimal); emit useMinimalTimelineFilterChanged(); } // ============================================================================= // Advanced. // ============================================================================= void SettingsModel::accessAdvancedSettings() { emit contactImporterChanged(); } //------------------------------------------------------------------------------ QString SettingsModel::getLogText()const{ return Logger::getInstance()->getLogText(); } QString SettingsModel::getLogsFolder () const { return getLogsFolder(mConfig); } void SettingsModel::setLogsFolder (const QString &folder) { // Copy all logs files in the new folder path to keep trace of old logs std::string logPath = Utils::appStringToCoreString(folder); QDir oldDirectory(Utils::coreStringToAppString(CoreManager::getInstance()->getCore()->getLogCollectionPath())); QFileInfoList logsFiles = oldDirectory.entryInfoList(QStringList("*.log"));// Get all log files for(int i = 0 ; i < logsFiles.size() ; ++i){ int count = 0; QString fileName = logsFiles[i].fileName(); while( QFile::exists(folder+QDir::separator()+fileName))// assure unicity of backup files fileName = logsFiles[i].baseName()+"_"+QString::number(++count)+"."+logsFiles[i].completeSuffix(); if(QFile::copy(logsFiles[i].filePath(), folder+QDir::separator()+fileName)) QFile::remove(logsFiles[i].filePath()); } mConfig->setString(UiSection, "logs_folder", logPath); // Update configuration file CoreManager::getInstance()->getCore()->setLogCollectionPath(logPath); // Update Core to the new path. Liblinphone should update it. emit logsFolderChanged(folder); } // ----------------------------------------------------------------------------- QString SettingsModel::getLogsUploadUrl () const { return Utils::coreStringToAppString( CoreManager::getInstance()->getCore()->getLogCollectionUploadServerUrl() ); } void SettingsModel::setLogsUploadUrl (const QString &url) { CoreManager::getInstance()->getCore()->setLogCollectionUploadServerUrl( Utils::appStringToCoreString(url) ); emit logsUploadUrlChanged(getLogsUploadUrl()); } // ----------------------------------------------------------------------------- bool SettingsModel::getLogsEnabled () const { return getLogsEnabled(mConfig); } void SettingsModel::setLogsEnabled (bool status) { mConfig->setInt(UiSection, "logs_enabled", status); Logger::getInstance()->enable(status); emit logsEnabledChanged(status); } // --------------------------------------------------------------------------- QString SettingsModel::getLogsEmail () const { return Utils::coreStringToAppString( mConfig->getString(UiSection, "logs_email", Constants::DefaultLogsEmail) ); } void SettingsModel::setLogsEmail (const QString &email) { mConfig->setString(UiSection, "logs_email", Utils::appStringToCoreString(email)); emit logsEmailChanged(email); } bool SettingsModel::isLdapAvailable(){ return CoreManager::getInstance()->getCore()->ldapAvailable(); } // --------------------------------------------------------------------------- QString SettingsModel::getLogsFolder (const shared_ptr &config) { return Utils::coreStringToAppString(config ? config->getString(UiSection, "logs_folder", Paths::getLogsDirPath()) : Paths::getLogsDirPath()); } bool SettingsModel::getLogsEnabled (const shared_ptr &config) { return config ? config->getInt(UiSection, "logs_enabled", false) : true; } // --------------------------------------------------------------------------- bool SettingsModel::isDeveloperSettingsAvailable() const { #ifdef DEBUG return true; #else return false; #endif } bool SettingsModel::getDeveloperSettingsEnabled () const { #ifdef DEBUG return !!mConfig->getInt(UiSection, "developer_settings", 0); #else return false; #endif // ifdef DEBUG } void SettingsModel::setDeveloperSettingsEnabled (bool status) { #ifdef DEBUG mConfig->setInt(UiSection, "developer_settings", status); emit developerSettingsEnabledChanged(status); #else Q_UNUSED(status) qWarning() << QStringLiteral("Unable to change developer settings mode in release version."); #endif // ifdef DEBUG } void SettingsModel::handleCallCreated(const shared_ptr &) { emit isInCallChanged(getIsInCall()); } void SettingsModel::handleCallStateChanged(const shared_ptr &, linphone::Call::State) { emit isInCallChanged(getIsInCall()); } void SettingsModel::handleEcCalibrationResult(linphone::EcCalibratorStatus status, int delayMs){ emit echoCancellationStatus((int)status, delayMs); } bool SettingsModel::getIsInCall() const { return CoreManager::getInstance()->getCore()->getCallsNb() != 0; } bool SettingsModel::isReadOnly(const std::string& section, const std::string& name) const { return mConfig->hasEntry(section, name+"/readonly"); } std::string SettingsModel::getEntryFullName(const std::string& section, const std::string& name) const { return isReadOnly(section, name)?name+"/readonly" : name; } linphone-desktop-5.0.2/linphone-app/src/components/settings/SettingsModel.hpp000066400000000000000000000777501434616504300275270ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SETTINGS_MODEL_H_ #define SETTINGS_MODEL_H_ #include #include #include #include #include #include "components/core/CoreHandlers.hpp" #include "components/contacts/ContactsImporterModel.hpp" #include "utils/LinphoneEnums.hpp" // ============================================================================= class TunnelModel; class SettingsModel : public QObject { Q_OBJECT // =========================================================================== // PROPERTIES. // =========================================================================== // Assistant. ---------------------------------------------------------------- Q_PROPERTY(bool createAppSipAccountEnabled READ getCreateAppSipAccountEnabled WRITE setCreateAppSipAccountEnabled NOTIFY createAppSipAccountEnabledChanged) Q_PROPERTY(bool fetchRemoteConfigurationEnabled READ getFetchRemoteConfigurationEnabled WRITE setFetchRemoteConfigurationEnabled NOTIFY fetchRemoteConfigurationEnabledChanged) Q_PROPERTY(bool useAppSipAccountEnabled READ getUseAppSipAccountEnabled WRITE setUseAppSipAccountEnabled NOTIFY useAppSipAccountEnabledChanged) Q_PROPERTY(bool useOtherSipAccountEnabled READ getUseOtherSipAccountEnabled WRITE setUseOtherSipAccountEnabled NOTIFY useOtherSipAccountEnabledChanged) Q_PROPERTY(bool assistantSupportsPhoneNumbers READ getAssistantSupportsPhoneNumbers WRITE setAssistantSupportsPhoneNumbers NOTIFY assistantSupportsPhoneNumbersChanged) // Webviews config Q_PROPERTY(QString assistantRegistrationUrl READ getAssistantRegistrationUrl WRITE setAssistantRegistrationUrl NOTIFY assistantRegistrationUrlChanged) Q_PROPERTY(QString assistantLoginUrl READ getAssistantLoginUrl WRITE setAssistantLoginUrl NOTIFY assistantLoginUrlChanged) Q_PROPERTY(QString assistantLogoutUrl READ getAssistantLogoutUrl WRITE setAssistantLogoutUrl NOTIFY assistantLogoutUrlChanged) //---- Q_PROPERTY(bool cguAccepted READ isCguAccepted WRITE acceptCgu NOTIFY cguAcceptedChanged) // SIP Accounts. ------------------------------------------------------------- Q_PROPERTY(QString deviceName READ getDeviceName WRITE setDeviceName NOTIFY deviceNameChanged) // Audio. -------------------------------------------------------------------- Q_PROPERTY(bool captureGraphRunning READ getCaptureGraphRunning NOTIFY captureGraphRunningChanged) Q_PROPERTY(QStringList captureDevices READ getCaptureDevices NOTIFY captureDevicesChanged) Q_PROPERTY(QStringList playbackDevices READ getPlaybackDevices NOTIFY playbackDevicesChanged) Q_PROPERTY(float playbackGain READ getPlaybackGain WRITE setPlaybackGain NOTIFY playbackGainChanged) Q_PROPERTY(float captureGain READ getCaptureGain WRITE setCaptureGain NOTIFY captureGainChanged) Q_PROPERTY(QString captureDevice READ getCaptureDevice WRITE setCaptureDevice NOTIFY captureDeviceChanged) Q_PROPERTY(QString playbackDevice READ getPlaybackDevice WRITE setPlaybackDevice NOTIFY playbackDeviceChanged) Q_PROPERTY(QString ringerDevice READ getRingerDevice WRITE setRingerDevice NOTIFY ringerDeviceChanged) Q_PROPERTY(QString ringPath READ getRingPath WRITE setRingPath NOTIFY ringPathChanged) Q_PROPERTY(bool echoCancellationEnabled READ getEchoCancellationEnabled WRITE setEchoCancellationEnabled NOTIFY echoCancellationEnabledChanged) Q_PROPERTY(bool showAudioCodecs READ getShowAudioCodecs WRITE setShowAudioCodecs NOTIFY showAudioCodecsChanged) // Video. -------------------------------------------------------------------- Q_PROPERTY(QStringList videoDevices READ getVideoDevices NOTIFY videoDevicesChanged) Q_PROPERTY(QString videoDevice READ getVideoDevice WRITE setVideoDevice NOTIFY videoDeviceChanged) Q_PROPERTY(QString videoPreset READ getVideoPreset WRITE setVideoPreset NOTIFY videoPresetChanged) Q_PROPERTY(int videoFramerate READ getVideoFramerate WRITE setVideoFramerate NOTIFY videoFramerateChanged) Q_PROPERTY(QVariantList supportedVideoDefinitions READ getSupportedVideoDefinitions CONSTANT) Q_PROPERTY(QVariantMap videoDefinition READ getVideoDefinition WRITE setVideoDefinition NOTIFY videoDefinitionChanged) Q_PROPERTY(bool videoSupported READ getVideoSupported CONSTANT) Q_PROPERTY(bool showVideoCodecs READ getShowVideoCodecs WRITE setShowVideoCodecs NOTIFY showVideoCodecsChanged) Q_PROPERTY(CameraMode gridCameraMode READ getGridCameraMode WRITE setGridCameraMode NOTIFY gridCameraModeChanged) Q_PROPERTY(CameraMode activeSpeakerCameraMode READ getActiveSpeakerCameraMode WRITE setActiveSpeakerCameraMode NOTIFY activeSpeakerCameraModeChanged) Q_PROPERTY(CameraMode callCameraMode READ getCallCameraMode WRITE setCallCameraMode NOTIFY callCameraModeChanged) Q_PROPERTY(LinphoneEnums::ConferenceLayout videoConferenceLayout READ getVideoConferenceLayout WRITE setVideoConferenceLayout NOTIFY videoConferenceLayoutChanged) // Chat & calls. ------------------------------------------------------------- Q_PROPERTY(bool autoAnswerStatus READ getAutoAnswerStatus WRITE setAutoAnswerStatus NOTIFY autoAnswerStatusChanged) Q_PROPERTY(bool autoAnswerVideoStatus READ getAutoAnswerVideoStatus WRITE setAutoAnswerVideoStatus NOTIFY autoAnswerVideoStatusChanged) Q_PROPERTY(int autoAnswerDelay READ getAutoAnswerDelay WRITE setAutoAnswerDelay NOTIFY autoAnswerDelayChanged) Q_PROPERTY(bool showTelKeypadAutomatically READ getShowTelKeypadAutomatically WRITE setShowTelKeypadAutomatically NOTIFY showTelKeypadAutomaticallyChanged) Q_PROPERTY(bool keepCallsWindowInBackground READ getKeepCallsWindowInBackground WRITE setKeepCallsWindowInBackground NOTIFY keepCallsWindowInBackgroundChanged) Q_PROPERTY(bool outgoingCallsEnabled READ getOutgoingCallsEnabled WRITE setOutgoingCallsEnabled NOTIFY outgoingCallsEnabledChanged) Q_PROPERTY(bool callRecorderEnabled READ getCallRecorderEnabled WRITE setCallRecorderEnabled NOTIFY callRecorderEnabledChanged) Q_PROPERTY(bool automaticallyRecordCalls READ getAutomaticallyRecordCalls WRITE setAutomaticallyRecordCalls NOTIFY automaticallyRecordCallsChanged) Q_PROPERTY(int autoDownloadMaxSize READ getAutoDownloadMaxSize WRITE setAutoDownloadMaxSize NOTIFY autoDownloadMaxSizeChanged) Q_PROPERTY(bool callPauseEnabled READ getCallPauseEnabled WRITE setCallPauseEnabled NOTIFY callPauseEnabledChanged) Q_PROPERTY(bool muteMicrophoneEnabled READ getMuteMicrophoneEnabled WRITE setMuteMicrophoneEnabled NOTIFY muteMicrophoneEnabledChanged) Q_PROPERTY(bool standardChatEnabled READ getStandardChatEnabled WRITE setStandardChatEnabled NOTIFY standardChatEnabledChanged) Q_PROPERTY(bool secureChatEnabled READ getSecureChatEnabled WRITE setSecureChatEnabled NOTIFY secureChatEnabledChanged) Q_PROPERTY(bool groupChatEnabled READ getGroupChatEnabled NOTIFY groupChatEnabledChanged) Q_PROPERTY(bool hideEmptyChatRooms READ getHideEmptyChatRooms WRITE setHideEmptyChatRooms NOTIFY hideEmptyChatRoomsChanged) Q_PROPERTY(bool waitRegistrationForCall READ getWaitRegistrationForCall WRITE setWaitRegistrationForCall NOTIFY waitRegistrationForCallChanged)// Allow call only if the current proxy has been registered Q_PROPERTY(bool incallScreenshotEnabled READ getIncallScreenshotEnabled WRITE setIncallScreenshotEnabled NOTIFY incallScreenshotEnabledChanged) Q_PROPERTY(bool conferenceEnabled READ getConferenceEnabled WRITE setConferenceEnabled NOTIFY conferenceEnabledChanged) Q_PROPERTY(bool videoConferenceEnabled READ getVideoConferenceEnabled NOTIFY videoConferenceEnabledChanged) Q_PROPERTY(bool chatNotificationsEnabled READ getChatNotificationsEnabled WRITE setChatNotificationsEnabled NOTIFY chatNotificationsEnabledChanged) Q_PROPERTY(bool chatNotificationSoundEnabled READ getChatNotificationSoundEnabled WRITE setChatNotificationSoundEnabled NOTIFY chatNotificationSoundEnabledChanged) Q_PROPERTY(QString chatNotificationSoundPath READ getChatNotificationSoundPath WRITE setChatNotificationSoundPath NOTIFY chatNotificationSoundPathChanged) Q_PROPERTY(QString fileTransferUrl READ getFileTransferUrl WRITE setFileTransferUrl NOTIFY fileTransferUrlChanged) Q_PROPERTY(bool limeIsSupported READ getLimeIsSupported CONSTANT) Q_PROPERTY(QVariantList supportedMediaEncryptions READ getSupportedMediaEncryptions CONSTANT) Q_PROPERTY(MediaEncryption mediaEncryption READ getMediaEncryption WRITE setMediaEncryption NOTIFY mediaEncryptionChanged) Q_PROPERTY(bool mediaEncryptionMandatory READ mandatoryMediaEncryptionEnabled WRITE enableMandatoryMediaEncryption NOTIFY mediaEncryptionChanged) Q_PROPERTY(bool isPostQuantumAvailable READ getPostQuantumAvailable CONSTANT) Q_PROPERTY(bool limeState READ getLimeState WRITE setLimeState NOTIFY limeStateChanged) Q_PROPERTY(bool contactsEnabled READ getContactsEnabled WRITE setContactsEnabled NOTIFY contactsEnabledChanged) // Network. ------------------------------------------------------------------ Q_PROPERTY(bool showNetworkSettings READ getShowNetworkSettings WRITE setShowNetworkSettings NOTIFY showNetworkSettingsChanged) Q_PROPERTY(bool useSipInfoForDtmfs READ getUseSipInfoForDtmfs WRITE setUseSipInfoForDtmfs NOTIFY dtmfsProtocolChanged) Q_PROPERTY(bool useRfc2833ForDtmfs READ getUseRfc2833ForDtmfs WRITE setUseRfc2833ForDtmfs NOTIFY dtmfsProtocolChanged) Q_PROPERTY(bool ipv6Enabled READ getIpv6Enabled WRITE setIpv6Enabled NOTIFY ipv6EnabledChanged) Q_PROPERTY(int downloadBandwidth READ getDownloadBandwidth WRITE setDownloadBandwidth NOTIFY downloadBandWidthChanged) Q_PROPERTY(int uploadBandwidth READ getUploadBandwidth WRITE setUploadBandwidth NOTIFY uploadBandWidthChanged) Q_PROPERTY( bool adaptiveRateControlEnabled READ getAdaptiveRateControlEnabled WRITE setAdaptiveRateControlEnabled NOTIFY adaptiveRateControlEnabledChanged ) Q_PROPERTY(int tcpPort READ getTcpPort WRITE setTcpPort NOTIFY tcpPortChanged) Q_PROPERTY(int udpPort READ getUdpPort WRITE setUdpPort NOTIFY udpPortChanged) Q_PROPERTY(QList audioPortRange READ getAudioPortRange WRITE setAudioPortRange NOTIFY audioPortRangeChanged) Q_PROPERTY(QList videoPortRange READ getVideoPortRange WRITE setVideoPortRange NOTIFY videoPortRangeChanged) Q_PROPERTY(bool iceEnabled READ getIceEnabled WRITE setIceEnabled NOTIFY iceEnabledChanged) Q_PROPERTY(bool turnEnabled READ getTurnEnabled WRITE setTurnEnabled NOTIFY turnEnabledChanged) Q_PROPERTY(QString stunServer READ getStunServer WRITE setStunServer NOTIFY stunServerChanged) Q_PROPERTY(QString turnUser READ getTurnUser WRITE setTurnUser NOTIFY turnUserChanged) Q_PROPERTY(QString turnPassword READ getTurnPassword WRITE setTurnPassword NOTIFY turnPasswordChanged) Q_PROPERTY(int dscpSip READ getDscpSip WRITE setDscpSip NOTIFY dscpSipChanged) Q_PROPERTY(int dscpAudio READ getDscpAudio WRITE setDscpAudio NOTIFY dscpAudioChanged) Q_PROPERTY(int dscpVideo READ getDscpVideo WRITE setDscpVideo NOTIFY dscpVideoChanged) Q_PROPERTY(bool rlsUriEnabled READ getRlsUriEnabled WRITE setRlsUriEnabled NOTIFY rlsUriEnabledChanged) // UI. ----------------------------------------------------------------------- Q_PROPERTY(QFont textMessageFont READ getTextMessageFont WRITE setTextMessageFont NOTIFY textMessageFontChanged) Q_PROPERTY(int textMessageFontSize READ getTextMessageFontSize WRITE setTextMessageFontSize NOTIFY textMessageFontSizeChanged) Q_PROPERTY(QString remoteProvisioning READ getRemoteProvisioning WRITE setRemoteProvisioning NOTIFY remoteProvisioningChanged) Q_PROPERTY(QString flexiAPIUrl READ getFlexiAPIUrl WRITE setFlexiAPIUrl NOTIFY flexiAPIUrlChanged) Q_PROPERTY(QString savedScreenshotsFolder READ getSavedScreenshotsFolder WRITE setSavedScreenshotsFolder NOTIFY savedScreenshotsFolderChanged) Q_PROPERTY(QString savedCallsFolder READ getSavedCallsFolder WRITE setSavedCallsFolder NOTIFY savedCallsFolderChanged) Q_PROPERTY(QString downloadFolder READ getDownloadFolder WRITE setDownloadFolder NOTIFY downloadFolderChanged) Q_PROPERTY(bool exitOnClose READ getExitOnClose WRITE setExitOnClose NOTIFY exitOnCloseChanged) Q_PROPERTY(bool checkForUpdateEnabled READ isCheckForUpdateEnabled WRITE setCheckForUpdateEnabled NOTIFY checkForUpdateEnabledChanged) Q_PROPERTY(QString versionCheckUrl READ getVersionCheckUrl WRITE setVersionCheckUrl NOTIFY versionCheckUrlChanged) Q_PROPERTY(VersionCheckType versionCheckType READ getVersionCheckType WRITE setVersionCheckType NOTIFY versionCheckTypeChanged) Q_PROPERTY(bool showLocalSipAccount READ getShowLocalSipAccount CONSTANT) Q_PROPERTY(bool showStartChatButton READ getShowStartChatButton CONSTANT) Q_PROPERTY(bool showStartVideoCallButton READ getShowStartVideoCallButton CONSTANT) Q_PROPERTY(bool mipmapEnabled READ isMipmapEnabled WRITE setMipmapEnabled NOTIFY mipmapEnabledChanged) Q_PROPERTY(bool useMinimalTimelineFilter READ useMinimalTimelineFilter WRITE setUseMinimalTimelineFilter NOTIFY useMinimalTimelineFilterChanged) // Advanced. ----------------------------------------------------------------- Q_PROPERTY(QString logsFolder READ getLogsFolder WRITE setLogsFolder NOTIFY logsFolderChanged) Q_PROPERTY(QString logsUploadUrl READ getLogsUploadUrl WRITE setLogsUploadUrl NOTIFY logsUploadUrlChanged) Q_PROPERTY(bool logsEnabled READ getLogsEnabled WRITE setLogsEnabled NOTIFY logsEnabledChanged) Q_PROPERTY(QString logsEmail READ getLogsEmail WRITE setLogsEmail NOTIFY logsEmailChanged) Q_PROPERTY(bool developerSettingsEnabled READ getDeveloperSettingsEnabled WRITE setDeveloperSettingsEnabled NOTIFY developerSettingsEnabledChanged) Q_PROPERTY(bool isInCall READ getIsInCall NOTIFY isInCallChanged) public: enum MediaEncryption { MediaEncryptionNone = int(linphone::MediaEncryption::None), MediaEncryptionDtls = int(linphone::MediaEncryption::DTLS), MediaEncryptionSrtp = int(linphone::MediaEncryption::SRTP), MediaEncryptionZrtp = int(linphone::MediaEncryption::ZRTP) }; Q_ENUM(MediaEncryption) enum VersionCheckType { VersionCheckType_Release, VersionCheckType_Nightly, VersionCheckType_Custom }; Q_ENUM(VersionCheckType); enum CameraMode{ CameraMode_Hybrid = 0, CameraMode_OccupyAllSpace = 1, CameraMode_BlackBars = 2 }; Q_ENUM(CameraMode); static SettingsModel::CameraMode cameraModefromString(const std::string& mode); static std::string toString(const CameraMode& mode); SettingsModel (QObject *parent = Q_NULLPTR); virtual ~SettingsModel (); // =========================================================================== // METHODS. // =========================================================================== Q_INVOKABLE void onSettingsTabChanged(int idx); Q_INVOKABLE void settingsWindowClosing(void); Q_INVOKABLE void reloadDevices(); // Assistant. ---------------------------------------------------------------- bool getCreateAppSipAccountEnabled () const; void setCreateAppSipAccountEnabled (bool status); bool getFetchRemoteConfigurationEnabled () const; void setFetchRemoteConfigurationEnabled (bool status); bool getUseAppSipAccountEnabled () const; void setUseAppSipAccountEnabled (bool status); bool getUseOtherSipAccountEnabled () const; void setUseOtherSipAccountEnabled (bool status); bool getAssistantSupportsPhoneNumbers () const; void setAssistantSupportsPhoneNumbers (bool status); Q_INVOKABLE bool useWebview() const; QString getAssistantRegistrationUrl () const; void setAssistantRegistrationUrl (QString url); QString getAssistantLoginUrl () const; void setAssistantLoginUrl (QString url); QString getAssistantLogoutUrl () const; void setAssistantLogoutUrl (QString url); bool isCguAccepted () const; void acceptCgu(const bool accept); // SIP Accounts. ------------------------------------------------------------- static QString getDeviceName(const std::shared_ptr& config); QString getDeviceName() const; void setDeviceName(const QString& deviceName); // Audio. -------------------------------------------------------------------- Q_INVOKABLE void startCaptureGraph(); Q_INVOKABLE void stopCaptureGraph(); Q_INVOKABLE void resetCaptureGraph(); void createCaptureGraph(); void deleteCaptureGraph(); bool getCaptureGraphRunning(); void accessAudioSettings(); void closeAudioSettings(); Q_INVOKABLE float getMicVolume(); float getPlaybackGain() const; void setPlaybackGain(float gain); float getCaptureGain() const; void setCaptureGain(float gain); QStringList getCaptureDevices () const; QStringList getPlaybackDevices () const; QString getCaptureDevice () const; void setCaptureDevice (const QString &device); QString getPlaybackDevice () const; void setPlaybackDevice (const QString &device); QString getRingerDevice () const; void setRingerDevice (const QString &device); QString getRingPath () const; void setRingPath (const QString &path); bool getEchoCancellationEnabled () const; void setEchoCancellationEnabled (bool status); Q_INVOKABLE void startEchoCancellerCalibration(); bool getShowAudioCodecs () const; void setShowAudioCodecs (bool status); // Video. -------------------------------------------------------------------- //Called from qml when accessing audio settings panel Q_INVOKABLE void accessVideoSettings(); QStringList getVideoDevices () const; QString getVideoDevice () const; void setVideoDevice (const QString &device); QString getVideoPreset () const; void setVideoPreset (const QString &preset); int getVideoFramerate () const; void setVideoFramerate (int framerate); QVariantList getSupportedVideoDefinitions () const; Q_INVOKABLE void setHighMosaicQuality(); Q_INVOKABLE void setLimitedMosaicQuality(); QVariantMap getVideoDefinition () const; Q_INVOKABLE QVariantMap getCurrentPreviewVideoDefinition () const; void setVideoDefinition (const QVariantMap &definition); bool getVideoSupported () const; bool getShowVideoCodecs () const; void setShowVideoCodecs (bool status); void updateCameraMode(); CameraMode getCameraMode() const; Q_INVOKABLE void setCameraMode(CameraMode mode); // Custom modes to set default for setCameraMode CameraMode getGridCameraMode() const; void setGridCameraMode(CameraMode mode); CameraMode getActiveSpeakerCameraMode() const; void setActiveSpeakerCameraMode(CameraMode mode); CameraMode getCallCameraMode() const; void setCallCameraMode(CameraMode mode); LinphoneEnums::ConferenceLayout getVideoConferenceLayout() const; void setVideoConferenceLayout(LinphoneEnums::ConferenceLayout layout); // Chat & calls. ------------------------------------------------------------- bool getAutoAnswerStatus () const; void setAutoAnswerStatus (bool status); bool getAutoAnswerVideoStatus () const; void setAutoAnswerVideoStatus (bool status); int getAutoAnswerDelay () const; void setAutoAnswerDelay (int delay); bool getShowTelKeypadAutomatically () const; void setShowTelKeypadAutomatically (bool status); bool getKeepCallsWindowInBackground () const; void setKeepCallsWindowInBackground (bool status); bool getOutgoingCallsEnabled () const; void setOutgoingCallsEnabled (bool status); bool getCallRecorderEnabled () const; void setCallRecorderEnabled (bool status); bool getAutomaticallyRecordCalls () const; void setAutomaticallyRecordCalls (bool status); int getAutoDownloadMaxSize() const; void setAutoDownloadMaxSize(int maxSize); bool getCallPauseEnabled () const; void setCallPauseEnabled (bool status); bool getMuteMicrophoneEnabled () const; void setMuteMicrophoneEnabled (bool status); bool getStandardChatEnabled () const; void setStandardChatEnabled (bool status); bool getSecureChatEnabled () const; void setSecureChatEnabled (bool status); bool getHideEmptyChatRooms() const; void setHideEmptyChatRooms(const bool& data); bool getWaitRegistrationForCall() const; void setWaitRegistrationForCall(const bool& status); bool getIncallScreenshotEnabled() const; void setIncallScreenshotEnabled(const bool& status); bool getGroupChatEnabled()const; bool getConferenceEnabled () const; void setConferenceEnabled (bool status); bool getVideoConferenceEnabled()const; bool getChatNotificationsEnabled () const; void setChatNotificationsEnabled (bool status); bool getChatNotificationSoundEnabled () const; void setChatNotificationSoundEnabled (bool status); QString getChatNotificationSoundPath () const; void setChatNotificationSoundPath (const QString &path); QString getFileTransferUrl () const; void setFileTransferUrl (const QString &url); bool getLimeIsSupported () const; QVariantList getSupportedMediaEncryptions () const; MediaEncryption getMediaEncryption () const; void setMediaEncryption (MediaEncryption encryption); bool mandatoryMediaEncryptionEnabled () const; void enableMandatoryMediaEncryption(bool mandatory); bool getPostQuantumAvailable() const; bool getLimeState () const; void setLimeState (const bool& state); bool getContactsEnabled () const; void setContactsEnabled (bool status); int getIncomingCallTimeout() const; // Network. ------------------------------------------------------------------ bool getShowNetworkSettings () const; void setShowNetworkSettings (bool status); bool getUseSipInfoForDtmfs () const; void setUseSipInfoForDtmfs (bool status); bool getUseRfc2833ForDtmfs () const; void setUseRfc2833ForDtmfs (bool status); bool getIpv6Enabled () const; void setIpv6Enabled (bool status); int getDownloadBandwidth () const; void setDownloadBandwidth (int bandwidth); int getUploadBandwidth () const; void setUploadBandwidth (int bandwidth); bool getAdaptiveRateControlEnabled () const; void setAdaptiveRateControlEnabled (bool status); int getTcpPort () const; void setTcpPort (int port); int getUdpPort () const; void setUdpPort (int port); QList getAudioPortRange () const; void setAudioPortRange (const QList &range); QList getVideoPortRange () const; void setVideoPortRange (const QList &range); bool getIceEnabled () const; void setIceEnabled (bool status); bool getTurnEnabled () const; void setTurnEnabled (bool status); QString getStunServer () const; void setStunServer (const QString &stunServer); QString getTurnUser () const; void setTurnUser (const QString &user); QString getTurnPassword () const; void setTurnPassword (const QString &password); int getDscpSip () const; void setDscpSip (int dscp); int getDscpAudio () const; void setDscpAudio (int dscp); int getDscpVideo () const; void setDscpVideo (int dscp); bool getRlsUriEnabled () const; void setRlsUriEnabled (bool status); void configureRlsUri (); void configureRlsUri (const std::string& domain); void configureRlsUri (const std::shared_ptr &account); Q_INVOKABLE bool tunnelAvailable() const; Q_INVOKABLE TunnelModel * getTunnel() const; // UI. ----------------------------------------------------------------------- QFont getTextMessageFont() const; void setTextMessageFont(const QFont& font); int getTextMessageFontSize() const; void setTextMessageFontSize(const int& size); QString getSavedScreenshotsFolder () const; void setSavedScreenshotsFolder (const QString &folder); QString getSavedCallsFolder () const; void setSavedCallsFolder (const QString &folder); QString getDownloadFolder () const; void setDownloadFolder (const QString &folder); QString getRemoteProvisioningRootUrl() const; QString getRemoteProvisioning () const; void setRemoteProvisioning (const QString &remoteProvisioning); Q_INVOKABLE bool isQRCodeAvailable() const; QString getFlexiAPIUrl() const; void setFlexiAPIUrl (const QString &url); bool getExitOnClose () const; void setExitOnClose (bool value); bool isCheckForUpdateEnabled() const; void setCheckForUpdateEnabled(bool enable); QString getVersionCheckUrl() const; void setVersionCheckUrl(const QString& url); VersionCheckType getVersionCheckType() const; void setVersionCheckType(const VersionCheckType& type); Q_INVOKABLE bool haveVersionNightlyUrl()const; Q_INVOKABLE bool getShowLocalSipAccount () const; Q_INVOKABLE bool getShowStartChatButton () const; Q_INVOKABLE bool getShowStartVideoCallButton () const; bool isMipmapEnabled() const; void setMipmapEnabled(const bool& enabled); bool useMinimalTimelineFilter() const; void setUseMinimalTimelineFilter(const bool& useMinimal); // Advanced. --------------------------------------------------------------------------- void accessAdvancedSettings(); Q_INVOKABLE QString getLogText()const; QString getLogsFolder () const; void setLogsFolder (const QString &folder); QString getLogsUploadUrl () const; void setLogsUploadUrl (const QString &url); bool getLogsEnabled () const; void setLogsEnabled (bool status); QString getLogsEmail () const; void setLogsEmail (const QString &email); Q_INVOKABLE bool isLdapAvailable(); // --------------------------------------------------------------------------- static QString getLogsFolder (const std::shared_ptr &config); static bool getLogsEnabled (const std::shared_ptr &config); // --------------------------------------------------------------------------- Q_INVOKABLE bool isDeveloperSettingsAvailable() const; bool getDeveloperSettingsEnabled () const; void setDeveloperSettingsEnabled (bool status); void handleCallCreated(const std::shared_ptr &call); void handleCallStateChanged(const std::shared_ptr &call, linphone::Call::State state); void handleEcCalibrationResult(linphone::EcCalibratorStatus status, int delayMs); bool getIsInCall() const; bool isReadOnly(const std::string& section, const std::string& name) const; std::string getEntryFullName(const std::string& section, const std::string& name) const; // Return the full name of the entry : 'name/readonly' or 'name' static const std::string UiSection; static const std::string ContactsSection; // =========================================================================== // SIGNALS. // =========================================================================== signals: // Assistant. ---------------------------------------------------------------- void createAppSipAccountEnabledChanged (bool status); void fetchRemoteConfigurationEnabledChanged (bool status); void useAppSipAccountEnabledChanged (bool status); void useOtherSipAccountEnabledChanged (bool status); void assistantSupportsPhoneNumbersChanged (bool status); void assistantRegistrationUrlChanged (QString url); void assistantLoginUrlChanged (QString url); void assistantLogoutUrlChanged (QString url); void cguAcceptedChanged(bool accepted); // SIP Accounts. ------------------------------------------------------------- void deviceNameChanged(); // Audio. -------------------------------------------------------------------- void captureGraphRunningChanged(bool running); void playbackGainChanged(float gain); void captureGainChanged(float gain); void captureDevicesChanged (const QStringList &devices); void playbackDevicesChanged (const QStringList &devices); void captureDeviceChanged (const QString &device); void playbackDeviceChanged (const QString &device); void ringerDeviceChanged (const QString &device); void ringPathChanged (const QString &path); void echoCancellationEnabledChanged (bool status); void echoCancellationStatus(int status, int msDelay); void showAudioCodecsChanged (bool status); // Video. -------------------------------------------------------------------- void videoDevicesChanged (const QStringList &devices); void videoDeviceChanged (const QString &device); void videoPresetChanged (const QString &preset); void videoFramerateChanged (int framerate); void videoDefinitionChanged (const QVariantMap &definition); void showVideoCodecsChanged (bool status); void cameraModeChanged(); void gridCameraModeChanged(); void activeSpeakerCameraModeChanged(); void callCameraModeChanged(); void videoConferenceLayoutChanged(); // Chat & calls. ------------------------------------------------------------- void autoAnswerStatusChanged (bool status); void autoAnswerVideoStatusChanged (bool status); void autoAnswerDelayChanged (int delay); void showTelKeypadAutomaticallyChanged (bool status); void keepCallsWindowInBackgroundChanged (bool status); void outgoingCallsEnabledChanged (bool status); void callRecorderEnabledChanged (bool status); void automaticallyRecordCallsChanged (bool status); void autoDownloadMaxSizeChanged (int maxSize); void callPauseEnabledChanged (bool status); void muteMicrophoneEnabledChanged (bool status); void standardChatEnabledChanged (bool status); void secureChatEnabledChanged (); void groupChatEnabledChanged(); void hideEmptyChatRoomsChanged (bool status); void waitRegistrationForCallChanged (bool status); void incallScreenshotEnabledChanged(bool status); void conferenceEnabledChanged (bool status); void videoConferenceEnabledChanged (); void chatNotificationsEnabledChanged (bool status); void chatNotificationSoundEnabledChanged (bool status); void chatNotificationSoundPathChanged (const QString &path); void fileTransferUrlChanged (const QString &url); void mediaEncryptionChanged (MediaEncryption encryption); void limeStateChanged (bool state); void contactsEnabledChanged (bool status); // Network. ------------------------------------------------------------------ void showNetworkSettingsChanged (bool status); void dtmfsProtocolChanged (); void ipv6EnabledChanged (bool status); void downloadBandWidthChanged (int bandwidth); void uploadBandWidthChanged (int bandwidth); bool adaptiveRateControlEnabledChanged (bool status); void tcpPortChanged (int port); void udpPortChanged (int port); void audioPortRangeChanged (int a, int b); void videoPortRangeChanged (int a, int b); void iceEnabledChanged (bool status); void turnEnabledChanged (bool status); void stunServerChanged (const QString &server); void turnUserChanged (const QString &user); void turnPasswordChanged (const QString &password); void dscpSipChanged (int dscp); void dscpAudioChanged (int dscp); void dscpVideoChanged (int dscp); void rlsUriEnabledChanged (bool status); // UI. ----------------------------------------------------------------------- void textMessageFontChanged(const QFont& font); void textMessageFontSizeChanged(const int& size); void savedScreenshotsFolderChanged (const QString &folder); void savedCallsFolderChanged (const QString &folder); void downloadFolderChanged (const QString &folder); void remoteProvisioningChanged (const QString &remoteProvisioning); void remoteProvisioningNotChanged (const QString &remoteProvisioning); void flexiAPIUrlChanged (const QString &url); void exitOnCloseChanged (bool value); void mipmapEnabledChanged(); void useMinimalTimelineFilterChanged(); void checkForUpdateEnabledChanged(); void versionCheckUrlChanged(); void versionCheckTypeChanged(); // Advanced. ----------------------------------------------------------------- void logsFolderChanged (const QString &folder); void logsUploadUrlChanged (const QString &url); void logsEnabledChanged (bool status); void logsEmailChanged (const QString &email); void contactImporterChanged(); bool developerSettingsEnabledChanged (bool status); bool isInCallChanged(bool); private: int mCurrentSettingsTab = 0; MediastreamerUtils::SimpleCaptureGraph *mSimpleCaptureGraph = nullptr; int mCaptureGraphListenerCount = 0; std::shared_ptr mConfig; }; Q_DECLARE_METATYPE(std::shared_ptr); #endif // SETTINGS_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/sip-addresses/000077500000000000000000000000001434616504300251235ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/sip-addresses/SipAddressObserver.cpp000066400000000000000000000032661434616504300314070ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "SipAddressObserver.hpp" // ============================================================================= SipAddressObserver::SipAddressObserver (const QString &peerAddress, const QString &localAddress) { mPeerAddress = peerAddress; mLocalAddress = localAddress; } void SipAddressObserver::setContact (QSharedPointer contact) { if (contact == mContact) return; mContact = contact; emit contactChanged(contact); } void SipAddressObserver::setPresenceStatus (const Presence::PresenceStatus &presenceStatus) { if (presenceStatus == mPresenceStatus) return; mPresenceStatus = presenceStatus; emit presenceStatusChanged(presenceStatus); } void SipAddressObserver::setUnreadMessageCount (int unreadMessageCount) { if (unreadMessageCount == mUnreadMessageCount) return; mUnreadMessageCount = unreadMessageCount; emit unreadMessageCountChanged(unreadMessageCount); } linphone-desktop-5.0.2/linphone-app/src/components/sip-addresses/SipAddressObserver.hpp000066400000000000000000000061151434616504300314100ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SIP_ADDRESS_OBSERVER_H_ #define SIP_ADDRESS_OBSERVER_H_ #include "components/presence/Presence.hpp" #include // ============================================================================= class ContactModel; class SipAddressObserver : public QObject { friend class SipAddressesModel; Q_OBJECT; Q_PROPERTY(QString peerAddress READ getPeerAddress CONSTANT); Q_PROPERTY(QString localAddress READ getLocalAddress CONSTANT); Q_PROPERTY(ContactModel *contact READ getContact NOTIFY contactChanged); Q_PROPERTY(Presence::PresenceStatus presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged); Q_PROPERTY(int unreadMessageCount READ getUnreadMessageCount NOTIFY unreadMessageCountChanged); Q_PROPERTY(bool isOneToOne MEMBER isOneToOne CONSTANT); Q_PROPERTY(bool haveEncryption MEMBER haveEncryption CONSTANT); Q_PROPERTY(int securityLevel MEMBER securityLevel CONSTANT); public: SipAddressObserver (const QString &peerAddress, const QString &localAddress); bool isOneToOne = true; bool haveEncryption = false; int securityLevel = 1; signals: void contactChanged (QSharedPointer); void presenceStatusChanged (const Presence::PresenceStatus &presenceStatus); void unreadMessageCountChanged (int unreadMessageCount); private: QString getPeerAddress () const { return mPeerAddress; } QString getLocalAddress () const { return mLocalAddress; } // --------------------------------------------------------------------------- ContactModel *getContact () const { return mContact.get(); } void setContact (QSharedPointer contact); // --------------------------------------------------------------------------- Presence::PresenceStatus getPresenceStatus () const { return mPresenceStatus; } void setPresenceStatus (const Presence::PresenceStatus &presenceStatus); // --------------------------------------------------------------------------- int getUnreadMessageCount () const { return mUnreadMessageCount; } void setUnreadMessageCount (int unreadMessageCount); QString mPeerAddress; QString mLocalAddress; QSharedPointer mContact; Presence::PresenceStatus mPresenceStatus = Presence::PresenceStatus::Offline; int mUnreadMessageCount = 0; }; Q_DECLARE_METATYPE(SipAddressObserver *); #endif // SIP_ADDRESS_OBSERVER_H_ linphone-desktop-5.0.2/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp000066400000000000000000000645621434616504300312160ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include "components/call/CallModel.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/history/HistoryModel.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "utils/Utils.hpp" #include "SipAddressesModel.hpp" // ============================================================================= using namespace std; // ----------------------------------------------------------------------------- static inline QVariantMap buildVariantMap (const SipAddressesModel::SipAddressEntry &sipAddressEntry) { return QVariantMap{ { "sipAddress", sipAddressEntry.sipAddress }, { "contactModel", QVariant::fromValue(sipAddressEntry.contact.get()) }, { "presenceStatus", sipAddressEntry.presenceStatus }, { "__localToConferenceEntry", QVariant::fromValue(&sipAddressEntry.localAddressToConferenceEntry) } }; } SipAddressesModel::SipAddressesModel (QObject *parent) : QAbstractListModel(parent) { initSipAddresses(); CoreManager *coreManager = CoreManager::getInstance(); mCoreHandlers = coreManager->getHandlers(); QObject::connect(coreManager, &CoreManager::chatRoomModelCreated, this, &SipAddressesModel::handleChatRoomModelCreated); QObject::connect(coreManager, &CoreManager::historyModelCreated, this, &SipAddressesModel::handleHistoryModelCreated); ContactsListModel *contacts = CoreManager::getInstance()->getContactsListModel(); QObject::connect(contacts, &ContactsListModel::contactAdded, this, &SipAddressesModel::handleContactAdded); QObject::connect(contacts, &ContactsListModel::contactRemoved, this, &SipAddressesModel::handleContactRemoved); QObject::connect(contacts, &ContactsListModel::sipAddressAdded, this, &SipAddressesModel::handleSipAddressAdded); QObject::connect(contacts, &ContactsListModel::sipAddressRemoved, this, &SipAddressesModel::handleSipAddressRemoved); CoreHandlers *coreHandlers = mCoreHandlers.get(); QObject::connect(coreHandlers, &CoreHandlers::messagesReceived, this, &SipAddressesModel::handleMessagesReceived); QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &SipAddressesModel::handleCallStateChanged); QObject::connect(coreHandlers, &CoreHandlers::presenceReceived, this, &SipAddressesModel::handlePresenceReceived); QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &SipAddressesModel::handleIsComposingChanged); } // ----------------------------------------------------------------------------- void SipAddressesModel::reset(){ mPeerAddressToSipAddressEntry.clear(); mRefs.clear(); resetInternalData(); initSipAddresses(); emit sipAddressReset(); } int SipAddressesModel::rowCount (const QModelIndex &) const { return mRefs.count(); } QHash SipAddressesModel::roleNames () const { QHash roles; roles[Qt::DisplayRole] = "$modelData"; return roles; } QVariant SipAddressesModel::data (const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mRefs.count()) return QVariant(); if (role == Qt::DisplayRole) return buildVariantMap(*mRefs[row]); return QVariant(); } // ----------------------------------------------------------------------------- QVariantMap SipAddressesModel::find (const QString &sipAddress) const { QString cleanedAddress = Utils::cleanSipAddress(sipAddress); auto it = mPeerAddressToSipAddressEntry.find(cleanedAddress); if (it == mPeerAddressToSipAddressEntry.end()) return QVariantMap(); return buildVariantMap(*it); } // ----------------------------------------------------------------------------- ContactModel *SipAddressesModel::mapSipAddressToContact (const QString &sipAddress) const { QString cleanedAddress = Utils::cleanSipAddress(sipAddress); auto it = mPeerAddressToSipAddressEntry.find(cleanedAddress); return it == mPeerAddressToSipAddressEntry.end() ? nullptr : it->contact.get(); } // ----------------------------------------------------------------------------- SipAddressObserver *SipAddressesModel::getSipAddressObserver (const QString &peerAddress, const QString &localAddress) { SipAddressObserver *model = new SipAddressObserver(peerAddress, localAddress); const QString cleanedPeerAddress = Utils::cleanSipAddress(peerAddress); const QString cleanedLocalAddress = Utils::cleanSipAddress(localAddress); auto it = mPeerAddressToSipAddressEntry.find(cleanedPeerAddress); if (it != mPeerAddressToSipAddressEntry.end()) { model->setContact(it->contact); model->setPresenceStatus(it->presenceStatus); auto it2 = it->localAddressToConferenceEntry.find(cleanedLocalAddress); if (it2 != it->localAddressToConferenceEntry.end()) model->setUnreadMessageCount(it2->unreadMessageCount+it2->missedCallCount); } mObservers.insert(cleanedPeerAddress, model); QObject::connect(model, &SipAddressObserver::destroyed, this, [this, model, cleanedPeerAddress, cleanedLocalAddress]() { // Do not use `model` methods. `model` is partially destroyed here! if (mObservers.remove(cleanedPeerAddress, model) == 0) qWarning() << QStringLiteral("Unable to remove (%1, %2) from observers.") .arg(cleanedPeerAddress).arg(cleanedLocalAddress); }); return model; } // ----------------------------------------------------------------------------- QString SipAddressesModel::getTransportFromSipAddress (const QString &sipAddress) { if( sipAddress.toUpper().contains("TRANSPORT=")) {// Transport has been specified : check for it const shared_ptr address = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(sipAddress)); if (!address) return QString("TLS"); // Return TLS by default switch (address->getTransport()) { case linphone::TransportType::Udp: return QStringLiteral("UDP"); case linphone::TransportType::Tcp: return QStringLiteral("TCP"); case linphone::TransportType::Tls: return QStringLiteral("TLS"); case linphone::TransportType::Dtls: return QStringLiteral("DTLS"); } } return QString("TLS"); } QString SipAddressesModel::addTransportToSipAddress (const QString &sipAddress, const QString &transport) { shared_ptr address = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(sipAddress)); if (!address) return QString(""); LinphoneEnums::TransportType transportType; LinphoneEnums::fromString(transport, &transportType); address->setTransport(LinphoneEnums::toLinphone(transportType)); return Utils::coreStringToAppString(address->asString()); } // ----------------------------------------------------------------------------- QString SipAddressesModel::interpretSipAddress (const QString &sipAddress, bool checkUsername) { shared_ptr lAddress = CoreManager::getInstance()->getCore()->interpretUrl( Utils::appStringToCoreString(sipAddress) ); if (lAddress && (!checkUsername || !lAddress->getUsername().empty())) return Utils::coreStringToAppString(lAddress->asStringUriOnly()); return QString(""); } QString SipAddressesModel::interpretSipAddress (const QString &sipAddress, const QString &domain) { auto core = CoreManager::getInstance()->getCore(); if(!core){ qWarning() << "No core to interpret address"; }else{ auto accountParams = CoreManager::getInstance()->getCore()->createAccountParams(); shared_ptr lAddressTemp = core->createPrimaryContactParsed();// Create an address if( lAddressTemp ){ lAddressTemp->setDomain(Utils::appStringToCoreString(domain)); // Set the domain and use the address into account accountParams->setIdentityAddress(lAddressTemp); auto account = CoreManager::getInstance()->getCore()->createAccount(accountParams); if( account){ shared_ptr lAddress = account->normalizeSipUri(Utils::appStringToCoreString(sipAddress)); if (lAddress) { return Utils::coreStringToAppString(lAddress->asStringUriOnly()); } else { qWarning() << "Cannot normalize Sip Uri : " << sipAddress << " / " << domain; return QString(""); } }else{ qWarning() << "Cannot create an account to interpret parse address : " << sipAddress; } }else{ qWarning() << "Cannot create a Primary Contact Parsed"; } } return QString(""); } QString SipAddressesModel::interpretSipAddress (const QUrl &sipAddress) { return sipAddress.toString(); } bool SipAddressesModel::addressIsValid (const QString &address) { return !!linphone::Factory::get()->createAddress(Utils::appStringToCoreString(address)); } bool SipAddressesModel::sipAddressIsValid (const QString &sipAddress) { shared_ptr address = linphone::Factory::get()->createAddress(Utils::appStringToCoreString(sipAddress)); return address && !address->getUsername().empty(); } // Return at most : sip:username@domain QString SipAddressesModel::cleanSipAddress (const QString &sipAddress) { return Utils::cleanSipAddress(sipAddress); } // ----------------------------------------------------------------------------- bool SipAddressesModel::removeRow (int row, const QModelIndex &parent) { return removeRows(row, 1, parent); } bool SipAddressesModel::removeRows (int row, int count, const QModelIndex &parent) { int limit = row + count - 1; if (row < 0 || count < 0 || limit >= mRefs.count()) return false; beginRemoveRows(parent, row, limit); for (int i = 0; i < count; ++i) mPeerAddressToSipAddressEntry.remove(mRefs.takeAt(row)->sipAddress); endRemoveRows(); return true; } // ----------------------------------------------------------------------------- void SipAddressesModel::handleChatRoomModelCreated (const QSharedPointer &chatRoomModel) { ChatRoomModel *ptr = chatRoomModel.get(); QObject::connect(ptr, &ChatRoomModel::allEntriesRemoved, this, [this, ptr] { handleAllEntriesRemoved(ptr); }); QObject::connect(ptr, &ChatRoomModel::lastEntryRemoved, this, [this, ptr] { handleLastEntryRemoved(ptr); }); QObject::connect(ptr, &ChatRoomModel::messageCountReset, this, [this, ptr] { handleMessageCountReset(ptr); }); QObject::connect(ptr, &ChatRoomModel::messageSent, this, &SipAddressesModel::handleMessageSent); } void SipAddressesModel::handleHistoryModelCreated (HistoryModel *historyModel) { QObject::connect(historyModel, &HistoryModel::callCountReset, this, [this] { handleAllCallCountReset(); }); } void SipAddressesModel::handleContactAdded (QSharedPointer contact) { for (const auto &sipAddress : contact->getVcardModel()->getSipAddresses()) { addOrUpdateSipAddress(sipAddress.toString(), contact); } } void SipAddressesModel::handleContactRemoved (QSharedPointer contact) { for (const auto &sipAddress : contact->getVcardModel()->getSipAddresses()) removeContactOfSipAddress(sipAddress.toString()); } void SipAddressesModel::handleSipAddressAdded (QSharedPointer contact, const QString &sipAddress) { ContactModel *mappedContact = mapSipAddressToContact(sipAddress); if (mappedContact) { qWarning() << "Unable to map sip address" << sipAddress << "to" << contact.get() << "- already used by" << mappedContact; return; } addOrUpdateSipAddress(sipAddress, contact); } void SipAddressesModel::handleSipAddressRemoved (QSharedPointer contact, const QString &sipAddress) { ContactModel *mappedContact = mapSipAddressToContact(sipAddress); if (contact.get() != mappedContact) { qWarning() << "Unable to remove sip address" << sipAddress << "of" << contact.get() << "- already used by" << mappedContact; return; } removeContactOfSipAddress(sipAddress); } void SipAddressesModel::handleMessageReceived (const shared_ptr &message) { const QString peerAddress(Utils::coreStringToAppString(message->getChatRoom()->getPeerAddress()->asStringUriOnly())); addOrUpdateSipAddress(peerAddress, message); } void SipAddressesModel::handleMessagesReceived (const std::list> &messages) { for(auto message: messages){ const QString peerAddress(Utils::coreStringToAppString(message->getChatRoom()->getPeerAddress()->asStringUriOnly())); addOrUpdateSipAddress(peerAddress, message); } } void SipAddressesModel::handleCallStateChanged ( const shared_ptr &call, linphone::Call::State state ) { if (state == linphone::Call::State::End || state == linphone::Call::State::Error) addOrUpdateSipAddress( Utils::coreStringToAppString(call->getRemoteAddress()->asStringUriOnly()), call ); } void SipAddressesModel::handlePresenceReceived ( const QString &sipAddress, const shared_ptr &presenceModel ) { Presence::PresenceStatus status; switch (presenceModel->getConsolidatedPresence()) { case linphone::ConsolidatedPresence::Online: status = Presence::PresenceStatus::Online; break; case linphone::ConsolidatedPresence::Busy: status = Presence::PresenceStatus::Busy; break; case linphone::ConsolidatedPresence::DoNotDisturb: status = Presence::PresenceStatus::DoNotDisturb; break; case linphone::ConsolidatedPresence::Offline: status = Presence::PresenceStatus::Offline; break; } auto it = mPeerAddressToSipAddressEntry.find(sipAddress); if (it != mPeerAddressToSipAddressEntry.end()) { qInfo() << QStringLiteral("Update presence of `%1`: %2.").arg(sipAddress).arg(status); it->presenceStatus = status; int row = mRefs.indexOf(&(*it)); Q_ASSERT(row != -1); emit dataChanged(index(row, 0), index(row, 0)); } updateObservers(sipAddress, status); } void SipAddressesModel::handleAllEntriesRemoved (ChatRoomModel *chatRoomModel) { auto it = mPeerAddressToSipAddressEntry.find(chatRoomModel->getPeerAddress()); if (it == mPeerAddressToSipAddressEntry.end()) return; auto it2 = it->localAddressToConferenceEntry.find(Utils::cleanSipAddress(chatRoomModel->getLocalAddress())); if (it2 == it->localAddressToConferenceEntry.end()) return; it->localAddressToConferenceEntry.erase(it2); int row = mRefs.indexOf(&(*it)); Q_ASSERT(row != -1); // No history, no contact => Remove sip address from list. if (!it->contact && it->localAddressToConferenceEntry.empty()) { removeRow(row); return; } emit dataChanged(index(row, 0), index(row, 0)); } void SipAddressesModel::handleLastEntryRemoved (ChatRoomModel *chatRoomModel) { auto it = mPeerAddressToSipAddressEntry.find(chatRoomModel->getPeerAddress()); if (it == mPeerAddressToSipAddressEntry.end()) return; auto it2 = it->localAddressToConferenceEntry.find(Utils::cleanSipAddress(chatRoomModel->getLocalAddress())); if (it2 == it->localAddressToConferenceEntry.end()) return; int row = mRefs.indexOf(&(*it)); Q_ASSERT(row != -1); Q_ASSERT(chatRoomModel->rowCount() > 0); const QVariantMap map = chatRoomModel->data( chatRoomModel->index(chatRoomModel->rowCount() - 1, 0), ChatRoomModel::ChatEntry ).toMap(); // Update the timestamp with the new last chat message timestamp. it2->timestamp = map["timestamp"].toDateTime(); emit dataChanged(index(row, 0), index(row, 0)); } void SipAddressesModel::handleAllCallCountReset () { for( auto peer = mPeerAddressToSipAddressEntry.begin() ; peer != mPeerAddressToSipAddressEntry.end() ; ++peer){ for( auto local = peer->localAddressToConferenceEntry.begin() ; local != peer->localAddressToConferenceEntry.end() ; ++local){ local->missedCallCount = 0; updateObservers(peer.key(), local.key(), local->unreadMessageCount, local->missedCallCount); } int row = mRefs.indexOf(&(*peer)); emit dataChanged(index(row, 0), index(row, 0)); } } void SipAddressesModel::handleMessageCountReset (ChatRoomModel *chatRoomModel) { const QString &peerAddress = Utils::cleanSipAddress(chatRoomModel->getPeerAddress()); auto it = mPeerAddressToSipAddressEntry.find(peerAddress); if (it == mPeerAddressToSipAddressEntry.end()) return; const QString &localAddress = Utils::cleanSipAddress(chatRoomModel->getLocalAddress()); auto it2 = it->localAddressToConferenceEntry.find(localAddress); if (it2 == it->localAddressToConferenceEntry.end()) return; it2->unreadMessageCount = 0; it2->missedCallCount = 0; int row = mRefs.indexOf(&(*it)); Q_ASSERT(row != -1); emit dataChanged(index(row, 0), index(row, 0)); updateObservers(peerAddress, localAddress, 0, 0); } void SipAddressesModel::handleMessageSent (const shared_ptr &message) { if(message->getChatRoom() && message->getChatRoom()->getPeerAddress()){ const QString peerAddress(Utils::coreStringToAppString(message->getChatRoom()->getPeerAddress()->asStringUriOnly())); addOrUpdateSipAddress(peerAddress, message); } } void SipAddressesModel::handleIsComposingChanged (const shared_ptr &chatRoom) { auto it = mPeerAddressToSipAddressEntry.find(Utils::cleanSipAddress(Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()))); if (it == mPeerAddressToSipAddressEntry.end()) return; auto it2 = it->localAddressToConferenceEntry.find( Utils::cleanSipAddress(Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly())) ); if (it2 == it->localAddressToConferenceEntry.end()) return; it2->isComposing = chatRoom->isRemoteComposing(); int row = mRefs.indexOf(&(*it)); Q_ASSERT(row != -1); emit dataChanged(index(row, 0), index(row, 0)); } // ----------------------------------------------------------------------------- void SipAddressesModel::addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, QSharedPointer contact) { const QString &sipAddress = sipAddressEntry.sipAddress; sipAddressEntry.contact = contact; if (!sipAddressEntry.contact) qWarning() << QStringLiteral("`contact` field is empty on sip address: `%1`.").arg(sipAddress); updateObservers(sipAddress, contact); } void SipAddressesModel::addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, const shared_ptr &call) { const shared_ptr callLog = call->getCallLog(); QString localAddress(Utils::cleanSipAddress(Utils::coreStringToAppString(callLog->getLocalAddress()->asStringUriOnly()))); QString peerAddress(Utils::cleanSipAddress(Utils::coreStringToAppString(callLog->getRemoteAddress()->asStringUriOnly()))); ConferenceEntry &conferenceEntry = sipAddressEntry.localAddressToConferenceEntry[ localAddress ]; qInfo() << QStringLiteral("Update (`%1`, `%2`) from chat call.").arg(sipAddressEntry.sipAddress, localAddress); conferenceEntry.timestamp = callLog->getStatus() == linphone::Call::Status::Success ? QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000) : QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000); conferenceEntry.missedCallCount = CoreManager::getInstance()->getMissedCallCount(peerAddress, localAddress); updateObservers(sipAddressEntry.sipAddress, localAddress, conferenceEntry.unreadMessageCount,conferenceEntry.missedCallCount); } void SipAddressesModel::addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, const shared_ptr &message) { shared_ptr chatRoom(message->getChatRoom()); int count = chatRoom->getUnreadMessagesCount(); QString localAddress(Utils::cleanSipAddress(Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly()))); QString peerAddress(Utils::cleanSipAddress(Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()))); qInfo() << QStringLiteral("Update (`%1`, `%2`) from chat message.").arg(sipAddressEntry.sipAddress, localAddress); ConferenceEntry &conferenceEntry = sipAddressEntry.localAddressToConferenceEntry[localAddress]; conferenceEntry.timestamp = QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000); conferenceEntry.unreadMessageCount = count ; conferenceEntry.missedCallCount = CoreManager::getInstance()->getMissedCallCount(peerAddress, localAddress); updateObservers(sipAddressEntry.sipAddress, localAddress, count,conferenceEntry.missedCallCount); } template void SipAddressesModel::addOrUpdateSipAddress (const QString &sipAddress, T data) { auto it = mPeerAddressToSipAddressEntry.find(sipAddress); if (it != mPeerAddressToSipAddressEntry.end()) { addOrUpdateSipAddress(*it, data); int row = mRefs.indexOf(&(*it)); Q_ASSERT(row != -1); emit dataChanged(index(row, 0), index(row, 0)); return; } SipAddressEntry sipAddressEntry{ sipAddress, nullptr, Presence::Offline, {} }; addOrUpdateSipAddress(sipAddressEntry, data); int row = mRefs.count(); beginInsertRows(QModelIndex(), row, row); mPeerAddressToSipAddressEntry[sipAddress] = move(sipAddressEntry); mRefs << &mPeerAddressToSipAddressEntry[sipAddress]; endInsertRows(); emit layoutChanged(); } // ----------------------------------------------------------------------------- void SipAddressesModel::removeContactOfSipAddress (const QString &sipAddress) { auto it = mPeerAddressToSipAddressEntry.find(sipAddress); if (it == mPeerAddressToSipAddressEntry.end()) { qWarning() << QStringLiteral("Unable to remove unavailable sip address: `%1`.").arg(sipAddress); return; } // Try to map other contact on this sip address. auto contactModel = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(sipAddress); updateObservers(sipAddress, contactModel); qInfo() << QStringLiteral("Map new contact on sip address: `%1`.").arg(sipAddress) << contactModel.get(); addOrUpdateSipAddress(*it, contactModel); int row = mRefs.indexOf(&(*it)); Q_ASSERT(row != -1); // History or contact exists, signal changes. if (!it->localAddressToConferenceEntry.empty() || contactModel) { emit dataChanged(index(row, 0), index(row, 0)); return; } // Remove sip address if no history. removeRow(row); } // ----------------------------------------------------------------------------- void SipAddressesModel::initSipAddresses () { QElapsedTimer timer, stepsTimer; timer.start(); stepsTimer.start(); initSipAddressesFromChat(); qInfo() << "Sip addresses model from Chats :" << stepsTimer.restart() << "ms."; initSipAddressesFromCalls(); qInfo() << "Sip addresses model from Calls :" << stepsTimer.restart() << "ms."; initRefs(); qInfo() << "Sip addresses model from Refs :" << stepsTimer.restart() << "ms."; initSipAddressesFromContacts(); qInfo() << "Sip addresses model from Contacts :" << stepsTimer.restart() << "ms."; qInfo() << "Sip addresses model initialized in:" << timer.elapsed() << "ms."; } void SipAddressesModel::initSipAddressesFromChat () { for (const auto &chatRoom : CoreManager::getInstance()->getCore()->getChatRooms()) { auto lastMessage = chatRoom->getLastMessageInHistory(); if( !lastMessage) continue; QString peerAddress(Utils::cleanSipAddress(Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()))); QString localAddress(Utils::cleanSipAddress(Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly()))); getSipAddressEntry(peerAddress)->localAddressToConferenceEntry[localAddress] = { chatRoom->getUnreadMessagesCount(), CoreManager::getInstance()->getMissedCallCount(peerAddress, localAddress), false, QDateTime::fromMSecsSinceEpoch(lastMessage->getTime() * 1000) }; } } void SipAddressesModel::initSipAddressesFromCalls () { using ConferenceId = QPair; QSet conferenceDone; for (const auto &callLog : CoreManager::getInstance()->getCore()->getCallLogs()) { const QString peerAddress(Utils::cleanSipAddress(Utils::coreStringToAppString(callLog->getRemoteAddress()->asStringUriOnly()))); const QString localAddress(Utils::cleanSipAddress(Utils::coreStringToAppString(callLog->getLocalAddress()->asStringUriOnly()))); ConferenceId conferenceId{ peerAddress, localAddress }; if (conferenceDone.contains(conferenceId)) continue; // Already used. conferenceDone << conferenceId; // The duration can be wrong if status is not success. QDateTime timestamp(callLog->getStatus() == linphone::Call::Status::Success ? QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000) : QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000)); auto &localToConferenceEntry = getSipAddressEntry(peerAddress)->localAddressToConferenceEntry; auto it = localToConferenceEntry.find(localAddress); if (it == localToConferenceEntry.end()) localToConferenceEntry[localAddress] = { 0,0, false, move(timestamp) }; else if (it->timestamp.isNull() || timestamp > it->timestamp) it->timestamp = move(timestamp); } } void SipAddressesModel::initSipAddressesFromContacts () { for (auto &contact : CoreManager::getInstance()->getContactsListModel()->mList) handleContactAdded(contact.objectCast()); } void SipAddressesModel::initRefs () { for (const auto &sipAddressEntry : mPeerAddressToSipAddressEntry) mRefs << &sipAddressEntry; } // ----------------------------------------------------------------------------- void SipAddressesModel::updateObservers (const QString &sipAddress, QSharedPointer contact) { for (auto &observer : mObservers.values(sipAddress)) observer->setContact(contact); } void SipAddressesModel::updateObservers (const QString &sipAddress, const Presence::PresenceStatus &presenceStatus) { for (auto &observer : mObservers.values(sipAddress)) observer->setPresenceStatus(presenceStatus); } void SipAddressesModel::updateObservers (const QString &peerAddress, const QString &localAddress, int messageCount, int missedCallCount) { for (auto &observer : mObservers.values(peerAddress)) { if (observer->getLocalAddress() == localAddress) { observer->setUnreadMessageCount(messageCount+missedCallCount); return; } } } linphone-desktop-5.0.2/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp000066400000000000000000000154241434616504300312140ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SIP_ADDRESSES_MODEL_H_ #define SIP_ADDRESSES_MODEL_H_ #include #include #include #include "SipAddressObserver.hpp" // ============================================================================= class QUrl; class ChatRoomModel; class CoreHandlers; class HistoryModel; class SipAddressesModel : public QAbstractListModel { Q_OBJECT; public: struct ConferenceEntry { int unreadMessageCount; int missedCallCount; bool isComposing; QDateTime timestamp; }; struct SipAddressEntry { QString sipAddress; QSharedPointer contact; Presence::PresenceStatus presenceStatus; QHash localAddressToConferenceEntry; }; SipAddressesModel (QObject *parent = Q_NULLPTR); void reset(); int rowCount (const QModelIndex &index = QModelIndex()) const override; QHash roleNames () const override; QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; //Q_PROPERTY(QList resultExceptions READ getResultExceptions WRITE setResultExceptions NOTIFY resultExceptionsChanged) Q_INVOKABLE QVariantMap find (const QString &sipAddress) const; Q_INVOKABLE ContactModel *mapSipAddressToContact (const QString &sipAddress) const; Q_INVOKABLE SipAddressObserver *getSipAddressObserver (const QString &peerAddress, const QString &localAddress); // --------------------------------------------------------------------------- // Sip addresses helpers. // --------------------------------------------------------------------------- Q_INVOKABLE static QString getTransportFromSipAddress (const QString &sipAddress); Q_INVOKABLE static QString addTransportToSipAddress (const QString &sipAddress, const QString &transport); Q_INVOKABLE static QString interpretSipAddress (const QString &sipAddress, bool checkUsername = true); Q_INVOKABLE static QString interpretSipAddress (const QUrl &sipAddress); Q_INVOKABLE static QString interpretSipAddress (const QString &sipAddress, const QString &domain); Q_INVOKABLE static bool addressIsValid (const QString &address); Q_INVOKABLE static bool sipAddressIsValid (const QString &sipAddress); Q_INVOKABLE static QString cleanSipAddress (const QString &sipAddress); // --------------------------------------------------------------------------- signals: void sipAddressReset();// The model has been reset public slots: void handleAllCallCountReset (); private: bool removeRow (int row, const QModelIndex &parent = QModelIndex()); bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; // --------------------------------------------------------------------------- void handleChatRoomModelCreated (const QSharedPointer &chatRoomModel); void handleHistoryModelCreated (HistoryModel *historyModel) ; void handleContactAdded (QSharedPointer contact); void handleContactRemoved (QSharedPointer contact); void handleSipAddressAdded (QSharedPointer contact, const QString &sipAddress); void handleSipAddressRemoved (QSharedPointer contact, const QString &sipAddress); void handleMessageReceived (const std::shared_ptr &message); void handleMessagesReceived (const std::list> &messages); void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); void handlePresenceReceived (const QString &sipAddress, const std::shared_ptr &presenceModel); void handleAllEntriesRemoved (ChatRoomModel *chatRoomModel); void handleLastEntryRemoved (ChatRoomModel *chatRoomModel); void handleMessageCountReset (ChatRoomModel *chatRoomModel); void handleMessageSent (const std::shared_ptr &message); void handleIsComposingChanged (const std::shared_ptr &chatRoom); // --------------------------------------------------------------------------- // A sip address exists in this list if a contact is linked to it, or a call, or a message. void addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, QSharedPointer contact); void addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, const std::shared_ptr &call); void addOrUpdateSipAddress (SipAddressEntry &sipAddressEntry, const std::shared_ptr &message); //NMN TODO bind to missedCall event and implement void addOrUpdateSipAddress template void addOrUpdateSipAddress (const QString &sipAddress, T data); // --------------------------------------------------------------------------- void removeContactOfSipAddress (const QString &sipAddress); void initSipAddresses (); void initSipAddressesFromChat (); void initSipAddressesFromCalls (); void initSipAddressesFromContacts (); void initRefs (); void updateObservers (const QString &sipAddress, QSharedPointer contact); void updateObservers (const QString &sipAddress, const Presence::PresenceStatus &presenceStatus); void updateObservers (const QString &peerAddress, const QString &localAddress, int messageCount, int missedCallCount); // --------------------------------------------------------------------------- SipAddressEntry *getSipAddressEntry (const QString &peerAddress) { auto it = mPeerAddressToSipAddressEntry.find(peerAddress); if (it == mPeerAddressToSipAddressEntry.end()) it = mPeerAddressToSipAddressEntry.insert(peerAddress, { peerAddress, nullptr, Presence::Offline, {} }); return &(*it); } QHash mPeerAddressToSipAddressEntry; QList mRefs; QMultiHash mObservers; std::shared_ptr mCoreHandlers; }; using LocalAddressToConferenceEntry = QHash; Q_DECLARE_METATYPE(const LocalAddressToConferenceEntry *); #endif // SIP_ADDRESSES_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/sip-addresses/SipAddressesProxyModel.cpp000066400000000000000000000104211434616504300322410ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/core/CoreManager.hpp" #include "SipAddressesModel.hpp" #include "SipAddressesProxyModel.hpp" // ============================================================================= namespace { constexpr int WeightPos0 = 5; constexpr int WeightPos1 = 4; constexpr int WeightPos2 = 3; constexpr int WeightPos3 = 2; constexpr int WeightPosOther = 1; } const QRegExp SipAddressesProxyModel::SearchSeparators("^[^_.-;@ ][_.-;@ ]"); // ----------------------------------------------------------------------------- SipAddressesProxyModel::SipAddressesProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { setSourceModel(CoreManager::getInstance()->getSipAddressesModel()); sort(0); } // ----------------------------------------------------------------------------- void SipAddressesProxyModel::setFilter (const QString &pattern) { mFilter = pattern; invalidate(); } // ----------------------------------------------------------------------------- bool SipAddressesProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); return computeEntryWeight(index.data().toMap()) > 0; } bool SipAddressesProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const QVariantMap mapA = sourceModel()->data(left).toMap(); const QVariantMap mapB = sourceModel()->data(right).toMap(); const QString sipAddressA = mapA["sipAddress"].toString(); const QString sipAddressB = mapB["sipAddress"].toString(); // TODO: Use a cache, do not compute the same value as `filterAcceptsRow`. int weightA = computeEntryWeight(mapA); int weightB = computeEntryWeight(mapB); // 1. Not the same weight. if (weightA != weightB) return weightA > weightB; const ContactModel *contactA = mapA.value("contact").value(); const ContactModel *contactB = mapB.value("contact").value(); // 2. No contacts. if (!contactA && !contactB) return sipAddressA <= sipAddressB; // 3. No contact for a or b. if (!contactA || !contactB) return !!contactA; // 4. Same contact (address). if (contactA == contactB) return sipAddressA <= sipAddressB; // 5. Not the same contact name. int diff = contactA->mLinphoneFriend->getName().compare(contactB->mLinphoneFriend->getName()); if (diff) return diff <= 0; // 6. Same contact name, so compare sip addresses. return sipAddressA < sipAddressB; } int SipAddressesProxyModel::computeEntryWeight (const QVariantMap &entry) const { int weight = computeStringWeight(entry["sipAddress"].toString().mid(4)); const ContactModel *contact = entry.value("contact").value(); if (contact) weight += computeStringWeight(contact->getVcardModel()->getUsername()); return weight; } int SipAddressesProxyModel::computeStringWeight (const QString &string) const { int index = -1; int offset = -1; while ((index = string.indexOf(mFilter, index + 1, Qt::CaseInsensitive)) != -1) { int tmpOffset = index - string.lastIndexOf(SearchSeparators, index) - 1; if ((tmpOffset != -1 && tmpOffset < offset) || offset == -1) if ((offset = tmpOffset) == 0) break; } switch (offset) { case -1: return 0; case 0: return WeightPos0; case 1: return WeightPos1; case 2: return WeightPos2; case 3: return WeightPos3; default: break; } return WeightPosOther; } linphone-desktop-5.0.2/linphone-app/src/components/sip-addresses/SipAddressesProxyModel.hpp000066400000000000000000000030741434616504300322540ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SIP_ADDRESSES_PROXY_MODEL_H_ #define SIP_ADDRESSES_PROXY_MODEL_H_ #include // ============================================================================= class SipAddressesProxyModel : public QSortFilterProxyModel { Q_OBJECT; public: SipAddressesProxyModel (QObject *parent = Q_NULLPTR); Q_INVOKABLE void setFilter (const QString &pattern); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; private: int computeEntryWeight (const QVariantMap &entry) const; int computeStringWeight (const QString &string) const; QString mFilter; static const QRegExp SearchSeparators; }; #endif // SIP_ADDRESSES_PROXY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/sip-addresses/SipAddressesSorter.cpp000066400000000000000000000073721434616504300314300ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/core/CoreManager.hpp" #include "SipAddressesSorter.hpp" #include "../search/SearchResultModel.hpp" // ============================================================================= namespace { constexpr int WeightPos0 = 5; constexpr int WeightPos1 = 4; constexpr int WeightPos2 = 3; constexpr int WeightPos3 = 2; constexpr int WeightPosOther = 1; } const QRegExp SipAddressesSorter::SearchSeparators("^[^_.-;@ ][_.-;@ ]"); // ----------------------------------------------------------------------------- SipAddressesSorter::SipAddressesSorter (QObject *parent) : QObject(parent) { } // ----------------------------------------------------------------------------- //bool SipAddressesSorter::lessThan (const QString& filter, const QVariantMap &left, const QVariantMap &right) { bool SipAddressesSorter::lessThan (const QString& filter, const SearchResultModel *left, const SearchResultModel *right) { const QString sipAddressA = left->getAddressString(); const QString sipAddressB = right->getAddressString(); // TODO: Use a cache, do not compute the same value as `filterAcceptsRow`. int weightA = computeEntryWeight(filter, left); int weightB = computeEntryWeight(filter, right); // 1. Not the same weight. if (weightA != weightB) return weightA > weightB; const ContactModel *contactA = left->getContactModel(); const ContactModel *contactB = right->getContactModel(); // 2. No contacts. if (!contactA && !contactB) return sipAddressA <= sipAddressB; // 3. No contact for a or b. if (!contactA || !contactB) return !!contactA; // 4. Same contact (address). if (contactA == contactB) return sipAddressA <= sipAddressB; // 5. Not the same contact name. int diff = contactA->mLinphoneFriend->getName().compare(contactB->mLinphoneFriend->getName()); if (diff) return diff <= 0; // 6. Same contact name, so compare sip addresses. return sipAddressA < sipAddressB; } int SipAddressesSorter::computeEntryWeight (const QString& filter, const SearchResultModel *entry) { int weight = computeStringWeight(filter, entry->getAddressString().mid(4)); const ContactModel *contact = entry->getContactModel(); if (contact) weight += computeStringWeight(filter, contact->getVcardModel()->getUsername()); return weight; } int SipAddressesSorter::computeStringWeight (const QString& filter, const QString &string) { int index = -1; int offset = -1; while ((index = string.indexOf(filter, index + 1, Qt::CaseInsensitive)) != -1) { int tmpOffset = index - string.lastIndexOf(SearchSeparators, index) - 1; if ((tmpOffset != -1 && tmpOffset < offset) || offset == -1) if ((offset = tmpOffset) == 0) break; } switch (offset) { case -1: return 0; case 0: return WeightPos0; case 1: return WeightPos1; case 2: return WeightPos2; case 3: return WeightPos3; default: break; } return WeightPosOther; } linphone-desktop-5.0.2/linphone-app/src/components/sip-addresses/SipAddressesSorter.hpp000066400000000000000000000027161434616504300314320ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SIP_ADDRESSES_SORTER_H_ #define SIP_ADDRESSES_SORTER_H_ #include #include #include // ============================================================================= class SearchResultModel; class SipAddressesSorter : public QObject{ Q_OBJECT public: SipAddressesSorter (QObject *parent = Q_NULLPTR); static bool lessThan (const QString& filter, const SearchResultModel *left, const SearchResultModel *right); private: static int computeEntryWeight (const QString& filter, const SearchResultModel *entry); static int computeStringWeight (const QString& filter, const QString &string); static const QRegExp SearchSeparators; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/sound-player/000077500000000000000000000000001434616504300247775ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/sound-player/SoundPlayer.cpp000066400000000000000000000142241434616504300277530ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include "components/core/CoreManager.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/Utils.hpp" #include "SoundPlayer.hpp" // ============================================================================= using namespace std; namespace { int ForceCloseTimerInterval = 20; } class SoundPlayer::Handlers : public linphone::PlayerListener { public: Handlers (SoundPlayer *soundPlayer) { mSoundPlayer = soundPlayer; } private: void onEofReached (const shared_ptr &) override { QMutex &mutex = mSoundPlayer->mForceCloseMutex; // Workaround. // This callback is called in a standard thread of mediastreamer, not a QThread. // Signals, connect functions, timers... are unavailable. mutex.lock(); mSoundPlayer->mForceClose = true; mutex.unlock(); } SoundPlayer *mSoundPlayer; }; // ----------------------------------------------------------------------------- SoundPlayer::SoundPlayer (QObject *parent) : QObject(parent) { CoreManager *coreManager = CoreManager::getInstance(); SettingsModel *settingsModel = coreManager->getSettingsModel(); mForceCloseTimer = new QTimer(this); mForceCloseTimer->setInterval(ForceCloseTimerInterval); QObject::connect(mForceCloseTimer, &QTimer::timeout, this, &SoundPlayer::handleEof); mHandlers = make_shared(this); // Connection to rebuilding player when changing ringer selection. This player is only for Ringer. QObject::connect(settingsModel, &SettingsModel::ringerDeviceChanged, this, [this] { rebuildInternalPlayer(); }); buildInternalPlayer(); } SoundPlayer::~SoundPlayer () { mForceCloseTimer->stop(); if( mInternalPlayer) mInternalPlayer->close(); } // ----------------------------------------------------------------------------- void SoundPlayer::pause () { if (mPlaybackState == SoundPlayer::PausedState) return; if (mInternalPlayer->pause()) { setError(QStringLiteral("Unable to pause: `%1`").arg(mSource)); return; } mForceCloseTimer->stop(); mPlaybackState = SoundPlayer::PausedState; emit paused(); emit playbackStateChanged(mPlaybackState); } void SoundPlayer::play () { if (mPlaybackState == SoundPlayer::PlayingState || mSource == "") return; if ( (mPlaybackState == SoundPlayer::StoppedState || mPlaybackState == SoundPlayer::ErrorState) && mInternalPlayer->open(Utils::appStringToCoreString(mSource)) ) { qWarning() << QStringLiteral("Unable to open: `%1`").arg(mSource); return; } if (mInternalPlayer->start() ) { setError(QStringLiteral("Unable to play: `%1`").arg(mSource)); return; } mForceCloseTimer->start(); mPlaybackState = SoundPlayer::PlayingState; emit playing(); emit playbackStateChanged(mPlaybackState); } void SoundPlayer::stop () { stop(false); } // ----------------------------------------------------------------------------- void SoundPlayer::seek (int offset) { mInternalPlayer->seek(offset); } // ----------------------------------------------------------------------------- int SoundPlayer::getPosition () const { return mInternalPlayer->getCurrentPosition(); } // ----------------------------------------------------------------------------- void SoundPlayer::buildInternalPlayer () { CoreManager *coreManager = CoreManager::getInstance(); SettingsModel *settingsModel = coreManager->getSettingsModel(); mInternalPlayer = coreManager->getCore()->createLocalPlayer( Utils::appStringToCoreString(mIsRinger ? settingsModel->getRingerDevice() : settingsModel->getPlaybackDevice()), "", nullptr ); if(mInternalPlayer) mInternalPlayer->addListener(mHandlers); } void SoundPlayer::rebuildInternalPlayer () { stop(true); buildInternalPlayer(); } void SoundPlayer::stop (bool force) { if (mPlaybackState == SoundPlayer::StoppedState && !force) return; mForceCloseTimer->stop(); mPlaybackState = SoundPlayer::StoppedState; if(mInternalPlayer) mInternalPlayer->close(); emit stopped(); emit playbackStateChanged(mPlaybackState); } // ----------------------------------------------------------------------------- void SoundPlayer::handleEof () { mForceCloseMutex.lock(); if (mForceClose) { mForceClose = false; stop(); } mForceCloseMutex.unlock(); } // ----------------------------------------------------------------------------- void SoundPlayer::setError (const QString &message) { qWarning() << message; mInternalPlayer->close(); if (mPlaybackState != SoundPlayer::ErrorState) { mPlaybackState = SoundPlayer::ErrorState; emit playbackStateChanged(mPlaybackState); } } // ----------------------------------------------------------------------------- QString SoundPlayer::getSource () const { return mSource; } void SoundPlayer::setSource (const QString &source) { if (source == mSource) return; stop(); mSource = source; emit sourceChanged(source); } // ----------------------------------------------------------------------------- SoundPlayer::PlaybackState SoundPlayer::getPlaybackState () const { return mPlaybackState; } void SoundPlayer::setPlaybackState (PlaybackState playbackState) { switch (playbackState) { case PlayingState: play(); break; case PausedState: pause(); break; case StoppedState: stop(); break; case ErrorState: break; } } // ----------------------------------------------------------------------------- int SoundPlayer::getDuration () const { return mInternalPlayer->getDuration(); } linphone-desktop-5.0.2/linphone-app/src/components/sound-player/SoundPlayer.hpp000066400000000000000000000050441434616504300277600ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef SOUND_PLAYER_H_ #define SOUND_PLAYER_H_ #include #include #include // ============================================================================= class QTimer; namespace linphone { class Player; } class SoundPlayer : public QObject { class Handlers; Q_OBJECT Q_PROPERTY(QString source READ getSource WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(PlaybackState playbackState READ getPlaybackState WRITE setPlaybackState NOTIFY playbackStateChanged) Q_PROPERTY(int duration READ getDuration NOTIFY sourceChanged) Q_PROPERTY(bool isRinger MEMBER mIsRinger) public: enum PlaybackState { PlayingState, PausedState, StoppedState, ErrorState }; Q_ENUM(PlaybackState); SoundPlayer (QObject *parent = Q_NULLPTR); ~SoundPlayer (); Q_INVOKABLE void pause (); Q_INVOKABLE void play (); Q_INVOKABLE void stop (); Q_INVOKABLE void seek (int offset); Q_INVOKABLE int getPosition () const; signals: void sourceChanged (const QString &source); void paused (); void playing (); void stopped (); void playbackStateChanged (PlaybackState playbackState); private: void buildInternalPlayer (); void rebuildInternalPlayer (); void stop (bool force); void handleEof (); void setError (const QString &message); QString getSource () const; void setSource (const QString &source); PlaybackState getPlaybackState () const; void setPlaybackState (PlaybackState playbackState); int getDuration () const; QString mSource; PlaybackState mPlaybackState = StoppedState; bool mIsRinger = false; bool mForceClose = false; QMutex mForceCloseMutex; QTimer *mForceCloseTimer = nullptr; std::shared_ptr mInternalPlayer; std::shared_ptr mHandlers; }; #endif // SOUND_PLAYER_H_ linphone-desktop-5.0.2/linphone-app/src/components/telephone-numbers/000077500000000000000000000000001434616504300260115ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/telephone-numbers/TelephoneNumbersModel.cpp000066400000000000000000000223161434616504300327610ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "TelephoneNumbersModel.hpp" #include "utils/Utils.hpp" // ============================================================================= using namespace std; const QList> TelephoneNumbersModel::mCountryCodes = { { QLocale::Afghanistan, "93" }, { QLocale::Albania, "355" }, { QLocale::Algeria, "213" }, { QLocale::AmericanSamoa, "1" }, { QLocale::Andorra, "376" }, { QLocale::Angola, "244" }, { QLocale::Anguilla, "1" }, { QLocale::AntiguaAndBarbuda, "1" }, { QLocale::Argentina, "54" }, { QLocale::Armenia, "374" }, { QLocale::Aruba, "297" }, { QLocale::Australia, "61" }, { QLocale::Austria, "43" }, { QLocale::Azerbaijan, "994" }, { QLocale::Bahamas, "1" }, { QLocale::Bahrain, "973" }, { QLocale::Bangladesh, "880" }, { QLocale::Barbados, "1" }, { QLocale::Belarus, "375" }, { QLocale::Belgium, "32" }, { QLocale::Belize, "501" }, { QLocale::Benin, "229" }, { QLocale::Bermuda, "1" }, { QLocale::Bhutan, "975" }, { QLocale::Bolivia, "591" }, { QLocale::BosniaAndHerzegowina, "387" }, { QLocale::Botswana, "267" }, { QLocale::Brazil, "55" }, { QLocale::Brunei, "673" }, { QLocale::Bulgaria, "359" }, { QLocale::BurkinaFaso, "226" }, { QLocale::Burundi, "257" }, { QLocale::Cambodia, "855" }, { QLocale::Cameroon, "237" }, { QLocale::Canada, "1" }, { QLocale::CapeVerde, "238" }, { QLocale::CaymanIslands, "1" }, { QLocale::CentralAfricanRepublic, "236" }, { QLocale::Chad, "235" }, { QLocale::Chile, "56" }, { QLocale::China, "86" }, { QLocale::Colombia, "57" }, { QLocale::Comoros, "269" }, { QLocale::PeoplesRepublicOfCongo, "242" }, { QLocale::DemocraticRepublicOfCongo, "243" }, { QLocale::CookIslands, "682" }, { QLocale::CostaRica, "506" }, { QLocale::IvoryCoast, "225" }, { QLocale::Croatia, "385" }, { QLocale::Cuba, "53" }, { QLocale::Cyprus, "357" }, { QLocale::CzechRepublic, "420" }, { QLocale::Denmark, "45" }, { QLocale::Djibouti, "253" }, { QLocale::Dominica, "1" }, { QLocale::DominicanRepublic, "1" }, { QLocale::Ecuador, "593" }, { QLocale::Egypt, "20" }, { QLocale::ElSalvador, "503" }, { QLocale::EquatorialGuinea, "240" }, { QLocale::Eritrea, "291" }, { QLocale::Estonia, "372" }, { QLocale::Ethiopia, "251" }, { QLocale::FalklandIslands, "500" }, { QLocale::FaroeIslands, "298" }, { QLocale::Fiji, "679" }, { QLocale::Finland, "358" }, { QLocale::France, "33" }, { QLocale::FrenchGuiana, "594" }, { QLocale::FrenchPolynesia, "689" }, { QLocale::Gabon, "241" }, { QLocale::Gambia, "220" }, { QLocale::Georgia, "995" }, { QLocale::Germany, "49" }, { QLocale::Ghana, "233" }, { QLocale::Gibraltar, "350" }, { QLocale::Greece, "30" }, { QLocale::Greenland, "299" }, { QLocale::Grenada, "1" }, { QLocale::Guadeloupe, "590" }, { QLocale::Guam, "1" }, { QLocale::Guatemala, "502" }, { QLocale::Guinea, "224" }, { QLocale::GuineaBissau, "245" }, { QLocale::Guyana, "592" }, { QLocale::Haiti, "509" }, { QLocale::Honduras, "504" }, { QLocale::HongKong, "852" }, { QLocale::Hungary, "36" }, { QLocale::Iceland, "354" }, { QLocale::India, "91" }, { QLocale::Indonesia, "62" }, { QLocale::Iran, "98" }, { QLocale::Iraq, "964" }, { QLocale::Ireland, "353" }, { QLocale::Israel, "972" }, { QLocale::Italy, "39" }, { QLocale::Jamaica, "1" }, { QLocale::Japan, "81" }, { QLocale::Jordan, "962" }, { QLocale::Kazakhstan, "7" }, { QLocale::Kenya, "254" }, { QLocale::Kiribati, "686" }, { QLocale::DemocraticRepublicOfKorea, "850" }, { QLocale::RepublicOfKorea, "82" }, { QLocale::Kuwait, "965" }, { QLocale::Kyrgyzstan, "996" }, { QLocale::Laos, "856" }, { QLocale::Latvia, "371" }, { QLocale::Lebanon, "961" }, { QLocale::Lesotho, "266" }, { QLocale::Liberia, "231" }, { QLocale::Libya, "218" }, { QLocale::Liechtenstein, "423" }, { QLocale::Lithuania, "370" }, { QLocale::Luxembourg, "352" }, { QLocale::Macau, "853" }, { QLocale::Macedonia, "389" }, { QLocale::Madagascar, "261" }, { QLocale::Malawi, "265" }, { QLocale::Malaysia, "60" }, { QLocale::Maldives, "960" }, { QLocale::Mali, "223" }, { QLocale::Malta, "356" }, { QLocale::MarshallIslands, "692" }, { QLocale::Martinique, "596" }, { QLocale::Mauritania, "222" }, { QLocale::Mauritius, "230" }, { QLocale::Mayotte, "262" }, { QLocale::Mexico, "52" }, { QLocale::Micronesia, "691" }, { QLocale::Moldova, "373" }, { QLocale::Monaco, "377" }, { QLocale::Mongolia, "976" }, { QLocale::Montenegro, "382" }, { QLocale::Montserrat, "664" }, { QLocale::Morocco, "212" }, { QLocale::Mozambique, "258" }, { QLocale::Myanmar, "95" }, { QLocale::Namibia, "264" }, { QLocale::NauruCountry, "674" }, { QLocale::Nepal, "43" }, { QLocale::Netherlands, "31" }, { QLocale::NewCaledonia, "687" }, { QLocale::NewZealand, "64" }, { QLocale::Nicaragua, "505" }, { QLocale::Niger, "227" }, { QLocale::Nigeria, "234" }, { QLocale::Niue, "683" }, { QLocale::NorfolkIsland, "672" }, { QLocale::NorthernMarianaIslands, "1" }, { QLocale::Norway, "47" }, { QLocale::Oman, "968" }, { QLocale::Pakistan, "92" }, { QLocale::Palau, "680" }, { QLocale::PalestinianTerritories, "970" }, { QLocale::Panama, "507" }, { QLocale::PapuaNewGuinea, "675" }, { QLocale::Paraguay, "595" }, { QLocale::Peru, "51" }, { QLocale::Philippines, "63" }, { QLocale::Poland, "48" }, { QLocale::Portugal, "351" }, { QLocale::PuertoRico, "1" }, { QLocale::Qatar, "974" }, { QLocale::Reunion, "262" }, { QLocale::Romania, "40" }, { QLocale::RussianFederation, "7" }, { QLocale::Rwanda, "250" }, { QLocale::SaintHelena, "290" }, { QLocale::SaintKittsAndNevis, "1" }, { QLocale::SaintLucia, "1" }, { QLocale::SaintPierreAndMiquelon, "508" }, { QLocale::SaintVincentAndTheGrenadines, "1" }, { QLocale::Samoa, "685" }, { QLocale::SanMarino, "378" }, { QLocale::SaoTomeAndPrincipe, "239" }, { QLocale::SaudiArabia, "966" }, { QLocale::Senegal, "221" }, { QLocale::Serbia, "381" }, { QLocale::Seychelles, "248" }, { QLocale::SierraLeone, "232" }, { QLocale::Singapore, "65" }, { QLocale::Slovakia, "421" }, { QLocale::Slovenia, "386" }, { QLocale::SolomonIslands, "677" }, { QLocale::Somalia, "252" }, { QLocale::SouthAfrica, "27" }, { QLocale::Spain, "34" }, { QLocale::SriLanka, "94" }, { QLocale::Sudan, "249" }, { QLocale::Suriname, "597" }, { QLocale::Swaziland, "268" }, { QLocale::Sweden, "46" }, { QLocale::Switzerland, "41" }, { QLocale::Syria, "963" }, { QLocale::Taiwan, "886" }, { QLocale::Tajikistan, "992" }, { QLocale::Tanzania, "255" }, { QLocale::Thailand, "66" }, { QLocale::Togo, "228" }, { QLocale::Tokelau, "690" }, { QLocale::Tonga, "676" }, { QLocale::TrinidadAndTobago, "1" }, { QLocale::Tunisia, "216" }, { QLocale::Turkey, "90" }, { QLocale::Turkmenistan, "993" }, { QLocale::TurksAndCaicosIslands, "1" }, { QLocale::Tuvalu, "688" }, { QLocale::Uganda, "256" }, { QLocale::Ukraine, "380" }, { QLocale::UnitedArabEmirates, "971" }, { QLocale::UnitedKingdom, "44" }, { QLocale::UnitedStates, "1" }, { QLocale::Uruguay, "598" }, { QLocale::Uzbekistan, "998" }, { QLocale::Vanuatu, "678" }, { QLocale::Venezuela, "58" }, { QLocale::Vietnam, "84" }, { QLocale::WallisAndFutunaIslands, "681" }, { QLocale::Yemen, "967" }, { QLocale::Zambia, "260" }, { QLocale::Zimbabwe, "263" } }; // ----------------------------------------------------------------------------- TelephoneNumbersModel::TelephoneNumbersModel (QObject *parent) : QAbstractListModel(parent) {} int TelephoneNumbersModel::rowCount (const QModelIndex &) const { return mCountryCodes.count(); } QHash TelephoneNumbersModel::roleNames () const { QHash roles; roles[Qt::DisplayRole] = "countryCode"; roles[Qt::DisplayRole+1] = "countryName"; return roles; } QVariant TelephoneNumbersModel::data (const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mCountryCodes.count()) return QVariant(); const QPair &countryCode = mCountryCodes[row]; if (role == Qt::DisplayRole) { return countryCode.second; }else if(role == Qt::DisplayRole+1) return QStringLiteral("%1 (+%2)") .arg(Utils::getCountryName(countryCode.first)) .arg(countryCode.second); return QVariant(); } int TelephoneNumbersModel::getDefaultIndex () const { QLocale::Country country = QLocale().country(); const auto it = find_if( mCountryCodes.cbegin(), mCountryCodes.cend(), [&country](const QPair &pair) { return country == pair.first; } ); return it != mCountryCodes.cend() ? int(distance(mCountryCodes.cbegin(), it)) : 0; } linphone-desktop-5.0.2/linphone-app/src/components/telephone-numbers/TelephoneNumbersModel.hpp000066400000000000000000000030401434616504300327570ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TELEPHONE_NUMBERS_MODEL_H_ #define TELEPHONE_NUMBERS_MODEL_H_ #include #include // ============================================================================= class TelephoneNumbersModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int defaultIndex READ getDefaultIndex CONSTANT) public: TelephoneNumbersModel (QObject *parent = Q_NULLPTR); int rowCount (const QModelIndex &index = QModelIndex()) const override; QHash roleNames () const override; QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; private: int getDefaultIndex () const; static const QList> mCountryCodes; }; #endif // ifndef TELEPHONE_NUMBERS_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/timeline/000077500000000000000000000000001434616504300241635ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/timeline/TimelineListModel.cpp000066400000000000000000000405201434616504300302530ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "TimelineListModel.hpp" #include "components/core/CoreManager.hpp" #include "components/core/CoreHandlers.hpp" #include "components/calls/CallsListModel.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "utils/Utils.hpp" #include "TimelineModel.hpp" #include "TimelineListModel.hpp" #include // ============================================================================= TimelineListModel::TimelineListModel (QObject *parent) : ProxyListModel(parent) { mSelectedCount = 0; CoreHandlers* coreHandlers= CoreManager::getInstance()->getHandlers().get(); connect(coreHandlers, &CoreHandlers::chatRoomRead, this, &TimelineListModel::onChatRoomRead); connect(coreHandlers, &CoreHandlers::chatRoomStateChanged, this, &TimelineListModel::onChatRoomStateChanged); connect(coreHandlers, &CoreHandlers::messagesReceived, this, &TimelineListModel::update); connect(coreHandlers, &CoreHandlers::messagesReceived, this, &TimelineListModel::updated); QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &TimelineListModel::onCallStateChanged); QObject::connect(coreHandlers, &CoreHandlers::callCreated, this, &TimelineListModel::onCallCreated); connect(CoreManager::getInstance()->getSettingsModel(), &SettingsModel::hideEmptyChatRoomsChanged, this, &TimelineListModel::update); connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultRegistrationChanged, this, &TimelineListModel::update); updateTimelines (); } TimelineListModel::TimelineListModel(const TimelineListModel* model){ mSelectedCount = model->mSelectedCount; CoreHandlers* coreHandlers= CoreManager::getInstance()->getHandlers().get(); connect(coreHandlers, &CoreHandlers::chatRoomRead, this, &TimelineListModel::onChatRoomRead); connect(coreHandlers, &CoreHandlers::chatRoomStateChanged, this, &TimelineListModel::onChatRoomStateChanged); connect(coreHandlers, &CoreHandlers::messagesReceived, this, &TimelineListModel::update); connect(coreHandlers, &CoreHandlers::messagesReceived, this, &TimelineListModel::updated); QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &TimelineListModel::onCallStateChanged); QObject::connect(coreHandlers, &CoreHandlers::callCreated, this, &TimelineListModel::onCallCreated); connect(CoreManager::getInstance()->getSettingsModel(), &SettingsModel::hideEmptyChatRoomsChanged, this, &TimelineListModel::update); connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultRegistrationChanged, this, &TimelineListModel::update); for(auto item : model->mList) { auto newItem = qobject_cast(item)->clone(); connect(newItem.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool))); connect(newItem.get(), &TimelineModel::chatRoomDeleted, this, &TimelineListModel::onChatRoomDeleted); mList << newItem; } } TimelineListModel::~TimelineListModel(){ } TimelineListModel* TimelineListModel::clone() const{ return new TimelineListModel(this); } // ----------------------------------------------------------------------------- void TimelineListModel::reset(){ updateTimelines (); } void TimelineListModel::update(){ updateTimelines (); } void TimelineListModel::selectAll(const bool& selected){ for(auto it = mList.begin() ; it != mList.end() ; ++it) it->objectCast()->setSelected(selected); } // ----------------------------------------------------------------------------- bool TimelineListModel::removeRows (int row, int count, const QModelIndex &parent) { QVector > oldTimelines; oldTimelines.reserve(count); int limit = row + count - 1; if (row < 0 || count < 0 || limit >= mList.count()) return false; beginRemoveRows(parent, row, limit); for (int i = 0; i < count; ++i){ auto timeline = mList.takeAt(row).objectCast(); timeline->disconnectChatRoomListener(); oldTimelines.push_back(timeline); } endRemoveRows(); for(auto timeline : oldTimelines) if(timeline->mSelected) { timeline->setSelected(false); } emit countChanged(); return true; } // ----------------------------------------------------------------------------- QSharedPointer TimelineListModel::getTimeline(std::shared_ptr chatRoom, const bool &create){ if(chatRoom){ for(auto it = mList.begin() ; it != mList.end() ; ++it){ auto timeline = it->objectCast(); if( timeline->getChatRoomModel()->getChatRoom() == chatRoom){ return timeline; } } if(create){ QSharedPointer model = TimelineModel::create(this, chatRoom); if(model){ connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool))); add(model); return model; } } } return nullptr; } QVariantList TimelineListModel::getLastChatRooms(const int& maxCount) const{ QVariantList contacts; QMultiMap sortedData; QDateTime currentDateTime = QDateTime::currentDateTime(); bool doTest = true; for(auto timeline : mList){ auto chatRoom = timeline.objectCast()->getChatRoomModel(); if(chatRoom && chatRoom->isCurrentAccount() && chatRoom->isOneToOne() && !chatRoom->haveEncryption()) { sortedData.insert(chatRoom->mLastUpdateTime.secsTo(currentDateTime),chatRoom); } } do{ int count = 0; for(auto contact : sortedData){ if(!doTest || Utils::hasCapability(contact->getFullPeerAddress(), LinphoneEnums::FriendCapabilityGroupChat) ) { ++count; contacts << QVariant::fromValue(contact); if(count >= maxCount) return contacts; } } doTest = false; }while( contacts.size() == 0 && sortedData.size() > 0);// no friends capability have been found : take contacts without testing capabilities. return contacts; } QSharedPointer TimelineListModel::getChatRoomModel(std::shared_ptr chatRoom, const bool& create){ if(chatRoom ){ for(auto timeline : mList){ auto model = timeline.objectCast()->mChatRoomModel; if(model->getChatRoom() == chatRoom) return model; } if(create){ QSharedPointer model = TimelineModel::create(this, chatRoom); if(model){ connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool))); add(model); return model->mChatRoomModel; } } } return nullptr; } QSharedPointer TimelineListModel::getChatRoomModel(ChatRoomModel * chatRoom){ for(auto timeline : mList){ auto model = timeline.objectCast()->mChatRoomModel; if(model == chatRoom) return model; } return nullptr; } //------------------------------------------------------------------------------------------------- void TimelineListModel::setSelectedCount(int selectedCount){ if(mSelectedCount != selectedCount) { mSelectedCount = selectedCount; if( mSelectedCount <= 1)// Do not send signals when selection is higher than max : this is a transition state emit selectedCountChanged(mSelectedCount); } } void TimelineListModel::onSelectedHasChanged(bool selected){ if( mSelectedCount == 1){//swap setSelectedCount(0); for(auto it = mList.begin() ; it != mList.end() ; ++it) if(it->get() != sender()) it->objectCast()->setSelected(false); if(!selected){ if( this == CoreManager::getInstance()->getTimelineListModel()) {// Clean memory only if the selection is about the main list. auto timeline = qobject_cast(sender()); timeline->getChatRoomModel()->resetData();// Cleanup leaving chat room } }else{ setSelectedCount(1); emit selectedChanged(qobject_cast(sender())); } }else if( mSelectedCount <1){//Select if(selected){ setSelectedCount(1); emit selectedChanged(qobject_cast(sender())); } }else{// Do nothing } } void TimelineListModel::updateTimelines () { CoreManager *coreManager = CoreManager::getInstance(); std::list> allChatRooms = coreManager->getCore()->getChatRooms(); // Clean terminated chat rooms and conferences from timeline. allChatRooms.remove_if([](std::shared_ptr chatRoom){ if( ChatRoomModel::isTerminated(chatRoom) && chatRoom->getUnreadMessagesCount() > 0) chatRoom->markAsRead(); if(chatRoom->getState() == linphone::ChatRoom::State::Deleted) return true; if(!chatRoom->hasCapability((int)linphone::ChatRoomCapabilities::Basic)){ auto conferenceAddress = chatRoom->getConferenceAddress(); if( conferenceAddress && conferenceAddress->getDomain() == Constants::LinphoneDomain) { QString conferenceAddressStr = Utils::coreStringToAppString(conferenceAddress->asStringUriOnly()); if( conferenceAddressStr.contains("conf-id")) return true; } } return false; }); //Remove no more chat rooms auto itTimeline = mList.begin(); while(itTimeline != mList.end()) { auto itDbTimeline = allChatRooms.begin(); if(*itTimeline) { auto chatRoomModel = itTimeline->objectCast()->getChatRoomModel(); if(chatRoomModel) { auto timeline = chatRoomModel->getChatRoom(); if( timeline ) { while(itDbTimeline != allChatRooms.end() && *itDbTimeline != timeline ){ ++itDbTimeline; } }else itDbTimeline = allChatRooms.end(); }else itDbTimeline = allChatRooms.end(); }else itDbTimeline = allChatRooms.end(); if( itDbTimeline == allChatRooms.end()){ int index = itTimeline - mList.begin(); if(index>0){ --itTimeline; removeRow(index);// This will call removeRows() ++itTimeline; }else{ removeRow(0);// This will call removeRows() itTimeline = mList.begin(); } }else ++itTimeline; } // Add new. // Call logs optimization : store all the list and check on it for each chat room instead of loading call logs on each chat room. See TimelineModel() std::list> callLogs = coreManager->getCore()->getCallLogs(); // for(auto dbChatRoom : allChatRooms){ auto haveTimeline = getTimeline(dbChatRoom, false); if(!haveTimeline && dbChatRoom){// Create a new Timeline if needed QSharedPointer model = TimelineModel::create(this, dbChatRoom, callLogs); if( model){ connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool))); add(model); } } } CoreManager::getInstance()->updateUnreadMessageCount(); } void TimelineListModel::add (QSharedPointer timeline){ auto chatRoomModel = timeline->getChatRoomModel(); auto chatRoom = chatRoomModel->getChatRoom(); connect(timeline.get(), &TimelineModel::chatRoomDeleted, this, &TimelineListModel::onChatRoomDeleted); connect(chatRoomModel, &ChatRoomModel::lastUpdateTimeChanged, this, &TimelineListModel::updated); ProxyListModel::add(timeline); emit layoutChanged(); emit countChanged(); } void TimelineListModel::removeChatRoomModel(QSharedPointer model){ if(!model || (model->getChatRoom()->isEmpty() && (model->isReadOnly() || !model->isGroupEnabled()))){ auto itTimeline = mList.begin(); while(itTimeline != mList.end()) { auto timeline = itTimeline->objectCast(); if(timeline->mChatRoomModel == model){ if(model) model->markAsToDelete(); remove(*itTimeline);// This will call removeRows() return; }else ++itTimeline; } } } void TimelineListModel::select(ChatRoomModel * chatRoomModel){ if(chatRoomModel) { auto timeline = getTimeline(chatRoomModel->getChatRoom(), false); if(timeline){ if(timeline->isUpdating()) timeline->delaySelected(); else timeline->setSelected(true); } } } void TimelineListModel::onChatRoomRead(const std::shared_ptr &chatRoom){ auto timeline = getTimeline(chatRoom, false); if(timeline) { if(timeline->getChatRoomModel()){ timeline->getChatRoomModel()->enableMarkAsRead(true); timeline->getChatRoomModel()->resetMessageCount(); timeline->getChatRoomModel()->enableMarkAsRead(false); } } } void TimelineListModel::onChatRoomStateChanged(const std::shared_ptr &chatRoom,linphone::ChatRoom::State state){ if( state == linphone::ChatRoom::State::Created && !getTimeline(chatRoom, false)){// Create a new Timeline if needed QSharedPointer model = TimelineModel::create(this, chatRoom); if(model){ connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(onSelectedHasChanged(bool))); add(model); } }else if(state == linphone::ChatRoom::State::Deleted || state == linphone::ChatRoom::State::Terminated){ auto timeline = getTimeline(chatRoom, false); if(timeline) { if(timeline->getChatRoomModel()) timeline->getChatRoomModel()->resetMessageCount(); if(state == linphone::ChatRoom::State::Deleted){ remove(timeline);// This will call removeRows() } } }else if(state == linphone::ChatRoom::State::CreationFailed){ auto timeline = getTimeline(chatRoom, false); if(timeline) { remove(timeline);// This will call removeRows() } } } void TimelineListModel::onCallStateChanged (const std::shared_ptr &call, linphone::Call::State state) { } void TimelineListModel::onCallCreated(const std::shared_ptr &call){ std::shared_ptr core = CoreManager::getInstance()->getCore(); std::shared_ptr params = core->createDefaultChatRoomParams(); std::list> participants; if( !call->getConference() && false ){ // Find all chat rooms with local address. If not, create one. bool isOutgoing = (call->getDir() == linphone::Call::Dir::Outgoing) ; bool found = false; auto callLog = call->getCallLog(); auto callLocalAddress = callLog->getLocalAddress()->clone(); callLocalAddress->clean(); auto currentParams = call->getCurrentParams(); bool isEncrypted = currentParams->getMediaEncryption() != linphone::MediaEncryption::None; bool createSecureChatRoom = false; SettingsModel * settingsModel = CoreManager::getInstance()->getSettingsModel(); if( settingsModel->getSecureChatEnabled() && (!settingsModel->getStandardChatEnabled() || (settingsModel->getStandardChatEnabled() && isEncrypted)) ){ params->enableEncryption(true); createSecureChatRoom = true; } participants.push_back(callLog->getRemoteAddress()->clone()); participants.back()->clean();// Not cleaning allows chatting to a specific device but the current design is not adapted to show them auto chatRoom = core->searchChatRoom(params, callLocalAddress , nullptr//callLog->getRemoteAddress() , participants); if(chatRoom){ for(auto item : mList){ auto timeline = item.objectCast(); if( chatRoom == timeline->mChatRoomModel->getChatRoom()){ found = true; if(isOutgoing)// If outgoing, we switch to this chat room timeline->setSelected(true); } } } if(!found){// Create a default chat room QVariantList participants; //participants << Utils::coreStringToAppString(callLog->getRemoteAddress()->asStringUriOnly()); // This allow chatting to a specific device but the current design is not adapted to show them auto remoteAddress = callLog->getRemoteAddress()->clone(); remoteAddress->clean(); participants << Utils::coreStringToAppString(remoteAddress->asStringUriOnly()); CoreManager::getInstance()->getCallsListModel()->createChatRoom("", (createSecureChatRoom?1:0), callLocalAddress, participants, isOutgoing); } } } void TimelineListModel::onChatRoomDeleted(){ remove(sender());// This will call removeRows() } linphone-desktop-5.0.2/linphone-app/src/components/timeline/TimelineListModel.hpp000066400000000000000000000062271434616504300302660ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TIMELINE_LIST_MODEL_H_ #define TIMELINE_LIST_MODEL_H_ #include #include #include "app/proxyModel/ProxyListModel.hpp" #include "components/chat-room/ChatRoomModel.hpp" class TimelineModel; // ============================================================================= class TimelineListModel : public ProxyListModel { Q_OBJECT public: Q_PROPERTY(int selectedCount MEMBER mSelectedCount WRITE setSelectedCount NOTIFY selectedCountChanged) Q_PROPERTY(int count READ rowCount NOTIFY countChanged) TimelineListModel (QObject *parent = Q_NULLPTR); TimelineListModel(const TimelineListModel* model); virtual ~TimelineListModel(); TimelineListModel * clone() const; void reset(); void selectAll(const bool& selected); TimelineModel * getAt(const int& index); QSharedPointer getTimeline(std::shared_ptr chatRoom, const bool &create); Q_INVOKABLE QVariantList getLastChatRooms(const int& maxCount) const; QSharedPointer getChatRoomModel(std::shared_ptr chatRoom, const bool &create); QSharedPointer getChatRoomModel(ChatRoomModel * chatRoom); void add (QSharedPointer timeline); // Use to add a timeline that is not in Linphone list (like empty chat rooms that were hide by configuration) Q_INVOKABLE void select(ChatRoomModel * chatRoomModel); void setSelectedCount(int selectedCount); int mSelectedCount; bool mAutoSelectAfterCreation = false;// Request to select the next chat room after creation public slots: void update(); void removeChatRoomModel(QSharedPointer model); void onSelectedHasChanged(bool selected); void onChatRoomRead(const std::shared_ptr &chatRoom); void onChatRoomStateChanged(const std::shared_ptr &chatRoom,linphone::ChatRoom::State state); void onCallStateChanged (const std::shared_ptr &call, linphone::Call::State state) ; void onCallCreated(const std::shared_ptr &call); void onChatRoomDeleted(); signals: void countChanged(); void selectedCountChanged(int selectedCount); void selectedChanged(TimelineModel * timelineModel); void updated(); private: virtual bool removeRows (int row, int count, const QModelIndex &parent) override; void updateTimelines(); }; #endif // TIMELINE_LIST_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/timeline/TimelineModel.cpp000066400000000000000000000361231434616504300274230ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/core/CoreManager.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "components/chat-room/ChatRoomModel.hpp" #include "components/chat-room/ChatRoomListener.hpp" #include "utils/Utils.hpp" #include "app/App.hpp" #include "TimelineModel.hpp" #include "TimelineListModel.hpp" #include "../calls/CallsListModel.hpp" #include #include #include void TimelineModel::connectTo(ChatRoomListener * listener){ connect(listener, &ChatRoomListener::isComposingReceived, this, &TimelineModel::onIsComposingReceived); connect(listener, &ChatRoomListener::messageReceived, this, &TimelineModel::onMessageReceived); connect(listener, &ChatRoomListener::messagesReceived, this, &TimelineModel::onMessagesReceived); connect(listener, &ChatRoomListener::newEvent, this, &TimelineModel::onNewEvent); connect(listener, &ChatRoomListener::chatMessageReceived, this, &TimelineModel::onChatMessageReceived); connect(listener, &ChatRoomListener::chatMessagesReceived, this, &TimelineModel::onChatMessagesReceived); connect(listener, &ChatRoomListener::chatMessageSending, this, &TimelineModel::onChatMessageSending); connect(listener, &ChatRoomListener::chatMessageSent, this, &TimelineModel::onChatMessageSent); connect(listener, &ChatRoomListener::participantAdded, this, &TimelineModel::onParticipantAdded); connect(listener, &ChatRoomListener::participantRemoved, this, &TimelineModel::onParticipantRemoved); connect(listener, &ChatRoomListener::participantAdminStatusChanged, this, &TimelineModel::onParticipantAdminStatusChanged); connect(listener, &ChatRoomListener::stateChanged, this, &TimelineModel::onStateChanged); connect(listener, &ChatRoomListener::securityEvent, this, &TimelineModel::onSecurityEvent); connect(listener, &ChatRoomListener::subjectChanged, this, &TimelineModel::onSubjectChanged); connect(listener, &ChatRoomListener::undecryptableMessageReceived, this, &TimelineModel::onUndecryptableMessageReceived); connect(listener, &ChatRoomListener::participantDeviceAdded, this, &TimelineModel::onParticipantDeviceAdded); connect(listener, &ChatRoomListener::participantDeviceRemoved, this, &TimelineModel::onParticipantDeviceRemoved); connect(listener, &ChatRoomListener::conferenceJoined, this, &TimelineModel::onConferenceJoined); connect(listener, &ChatRoomListener::conferenceLeft, this, &TimelineModel::onConferenceLeft); connect(listener, &ChatRoomListener::ephemeralEvent, this, &TimelineModel::onEphemeralEvent); connect(listener, &ChatRoomListener::ephemeralMessageTimerStarted, this, &TimelineModel::onEphemeralMessageTimerStarted); connect(listener, &ChatRoomListener::ephemeralMessageDeleted, this, &TimelineModel::onEphemeralMessageDeleted); connect(listener, &ChatRoomListener::conferenceAddressGeneration, this, &TimelineModel::onConferenceAddressGeneration); connect(listener, &ChatRoomListener::participantRegistrationSubscriptionRequested, this, &TimelineModel::onParticipantRegistrationSubscriptionRequested); connect(listener, &ChatRoomListener::participantRegistrationUnsubscriptionRequested, this, &TimelineModel::onParticipantRegistrationUnsubscriptionRequested); connect(listener, &ChatRoomListener::chatMessageShouldBeStored, this, &TimelineModel::onChatMessageShouldBeStored); connect(listener, &ChatRoomListener::chatMessageParticipantImdnStateChanged, this, &TimelineModel::onChatMessageParticipantImdnStateChanged); } // ============================================================================= QSharedPointer TimelineModel::create(TimelineListModel * mainList, std::shared_ptr chatRoom, const std::list>& callLogs, QObject *parent){ if((!chatRoom || chatRoom->getState() != linphone::ChatRoom::State::Deleted) && (!mainList || !mainList->getTimeline(chatRoom, false)) ) { QSharedPointer model = QSharedPointer::create(chatRoom,callLogs, parent); if(model && model->getChatRoomModel()){ return model; } } return nullptr; } TimelineModel::TimelineModel (std::shared_ptr chatRoom, QObject *parent) : QObject(parent) { TimelineModel(chatRoom, std::list>(), parent); } TimelineModel::TimelineModel (std::shared_ptr chatRoom, const std::list>& callLogs, QObject *parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mChatRoomModel = ChatRoomModel::create(chatRoom, callLogs); if( mChatRoomModel ){ CoreManager::getInstance()->handleChatRoomCreated(mChatRoomModel); QObject::connect(this, &TimelineModel::selectedChanged, this, &TimelineModel::updateUnreadCount); QObject::connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &TimelineModel::onDefaultAccountChanged); QObject::connect(mChatRoomModel.get(), &ChatRoomModel::chatRoomDeleted, this, &TimelineModel::onChatRoomDeleted); QObject::connect(mChatRoomModel.get(), &ChatRoomModel::updatingChanged, this, &TimelineModel::updatingChanged); QObject::connect(mChatRoomModel.get(), &ChatRoomModel::stateChanged, this, &TimelineModel::onChatRoomStateChanged); } if(chatRoom){ mChatRoomListener = std::make_shared(); connectTo(mChatRoomListener.get()); chatRoom->addListener(mChatRoomListener); } mSelected = false; } TimelineModel::TimelineModel(const TimelineModel * model){ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mChatRoomModel = model->mChatRoomModel; if( mChatRoomModel ){ QObject::connect(this, &TimelineModel::selectedChanged, this, &TimelineModel::updateUnreadCount); QObject::connect(CoreManager::getInstance()->getAccountSettingsModel(), &AccountSettingsModel::defaultAccountChanged, this, &TimelineModel::onDefaultAccountChanged); QObject::connect(mChatRoomModel.get(), &ChatRoomModel::chatRoomDeleted, this, &TimelineModel::onChatRoomDeleted); QObject::connect(mChatRoomModel.get(), &ChatRoomModel::updatingChanged, this, &TimelineModel::updatingChanged); QObject::connect(mChatRoomModel.get(), &ChatRoomModel::stateChanged, this, &TimelineModel::onChatRoomStateChanged); } if(mChatRoomModel->getChatRoom()){ mChatRoomListener = model->mChatRoomListener; connectTo(mChatRoomListener.get()); mChatRoomModel->getChatRoom()->addListener(mChatRoomListener); } mSelected = model->mSelected; } QSharedPointer TimelineModel::clone()const{ return QSharedPointer::create(this); } TimelineModel::~TimelineModel(){ if(mChatRoomModel && mChatRoomListener && mChatRoomModel->getChatRoom()) mChatRoomModel->getChatRoom()->removeListener(mChatRoomListener); } QString TimelineModel::getFullPeerAddress() const{ return mChatRoomModel->getFullPeerAddress(); } QString TimelineModel::getFullLocalAddress() const{ return mChatRoomModel->getLocalAddress(); } QString TimelineModel::getUsername() const{ return mChatRoomModel->getUsername(); } QString TimelineModel::getAvatar() const{ return ""; } int TimelineModel::getPresenceStatus() const{ return 0; } bool TimelineModel::isUpdating() const{ return !mChatRoomModel || mChatRoomModel->isUpdating(); } ChatRoomModel *TimelineModel::getChatRoomModel() const{ return mChatRoomModel.get(); } void TimelineModel::setSelected(const bool& selected){ if(mChatRoomModel && (selected != mSelected || selected)){ mSelected = selected; if(mSelected){ qInfo() << "Chat room selected : Subject :" << mChatRoomModel->getSubject() << ", Username:" << mChatRoomModel->getUsername() << ", GroupEnabled:"<< mChatRoomModel->isGroupEnabled() << ", isConference:"<< mChatRoomModel->isConference() << ", isOneToOne:"<< mChatRoomModel->isOneToOne() << ", Encrypted:"<< mChatRoomModel->haveEncryption() << ", ephemeralEnabled:" << mChatRoomModel->isEphemeralEnabled() << ", isAdmin:"<< mChatRoomModel->isMeAdmin() << ", canHandleParticipants:"<< mChatRoomModel->canHandleParticipants() << ", isReadOnly:" << mChatRoomModel->isReadOnly() << ", state:" << mChatRoomModel->getState(); } emit selectedChanged(mSelected); } } void TimelineModel::delaySelected(){ if( mChatRoomModel->getState() == LinphoneEnums::ChatRoomStateCreated){ QTimer::singleShot(200, [&](){// Delay process in order to let GUI time for Timeline building/linking before doing actions setSelected(true); }); }else mDelaySelection = true; } void TimelineModel::updateUnreadCount(){ if(!mSelected){// updateUnreadCount is called when selected has changed;: So if mSelected is false then we are going out of it. mChatRoomModel->resetMessageCount();// The reset will appear when the chat room has "mark as read enabled", that means that we should have read messages when going out. } } void TimelineModel::onDefaultAccountChanged(){ if( mSelected && !mChatRoomModel->isCurrentAccount()) setSelected(false); } void TimelineModel::disconnectChatRoomListener(){ if( mChatRoomModel && mChatRoomListener && mChatRoomModel->getChatRoom()){ mChatRoomModel->getChatRoom()->removeListener(mChatRoomListener); mChatRoomListener = nullptr; } } //---------------------------------------------------------- //------ CHAT ROOM HANDLERS //---------------------------------------------------------- void TimelineModel::onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing){ } void TimelineModel::onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} void TimelineModel::onMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & messages){} void TimelineModel::onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onChatMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & eventLogs){} void TimelineModel::onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState){ if(newState == linphone::ChatRoom::State::Created && CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation) { CoreManager::getInstance()->getTimelineListModel()->mAutoSelectAfterCreation = false; QTimer::singleShot(200, [=](){// Delay process in order to let GUI time for Timeline building/linking before doing actions setSelected(true); }); } } void TimelineModel::onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { emit usernameChanged(); } void TimelineModel::onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} void TimelineModel::onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ } void TimelineModel::onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ } void TimelineModel::onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onConferenceAddressGeneration(const std::shared_ptr & chatRoom){} void TimelineModel::onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){} void TimelineModel::onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){} void TimelineModel::onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} void TimelineModel::onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state){} void TimelineModel::onChatRoomDeleted(){ emit chatRoomDeleted(); } void TimelineModel::onChatRoomStateChanged(){ if(mDelaySelection && mChatRoomModel->getState() == LinphoneEnums::ChatRoomStateCreated){ mDelaySelection = false; setSelected(true); } }linphone-desktop-5.0.2/linphone-app/src/components/timeline/TimelineModel.hpp000066400000000000000000000164141434616504300274310ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TIMELINE_MODEL_H_ #define TIMELINE_MODEL_H_ #include // ============================================================================= #include #include #include #include #include "../contact/ContactModel.hpp" class ChatRoomModel; class ChatRoomListener; class TimelineListModel; class TimelineModel : public QObject { Q_OBJECT public: static QSharedPointer create(TimelineListModel * mainList, std::shared_ptr chatRoom, const std::list>& callLogs = std::list>(), QObject *parent = Q_NULLPTR); TimelineModel (std::shared_ptr chatRoom, QObject *parent = Q_NULLPTR); TimelineModel (std::shared_ptr chatRoom, const std::list>& callLogs, QObject *parent = Q_NULLPTR); TimelineModel(const TimelineModel * model); virtual ~TimelineModel(); QSharedPointer clone() const; Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress NOTIFY fullLocalAddressChanged) Q_PROPERTY(ChatRoomModel* chatRoomModel READ getChatRoomModel CONSTANT) Q_PROPERTY(bool selected MEMBER mSelected WRITE setSelected NOTIFY selectedChanged) Q_PROPERTY(bool updating READ isUpdating NOTIFY updatingChanged) QString getFullPeerAddress() const; QString getFullLocalAddress() const; QString getUsername() const; QString getAvatar() const; int getPresenceStatus() const; bool isUpdating() const; void setSelected(const bool& selected); void delaySelected(); Q_INVOKABLE ChatRoomModel* getChatRoomModel() const; void disconnectChatRoomListener(); bool mSelected; QSharedPointer mChatRoomModel; virtual void onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing); virtual void onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message); virtual void onMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & messages); virtual void onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onChatMessagesReceived(const std::shared_ptr & chatRoom, const std::list> & eventLogs); virtual void onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState); virtual void onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message); virtual void onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); virtual void onConferenceAddressGeneration(const std::shared_ptr & chatRoom); virtual void onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); virtual void onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); virtual void onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message); virtual void onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state); public slots: void updateUnreadCount(); void onDefaultAccountChanged(); void onChatRoomDeleted(); void onChatRoomStateChanged(); signals: void fullPeerAddressChanged(); void fullLocalAddressChanged(); void usernameChanged(); void avatarChanged(); void presenceStatusChanged(); void selectedChanged(bool selected); void conferenceLeft(); void chatRoomDeleted(); void updatingChanged(); private: bool mDelaySelection = false; void connectTo(ChatRoomListener * listener); std::shared_ptr mChatRoomListener; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/timeline/TimelineProxyModel.cpp000066400000000000000000000144271434616504300304700ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "components/core/CoreManager.hpp" #include "components/participant/ParticipantListModel.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include "utils/Utils.hpp" #include "TimelineProxyModel.hpp" #include "TimelineListModel.hpp" #include "TimelineModel.hpp" #include // ============================================================================= // ----------------------------------------------------------------------------- TimelineProxyModel::TimelineProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { } // ----------------------------------------------------------------------------- void TimelineProxyModel::unselectAll(){ if( sourceModel()) qobject_cast(sourceModel())->selectAll(false); } void TimelineProxyModel::setFilterFlags(const int& filterFlags){ if( mFilterFlags != filterFlags){ mFilterFlags = filterFlags; invalidate(); emit filterFlagsChanged(); } } void TimelineProxyModel::setFilterText(const QString& text){ if( mFilterText != text){ mFilterText = text; invalidate(); emit filterTextChanged(); } } TimelineProxyModel::TimelineListSource TimelineProxyModel::getListSource() const{ return mListSource; } void TimelineProxyModel::setListSource(const TimelineListSource& source){ if(source != mListSource) { TimelineListModel * model = nullptr; if( source != Undefined){ CoreManager *coreManager = CoreManager::getInstance(); AccountSettingsModel *accountSettingsModel = coreManager->getAccountSettingsModel(); model = source == Main ? CoreManager::getInstance()->getTimelineListModel() : CoreManager::getInstance()->getTimelineListModel()->clone(); connect(model, SIGNAL(selectedCountChanged(int)), this, SIGNAL(selectedCountChanged(int))); connect(model, &TimelineListModel::updated, this, &TimelineProxyModel::invalidate); connect(model, &TimelineListModel::selectedChanged, this, &TimelineProxyModel::selectedChanged); connect(model, &TimelineListModel::countChanged, this, &TimelineProxyModel::countChanged); QObject::connect(accountSettingsModel, &AccountSettingsModel::defaultAccountChanged, this, [this]() { qobject_cast(sourceModel())->update(); invalidate(); }); QObject::connect(coreManager->getSipAddressesModel(), &SipAddressesModel::sipAddressReset, this, [this]() { qobject_cast(sourceModel())->reset(); invalidate();// Invalidate and reload GUI if the model has been reset }); } if( mListSource != Main && sourceModel()){ sourceModel()->deleteLater(); } setSourceModel(model); sort(0); mListSource = source; emit listSourceChanged(); } } // ----------------------------------------------------------------------------- bool TimelineProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { if(!sourceModel()) return false; const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); auto timeline = sourceModel()->data(index).value(); if(!timeline || !timeline->getChatRoomModel() || timeline->getChatRoomModel()->getState() == (int)linphone::ChatRoom::State::Deleted) return false; bool haveEncryption = timeline->getChatRoomModel()->haveEncryption(); if(!CoreManager::getInstance()->getSettingsModel()->getStandardChatEnabled() && !haveEncryption) return false; if(!CoreManager::getInstance()->getSettingsModel()->getSecureChatEnabled() && haveEncryption) return false; bool show = (mFilterFlags==0);// Show all at 0 (no hide all) bool isGroup = timeline->getChatRoomModel()->isGroupEnabled(); bool isEphemeral = timeline->getChatRoomModel()->isEphemeralEnabled(); if( mFilterFlags > 0) { show = !(( ( (mFilterFlags & TimelineFilter::SimpleChatRoom) == TimelineFilter::SimpleChatRoom) && isGroup) || ( ( (mFilterFlags & TimelineFilter::SecureChatRoom) == TimelineFilter::SecureChatRoom) && !haveEncryption) || ( ( (mFilterFlags & TimelineFilter::GroupChatRoom) == TimelineFilter::GroupChatRoom) && !isGroup) || ( ( (mFilterFlags & TimelineFilter::StandardChatRoom) == TimelineFilter::StandardChatRoom) && haveEncryption) || ( ( (mFilterFlags & TimelineFilter::EphemeralChatRoom) == TimelineFilter::EphemeralChatRoom) && !isEphemeral) || ( ( (mFilterFlags & TimelineFilter::NoEphemeralChatRoom) == TimelineFilter::NoEphemeralChatRoom) && isEphemeral)); } if(show && mFilterText != ""){ QRegularExpression search(QRegularExpression::escape(mFilterText), QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); show = timeline->getChatRoomModel()->getSubject().contains(search) || timeline->getChatRoomModel()->getUsername().contains(search); //|| timeline->getChatRoomModel()->getFullPeerAddress().contains(search); not enough significant? } if(show) show = timeline->getChatRoomModel()->isCurrentAccount(); return show; } bool TimelineProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { if( !sourceModel()) return false; const TimelineModel* a = sourceModel()->data(left).value(); const TimelineModel* b = sourceModel()->data(right).value(); bool aHaveUnread = a->getChatRoomModel()->getAllUnreadCount() > 0; bool bHaveUnread = b->getChatRoomModel()->getAllUnreadCount() > 0; return (aHaveUnread && !bHaveUnread) || (aHaveUnread == bHaveUnread && a->getChatRoomModel()->mLastUpdateTime > b->getChatRoomModel()->mLastUpdateTime); } linphone-desktop-5.0.2/linphone-app/src/components/timeline/TimelineProxyModel.hpp000066400000000000000000000055511434616504300304730ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TIMELINE_PROXY_MODEL_H_ #define TIMELINE_PROXY_MODEL_H_ #include // ============================================================================= #include "../chat-room/ChatRoomModel.hpp" class TimelineModel; class TimelineProxyModel : public QSortFilterProxyModel { Q_OBJECT public: enum TimelineFilter { StandardChatRoom=1, SecureChatRoom=2, SimpleChatRoom=4, GroupChatRoom=8, EphemeralChatRoom=16, NoEphemeralChatRoom=32, AllChatRooms = 0 }; Q_ENUM(TimelineFilter) enum TimelineListSource{ Undefined, Main, // Timeline list comes from the singleton stored in CoreManager. Copy // Timeline list is created from Main but have no attach to the main list (aside of root items). }; Q_ENUM(TimelineListSource) TimelineProxyModel (QObject *parent = Q_NULLPTR); Q_PROPERTY(TimelineListSource listSource READ getListSource WRITE setListSource NOTIFY listSourceChanged) Q_PROPERTY(int filterFlags MEMBER mFilterFlags WRITE setFilterFlags NOTIFY filterFlagsChanged) Q_PROPERTY(QString filterText MEMBER mFilterText WRITE setFilterText NOTIFY filterTextChanged) Q_PROPERTY(int count READ rowCount NOTIFY countChanged) Q_INVOKABLE void unselectAll(); Q_INVOKABLE void setFilterFlags(const int& filterFlags); Q_INVOKABLE void setFilterText(const QString& text); TimelineListSource getListSource() const; void setListSource(const TimelineListSource& source); signals: void countChanged(); void selectedCountChanged(int selectedCount); void selectedChanged(TimelineModel * timelineModel); void filterFlagsChanged(); void filterTextChanged(); void listSourceChanged(); protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; QString getLocalAddress () const; QString getCleanedLocalAddress () const; void handleLocalAddressChanged (const QString &localAddress); private: int mFilterFlags = 0; QString mFilterText; TimelineListSource mListSource = Undefined; }; #endif // TIMELINE_PROXY_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/tunnel/000077500000000000000000000000001434616504300236625ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/tunnel/TunnelConfigListModel.cpp000066400000000000000000000051611434616504300306010ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "TunnelConfigListModel.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" // ============================================================================= TunnelConfigListModel::TunnelConfigListModel (std::shared_ptr tunnel, QObject *parent) : ProxyListModel(parent) { std::list> tunnelConfigs = tunnel->getServers() ; for(auto config : tunnelConfigs){ auto configModel = QSharedPointer::create(config); mList << configModel; } if( mList.size() == 0) { mList << QSharedPointer::create(linphone::Factory::get()->createTunnelConfig()); } } void TunnelConfigListModel::updateTunnelConfigs(std::shared_ptr tunnel){ std::list> tunnelConfigs = tunnel->getServers() ; beginResetModel(); mList.clear(); for(auto config : tunnelConfigs){ mList << QSharedPointer::create(config); } endResetModel(); emit layoutChanged(); } bool TunnelConfigListModel::apply(std::shared_ptr tunnel){ tunnel->cleanServers(); for(auto config : mList){ tunnel->addServer(config.objectCast()->getTunnelConfig()); } updateTunnelConfigs(tunnel); return true; } void TunnelConfigListModel::addTunnelConfig(){ int row = rowCount(); beginInsertRows(QModelIndex(),row,row); mList << QSharedPointer::create(linphone::Factory::get()->createTunnelConfig()); endInsertRows(); } void TunnelConfigListModel::removeTunnelConfig(std::shared_ptr tunnel, TunnelConfigModel * model){ int row = 0; while(row < mList.size() && mList[row].get() != model) ++row; if( row < mList.size()) { removeRow(row); tunnel->removeServer(model->getTunnelConfig()); } } linphone-desktop-5.0.2/linphone-app/src/components/tunnel/TunnelConfigListModel.hpp000066400000000000000000000031321434616504300306020ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TUNNEL_CONFIG_LIST_MODEL_H_ #define TUNNEL_CONFIG_LIST_MODEL_H_ #include // ============================================================================= #include #include #include #include "app/proxyModel/ProxyListModel.hpp" class TunnelConfigModel; class TunnelConfigListModel : public ProxyListModel { Q_OBJECT public: TunnelConfigListModel (std::shared_ptr tunnel, QObject *parent = nullptr); void updateTunnelConfigs(std::shared_ptr tunnel); bool apply(std::shared_ptr tunnel); void addTunnelConfig(); void removeTunnelConfig(std::shared_ptr tunnel, TunnelConfigModel * model); }; Q_DECLARE_METATYPE(QSharedPointer) #endif // TUNNEL_CONFIG_LIST_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/tunnel/TunnelConfigModel.cpp000066400000000000000000000052361434616504300277500ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "TunnelConfigModel.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" // ============================================================================= using namespace std; TunnelConfigModel::TunnelConfigModel (shared_ptr tunnelConfig, QObject *parent) : QObject(parent) { mTunnelConfig = tunnelConfig; } // ----------------------------------------------------------------------------- QString TunnelConfigModel::getHost() const{ return Utils::coreStringToAppString(mTunnelConfig->getHost()); } QString TunnelConfigModel::getHost2() const{ return Utils::coreStringToAppString(mTunnelConfig->getHost2()); } int TunnelConfigModel::getPort() const{ return mTunnelConfig->getPort(); } int TunnelConfigModel::getPort2() const{ return mTunnelConfig->getPort2(); } int TunnelConfigModel::getRemoteUdpMirrorPort() const{ return mTunnelConfig->getRemoteUdpMirrorPort(); } int TunnelConfigModel::getDelay() const{ return mTunnelConfig->getDelay(); } void TunnelConfigModel::setHost(const QString& host){ mTunnelConfig->setHost(Utils::appStringToCoreString(host)); emit hostChanged(); } void TunnelConfigModel::setHost2(const QString& host){ mTunnelConfig->setHost2(Utils::appStringToCoreString(host)); emit host2Changed(); } void TunnelConfigModel::setPort(const int& port){ mTunnelConfig->setPort(port); emit portChanged(); } void TunnelConfigModel::setPort2(const int& port){ mTunnelConfig->setPort2(port); emit port2Changed(); } void TunnelConfigModel::setRemoteUdpMirrorPort(const int& port){ mTunnelConfig->setRemoteUdpMirrorPort(port); emit remoteUdpMirrorPortChanged(); } void TunnelConfigModel::setDelay(const int& delay){ mTunnelConfig->setDelay(delay); emit delayChanged(); } std::shared_ptr TunnelConfigModel::getTunnelConfig(){ return mTunnelConfig; } linphone-desktop-5.0.2/linphone-app/src/components/tunnel/TunnelConfigModel.hpp000066400000000000000000000045651434616504300277610ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TUNNEL_CONFIG_MODEL_H_ #define TUNNEL_CONFIG_MODEL_H_ #include // ============================================================================= #include #include class TunnelConfigModel : public QObject { Q_OBJECT public: TunnelConfigModel (std::shared_ptr config, QObject *parent = nullptr); Q_PROPERTY(QString host READ getHost WRITE setHost NOTIFY hostChanged) Q_PROPERTY(QString host2 READ getHost2 WRITE setHost2 NOTIFY host2Changed) Q_PROPERTY(int port READ getPort WRITE setPort NOTIFY portChanged) Q_PROPERTY(int port2 READ getPort2 WRITE setPort2 NOTIFY port2Changed) Q_PROPERTY(int remoteUdpMirrorPort READ getRemoteUdpMirrorPort WRITE setRemoteUdpMirrorPort NOTIFY remoteUdpMirrorPortChanged) Q_PROPERTY(int delay READ getDelay WRITE setDelay NOTIFY delayChanged) QString getHost() const; QString getHost2() const; int getPort() const; int getPort2() const; int getRemoteUdpMirrorPort() const; int getDelay() const; void setHost(const QString& host); void setHost2(const QString& host); void setPort(const int& port); void setPort2(const int& port); void setRemoteUdpMirrorPort(const int& port); void setDelay(const int& delay); std::shared_ptr getTunnelConfig(); signals: void hostChanged(); void host2Changed(); void portChanged(); void port2Changed(); void remoteUdpMirrorPortChanged(); void delayChanged(); private: std::shared_ptr mTunnelConfig; }; Q_DECLARE_METATYPE(QSharedPointer) #endif // TUNNEL_CONFIG_MODEL linphone-desktop-5.0.2/linphone-app/src/components/tunnel/TunnelConfigProxyModel.cpp000066400000000000000000000036161434616504300310120ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "TunnelConfigProxyModel.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" #include "TunnelConfigListModel.hpp" // ============================================================================= TunnelConfigProxyModel::TunnelConfigProxyModel (QObject *parent) : QSortFilterProxyModel(parent){ } TunnelConfigProxyModel::~TunnelConfigProxyModel() { } bool TunnelConfigProxyModel::filterAcceptsRow ( int sourceRow, const QModelIndex &sourceParent ) const { Q_UNUSED(sourceRow) Q_UNUSED(sourceParent) return true; } bool TunnelConfigProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { const TunnelConfigModel *deviceA = sourceModel()->data(left).value(); const TunnelConfigModel *deviceB = sourceModel()->data(right).value(); return deviceA->getHost() > deviceB->getHost(); } //--------------------------------------------------------------------------------- void TunnelConfigProxyModel::setTunnel(TunnelModel * tunnel){ setSourceModel(tunnel->getTunnelConfigs().get()); }linphone-desktop-5.0.2/linphone-app/src/components/tunnel/TunnelConfigProxyModel.hpp000066400000000000000000000031101434616504300310040ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TUNNEL_CONFIG_PROXY_MODEL_H_ #define TUNNEL_CONFIG_PROXY_MODEL_H_ #include // ============================================================================= #include #include #include #include class TunnelConfigListModel; class TunnelModel; class TunnelConfigProxyModel : public QSortFilterProxyModel { Q_OBJECT public: TunnelConfigProxyModel (QObject *parent = nullptr); virtual ~TunnelConfigProxyModel(); void setTunnel(TunnelModel * tunnel); protected: virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; std::shared_ptr mTunnelConfigs; }; #endif linphone-desktop-5.0.2/linphone-app/src/components/tunnel/TunnelModel.cpp000066400000000000000000000076361434616504300266300ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "app/App.hpp" #include "TunnelModel.hpp" #include "utils/Utils.hpp" #include "components/Components.hpp" // ============================================================================= using namespace std; TunnelModel::TunnelModel (shared_ptr tunnel, QObject *parent) : QObject(parent) { App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE mTunnel = tunnel; if(mTunnel){ mTunnelConfigs = std::make_shared(mTunnel); } } TunnelModel::~TunnelModel(){ } // ----------------------------------------------------------------------------- QString TunnelModel::getDomain() const{ return mTunnel ? Utils::coreStringToAppString(mTunnel->getDomain()) : ""; } QString TunnelModel::getUsername() const{ return mTunnel ? Utils::coreStringToAppString(mTunnel->getUsername()) : ""; } bool TunnelModel::getDualModeEnabled() const{ return mTunnel ? mTunnel->dualModeEnabled() : false; } LinphoneEnums::TunnelMode TunnelModel::getMode() const{ return mTunnel ? LinphoneEnums::fromLinphone(mTunnel->getMode()) : LinphoneEnums::TunnelMode::TunnelModeDisable; } bool TunnelModel::getSipEnabled() const{ return mTunnel ? mTunnel->sipEnabled() : false; } // ----------------------------------------------------------------------------- void TunnelModel::setDomain(const QString& data){ if(mTunnel) mTunnel->setDomain(Utils::appStringToCoreString(data)); emit domainChanged(); } void TunnelModel::setUsername(const QString& data){ if(mTunnel) mTunnel->setUsername(Utils::appStringToCoreString(data)); emit usernameChanged(); } void TunnelModel::setDualModeEnabled(const bool& data){ if(mTunnel) mTunnel->enableDualMode(data); emit dualModeEnabledChanged(); } void TunnelModel::setMode(const LinphoneEnums::TunnelMode& data){ if(mTunnel) mTunnel->setMode(LinphoneEnums::toLinphone(data)); emit modeChanged(); } void TunnelModel::setSipEnabled(const bool& data){ if(mTunnel) mTunnel->enableSip(data); emit sipEnabledChanged(); } // ----------------------------------------------------------------------------- std::shared_ptr TunnelModel::getTunnel(){ return mTunnel; } TunnelConfigProxyModel * TunnelModel::getTunnelProxyConfigs(){ TunnelConfigProxyModel * configs = new TunnelConfigProxyModel(); configs->setTunnel(this); return configs; } std::shared_ptr TunnelModel::getTunnelConfigs(){ return mTunnelConfigs; } bool TunnelModel::apply(){ return mTunnelConfigs->apply(mTunnel); } void TunnelModel::addTunnelConfig(){ mTunnelConfigs->addTunnelConfig(); } void TunnelModel::removeTunnelConfig(TunnelConfigModel * model){ mTunnelConfigs->removeTunnelConfig(mTunnel, model); } bool TunnelModel::getActivated()const{ if(mTunnel) return mTunnel->getActivated(); else return false; } void TunnelModel::setHttpProxy(const QString& host, int port, const QString& username, const QString& passwd){ if(mTunnel) mTunnel->setHttpProxy(Utils::appStringToCoreString(host), port, Utils::appStringToCoreString(username), Utils::appStringToCoreString(passwd)); }linphone-desktop-5.0.2/linphone-app/src/components/tunnel/TunnelModel.hpp000066400000000000000000000056431434616504300266310ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef TUNNEL_MODEL_H_ #define TUNNEL_MODEL_H_ #include "utils/LinphoneEnums.hpp" #include // ============================================================================= #include #include #include class TunnelConfigListModel; class TunnelConfigProxyModel; class TunnelConfigModel; class TunnelModel : public QObject { Q_OBJECT public: TunnelModel (std::shared_ptr linphoneTunnel, QObject *parent = nullptr); virtual ~TunnelModel(); Q_PROPERTY(QString domain READ getDomain WRITE setDomain NOTIFY domainChanged) Q_PROPERTY(QString username READ getUsername WRITE setUsername NOTIFY usernameChanged) Q_PROPERTY(bool dualModeEnabled READ getDualModeEnabled WRITE setDualModeEnabled NOTIFY dualModeEnabledChanged) Q_PROPERTY(LinphoneEnums::TunnelMode mode READ getMode WRITE setMode NOTIFY modeChanged) Q_PROPERTY(bool sipEnabled READ getSipEnabled WRITE setSipEnabled NOTIFY sipEnabledChanged) QString getDomain() const; QString getUsername() const; bool getDualModeEnabled() const; LinphoneEnums::TunnelMode getMode() const; bool getSipEnabled() const; void setDomain(const QString& data); void setUsername(const QString& data); void setDualModeEnabled(const bool& data); void setMode(const LinphoneEnums::TunnelMode& data); void setSipEnabled(const bool& data); std::shared_ptr getTunnel(); Q_INVOKABLE TunnelConfigProxyModel * getTunnelProxyConfigs(); std::shared_ptr getTunnelConfigs(); Q_INVOKABLE bool apply(); Q_INVOKABLE void addTunnelConfig(); Q_INVOKABLE void removeTunnelConfig(TunnelConfigModel * model); Q_INVOKABLE bool getActivated()const; Q_INVOKABLE void setHttpProxy(const QString& host, int port, const QString& username, const QString& passwd); signals: void domainChanged(); void usernameChanged(); void sipAddressChanged(); void dualModeEnabledChanged(); void modeChanged(); void sipEnabledChanged(); private: std::shared_ptr mTunnel; std::shared_ptr mTunnelConfigs; }; Q_DECLARE_METATYPE(std::shared_ptr); #endif // TUNNEL_MODEL_H_ linphone-desktop-5.0.2/linphone-app/src/components/url-handlers/000077500000000000000000000000001434616504300247555ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/components/url-handlers/UrlHandlers.cpp000066400000000000000000000024101434616504300277010ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "components/sip-addresses/SipAddressesModel.hpp" #include "UrlHandlers.hpp" // ============================================================================= UrlHandlers::UrlHandlers (QObject *parent) : QObject(parent) { QDesktopServices::setUrlHandler("sip", this, "handleSip"); QDesktopServices::setUrlHandler("sips", this, "handleSip"); } void UrlHandlers::handleSip (const QUrl &url) { emit sip(SipAddressesModel::interpretSipAddress(url)); } linphone-desktop-5.0.2/linphone-app/src/components/url-handlers/UrlHandlers.hpp000066400000000000000000000022331434616504300277110ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef URL_HANDLERS_H_ #define URL_HANDLERS_H_ #include // ============================================================================= class UrlHandlers : public QObject { Q_OBJECT; public: UrlHandlers (QObject *parent = Q_NULLPTR); public slots: void handleSip (const QUrl &url); signals: void sip (const QString &sipAddress); }; #endif // URL_HANDLERS_H_ linphone-desktop-5.0.2/linphone-app/src/config.h.cmake000066400000000000000000000032141434616504300226650ustar00rootroot00000000000000/******************************************************************************* * config.h.cmake * Copyright (C) 2017-2018 Belledonne Communications, Grenoble France * ******************************************************************************** * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * *******************************************************************************/ #cmakedefine APPLICATION_DESCRIPTION "${APPLICATION_DESCRIPTION}" #cmakedefine APPLICATION_ID "${APPLICATION_ID}" #cmakedefine APPLICATION_NAME "${APPLICATION_NAME}" #cmakedefine APPLICATION_VENDOR "${APPLICATION_VENDOR}" #cmakedefine APPLICATION_URL "${APPLICATION_URL}" #cmakedefine APPLICATION_LICENCE "${APPLICATION_LICENCE}" #cmakedefine APPLICATION_SEMVER "${APPLICATION_SEMVER}" #cmakedefine COPYRIGHT_RANGE_DATE "${COPYRIGHT_RANGE_DATE}" #cmakedefine ENABLE_UPDATE_CHECK 1 #cmakedefine EXECUTABLE_NAME "${EXECUTABLE_NAME}" #cmakedefine MSPLUGINS_DIR "${MSPLUGINS_DIR}" #cmakedefine ENABLE_APP_WEBVIEW "${ENABLE_APP_WEBVIEW}" linphone-desktop-5.0.2/linphone-app/src/utils/000077500000000000000000000000001434616504300213305ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/utils/Constants.cpp000066400000000000000000000107211434616504300240110ustar00rootroot00000000000000#include "Constants.hpp" constexpr char Constants::WindowIconPath[]; constexpr char Constants::DefaultLocale[]; constexpr char Constants::LanguagePath[]; // The main windows of Linphone desktop. constexpr char Constants::QmlViewMainWindow[]; constexpr char Constants::QmlViewCallsWindow[]; constexpr char Constants::QmlViewSettingsWindow[]; #ifdef ENABLE_UPDATE_CHECK constexpr int Constants::VersionUpdateCheckInterval; #endif // ifdef ENABLE_UPDATE_CHECK constexpr char Constants::MainQmlUri[]; constexpr char Constants::AttachVirtualWindowMethodName[]; constexpr char Constants::AboutPath[]; constexpr char Constants::AssistantViewName[]; constexpr char Constants::ApplicationMinimalQtVersion[]; constexpr char Constants::DefaultFont[]; constexpr char Constants::QtDomain[]; constexpr size_t Constants::MaxLogsCollectionSize; constexpr char Constants::SrcPattern[]; constexpr char Constants::LinphoneLocaleEncoding[]; constexpr char Constants::PathAssistantConfig[]; constexpr char Constants::PathAvatars[]; constexpr char Constants::PathCaptures[]; constexpr char Constants::PathCodecs[]; constexpr char Constants::PathData[]; constexpr char Constants::PathTools[]; constexpr char Constants::PathLogs[]; #ifdef APPLE constexpr char Constants::PathPlugins[]; #else constexpr char Constants::PathPlugins[]; #endif constexpr char Constants::PathPluginsApp[]; constexpr char Constants::PathSounds[]; constexpr char Constants::PathThumbnails[]; constexpr char Constants::PathUserCertificates[]; constexpr char Constants::PathCallHistoryList[]; constexpr char Constants::PathConfig[]; constexpr char Constants::PathDatabase[]; constexpr char Constants::PathFactoryConfig[]; constexpr char Constants::PathRootCa[]; constexpr char Constants::PathFriendsList[]; constexpr char Constants::PathLimeDatabase[]; constexpr char Constants::PathMessageHistoryList[]; constexpr char Constants::PathZrtpSecrets[]; // Max image size in bytes. (100Kb) constexpr qint64 Constants::MaxImageSize; constexpr int Constants::ThumbnailImageFileWidth; constexpr int Constants::ThumbnailImageFileHeight; // In Bytes. constexpr qint64 Constants::FileSizeLimit; constexpr char Constants::DefaultXmlrpcUri[]; constexpr char Constants::DefaultUploadLogsServer[]; constexpr char Constants::DefaultConferenceURI[]; constexpr char Constants::DefaultVideoConferenceURI[]; constexpr char Constants::DefaultLimeServerURL[]; constexpr char Constants::DefaultFlexiAPIURL[]; constexpr char Constants::RemoteProvisioningURL[]; constexpr char Constants::DefaultAssistantRegistrationUrl[]; constexpr char Constants::DefaultAssistantLoginUrl[]; constexpr char Constants::DefaultAssistantLogoutUrl[]; #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) constexpr char Constants::H264Description[]; #endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN) #ifdef Q_OS_LINUX constexpr char Constants::LibraryExtension[]; constexpr char Constants::H264InstallName[]; #ifdef Q_PROCESSOR_X86_64 constexpr char Constants::PluginUrlH264[]; constexpr char Constants::PluginH264Check[]; #else constexpr char Constants::PluginUrlH264[]; constexpr char Constants::PluginH264Check[]; #endif // ifdef Q_PROCESSOR_X86_64 #elif defined(Q_OS_WIN) constexpr char Constants::LibraryExtension[]; constexpr char Constants::H264InstallName[]; #ifdef Q_OS_WIN64 constexpr char Constants::PluginUrlH264[]; constexpr char Constants::PluginH264Check[]; #else constexpr char Constants::PluginUrlH264[]; constexpr char Constants::PluginH264Check[]; #endif // ifdef Q_OS_WIN64 #endif // ifdef Q_OS_LINUX constexpr char Constants::VcardScheme[]; constexpr int Constants::CbsCallInterval; constexpr char Constants::RcVersionName[]; constexpr int Constants::RcVersionCurrent; // TODO: Remove hardcoded values. Use config directly. constexpr char Constants::LinphoneDomain[]; constexpr char Constants::DefaultContactParameters[]; constexpr char Constants::DefaultContactParametersOnRemove[]; constexpr int Constants::DefaultExpires; constexpr char Constants::DownloadUrl[]; constexpr char Constants::VersionCheckReleaseUrl[]; constexpr char Constants::VersionCheckNightlyUrl[]; constexpr char Constants::PasswordRecoveryUrl[]; constexpr char Constants::CguUrl[]; constexpr char Constants::PrivatePolicyUrl[]; constexpr char Constants::ContactUrl[]; constexpr char Constants::TranslationUrl[]; constexpr int Constants::MaxMosaicParticipants; constexpr char Constants::LinphoneBZip2_exe[]; constexpr char Constants::LinphoneBZip2_dll[]; constexpr char Constants::DefaultRlsUri[]; constexpr char Constants::DefaultLogsEmail[]; linphone-desktop-5.0.2/linphone-app/src/utils/Constants.hpp000066400000000000000000000224011434616504300240140ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef CONSTANTS_H_ #define CONSTANTS_H_ #include #include #include #include "config.h" // ============================================================================= class Constants : public QObject{ Q_OBJECT public: Constants(QObject * parent = nullptr) : QObject(parent){} //---------------------------------------------------------------------------------- static constexpr char DefaultLocale[] = "en"; static constexpr char DefaultFont[] = "Noto Sans"; static constexpr size_t MaxLogsCollectionSize = 10485760*5; // 50MB. #ifdef ENABLE_UPDATE_CHECK static constexpr int VersionUpdateCheckInterval = 86400000; // 24 hours in milliseconds. #endif // ifdef ENABLE_UPDATE_CHECK static constexpr char DefaultXmlrpcUri[] = "https://subscribe.linphone.org:444/wizard.php"; static constexpr char DefaultUploadLogsServer[] = "https://www.linphone.org:444/lft.php"; static constexpr char DefaultContactParameters[] = "message-expires=604800"; static constexpr char DefaultContactParametersOnRemove[] = "message-expires=0"; static constexpr int DefaultExpires = 3600; static constexpr char DownloadUrl[] = "https://www.linphone.org/technical-corner/linphone"; static constexpr char VersionCheckReleaseUrl[] = "https://linphone.org/releases"; static constexpr char VersionCheckNightlyUrl[] = "https://linphone.org/snapshots"; static constexpr char PasswordRecoveryUrl[] = "https://subscribe.linphone.org/login"; static constexpr char CguUrl[] = "https://www.linphone.org/general-terms"; static constexpr char PrivatePolicyUrl[] = "https://www.linphone.org/privacy-policy"; static constexpr char ContactUrl[] = "https://www.linphone.org/contact"; static constexpr char TranslationUrl[] = "https://weblate.linphone.org/projects/linphone-desktop/"; static constexpr int MaxMosaicParticipants = 6;// From 7, the mosaic quality will be limited to avoid useless computations static constexpr char LinphoneBZip2_exe[] = "https://www.linphone.org/releases/windows/tools/bzip2/bzip2.exe"; static constexpr char LinphoneBZip2_dll[] = "https://www.linphone.org/releases/windows/tools/bzip2/bzip2.dll"; static constexpr char DefaultRlsUri[] = "sips:rls@sip.linphone.org"; static constexpr char DefaultLogsEmail[] = "linphone-desktop@belledonne-communications.com"; static constexpr char DefaultFlexiAPIURL[] = "http://fs-test-sandbox.linphone.org/flexiapi/api/";// Need "/" at the end static constexpr char RemoteProvisioningURL[] = "http://fs-test-sandbox.linphone.org/flexiapi/provisioning"; Q_PROPERTY(QString PasswordRecoveryUrl MEMBER PasswordRecoveryUrl CONSTANT) Q_PROPERTY(QString CguUrl MEMBER CguUrl CONSTANT) Q_PROPERTY(QString PrivatePolicyUrl MEMBER PrivatePolicyUrl CONSTANT) Q_PROPERTY(QString ContactUrl MEMBER ContactUrl CONSTANT) Q_PROPERTY(QString TranslationUrl MEMBER TranslationUrl CONSTANT) Q_PROPERTY(int maxMosaicParticipants MEMBER MaxMosaicParticipants CONSTANT) // For Webviews static constexpr char DefaultAssistantRegistrationUrl[] = "https://subscribe.linphone.org/register"; static constexpr char DefaultAssistantLoginUrl[] = "https://subscribe.linphone.org/login"; static constexpr char DefaultAssistantLogoutUrl[] = "https://subscribe.linphone.org/logout"; //-------------- // Max image size in bytes. (100Kb) static constexpr qint64 MaxImageSize = 102400;// In Bytes. static constexpr qint64 FileSizeLimit = 524288000;// In Bytes. static constexpr int ThumbnailImageFileWidth = 100; static constexpr int ThumbnailImageFileHeight = 100; //-------------------------------------------------------------------------------- // LINPHONE //-------------------------------------------------------------------------------- static constexpr char LinphoneDomain[] = "sip.linphone.org"; // Use for checking if config are a Linphone static constexpr char WindowIconPath[] = ":/assets/images/linphone_logo.svg"; static constexpr char ApplicationMinimalQtVersion[] = "5.10.0"; static constexpr char DefaultConferenceURI[] = "sip:conference-factory@sip.linphone.org"; // Default for a Linphone account static constexpr char DefaultVideoConferenceURI[] = "sip:videoconference-factory@sip.linphone.org"; // Default for a Linphone account static constexpr char DefaultLimeServerURL[] = "https://lime.linphone.org/lime-server/lime-server.php"; // Default for a Linphone account static constexpr char PathAssistantConfig[] = "/" EXECUTABLE_NAME "/assistant/"; static constexpr char PathAvatars[] = "/avatars/"; static constexpr char PathCaptures[] = "/" EXECUTABLE_NAME "/captures/"; static constexpr char PathCodecs[] = "/codecs/"; static constexpr char PathData[] = "/" EXECUTABLE_NAME; static constexpr char PathTools[] = "/tools/"; static constexpr char PathLogs[] = "/logs/"; #ifdef APPLE static constexpr char PathPlugins[] = "/Plugins/"; #else static constexpr char PathPlugins[] = "/plugins/"; #endif static constexpr char PathPluginsApp[] = "app/"; static constexpr char PathSounds[] = "/sounds/" EXECUTABLE_NAME; static constexpr char PathThumbnails[] = "/thumbnails/"; static constexpr char PathUserCertificates[] = "/usr-crt/"; static constexpr char PathCallHistoryList[] = "/call-history.db"; static constexpr char PathConfig[] = "/linphonerc"; static constexpr char PathDatabase[] = "/linphone.db"; static constexpr char PathFactoryConfig[] = "/" EXECUTABLE_NAME "/linphonerc-factory"; static constexpr char PathRootCa[] = "/" EXECUTABLE_NAME "/rootca.pem"; static constexpr char PathFriendsList[] = "/friends.db"; static constexpr char PathLimeDatabase[] = "/x3dh.c25519.sqlite3"; static constexpr char PathMessageHistoryList[] = "/message-history.db"; static constexpr char PathZrtpSecrets[] = "/zidcache"; static constexpr char LanguagePath[] = ":/languages/"; // The main windows of Linphone desktop. static constexpr char QmlViewMainWindow[] = "qrc:/ui/views/App/Main/MainWindow.qml"; static constexpr char QmlViewCallsWindow[] = "qrc:/ui/views/App/Calls/CallsWindow.qml"; static constexpr char QmlViewSettingsWindow[] = "qrc:/ui/views/App/Settings/SettingsWindow.qml"; static constexpr char MainQmlUri[] = "Linphone"; static constexpr char AttachVirtualWindowMethodName[] = "attachVirtualWindow"; static constexpr char AboutPath[] = "qrc:/ui/views/App/Main/Dialogs/About.qml"; static constexpr char AssistantViewName[] = "Assistant"; static constexpr char QtDomain[] = "qt"; static constexpr char SrcPattern[] = "/src/"; static constexpr char LinphoneLocaleEncoding[] = "UTF-8";// Alternative is to use "locale" static constexpr char VcardScheme[] = EXECUTABLE_NAME "-desktop:/"; static constexpr int CbsCallInterval = 20; static constexpr char RcVersionName[] = "rc_version"; static constexpr int RcVersionCurrent = 5; // 2 = Conference URI // 3 = CPIM on basic chat rooms // 4 = RTP bundle mode // 5 = Video Conference URI //-------------------------------------------------------------------------------- // CISCO //-------------------------------------------------------------------------------- #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) static constexpr char H264Description[] = "Provided by CISCO SYSTEM,INC"; #endif // if defined(Q_OS_LINUX) || defined(Q_OS_WIN) #ifdef Q_OS_LINUX static constexpr char LibraryExtension[] = "so"; static constexpr char H264InstallName[] = "libopenh264.so"; #ifdef Q_PROCESSOR_X86_64 static constexpr char PluginUrlH264[] = "http://ciscobinary.openh264.org/libopenh264-2.2.0-linux64.6.so.bz2"; static constexpr char PluginH264Check[] = "45ba1aaeb6213c19cd9622b79788e16b05beabc4d16a3a74e57f046a0826fd77"; #else static constexpr char PluginUrlH264[] = "http://ciscobinary.openh264.org/libopenh264-2.2.0-linux32.6.so.bz2"; static constexpr char PluginH264Check[] = "bf18e0e79c4a23018b0ea5ad6d7dd14fd1b6a6189d2f88fd56dece019fc415c8"; #endif // ifdef Q_PROCESSOR_X86_64 #elif defined(Q_OS_WIN) static constexpr char LibraryExtension[] = "dll"; static constexpr char H264InstallName[] = "openh264.dll"; #ifdef Q_OS_WIN64 static constexpr char PluginUrlH264[] = "http://ciscobinary.openh264.org/openh264-2.2.0-win64.dll.bz2"; static constexpr char PluginH264Check[] = "799e08c418b6cdeadfbe18d027392158face4a5c901d41f83712a20f0d41ad7d"; #else static constexpr char PluginUrlH264[] = "http://ciscobinary.openh264.org/openh264-2.2.0-win32.dll.bz2"; static constexpr char PluginH264Check[] = "2205097a3a309271e15879b25a905eb290cfdd7fd7a8a0c1037e0458e5dc1f21"; #endif // ifdef Q_OS_WIN64 #endif // ifdef Q_OS_LINUX //-------------------------------------------------------------------------------- }; #endif linphone-desktop-5.0.2/linphone-app/src/utils/LinphoneEnums.cpp000066400000000000000000000147641434616504300246340ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "LinphoneEnums.hpp" // ============================================================================= void LinphoneEnums::registerMetaTypes(){ qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); } linphone::MediaEncryption LinphoneEnums::toLinphone(const LinphoneEnums::MediaEncryption& data){ return static_cast(data); } LinphoneEnums::MediaEncryption LinphoneEnums::fromLinphone(const linphone::MediaEncryption& data){ return static_cast(data); } linphone::FriendCapability LinphoneEnums::toLinphone(const LinphoneEnums::FriendCapability& data){ return static_cast(data); } LinphoneEnums::FriendCapability LinphoneEnums::fromLinphone(const linphone::FriendCapability& data){ return static_cast(data); } linphone::EventLog::Type LinphoneEnums::toLinphone(const LinphoneEnums::EventLogType& data){ return static_cast(data); } LinphoneEnums::EventLogType LinphoneEnums::fromLinphone(const linphone::EventLog::Type& data){ return static_cast(data); } linphone::ChatMessage::State LinphoneEnums::toLinphone(const LinphoneEnums::ChatMessageState& data){ return static_cast(data); } LinphoneEnums::ChatMessageState LinphoneEnums::fromLinphone(const linphone::ChatMessage::State& data){ return static_cast(data); } linphone::ChatRoom::State LinphoneEnums::toLinphone(const LinphoneEnums::ChatRoomState& data){ return static_cast(data); } LinphoneEnums::ChatRoomState LinphoneEnums::fromLinphone(const linphone::ChatRoom::State& data){ return static_cast(data); } linphone::Call::Status LinphoneEnums::toLinphone(const LinphoneEnums::CallStatus& data){ return static_cast(data); } LinphoneEnums::CallStatus LinphoneEnums::fromLinphone(const linphone::Call::Status& data){ return static_cast(data); } linphone::ConferenceLayout LinphoneEnums::toLinphone(const LinphoneEnums::ConferenceLayout& layout){ if( layout != LinphoneEnums::ConferenceLayoutAudioOnly) return static_cast(layout); else return linphone::ConferenceLayout::Grid;// Audio Only mode } LinphoneEnums::ConferenceLayout LinphoneEnums::fromLinphone(const linphone::ConferenceLayout& layout){ return static_cast(layout); } linphone::ConferenceInfo::State LinphoneEnums::toLinphone(const LinphoneEnums::ConferenceInfoState& state){ return static_cast(state); } LinphoneEnums::ConferenceInfoState LinphoneEnums::fromLinphone(const linphone::ConferenceInfo::State& state){ return static_cast(state); } linphone::ConferenceScheduler::State LinphoneEnums::toLinphone(const LinphoneEnums::ConferenceSchedulerState& state){ return static_cast(state); } LinphoneEnums::ConferenceSchedulerState LinphoneEnums::fromLinphone(const linphone::ConferenceScheduler::State& state){ return static_cast(state); } linphone::ParticipantDeviceState LinphoneEnums::toLinphone(const LinphoneEnums::ParticipantDeviceState& state){ return static_cast(state); } LinphoneEnums::ParticipantDeviceState LinphoneEnums::fromLinphone(const linphone::ParticipantDeviceState& state){ return static_cast(state); } linphone::Tunnel::Mode LinphoneEnums::toLinphone(const LinphoneEnums::TunnelMode& data){ return static_cast(data); } LinphoneEnums::TunnelMode LinphoneEnums::fromLinphone(const linphone::Tunnel::Mode& data){ return static_cast(data); } linphone::RecorderState LinphoneEnums::toLinphone(const LinphoneEnums::RecorderState& data){ return static_cast(data); } LinphoneEnums::RecorderState LinphoneEnums::fromLinphone(const linphone::RecorderState& data){ return static_cast(data); } linphone::TransportType LinphoneEnums::toLinphone(const LinphoneEnums::TransportType& type){ return static_cast(type); } LinphoneEnums::TransportType LinphoneEnums::fromLinphone(const linphone::TransportType& type){ return static_cast(type); } QString LinphoneEnums::toString(const LinphoneEnums::TransportType& type){ switch(type) { case TransportTypeTcp: return "TCP"; case TransportTypeUdp: return "UDP"; case TransportTypeTls: return "TLS"; case TransportTypeDtls: return "DTLS"; } } void LinphoneEnums::fromString(const QString& transportType, LinphoneEnums::TransportType *transport){ if (transportType.toUpper() == QLatin1String("TCP")) *transport = TransportTypeTcp; else if (transportType.toUpper() == QLatin1String("UDP")) *transport = TransportTypeUdp; else if (transportType.toUpper() == QLatin1String("TLS")) *transport = TransportTypeTls; else *transport = TransportTypeDtls; } linphone-desktop-5.0.2/linphone-app/src/utils/LinphoneEnums.hpp000066400000000000000000000264511434616504300246350ustar00rootroot00000000000000/* * Copyright (c) 2021 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef LINPHONE_ENUMS_H_ #define LINPHONE_ENUMS_H_ #include #include // This namespace is used to pass Linphone enumerators to QML // ============================================================================= namespace LinphoneEnums { Q_NAMESPACE void registerMetaTypes(); enum MediaEncryption { MediaEncryptionNone = int(linphone::MediaEncryption::None), MediaEncryptionDtls = int(linphone::MediaEncryption::DTLS), MediaEncryptionSrtp = int(linphone::MediaEncryption::SRTP), MediaEncryptionZrtp = int(linphone::MediaEncryption::ZRTP) }; Q_ENUM_NS(MediaEncryption) linphone::MediaEncryption toLinphone(const LinphoneEnums::MediaEncryption& encryption); LinphoneEnums::MediaEncryption fromLinphone(const linphone::MediaEncryption& encryption); enum FriendCapability { FriendCapabilityNone = int(linphone::FriendCapability::None), FriendCapabilityGroupChat = int(linphone::FriendCapability::GroupChat), FriendCapabilityLimeX3Dh = int(linphone::FriendCapability::LimeX3Dh), FriendCapabilityEphemeralMessages = int(linphone::FriendCapability::EphemeralMessages) }; Q_ENUM_NS(FriendCapability) linphone::FriendCapability toLinphone(const LinphoneEnums::FriendCapability& capability); LinphoneEnums::FriendCapability fromLinphone(const linphone::FriendCapability& capability); enum EventLogType { EventLogTypeNone = int(linphone::EventLog::Type::None), EventLogTypeConferenceCreated = int(linphone::EventLog::Type::ConferenceCreated), EventLogTypeConferenceTerminated = int(linphone::EventLog::Type::ConferenceTerminated), EventLogTypeConferenceCallStarted = int(linphone::EventLog::Type::ConferenceCallStarted), EventLogTypeConferenceCallEnded = int(linphone::EventLog::Type::ConferenceCallEnded), EventLogTypeConferenceChatMessage = int(linphone::EventLog::Type::ConferenceChatMessage), EventLogTypeConferenceParticipantAdded = int(linphone::EventLog::Type::ConferenceParticipantAdded), EventLogTypeConferenceParticipantRemoved = int(linphone::EventLog::Type::ConferenceParticipantRemoved), EventLogTypeConferenceParticipantSetAdmin = int(linphone::EventLog::Type::ConferenceParticipantSetAdmin), EventLogTypeConferenceParticipantUnsetAdmin = int(linphone::EventLog::Type::ConferenceParticipantUnsetAdmin), EventLogTypeConferenceParticipantDeviceAdded = int(linphone::EventLog::Type::ConferenceParticipantDeviceAdded), EventLogTypeConferenceParticipantDeviceRemoved = int(linphone::EventLog::Type::ConferenceParticipantDeviceRemoved), EventLogTypeConferenceParticipantDeviceMediaAvailabilityChanged = int(linphone::EventLog::Type::ConferenceParticipantDeviceMediaAvailabilityChanged), EventLogTypeConferenceSubjectChanged= int(linphone::EventLog::Type::ConferenceSubjectChanged), EventLogTypeConferenceAvailableMediaChanged = int(linphone::EventLog::Type::ConferenceAvailableMediaChanged), EventLogTypeConferenceSecurityEvent = int(linphone::EventLog::Type::ConferenceSecurityEvent), EventLogTypeConferenceEphemeralMessageLifetimeChanged = int(linphone::EventLog::Type::ConferenceEphemeralMessageLifetimeChanged), EventLogTypeConferenceEphemeralMessageEnabled = int(linphone::EventLog::Type::ConferenceEphemeralMessageEnabled), EventLogTypeConferenceEphemeralMessageDisabled = int(linphone::EventLog::Type::ConferenceEphemeralMessageDisabled) }; Q_ENUM_NS(EventLogType) linphone::EventLog::Type toLinphone(const LinphoneEnums::EventLogType& capability); LinphoneEnums::EventLogType fromLinphone(const linphone::EventLog::Type& data); enum ChatMessageState { ChatMessageStateIdle = int(linphone::ChatMessage::State::Idle), ChatMessageStateInProgress = int(linphone::ChatMessage::State::InProgress), ChatMessageStateDelivered = int(linphone::ChatMessage::State::Delivered), ChatMessageStateNotDelivered = int(linphone::ChatMessage::State::NotDelivered), ChatMessageStateFileTransferError = int(linphone::ChatMessage::State::FileTransferError), ChatMessageStateFileTransferDone = int(linphone::ChatMessage::State::FileTransferDone), ChatMessageStateDeliveredToUser = int(linphone::ChatMessage::State::DeliveredToUser), ChatMessageStateDisplayed = int(linphone::ChatMessage::State::Displayed), ChatMessageStateFileTransferInProgress = int(linphone::ChatMessage::State::FileTransferInProgress) }; Q_ENUM_NS(ChatMessageState) linphone::ChatMessage::State toLinphone(const LinphoneEnums::ChatMessageState& data); LinphoneEnums::ChatMessageState fromLinphone(const linphone::ChatMessage::State& data); enum ChatRoomState { ChatRoomStateNone = int(linphone::ChatRoom::State::None), ChatRoomStateInstantiated = int(linphone::ChatRoom::State::Instantiated), ChatRoomStateCreationPending = int(linphone::ChatRoom::State::CreationPending), ChatRoomStateCreated = int(linphone::ChatRoom::State::Created), ChatRoomStateCreationFailed = int(linphone::ChatRoom::State::CreationFailed), ChatRoomStateTerminationPending = int(linphone::ChatRoom::State::TerminationPending), ChatRoomStateTerminated = int(linphone::ChatRoom::State::Terminated), ChatRoomStateTerminationFailed = int(linphone::ChatRoom::State::TerminationFailed), ChatRoomStateDeleted = int(linphone::ChatRoom::State::Deleted), }; Q_ENUM_NS(ChatRoomState) linphone::ChatRoom::State toLinphone(const LinphoneEnums::ChatRoomState& data); LinphoneEnums::ChatRoomState fromLinphone(const linphone::ChatRoom::State& data); enum CallStatus { CallStatusDeclined = int(linphone::Call::Status::Declined), CallStatusMissed = int(linphone::Call::Status::Missed), CallStatusSuccess = int(linphone::Call::Status::Success), CallStatusAborted = int(linphone::Call::Status::Aborted), CallStatusEarlyAborted = int(linphone::Call::Status::EarlyAborted), CallStatusAcceptedElsewhere = int(linphone::Call::Status::AcceptedElsewhere), CallStatusDeclinedElsewhere = int(linphone::Call::Status::DeclinedElsewhere) }; Q_ENUM_NS(CallStatus) linphone::Call::Status toLinphone(const LinphoneEnums::CallStatus& capability); LinphoneEnums::CallStatus fromLinphone(const linphone::Call::Status& capability); enum ConferenceLayout { ConferenceLayoutGrid = int(linphone::ConferenceLayout::Grid), ConferenceLayoutActiveSpeaker = int(linphone::ConferenceLayout::ActiveSpeaker), ConferenceLayoutAudioOnly = ConferenceLayoutGrid + ConferenceLayoutActiveSpeaker + 1, }; Q_ENUM_NS(ConferenceLayout) linphone::ConferenceLayout toLinphone(const LinphoneEnums::ConferenceLayout& layout); LinphoneEnums::ConferenceLayout fromLinphone(const linphone::ConferenceLayout& layout); enum ConferenceInfoState { ConferenceInfoStateNew = int(linphone::ConferenceInfo::State::New), ConferenceInfoStateUpdated = int(linphone::ConferenceInfo::State::Updated), ConferenceInfoStateCancelled = int(linphone::ConferenceInfo::State::Cancelled) }; Q_ENUM_NS(ConferenceInfoState) linphone::ConferenceInfo::State toLinphone(const LinphoneEnums::ConferenceInfoState& state); LinphoneEnums::ConferenceInfoState fromLinphone(const linphone::ConferenceInfo::State& state); enum ConferenceSchedulerState { ConferenceSchedulerStateAllocationPending = int(linphone::ConferenceScheduler::State::AllocationPending), ConferenceSchedulerStateError = int(linphone::ConferenceScheduler::State::Error), ConferenceSchedulerStateIdle = int(linphone::ConferenceScheduler::State::Idle), ConferenceSchedulerStateReady = int(linphone::ConferenceScheduler::State::Ready), ConferenceSchedulerStateUpdating = int(linphone::ConferenceScheduler::State::Updating) }; Q_ENUM_NS(ConferenceSchedulerState) linphone::ConferenceScheduler::State toLinphone(const LinphoneEnums::ConferenceSchedulerState& state); LinphoneEnums::ConferenceSchedulerState fromLinphone(const linphone::ConferenceScheduler::State& state); enum ParticipantDeviceState { ParticipantDeviceStateJoining = int(linphone::ParticipantDeviceState::Joining), ParticipantDeviceStatePresent = int(linphone::ParticipantDeviceState::Present), ParticipantDeviceStateLeaving = int(linphone::ParticipantDeviceState::Leaving), ParticipantDeviceStateLeft = int(linphone::ParticipantDeviceState::Left), ParticipantDeviceStateScheduledForJoining = int(linphone::ParticipantDeviceState::ScheduledForJoining), ParticipantDeviceStateScheduledForLeaving = int(linphone::ParticipantDeviceState::ScheduledForLeaving), ParticipantDeviceStateOnHold = int(linphone::ParticipantDeviceState::OnHold), ParticipantDeviceStateAlerting = int(linphone::ParticipantDeviceState::Alerting), ParticipantDeviceStateMutedByFocus = int(linphone::ParticipantDeviceState::MutedByFocus), }; Q_ENUM_NS(ParticipantDeviceState) linphone::ParticipantDeviceState toLinphone(const LinphoneEnums::ParticipantDeviceState& state); LinphoneEnums::ParticipantDeviceState fromLinphone(const linphone::ParticipantDeviceState& state); enum TunnelMode { TunnelModeDisable = int(linphone::Tunnel::Mode::Disable), TunnelModeEnable= int(linphone::Tunnel::Mode::Enable), TunnelModeAuto = int(linphone::Tunnel::Mode::Auto) }; Q_ENUM_NS(TunnelMode) linphone::Tunnel::Mode toLinphone(const LinphoneEnums::TunnelMode& mode); LinphoneEnums::TunnelMode fromLinphone(const linphone::Tunnel::Mode& mode); enum RecorderState{ RecorderStateClosed = int(linphone::RecorderState::Closed), RecorderStatePaused = int(linphone::RecorderState::Paused), RecorderStateRunning = int(linphone::RecorderState::Running) }; Q_ENUM_NS(RecorderState) linphone::RecorderState toLinphone(const LinphoneEnums::RecorderState& state); LinphoneEnums::RecorderState fromLinphone(const linphone::RecorderState& state); enum TransportType{ TransportTypeDtls = int(linphone::TransportType::Dtls), TransportTypeTcp = int(linphone::TransportType::Tcp), TransportTypeTls = int(linphone::TransportType::Tls), TransportTypeUdp = int(linphone::TransportType::Udp) }; Q_ENUM_NS(TransportType) linphone::TransportType toLinphone(const LinphoneEnums::TransportType& type); LinphoneEnums::TransportType fromLinphone(const linphone::TransportType& type); QString toString(const LinphoneEnums::TransportType& type); void fromString(const QString& transportType, LinphoneEnums::TransportType *transport); } Q_DECLARE_METATYPE(LinphoneEnums::CallStatus) Q_DECLARE_METATYPE(LinphoneEnums::ChatMessageState) Q_DECLARE_METATYPE(LinphoneEnums::ChatRoomState) Q_DECLARE_METATYPE(LinphoneEnums::ConferenceLayout) Q_DECLARE_METATYPE(LinphoneEnums::ConferenceInfoState) Q_DECLARE_METATYPE(LinphoneEnums::ConferenceSchedulerState) Q_DECLARE_METATYPE(LinphoneEnums::EventLogType) Q_DECLARE_METATYPE(LinphoneEnums::FriendCapability) Q_DECLARE_METATYPE(LinphoneEnums::MediaEncryption) Q_DECLARE_METATYPE(LinphoneEnums::ParticipantDeviceState) Q_DECLARE_METATYPE(LinphoneEnums::RecorderState) Q_DECLARE_METATYPE(LinphoneEnums::TunnelMode) Q_DECLARE_METATYPE(LinphoneEnums::TransportType) #endif linphone-desktop-5.0.2/linphone-app/src/utils/MediastreamerUtils.cpp000066400000000000000000000133641434616504300256460ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include "mediastreamer2/msvolume.h" #include "mediastreamer2/mssndcard.h" #include "mediastreamer2/msticker.h" #include "components/core/CoreManager.hpp" #include "MediastreamerUtils.hpp" #include using namespace MediastreamerUtils; SimpleCaptureGraph::SimpleCaptureGraph(const std::string &capture, const std::string &playback) : captureCardId(capture), playbackCardId(playback) { LinphoneCore *ccore = CoreManager::getInstance()->getCore()->cPtr(); msFactory = linphone_core_get_ms_factory(ccore); playbackCard = ms_snd_card_manager_get_card(ms_factory_get_snd_card_manager(msFactory), playbackCardId.c_str()); if (!playbackCard) qWarning("Cannot get playback card from MSFactory with : %s", playbackCardId.c_str()); captureCard = ms_snd_card_manager_get_card(ms_factory_get_snd_card_manager(msFactory), captureCardId.c_str()); if (!captureCard) qWarning("Cannot get capture card from MSFactory with : %s", captureCardId.c_str()); if(playbackCard && captureCard)// Assure to initialize when playback and capture are available init(); } SimpleCaptureGraph::~SimpleCaptureGraph() { destroy(); } void SimpleCaptureGraph::init() { if (!audioCapture) { audioCapture = ms_snd_card_create_reader(captureCard); } if (!audioSink) { audioSink = ms_snd_card_create_writer(playbackCard); } if (!captureVolumeFilter) { captureVolumeFilter = ms_factory_create_filter(msFactory, MS_VOLUME_ID); } if (!playbackVolumeFilter) { playbackVolumeFilter = ms_factory_create_filter(msFactory, MS_VOLUME_ID); } if(!resamplerFilter) resamplerFilter = ms_factory_create_filter(msFactory, MS_RESAMPLE_ID); int captureRate, playbackRate, captureChannels, playbackChannels; ms_filter_call_method(audioCapture,MS_FILTER_GET_SAMPLE_RATE,&captureRate); ms_filter_call_method(audioSink,MS_FILTER_GET_SAMPLE_RATE,&playbackRate); ms_filter_call_method(audioCapture,MS_FILTER_GET_NCHANNELS,&captureChannels); ms_filter_call_method(audioSink,MS_FILTER_GET_NCHANNELS,&playbackChannels); ms_filter_call_method(resamplerFilter,MS_FILTER_SET_SAMPLE_RATE,&captureRate); ms_filter_call_method(resamplerFilter,MS_FILTER_SET_OUTPUT_SAMPLE_RATE,&playbackRate); ms_filter_call_method(resamplerFilter,MS_FILTER_SET_NCHANNELS,&captureChannels); ms_filter_call_method(resamplerFilter,MS_FILTER_SET_OUTPUT_NCHANNELS,&playbackChannels); ms_filter_link(audioCapture, 0, captureVolumeFilter, 0); ms_filter_link(captureVolumeFilter, 0, resamplerFilter, 0); ms_filter_link(resamplerFilter, 0, playbackVolumeFilter, 0); ms_filter_link(playbackVolumeFilter, 0, audioSink, 0); //Mute playback float muteGain = 0.0f; ms_filter_call_method(playbackVolumeFilter, static_cast(MS_VOLUME_SET_GAIN), &muteGain); ticker = ms_ticker_new(); running = false; } void SimpleCaptureGraph::start() { if (!running && audioCapture) { running = true; ms_ticker_attach(ticker, audioCapture); } } void SimpleCaptureGraph::stop() { if (running && audioCapture){ ms_ticker_detach(ticker, audioCapture); running = false; } } void SimpleCaptureGraph::destroy() { if (running) { stop(); } if (audioSink) ms_filter_unlink(playbackVolumeFilter, 0, audioSink, 0); if (captureVolumeFilter && resamplerFilter) ms_filter_unlink(captureVolumeFilter, 0, resamplerFilter, 0); if (resamplerFilter && playbackVolumeFilter) ms_filter_unlink(resamplerFilter, 0, playbackVolumeFilter, 0); if (audioCapture) ms_filter_unlink(audioCapture, 0, captureVolumeFilter, 0); if (playbackVolumeFilter) ms_filter_destroy(playbackVolumeFilter); if (captureVolumeFilter) ms_filter_destroy(captureVolumeFilter); if (resamplerFilter) ms_filter_destroy(resamplerFilter); if (audioSink) ms_filter_destroy(audioSink); if (audioCapture) ms_filter_destroy(audioCapture); if (ticker) { ms_ticker_destroy(ticker);// Destroy ticker at the end to avoid conflicts between attached filters } ticker = nullptr; playbackVolumeFilter = nullptr; captureVolumeFilter = nullptr; resamplerFilter = nullptr; audioSink = nullptr; audioCapture = nullptr; } float SimpleCaptureGraph::getCaptureGain() { float gain = 0.0f; if (isRunning() && audioCapture) { ms_filter_call_method(audioCapture, MS_AUDIO_CAPTURE_GET_VOLUME_GAIN, &gain); } return gain; } void SimpleCaptureGraph::setCaptureGain(float gain) { if (isRunning() && audioCapture) { ms_filter_call_method(audioCapture, MS_AUDIO_CAPTURE_SET_VOLUME_GAIN, &gain); } } float SimpleCaptureGraph::getPlaybackGain() { float gain = 0.0f; if (isRunning() && audioSink) { ms_filter_call_method(audioSink, MS_AUDIO_PLAYBACK_GET_VOLUME_GAIN, &gain); } return gain; } void SimpleCaptureGraph::setPlaybackGain(float gain) { if (isRunning() && audioSink) { ms_filter_call_method(audioSink, MS_AUDIO_PLAYBACK_SET_VOLUME_GAIN, &gain); } } float SimpleCaptureGraph::getCaptureVolume() { float vol = 0; if (captureVolumeFilter) { ms_filter_call_method(captureVolumeFilter, MS_VOLUME_GET, &vol); vol = MediastreamerUtils::dbToLinear(vol); } return vol; } linphone-desktop-5.0.2/linphone-app/src/utils/MediastreamerUtils.hpp000066400000000000000000000052271434616504300256520ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef MEDIASTREAMER_UTILS_H_ #define MEDIASTREAMER_UTILS_H_ #include #include "mediastreamer2/mssndcard.h" #include "mediastreamer2/msvolume.h" #include "mediastreamer2/msfilter.h" #include "mediastreamer2/msticker.h" #include #include // ============================================================================= namespace MediastreamerUtils { inline float computeVu (float volume) { constexpr float VuMin = -20.f; constexpr float VuMax = 4.f; if (volume < VuMin) return 0.f; if (volume > VuMax) return 1.f; return (volume - VuMin) / (VuMax - VuMin); } inline float dbToLinear(float volume) { return static_cast(pow(10.0, volume / 10.0)); } inline float linearToDb(float volume) { if (qFuzzyIsNull(volume)) { return MS_VOLUME_DB_LOWEST; } return static_cast(10.0 * log10(volume)); } //Simple mediastreamer audio capture graph //Used to get current microphone volume in audio settings class SimpleCaptureGraph { public: SimpleCaptureGraph(const std::string &captureCardId, const std::string &playbackCardId); ~SimpleCaptureGraph(); void start(); void stop(); float getCaptureVolume(); float getCaptureGain(); float getPlaybackGain(); void setCaptureGain(float volume); void setPlaybackGain(float volume); bool isRunning() const { return running; } protected: void init(); void destroy(); bool running = false; std::string captureCardId; std::string playbackCardId; MSFilter *audioSink = nullptr; MSFilter *audioCapture = nullptr; MSFilter *captureVolumeFilter = nullptr; MSFilter *playbackVolumeFilter = nullptr; MSFilter *resamplerFilter = nullptr; MSTicker *ticker = nullptr; MSSndCard *playbackCard = nullptr; MSSndCard *captureCard = nullptr; MSFactory *msFactory = nullptr; }; } #endif // ifndef MEDIASTREAMER_UTILS_H_ linphone-desktop-5.0.2/linphone-app/src/utils/QExifImageHeader.cpp000066400000000000000000001445561434616504300251430ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt scene graph research project. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // This file was copied from Qt Extended 4.5 #include #include #include #include #include #include #include #include "Utils.hpp" #include "QExifImageHeader.hpp" /*! \typedef QExifSRational A synonym for \c QPair representing a signed rational number as stored in EXIF headers. The first integer in the pair is the numerator and the second the denominator. */ /*! \typedef QExifURational A synonym for \c QPair representing an unsigned rational number as stored in EXIF headers. The first integer in the pair is the numerator and the second the denominator. */ struct ExifIfdHeader { quint16 tag; quint16 type; quint32 count; union { quint32 offset; quint8 offsetBytes[4]; char offsetAscii[4]; quint16 offsetShorts[2]; }; }; QDataStream &operator>> (QDataStream &stream, ExifIfdHeader &header) { stream >> header.tag; stream >> header.type; stream >> header.count; if (header.type == QExifValue::Byte && header.count <= 4) { stream.readRawData(header.offsetAscii, 4); } else if (header.type == QExifValue::Ascii && header.count <= 4) { stream.readRawData(header.offsetAscii, 4); } else if (header.type == QExifValue::Short && header.count <= 2) { stream >> header.offsetShorts[0]; stream >> header.offsetShorts[1]; } else { stream >> header.offset; } return stream; } class QExifValuePrivate : public QSharedData { public: QExifValuePrivate (quint16 t, int c) : type(t), count(c) {} virtual ~QExifValuePrivate () {} quint16 type; int count; }; class QExifByteValuePrivate : public QExifValuePrivate { public: QExifByteValuePrivate () : QExifValuePrivate(QExifValue::Byte, 0) { ref.ref(); } QExifByteValuePrivate (const QVector &v) : QExifValuePrivate(QExifValue::Byte, v.size()), value(v) {} ~QExifByteValuePrivate(){} QVector value; }; class QExifUndefinedValuePrivate : public QExifValuePrivate { public: QExifUndefinedValuePrivate (const QByteArray &v) : QExifValuePrivate(QExifValue::Undefined, v.size()), value(v) {} QByteArray value; }; class QExifAsciiValuePrivate : public QExifValuePrivate { public: QExifAsciiValuePrivate (const QString &v) : QExifValuePrivate(QExifValue::Ascii, v.size() + 1), value(v) {} QString value; }; class QExifShortValuePrivate : public QExifValuePrivate { public: QExifShortValuePrivate (const QVector &v) : QExifValuePrivate(QExifValue::Short, v.size()), value(v) {} QVector value; }; class QExifLongValuePrivate : public QExifValuePrivate { public: QExifLongValuePrivate (const QVector &v) : QExifValuePrivate(QExifValue::Long, v.size()), value(v) {} QVector value; }; class QExifSignedLongValuePrivate : public QExifValuePrivate { public: QExifSignedLongValuePrivate (const QVector &v) : QExifValuePrivate(QExifValue::SignedLong, v.size()), value(v) {} QVector value; }; class QExifRationalValuePrivate : public QExifValuePrivate { public: QExifRationalValuePrivate (const QVector &v) : QExifValuePrivate(QExifValue::Rational, v.size()), value(v) {} QVector value; }; class QExifSignedRationalValuePrivate : public QExifValuePrivate { public: QExifSignedRationalValuePrivate (const QVector &v) : QExifValuePrivate(QExifValue::SignedRational, v.size()), value(v) {} QVector value; }; Q_GLOBAL_STATIC(QExifByteValuePrivate, qExifValuePrivateSharedNull) /*! \class QExifValue \inpublicgroup QtBaseModule \brief The QExifValue class represents data types found in EXIF image headers. Tag values in EXIF headers are stored as arrays of a limited number of data types. QExifValue encapsulates a union of these types and provides conversions to and from appropriate Qt types. \section1 String encoding Most tags with string values in EXIF headers are ASCII encoded and have the Ascii value type, but some tags allow other encodings. In this case the value type is Undefined and the encoding of the text is given by the encoding function(). \section1 Date-time values Date-time values in EXIF headers are stored in ASCII encoded strings of the form \c {yyyy:MM:dd HH:mm:ss}. Constructing a QExifValue from a QDateTime will perform this conversion and likewise an appropriately formed QExifValue can be converted to a QDateTime using the toDateTime() function. \sa QExifImageHeader \preliminary */ /*! \enum QExifValue::Type Enumerates the possible types of EXIF values. \value Byte An unsigned 8 bit integer. \value Ascii A null terminated ascii string. \value Short An unsigned 16 bit integer. \value Long An unsigned 32 bit integer. \value Rational Two unsigned 32 bit integers, representing a the numerator and denominator of an unsigned rational number. \value Undefined An array of 8 bit integers. \value SignedLong A signed 32 bit integer. \value SignedRational Two signed 32 bit integers representing the numerator and denominator of a signed rational number. */ /*! \enum QExifValue::TextEncoding Enumerates the encodings of text strings in EXIF values of Undefined type. \value NoEncoding An ASCII string of Ascii type. \value AsciiEncoding An ASCII string of Undefined type. \value JisEncoding A JIS X208-1990 string of Undefined type. \value UnicodeEncoding A Unicode string of Undefined type. \value UndefinedEncoding An unspecified string encoding of Undefined type. Assumed to be the local 8-bit encoding. */ /*! Constructs a null QExifValue. */ QExifValue::QExifValue () : d(qExifValuePrivateSharedNull()) {} /*! Constructs a QExifValue with a \a value of type Byte. */ QExifValue::QExifValue (quint8 value) : d(new QExifByteValuePrivate(QVector(1, value))) {} /*! Constructs a QExifValue with an array of \a values of type Byte. */ QExifValue::QExifValue (const QVector &values) : d(new QExifByteValuePrivate(values)) {} /*! Constructs a QExifValue with a \a value of type Ascii or Undefined. If the \a encoding is NoEncoding the value will be of type Ascii, otherwise it will be Undefined and the string encoded using the given \a encoding. */ QExifValue::QExifValue (const QString &value, TextEncoding encoding) : d(qExifValuePrivateSharedNull()) { switch (encoding) { case AsciiEncoding: d = new QExifUndefinedValuePrivate(QByteArray::fromRawData("ASCII\0\0\0", 8) + value.toUtf8()); break; case JisEncoding: { QTextCodec *codec = QTextCodec::codecForName("JIS X 0208"); if (codec) d = new QExifUndefinedValuePrivate(QByteArray::fromRawData("JIS\0\0\0\0\0", 8) + codec->fromUnicode(value)); } break; case UnicodeEncoding: { QTextCodec *codec = QTextCodec::codecForName("UTF-16"); if (codec) d = new QExifUndefinedValuePrivate(QByteArray::fromRawData("UNICODE\0", 8) + codec->fromUnicode(value)); } break; case UndefinedEncoding: d = new QExifUndefinedValuePrivate(QByteArray::fromRawData("\0\0\0\0\0\0\0\\0", 8) + value.toLocal8Bit()); break; default: d = new QExifAsciiValuePrivate(value); } } /*! Constructs a QExifValue with a \a value of type Short. */ QExifValue::QExifValue (quint16 value) : d(new QExifShortValuePrivate(QVector(1, value))) {} /*! Constructs a QExifValue with an array of \a values of type Short. */ QExifValue::QExifValue (const QVector &values) : d(new QExifShortValuePrivate(values)) {} /*! Constructs a QExifValue with a \a value of type Long. */ QExifValue::QExifValue (quint32 value) : d(new QExifLongValuePrivate(QVector(1, value))) {} /*! Constructs a QExifValue with an array of \a values of type Long. */ QExifValue::QExifValue (const QVector &values) : d(new QExifLongValuePrivate(values)) {} /*! Constructs a QExifValue with a \a value of type Rational. */ QExifValue::QExifValue (const QExifURational &value) : d(new QExifRationalValuePrivate(QVector(1, value))) {} /*! Constructs a QExifValue with an array of \a values of type Rational. */ QExifValue::QExifValue (const QVector &values) : d(new QExifRationalValuePrivate(values)) {} /*! Constructs a QExifValue with a \a value of type Undefined. */ QExifValue::QExifValue (const QByteArray &value) : d(new QExifUndefinedValuePrivate(value)) {} /*! Constructs a QExifValue with a \a value of type SignedLong. */ QExifValue::QExifValue (qint32 value) : d(new QExifSignedLongValuePrivate(QVector(1, value))) {} /*! Constructs a QExifValue with an array of \a values of type SignedLong. */ QExifValue::QExifValue (const QVector &values) : d(new QExifSignedLongValuePrivate(values)) {} /*! Constructs a QExifValue with a \a value of type SignedRational. */ QExifValue::QExifValue (const QExifSRational &value) : d(new QExifSignedRationalValuePrivate(QVector(1, value))) {} /*! Constructs a QExifValue with an array of \a values of type SignedRational. */ QExifValue::QExifValue (const QVector &values) : d(new QExifSignedRationalValuePrivate(values)) {} /*! Constructs a QExifValue of type Ascii with an ascii string formatted from a date-time \a value. Date-times are stored as strings in the format \c {yyyy:MM:dd HH:mm:ss}. */ QExifValue::QExifValue (const QDateTime &value) : d(new QExifAsciiValuePrivate(value.toString(QLatin1String("yyyy:MM:dd HH:mm:ss")))) {} /*! Constructs a copy of the QExifValue \a other. */ QExifValue::QExifValue (const QExifValue &other) : d(other.d) {} /*! Assigns the value of \a other to a QExifValue. */ QExifValue &QExifValue::operator= (const QExifValue &other) { d = other.d; return *this; } /*! Destroys a QExifValue. */ QExifValue::~QExifValue () {} /*! Compares a QExifValue to \a other. Returns true if they are the same value and false otherwise. */ bool QExifValue::operator== (const QExifValue &other) const { return d == other.d; } /*! Returns true if a QExifValue has a null value and false otherwise. */ bool QExifValue::isNull () const { return d == qExifValuePrivateSharedNull(); } /*! Returns the type of a QExifValue. */ int QExifValue::type () const { return d->type; } /*! Returns the number of elements in a QExifValue. For ascii strings this is the length of the string including the terminating null. */ int QExifValue::count () const { return d->count; } /*! Returns the encoding of strings stored in Undefined values. */ QExifValue::TextEncoding QExifValue::encoding () const { if (d->type == Undefined && d->count > 8) { QByteArray value = static_cast(d.constData())->value; if (value.startsWith(QByteArray::fromRawData("ASCII\0\0\0", 8))) return AsciiEncoding; else if (value.startsWith(QByteArray::fromRawData("JIS\0\0\0\0\0", 8))) return JisEncoding; else if (value.startsWith(QByteArray::fromRawData("UNICODE\0", 8))) return UnicodeEncoding; else if (value.startsWith(QByteArray::fromRawData("\0\0\0\0\0\0\0\0", 8))) return UndefinedEncoding; } return NoEncoding; } /*! Returns the value of a single element QExifValue of type Byte. */ quint8 QExifValue::toByte () const { return d->type == Byte && d->count == 1 ? static_cast(d.constData())->value.at(0) : 0; } /*! Returns the value of a multiple element QExifValue of type Byte. */ QVector QExifValue::toByteVector () const { return d->type == Byte ? static_cast(d.constData())->value : QVector(); } /*! Returns the value of a QExifValue of type Ascii. */ QString QExifValue::toString () const { switch (d->type) { case Ascii: return static_cast(d.constData())->value; case Undefined: { QByteArray string = static_cast(d.constData())->value.mid(8); switch (encoding()) { case AsciiEncoding: return QString::fromUtf8(string.constData(), string.length()); case JisEncoding: { QTextCodec *codec = QTextCodec::codecForName("JIS X 0208"); if (codec) return codec->toUnicode(string); } break; case UnicodeEncoding: { QTextCodec *codec = QTextCodec::codecForName("UTF-16"); if (codec) return codec->toUnicode(string); return QString::fromLocal8Bit(string.constData(), string.length()); } case UndefinedEncoding: return QString::fromLocal8Bit(string.constData(), string.length()); default: break; } return QString(); } default: return QString(); } } /*! Returns the value of a single element QExifValue of type Byte or Short. */ quint16 QExifValue::toShort () const { if (d->count == 1) { switch (d->type) { case Byte: return static_cast(d.constData())->value.at(0); case Short: return static_cast(d.constData())->value.at(0); } } return 0; } /*! Returns the value of a single element QExifValue of type Short. */ QVector QExifValue::toShortVector () const { return d->type == Short ? static_cast(d.constData())->value : QVector(); } /*! Returns the value of a single element QExifValue of type Byte, Short, Long, or SignedLong. */ quint32 QExifValue::toLong () const { if (d->count == 1) { switch (d->type) { case Byte: return static_cast(d.constData())->value.at(0); case Short: return static_cast(d.constData())->value.at(0); case Long: return static_cast(d.constData())->value.at(0); case SignedLong: return quint32(static_cast(d.constData())->value.at(0)); } } return 0; } /*! Returns the value of a multiple element QExifValue of type Long. */ QVector QExifValue::toLongVector () const { return d->type == Long ? static_cast(d.constData())->value : QVector(); } /*! Returns the value of a multiple element QExifValue of type Rational. */ QExifURational QExifValue::toRational () const { return d->type == Rational && d->count == 1 ? static_cast(d.constData())->value.at(0) : QExifURational(); } /*! Returns the value of a multiple element QExifValue of type Rational. */ QVector QExifValue::toRationalVector () const { return d->type == Rational ? static_cast(d.constData())->value : QVector(); } /*! Returns the value of a QExifValue of type Undefined. */ QByteArray QExifValue::toByteArray () const { switch (d->type) { case Ascii: return static_cast(d.constData())->value.toUtf8(); case Undefined: return static_cast(d.constData())->value; default: return QByteArray(); } } /*! Returns the value of a single element QExifValue of type Byte, Short, Long, or SignedLong. */ qint32 QExifValue::toSignedLong () const { if (d->count == 1) { switch (d->type) { case Byte: return static_cast(d.constData())->value.at(0); case Short: return static_cast(d.constData())->value.at(0); case Long: return qint32(static_cast(d.constData())->value.at(0)); case SignedLong: return static_cast(d.constData())->value.at(0); } } return 0; } /*! Returns the value of a multiple element QExifValue of type SignedLong. */ QVector QExifValue::toSignedLongVector () const { return d->type == SignedLong ? static_cast(d.constData())->value : QVector(); } /*! Returns the value of a single element QExifValue of type SignedRational. */ QExifSRational QExifValue::toSignedRational () const { return d->type == SignedRational && d->count == 1 ? static_cast(d.constData())->value.at(0) : QExifSRational(); } /*! Returns the value of a multiple element QExifValue of type SignedRational. */ QVector QExifValue::toSignedRationalVector () const { return d->type == SignedRational ? static_cast(d.constData())->value : QVector(); } /*! Returns the value of QExifValue storing a date-time. Date-times are stored as ascii strings in the format \c {yyyy:MM:dd HH:mm:ss}. */ QDateTime QExifValue::toDateTime () const { return d->type == Ascii && d->count == 20 ? QDateTime::fromString(static_cast(d.constData())->value, QLatin1String("yyyy:MM:dd HH:mm:ss")) : QDateTime(); } class QExifImageHeaderPrivate { public: QSysInfo::Endian byteOrder; mutable qint64 size; QMap imageIfdValues; QMap exifIfdValues; QMap gpsIfdValues; QSize thumbnailSize; QByteArray thumbnailData; QExifValue thumbnailXResolution; QExifValue thumbnailYResolution; QExifValue thumbnailResolutionUnit; QExifValue thumbnailOrientation; }; /*! \class QExifImageHeader \inpublicgroup QtBaseModule \brief The QExifImageHeader class provides functionality for reading and writing EXIF image headers. EXIF headers are a collection of properties that describe the image they're embedded in. Each property is identified by a tag of which there are three kinds. \l {ImageTag}{Image tags} which mostly describe the format (dimensions, resolution, orientation) but also include some descriptive information (description, camera make and model, artist). \l {ExifExtendedTag} {EXIF extended tags} which elaborate on some of the image tags and record the camera settings at time of capture among other things. Finally there are \l {GpsTag}{GPS tags} which record the location the image was captured. EXIF tags are typically found in JPEG images but may be found in other image formats. To read headers from a JPEG image QExifImageHeader provides the loadFromJpeg() function, and the complementary saveToJpeg() function for writing. To allow reading and writing arbitrary formats QExifImageHeader provides the read() and write() functions which work with just the EXIF header data itself. \preliminary */ /*! \enum QExifImageHeader::ImageTag Enumerates the TIFF image tag IDs defined in the EXIF specification. \value ImageWidth \value ImageLength \value BitsPerSample \value Compression \value PhotometricInterpretation \value Orientation \value SamplesPerPixel \value PlanarConfiguration \value YCbCrSubSampling \value XResolution \value YResolution \value ResolutionUnit \value StripOffsets \value RowsPerStrip \value StripByteCounts \value TransferFunction \value WhitePoint \value PrimaryChromaciticies \value YCbCrCoefficients \value ReferenceBlackWhite \value DateTime \value ImageDescription \value Make \value Model \value Software \value Artist \value Copyright */ /*! \enum QExifImageHeader::ExifExtendedTag Enumerates the extended EXIF tag IDs defined in the EXIF specification. \value ExifVersion \value FlashPixVersion \value ColorSpace \value ComponentsConfiguration \value CompressedBitsPerPixel \value PixelXDimension \value PixelYDimension \value MakerNote \value UserComment \value RelatedSoundFile \value DateTimeOriginal \value DateTimeDigitized \value SubSecTime \value SubSecTimeOriginal \value SubSecTimeDigitized \value ImageUniqueId \value ExposureTime \value FNumber \value ExposureProgram \value SpectralSensitivity \value ISOSpeedRatings \value Oecf \value ShutterSpeedValue \value ApertureValue \value BrightnessValue \value ExposureBiasValue \value MaxApertureValue \value SubjectDistance \value MeteringMode \value LightSource \value Flash \value FocalLength \value SubjectArea \value FlashEnergy \value SpatialFrequencyResponse \value FocalPlaneXResolution \value FocalPlaneYResolution \value FocalPlaneResolutionUnit \value SubjectLocation \value ExposureIndex \value SensingMethod \value FileSource \value SceneType \value CfaPattern \value CustomRendered \value ExposureMode \value WhiteBalance \value DigitalZoomRatio \value FocalLengthIn35mmFilm \value SceneCaptureType \value GainControl \value Contrast \value Saturation \value Sharpness \value DeviceSettingDescription \value SubjectDistanceRange */ /*! \enum QExifImageHeader::GpsTag Enumerates the GPS tag IDs from the EXIF specification. \value GpsVersionId \value GpsLatitudeRef \value GpsLatitude \value GpsLongitudeRef \value GpsLongitude \value GpsAltitudeRef \value GpsAltitude \value GpsTimeStamp \value GpsSatellites \value GpsStatus \value GpsMeasureMode \value GpsDop \value GpsSpeedRef \value GpsSpeed \value GpsTrackRef \value GpsTrack \value GpsImageDirectionRef \value GpsImageDirection \value GpsMapDatum \value GpsDestLatitudeRef \value GpsDestLatitude \value GpsDestLongitudeRef \value GpsDestLongitude \value GpsDestBearingRef \value GpsDestBearing \value GpsDestDistanceRef \value GpsDestDistance \value GpsProcessingMethod \value GpsAreaInformation \value GpsDateStamp \value GpsDifferential */ /*! Constructs a new EXIF image data editor. */ QExifImageHeader::QExifImageHeader () : d(new QExifImageHeaderPrivate) { d->byteOrder = QSysInfo::ByteOrder; d->size = -1; } /*! Constructs a new EXIF image data editor and reads the meta-data from a JPEG image with the given \a fileName. */ QExifImageHeader::QExifImageHeader (const QString &fileName) : d(new QExifImageHeaderPrivate) { d->byteOrder = QSysInfo::ByteOrder; d->size = -1; loadFromJpeg(fileName); } /*! Destroys an EXIF image data editor. */ QExifImageHeader::~QExifImageHeader () { clear(); delete d; } /*! Reads meta-data from a JPEG image with the given \a fileName. Returns true if the data was successfully parsed and false otherwise. */ bool QExifImageHeader::loadFromJpeg (const QString &fileName) { QFile file(fileName); if (file.open(QIODevice::ReadOnly)) return loadFromJpeg(&file); else return false; } /*! Reads meta-data from an I/O \a device containing a JPEG image. Returns true if the data was successfully parsed and false otherwise. */ bool QExifImageHeader::loadFromJpeg (QIODevice *device) { clear(); QByteArray exifData = extractExif(device); if (!exifData.isEmpty()) { QBuffer buffer(&exifData); return buffer.open(QIODevice::ReadOnly) && read(&buffer); } return false; } /*! Saves meta-data to a JPEG image with the given \a fileName. Returns true if the data was successfully written. */ bool QExifImageHeader::saveToJpeg (const QString &fileName) const { QFile file(fileName); if (file.open(QIODevice::ReadWrite)) return saveToJpeg(&file); else return false; } /*! Save meta-data to the given I/O \a device. The device must be non-sequential and already contain a valid JPEG image. Returns true if the data was successfully written. */ bool QExifImageHeader::saveToJpeg (QIODevice *device) const { if (device->isSequential()) return false; QByteArray exif; { QBuffer buffer(&exif); if (!buffer.open(QIODevice::WriteOnly)) return false; write(&buffer); buffer.close(); exif = QByteArray::fromRawData("Exif\0\0", 6) + exif; } QDataStream stream(device); stream.setByteOrder(QDataStream::BigEndian); if (device->read(2) != "\xFF\xD8") // Not a valid JPEG image. return false; quint16 segmentId; quint16 segmentLength; stream >> segmentId; stream >> segmentLength; if (segmentId == 0xFFE0) { QByteArray jfif = device->read(segmentLength - 2); if (!jfif.startsWith("JFIF")) return false; stream >> segmentId; stream >> segmentLength; if (segmentId == 0xFFE1) { QByteArray oldExif = device->read(segmentLength - 2); if (!oldExif.startsWith("Exif")) return false; int dSize = oldExif.size() - exif.size(); if (dSize > 0) exif += QByteArray(dSize, '\0'); QByteArray remainder = device->readAll(); device->seek(0); stream << quint16(0xFFD8); // SOI stream << quint16(0xFFE0); // APP0 stream << quint16(jfif.size() + 2); device->write(jfif); stream << quint16(0xFFE1); // APP1 stream << quint16(exif.size() + 2); device->write(exif); device->write(remainder); } else { QByteArray remainder = device->readAll(); device->seek(0); stream << quint16(0xFFD8); // SOI stream << quint16(0xFFE0); // APP0 stream << quint16(jfif.size() + 2); device->write(jfif); stream << quint16(0xFFE1); // APP1 stream << quint16(exif.size() + 2); device->write(exif); stream << quint16(0xFFE0); // APP0 stream << segmentId; stream << segmentLength; device->write(remainder); } } else if (segmentId == 0xFFE1) { QByteArray oldExif = device->read(segmentLength - 2); if (!oldExif.startsWith("Exif")) return false; int dSize = oldExif.size() - exif.size(); if (dSize > 0) exif += QByteArray(dSize, '\0'); QByteArray remainder = device->readAll(); device->seek(0); stream << quint16(0xFFD8); // SOI stream << quint16(0xFFE1); // APP1 stream << quint16(exif.size() + 2); device->write(exif); device->write(remainder); } else { QByteArray remainder = device->readAll(); device->seek(0); stream << quint16(0xFFD8); // SOI stream << quint16(0xFFE1); // APP1 stream << quint16(exif.size() + 2); device->write(exif); stream << segmentId; stream << segmentLength; device->write(remainder); } return true; } /*! Returns the byte order of EXIF file. */ QSysInfo::Endian QExifImageHeader::byteOrder () const { return d->byteOrder; } quint32 QExifImageHeader::sizeOf (const QExifValue &value) const { switch (value.type()) { case QExifValue::Byte: case QExifValue::Undefined: return quint32(value.count() > 4 ? 12 + value.count() : 12); case QExifValue::Ascii: return quint32(value.count() > 4 ? 12 + value.count() : 12); case QExifValue::Short: return value.count() > 2 ? quint32(12 + quint32(value.count()) * sizeof(quint16)) : 12; case QExifValue::Long: case QExifValue::SignedLong: return value.count() > 1 ? quint32(12 + quint32(value.count()) * sizeof(quint32)) : 12; case QExifValue::Rational: case QExifValue::SignedRational: return value.count() > 0 ? quint32(12 + quint32(value.count()) * sizeof(quint32) * 2) : 12; default: return 0; } } template quint32 QExifImageHeader::calculateSize (const QMap &values) const { quint32 size = sizeof(quint16); foreach(const QExifValue &value, values) size += sizeOf(value); return size; } /*! Returns the size of EXIF data in bytes. */ qint64 QExifImageHeader::size () const { if (d->size == -1) { d->size = 2 + // Byte Order 2 + // Marker 4 + // Image Ifd offset 12 + // ExifIfdPointer Ifd 4 + // Thumbnail Ifd offset calculateSize(d->imageIfdValues) + // Image headers and values. calculateSize(d->exifIfdValues); // Exif headers and values. if (!d->gpsIfdValues.isEmpty()) { d->size += 12 + // GpsInfoIfdPointer Ifd calculateSize(d->gpsIfdValues); // Gps headers and values. } if (!d->thumbnailData.isEmpty()) { d->size += 2 + // Thumbnail Ifd count 12 + // Compression Ifd 20 + // XResolution Ifd 20 + // YResolution Ifd 12 + // ResolutionUnit Ifd 12 + // JpegInterchangeFormat Ifd 12 + // JpegInterchangeFormatLength Ifd d->thumbnailData.size(); // Thumbnail data size. } } return d->size; } /*! Clears all image meta-data. */ void QExifImageHeader::clear () { d->imageIfdValues.clear(); d->exifIfdValues.clear(); d->gpsIfdValues.clear(); d->thumbnailData.clear(); d->size = -1; } /*! Returns a list of all image tags in an EXIF header. */ QList QExifImageHeader::imageTags () const { return d->imageIfdValues.keys(); } /*! Returns a list of all extended EXIF tags in a header. */ QList QExifImageHeader::extendedTags () const { return d->exifIfdValues.keys(); } /*! Returns a list of all GPS tags in an EXIF header. */ QList QExifImageHeader::gpsTags () const { return d->gpsIfdValues.keys(); } /*! Returns true if an EXIf header contains a value for an image \a tag and false otherwise. */ bool QExifImageHeader::contains (ImageTag tag) const { return d->imageIfdValues.contains(tag); } /*! Returns true if a header contains a a value for an extended EXIF \a tag and false otherwise. */ bool QExifImageHeader::contains (ExifExtendedTag tag) const { return d->exifIfdValues.contains(tag); } /*! Returns true if an EXIf header contains a value for a GPS \a tag and false otherwise. */ bool QExifImageHeader::contains (GpsTag tag) const { return d->gpsIfdValues.contains(tag); } /*! Removes the value for an image \a tag. */ void QExifImageHeader::remove (ImageTag tag) { d->imageIfdValues.remove(tag); d->size = -1; } /*! Removes the value for an extended EXIF \a tag. */ void QExifImageHeader::remove (ExifExtendedTag tag) { d->exifIfdValues.remove(tag); d->size = -1; } /*! Removes the value for a GPS \a tag. */ void QExifImageHeader::remove (GpsTag tag) { d->gpsIfdValues.remove(tag); d->size = -1; } /*! Returns the value for an image \a tag. */ QExifValue QExifImageHeader::value (ImageTag tag) const { return d->imageIfdValues.value(tag); } /*! Returns the value for an extended EXIF \a tag. */ QExifValue QExifImageHeader::value (ExifExtendedTag tag) const { return d->exifIfdValues.value(tag); } /*! Returns the value for a GPS tag. */ QExifValue QExifImageHeader::value (GpsTag tag) const { return d->gpsIfdValues.value(tag); } /*! Sets the \a value for an image \a tag. */ void QExifImageHeader::setValue (ImageTag tag, const QExifValue &value) { d->imageIfdValues[tag] = value; d->size = -1; } /*! Sets the \a value for an extended EXIF \a tag. */ void QExifImageHeader::setValue (ExifExtendedTag tag, const QExifValue &value) { d->exifIfdValues[tag] = value; d->size = -1; } /*! Sets the \a value for an GPS \a tag. */ void QExifImageHeader::setValue (GpsTag tag, const QExifValue &value) { d->gpsIfdValues[tag] = value; d->size = -1; } /*! Returns the image thumbnail. */ QImage QExifImageHeader::thumbnail () const { QImage image; image.loadFromData(d->thumbnailData, "JPG"); if (!d->thumbnailOrientation.isNull()) { switch (d->thumbnailOrientation.toShort()) { case 1: return image; case 2: return image.transformed(QTransform().rotate(180, Qt::YAxis)); case 3: return image.transformed(QTransform().rotate(180, Qt::ZAxis)); case 4: return image.transformed(QTransform().rotate(180, Qt::XAxis)); case 5: return image.transformed(QTransform().rotate(180, Qt::YAxis).rotate(90, Qt::ZAxis)); case 6: return image.transformed(QTransform().rotate(90, Qt::ZAxis)); case 7: return image.transformed(QTransform().rotate(180, Qt::XAxis).rotate(90, Qt::ZAxis)); case 8: return image.transformed(QTransform().rotate(270, Qt::ZAxis)); } } return image; } /*! Sets the image \a thumbnail. */ void QExifImageHeader::setThumbnail (const QImage &thumbnail) { if (!thumbnail.isNull()) { QBuffer buffer; if (buffer.open(QIODevice::WriteOnly) && thumbnail.save(&buffer, "JPG")) { buffer.close(); d->thumbnailSize = thumbnail.size(); d->thumbnailData = buffer.data(); d->thumbnailOrientation = QExifValue(); } } else { d->thumbnailSize = QSize(); d->thumbnailData = QByteArray(); } d->size = -1; } QByteArray QExifImageHeader::extractExif (QIODevice *device) const { QDataStream stream(device); stream.setByteOrder(QDataStream::BigEndian); if (device->read(2) != "\xFF\xD8") return QByteArray(); while (device->read(2) != "\xFF\xE1") { if (device->atEnd()) return QByteArray(); quint16 length; stream >> length; device->seek(device->pos() + length - 2); } quint16 length; stream >> length; if (device->read(4) != "Exif") return QByteArray(); device->read(2); return device->read(length - 8); } QList QExifImageHeader::readIfdHeaders (QDataStream &stream) const { QList headers; quint16 count; stream >> count; for (quint16 i = 0; i < count; i++) { ExifIfdHeader header; stream >> header; headers.append(header); } return headers; } QExifValue QExifImageHeader::readIfdValue (QDataStream &stream, int startPos, const ExifIfdHeader &header) const { switch (header.type) { case QExifValue::Byte: { QVector value(int(header.count)); if (header.count > 4) { stream.device()->seek(startPos + qint64(header.offset)); for (quint32 i = 0; i < header.count; i++) stream >> value[int(i)]; } else { for (quint32 i = 0; i < header.count; i++) value[int(i)] = header.offsetBytes[i]; } return QExifValue(value); } case QExifValue::Undefined: if (header.count > 4) { stream.device()->seek(startPos + qint64(header.offset)); return QExifValue(stream.device()->read(header.count)); } else { return QExifValue(QByteArray::fromRawData(header.offsetAscii, int(header.count))); } case QExifValue::Ascii: if (header.count > 4) { stream.device()->seek(startPos + qint64(header.offset)); QByteArray ascii = stream.device()->read(header.count); return QExifValue(QString::fromUtf8(ascii.constData(), ascii.size() - 1)); } else { return QExifValue(QString::fromUtf8(header.offsetAscii, int(header.count) - 1)); } case QExifValue::Short: { QVector value(int(header.count)); if (header.count > 2) { stream.device()->seek(startPos + qint64(header.offset)); for (quint32 i = 0; i < header.count; i++) stream >> value[int(i)]; } else { for (quint32 i = 0; i < header.count; i++) value[int(i)] = header.offsetShorts[i]; } return QExifValue(value); } case QExifValue::Long: { QVector value(int(header.count)); if (header.count > 1) { stream.device()->seek(startPos + qint64(header.offset)); for (quint32 i = 0; i < header.count; i++) stream >> value[int(i)]; } else if (header.count == 1) { value[0] = header.offset; } return QExifValue(value); } case QExifValue::SignedLong: { QVector value(int(header.count)); if (header.count > 1) { stream.device()->seek(startPos + qint64(header.offset)); for (quint32 i = 0; i < header.count; i++) stream >> value[int(i)]; } else if (header.count == 1) { value[0] = int(header.offset); } return QExifValue(value); } case QExifValue::Rational: { QVector value(int(header.count)); stream.device()->seek(startPos + qint64(header.offset)); for (quint32 i = 0; i < header.count; i++) stream >> value[int(i)]; return QExifValue(value); } case QExifValue::SignedRational: { QVector value(int(header.count)); stream.device()->seek(startPos + qint64(header.offset)); for (quint32 i = 0; i < header.count; i++) stream >> value[int(i)]; return QExifValue(value); } default: qWarning() << "Invalid Ifd Type" << header.type; return QExifValue(); } } template QMap QExifImageHeader::readIfdValues ( QDataStream &stream, int startPos, const QList &headers ) const { QMap values; // This needs to be non-const so it works with gcc3 QList headers_ = headers; foreach(const ExifIfdHeader &header, headers_) values[T(header.tag)] = readIfdValue(stream, startPos, header); return values; } template QMap QExifImageHeader::readIfdValues ( QDataStream &stream, int startPos, const QExifValue &pointer ) const { if (pointer.type() == QExifValue::Long && pointer.count() == 1) { stream.device()->seek(qint64(startPos) + pointer.toLong()); QList headers = readIfdHeaders(stream); return readIfdValues(stream, startPos, headers); } else { return QMap(); } } /*! Reads the contents of an EXIF header from an I/O \a device. Returns true if the header was read and false otherwise. \sa loadFromJpeg(), write() */ bool QExifImageHeader::read (QIODevice *device) { clear(); int startPos = int(device->pos()); QDataStream stream(device); QByteArray byteOrder = device->read(2); if (byteOrder == "II") { d->byteOrder = QSysInfo::LittleEndian; stream.setByteOrder(QDataStream::LittleEndian); } else if (byteOrder == "MM") { d->byteOrder = QSysInfo::BigEndian; stream.setByteOrder(QDataStream::BigEndian); } else { return false; } quint16 id; quint32 offset; stream >> id; stream >> offset; if (id != 0x002A) return false; device->seek(startPos + qint64(offset)); QList headers = readIfdHeaders(stream); stream >> offset; d->imageIfdValues = readIfdValues(stream, startPos, headers); QExifValue exifIfdPointer = d->imageIfdValues.take(ImageTag(ExifIfdPointer)); QExifValue gpsIfdPointer = d->imageIfdValues.take(ImageTag(GpsInfoIfdPointer)); d->exifIfdValues = readIfdValues(stream, startPos, exifIfdPointer); d->gpsIfdValues = readIfdValues(stream, startPos, gpsIfdPointer); d->exifIfdValues.remove(ExifExtendedTag(InteroperabilityIfdPointer)); if (offset) { device->seek(startPos + qint64(offset)); QMap thumbnailIfdValues = readIfdValues( stream, startPos, readIfdHeaders(stream)); QExifValue jpegOffset = thumbnailIfdValues.value(JpegInterchangeFormat); QExifValue jpegLength = thumbnailIfdValues.value(JpegInterchangeFormatLength); if (jpegOffset.type() == QExifValue::Long && jpegOffset.count() == 1 && jpegLength.type() == QExifValue::Long && jpegLength.count() == 1) { device->seek(startPos + qint64(jpegOffset.toLong())); d->thumbnailData = device->read(jpegLength.toLong()); d->thumbnailXResolution = thumbnailIfdValues.value(XResolution); d->thumbnailYResolution = thumbnailIfdValues.value(YResolution); d->thumbnailResolutionUnit = thumbnailIfdValues.value(ResolutionUnit); d->thumbnailOrientation = thumbnailIfdValues.value(Orientation); } } return true; } quint32 QExifImageHeader::writeExifHeader (QDataStream &stream, quint16 tag, const QExifValue &value, quint32 offset) const { stream << tag; stream << quint16(value.type()); stream << quint32(value.count()); switch (value.type()) { case QExifValue::Byte: if (value.count() <= 4) { foreach(quint8 byte, value.toByteVector()) stream << byte; for (int j = value.count(); j < 4; j++) stream << quint8(0); } else { stream << offset; offset += quint32(value.count()); } break; case QExifValue::Undefined: if (value.count() <= 4) { stream.device()->write(value.toByteArray()); if (value.count() < 4) stream.writeRawData("\0\0\0\0", 4 - value.count()); } else { stream << offset; offset += quint32(value.count()); } break; case QExifValue::Ascii: if (value.count() <= 4) { QByteArray bytes = value.toByteArray(); stream.writeRawData(bytes.constData(), value.count()); if (value.count() < 4) stream.writeRawData("\0\0\0\0", 4 - value.count()); } else { stream << offset; offset += quint32(value.count()); } break; case QExifValue::Short: if (value.count() <= 2) { foreach(quint16 shrt, value.toShortVector()) stream << shrt; for (int j = value.count(); j < 2; j++) stream << quint16(0); } else { stream << offset; offset += quint32(value.count()) * quint32(sizeof(quint16)); } break; case QExifValue::Long: if (value.count() == 0) { stream << quint32(0); } else if (value.count() == 1) { stream << value.toLong(); } else { stream << offset; offset += quint32(value.count()) * quint32(sizeof(quint32)); } break; case QExifValue::SignedLong: if (value.count() == 0) { stream << quint32(0); } else if (value.count() == 1) { stream << value.toSignedLong(); } else { stream << offset; offset += quint32(value.count()) * quint32(sizeof(quint32)); } break; case QExifValue::Rational: if (value.count() == 0) { stream << quint32(0); } else { stream << offset; offset += quint32(value.count()) * quint32(sizeof(quint32)) * 2; } break; case QExifValue::SignedRational: if (value.count() == 0) { stream << quint32(0); } else { stream << offset; offset += quint32(value.count()) * quint32(sizeof(quint32)) * 2; } break; default: qWarning() << "Invalid Ifd Type" << value.type(); stream << quint32(0); } return offset; } void QExifImageHeader::writeExifValue (QDataStream &stream, const QExifValue &value) const { switch (value.type()) { case QExifValue::Byte: if (value.count() > 4) foreach(quint8 byte, value.toByteVector()) stream << byte; break; case QExifValue::Undefined: if (value.count() > 4) stream.device()->write(value.toByteArray()); break; case QExifValue::Ascii: if (value.count() > 4) { QByteArray bytes = value.toByteArray(); stream.writeRawData(bytes.constData(), bytes.size() + 1); } break; case QExifValue::Short: if (value.count() > 2) foreach(quint16 shrt, value.toShortVector()) stream << shrt; break; case QExifValue::Long: if (value.count() > 1) foreach(quint32 lng, value.toLongVector()) stream << lng; break; case QExifValue::SignedLong: if (value.count() > 1) foreach(qint32 lng, value.toSignedLongVector()) stream << lng; break; case QExifValue::Rational: if (value.count() > 0) foreach(QExifURational rational, value.toRationalVector()) stream << rational; break; case QExifValue::SignedRational: if (value.count() > 0) foreach(QExifSRational rational, value.toSignedRationalVector()) stream << rational; break; default: qWarning() << "Invalid Ifd Type" << value.type(); break; } } template quint32 QExifImageHeader::writeExifHeaders ( QDataStream &stream, const QMap &values, quint32 offset ) const { offset += quint32(values.count() * 12); for (typename QMap::const_iterator i = values.constBegin(); i != values.constEnd(); i++) offset = writeExifHeader(stream, i.key(), i.value(), offset); return offset; } template void QExifImageHeader::writeExifValues ( QDataStream &stream, const QMap &values ) const { for (typename QMap::const_iterator i = values.constBegin(); i != values.constEnd(); i++) writeExifValue(stream, i.value()); } /*! Writes an EXIF header to an I/O \a device. Returns the total number of bytes written. */ qint64 QExifImageHeader::write (QIODevice *device) const { // #ifndef QT_NO_DEBUG qint64 startPos = device->pos(); // #endif QDataStream stream(device); if (d->byteOrder == QSysInfo::LittleEndian) { stream.setByteOrder(QDataStream::LittleEndian); device->write("II", 2); device->write("\x2A\x00", 2); device->write("\x08\x00\x00\x00", 4); } else if (d->byteOrder == QSysInfo::BigEndian) { stream.setByteOrder(QDataStream::BigEndian); device->write("MM", 2); device->write("\x00\x2A", 2); device->write("\x00\x00\x00\x08", 4); } quint16 count = quint16(d->imageIfdValues.count() + 1); quint32 offset = 26; if (!d->gpsIfdValues.isEmpty()) { count++; offset += 12; } stream << count; offset = writeExifHeaders(stream, d->imageIfdValues, offset); quint32 exifIfdOffset = offset; stream << quint16(ExifIfdPointer); stream << quint16(QExifValue::Long); stream << quint32(1); stream << exifIfdOffset; offset += calculateSize(d->exifIfdValues); quint32 gpsIfdOffset = offset; if (!d->gpsIfdValues.isEmpty()) { stream << quint16(GpsInfoIfdPointer); stream << quint16(QExifValue::Long); stream << quint32(1); stream << gpsIfdOffset; d->imageIfdValues.insert(ImageTag(GpsInfoIfdPointer), QExifValue(offset)); offset += calculateSize(d->gpsIfdValues); } if (!d->thumbnailData.isEmpty()) stream << offset; // Write offset to thumbnail Ifd. else stream << quint32(0); writeExifValues(stream, d->imageIfdValues); Q_ASSERT(startPos + exifIfdOffset == device->pos()); stream << quint16(d->exifIfdValues.count()); writeExifHeaders(stream, d->exifIfdValues, exifIfdOffset); writeExifValues(stream, d->exifIfdValues); Q_ASSERT(startPos + gpsIfdOffset == device->pos()); if (!d->gpsIfdValues.isEmpty()) { stream << quint16(d->gpsIfdValues.count()); writeExifHeaders(stream, d->gpsIfdValues, gpsIfdOffset); writeExifValues(stream, d->gpsIfdValues); } Q_ASSERT(startPos + offset == device->pos()); if (!d->thumbnailData.isEmpty()) { offset += 86; stream << quint16(7); QExifValue xResolution = d->thumbnailXResolution.isNull() ? QExifValue(QExifURational(72, 1)) : d->thumbnailXResolution; QExifValue yResolution = d->thumbnailYResolution.isNull() ? QExifValue(QExifURational(72, 1)) : d->thumbnailYResolution; QExifValue resolutionUnit = d->thumbnailResolutionUnit.isNull() ? QExifValue(quint16(2)) : d->thumbnailResolutionUnit; QExifValue orientation = d->thumbnailOrientation.isNull() ? QExifValue(quint16(0)) : d->thumbnailOrientation; writeExifHeader(stream, Compression, QExifValue(quint16(6)), offset); offset = writeExifHeader(stream, XResolution, xResolution, offset); offset = writeExifHeader(stream, YResolution, yResolution, offset); writeExifHeader(stream, ResolutionUnit, resolutionUnit, offset); writeExifHeader(stream, Orientation, orientation, offset); writeExifHeader(stream, JpegInterchangeFormat, QExifValue(offset), offset); writeExifHeader(stream, JpegInterchangeFormatLength, QExifValue(quint32(d->thumbnailData.size())), offset); writeExifValue(stream, xResolution); writeExifValue(stream, yResolution); Q_ASSERT(startPos + offset == device->pos()); device->write(d->thumbnailData); offset += quint32(d->thumbnailData.size()); } Q_ASSERT(startPos + offset == device->pos()); d->size = offset; return offset; } linphone-desktop-5.0.2/linphone-app/src/utils/QExifImageHeader.hpp000066400000000000000000000227521434616504300251410ustar00rootroot00000000000000/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt scene graph research project. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // This file was copied from Qt Extended 4.5 #ifndef QEXIFIMAGEHEADER_H_ #define QEXIFIMAGEHEADER_H_ #include #include #include #include #include #include typedef QPair QExifURational; typedef QPair QExifSRational; class QExifValuePrivate; class QExifValue { public: enum Type { Byte = 1, Ascii = 2, Short = 3, Long = 4, Rational = 5, Undefined = 7, SignedLong = 9, SignedRational = 10 }; enum TextEncoding { NoEncoding, AsciiEncoding, JisEncoding, UnicodeEncoding, UndefinedEncoding }; QExifValue (); QExifValue (quint8 value); QExifValue (const QVector &value); QExifValue (const QString &value, TextEncoding encoding = NoEncoding); QExifValue (quint16 value); QExifValue (const QVector &value); QExifValue (quint32 value); QExifValue (const QVector &value); QExifValue (const QExifURational &value); QExifValue (const QVector &value); QExifValue (const QByteArray &value); QExifValue (qint32 value); QExifValue (const QVector &value); QExifValue (const QExifSRational &value); QExifValue (const QVector &value); QExifValue (const QDateTime &value); QExifValue (const QExifValue &other); QExifValue &operator= (const QExifValue &other); ~QExifValue (); bool operator== (const QExifValue &other) const; bool isNull () const; int type () const; int count () const; TextEncoding encoding () const; quint8 toByte () const; QVector toByteVector () const; QString toString () const; quint16 toShort () const; QVector toShortVector () const; quint32 toLong () const; QVector toLongVector () const; QExifURational toRational () const; QVector toRationalVector () const; QByteArray toByteArray () const; qint32 toSignedLong () const; QVector toSignedLongVector () const; QExifSRational toSignedRational () const; QVector toSignedRationalVector () const; QDateTime toDateTime () const; private: QExplicitlySharedDataPointer d; }; struct ExifIfdHeader; class QExifImageHeaderPrivate; class QExifImageHeader { Q_DISABLE_COPY(QExifImageHeader) public: enum ImageTag { ImageWidth = 0x0100, ImageLength = 0x0101, BitsPerSample = 0x0102, Compression = 0x0103, PhotometricInterpretation = 0x0106, Orientation = 0x0112, SamplesPerPixel = 0x0115, PlanarConfiguration = 0x011C, YCbCrSubSampling = 0x0212, XResolution = 0x011A, YResolution = 0x011B, ResolutionUnit = 0x0128, StripOffsets = 0x0111, RowsPerStrip = 0x0116, StripByteCounts = 0x0117, TransferFunction = 0x012D, WhitePoint = 0x013E, PrimaryChromaciticies = 0x013F, YCbCrCoefficients = 0x0211, ReferenceBlackWhite = 0x0214, DateTime = 0x0132, ImageDescription = 0x010E, Make = 0x010F, Model = 0x0110, Software = 0x0131, Artist = 0x013B, Copyright = 0x8298 }; enum ExifExtendedTag { ExifVersion = 0x9000, FlashPixVersion = 0xA000, ColorSpace = 0xA001, ComponentsConfiguration = 0x9101, CompressedBitsPerPixel = 0x9102, PixelXDimension = 0xA002, PixelYDimension = 0xA003, MakerNote = 0x927C, UserComment = 0x9286, RelatedSoundFile = 0xA004, DateTimeOriginal = 0x9003, DateTimeDigitized = 0x9004, SubSecTime = 0x9290, SubSecTimeOriginal = 0x9291, SubSecTimeDigitized = 0x9292, ImageUniqueId = 0xA420, ExposureTime = 0x829A, FNumber = 0x829D, ExposureProgram = 0x8822, SpectralSensitivity = 0x8824, ISOSpeedRatings = 0x8827, Oecf = 0x8828, ShutterSpeedValue = 0x9201, ApertureValue = 0x9202, BrightnessValue = 0x9203, ExposureBiasValue = 0x9204, MaxApertureValue = 0x9205, SubjectDistance = 0x9206, MeteringMode = 0x9207, LightSource = 0x9208, Flash = 0x9209, FocalLength = 0x920A, SubjectArea = 0x9214, FlashEnergy = 0xA20B, SpatialFrequencyResponse = 0xA20C, FocalPlaneXResolution = 0xA20E, FocalPlaneYResolution = 0xA20F, FocalPlaneResolutionUnit = 0xA210, SubjectLocation = 0xA214, ExposureIndex = 0xA215, SensingMethod = 0xA217, FileSource = 0xA300, SceneType = 0xA301, CfaPattern = 0xA302, CustomRendered = 0xA401, ExposureMode = 0xA402, WhiteBalance = 0xA403, DigitalZoomRatio = 0xA404, FocalLengthIn35mmFilm = 0xA405, SceneCaptureType = 0xA406, GainControl = 0xA407, Contrast = 0xA408, Saturation = 0xA409, Sharpness = 0xA40A, DeviceSettingDescription = 0xA40B, SubjectDistanceRange = 0x40C }; enum GpsTag { GpsVersionId = 0x0000, GpsLatitudeRef = 0x0001, GpsLatitude = 0x0002, GpsLongitudeRef = 0x0003, GpsLongitude = 0x0004, GpsAltitudeRef = 0x0005, GpsAltitude = 0x0006, GpsTimeStamp = 0x0007, GpsSatellites = 0x0008, GpsStatus = 0x0009, GpsMeasureMode = 0x000A, GpsDop = 0x000B, GpsSpeedRef = 0x000C, GpsSpeed = 0x000D, GpsTrackRef = 0x000E, GpsTrack = 0x000F, GpsImageDirectionRef = 0x0010, GpsImageDirection = 0x0011, GpsMapDatum = 0x0012, GpsDestLatitudeRef = 0x0013, GpsDestLatitude = 0x0014, GpsDestLongitudeRef = 0x0015, GpsDestLongitude = 0x0016, GpsDestBearingRef = 0x0017, GpsDestBearing = 0x0018, GpsDestDistanceRef = 0x0019, GpsDestDistance = 0x001A, GpsProcessingMethod = 0x001B, GpsAreaInformation = 0x001C, GpsDateStamp = 0x001D, GpsDifferential = 0x001E }; QExifImageHeader (); explicit QExifImageHeader (const QString &fileName); ~QExifImageHeader (); bool loadFromJpeg (const QString &fileName); bool loadFromJpeg (QIODevice *device); bool saveToJpeg (const QString &fileName) const; bool saveToJpeg (QIODevice *device) const; bool read (QIODevice *device); qint64 write (QIODevice *device) const; qint64 size () const; QSysInfo::Endian byteOrder () const; void clear (); QList imageTags () const; QList extendedTags () const; QList gpsTags () const; bool contains (ImageTag tag) const; bool contains (ExifExtendedTag tag) const; bool contains (GpsTag tag) const; void remove (ImageTag tag); void remove (ExifExtendedTag tag); void remove (GpsTag tag); QExifValue value (ImageTag tag) const; QExifValue value (ExifExtendedTag tag) const; QExifValue value (GpsTag tag) const; void setValue (ImageTag tag, const QExifValue &value); void setValue (ExifExtendedTag tag, const QExifValue &value); void setValue (GpsTag tag, const QExifValue &value); QImage thumbnail () const; void setThumbnail (const QImage &thumbnail); private: enum PrivateTag { ExifIfdPointer = 0x8769, GpsInfoIfdPointer = 0x8825, InteroperabilityIfdPointer = 0xA005, JpegInterchangeFormat = 0x0201, JpegInterchangeFormatLength = 0x0202 }; QByteArray extractExif (QIODevice *device) const; QList readIfdHeaders (QDataStream &stream) const; QExifValue readIfdValue (QDataStream &stream, int startPos, const ExifIfdHeader &header) const; template QMap readIfdValues (QDataStream &stream, int startPos, const QList &headers) const; template QMap readIfdValues (QDataStream &stream, int startPos, const QExifValue &pointer) const; quint32 writeExifHeader (QDataStream &stream, quint16 tag, const QExifValue &value, quint32 offset) const; void writeExifValue (QDataStream &stream, const QExifValue &value) const; template quint32 writeExifHeaders (QDataStream &stream, const QMap &values, quint32 offset) const; template void writeExifValues (QDataStream &target, const QMap &values) const; quint32 sizeOf (const QExifValue &value) const; template quint32 calculateSize (const QMap &values) const; QExifImageHeaderPrivate *d; }; #endif // ifndef QEXIFIMAGEHEADER_H_ linphone-desktop-5.0.2/linphone-app/src/utils/UriTools.cpp000066400000000000000000000230221434616504300236130ustar00rootroot00000000000000/* * Copyright (c) 2010-2023 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // Library to deal with IRI and URI. // See: // IRI : https://tools.ietf.org/html/rfc3987 // URI : https://tools.ietf.org/html/rfc3986 // ============================================================================= #include "UriTools.hpp" static UriTools gUriTools; UriTools::UriTools(){ initRegularExpressions(); } QVector > UriTools::parseIri(const QString& text){ return parse(text, gUriTools.mIriRegularExpression); } QVector > UriTools::parseUri(const QString& text){ return parse(text, gUriTools.mUriRegularExpression); } // Parse a text and return all lines where regex is matched or not QVector > UriTools::parse(const QString& text, const QRegularExpression regex){ QVector > results; int currentIndex = 0; auto match = regex.match(text); for (int i = 0; i <= match.lastCapturedIndex(); ++i) { int startIndex = match.capturedStart(i); if(currentIndex != startIndex){ results.push_back({false, text.mid(currentIndex, startIndex - currentIndex)}); } results.push_back({true, match.captured(i)}); currentIndex = startIndex; } if(results.size() == 0) results.push_back({false, text}); else{ currentIndex += results.back().second.length(); if( currentIndex < text.size()) results.push_back({false, text.mid(currentIndex)}); } return results; } void UriTools::initRegularExpressions() { // Level 0. -------------------------------------------------------------------- QString URI_DEC_OCTET = QString("(?:") + "25[0-5]" + "|" + "2[0-4]\\d" + "|" + "1\\d{2}" + "|" + "[1-9]\\d" + "|" + "\\d" + ")"; QString URI_H16 = "[0-9A-Fa-f]{1,4}"; QString URI_PCT_ENCODED = "%[A-Fa-f\\d]{2}"; QString URI_PORT = "\\d*"; QString URI_SCHEME = "[a-zA-Z][\\w+\\.\\-]*"; QString URI_SUB_DELIMS = "[!$&\"()*+,;=]"; QString URI_UNRESERVED = "[\\w\\._~\\-]"; QString IRI_UCS_CHAR = QString("(?:") + "[\\x{00A0}-\\x{D7FF}]" + "|" + "[\\x{F900}-\\x{FDCF}]" + "|" + "[\\x{FDF0}-\\x{FFEF}]" + "|" + "[\\x{10000}-\\x{1FFFD}]" + "|" + "[\\x{20000}-\\x{2FFFD}]" + "|" + "[\\x{30000}-\\x{3FFFD}]" + //"|" + "[\\x{D800\\x{DC00}-\\x{D83F\\x{DFFD}]" + "|" + "[\\x{D840\\x{DC00}-\\x{D87F\\x{DFFD}]" + "|" + "[\\x{D880\\x{DC00}-\\x{D8BF\\x{DFFD}]" + "|" + "[\\x{40000}-\\x{4FFFD}]" + "|" + "[\\x{50000}-\\x{5FFFD}]" + "|" + "[\\x{60000}-\\x{6FFFD}]" + //"|" + "[\\x{D8C0\\x{DC00}-\\x{D8FF\\x{DFFD}]" + "|" + "[\\x{D900\\x{DC00}-\\x{D93F\\x{DFFD}]" + "|" + "[\\x{D940\\x{DC00}-\\x{D97F\\x{DFFD}]" + "|" + "[\\x{70000}-\\x{7FFFD}]" + "|" + "[\\x{80000}-\\x{8FFFD}]" + "|" + "[\\x{90000}-\\x{9FFFD}]" + //"|" + "[\\x{D980\\x{DC00}-\\x{D9BF\\x{DFFD}]" + "|" + "[\\x{D9C0\\x{DC00}-\\x{D9FF\\x{DFFD}]" + "|" + "[\\x{DA00\\x{DC00}-\\x{DA3F\\x{DFFD}]" + "|" + "[\\x{A0000}-\\x{AFFFD}]" + "|" + "[\\x{B0000}-\\x{BFFFD}]" + "|" + "[\\x{C0000}-\\x{CFFFD}]" + //"|" + "[\\x{DA40\\x{DC00}-\\x{DA7F\\x{DFFD}]" + "|" + "[\\x{DA80\\x{DC00}-\\x{DABF\\x{DFFD}]" + "|" + "[\\x{DAC0\\x{DC00}-\\x{DAFF\\x{DFFD}]" + "|" + "[\\x{D0000}-\\x{DFFFD}]" + "|" + "[\\x{E1000}-\\x{EFFFD}]" + //"|" + "[\\x{DB00\\x{DC00}-\\x{DB3F\\x{DFFD}]" + "|" + "[\\x{DB44\\x{DC00}-\\x{DB7F\\x{DFFD}]" + ")"; QString IRI_PRIVATE = QString("(?:") + "[\\x{E000}-\\x{F8FF}]" + "|" + "[\\x{F0000}-\\x{FFFFD}]" + "|" + "[\\x{100000}-\\x{10FFFD}]" + //"|" + "[\\x{DBC0\\x{DC00}-\\x{DBFF\\x{DFFD}]" + "|" + "[\\x{DBC0\\x{DC00}-\\x{DBFF\\x{DFFD}]" + ")"; // Level 1. -------------------------------------------------------------------- QString URI_IPV_FUTURE = QString("v[0-9A-Fa-f]+\\.") + "(?:" + URI_UNRESERVED + URI_SUB_DELIMS + ":" + ")"; QString IRI_UNRESERVED = QString("(?:") + "[\\w\\._~\\-]" + "|" + IRI_UCS_CHAR + ")"; QString URI_IPV4_ADDRESS = URI_DEC_OCTET + "\\." + URI_DEC_OCTET + "\\." + URI_DEC_OCTET + "\\." + URI_DEC_OCTET; QString URI_PCHAR = "(?:" + URI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + "|" + "[:@]" + ")"; QString URI_REG_NAME = "(?:" + URI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + ")*"; QString URI_USERINFO = "(?:" + URI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + "|" + ":" + ")*"; // Level 2. -------------------------------------------------------------------- QString URI_FRAGMENT = "(?:" + URI_PCHAR + "|" + "[/?]" + ")*"; QString URI_LS32 = "(?:" + URI_H16 + ":" + URI_H16 + "|" + URI_IPV4_ADDRESS + ")"; QString URI_QUERY = "(?:" + URI_PCHAR + "|" + "[/?]" + ")*"; QString URI_SEGMENT = URI_PCHAR + "*"; QString URI_SEGMENT_NZ = URI_PCHAR + "+"; QString IRI_PCHAR = "(?:" + IRI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + "|" + "[:@]" + ")"; QString IRI_REG_NAME = "(?:" + IRI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + ")*"; QString IRI_USERINFO = "(?:" + IRI_UNRESERVED + "|" + URI_PCT_ENCODED + "|" + URI_SUB_DELIMS + "|" + ":" + ")*"; // Level 3. -------------------------------------------------------------------- QString URI_IPV6_ADDRESS = QString("(?:") + "(?:" + URI_H16 + ":){6}" + URI_LS32 + "|" + "::(?:" + URI_H16 + ":){5}" + URI_LS32 + "|" + "\\[" + URI_H16 + "\\]::(?:" + URI_H16 + ":){4}" + URI_LS32 + "|" + "\\[" + "(?:" + URI_H16 + ":)?" + URI_H16 + "\\]::(?:" + URI_H16 + ":){3}" + URI_LS32 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,2}" + URI_H16 + "\\]::(?:" + URI_H16 + ":){2}" + URI_LS32 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,3}" + URI_H16 + "\\]::" + URI_H16 + ":" + URI_LS32 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,4}" + URI_H16 + "\\]::" + URI_LS32 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,5}" + URI_H16 + "\\]::" + URI_H16 + "|" + "\\[" + "(?:" + URI_H16 + ":){0,6}" + URI_H16 + "\\]::" + ")"; QString URI_PATH_ABEMPTY = QString("(?:") + "/" + URI_SEGMENT + ")*"; QString URI_PATH_ABSOLUTE = QString("/") + "(?:" + URI_SEGMENT_NZ + "(?:" + "/" + URI_SEGMENT + ")*" + ")?"; QString URI_PATH_ROOTLESS = URI_SEGMENT_NZ + "(?:" + "/" + URI_SEGMENT + ")*"; QString IRI_FRAGMENT = "(?:" + IRI_PCHAR + "|" + "[/?]" + ")*"; QString IRI_QUERY = "(?:" + IRI_PCHAR + "|" + IRI_PRIVATE + "|" + "[/?]" + ")*"; QString IRI_SEGMENT = IRI_PCHAR + "*"; QString IRI_SEGMENT_NZ = IRI_PCHAR + "+"; // Level 4. -------------------------------------------------------------------- QString URI_IP_LITERAL = QString("\\[" )+ "(?:" + URI_IPV6_ADDRESS + "|" + URI_IPV_FUTURE + ")" + "\\]"; QString IRI_PATH_ABEMPTY = QString("(?:") + "/" + IRI_SEGMENT + ")*"; QString IRI_PATH_ABSOLUTE = QString("/") + "(?:" + IRI_SEGMENT_NZ + "(?:" + "/" + IRI_SEGMENT + ")*" + ")?"; QString IRI_PATH_ROOTLESS = IRI_SEGMENT_NZ + "(?:" + "/" + IRI_SEGMENT + ")*"; // Level 5. -------------------------------------------------------------------- QString URI_HOST = "(?:" + URI_REG_NAME + "|" + URI_IPV4_ADDRESS + "|" + URI_IP_LITERAL + ")"; QString IRI_HOST = "(?:" + IRI_REG_NAME + "|" + URI_IPV4_ADDRESS + "|" + URI_IP_LITERAL + ")"; // Level 6. -------------------------------------------------------------------- QString URI_AUTHORITY = "(?:" + URI_USERINFO + "@" + ")?" + URI_HOST + "(?:" + ":" + URI_PORT + ")?"; QString IRI_AUTHORITY = "(?:" + IRI_USERINFO + "@" + ")?" + IRI_HOST + "(?:" + ":" + URI_PORT + ")?"; // Level 7. -------------------------------------------------------------------- // `path-empty` not used. QString URI_HIER_PART = QString("(?:") + "//" + URI_AUTHORITY + URI_PATH_ABEMPTY + "|" + URI_PATH_ABSOLUTE + "|" + URI_PATH_ROOTLESS + ")"; QString IRI_HIER_PART = QString("(?:") + "//" + IRI_AUTHORITY + IRI_PATH_ABEMPTY + "|" + IRI_PATH_ABSOLUTE + "|" + IRI_PATH_ROOTLESS + ")"; // Level 8. -------------------------------------------------------------------- // Regex to match URI. It respects the RFC 3986. QString URI = "(?:" + URI_SCHEME + ":" + "|" + "www\\." + ")" + URI_HIER_PART + "(?:" + "\\?" + URI_QUERY + ")?" + "(?:" + "#" + URI_FRAGMENT + ")?"; // Regex to match URI. It respects the RFC 3987. QString IRI = "(?:" + URI_SCHEME + ":" + "|" + "www\\." + ")" + IRI_HIER_PART + "(?:" + "\\?" + IRI_QUERY + ")?" + "(?:" + "#" + IRI_FRAGMENT + ")?"; mIriRegularExpression = QRegularExpression(IRI,QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); mUriRegularExpression = QRegularExpression(URI,QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption); } linphone-desktop-5.0.2/linphone-app/src/utils/UriTools.hpp000066400000000000000000000037351434616504300236310ustar00rootroot00000000000000/* * Copyright (c) 2010-2023 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // Library to deal with IRI and URI. // See: // IRI : https://tools.ietf.org/html/rfc3987 // NOTE : Unicodes after \uFFFF are not supported by the QML RegExp (or the right syntax has not been found) : "Invalid regular expression" (even with surrogate pairs). Parts have been commented out for latter use. // URI : https://tools.ietf.org/html/rfc3986 // ============================================================================= #ifndef URI_TOOLS_H #define URI_TOOLS_H #include #include #include #include class UriTools{ public: UriTools(); bool mSupportUrl = true; static QVector > parseIri(const QString& text); static QVector > parseUri(const QString& text); static QRegularExpression getRegularExpression(); private: void initRegularExpressions(); static QVector > parse(const QString& text, const QRegularExpression regex); QRegularExpression mIriRegularExpression;//https://tools.ietf.org/html/rfc3987 QRegularExpression mUriRegularExpression;//https://tools.ietf.org/html/rfc3986 }; #endiflinphone-desktop-5.0.2/linphone-app/src/utils/Utils.cpp000066400000000000000000001266301434616504300231440ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "Utils.hpp" #include "UriTools.hpp" #include "components/core/CoreManager.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "app/paths/Paths.hpp" #ifdef _WIN32 #include #endif // ============================================================================= namespace { constexpr int SafeFilePathLimit = 100; } std::shared_ptr Utils::interpretUrl(const QString& address){ auto interpretedAddress = CoreManager::getInstance()->getCore()->interpretUrl(Utils::appStringToCoreString(address), true); if(!interpretedAddress){// Try by removing scheme. QStringList splitted = address.split(":"); if(splitted.size() > 0 && splitted[0] == "sip"){ splitted.removeFirst(); interpretedAddress = CoreManager::getInstance()->getCore()->interpretUrl(Utils::appStringToCoreString(splitted.join(":")), true); } } return interpretedAddress; } char *Utils::rstrstr (const char *a, const char *b) { size_t a_len = strlen(a); size_t b_len = strlen(b); if (b_len > a_len) return nullptr; for (const char *s = a + a_len - b_len; s >= a; --s) { if (!strncmp(s, b, b_len)) return const_cast(s); } return nullptr; } // ----------------------------------------------------------------------------- bool Utils::hasCapability(const QString& address, const LinphoneEnums::FriendCapability& capability){ auto addressCleaned = cleanSipAddress(address); auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(addressCleaned); if(contact) return contact->hasCapability(capability); else return false; } QDateTime Utils::addMinutes(QDateTime date, const int& min){ return date.addSecs(min*60); } QDateTime Utils::getOffsettedUTC(const QDateTime& date){ QDateTime utc = date.toUTC();// Get a date free of any offsets. auto timezone = date.timeZone(); utc = utc.addSecs(timezone.offsetFromUtc(date));// add offset from date timezone utc.setTimeSpec(Qt::UTC);// ensure to have an UTC date return utc; } QString Utils::toDateTimeString(QDateTime date){ if(date.date() == QDate::currentDate()) return toTimeString(date); else{ return getOffsettedUTC(date).toString("yyyy/MM/dd hh:mm:ss"); } } QString Utils::toTimeString(QDateTime date, const QString& format){ // Issue : date.toString() will not print the good time in timezones. Get it from date and add ourself the offset. return getOffsettedUTC(date).toString(format); } QString Utils::toDateString(QDateTime date){ return getOffsettedUTC(date).toString("yyyy/MM/dd"); } QString Utils::getDisplayName(const QString& address){ return getDisplayName(interpretUrl(address)); } QString Utils::toString(const LinphoneEnums::TunnelMode& mode){ switch(mode){ case LinphoneEnums::TunnelMode::TunnelModeEnable : //: 'Enable' : One word for button action to enable tunnel mode. return QObject::tr("LinphoneEnums_TunnelModeEnable"); case LinphoneEnums::TunnelMode::TunnelModeDisable : //: 'Disable' : One word for button action to disable tunnel mode. return QObject::tr("LinphoneEnums_TunnelModeDisable"); case LinphoneEnums::TunnelMode::TunnelModeAuto : //: 'Auto' : One word for button action to set the auto tunnel mode. return QObject::tr("LinphoneEnums_TunnelModeAuto"); default: return ""; } } QImage Utils::getImage(const QString &pUri) { QImage image(pUri); QImageReader reader(pUri); reader.setAutoTransform(true); if(image.isNull()){// Try to determine format from headers instead of using suffix reader.setDecideFormatFromContent(true); } return reader.read(); } QString Utils::getSafeFilePath (const QString &filePath, bool *soFarSoGood) { if (soFarSoGood) *soFarSoGood = true; QFileInfo info(filePath); if (!info.exists()) return filePath; const QString prefix = QStringLiteral("%1/%2").arg(info.absolutePath()).arg(info.baseName()); const QString ext = info.completeSuffix(); for (int i = 1; i < SafeFilePathLimit; ++i) { QString safePath = QStringLiteral("%1 (%3).%4").arg(prefix).arg(i).arg(ext); if (!QFileInfo::exists(safePath)) return safePath; } if (soFarSoGood) *soFarSoGood = false; return QString(""); } std::shared_ptr Utils::getMatchingLocalAddress(std::shared_ptr p_localAddress){ QVector > addresses; // Get default account addresses.push_back(CoreManager::getInstance()->getCore()->createPrimaryContactParsed()); auto accounts = CoreManager::getInstance()->getAccountList(); foreach(auto account, accounts) addresses.push_back(account->getParams()->getIdentityAddress()->clone()); foreach(auto address, addresses){ if( address->getUsername() == p_localAddress->getUsername() && address->getDomain() == p_localAddress->getDomain()) return address; } return p_localAddress; } // Return at most : sip:username@domain QString Utils::cleanSipAddress (const QString &sipAddress) { std::shared_ptr addr = linphone::Factory::get()->createAddress(sipAddress.toStdString()); if( addr) { addr->clean(); QStringList fields = Utils::coreStringToAppString(addr->asStringUriOnly()).split('@'); if(fields.size() > 0){// maybe useless but it's just to be sure to have a domain fields.removeLast(); QString domain = Utils::coreStringToAppString(addr->getDomain()); if( domain.count(':')>1) fields.append('['+domain+']'); else fields.append(domain); return fields.join('@'); } } return sipAddress; } // Data to retrieve WIN32 process #ifdef _WIN32 #include struct EnumData { DWORD dwProcessId; HWND hWnd; }; // Application-defined callback for EnumWindows BOOL CALLBACK EnumProc(HWND hWnd, LPARAM lParam) { // Retrieve storage location for communication data EnumData& ed = *(EnumData*)lParam; DWORD dwProcessId = 0x0; // Query process ID for hWnd GetWindowThreadProcessId(hWnd, &dwProcessId); // Apply filter - if you want to implement additional restrictions, // this is the place to do so. if (ed.dwProcessId == dwProcessId) { // Found a window matching the process ID ed.hWnd = hWnd; // Report success SetLastError(ERROR_SUCCESS); // Stop enumeration return FALSE; } // Continue enumeration return TRUE; } // Main entry HWND FindWindowFromProcessId(DWORD dwProcessId) { EnumData ed = { dwProcessId }; if (!EnumWindows(EnumProc, (LPARAM)&ed) && (GetLastError() == ERROR_SUCCESS)) { return ed.hWnd; } return NULL; } // Helper method for convenience HWND FindWindowFromProcess(HANDLE hProcess) { return FindWindowFromProcessId(GetProcessId(hProcess)); } #endif bool Utils::processExists(const quint64& p_processId) { #ifdef _WIN32 return FindWindowFromProcessId(p_processId) != NULL; #else return true; #endif } QString Utils::getCountryName(const QLocale::Country& p_country) { QString countryName; switch(p_country) { case QLocale::Afghanistan: if((countryName = QCoreApplication::translate("country", "Afghanistan"))== "Afghanistan") countryName = "";break; case QLocale::Albania: if((countryName = QCoreApplication::translate("country", "Albania"))== "Albania") countryName = "";break; case QLocale::Algeria: if((countryName = QCoreApplication::translate("country", "Algeria"))== "Algeria") countryName = "";break; case QLocale::AmericanSamoa: if((countryName = QCoreApplication::translate("country", "AmericanSamoa"))== "AmericanSamoa") countryName = "";break; case QLocale::Andorra: if((countryName = QCoreApplication::translate("country", "Andorra"))== "Andorra") countryName = "";break; case QLocale::Angola: if((countryName = QCoreApplication::translate("country", "Angola"))== "Angola") countryName = "";break; case QLocale::Anguilla: if((countryName = QCoreApplication::translate("country", "Anguilla"))== "Anguilla") countryName = "";break; case QLocale::AntiguaAndBarbuda: if((countryName = QCoreApplication::translate("country", "AntiguaAndBarbuda"))== "AntiguaAndBarbuda") countryName = "";break; case QLocale::Argentina: if((countryName = QCoreApplication::translate("country", "Argentina"))== "Argentina") countryName = "";break; case QLocale::Armenia: if((countryName = QCoreApplication::translate("country", "Armenia"))== "Armenia") countryName = "";break; case QLocale::Aruba: if((countryName = QCoreApplication::translate("country", "Aruba"))== "Aruba") countryName = "";break; case QLocale::Australia: if((countryName = QCoreApplication::translate("country", "Australia"))== "Australia") countryName = "";break; case QLocale::Austria: if((countryName = QCoreApplication::translate("country", "Austria"))== "Austria") countryName = "";break; case QLocale::Azerbaijan: if((countryName = QCoreApplication::translate("country", "Azerbaijan"))== "Azerbaijan") countryName = "";break; case QLocale::Bahamas: if((countryName = QCoreApplication::translate("country", "Bahamas"))== "Bahamas") countryName = "";break; case QLocale::Bahrain: if((countryName = QCoreApplication::translate("country", "Bahrain"))== "Bahrain") countryName = "";break; case QLocale::Bangladesh: if((countryName = QCoreApplication::translate("country", "Bangladesh"))== "Bangladesh") countryName = "";break; case QLocale::Barbados: if((countryName = QCoreApplication::translate("country", "Barbados"))== "Barbados") countryName = "";break; case QLocale::Belarus: if((countryName = QCoreApplication::translate("country", "Belarus"))== "Belarus") countryName = "";break; case QLocale::Belgium: if((countryName = QCoreApplication::translate("country", "Belgium"))== "Belgium") countryName = "";break; case QLocale::Belize: if((countryName = QCoreApplication::translate("country", "Belize"))== "Belize") countryName = "";break; case QLocale::Benin: if((countryName = QCoreApplication::translate("country", "Benin"))== "Benin") countryName = "";break; case QLocale::Bermuda: if((countryName = QCoreApplication::translate("country", "Bermuda"))== "Bermuda") countryName = "";break; case QLocale::Bhutan: if((countryName = QCoreApplication::translate("country", "Bhutan"))== "Bhutan") countryName = "";break; case QLocale::Bolivia: if((countryName = QCoreApplication::translate("country", "Bolivia"))== "Bolivia") countryName = "";break; case QLocale::BosniaAndHerzegowina: if((countryName = QCoreApplication::translate("country", "BosniaAndHerzegowina"))== "BosniaAndHerzegowina") countryName = "";break; case QLocale::Botswana: if((countryName = QCoreApplication::translate("country", "Botswana"))== "Botswana") countryName = "";break; case QLocale::Brazil: if((countryName = QCoreApplication::translate("country", "Brazil"))== "Brazil") countryName = "";break; case QLocale::Brunei: if((countryName = QCoreApplication::translate("country", "Brunei"))== "Brunei") countryName = "";break; case QLocale::Bulgaria: if((countryName = QCoreApplication::translate("country", "Bulgaria"))== "Bulgaria") countryName = "";break; case QLocale::BurkinaFaso: if((countryName = QCoreApplication::translate("country", "BurkinaFaso"))== "BurkinaFaso") countryName = "";break; case QLocale::Burundi: if((countryName = QCoreApplication::translate("country", "Burundi"))== "Burundi") countryName = "";break; case QLocale::Cambodia: if((countryName = QCoreApplication::translate("country", "Cambodia"))== "Cambodia") countryName = "";break; case QLocale::Cameroon: if((countryName = QCoreApplication::translate("country", "Cameroon"))== "Cameroon") countryName = "";break; case QLocale::Canada: if((countryName = QCoreApplication::translate("country", "Canada"))== "Canada") countryName = "";break; case QLocale::CapeVerde: if((countryName = QCoreApplication::translate("country", "CapeVerde"))== "CapeVerde") countryName = "";break; case QLocale::CaymanIslands: if((countryName = QCoreApplication::translate("country", "CaymanIslands"))== "CaymanIslands") countryName = "";break; case QLocale::CentralAfricanRepublic: if((countryName = QCoreApplication::translate("country", "CentralAfricanRepublic"))== "CentralAfricanRepublic") countryName = "";break; case QLocale::Chad: if((countryName = QCoreApplication::translate("country", "Chad"))== "Chad") countryName = "";break; case QLocale::Chile: if((countryName = QCoreApplication::translate("country", "Chile"))== "Chile") countryName = "";break; case QLocale::China: if((countryName = QCoreApplication::translate("country", "China"))== "China") countryName = "";break; case QLocale::Colombia: if((countryName = QCoreApplication::translate("country", "Colombia"))== "Colombia") countryName = "";break; case QLocale::Comoros: if((countryName = QCoreApplication::translate("country", "Comoros"))== "Comoros") countryName = "";break; case QLocale::PeoplesRepublicOfCongo: if((countryName = QCoreApplication::translate("country", "PeoplesRepublicOfCongo"))== "PeoplesRepublicOfCongo") countryName = "";break; case QLocale::DemocraticRepublicOfCongo: if((countryName = QCoreApplication::translate("country", "DemocraticRepublicOfCongo"))== "DemocraticRepublicOfCongo") countryName = "";break; case QLocale::CookIslands: if((countryName = QCoreApplication::translate("country", "CookIslands"))== "CookIslands") countryName = "";break; case QLocale::CostaRica: if((countryName = QCoreApplication::translate("country", "CostaRica"))== "CostaRica") countryName = "";break; case QLocale::IvoryCoast: if((countryName = QCoreApplication::translate("country", "IvoryCoast"))== "IvoryCoast") countryName = "";break; case QLocale::Croatia: if((countryName = QCoreApplication::translate("country", "Croatia"))== "Croatia") countryName = "";break; case QLocale::Cuba: if((countryName = QCoreApplication::translate("country", "Cuba"))== "Cuba") countryName = "";break; case QLocale::Cyprus: if((countryName = QCoreApplication::translate("country", "Cyprus"))== "Cyprus") countryName = "";break; case QLocale::CzechRepublic: if((countryName = QCoreApplication::translate("country", "CzechRepublic"))== "CzechRepublic") countryName = "";break; case QLocale::Denmark: if((countryName = QCoreApplication::translate("country", "Denmark"))== "Denmark") countryName = "";break; case QLocale::Djibouti: if((countryName = QCoreApplication::translate("country", "Djibouti"))== "Djibouti") countryName = "";break; case QLocale::Dominica: if((countryName = QCoreApplication::translate("country", "Dominica"))== "Dominica") countryName = "";break; case QLocale::DominicanRepublic: if((countryName = QCoreApplication::translate("country", "DominicanRepublic"))== "DominicanRepublic") countryName = "";break; case QLocale::Ecuador: if((countryName = QCoreApplication::translate("country", "Ecuador"))== "Ecuador") countryName = "";break; case QLocale::Egypt: if((countryName = QCoreApplication::translate("country", "Egypt"))== "Egypt") countryName = "";break; case QLocale::ElSalvador: if((countryName = QCoreApplication::translate("country", "ElSalvador"))== "ElSalvador") countryName = "";break; case QLocale::EquatorialGuinea: if((countryName = QCoreApplication::translate("country", "EquatorialGuinea"))== "EquatorialGuinea") countryName = "";break; case QLocale::Eritrea: if((countryName = QCoreApplication::translate("country", "Eritrea"))== "Eritrea") countryName = "";break; case QLocale::Estonia: if((countryName = QCoreApplication::translate("country", "Estonia"))== "Estonia") countryName = "";break; case QLocale::Ethiopia: if((countryName = QCoreApplication::translate("country", "Ethiopia"))== "Ethiopia") countryName = "";break; case QLocale::FalklandIslands: if((countryName = QCoreApplication::translate("country", "FalklandIslands"))== "FalklandIslands") countryName = "";break; case QLocale::FaroeIslands: if((countryName = QCoreApplication::translate("country", "FaroeIslands"))== "FaroeIslands") countryName = "";break; case QLocale::Fiji: if((countryName = QCoreApplication::translate("country", "Fiji"))== "Fiji") countryName = "";break; case QLocale::Finland: if((countryName = QCoreApplication::translate("country", "Finland"))== "Finland") countryName = "";break; case QLocale::France: if((countryName = QCoreApplication::translate("country", "France"))== "France") countryName = "";break; case QLocale::FrenchGuiana: if((countryName = QCoreApplication::translate("country", "FrenchGuiana"))== "FrenchGuiana") countryName = "";break; case QLocale::FrenchPolynesia: if((countryName = QCoreApplication::translate("country", "FrenchPolynesia"))== "FrenchPolynesia") countryName = "";break; case QLocale::Gabon: if((countryName = QCoreApplication::translate("country", "Gabon"))== "Gabon") countryName = "";break; case QLocale::Gambia: if((countryName = QCoreApplication::translate("country", "Gambia"))== "Gambia") countryName = "";break; case QLocale::Georgia: if((countryName = QCoreApplication::translate("country", "Georgia"))== "Georgia") countryName = "";break; case QLocale::Germany: if((countryName = QCoreApplication::translate("country", "Germany"))== "Germany") countryName = "";break; case QLocale::Ghana: if((countryName = QCoreApplication::translate("country", "Ghana"))== "Ghana") countryName = "";break; case QLocale::Gibraltar: if((countryName = QCoreApplication::translate("country", "Gibraltar"))== "Gibraltar") countryName = "";break; case QLocale::Greece: if((countryName = QCoreApplication::translate("country", "Greece"))== "Greece") countryName = "";break; case QLocale::Greenland: if((countryName = QCoreApplication::translate("country", "Greenland"))== "Greenland") countryName = "";break; case QLocale::Grenada: if((countryName = QCoreApplication::translate("country", "Grenada"))== "Grenada") countryName = "";break; case QLocale::Guadeloupe: if((countryName = QCoreApplication::translate("country", "Guadeloupe"))== "Guadeloupe") countryName = "";break; case QLocale::Guam: if((countryName = QCoreApplication::translate("country", "Guam"))== "Guam") countryName = "";break; case QLocale::Guatemala: if((countryName = QCoreApplication::translate("country", "Guatemala"))== "Guatemala") countryName = "";break; case QLocale::Guinea: if((countryName = QCoreApplication::translate("country", "Guinea"))== "Guinea") countryName = "";break; case QLocale::GuineaBissau: if((countryName = QCoreApplication::translate("country", "GuineaBissau"))== "GuineaBissau") countryName = "";break; case QLocale::Guyana: if((countryName = QCoreApplication::translate("country", "Guyana"))== "Guyana") countryName = "";break; case QLocale::Haiti: if((countryName = QCoreApplication::translate("country", "Haiti"))== "Haiti") countryName = "";break; case QLocale::Honduras: if((countryName = QCoreApplication::translate("country", "Honduras"))== "Honduras") countryName = "";break; case QLocale::HongKong: if((countryName = QCoreApplication::translate("country", "HongKong"))== "HongKong") countryName = "";break; case QLocale::Hungary: if((countryName = QCoreApplication::translate("country", "Hungary"))== "Hungary") countryName = "";break; case QLocale::Iceland: if((countryName = QCoreApplication::translate("country", "Iceland"))== "Iceland") countryName = "";break; case QLocale::India: if((countryName = QCoreApplication::translate("country", "India"))== "India") countryName = "";break; case QLocale::Indonesia: if((countryName = QCoreApplication::translate("country", "Indonesia"))== "Indonesia") countryName = "";break; case QLocale::Iran: if((countryName = QCoreApplication::translate("country", "Iran"))== "Iran") countryName = "";break; case QLocale::Iraq: if((countryName = QCoreApplication::translate("country", "Iraq"))== "Iraq") countryName = "";break; case QLocale::Ireland: if((countryName = QCoreApplication::translate("country", "Ireland"))== "Ireland") countryName = "";break; case QLocale::Israel: if((countryName = QCoreApplication::translate("country", "Israel"))== "Israel") countryName = "";break; case QLocale::Italy: if((countryName = QCoreApplication::translate("country", "Italy"))== "Italy") countryName = "";break; case QLocale::Jamaica: if((countryName = QCoreApplication::translate("country", "Jamaica"))== "Jamaica") countryName = "";break; case QLocale::Japan: if((countryName = QCoreApplication::translate("country", "Japan"))== "Japan") countryName = "";break; case QLocale::Jordan: if((countryName = QCoreApplication::translate("country", "Jordan"))== "Jordan") countryName = "";break; case QLocale::Kazakhstan: if((countryName = QCoreApplication::translate("country", "Kazakhstan"))== "Kazakhstan") countryName = "";break; case QLocale::Kenya: if((countryName = QCoreApplication::translate("country", "Kenya"))== "Kenya") countryName = "";break; case QLocale::Kiribati: if((countryName = QCoreApplication::translate("country", "Kiribati"))== "Kiribati") countryName = "";break; case QLocale::DemocraticRepublicOfKorea: if((countryName = QCoreApplication::translate("country", "DemocraticRepublicOfKorea"))== "DemocraticRepublicOfKorea") countryName = "";break; case QLocale::RepublicOfKorea: if((countryName = QCoreApplication::translate("country", "RepublicOfKorea"))== "RepublicOfKorea") countryName = "";break; case QLocale::Kuwait: if((countryName = QCoreApplication::translate("country", "Kuwait"))== "Kuwait") countryName = "";break; case QLocale::Kyrgyzstan: if((countryName = QCoreApplication::translate("country", "Kyrgyzstan"))== "Kyrgyzstan") countryName = "";break; case QLocale::Laos: if((countryName = QCoreApplication::translate("country", "Laos"))== "Laos") countryName = "";break; case QLocale::Latvia: if((countryName = QCoreApplication::translate("country", "Latvia"))== "Latvia") countryName = "";break; case QLocale::Lebanon: if((countryName = QCoreApplication::translate("country", "Lebanon"))== "Lebanon") countryName = "";break; case QLocale::Lesotho: if((countryName = QCoreApplication::translate("country", "Lesotho"))== "Lesotho") countryName = "";break; case QLocale::Liberia: if((countryName = QCoreApplication::translate("country", "Liberia"))== "Liberia") countryName = "";break; case QLocale::Libya: if((countryName = QCoreApplication::translate("country", "Libya"))== "Libya") countryName = "";break; case QLocale::Liechtenstein: if((countryName = QCoreApplication::translate("country", "Liechtenstein"))== "Liechtenstein") countryName = "";break; case QLocale::Lithuania: if((countryName = QCoreApplication::translate("country", "Lithuania"))== "Lithuania") countryName = "";break; case QLocale::Luxembourg: if((countryName = QCoreApplication::translate("country", "Luxembourg"))== "Luxembourg") countryName = "";break; case QLocale::Macau: if((countryName = QCoreApplication::translate("country", "Macau"))== "Macau") countryName = "";break; case QLocale::Macedonia: if((countryName = QCoreApplication::translate("country", "Macedonia"))== "Macedonia") countryName = "";break; case QLocale::Madagascar: if((countryName = QCoreApplication::translate("country", "Madagascar"))== "Madagascar") countryName = "";break; case QLocale::Malawi: if((countryName = QCoreApplication::translate("country", "Malawi"))== "Malawi") countryName = "";break; case QLocale::Malaysia: if((countryName = QCoreApplication::translate("country", "Malaysia"))== "Malaysia") countryName = "";break; case QLocale::Maldives: if((countryName = QCoreApplication::translate("country", "Maldives"))== "Maldives") countryName = "";break; case QLocale::Mali: if((countryName = QCoreApplication::translate("country", "Mali"))== "Mali") countryName = "";break; case QLocale::Malta: if((countryName = QCoreApplication::translate("country", "Malta"))== "Malta") countryName = "";break; case QLocale::MarshallIslands: if((countryName = QCoreApplication::translate("country", "MarshallIslands"))== "MarshallIslands") countryName = "";break; case QLocale::Martinique: if((countryName = QCoreApplication::translate("country", "Martinique"))== "Martinique") countryName = "";break; case QLocale::Mauritania: if((countryName = QCoreApplication::translate("country", "Mauritania"))== "Mauritania") countryName = "";break; case QLocale::Mauritius: if((countryName = QCoreApplication::translate("country", "Mauritius"))== "Mauritius") countryName = "";break; case QLocale::Mayotte: if((countryName = QCoreApplication::translate("country", "Mayotte"))== "Mayotte") countryName = "";break; case QLocale::Mexico: if((countryName = QCoreApplication::translate("country", "Mexico"))== "Mexico") countryName = "";break; case QLocale::Micronesia: if((countryName = QCoreApplication::translate("country", "Micronesia"))== "Micronesia") countryName = "";break; case QLocale::Moldova: if((countryName = QCoreApplication::translate("country", "Moldova"))== "Moldova") countryName = "";break; case QLocale::Monaco: if((countryName = QCoreApplication::translate("country", "Monaco"))== "Monaco") countryName = "";break; case QLocale::Mongolia: if((countryName = QCoreApplication::translate("country", "Mongolia"))== "Mongolia") countryName = "";break; case QLocale::Montenegro: if((countryName = QCoreApplication::translate("country", "Montenegro"))== "Montenegro") countryName = "";break; case QLocale::Montserrat: if((countryName = QCoreApplication::translate("country", "Montserrat"))== "Montserrat") countryName = "";break; case QLocale::Morocco: if((countryName = QCoreApplication::translate("country", "Morocco"))== "Morocco") countryName = "";break; case QLocale::Mozambique: if((countryName = QCoreApplication::translate("country", "Mozambique"))== "Mozambique") countryName = "";break; case QLocale::Myanmar: if((countryName = QCoreApplication::translate("country", "Myanmar"))== "Myanmar") countryName = "";break; case QLocale::Namibia: if((countryName = QCoreApplication::translate("country", "Namibia"))== "Namibia") countryName = "";break; case QLocale::NauruCountry: if((countryName = QCoreApplication::translate("country", "NauruCountry"))== "NauruCountry") countryName = "";break; case QLocale::Nepal: if((countryName = QCoreApplication::translate("country", "Nepal"))== "Nepal") countryName = "";break; case QLocale::Netherlands: if((countryName = QCoreApplication::translate("country", "Netherlands"))== "Netherlands") countryName = "";break; case QLocale::NewCaledonia: if((countryName = QCoreApplication::translate("country", "NewCaledonia"))== "NewCaledonia") countryName = "";break; case QLocale::NewZealand: if((countryName = QCoreApplication::translate("country", "NewZealand"))== "NewZealand") countryName = "";break; case QLocale::Nicaragua: if((countryName = QCoreApplication::translate("country", "Nicaragua"))== "Nicaragua") countryName = "";break; case QLocale::Niger: if((countryName = QCoreApplication::translate("country", "Niger"))== "Niger") countryName = "";break; case QLocale::Nigeria: if((countryName = QCoreApplication::translate("country", "Nigeria"))== "Nigeria") countryName = "";break; case QLocale::Niue: if((countryName = QCoreApplication::translate("country", "Niue"))== "Niue") countryName = "";break; case QLocale::NorfolkIsland: if((countryName = QCoreApplication::translate("country", "NorfolkIsland"))== "NorfolkIsland") countryName = "";break; case QLocale::NorthernMarianaIslands: if((countryName = QCoreApplication::translate("country", "NorthernMarianaIslands"))== "NorthernMarianaIslands") countryName = "";break; case QLocale::Norway: if((countryName = QCoreApplication::translate("country", "Norway"))== "Norway") countryName = "";break; case QLocale::Oman: if((countryName = QCoreApplication::translate("country", "Oman"))== "Oman") countryName = "";break; case QLocale::Pakistan: if((countryName = QCoreApplication::translate("country", "Pakistan"))== "Pakistan") countryName = "";break; case QLocale::Palau: if((countryName = QCoreApplication::translate("country", "Palau"))== "Palau") countryName = "";break; case QLocale::PalestinianTerritories: if((countryName = QCoreApplication::translate("country", "PalestinianTerritories"))== "PalestinianTerritories") countryName = "";break; case QLocale::Panama: if((countryName = QCoreApplication::translate("country", "Panama"))== "Panama") countryName = "";break; case QLocale::PapuaNewGuinea: if((countryName = QCoreApplication::translate("country", "PapuaNewGuinea"))== "PapuaNewGuinea") countryName = "";break; case QLocale::Paraguay: if((countryName = QCoreApplication::translate("country", "Paraguay"))== "Paraguay") countryName = "";break; case QLocale::Peru: if((countryName = QCoreApplication::translate("country", "Peru"))== "Peru") countryName = "";break; case QLocale::Philippines: if((countryName = QCoreApplication::translate("country", "Philippines"))== "Philippines") countryName = "";break; case QLocale::Poland: if((countryName = QCoreApplication::translate("country", "Poland"))== "Poland") countryName = "";break; case QLocale::Portugal: if((countryName = QCoreApplication::translate("country", "Portugal"))== "Portugal") countryName = "";break; case QLocale::PuertoRico: if((countryName = QCoreApplication::translate("country", "PuertoRico"))== "PuertoRico") countryName = "";break; case QLocale::Qatar: if((countryName = QCoreApplication::translate("country", "Qatar"))== "Qatar") countryName = "";break; case QLocale::Reunion: if((countryName = QCoreApplication::translate("country", "Reunion"))== "Reunion") countryName = "";break; case QLocale::Romania: if((countryName = QCoreApplication::translate("country", "Romania"))== "Romania") countryName = "";break; case QLocale::RussianFederation: if((countryName = QCoreApplication::translate("country", "RussianFederation"))== "RussianFederation") countryName = "";break; case QLocale::Rwanda: if((countryName = QCoreApplication::translate("country", "Rwanda"))== "Rwanda") countryName = "";break; case QLocale::SaintHelena: if((countryName = QCoreApplication::translate("country", "SaintHelena"))== "SaintHelena") countryName = "";break; case QLocale::SaintKittsAndNevis: if((countryName = QCoreApplication::translate("country", "SaintKittsAndNevis"))== "SaintKittsAndNevis") countryName = "";break; case QLocale::SaintLucia: if((countryName = QCoreApplication::translate("country", "SaintLucia"))== "SaintLucia") countryName = "";break; case QLocale::SaintPierreAndMiquelon: if((countryName = QCoreApplication::translate("country", "SaintPierreAndMiquelon"))== "SaintPierreAndMiquelon") countryName = "";break; case QLocale::SaintVincentAndTheGrenadines: if((countryName = QCoreApplication::translate("country", "SaintVincentAndTheGrenadines"))== "SaintVincentAndTheGrenadines") countryName = "";break; case QLocale::Samoa: if((countryName = QCoreApplication::translate("country", "Samoa"))== "Samoa") countryName = "";break; case QLocale::SanMarino: if((countryName = QCoreApplication::translate("country", "SanMarino"))== "SanMarino") countryName = "";break; case QLocale::SaoTomeAndPrincipe: if((countryName = QCoreApplication::translate("country", "SaoTomeAndPrincipe"))== "SaoTomeAndPrincipe") countryName = "";break; case QLocale::SaudiArabia: if((countryName = QCoreApplication::translate("country", "SaudiArabia"))== "SaudiArabia") countryName = "";break; case QLocale::Senegal: if((countryName = QCoreApplication::translate("country", "Senegal"))== "Senegal") countryName = "";break; case QLocale::Serbia: if((countryName = QCoreApplication::translate("country", "Serbia"))== "Serbia") countryName = "";break; case QLocale::Seychelles: if((countryName = QCoreApplication::translate("country", "Seychelles"))== "Seychelles") countryName = "";break; case QLocale::SierraLeone: if((countryName = QCoreApplication::translate("country", "SierraLeone"))== "SierraLeone") countryName = "";break; case QLocale::Singapore: if((countryName = QCoreApplication::translate("country", "Singapore"))== "Singapore") countryName = "";break; case QLocale::Slovakia: if((countryName = QCoreApplication::translate("country", "Slovakia"))== "Slovakia") countryName = "";break; case QLocale::Slovenia: if((countryName = QCoreApplication::translate("country", "Slovenia"))== "Slovenia") countryName = "";break; case QLocale::SolomonIslands: if((countryName = QCoreApplication::translate("country", "SolomonIslands"))== "SolomonIslands") countryName = "";break; case QLocale::Somalia: if((countryName = QCoreApplication::translate("country", "Somalia"))== "Somalia") countryName = "";break; case QLocale::SouthAfrica: if((countryName = QCoreApplication::translate("country", "SouthAfrica"))== "SouthAfrica") countryName = "";break; case QLocale::Spain: if((countryName = QCoreApplication::translate("country", "Spain"))== "Spain") countryName = "";break; case QLocale::SriLanka: if((countryName = QCoreApplication::translate("country", "SriLanka"))== "SriLanka") countryName = "";break; case QLocale::Sudan: if((countryName = QCoreApplication::translate("country", "Sudan"))== "Sudan") countryName = "";break; case QLocale::Suriname: if((countryName = QCoreApplication::translate("country", "Suriname"))== "Suriname") countryName = "";break; case QLocale::Swaziland: if((countryName = QCoreApplication::translate("country", "Swaziland"))== "Swaziland") countryName = "";break; case QLocale::Sweden: if((countryName = QCoreApplication::translate("country", "Sweden"))== "Sweden") countryName = "";break; case QLocale::Switzerland: if((countryName = QCoreApplication::translate("country", "Switzerland"))== "Switzerland") countryName = "";break; case QLocale::Syria: if((countryName = QCoreApplication::translate("country", "Syria"))== "Syria") countryName = "";break; case QLocale::Taiwan: if((countryName = QCoreApplication::translate("country", "Taiwan"))== "Taiwan") countryName = "";break; case QLocale::Tajikistan: if((countryName = QCoreApplication::translate("country", "Tajikistan"))== "Tajikistan") countryName = "";break; case QLocale::Tanzania: if((countryName = QCoreApplication::translate("country", "Tanzania"))== "Tanzania") countryName = "";break; case QLocale::Thailand: if((countryName = QCoreApplication::translate("country", "Thailand"))== "Thailand") countryName = "";break; case QLocale::Togo: if((countryName = QCoreApplication::translate("country", "Togo"))== "Togo") countryName = "";break; case QLocale::Tokelau: if((countryName = QCoreApplication::translate("country", "Tokelau"))== "Tokelau") countryName = "";break; case QLocale::Tonga: if((countryName = QCoreApplication::translate("country", "Tonga"))== "Tonga") countryName = "";break; case QLocale::TrinidadAndTobago: if((countryName = QCoreApplication::translate("country", "TrinidadAndTobago"))== "TrinidadAndTobago") countryName = "";break; case QLocale::Tunisia: if((countryName = QCoreApplication::translate("country", "Tunisia"))== "Tunisia") countryName = "";break; case QLocale::Turkey: if((countryName = QCoreApplication::translate("country", "Turkey"))== "Turkey") countryName = "";break; case QLocale::Turkmenistan: if((countryName = QCoreApplication::translate("country", "Turkmenistan"))== "Turkmenistan") countryName = "";break; case QLocale::TurksAndCaicosIslands: if((countryName = QCoreApplication::translate("country", "TurksAndCaicosIslands"))== "TurksAndCaicosIslands") countryName = "";break; case QLocale::Tuvalu: if((countryName = QCoreApplication::translate("country", "Tuvalu"))== "Tuvalu") countryName = "";break; case QLocale::Uganda: if((countryName = QCoreApplication::translate("country", "Uganda"))== "Uganda") countryName = "";break; case QLocale::Ukraine: if((countryName = QCoreApplication::translate("country", "Ukraine"))== "Ukraine") countryName = "";break; case QLocale::UnitedArabEmirates: if((countryName = QCoreApplication::translate("country", "UnitedArabEmirates"))== "UnitedArabEmirates") countryName = "";break; case QLocale::UnitedKingdom: if((countryName = QCoreApplication::translate("country", "UnitedKingdom"))== "UnitedKingdom") countryName = "";break; case QLocale::UnitedStates: if((countryName = QCoreApplication::translate("country", "UnitedStates"))== "UnitedStates") countryName = "";break; case QLocale::Uruguay: if((countryName = QCoreApplication::translate("country", "Uruguay"))== "Uruguay") countryName = "";break; case QLocale::Uzbekistan: if((countryName = QCoreApplication::translate("country", "Uzbekistan"))== "Uzbekistan") countryName = "";break; case QLocale::Vanuatu: if((countryName = QCoreApplication::translate("country", "Vanuatu"))== "Vanuatu") countryName = "";break; case QLocale::Venezuela: if((countryName = QCoreApplication::translate("country", "Venezuela"))== "Venezuela") countryName = "";break; case QLocale::Vietnam: if((countryName = QCoreApplication::translate("country", "Vietnam"))== "Vietnam") countryName = "";break; case QLocale::WallisAndFutunaIslands: if((countryName = QCoreApplication::translate("country", "WallisAndFutunaIslands"))== "WallisAndFutunaIslands") countryName = "";break; case QLocale::Yemen: if((countryName = QCoreApplication::translate("country", "Yemen"))== "Yemen") countryName = "";break; case QLocale::Zambia: if((countryName = QCoreApplication::translate("country", "Zambia"))== "Zambia") countryName = "";break; case QLocale::Zimbabwe: if((countryName = QCoreApplication::translate("country", "Zimbabwe"))== "Zimbabwe") countryName = "";break; default: { countryName = QLocale::countryToString(p_country); } } if( countryName == "") countryName = QLocale::countryToString(p_country); return countryName; } // Copy a folder recursively without erasing old file void Utils::copyDir(QString from, QString to) { QDir dir; dir.setPath(from); from += QDir::separator(); to += QDir::separator(); foreach (QString copyFile, dir.entryList(QDir::Files)) {// Copy each files QString toFile = to + copyFile; if (!QFile::exists(toFile)) QFile::copy(from+copyFile, toFile); } foreach (QString nextDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {// Copy folder QString toDir = to + nextDir; QDir().mkpath(toDir);// no need to check if dir exists copyDir(from + nextDir, toDir);//Go up } } QString Utils::getDisplayName(const std::shared_ptr& address){ QString displayName; if(address){ std::shared_ptr cleanAddress = address->clone(); cleanAddress->clean(); QString qtAddress = Utils::coreStringToAppString(cleanAddress->asStringUriOnly()); auto model = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(qtAddress); if(model && model->getVcardModel()) displayName = model->getVcardModel()->getUsername(); else{ // Try to get display from full address displayName = QString::fromStdString(address->getDisplayName()); if( displayName == ""){ // Try to get display name from logs auto callHistory = CoreManager::getInstance()->getCore()->getCallLogs(); auto callLog = std::find_if(callHistory.begin(), callHistory.end(), [address](std::shared_ptr& cl){ return cl->getRemoteAddress()->weakEqual(address); }); if(callLog != callHistory.end()) displayName = QString::fromStdString((*callLog)->getRemoteAddress()->getDisplayName()); if(displayName == "") displayName = QString::fromStdString(address->getDisplayName()); if(displayName == "") displayName = Utils::coreStringToAppString(address->getUsername()); } } } return displayName; } std::shared_ptr Utils::getConfigIfExists (const QString &configPath) { std::string factoryPath(Paths::getFactoryConfigFilePath()); if (!Paths::filePathExists(factoryPath)) factoryPath.clear(); return linphone::Config::newWithFactory(configPath.toStdString(), factoryPath); } QString Utils::computeUserAgent(const std::shared_ptr& config){ return QStringLiteral(APPLICATION_NAME" Desktop/%1 (%2) %3, Qt %4 LinphoneCore") .arg(QCoreApplication::applicationVersion()) .arg(SettingsModel::getDeviceName(config)) .arg(QSysInfo::prettyProductName()) .arg(qVersion()); } bool Utils::isMe(const QString& address){ return !address.isEmpty() ? CoreManager::getInstance()->getAccountSettingsModel()->getUsedSipAddress()->weakEqual(Utils::interpretUrl(address)) : false; } bool Utils::isMe(const std::shared_ptr& address){ return address ? CoreManager::getInstance()->getAccountSettingsModel()->getUsedSipAddress()->weakEqual(address) : false; } bool Utils::isAnimatedImage(const QString& path){ QFileInfo info(path); if( !info.exists()) return false; QImageReader reader(path); return reader.supportsAnimation() && reader.imageCount() > 1; } bool Utils::isPhoneNumber(const QString& txt){ auto core = CoreManager::getInstance()->getCore(); if(!core) return false; auto account = core->getDefaultAccount(); return account && account->isPhoneNumber(Utils::appStringToCoreString(txt)); } QSize Utils::getImageSize(const QString& url){ QString path; QUrl urlDecode(url); if(urlDecode.isLocalFile()) path = QDir::toNativeSeparators(urlDecode.toLocalFile()); else path = url; QFileInfo info(path); if( !info.exists()) return QSize(0,0); QImageReader reader(path); QSize s = reader.size(); if( s.isValid()) return s; else return QSize(0,0); } QPoint Utils::getCursorPosition(){ return QCursor::pos(); } QString Utils::getFileChecksum(const QString& filePath){ QFile file(filePath); if (file.open(QFile::ReadOnly)) { QCryptographicHash hash(QCryptographicHash::Sha256); if (hash.addData(&file)) { return hash.result().toHex(); } } return QString(); } QString Utils::encodeTextToQmlRichFormat(const QString& text, const QVariantMap& options){ QString images; QStringList formattedText; QStringList imageFormat; for(auto format : QImageReader::supportedImageFormats()) imageFormat.append(QString::fromLatin1(format).toUpper()); auto iriParsed = UriTools::parseIri(text); for(int i = 0 ; i < iriParsed.size() ; ++i){ QString iri = iriParsed[i].second.replace('&', "&") .replace('<', "\u2063<") .replace('>', "\u2063>") .replace('"', """) .replace('\'', "'"); if(!iriParsed[i].first) formattedText.append(iri); else{ QString uri = iriParsed[i].second.left(3) == "www" ? "http://"+iriParsed[i].second : iriParsed[i].second ; int extIndex = iriParsed[i].second.lastIndexOf('.'); QString ext; if( extIndex >= 0) ext = iriParsed[i].second.mid(extIndex+1).toUpper(); if(imageFormat.contains(ext.toLatin1())){// imagesHeight is not used because of bugs on display (blank image if set without width) images += ""; }else formattedText.append( "" + iri + ""); } } if(images != "") images = "
" + images +"
"; return images + "

" + formattedText.join("") + "

"; }linphone-desktop-5.0.2/linphone-app/src/utils/Utils.hpp000066400000000000000000000134741434616504300231520ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #ifndef UTILS_H_ #define UTILS_H_ #include #include #include #include #include #include #include "LinphoneEnums.hpp" #include "Constants.hpp" // ============================================================================= /* * Define telling g++ that a 'break' statement has been deliberately omitted * in switch block. */ #ifndef UTILS_NO_BREAK #if defined(__GNUC__) && __GNUC__ >= 7 #define UTILS_NO_BREAK __attribute__((fallthrough)) #else #define UTILS_NO_BREAK #endif // if defined(__GNUC__) && __GNUC__ >= 7 #endif // ifndef UTILS_NO_BREAK class Utils : public QObject{ Q_OBJECT public: Utils(QObject * parent = nullptr) : QObject(parent){} // Qt interfaces Q_INVOKABLE static bool hasCapability(const QString& address, const LinphoneEnums::FriendCapability& capability); Q_INVOKABLE static QDateTime addMinutes(QDateTime date, const int& min); static QDateTime getOffsettedUTC(const QDateTime& date); Q_INVOKABLE static QString toDateTimeString(QDateTime date); Q_INVOKABLE static QString toTimeString(QDateTime date, const QString& format = "hh:mm:ss"); Q_INVOKABLE static QString toDateString(QDateTime date); Q_INVOKABLE static QString getDisplayName(const QString& address); Q_INVOKABLE static QString toString(const LinphoneEnums::TunnelMode& mode); Q_INVOKABLE static bool isMe(const QString& address); Q_INVOKABLE static bool isAnimatedImage(const QString& path); Q_INVOKABLE static bool isPhoneNumber(const QString& txt); Q_INVOKABLE QSize getImageSize(const QString& url); Q_INVOKABLE static QPoint getCursorPosition(); Q_INVOKABLE static QString getFileChecksum(const QString& filePath); Q_INVOKABLE static QString encodeTextToQmlRichFormat(const QString& text, const QVariantMap& options); //---------------------------------------------------------------------------------- static inline QString coreStringToAppString (const std::string &str) { if(Constants::LinphoneLocaleEncoding == QString("UTF-8")) return QString::fromStdString(str); else return QString::fromLocal8Bit(str.c_str(), int(str.size()));// When using Locale. Be careful about conversion bijection with UTF-8, you may loss characters } static inline std::string appStringToCoreString (const QString &str) { if(Constants::LinphoneLocaleEncoding == QString("UTF-8")) return str.toStdString(); else return qPrintable(str); } // Reverse function of strstr. static char *rstrstr (const char *a, const char *b); // Return the path if it is an image else an empty path. static QImage getImage(const QString &pUri); // Returns the same path given in parameter if `filePath` exists. // Otherwise returns a safe path with a unique number before the extension. static QString getSafeFilePath (const QString &filePath, bool *soFarSoGood = nullptr); static std::shared_ptr getMatchingLocalAddress(std::shared_ptr p_localAddress); static QString cleanSipAddress (const QString &sipAddress);// Return at most : sip:username@domain // Test if the process exists static bool processExists(const quint64& p_processId); // Connect once to a member function. template static inline QMetaObject::Connection connectOnce ( typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, typename QtPrivate::FunctionPointer::Object *receiver, Func2 slot ) { QMetaObject::Connection connection = QObject::connect(sender, signal, receiver, slot); QMetaObject::Connection *deleter = new QMetaObject::Connection(); *deleter = QObject::connect(sender, signal, [connection, deleter] { QObject::disconnect(connection); QObject::disconnect(*deleter); delete deleter; }); return connection; } // Connect once to a function. template static inline QMetaObject::Connection connectOnce ( typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, const QObject *receiver, Func2 slot ) { QMetaObject::Connection connection = QObject::connect(sender, signal, receiver, slot); QMetaObject::Connection *deleter = new QMetaObject::Connection(); *deleter = QObject::connect(sender, signal, [connection, deleter] { QObject::disconnect(connection); QObject::disconnect(*deleter); delete deleter; }); return connection; } static std::shared_ptr interpretUrl(const QString& address); static QString getCountryName(const QLocale::Country& country); static void copyDir(QString from, QString to);// Copy a folder recursively without erasing old file static QString getDisplayName(const std::shared_ptr& address); // Get the displayname from addres in this order : Friends, Contact, Display address, Username address static std::shared_ptr getConfigIfExists (const QString& configPath); static QString computeUserAgent(const std::shared_ptr& config); static bool isMe(const std::shared_ptr& address); }; #endif // UTILS_H_ linphone-desktop-5.0.2/linphone-app/src/utils/hacks/000077500000000000000000000000001434616504300224215ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/utils/hacks/ReadMe.txt000066400000000000000000000002511434616504300243150ustar00rootroot00000000000000This folder regroup all hacks needed for unexpected behaviour. The goal is to know more esealy what strange things have been done that could be resolved from SDK fixes. linphone-desktop-5.0.2/linphone-app/src/utils/plugins/000077500000000000000000000000001434616504300230115ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/src/utils/plugins/LinphonePlugin.cpp000066400000000000000000000000621434616504300264460ustar00rootroot00000000000000#include "include/LinphoneApp/LinphonePlugin.hpp" linphone-desktop-5.0.2/linphone-app/src/utils/plugins/PluginDataAPI.cpp000066400000000000000000000133771434616504300261120ustar00rootroot00000000000000#include "include/LinphoneApp/PluginDataAPI.hpp" #include #include #include #include #include // This class regroup Data interface for importing contacts #include "include/LinphoneApp/LinphonePlugin.hpp" PluginDataAPI::PluginDataAPI(LinphonePlugin * plugin, void* linphoneCore, QPluginLoader * pluginLoader) : mPlugin(plugin), mLinphoneCore(linphoneCore), mPluginLoader(pluginLoader){ QVariantMap defaultValues; QJsonDocument doc = QJsonDocument::fromJson(mPlugin->getGUIDescriptionToJson().toUtf8()); QVariantMap description = doc.toVariant().toMap(); mPluginLoader->setLoadHints(0); // First, get all fields where their target is ALL. It will be act as a "default field" for(auto field : description["fields"].toList()){ auto details = field.toMap(); if( details.contains("fieldId") && details.contains("defaultData")){ int fieldCapability = details["capability"].toInt(); if( fieldCapability == PluginCapability::ALL){ for(int capability = PluginCapability::CONTACTS ; capability != PluginCapability::LAST ; ++capability){ mInputFields[static_cast(capability)][details["fieldId"].toString()] = details["defaultData"].toString(); } } } } // Second, get all fields that are not for ALL and add them for(auto field : description["fields"].toList()){ auto details = field.toMap(); if( details.contains("fieldId") && details.contains("defaultData")){ int fieldCapability = details["capability"].toInt(); if( fieldCapability> PluginCapability::NOTHING) mInputFields[static_cast(fieldCapability)][details["fieldId"].toString()] = details["defaultData"].toString(); } } for(auto inputFields : mInputFields) inputFields["enabled"] = 0; } PluginDataAPI::~PluginDataAPI(){ } void PluginDataAPI::setInputFields(const PluginCapability& pCapability, const QVariantMap &inputFields){ for(int capabilityIndex = (pCapability == PluginCapability::ALL?PluginCapability::CONTACTS:pCapability); capabilityIndex != (pCapability == PluginCapability::ALL?PluginCapability::LAST:pCapability+1) ; ++capabilityIndex){ PluginCapability selectedCapability = static_cast(capabilityIndex); if(mInputFields[selectedCapability] != inputFields) { mInputFields[selectedCapability] = inputFields; if( isValid(false)) saveConfiguration(selectedCapability); emit inputFieldsChanged(selectedCapability, mInputFields[selectedCapability]); } } } QMap PluginDataAPI::getInputFields(const PluginCapability& capability){ if( capability == PluginCapability::ALL) return mInputFields; else{ QMap data; data[capability] = mInputFields[capability]; return data; } } QMap PluginDataAPI::getInputFieldsToSave(const PluginCapability& capability) { return getInputFields(capability); } //----------------------------- CONFIGURATION --------------------------------------- void PluginDataAPI::setSectionConfiguration(const QString& section){ mSectionConfigurationName = section; } void PluginDataAPI::loadConfiguration(const PluginCapability& pCapability){ if( mSectionConfigurationName != "") { for(int capabilityIndex = (pCapability == PluginCapability::ALL?PluginCapability::CONTACTS:pCapability); capabilityIndex != (pCapability == PluginCapability::ALL?PluginCapability::LAST:pCapability+1) ; ++capabilityIndex){ PluginCapability currentCapability = static_cast(capabilityIndex); std::shared_ptr config = static_cast(mLinphoneCore)->getConfig(); QVariantMap importData; std::string sectionName = (mSectionConfigurationName+"_"+QString::number(capabilityIndex)).toStdString(); std::list keys = config->getKeysNamesList(sectionName); for(auto key : keys){ std::string value = config->getString(sectionName, key, ""); importData[QString::fromLocal8Bit(key.c_str(), int(key.size()))] = QString::fromLocal8Bit(value.c_str(), int(value.size())); } //Do not use setInputFields(importData); as we don't want to save the configuration mInputFields[currentCapability] = importData; emit inputFieldsChanged(currentCapability, mInputFields[currentCapability]); } } } void PluginDataAPI::saveConfiguration(const PluginCapability& pCapability){ if( mSectionConfigurationName != "") { auto inputs = getInputFieldsToSave(pCapability); for(QMap::Iterator input = inputs.begin() ; input != inputs.end() ; ++input){ PluginCapability currentCapability = input.key(); std::string sectionName = (mSectionConfigurationName+"_"+QString::number(currentCapability)).toStdString(); std::shared_ptr config = static_cast(mLinphoneCore)->getConfig(); QVariantMap inputsToSave = inputs[currentCapability]; config->cleanSection(sectionName);// Remove fields that doesn't exist anymore (like temporary variables) for(auto field = inputsToSave.begin() ; field != inputsToSave.end() ; ++field) config->setString(sectionName, qPrintable(field.key()), qPrintable(field.value().toString())); } } } void PluginDataAPI::cleanAllConfigurations(){ for(int capabilityIndex = PluginCapability::ALL ; capabilityIndex != PluginCapability::LAST ; ++capabilityIndex){ std::string sectionName = (mSectionConfigurationName+"_"+QString::number(capabilityIndex)).toStdString(); std::shared_ptr config = static_cast(mLinphoneCore)->getConfig(); config->cleanSection(sectionName); } } //----------------------------- ------------------------------------------------------- QPluginLoader * PluginDataAPI::getPluginLoader(){ return mPluginLoader; } linphone-desktop-5.0.2/linphone-app/src/utils/plugins/PluginNetworkHelper.cpp000066400000000000000000000041041434616504300274640ustar00rootroot00000000000000#include "include/LinphoneApp/PluginNetworkHelper.hpp" #include #include // This class is used to define network operation to retrieve Addresses from Network PluginNetworkHelper::PluginNetworkHelper(){} PluginNetworkHelper::~PluginNetworkHelper(){} void PluginNetworkHelper::request(){ // Create QNetworkReply and make network requests QNetworkRequest request(prepareRequest()); request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); mNetworkReply = mManager.get(request); #if QT_CONFIG(ssl) mNetworkReply->ignoreSslErrors(); #endif QNetworkReply *data = mNetworkReply.data(); QObject::connect(data, &QNetworkReply::readyRead, this, &PluginNetworkHelper::handleReadyData); QObject::connect(data, &QNetworkReply::finished, this, &PluginNetworkHelper::handleFinished); QObject::connect(data, QNonConstOverload::of(&QNetworkReply::error), this, &PluginNetworkHelper::handleError); #if QT_CONFIG(ssl) QObject::connect(data, &QNetworkReply::sslErrors, this, &PluginNetworkHelper::handleSslErrors); #endif } void PluginNetworkHelper::handleReadyData(){ mBuffer.append(mNetworkReply->readAll()); } void PluginNetworkHelper::handleFinished (){ if (mNetworkReply->error() == QNetworkReply::NoError){ mBuffer.append(mNetworkReply->readAll()); emit requestFinished(mBuffer); }else { qWarning() << mNetworkReply->errorString(); emit message(QtWarningMsg, "Error while dealing with network. See logs for details."); } mBuffer.clear(); } void PluginNetworkHelper::handleError (QNetworkReply::NetworkError code) { if (code != QNetworkReply::OperationCanceledError) { QString url = mNetworkReply->url().host(); QString errorString = mNetworkReply->errorString(); qWarning() << QStringLiteral("Download failed: %1 from %2").arg(errorString).arg(url); } } void PluginNetworkHelper::handleSslErrors (const QList &sslErrors){ #if QT_CONFIG(ssl) for (const QSslError &error : sslErrors) qWarning() << QStringLiteral("SSL error %1 : %2").arg(error.error()).arg(error.errorString()); #else Q_UNUSED(sslErrors); #endif } linphone-desktop-5.0.2/linphone-app/src/utils/plugins/PluginsManager.cpp000066400000000000000000000256001434616504300264340ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ #include "PluginsManager.hpp" //#include "ContactsImporterModel.hpp" #include "../../../include/LinphoneApp/LinphonePlugin.hpp" #include "../../../include/LinphoneApp/PluginDataAPI.hpp" #include "../../../include/LinphoneApp/PluginNetworkHelper.hpp" #include "utils/Utils.hpp" #include "app/paths/Paths.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" #include "components/contacts/ContactsImporterListModel.hpp" #include "components/contacts/ContactsImporterModel.hpp" #include "components/core/CoreManager.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" #include #include #include #include #include #include // ============================================================================= QMap PluginsManager::gPluginsMap; QString PluginsManager::gPluginsConfigSection = "AppPlugin"; PluginsManager::PluginsManager(QObject * parent) : QObject(parent){ } QPluginLoader * PluginsManager::getPlugin(const QString &pluginIdentity){ QStringList pluginPaths = Paths::getPluginsAppFolders();// Get all paths if( gPluginsMap.contains(pluginIdentity)){ for(int i = 0 ; i < pluginPaths.size() ; ++i) { QString pluginPath = pluginPaths[i] +gPluginsMap[pluginIdentity]; QPluginLoader * loader = new QPluginLoader(pluginPath); loader->setLoadHints(0); // this force Qt to unload the plugin from memory when we request it. Be carefull by not having a plugin instance or data created inside the plugin after the unload. if( auto instance = loader->instance()) { auto plugin = qobject_cast< LinphonePlugin* >(instance); if (plugin ) return loader; else{ qWarning() << loader->errorString(); loader->unload(); } } loader->deleteLater(); } } return nullptr; } void * PluginsManager::createInstance(const QString &pluginIdentity){ void * dataInstance = nullptr; LinphonePlugin * plugin = nullptr; if( gPluginsMap.contains(pluginIdentity)){ QStringList pluginPaths = Paths::getPluginsAppFolders(); for(int i = 0 ; i < pluginPaths.size() ; ++i) { QString pluginPath = pluginPaths[i] +gPluginsMap[pluginIdentity]; QPluginLoader * loader = new QPluginLoader(pluginPath); loader->setLoadHints(0); // this force Qt to unload the plugin from memory when we request it. Be carefull by not having a plugin instance or data created inside the plugin after the unload. if( auto instance = loader->instance()) { plugin = qobject_cast< LinphonePlugin* >(instance); if (plugin) { try{ dataInstance = plugin->createInstance(CoreManager::getInstance()->getCore().get(), loader); return dataInstance; }catch(...){ loader->unload(); } }else loader->unload(); } loader->deleteLater(); } } return dataInstance; } QJsonDocument PluginsManager::getJson(const QString &pluginIdentity){ QJsonDocument doc; QPluginLoader * pluginLoader = getPlugin(pluginIdentity); if( pluginLoader ){ auto instance = pluginLoader->instance(); if( instance ){ LinphonePlugin * plugin = qobject_cast< LinphonePlugin* >(instance); if( plugin ){ doc = QJsonDocument::fromJson(plugin->getGUIDescriptionToJson().toUtf8()); } } pluginLoader->unload(); pluginLoader->deleteLater(); } return doc; } QList> PluginsManager::getImporterModels(const QStringList &capabilities){ QList> models; for(int i = 0 ; i < capabilities.size() ; ++i){ if( capabilities[i] == "CONTACTS") models << CoreManager::getInstance()->getContactsImporterListModel()->getSharedList(); } return models; } void PluginsManager::openNewPlugin(const QString &pTitle){ QString fileName = QFileDialog::getOpenFileName(nullptr, pTitle); QString pluginIdentity; QStringList capabilities; QList> modelsToReset; int doCopy = QMessageBox::Yes; bool cannotRemovePlugin = false; //QVersionNumber pluginVersion, apiVersion = LinphonePlugin::gPluginVersion; if(fileName != ""){ QFileInfo fileInfo(fileName); QString path = Utils::coreStringToAppString(Paths::getPluginsAppDirPath()); if( !QLibrary::isLibrary(fileName)){ QMessageBox::information(nullptr, pTitle, "The file is not a plugin"); }else{ QPluginLoader loader(fileName); loader.setLoadHints(0); QJsonObject metaData = loader.metaData()["MetaData"].toObject(); if( metaData.contains("ID") && metaData.contains("Capabilities")){ capabilities = metaData["Capabilities"].toString().toUpper().remove(' ').split(","); pluginIdentity = metaData["ID"].toString(); } if(!pluginIdentity.isEmpty()){// Check all plugins that have this title QStringList oldPlugins; if( gPluginsMap.contains(pluginIdentity)) oldPlugins << gPluginsMap[pluginIdentity]; if( QFile::exists(path+fileInfo.fileName())) oldPlugins << path+fileInfo.fileName(); if(oldPlugins.size() > 0){ doCopy = QMessageBox::question(nullptr, pTitle, "The plugin already exists. Do you want to overwrite it?\n"+oldPlugins.join('\n'), QMessageBox::Yes, QMessageBox::No); if( doCopy == QMessageBox::Yes){ if(gPluginsMap.contains(pluginIdentity)){ auto importers = CoreManager::getInstance()->getContactsImporterListModel()->getSharedList(); for(auto importer : importers){ QJsonObject pluginMetaData(importer->getDataAPI()->getPluginLoader()->metaData()); if( pluginMetaData.contains("ID") && pluginMetaData["ID"].toString() == pluginIdentity){ importer->setDataAPI(nullptr); modelsToReset.append(importer); } } QStringList pluginPaths = Paths::getPluginsAppFolders(); for(int i = 0 ; !cannotRemovePlugin && i < pluginPaths.size()-1 ; ++i) {// Ignore the last path as it is the app folder QString pluginPath = pluginPaths[i]; if(QFile::exists(pluginPath+gPluginsMap[pluginIdentity])){ cannotRemovePlugin = !QFile::remove(pluginPath+gPluginsMap[pluginIdentity]); } } } if(!cannotRemovePlugin && QFile::exists(path+fileInfo.fileName())) cannotRemovePlugin = !QFile::remove(path+fileInfo.fileName()); if(!cannotRemovePlugin) gPluginsMap[pluginIdentity] = ""; } } }else doCopy = QMessageBox::No; if(doCopy == QMessageBox::Yes ){ if( cannotRemovePlugin)// Qt will not unload library from memory so files cannot be removed. See https://bugreports.qt.io/browse/QTBUG-68880 QMessageBox::information(nullptr, pTitle, "The plugin cannot be replaced. You have to exit the application and delete manually the plugin file in\n"+path); else if( !QFile::copy(fileName, path+fileInfo.fileName())) QMessageBox::information(nullptr, pTitle, "The plugin cannot be copied. You have to copy manually the plugin file to\n"+path); else { gPluginsMap[pluginIdentity] = fileInfo.fileName(); for(auto importer : modelsToReset) importer->setDataAPI(static_cast(createInstance(pluginIdentity))); } } } } } QVariantList PluginsManager::getPlugins(const int& capabilities) { QVariantList plugins; QStringList pluginPaths = Paths::getPluginsAppFolders(); if(capabilities<0) gPluginsMap.clear(); for(int pathIndex = pluginPaths.size()-1 ; pathIndex >= 0 ; --pathIndex) {// Start from app package. This sort ensure the priority on user plugins QString pluginPath = pluginPaths[pathIndex]; QDir dir(pluginPath); QStringList pluginFiles = dir.entryList(QDir::Files); for(int i = 0 ; i < pluginFiles.size() ; ++i) { if( QLibrary::isLibrary(pluginPath+pluginFiles[i])){ QPluginLoader loader(pluginPath+pluginFiles[i]); loader.setLoadHints(0); // this force Qt to unload the plugin from memory when we request it. Be carefull by not having a plugin instance or data created inside the plugin after the unload. if (auto instance = loader.instance()) { LinphonePlugin * plugin = qobject_cast< LinphonePlugin* >(instance); if ( plugin){ QJsonObject metaData = loader.metaData()["MetaData"].toObject(); if( metaData.contains("ID")){ bool getIt = false; if(capabilities>=0 ){ if(metaData.contains("Capabilities")){ QString pluginCapabilities = metaData["Capabilities"].toString().toUpper().remove(' '); if( (capabilities & PluginDataAPI::CONTACTS) == PluginDataAPI::CONTACTS && pluginCapabilities.contains("CONTACTS")){ getIt = true; } }else qWarning()<< "The plugin " << pluginFiles[i] << " must have Capabilities in its metadata"; }else getIt = true; if(getIt){ QJsonDocument doc = QJsonDocument::fromJson(plugin->getGUIDescriptionToJson().toUtf8()); QVariantMap desc; desc["pluginTitle"] = doc["pluginTitle"]; desc["pluginID"] = metaData["ID"].toString(); if(!doc["pluginTitle"].toString().isEmpty()){ gPluginsMap[metaData["ID"].toString()] = pluginFiles[i]; plugins.push_back(desc); } } }else qWarning()<< "The plugin " << pluginFiles[i] << " must have ID in its metadata"; } else { qWarning()<< "The plugin " << pluginFiles[i] << " should be updated to this version of API : " << loader.metaData()["IID"].toString(); } loader.unload(); } else { qWarning()<< "The plugin " << pluginFiles[i] << " cannot be used : " << loader.errorString(); } } } std::sort(plugins.begin(), plugins.end()); } return plugins; } QVariantMap PluginsManager::getPluginDescription(const QString& pluginIdentity) { QVariantMap description; QJsonDocument doc = getJson(pluginIdentity); description = doc.toVariant().toMap(); return description; } QVariantMap PluginsManager::getDefaultValues(const QString& pluginIdentity){ QVariantMap defaultValues; QVariantMap description; QJsonDocument doc = getJson(pluginIdentity); description = doc.toVariant().toMap(); for(auto field : description["fields"].toList()){ auto details = field.toMap(); if( details.contains("fieldId") && details.contains("defaultData")){ defaultValues[details["fieldId"].toString()] = details["defaultData"].toString(); } } return defaultValues; } linphone-desktop-5.0.2/linphone-app/src/utils/plugins/PluginsManager.hpp000066400000000000000000000043711434616504300264430ustar00rootroot00000000000000//const QVersionNumber ContactsImporterPlugin::gPluginVersion = QVersionNumber::fromString(PLUGIN_CONTACT_VERSION); //const QVersionNumber ContactsImporterPlugin::gPluginVersion = QVersionNumber::fromString("1.0.0"); //const QVersionNumber _ContactsImporterPlugin::gPluginVersion = QVersionNumber::fromString("1.0.0"); #ifndef PLUGINS_MANAGER_MODEL_H_ #define PLUGINS_MANAGER_MODEL_H_ #include #include #include // ============================================================================= class ContactsImporterModel; class PluginDataAPI; class QPluginLoader; class PluginsModel : public QObject{ Q_OBJECT public: PluginsModel(QObject *parent = nullptr) : QObject(parent){} virtual ~PluginsModel(){} virtual void setDataAPI(PluginDataAPI*) = 0; virtual PluginDataAPI* getDataAPI() = 0; virtual int getIdentity()const = 0; virtual QVariantMap getFields() = 0; }; class PluginsManager : public QObject{ Q_OBJECT public: PluginsManager (QObject *parent = Q_NULLPTR); static QPluginLoader * getPlugin(const QString &pluginIdentity); // Return a plugin loader with Hints to 0 (unload will force Qt to remove the plugin from memory). static QVariantList getPlugins(const int& capabilities = -1); // Return all loaded plugins that have selected capabilities (PluginCapability flags) static void * createInstance(const QString &pluginIdentity); //Return a data instance from a plugin name. static QJsonDocument getJson(const QString &pluginIdentity); // Get the description of the plugin int the Json format. Q_INVOKABLE static void openNewPlugin(const QString &pTitle); // Open a File Dialog. Test if the file can be load and have a matched version. Replace old plugins from custom paths and with the same plugin title. static QVariantMap getDefaultValues(const QString& pluginIdentity); // Get the default values of each fields for th eplugin QVariantMap getPluginDescription(const QString& pluginIdentity); QList> getImporterModels(const QStringList &capabilities); static QMap gPluginsMap; // Map between Identity and plugin path static QString gPluginsConfigSection; // The root name of the plugin's section in configuration file }; #endif // CONTACTS_IMPORTER_PLUGINS_MANAGER_MODEL_H_ linphone-desktop-5.0.2/linphone-app/tools/000077500000000000000000000000001434616504300205415ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/tools/create_appimage.sh000077500000000000000000000112431434616504300242070ustar00rootroot00000000000000#!/bin/bash ## ## Copyright (c) 2010-2020 Belledonne Communications SARL. ## ## This file is part of linphone-desktop ## (see https://www.linphone.org). ## ## 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 . ## # Arguments : # $1 = Executable Name # $2 = Output Filename # $3 = Qt root path (eg. "~/Qt/5.15.2/gcc_64") # $4 = Key of the code sign (optional but mendatory if code signing) APP_NAME="$1" QT_PATH="$3" BIN_SOURCE_DIR="OUTPUT/" WORK_DIR="WORK/Packages/AppImageDir" rm -rf ${WORK_DIR}/AppDir mkdir -p "${WORK_DIR}/AppDir/usr/" #Copy all files from the output project cp -rf "${BIN_SOURCE_DIR}"/* "${WORK_DIR}/AppDir/usr/" #remove Packages folder : it is not part of the project rm -rf "${WORK_DIR}/AppDir/usr/Packages" #remove libraries : there are automatically found by linuxdeploy rm -rf "${WORK_DIR}/AppDir/usr/lib" rm -rf "${WORK_DIR}/AppDir/usr/lib64" #Copy soci sqlite3 backend mkdir -p "${WORK_DIR}/AppDir/usr/lib" cp -f "${BIN_SOURCE_DIR}/lib"/libsoci_sqlite3* "${WORK_DIR}/AppDir/usr/lib/" cp -f "${BIN_SOURCE_DIR}/lib"/libapp-plugin* "${WORK_DIR}/AppDir/usr/lib/" cp -f "${BIN_SOURCE_DIR}/lib64"/libsoci_sqlite3* "${WORK_DIR}/AppDir/usr/lib/" cp -f "${BIN_SOURCE_DIR}/lib64"/libapp-plugin* "${WORK_DIR}/AppDir/usr/lib/" if [ -d "${BIN_SOURCE_DIR}/lib64/mediastreamer" ]; then mkdir -p "${WORK_DIR}/AppDir/usr/plugins/" cp -rf "${BIN_SOURCE_DIR}"/lib64/mediastreamer "${WORK_DIR}/AppDir/usr/plugins/" fi if [ -d "${BIN_SOURCE_DIR}/lib/mediastreamer" ]; then mkdir -p "${WORK_DIR}/AppDir/usr/plugins/" cp -rf "${BIN_SOURCE_DIR}"/lib/mediastreamer "${WORK_DIR}/AppDir/usr/plugins/" fi if [ -f "${WORK_DIR}/AppBin/linuxdeploy-x86_64.AppImage" ]; then echo "linuxdeploy-x86_64.AppImage exists" else wget -P "${WORK_DIR}/AppBin" https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage #wget -P "${WORK_DIR}/AppBin" https://artifacts.assassinate-you.net/linuxdeploy/travis-456/linuxdeploy-x86_64.AppImage chmod +x "${WORK_DIR}/AppBin/linuxdeploy-x86_64.AppImage" fi if [ -f "${WORK_DIR}/AppBin/linuxdeploy-plugin-qt-x86_64.AppImage" ]; then echo "linuxdeploy-plugin-qt-x86_64.AppImage exists" else wget -P "${WORK_DIR}/AppBin" https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage chmod +x "${WORK_DIR}/AppBin/linuxdeploy-plugin-qt-x86_64.AppImage" fi ########################################################################################### export QML_SOURCES_PATHS=${QML_SOURCES_PATHS}:${WORK_DIR}/.. export LD_LIBRARY_PATH=${QT_PATH}/lib #export EXTRA_QT_PLUGINS=webenginecore;webview;webengine echo "-- Generating AppDir for AppImage" if [ -z "$4" ]; then ./${WORK_DIR}/AppBin/linuxdeploy-x86_64.AppImage --appdir=${WORK_DIR}/AppDir -e ${WORK_DIR}/AppDir/usr/bin/${APP_NAME} --output appimage --desktop-file=${WORK_DIR}/AppDir/usr/share/applications/${APP_NAME}.desktop -i ${WORK_DIR}/AppDir/usr/share/icons/hicolor/scalable/apps/${APP_NAME}.svg --plugin qt else if [ -f "${WORK_DIR}/AppBin/appimagetool-x86_64.AppImage" ]; then echo "appimagetool-x86_64.AppImage exists" else wget -P "${WORK_DIR}/AppBin" https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage chmod +x "${WORK_DIR}/AppBin/appimagetool-x86_64.AppImage" fi ./${WORK_DIR}/AppBin/linuxdeploy-x86_64.AppImage --appdir=${WORK_DIR}/AppDir -e ${WORK_DIR}/AppDir/usr/bin/${APP_NAME} --desktop-file=${WORK_DIR}/AppDir/usr/share/applications/${APP_NAME}.desktop -i ${WORK_DIR}/AppDir/usr/share/icons/hicolor/scalable/apps/${APP_NAME}.svg --plugin qt #./linuxdeploy-x86_64.AppImage --appdir=${WORK_DIR}/ -e ${WORK_DIR}/app/bin/${APP_NAME} --output appimage --desktop-file=${WORK_DIR}/app/share/applications/${APP_NAME}.desktop -i ${WORK_DIR}/app/share/icons/hicolor/scalable/apps/${APP_NAME}.svg echo "-- Code Signing of AppImage" # APPIMAGETOOL_SIGN_PASSPHRASE has to the parent environment (not here). Do not use export. ./${WORK_DIR}/AppBin/appimagetool-x86_64.AppImage ${WORK_DIR}/AppDir --sign --sign-key $4 fi mkdir -p "${BIN_SOURCE_DIR}/Packages" mv *.AppImage "${BIN_SOURCE_DIR}/Packages/$2.AppImage" linphone-desktop-5.0.2/linphone-app/tools/sign_package.bat000066400000000000000000000002371434616504300236460ustar00rootroot00000000000000@echo off if [%5]==[] goto simple set /p passphrase=<%1 %2 sign /f %3 /fd SHA256 /p %passphrase% /t %4 %5 goto :eof :simple %1 sign /fd SHA256 /t %2 %3 :eof linphone-desktop-5.0.2/linphone-app/ui/000077500000000000000000000000001434616504300200165ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/dev-modules/000077500000000000000000000000001434616504300222425ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/dev-modules/Units/000077500000000000000000000000001434616504300233445ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/dev-modules/Units/Units.qml000066400000000000000000000002321434616504300251560ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 // ============================================================================= QtObject { property real dp: 1.0 } linphone-desktop-5.0.2/linphone-app/ui/dev-modules/Units/qmldir000066400000000000000000000003541434616504300245610ustar00rootroot00000000000000# ============================================================================== # Units component to export. # ============================================================================== module Units singleton Units 1.0 Units.qml linphone-desktop-5.0.2/linphone-app/ui/modules/000077500000000000000000000000001434616504300214665ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/000077500000000000000000000000001434616504300227165ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Animations/000077500000000000000000000000001434616504300250205ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Animations/BusyIndicator.qml000066400000000000000000000040461434616504300303160ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import Common.Styles 1.0 // ============================================================================= BusyIndicator { id: busyIndicator // --------------------------------------------------------------------------- property color color: BusyIndicatorStyle.color readonly property int _rotation: 360 readonly property int _size: width < height ? width : height // --------------------------------------------------------------------------- visible: running contentItem: Item { x: parent.width / 2 - width / 2 y: parent.height / 2 - height / 2 height: _size width: _size Item { id: item height: parent.height width: parent.width opacity: busyIndicator.running ? 1 : 0 // ----------------------------------------------------------------------- // Animation. // ----------------------------------------------------------------------- Behavior on opacity { OpacityAnimator { duration: 250 } } RotationAnimator { duration: BusyIndicatorStyle.duration loops: Animation.Infinite running: busyIndicator.visible target: item from: 0 to: busyIndicator._rotation } // ----------------------------------------------------------------------- // Items to draw. // ----------------------------------------------------------------------- Repeater { id: repeater model: BusyIndicatorStyle.nSpheres Rectangle { x: item.width / 2 - width / 2 y: item.height / 2 - height / 2 property real ratio : (3+index) / repeater.count height: item.height / 3 * ratio width: item.width / 3 * ratio opacity: ratio color: busyIndicator.color radius: (width > height ? width : height) / 2 transform: [ Translate { y: busyIndicator._size / 2 }, Rotation { angle: index / repeater.count * busyIndicator._rotation origin { x: width / 2 y: height / 2 } } ] } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Constants/000077500000000000000000000000001434616504300246725ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Constants/Constants.qml000066400000000000000000000003321434616504300273570ustar00rootroot00000000000000pragma Singleton import QtQuick 2.7 // ============================================================================= QtObject { property int zPopup: 999 property int zMax: 999999 property int sizeMax: 999999 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Dialog/000077500000000000000000000000001434616504300241155ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Dialog/ConfirmDialog.qml000066400000000000000000000014771434616504300273560ustar00rootroot00000000000000import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 // ============================================================================= // A dialog with OK/Cancel buttons. // ============================================================================= DialogPlus { id: mainItem property int showButtonOnly : -1 property var buttonTexts : [qsTr('cancel') , qsTr('confirm')] buttons: [ TextButtonA { text: mainItem.buttonTexts[0] visible: mainItem.showButtonOnly<0 || mainItem.showButtonOnly == 0 onClicked: exit(0) }, TextButtonB { text: mainItem.buttonTexts[1] visible: mainItem.showButtonOnly<0 || mainItem.showButtonOnly == 1 onClicked: exit(1) } ] buttonsAlignment: Qt.AlignCenter height: DialogStyle.confirmDialog.height + 30 width: DialogStyle.confirmDialog.width } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Dialog/DateTimeDialog.qml000066400000000000000000000032361434616504300274500ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 // ============================================================================= DialogPlus { id: mainItem height: timePicker.visible ? 575 : 500 width: 500 property alias hideOldDates: datePicker.hideOldDates property alias showDatePicker : datePicker.visible property alias showTimePicker: timePicker.visible property alias selectedDate: datePicker.selectedDate property alias selectedTime: timePicker.selectedTime // --------------------------------------------------------------------------- buttons: [ TextButtonB { text: 'ok' onClicked: { exit({selectedDate: mainItem.selectedDate, selectedTime: mainItem.selectedTime}) } } ] buttonsAlignment: Qt.AlignCenter //: 'Select date' : Menu title to show select date. property string dateTitle: qsTr('dateTimeDialogDate') //: 'Select time' : Menu title to show select time. property string timeTitle: qsTr('dateTimeDialogTime') //: 'Select date and time' : Menu title to show select date and time. property string dateTimeTitle: qsTr('dateTimeDialogDateTime') title: showDatePicker ? showTimePicker ? dateTimeTitle : timeTitle : timeTitle showCloseCross: true // --------------------------------------------------------------------------- RowLayout{ anchors.fill: parent DatePicker{ id: datePicker visible: false Layout.fillHeight: true Layout.fillWidth: true } TimePicker{ id: timePicker visible: false Layout.fillHeight: true Layout.fillWidth: true } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Dialog/DialogDescription.qml000066400000000000000000000017521434616504300302400ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 // ============================================================================= // Description content used by dialogs. // ============================================================================= Item { property alias text: description.text property alias horizontalAlignment: description.horizontalAlignment property int marginOffset: 0 height: !text ? (DialogStyle.description.verticalMargin + marginOffset) : undefined implicitHeight: text ? description.implicitHeight + (DialogStyle.description.verticalMargin + marginOffset) * 2 : 0 Text { id: description anchors { fill: parent leftMargin: DialogStyle.description.leftMargin rightMargin: DialogStyle.description.rightMargin } color: DialogStyle.description.color font.pointSize: DialogStyle.description.pointSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Dialog/DialogPlus.qml000066400000000000000000000066121434616504300267000ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 // ============================================================================= // Helper to build quickly dialogs. // ============================================================================= Rectangle { id: dialog property alias buttons: buttonsView.data // Optionnal. property alias title : titleBar.text //Optionnal. Show a title bar with a close button. property alias descriptionText: description.text // Optionnal. property int buttonsAlignment : Qt.AlignLeft property bool flat : false property bool showMargins: !flat property bool expandHeight: flat property alias showCloseCross : titleBar.showCloseCross property alias showTitleBar: titleBar.showBar property int buttonsLeftMargin :(buttonsAlignment & Qt.AlignLeft )== Qt.AlignLeft ? DialogStyle.buttons.leftMargin : (buttonsAlignment & Qt.AlignRight) == Qt.AlignRight ? DialogStyle.buttons.rightMargin : DialogStyle.buttons.leftMargin property int buttonsRightMargin : (buttonsAlignment & Qt.AlignRight )== Qt.AlignRight ? DialogStyle.buttons.rightMargin : (buttonsAlignment & Qt.AlignLeft) == Qt.AlignLeft ? DialogStyle.buttons.leftMargin : DialogStyle.buttons.rightMargin default property alias _content: content.data property alias foregroundItem: foregroundItemData.data readonly property bool contentIsEmpty: { return _content == null || !_content.length } // --------------------------------------------------------------------------- signal exitStatus (var status) // --------------------------------------------------------------------------- function exit (status) { exitStatus(status) } // --------------------------------------------------------------------------- color: DialogStyle.color layer { enabled: !dialog.flat effect: PopupShadow {} } // --------------------------------------------------------------------------- Shortcut { sequence: StandardKey.Close onActivated: exit(0) } // --------------------------------------------------------------------------- ColumnLayout { anchors.fill: parent spacing: 0 DialogTitle{ id:titleBar //Layout.fillHeight: dialog.contentIsEmpty flat: dialog.flat showCloseCross:dialog.showCloseCross Layout.fillWidth: true onClose: exitStatus(0) } DialogDescription { id: description Layout.fillHeight: dialog.contentIsEmpty Layout.fillWidth: true visible: text!='' } Item { id: content Layout.fillHeight: (expandHeight ? true : !dialog.contentIsEmpty) Layout.fillWidth: true Layout.topMargin: (showMargins ? titleBar.showBar ? 1 : 2 : 0) * DialogStyle.content.topMargin Layout.bottomMargin: (showMargins ? DialogStyle.content.bottomMargin : 0) Layout.leftMargin: (showMargins ? DialogStyle.content.leftMargin : 0) Layout.rightMargin: (showMargins ? DialogStyle.content.rightMargin : 0) } RowLayout { id: buttonsView Layout.alignment: buttonsAlignment Layout.bottomMargin: DialogStyle.buttons.bottomMargin Layout.leftMargin: buttonsLeftMargin Layout.rightMargin: buttonsRightMargin Layout.topMargin: DialogStyle.buttons.topMargin spacing: DialogStyle.buttons.spacing visible: children.length>0 } } Item{ id: foregroundItemData anchors.fill: parent } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Dialog/DialogTitle.qml000066400000000000000000000027741434616504300270430ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Units 1.0 import ColorsList 1.0 // ============================================================================= // Title bar used by dialogs. // ============================================================================= Item { id:mainItem property alias text: title.text property bool showBar : text != '' property bool showCloseCross: showBar property bool flat: false signal close() height: showBar || showCloseCross ? (flat && text != '' ? 60 : 30) : 0 Rectangle{ anchors.fill:parent gradient: Gradient { GradientStop { position: 0.0; color: DialogStyle.title.lowGradient } GradientStop { position: 1.0; color: DialogStyle.title.highGradient } } visible: showBar && !flat } Text { id: title anchors { fill: parent leftMargin: DialogStyle.description.leftMargin rightMargin: DialogStyle.description.rightMargin } color: DialogStyle.description.color font.pointSize: !flat ? Units.dp * 10 : Units.dp * 14 font.weight: !flat ? Font.Normal : Font.Bold horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap visible: showBar } ActionButton{ anchors.right:parent.right anchors.rightMargin: 14 anchors.top:parent.top anchors.topMargin: 5 height: DialogStyle.closeButton.iconSize isCustom: true backgroundRadius: 90 colorSet: DialogStyle.closeButton visible:mainItem.showCloseCross onClicked: close() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/000077500000000000000000000000001434616504300236215ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/ActionBar.qml000066400000000000000000000004751434616504300262040ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 // ============================================================================= // Bar which can contains ActionButtons. // ============================================================================= Row { property int iconSize spacing: ActionBarStyle.spacing } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/ActionButton.qml000066400000000000000000000351251434616504300267530ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import Common 1.0 // ============================================================================= // An animated (or not) button with image(s). // ============================================================================= Item { id: wrappedButton property color defaultBackgroundColor: 'white' property color defaultForegroundColor: 'black' // --------------------------------------------------------------------------- readonly property QtObject defaultColorSet : QtObject { property int iconSize: 30 property string icon : '' property color backgroundNormalColor : defaultBackgroundColor property color backgroundDisabledColor : defaultBackgroundColor property color backgroundHoveredColor : defaultBackgroundColor property color backgroundUpdatingColor : defaultBackgroundColor property color backgroundPressedColor : defaultBackgroundColor // Color for shown part property color foregroundNormalColor : defaultForegroundColor property color foregroundDisabledColor : defaultForegroundColor property color foregroundHoveredColor : defaultForegroundColor property color foregroundUpdatingColor : defaultForegroundColor property color foregroundPressedColor : defaultForegroundColor } property QtObject colorSet: defaultColorSet onColorSetChanged: if(!colorSet) colorSet = defaultColorSet property bool isCustom : false property bool iconIsCustom: isCustom property bool enabled: true property bool updating: false property bool useStates: true property bool toggled: false //property bool autoIcon : false // hovered/pressed : use an automatic layer instead of specific icon image property int iconSize : colorSet ? colorSet.iconSize : 0 property int iconHeight: colorSet.iconHeight ? colorSet.iconHeight : 0 property int iconWidth: colorSet.iconWidth ? colorSet.iconWidth : 0 readonly property alias hovered: button.hovered property alias text: button.text property alias longPressedTimeout: longPressedTimeout.interval // default: 500ms // Tooltip aliases property alias tooltipText : tooltip.text property alias tooltipIsClickable : tooltip.isClickable property alias tooltipMaxWidth: tooltip.maxWidth property alias tooltipVisible: tooltip.visible // Custom mode property alias backgroundRadius : backgroundColor.radius property alias horizontalAlignment: icon.horizontalAlignment property alias verticalAlignment: icon.verticalAlignment property alias fillMode: icon.fillMode // Hidden part : transparent if not specified property color backgroundHiddenPartNormalColor : colorSet.backgroundHiddenPartNormalColor ? colorSet.backgroundHiddenPartNormalColor : (colorSet.backgroundNormalColor ? colorSet.backgroundNormalColor : 'transparent') property color backgroundHiddenPartDisabledColor : colorSet.backgroundHiddenPartDisabledColor ? colorSet.backgroundHiddenPartDisabledColor : (colorSet.backgroundDisabledColor ? colorSet.backgroundDisabledColor : 'transparent') property color backgroundHiddenPartHoveredColor : colorSet.backgroundHiddenPartHoveredColor ? colorSet.backgroundHiddenPartHoveredColor : (colorSet.backgroundHoveredColor ? colorSet.backgroundHoveredColor : 'transparent') property color backgroundHiddenPartUpdatingColor : colorSet.backgroundHiddenPartUpdatingColor ? colorSet.backgroundHiddenPartUpdatingColor : (colorSet.backgroundUpdatingColor ? colorSet.backgroundUpdatingColor : 'transparent') property color backgroundHiddenPartPressedColor : colorSet.backgroundHiddenPartPressedColor ? colorSet.backgroundHiddenPartPressedColor : (colorSet.backgroundPressedColor ? colorSet.backgroundPressedColor : 'transparent') // AutoColor : alpha /4 for foreground property color foregroundHiddenPartNormalColor : colorSet.foregroundHiddenPartNormalColor ? colorSet.foregroundHiddenPartNormalColor : (colorSet.foregroundNormalColor ? Qt.rgba(colorSet.foregroundNormalColor.r, colorSet.foregroundNormalColor.g, colorSet.foregroundNormalColor.b, colorSet.foregroundNormalColor.a/4) : 'transparent') property color foregroundHiddenPartDisabledColor : colorSet.foregroundHiddenPartDisabledColor ? colorSet.foregroundHiddenPartDisabledColor : (colorSet.foregroundDisabledColor ? Qt.rgba(colorSet.foregroundDisabledColor.r, colorSet.foregroundDisabledColor.g, colorSet.foregroundDisabledColor.b, colorSet.foregroundDisabledColor.a/4): 'transparent') property color foregroundHiddenPartHoveredColor : colorSet.foregroundHiddenPartHoveredColor ? colorSet.foregroundHiddenPartHoveredColor : (colorSet.foregroundHoveredColor ? Qt.rgba(colorSet.foregroundHoveredColor.r, colorSet.foregroundHoveredColor.g, colorSet.foregroundHoveredColor.b, colorSet.foregroundHoveredColor.a/4): 'transparent') property color foregroundHiddenPartUpdatingColor : colorSet.foregroundHiddenPartUpdatingColor ? colorSet.foregroundHiddenPartUpdatingColor : (colorSet.foregroundUpdatingColor ? Qt.rgba(colorSet.foregroundUpdatingColor.r, colorSet.foregroundUpdatingColor.g, colorSet.foregroundUpdatingColor.b, colorSet.foregroundUpdatingColor.a/4): 'transparent') property color foregroundHiddenPartPressedColor : colorSet.foregroundHiddenPartPressedColor ? colorSet.foregroundHiddenPartPressedColor : (colorSet.foregroundPressedColor ? Qt.rgba(colorSet.foregroundPressedColor.r, colorSet.foregroundPressedColor.g, colorSet.foregroundPressedColor.b, colorSet.foregroundPressedColor.a/4): 'transparent') //--------------------------------------------- property int percentageDisplayed : 100 // If `useStates` = true, the used icons are: // `icon`_pressed, `icon`_hovered and `icon`_normal. property string icon : colorSet ? colorSet.icon : '' // --------------------------------------------------------------------------- signal clicked(real x, real y) signal pressed(real x, real y) signal released(real x, real y) signal longPressed() // --------------------------------------------------------------------------- function _getIcon () { if(isCustom) return wrappedButton.icon if(wrappedButton.icon == '') return wrappedButton.icon; if (wrappedButton.updating || wrappedButton.toggled) { return wrappedButton.icon + '_updating' } if (!useStates) { return wrappedButton.icon } if (!wrappedButton.enabled) { return wrappedButton.icon + '_disabled' } // if(!autoIcon) { return wrappedButton.icon + ( button.down ? '_pressed' : (button.hovered ? '_hovered' : '_normal') ) // } // return wrappedButton.icon; } function getColor(color, defaultColor, debugVar){ if(color) return color else{ console.warn("No color defined for :"+debugVar+ " on "+_getIcon()) return defaultColor } } function getBackgroundColor(){ var defaultColor = 'transparent' if(isCustom){ //if(wrappedButton.icon == '') //return getColor(wrappedButton.colorSet.backgroundNormalColor, defaultColor, 'backgroundNormalColor') if (wrappedButton.updating || wrappedButton.toggled) return getColor(wrappedButton.colorSet.backgroundUpdatingColor, defaultColor, 'backgroundUpdatingColor') if (!useStates) return getColor(wrappedButton.colorSet.backgroundNormalColor, defaultColor, 'backgroundNormalColor') if (!wrappedButton.enabled) return getColor(wrappedButton.colorSet.backgroundDisabledColor, defaultColor, 'backgroundDisabledColor') return button.down ? getColor(wrappedButton.colorSet.backgroundPressedColor, defaultColor, 'backgroundPressedColor') : (button.hovered ? getColor(wrappedButton.colorSet.backgroundHoveredColor, defaultColor, 'backgroundHoveredColor') : getColor(wrappedButton.colorSet.backgroundNormalColor, defaultColor, 'backgroundNormalColor')) }else return defaultColor } function getForegroundColor(){ var defaultColor = 'black' if(isCustom){ //if(wrappedButton.icon == '') //return getColor(wrappedButton.colorSet.foregroundNormalColor, defaultColor, 'foregroundNormalColor') if (wrappedButton.updating || wrappedButton.toggled) return getColor(wrappedButton.colorSet.foregroundUpdatingColor, defaultColor, 'foregroundUpdatingColor') if (!useStates) return getColor(wrappedButton.colorSet.foregroundNormalColor, defaultColor, 'foregroundNormalColor') if (!wrappedButton.enabled) return getColor(wrappedButton.colorSet.foregroundDisabledColor, defaultColor, 'foregroundDisabledColor') return button.down ? getColor(wrappedButton.colorSet.foregroundPressedColor, defaultColor, 'foregroundPressedColor') : (button.hovered ? getColor(wrappedButton.colorSet.foregroundHoveredColor, defaultColor, 'foregroundHoveredColor') : getColor(wrappedButton.colorSet.foregroundNormalColor, defaultColor, 'foregroundNormalColor')) }else return defaultColor } function getBackgroundHiddenPartColor(){ var defaultColor = 'transparent' if(isCustom){ //if(wrappedButton.icon == '') //return getColor(wrappedButton.colorSet.backgroundHiddenPartNormalColor, defaultColor, 'backgroundHiddenPartNormalColor') if (wrappedButton.updating || wrappedButton.toggled) return getColor(wrappedButton.colorSet.backgroundHiddenPartUpdatingColor, defaultColor, 'backgroundHiddenPartUpdatingColor') if (!useStates) return getColor(wrappedButton.colorSet.backgroundHiddenPartNormalColor, defaultColor, 'backgroundHiddenPartNormalColor') if (!wrappedButton.enabled) return getColor(wrappedButton.colorSet.backgroundHiddenPartDisabledColor, defaultColor, 'backgroundHiddenPartDisabledColor') return button.down ? getColor(wrappedButton.colorSet.backgroundHiddenPartPressedColor, defaultColor, 'backgroundHiddenPartPressedColor') : (button.hovered ? getColor(wrappedButton.colorSet.backgroundHiddenPartHoveredColor, defaultColor, 'backgroundHiddenPartHoveredColor') : getColor(wrappedButton.colorSet.backgroundHiddenPartNormalColor, defaultColor, 'backgroundHiddenPartNormalColor')) }else return defaultColor } function getForegroundHiddenPartColor(){ var defaultColor = '#80FFFFFF' if(isCustom){ //if(wrappedButton.icon == '') //return getColor(wrappedButton.colorSet.foregroundHiddenPartNormalColor, defaultColor, 'foregroundHiddenPartNormalColor') if (wrappedButton.updating || wrappedButton.toggled) return getColor(wrappedButton.colorSet.foregroundHiddenPartUpdatingColor, defaultColor, 'foregroundHiddenPartUpdatingColor') if (!useStates) return getColor(wrappedButton.colorSet.foregroundHiddenPartNormalColor, defaultColor, 'foregroundHiddenPartNormalColor') if (!wrappedButton.enabled) return getColor(wrappedButton.colorSet.foregroundHiddenPartDisabledColor, defaultColor, 'foregroundHiddenPartDisabledColor') return button.down ? getColor(wrappedButton.colorSet.foregroundHiddenPartPressedColor, defaultColor, 'foregroundHiddenPartPressedColor') : (button.hovered ? getColor(wrappedButton.colorSet.foregroundHiddenPartHoveredColor, defaultColor, 'foregroundHiddenPartHoveredColor') : getColor(wrappedButton.colorSet.foregroundHiddenPartNormalColor, defaultColor, 'foregroundHiddenPartNormalColor')) }else return defaultColor } // --------------------------------------------------------------------------- property int fitHeight: iconHeight || iconSize || parent.iconSize || parent.height property int fitWidth: iconWidth || iconSize || parent.iconSize || parent.width height: fitHeight width: fitWidth Button { id: button anchors.fill: parent background: Row{ anchors.fill: parent Rectangle { height: parent.height width:parent.width * wrappedButton.percentageDisplayed / 100 id: backgroundColor color: getBackgroundColor() } Rectangle { height: parent.height width: parent.width * ( 1 - wrappedButton.percentageDisplayed / 100 ) id: backgroundHiddenPartColor color: width > 0 ? getBackgroundHiddenPartColor() : 'transparent' } } hoverEnabled: !wrappedButton.updating//|| wrappedButton.autoIcon onClicked: { longPressedTimeout.stop() if(!wrappedButton.updating && wrappedButton.enabled) wrappedButton.clicked(pressX, pressY) } onPressed: if(!wrappedButton.updating && wrappedButton.enabled){ longPressedTimeout.restart() wrappedButton.pressed(pressX, pressY) } onReleased: { longPressedTimeout.stop() if(!wrappedButton.updating && wrappedButton.enabled) wrappedButton.released(pressX, pressY) } onHoveredChanged: if(!hovered) longPressedTimeout.stop() Timer{ id: longPressedTimeout interval: 500 repeat: false onTriggered: if(!wrappedButton.updating && wrappedButton.enabled) wrappedButton.longPressed() } Rectangle{ id: foregroundColor anchors.fill:parent visible: false color: 'transparent' Rectangle{ anchors.fill:parent color: getForegroundColor() anchors.rightMargin: parent.width * ( 1 - wrappedButton.percentageDisplayed / 100 ) } } Rectangle{ id: foregroundHiddenPartColor anchors.fill:parent visible: false color: 'transparent' Rectangle{ anchors.fill:parent color: percentageDisplayed != 100 ? getForegroundHiddenPartColor() : 'transparent' anchors.leftMargin: parent.width * wrappedButton.percentageDisplayed / 100 } } Icon { id: icon anchors.centerIn: parent anchors.fill: iconHeight>0 || iconWidth ? parent : undefined icon: { var iconString = _getIcon() if( iconString ) { if(Images[iconString]) return Images[iconString].id else console.log("No images for: "+iconString) } return '' } iconSize: wrappedButton.iconSize || ( parent.width > parent.height ? parent.height : parent.width ) iconHeight: wrappedButton.iconHeight iconWidth: wrappedButton.iconWidth visible: !iconIsCustom } OpacityMask{ anchors.fill: icon source: foregroundColor maskSource: icon visible: iconIsCustom MouseArea{ anchors.fill:parent hoverEnabled: true acceptedButtons: Qt.NoButton cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor } } OpacityMask{ id: mask anchors.fill: icon source: foregroundHiddenPartColor maskSource: icon visible: iconIsCustom && percentageDisplayed != 100 /* layer { enabled: true effect: ColorOverlay { color: "#80FFFFFF" } }*/ MouseArea{ anchors.fill:parent hoverEnabled: true acceptedButtons: Qt.NoButton cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor } } TooltipArea { id:tooltip text: '' visible:text!='' } MouseArea{ anchors.fill:parent hoverEnabled: true acceptedButtons: Qt.NoButton cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor visible: !iconIsCustom && !tooltip.visible } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/ActionSwitch.qml000066400000000000000000000020371434616504300267350ustar00rootroot00000000000000import QtQuick 2.7 // ============================================================================= Item { property alias updating: actionButton.updating property alias useStates: actionButton.useStates property alias text: actionButton.text property bool enabled: true // Custom mode property alias isCustom : actionButton.isCustom property alias backgroundRadius : actionButton.backgroundRadius property alias colorSet : actionButton.colorSet property alias iconSize : actionButton.iconSize property alias icon : actionButton.icon // --------------------------------------------------------------------------- signal clicked // --------------------------------------------------------------------------- height: iconSize || parent.iconSize || parent.height width: iconSize || parent.iconSize || parent.width ActionButton { id: actionButton enabled: parent.enabled anchors.fill: parent //icon: parent.icon// + (parent.enabled ? '_on' : '_off') //iconSize: parent.iconSize onClicked: parent.clicked() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Buttons/000077500000000000000000000000001434616504300252575ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Buttons/AbstractTextButton.qml000066400000000000000000000057071434616504300316070ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import Common 1.0 import Common.Styles 1.0 // ============================================================================= Item { id: wrappedButton property color colorDisabled property color colorHovered property color colorNormal property color colorPressed // By default textColorNormal is the hovered/pressed text color. property color textColorDisabled property color textColorHovered: textColorNormal property color textColorNormal property color textColorPressed: textColorNormal property color borderColorDisabled property color borderColorHovered property color borderColorNormal property color borderColorPressed property alias text: button.text property bool enabled: true property bool showBorder : false property alias toggled : button.checked property alias capitalization : button.capitalization //Additional size around text property int addHeight: 25 property int addWidth: 60 signal clicked // --------------------------------------------------------------------------- function _getBackgroundColor () { if (!wrappedButton.enabled) { return colorDisabled } return button.down || button.checked ? colorPressed : (button.hovered ? colorHovered : colorNormal) } function _getBorderColor () { if (!wrappedButton.enabled) { return borderColorDisabled } return button.down || button.checked ? borderColorPressed : (button.hovered ? borderColorHovered : borderColorNormal) } function _getTextColor () { if (!wrappedButton.enabled) { return textColorDisabled } return button.down || button.checked ? textColorPressed : (button.hovered ? textColorHovered : textColorNormal) } // --------------------------------------------------------------------------- property int fitHeight: button.contentItem.implicitHeight + addHeight property int fitWidth: button.contentItem.implicitWidth + addWidth height: fitHeight width: fitWidth // --------------------------------------------------------------------------- Button { id: button property int capitalization background: Rectangle { color: _getBackgroundColor() radius: AbstractTextButtonStyle.background.radius border.color: _getBorderColor() border.width: (showBorder ? 1 : 0) } contentItem: Text { color: _getTextColor() font { bold: true pointSize: AbstractTextButtonStyle.text.pointSize capitalization: button.capitalization } wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter text: button.text verticalAlignment: Text.AlignVCenter } hoverEnabled: true MouseArea { id: mouseArea anchors.fill: parent onPressed: mouse.accepted = false } height: parent.height width: parent.width onClicked: wrappedButton.enabled && parent.clicked() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Buttons/ExclusiveButtons.qml000066400000000000000000000031011434616504300313130ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 // ============================================================================= Row { id: item // --------------------------------------------------------------------------- property int selectedButton: 0 property var texts property int capitalization property QtObject style: ExclusiveButtonsStyle // --------------------------------------------------------------------------- // Emitted when the selected button is changed. // Gives the selected button id. signal clicked (int button) // --------------------------------------------------------------------------- spacing: ExclusiveButtonsStyle.buttonsSpacing Keys.onLeftPressed: { if (selectedButton > 0) { clicked(--selectedButton) } } Keys.onRightPressed: { if (selectedButton < repeater.count - 1) { clicked(++selectedButton) } } // --------------------------------------------------------------------------- Repeater { id: repeater model: texts SmallButton { capitalization: item.capitalization anchors.verticalCenter: parent.verticalCenter backgroundColor: item.style ? selectedButton === index ? item.style.button.color.selected : (down ? item.style.button.color.pressed : (hovered ? item.style.button.color.hovered : item.style.button.color.normal ) ) : '' text: modelData radius: height/2 onClicked: { if (selectedButton !== index) { selectedButton = index item.clicked(index) } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Buttons/ExclusiveButtons.spec.qml000066400000000000000000000037251434616504300322600ustar00rootroot00000000000000import QtQuick 2.7 import QtTest 1.1 // ============================================================================= Item { id: root function buildExclusiveButtons (defaultSelectedButton) { var container = builder.createObject(root) testCase.verify(container) container.data[0].selectedButton = defaultSelectedButton return container } // Avoid `Test 'XXX' has invalid size QSize(0, 0), resizing.` warning. height: 100 width: 300 Component { id: builder Item { ExclusiveButtons { id: exclusiveButtons texts: ['A', 'B', 'C', 'D', 'E'] } } } // --------------------------------------------------------------------------- TestCase { id: testCase when: windowShown function test_signals_data () { return [ { defaultSelectedButton: 0, buttonToClick: 2 }, { defaultSelectedButton: 1, buttonToClick: 4 }, { defaultSelectedButton: 3, buttonToClick: 1 }, { defaultSelectedButton: 4, buttonToClick: 0 } ] } function test_signals (data) { var container = buildExclusiveButtons(data.defaultSelectedButton) var exclusiveButtons = container.data[0] var buttonToClick = data.buttonToClick // Test default selected button. compare(exclusiveButtons.selectedButton, data.defaultSelectedButton) var button = -1 var count = 0 exclusiveButtons.clicked.connect(function (_button) { button = _button count += 1 }) // Test a click to change the selected button. mouseClick(exclusiveButtons.data[buttonToClick]) compare(button, buttonToClick) compare(exclusiveButtons.selectedButton, buttonToClick) compare(count, 1) // No signal must be emitted. mouseClick(exclusiveButtons.data[buttonToClick]) compare(button, buttonToClick) compare(exclusiveButtons.selectedButton, buttonToClick) compare(count, 1) container.destroy() } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Buttons/FileChooserButton.qml000066400000000000000000000044321434616504300313730ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Dialogs 1.2 import Common 1.0 import Common.Styles 1.0 import Utils 1.0 // ============================================================================= TextField { id: textField // --------------------------------------------------------------------------- property alias selectExisting: fileDialog.selectExisting property alias selectFolder: fileDialog.selectFolder property alias title: fileDialog.title property string selectedFile: '' // --------------------------------------------------------------------------- signal accepted (var selectedFile) signal rejected // --------------------------------------------------------------------------- text: { var path = textField.selectedFile return path.length ? path : '' } tools: Item { height: parent.height width: FileChooserButtonStyle.tools.width Rectangle { anchors { fill: parent margins: TextFieldStyle.normal.background.border.width } color: mouseArea.pressed ? FileChooserButtonStyle.tools.button.color.pressed : ( mouseArea.containsMouse ? FileChooserButtonStyle.tools.button.color.hovered : FileChooserButtonStyle.tools.button.color.normal ) ActionButton { anchors.centerIn: parent isCustom: true backgroundRadius: 90 colorSet: textField.selectFolder ? FileChooserButtonStyle.folder : FileChooserButtonStyle.file } } } // --------------------------------------------------------------------------- FileDialog { id: fileDialog folder: { var selectedFile = textField.selectedFile if (!selectedFile.length) { return '' } return Utils.getUriFromSystemPath( textField.selectFolder ? selectedFile : Utils.dirname(selectedFile) ) } onAccepted: { var selectedFile = Utils.getSystemPathFromUri(fileUrl) textField.selectedFile = selectedFile textField.accepted(selectedFile) } onRejected: textField.rejected() } // --------------------------------------------------------------------------- MouseArea { id: mouseArea anchors.fill: parent enabled: !textField.readOnly onClicked: fileDialog.open() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Buttons/SmallButton.qml000066400000000000000000000022651434616504300302430ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import Common 1.0 import Common.Styles 1.0 // ============================================================================= Button { id: button property alias backgroundColor: background.color property alias radius: background.radius property int capitalization background: Rectangle { id: background color: button.down ? SmallButtonStyle.background.color.pressed : (button.hovered ? SmallButtonStyle.background.color.hovered : SmallButtonStyle.background.color.normal ) implicitHeight: SmallButtonStyle.background.height radius: SmallButtonStyle.background.radius } contentItem: Text { color: SmallButtonStyle.text.color font.pointSize: SmallButtonStyle.text.pointSize font.weight: Font.Bold font.capitalization: button.capitalization horizontalAlignment: Text.AlignHCenter text: button.text verticalAlignment: Text.AlignVCenter leftPadding: SmallButtonStyle.leftPadding rightPadding: SmallButtonStyle.rightPadding } hoverEnabled: true MouseArea { id: mouseArea anchors.fill: parent onPressed: mouse.accepted = false } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Buttons/TextButtonA.qml000066400000000000000000000015161434616504300302160ustar00rootroot00000000000000import Common.Styles 1.0 // ============================================================================= AbstractTextButton { property var textButtonStyle : TextButtonAStyle colorDisabled: textButtonStyle.backgroundColor.disabled colorHovered: textButtonStyle.backgroundColor.hovered colorNormal: textButtonStyle.backgroundColor.normal colorPressed: textButtonStyle.backgroundColor.pressed textColorDisabled: textButtonStyle.textColor.disabled textColorHovered: textButtonStyle.textColor.hovered textColorNormal: textButtonStyle.textColor.normal textColorPressed: textButtonStyle.textColor.pressed borderColorDisabled: textButtonStyle.borderColor.disabled borderColorHovered: textButtonStyle.borderColor.hovered borderColorNormal: textButtonStyle.borderColor.normal borderColorPressed: textButtonStyle.borderColor.pressed } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Buttons/TextButtonB.qml000066400000000000000000000017711434616504300302220ustar00rootroot00000000000000import Common.Styles 1.0 // ============================================================================= AbstractTextButton { property var textButtonStyle : TextButtonBStyle colorDisabled: textButtonStyle.backgroundColor.disabled colorHovered: textButtonStyle.backgroundColor.hovered colorNormal: textButtonStyle.backgroundColor.normal colorPressed: textButtonStyle.backgroundColor.pressed textColorDisabled: textButtonStyle.textColor.disabled textColorHovered: textButtonStyle.textColor.hovered textColorNormal: textButtonStyle.textColor.normal textColorPressed: textButtonStyle.textColor.pressed borderColorDisabled: (textButtonStyle.borderColor?textButtonStyle.borderColor.disabled:colorDisabled) borderColorHovered: (textButtonStyle.borderColor?textButtonStyle.borderColor.hovered:colorHovered) borderColorNormal: (textButtonStyle.borderColor?textButtonStyle.borderColor.normal:colorNormal) borderColorPressed: (textButtonStyle.borderColor?textButtonStyle.borderColor.pressed:colorPressed) } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Buttons/TextButtonC.qml000066400000000000000000000017711434616504300302230ustar00rootroot00000000000000import Common.Styles 1.0 // ============================================================================= AbstractTextButton { property var textButtonStyle : TextButtonCStyle colorDisabled: textButtonStyle.backgroundColor.disabled colorHovered: textButtonStyle.backgroundColor.hovered colorNormal: textButtonStyle.backgroundColor.normal colorPressed: textButtonStyle.backgroundColor.pressed textColorDisabled: textButtonStyle.textColor.disabled textColorHovered: textButtonStyle.textColor.hovered textColorNormal: textButtonStyle.textColor.normal textColorPressed: textButtonStyle.textColor.pressed borderColorDisabled: (textButtonStyle.borderColor?textButtonStyle.borderColor.disabled:colorDisabled) borderColorHovered: (textButtonStyle.borderColor?textButtonStyle.borderColor.hovered:colorHovered) borderColorNormal: (textButtonStyle.borderColor?textButtonStyle.borderColor.normal:colorNormal) borderColorPressed: (textButtonStyle.borderColor?textButtonStyle.borderColor.pressed:colorPressed) } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/CheckBoxText.qml000066400000000000000000000057701434616504300267000ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.4 import QtQuick.Shapes 1.10 import Common.Styles 1.0 // ============================================================================= // Checkbox with clickable text. // ============================================================================= CheckBox { id: checkBox contentItem: Text { color: checkBox.down ? CheckBoxTextStyle.color.pressed : ( checkBox.hovered ? CheckBoxTextStyle.color.hovered : CheckBoxTextStyle.color.normal ) elide: Text.ElideRight font: checkBox.font leftPadding: checkBox.indicator.width + checkBox.spacing text: checkBox.text width: parent.width wrapMode: Text.WordWrap verticalAlignment: Text.AlignVCenter onLinkActivated: Qt.openUrlExternally(link) MouseArea { id: mouseArea anchors.fill: parent cursorShape: parent.hoveredLink != '' ? Qt.PointingHandCursor : Qt.ArrowCursor acceptedButtons: Qt.NoButton } } font.pointSize: CheckBoxTextStyle.pointSize hoverEnabled: true indicator: Rectangle { border.color: checkBox.down ? CheckBoxTextStyle.color.pressed : ( checkBox.hovered ? CheckBoxTextStyle.color.hovered : ( checkBox.checked ? CheckBoxTextStyle.color.selected : CheckBoxTextStyle.color.normal ) ) implicitHeight: CheckBoxTextStyle.size implicitWidth: CheckBoxTextStyle.size radius: CheckBoxTextStyle.radius x: checkBox.leftPadding y: parent.height / 2 - height / 2 Rectangle { color: checkBox.down ? CheckBoxTextStyle.color.pressed : (checkBox.hovered ? CheckBoxTextStyle.color.hovered : ( checkBox.checked ? CheckBoxTextStyle.color.selected : CheckBoxTextStyle.color.normal ) ) height: parent.height - y * 2 width: parent.width - x * 2 radius: CheckBoxTextStyle.radius visible: checkBox.checkState == Qt.Checked x: 4 // Fixed, no needed to use style file. y: 4 // Same thing. } Shape{ id: partiallyShape anchors.fill: parent visible: checkBox.checkState == Qt.PartiallyChecked ShapePath{ strokeColor: checkBox.down ? CheckBoxTextStyle.color.pressed : (checkBox.hovered ? CheckBoxTextStyle.color.hovered : ( checkBox.checked ? CheckBoxTextStyle.color.selected : CheckBoxTextStyle.color.normal ) ) strokeWidth: 2 fillColor: 'transparent' joinStyle: ShapePath.MiterJoin startX: 6 startY: 6 PathLine{x: partiallyShape.width - 6; y: partiallyShape.height - 6} } ShapePath{ strokeColor: checkBox.down ? CheckBoxTextStyle.color.pressed : (checkBox.hovered ? CheckBoxTextStyle.color.hovered : CheckBoxTextStyle.color.normal ) strokeWidth: 2 fillColor: 'transparent' joinStyle: ShapePath.MiterJoin startX: partiallyShape.width - 6 startY: 6 PathLine{x: 6; y: partiallyShape.height - 6} } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/ComboBox.js000066400000000000000000000044701434616504300256740ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `ComboBox.qml` Logic. // ============================================================================= .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function getSelectedEntryIcon () { var iconRole = comboBox.iconRole if (iconRole == null || iconRole.length === 0) { return '' } var currentIndex = comboBox.currentIndex if (currentIndex < 0) { return '' } var model = comboBox.model if (Utils.isFunction(iconRole)) { return iconRole( Utils.isArray(model) ? model[currentIndex] : model.get(currentIndex) ) } return ( Utils.isArray(model) ? model[currentIndex][iconRole] : model.get(currentIndex)[iconRole] ) || '' } function getSelectedEntryText () { if (comboBox.currentIndex < 0) { return '' } var text = comboBox.displayText if (text.length > 0) { return text } // With a `QAbstractListModel`, `text` is empty. QML bug? var model = comboBox.model if (model.data) { var item = model.data(model.index(comboBox.currentIndex, 0)) var textRole = comboBox.textRole return textRole.length > 0 ? item[textRole] : item } return '' } function getItemIcon (item) { var iconRole = comboBox.iconRole if (iconRole == null || iconRole.length === 0) { return '' } return Utils.isFunction(iconRole) ? iconRole(item.flattenedModel) : item.flattenedModel[iconRole] } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/ComboBox.qml000066400000000000000000000067321434616504300260540ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 import 'ComboBox.js' as Logic // ============================================================================= Controls.ComboBox { id: comboBox // --------------------------------------------------------------------------- property var iconRole property bool haveBorder: true property bool haveMargin: true property color backgroundColor: ComboBoxStyle.background.color.normal property color foregroundColor: ComboBoxStyle.contentItem.text.color property var rootItem property int yPopup: rootItem ? -mapToItem(rootItem,x,y).y : height + 1 property int maxPopupHeight : rootItem ? rootItem.height : 400 property int selectionWidth: width property int fitWidth: contentItem.fitWidth + ComboBoxStyle.indicator.dropDown.iconSize clip: true // --------------------------------------------------------------------------- background: Rectangle { border { color: ComboBoxStyle.background.border.color width: comboBox.haveBorder ? ComboBoxStyle.background.border.width : 0 } color: comboBox.enabled ? comboBox.backgroundColor : ComboBoxStyle.background.color.readOnly radius: ComboBoxStyle.background.radius implicitHeight: ComboBoxStyle.background.height implicitWidth: ComboBoxStyle.background.width } // --------------------------------------------------------------------------- contentItem: Item { property int fitWidth: contentText.implicitWidth + ComboBoxStyle.contentItem.iconSize + contentLayout.anchors.leftMargin height: comboBox.height width: comboBox.selectionWidth clip: true RowLayout { id: contentLayout anchors { fill: parent leftMargin: comboBox.haveMargin ? ComboBoxStyle.contentItem.leftMargin : 0 } spacing: ComboBoxStyle.contentItem.spacing Icon { icon: Logic.getSelectedEntryIcon() iconSize: ComboBoxStyle.contentItem.iconSize visible: icon.length > 0 } Text { id: contentText Layout.fillWidth: true color: comboBox.foregroundColor elide: Text.ElideRight font.pointSize: ComboBoxStyle.contentItem.text.pointSize rightPadding: comboBox.indicator.width + comboBox.spacing text: Logic.getSelectedEntryText() } } } // --------------------------------------------------------------------------- indicator: Icon { icon: ComboBoxStyle.indicator.dropDown.icon iconSize: ComboBoxStyle.indicator.dropDown.iconSize overwriteColor: ComboBoxStyle.indicator.dropDown.color x: comboBox.width - width - comboBox.rightPadding y: comboBox.topPadding + (comboBox.availableHeight - height) / 2 } // --------------------------------------------------------------------------- popup: Controls.Popup{ id: popupItem y: comboBox.yPopup x: comboBox.rootItem ? comboBox.width : 0 width: comboBox.selectionWidth implicitHeight: selector.contentHeight Connections{// Break binding loops target: selector ignoreUnknownSignals: true onContentHeightChanged: Qt.callLater(function(){popupItem.implicitHeight = selector.contentHeight}) } topPadding: 0 bottomPadding: 0 leftPadding: 0 rightPadding: 0 contentItem: ListItemSelector{ id: selector model: comboBox.popup.visible ? comboBox.model : null currentIndex: comboBox.highlightedIndex textRole: comboBox.textRole onActivated: {comboBox.activated(index);comboBox.currentIndex = index;comboBox.popup.close()} } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/CommonItemDelegate.qml000066400000000000000000000032551434616504300300430ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 // ============================================================================= Controls.ItemDelegate { id: item property var container property var flattenedModel property var itemIcon default property alias _content: content.data hoverEnabled: true background: Rectangle { color: item.hovered ? CommonItemDelegateStyle.color.hovered : CommonItemDelegateStyle.color.normal Rectangle { anchors.left: parent.left color: CommonItemDelegateStyle.indicator.color height: parent.height width: CommonItemDelegateStyle.indicator.width visible: item.hovered } Rectangle { anchors.bottom: parent.bottom color: CommonItemDelegateStyle.separator.color height: CommonItemDelegateStyle.separator.height width: parent.width visible: container.count !== index + 1 } } contentItem: RowLayout { spacing: CommonItemDelegateStyle.contentItem.spacing width: item.width Icon { icon: item.itemIcon iconSize: CommonItemDelegateStyle.contentItem.iconSize visible: icon.length > 0 } Text { Layout.fillWidth: true color: CommonItemDelegateStyle.contentItem.text.color elide: Text.ElideRight font { bold: container.currentIndex === index pointSize: CommonItemDelegateStyle.contentItem.text.pointSize } text: item.flattenedModel[container.textRole] || modelData } Item { id: content Layout.preferredWidth: CommonItemDelegateStyle.contentItem.iconSize Layout.preferredHeight: CommonItemDelegateStyle.contentItem.iconSize } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/DroppableTextArea.qml000066400000000000000000000177101434616504300277100ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.12 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Utils 1.0 // ============================================================================= Item { id: droppableTextArea property int minimumHeight property int maximumHeight property alias placeholderText: textArea.placeholderText property alias text: textArea.text property alias cursorPosition: textArea.cursorPosition property alias recordAudioToggled: recordAudioButton.toggled property bool dropEnabled: true property string dropDisabledReason property bool isEphemeral : false property int textLeftMargin: (fileChooserButton.visible? fileChooserButton.totalWidth + DroppableTextAreaStyle.fileChooserButton.margins: 0) property int textRightMargin: sendButton.visible ? sendButton.totalWidth + DroppableTextAreaStyle.fileChooserButton.margins : 0 // --------------------------------------------------------------------------- signal dropped (var files) signal validText (string text) signal audioRecordRequest() // --------------------------------------------------------------------------- function _emitFiles (files) { // Filtering files, other urls are forbidden. files = files.reduce(function (files, file) { if (file.startsWith('file:')) { files.push(Utils.getSystemPathFromUri(file)) } return files }, []) if (files.length > 0) { dropped(files) } } Rectangle{ anchors.fill: parent color: DroppableTextAreaStyle.outsideBackgroundColor // --------------------------------------------------------------------------- RowLayout{ anchors.fill: parent spacing: 0 // Handle click to select files. ActionButton { id: fileChooserButton property int totalWidth: width Layout.leftMargin: DroppableTextAreaStyle.fileChooserButton.margins Layout.alignment: Qt.AlignVCenter //anchors.verticalCenter: parent.verticalCenter enabled: droppableTextArea.dropEnabled isCustom: true backgroundRadius: 8 colorSet: DroppableTextAreaStyle.fileChooserButton visible: droppableTextArea.enabled onClicked: fileDialogLoader.active=true Loader{// Qt display warnings on open this FileDialog. A loader is used to avoid printing warnings each time a chat is shown. id: fileDialogLoader active: false sourceComponent: Component{ FileDialog { id: fileDialog folder: shortcuts.home title: qsTr('fileChooserTitle') onAccepted: {_emitFiles(fileDialog.fileUrls);fileDialogLoader.active = false} onRejected: fileDialogLoader.active = false Component.onCompleted: fileDialog.open() } } } tooltipText: droppableTextArea.dropEnabled ? qsTr('attachmentTooltip') : droppableTextArea.dropDisabledReason } // Record audio ActionButton { id: recordAudioButton visible: droppableTextArea.enabled Layout.alignment: Qt.AlignVCenter Layout.leftMargin: 0 enabled: droppableTextArea.dropEnabled isCustom: true backgroundRadius: 8 colorSet: DroppableTextAreaStyle.chatMicro onClicked: droppableTextArea.audioRecordRequest() } // Text area. Flickable { id:flickableArea Layout.fillWidth: true Layout.fillHeight: true Layout.maximumHeight: parent.height-20 Layout.topMargin: 10 Layout.bottomMargin: 10 Layout.leftMargin: 2 //anchors.fill: parent boundsBehavior: Flickable.StopAtBounds clip:true ScrollBar.vertical: ForceScrollBar { id: scrollBar visible:false } TextArea.flickable: TextArea { id: textArea onLineCountChanged: { if(textArea.contentHeight+20 Qt.Key_Any && event.key <= Qt.Key_ydiaeresis)// Remove the previous character if it is a printable character textArea.remove(cursorPosition-1, cursorPosition) } }else isAutoRepeating = false// We are no more repeating. Final decision is done on Releasing } Keys.onPressed: { if(event.isAutoRepeat){ isAutoRepeating = true// Where are repeating the key. Set the state. if(event.key > Qt.Key_Any && event.key <= Qt.Key_ydiaeresis){// Ignore character if it is repeating and printable character event.accepted = true } }else if (event.matches(StandardKey.InsertLineSeparator)) { insert(cursorPosition, '') } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { handleValidation() event.accepted = true } } } } // Handle click to select files. ActionButton { id: sendButton property int totalWidth: Layout.rightMargin + Layout.leftMargin + width Layout.rightMargin: droppableTextArea.isEphemeral ? 20 : 15 Layout.leftMargin: droppableTextArea.isEphemeral ? 5 : 10 Layout.alignment: Qt.AlignVCenter Layout.preferredWidth: fitWidth Layout.preferredHeight: fitHeight visible: droppableTextArea.enabled isCustom: true backgroundRadius: 8 colorSet: DroppableTextAreaStyle.send onClicked: textArea.handleValidation() Icon{ visible: sendButton.visible && droppableTextArea.isEphemeral icon: DroppableTextAreaStyle.ephemeralTimer.icon overwriteColor: DroppableTextAreaStyle.ephemeralTimer.timerColor iconSize: DroppableTextAreaStyle.ephemeralTimer.iconSize anchors.right:parent.right anchors.bottom : parent.bottom anchors.bottomMargin:-5 anchors.rightMargin:-17 } } } // Hovered style. Rectangle { id: hoverContent anchors.fill: parent color: DroppableTextAreaStyle.hoverContent.backgroundColor visible: false Text { anchors.centerIn: parent color: DroppableTextAreaStyle.hoverContent.text.color font.pointSize: DroppableTextAreaStyle.hoverContent.text.pointSize text: qsTr('dropYourAttachment') } } DropArea { anchors.fill: parent keys: [ 'text/uri-list' ] visible: droppableTextArea.dropEnabled onDropped: { state = '' if (drop.hasUrls) { _emitFiles(drop.urls) } } onEntered: state = 'hover' onExited: state = '' states: State { name: 'hover' PropertyChanges { target: hoverContent; visible: true } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Fields/000077500000000000000000000000001434616504300250275ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Fields/HexField.qml000066400000000000000000000022431434616504300272330ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 // ============================================================================= Item { id: wrapper // --------------------------------------------------------------------------- property alias readOnly: textField.readOnly property string text // --------------------------------------------------------------------------- signal editingFinished (int value) // --------------------------------------------------------------------------- function _computeText (text) { return (+text).toString(16).toUpperCase() } // --------------------------------------------------------------------------- implicitHeight: textField.height width: TextFieldStyle.normal.background.width // --------------------------------------------------------------------------- Binding { property: 'text' target: textField value: _computeText(wrapper.text) } TextField { id: textField validator: RegExpValidator { regExp: /[0-9A-Fa-f]*/ } width: parent.width onEditingFinished: { text = _computeText('0x' + text) wrapper.editingFinished(parseInt(text, 16)) } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Fields/NumericField.qml000066400000000000000000000047641434616504300301230ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import Common 1.0 import Common.Styles 1.0 // ============================================================================= TextField { id: numericField property int maxValue: 9999 property int minValue: 0 property int step: 1 // --------------------------------------------------------------------------- function _decrease () { var value = +numericField.text if (value === minValue) { return } numericField.text = value - step >= minValue ? value - step : minValue numericField.editingFinished() } function _increase () { var value = +numericField.text if (value === maxValue) { return } numericField.text = value + step <= maxValue ? value + step : maxValue numericField.editingFinished() } // --------------------------------------------------------------------------- text: minValue tools: Item { height: parent.height width: NumericFieldStyle.tools.width Column { id: container anchors { fill: parent margins: TextFieldStyle.normal.background.border.width } // ----------------------------------------------------------------------- Component { id: button Button { id: buttonInstance autoRepeat: true background: Rectangle { color: buttonInstance.down && !numericField.readOnly ? NumericFieldStyle.tools.button.color.pressed : NumericFieldStyle.tools.button.color.normal } contentItem: Text { color: NumericFieldStyle.tools.button.text.color text: buttonInstance.text font.pointSize: NumericFieldStyle.tools.button.text.pointSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } text: parent.text height: container.height / 2 width: container.width onClicked: !numericField.readOnly && handler() } } // ----------------------------------------------------------------------- Loader { property string text: '+' property var handler: _increase sourceComponent: button } Loader { property string text: '-' property var handler: _decrease sourceComponent: button } } } validator: IntValidator { bottom: numericField.minValue top: numericField.maxValue } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Fields/PasswordField.qml000066400000000000000000000002461434616504300303120ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 // ============================================================================= TextField { echoMode: TextInput.Password } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Fields/PortField.qml000066400000000000000000000036551434616504300274430ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 import Utils 1.0 // ============================================================================= Item { id: wrapper // --------------------------------------------------------------------------- property alias readOnly: textField.readOnly property bool supportsRange: false property string text // --------------------------------------------------------------------------- signal editingFinished (int portA, int portB) // --------------------------------------------------------------------------- function _extractPorts (text) { var portA = +text.split(':')[0] var portB = (function () { var port = text.split(':')[1] return port && port.length > 0 ? +port : -1 })() if (portB < 0 || portA === portB) { return [ portA, -1 ] } if (portA < portB) { return [ portA, portB ] } return [ portB, portA ] } function _computeText (range) { return range[1] < 0 ? range[0] : range[0] + ':' + range[1] } // --------------------------------------------------------------------------- implicitHeight: textField.height width: TextFieldStyle.normal.background.width // --------------------------------------------------------------------------- Binding { property: 'text' target: textField value: _computeText(_extractPorts(wrapper.text)) } TextField { id: textField validator: RegExpValidator { regExp: wrapper.supportsRange ? Utils.PORT_RANGE_REGEX : Utils.PORT_REGEX } width: parent.width // Workaround to supports empty string. Keys.onEnterPressed: editingFinished() Keys.onReturnPressed: editingFinished() onActiveFocusChanged: !activeFocus && editingFinished() onEditingFinished: { var range = _extractPorts(text) textField.text = _computeText(range) wrapper.editingFinished(range[0], range[1]) } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Fields/ScrollableListViewField.qml000066400000000000000000000020671434616504300322640ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Common.Styles 1.0 // ============================================================================= Rectangle { id: field property bool readOnly: false default property alias _content: content.data property QtObject textFieldStyle : TextFieldStyle.normal color: textFieldStyle ? textFieldStyle.background.color.normal : '' radius: textFieldStyle ? textFieldStyle.background.radius : 0 Item { id: content anchors.fill: parent } Rectangle { anchors.fill: parent border { color: textFieldStyle ? textFieldStyle.background.border.color.normal : '' width: textFieldStyle ? textFieldStyle.background.border.width : 0 } color: 'transparent' radius: field.radius } Rectangle { anchors.fill: parent color: textFieldStyle ? textFieldStyle.background.color.readOnly : '' opacity: 0.8 visible: field.readOnly } MouseArea { anchors.fill: parent cursorShape: Qt.ArrowCursor visible: field.readOnly onWheel: wheel.accepted = true } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Fields/TextAreaField.qml000066400000000000000000000041011434616504300302170ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import Common 1.0 import Common.Styles 1.0 // ============================================================================= Rectangle { id: mainItem property alias text: textArea.text property alias placeholderText: textArea.placeholderText readonly property alias length: textArea.length property alias boundsBehavior: flickable.boundsBehavior property alias font: textArea.font property alias textColor: textArea.color property alias readOnly: textArea.readOnly property int padding: TextAreaFieldStyle.text.padding property alias implicitHeight: flickable.contentHeight property int fitWidth: 0 height: TextAreaFieldStyle.background.height width: TextAreaFieldStyle.background.width border { color: TextAreaFieldStyle.background.border.color width: TextAreaFieldStyle.background.border.width } color: textArea.readOnly ? TextAreaFieldStyle.background.color.readOnly : TextAreaFieldStyle.background.color.normal radius: TextAreaFieldStyle.background.radius // Fit Width computation onTextChanged:{ var lines = text.split('\n') var totalWidth = 0 for(var index in lines){ metrics.text = lines[index] if( totalWidth < metrics.width) totalWidth = metrics.width } fitWidth = totalWidth } TextMetrics{ id: metrics font: mainItem.font } //----------------------------------- Flickable { id: flickable anchors.fill: parent boundsBehavior: Flickable.StopAtBounds ScrollBar.vertical: ForceScrollBar { id: scrollBar contentSizeTarget: flickable.contentHeight sizeTarget: flickable.height Component.onCompleted: updatePolicy() } TextArea.flickable: TextArea { id: textArea background: Item{} color: TextAreaFieldStyle.text.color font.pointSize: TextAreaFieldStyle.text.pointSize selectByMouse: true wrapMode: TextArea.Wrap height: flickable.height bottomPadding: mainItem.padding leftPadding: mainItem.padding rightPadding: mainItem.padding + Number(scrollBar.visible) * scrollBar.width topPadding: mainItem.padding } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Fields/TextField.qml000066400000000000000000000070111434616504300274310ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import Common 1.0 import Common.Styles 1.0 // ============================================================================= // A classic TextInput which supports an icon attribute. // ============================================================================= Controls.TextField { id: textField // --------------------------------------------------------------------------- property alias icon: icon.icon property alias iconSize: icon.iconSize property bool isMandatory: false property alias overwriteColor: icon.overwriteColor property string error: '' property var tools property QtObject textFieldStyle : TextFieldStyle.normal onTextFieldStyleChanged: if( !textFieldStyle) textFieldStyle = TextFieldStyle.normal // --------------------------------------------------------------------------- background: Rectangle { border { color: textField.error.length > 0 ? textFieldStyle.background.border.color.error : ( textField.activeFocus && !textField.readOnly ? textFieldStyle.background.border.color.selected : textFieldStyle.background.border.color.normal ) width: textFieldStyle.background.border.width } color: textField.readOnly ? textFieldStyle.background.color.readOnly : textFieldStyle.background.color.normal implicitHeight: textFieldStyle.background.height implicitWidth: textFieldStyle.background.width radius: textFieldStyle.background.radius MouseArea { anchors.right: parent.right height: parent.height cursorShape: Qt.ArrowCursor implicitWidth: tools ? tools.width : 0 Rectangle { id: toolsContainer border { color: textField.error.length > 0 ? textFieldStyle.background.border.color.error : textFieldStyle.background.border.color.normal width: textFieldStyle.background.border.width } anchors.fill: parent color: background.color data: tools || [] } } } color: textField.readOnly ? textFieldStyle.text.readOnly : textFieldStyle.text.normal font.pointSize: textFieldStyle.text.pointSize rightPadding: textFieldStyle.text.rightPadding + toolsContainer.width + (icon.visible ? icon.iconSize + 10: 0) selectByMouse: true // --------------------------------------------------------------------------- Icon { id: icon anchors { right: parent.right rightMargin: 10 verticalCenter: parent.verticalCenter } iconSize: parent.contentHeight visible: icon.icon != '' Text{ anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 5 textFormat: Text.RichText text : '*' color: textFieldStyle.background.mandatory.color font.pointSize: textFieldStyle.background.mandatory.pointSize visible: textField.isMandatory } MouseArea{ anchors.fill: parent onClicked: textField.text = '' } } bottomPadding: (statusItem.visible?statusItem.implicitHeight:2) TextEdit{ id:statusItem selectByMouse: true readOnly:true color: textFieldStyle.background.border.color.error width:parent.width anchors.bottom:parent.bottom anchors.bottomMargin: 2 anchors.right:parent.right anchors.rightMargin:10 + toolsContainer.width horizontalAlignment:Text.AlignRight verticalAlignment: Text.AlignVCenter wrapMode: TextEdit.WordWrap font { italic: true pointSize: textFieldStyle.text.pointSize } visible:error!= '' text:error } Keys.onPressed:{ if( event.key == Qt.Key_Escape) focus = false } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/ListForm.js000066400000000000000000000056671434616504300257340ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `ListForm.qml` Logic. // ============================================================================= .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function setData (data) { var model = values.model model.clear() if(data) data.forEach(function (data) { model.append({ $value: data, $isInvalid: false }) }) } function setInvalid (index, status) { Utils.assert( index >= 0 && index < values.model.count, 'Index ' + index + 'not exists.' ) values.model.setProperty(index, '$isInvalid', status) } function updateValue (index, value) { var model = values.model // Unable to set property directly. Qt uses a cache of the value. // It's necessary to remove then insert. model.remove(index) model.insert(index, { $isInvalid: false, $value: value }) } // ----------------------------------------------------------------------------- function addValue (value) { values.model.append({ $value: value, $isInvalid: false }) if (value.length === 0) { addButton.enabled = false } } function handleEditionFinished (index, text) { var model = values.model var oldValue = model.get(index).$value if (text.length === 0) { // No changes. It must exists at least n min values. if (minValues != null && minValues >= model.count) { updateValue(index, oldValue) return } model.remove(index) if (oldValue.length !== 0) { listForm.removed(index, oldValue) } } else if (text !== oldValue) { // Update changes. updateValue(index, text) listForm.changed(index, oldValue, text) } addButton.enabled = true } function handleItemCreation () { if (this.text.length === 0) { // FIXME: Find the source of this problem. // // Magic code. If it's the first inserted value, // an event or a callback steal the item focus. // I suppose it's an internal Qt qml event... // // So, I choose to run a callback executed after this // internal event. Utils.setTimeout(values, 0, this.forceActiveFocus) } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/ListForm.qml000066400000000000000000000064751434616504300261070ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 import 'ListForm.js' as Logic // ============================================================================= RowLayout { id: listForm // --------------------------------------------------------------------------- property alias placeholder: placeholder.text property alias title: text.text property bool readOnly: false property int inputMethodHints property var minValues readonly property int count: values.count // --------------------------------------------------------------------------- signal changed (int index, string oldValue, string newValue) signal removed (int index, string value) // --------------------------------------------------------------------------- function setData () { Logic.setData.apply(this, arguments) } function setInvalid () { Logic.setInvalid.apply(this, arguments) } function updateValue () { Logic.updateValue.apply(this, arguments) } // --------------------------------------------------------------------------- spacing: 0 // --------------------------------------------------------------------------- // Title area. // --------------------------------------------------------------------------- RowLayout { Layout.alignment: Qt.AlignTop Layout.preferredHeight: ListFormStyle.lineHeight spacing: ListFormStyle.titleArea.spacing ActionButton { id: addButton colorSet: ListFormStyle.titleArea.add isCustom: true backgroundRadius: 90 opacity: !listForm.readOnly ? 1 : 0 onClicked: !listForm.readOnly && Logic.addValue('') } Text { id: text Layout.preferredWidth: ListFormStyle.titleArea.text.width color: ListFormStyle.titleArea.text.color elide: Text.ElideRight font { bold: true pointSize: ListFormStyle.titleArea.text.pointSize } } } // --------------------------------------------------------------------------- // Placeholder. // --------------------------------------------------------------------------- Text { id: placeholder Layout.fillWidth: true Layout.preferredHeight: ListFormStyle.lineHeight color: ListFormStyle.value.placeholder.color font { italic: true pointSize: ListFormStyle.value.placeholder.pointSize } padding: ListFormStyle.value.text.padding visible: values.model.count === 0 verticalAlignment: Text.AlignVCenter MouseArea { anchors.fill: parent onClicked: !listForm.readOnly && Logic.addValue('') } } // --------------------------------------------------------------------------- // Values. // --------------------------------------------------------------------------- ListView { id: values Layout.fillWidth: true Layout.preferredHeight: count * ListFormStyle.lineHeight interactive: false visible: model.count > 0 delegate: Item { implicitHeight: textInput.height width: values.width TransparentTextInput { id: textInput inputMethodHints: listForm.inputMethodHints isInvalid: $isInvalid readOnly: listForm.readOnly text: $value height: ListFormStyle.lineHeight width: parent.width Component.onCompleted: Logic.handleItemCreation.apply(this) onEditingFinished: Logic.handleEditionFinished(index, text) } } model: ListModel {} } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/ListItemSelector.js000066400000000000000000000025231434616504300274140ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `ListItemSelector.qml` Logic. // ============================================================================= .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function getItemIcon (item) { var iconRole = view.iconRole if (iconRole == null || iconRole.length === 0) { return '' } return Utils.isFunction(iconRole) ? iconRole(item.flattenedModel) : item.flattenedModel[iconRole] } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/ListItemSelector.qml000066400000000000000000000021201434616504300275620ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import Common 1.0 import 'ListItemSelector.js' as Logic // ============================================================================= ScrollableListViewField { property alias currentIndex: view.currentIndex property alias iconRole: view.iconRole property alias model: view.model property alias textRole: view.textRole property alias contentHeight: view.contentHeight signal activated (int index) radius: 0 ScrollableListView { id: view // ------------------------------------------------------------------------- property string textRole property var iconRole onContentHeightChanged: parent.implicitHeight = contentHeight // ------------------------------------------------------------------------- anchors.fill: parent currentIndex: -1 delegate: CommonItemDelegate { id: item container: view flattenedModel: view.textRole.length && (typeof modelData !== 'undefined' ? modelData : model) itemIcon: Logic.getItemIcon(item) width: view.width onClicked: {activated(index)} } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Mosaic.qml000066400000000000000000000055421434616504300255550ustar00rootroot00000000000000import QtQuick 2.12 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 import QtQml.Models 2.12 import Common 1.0 import Common.Styles 1.0 // ============================================================================= ColumnLayout{ id: mainLayout property alias delegateModel: grid.model property alias cellHeight: grid.cellHeight property alias cellWidth: grid.cellWidth function appendItem(item){ mainLayout.delegateModel.model.append(item) } function add(item){ if( !grid.isLayoutWillChanged()) appendItem(item) else bufferModels.append(item) } function remove(index){ if(mainLayout.delegateModel.model.count > index) mainLayout.delegateModel.model.remove( index, 1) } function get(index){ return mainLayout.delegateModel.model.get(index) } function tryToAdd(item){ if( !grid.isLayoutWillChanged()) { appendItem(item) return true }else return false } function clear(){ if(mainLayout.delegateModel.model.clear) { mainLayout.delegateModel.model.clear() bufferModels.clear() } } property int transitionCount : 0 property var bufferModels : ListModel{} onWidthChanged: grid.updateLayout() onHeightChanged: grid.updateLayout() GridView{ id: grid property int margin: 10 property int itemCount: model.count ? model.count :( model.length ? model.length : 0) property int columns: 1 property int rows: 1 function getBestLayout(itemCount){ var availableW = mainLayout.width - grid.margin var availableH = mainLayout.height - grid.margin var bestSize = 0 var bestC = 1, bestR = 1 for(var R = 1 ; R <= itemCount ; ++R){ for(var C = itemCount ; C >= 1 ; --C){ if( R * C >= itemCount){// This is a good layout candidate var estimatedSize = Math.min(availableW / C, availableH / R) if(estimatedSize > bestSize // Size is better || (estimatedSize == bestSize && Math.abs(bestR-bestC) > Math.abs(R - C) )){// Stickers are more homogenized bestSize = estimatedSize bestC = C bestR = R } } } } return [bestR, bestC] } function updateLayout(){ var bestLayout = getBestLayout(itemCount) if( rows != bestLayout[0]) rows = bestLayout[0] if( columns != bestLayout[1]) columns = bestLayout[1] } property int computedWidth: (mainLayout.width - grid.margin ) / columns property int computedHeight: (mainLayout.height - grid.margin ) / rows cellWidth: Math.min(computedWidth, computedHeight) cellHeight: Math.min(computedWidth, computedHeight) function isLayoutWillChanged(){ var bestLayout = getBestLayout(itemCount+1) return rows !== bestLayout[0] || columns !== bestLayout[1] } Layout.preferredWidth: cellWidth * columns Layout.preferredHeight: cellHeight * rows Layout.alignment: Qt.AlignCenter interactive: false model: DelegateModel{} onCountChanged: grid.updateLayout() } }linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/MouseArea.qml000066400000000000000000000003341434616504300262150ustar00rootroot00000000000000import QtQuick 2.7 as Quick import Common 1.0 import Common.Styles 1.0 Quick.MouseArea { cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor hoverEnabled: true } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/000077500000000000000000000000001434616504300257145ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/Form.qml000066400000000000000000000031041434616504300273300ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common.Styles 1.0 import Common 1.0 // ============================================================================= Column { property alias title: title.text property int orientation: Qt.Horizontal property bool addButton : false signal addButtonClicked; property bool removeButton : false signal removeButtonClicked; // --------------------------------------------------------------------------- spacing: FormStyle.spacing // --------------------------------------------------------------------------- ColumnLayout { spacing: FormStyle.header.spacing visible: parent.title.length > 0 width: parent.width Row{ spacing:10 Text { id: title anchors.verticalCenter: parent.verticalCenter color: FormStyle.header.title.color font { bold: true pointSize: FormStyle.header.title.pointSize } } ActionButton { visible:addButton anchors.verticalCenter: parent.verticalCenter isCustom: true backgroundRadius: 90 colorSet: FormStyle.header.add scale:0.8 onClicked:addButtonClicked() } ActionButton { visible:removeButton anchors.verticalCenter: parent.verticalCenter isCustom: true backgroundRadius: 90 colorSet: FormStyle.header.deleteAction scale:0.8 onClicked:removeButtonClicked() } } Rectangle { Layout.fillWidth: true Layout.preferredHeight: FormStyle.header.separator.height color: FormStyle.header.separator.color } Item { height: FormStyle.header.bottomMargin } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/FormEmptyLine.qml000066400000000000000000000003071434616504300311610ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 // ============================================================================= Item { height: FormHGroupStyle.legend.height width: parent.width } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/FormGroup.qml000066400000000000000000000022171434616504300303510ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 // ============================================================================= Loader { id: loader // --------------------------------------------------------------------------- property string label property var labelFont: item ? item.labelFont : Application.font property bool fitLabel: false readonly property int orientation: parent.orientation default property var _content: null property int maxWidth: orientation === Qt.Horizontal ? FormHGroupStyle.content.maxWidth : FormVGroupStyle.content.maxWidth // --------------------------------------------------------------------------- sourceComponent: orientation === Qt.Horizontal ? hGroup : vGroup width: parent.maxItemWidth // --------------------------------------------------------------------------- Component { id: hGroup FormHGroup { _content: loader._content label: loader.label maxWidth: loader.maxWidth fitLabel: loader.fitLabel } } Component { id: vGroup FormVGroup { _content: loader._content label: loader.label maxWidth: loader.maxWidth } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/FormHGroup.qml000066400000000000000000000036541434616504300304670ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 // ============================================================================= RowLayout { property alias label: label.text property alias labelFont: label.font default property var _content: null property int maxWidth: FormHGroupStyle.content.maxWidth property bool fitLabel : false // --------------------------------------------------------------------------- spacing: FormHGroupStyle.spacing // --------------------------------------------------------------------------- Text { id: label computeFitWidth: parent.fitLabel Layout.preferredHeight: FormHGroupStyle.legend.height Layout.preferredWidth: fitLabel ? Math.min(label.fitWidth, FormHGroupStyle.legend.width) : FormHGroupStyle.legend.width color: FormHGroupStyle.legend.color elide: Text.ElideRight font.pointSize: FormHGroupStyle.legend.pointSize horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter TooltipArea { delay: 0 text: parent.text visible: parent.truncated } } // --------------------------------------------------------------------------- Item { readonly property int currentHeight: _content ? _content.height : 0 Layout.alignment: ( currentHeight < FormHGroupStyle.legend.height ? Qt.AlignVCenter : Qt.AlignTop ) | Qt.AlignLeft Layout.fillWidth: true Layout.preferredHeight: currentHeight Loader { active: !!_content anchors.fill: parent sourceComponent: Item { id: content data: [ _content ] width: parent.width Component.onCompleted: _content.width = Qt.binding(function () { var contentWidth = content.width var wishedWidth = parent.parent.parent.maxWidth return contentWidth > wishedWidth ? wishedWidth : contentWidth }) } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/FormLine.qml000066400000000000000000000013321434616504300301410ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 // ============================================================================= Row { property int childrenCount: children.length property double maxItemWidth: { var n = childrenCount var curWidth = width / n - (n - 1) * spacing var maxWidth = orientation === Qt.Horizontal ? FormHGroupStyle.legend.width + FormHGroupStyle.content.maxWidth + FormHGroupStyle.spacing : FormVGroupStyle.content.maxWidth return curWidth < maxWidth ? curWidth : maxWidth } readonly property int orientation: parent.orientation // --------------------------------------------------------------------------- spacing: FormLineStyle.spacing width: parent.width } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/FormTable.qml000066400000000000000000000037021434616504300303040ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 // ============================================================================= Column { id: formTable width: parent.width property alias titles: header.model property bool disableLineTitle: false property int legendLineWidth: FormTableStyle.entry.width property var maxWidthStyle : FormTableStyle.entry.maxWidth readonly property double maxItemWidth: computeMaxItemWidth() // --------------------------------------------------------------------------- function updateMaxItemWidth(){ maxItemWidth = computeMaxItemWidth(); } function computeMaxItemWidth(){ var n = 1; // if( titles) // n = titles.length // else{ for(var line = 0 ; line < formTable.visibleChildren.length ; ++line){ var column = formTable.visibleChildren[line].visibleChildren.length; n = Math.max(n, column-1); } // } var curWidth = (width - (disableLineTitle?0:legendLineWidth) ) /n - FormTableLineStyle.spacing var maxWidth = maxWidthStyle return curWidth < maxWidth ? curWidth : maxWidth } spacing: FormTableStyle.spacing // --------------------------------------------------------------------------- Row { spacing: FormTableLineStyle.spacing width: parent.width // No title for the titles column. Item { height: header.model.count > 0 ? FormTableStyle.entry.height : 0 width: formTable.legendLineWidth visible: !formTable.disableLineTitle } Repeater { id: header Text { id: text color: FormTableStyle.entry.text.color elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter text: modelData height: FormTableStyle.entry.height width: formTable.maxItemWidth font { bold: true pointSize: FormTableStyle.entry.text.pointSize } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/FormTableEntry.qml000066400000000000000000000011021434616504300313160ustar00rootroot00000000000000import QtQuick 2.7 import Common.Styles 1.0 // ============================================================================= Item { default property var _content readonly property int currentHeight: _content ? _content.height : 0 // --------------------------------------------------------------------------- height: currentHeight width: parent.maxItemWidth Loader { active: !!_content anchors.fill: parent sourceComponent: Item { id: item data: [ _content ] Component.onCompleted: _content.anchors.centerIn = item } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/FormTableLine.qml000066400000000000000000000016211434616504300311120ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common.Styles 1.0 // ============================================================================= Row { id: formTableLine property alias title: title.text readonly property double maxItemWidth: parent.maxItemWidth // --------------------------------------------------------------------------- spacing: FormLineStyle.spacing width: parent.width // --------------------------------------------------------------------------- Text { id: title color: FormTableStyle.entry.text.color elide: Text.ElideRight horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter height: FormTableStyle.entry.height width: formTableLine.parent.legendLineWidth font { bold: false pointSize: FormTableStyle.entry.text.pointSize } visible: !formTableLine.parent.disableLineTitle } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Placements/FormVGroup.qml000066400000000000000000000027611434616504300305030ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 // ============================================================================= ColumnLayout { property alias label: label.text property alias labelFont: label.font default property var _content: null property int maxWidth: FormVGroupStyle.content.maxWidth // --------------------------------------------------------------------------- spacing: FormVGroupStyle.spacing width: parent.maxItemWidth // --------------------------------------------------------------------------- Text { id: label Layout.fillWidth: true color: FormVGroupStyle.legend.color elide: Text.ElideRight font.pointSize: FormVGroupStyle.legend.pointSize verticalAlignment: Text.AlignVCenter TooltipArea { delay: 0 text: parent.text visible: parent.truncated } } // --------------------------------------------------------------------------- Item { readonly property int currentHeight: _content ? _content.height : 0 Layout.fillWidth: true Layout.preferredHeight: currentHeight Loader { active: !!_content anchors.fill: parent sourceComponent: Item { id: content data: [ _content ] width: parent.width Component.onCompleted: _content.width = Qt.binding(function () { var contentWidth = content.width var wishedWidth = parent.parent.parent.maxWidth return contentWidth > wishedWidth ? wishedWidth : contentWidth }) } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/RadioButton.qml000066400000000000000000000030621434616504300265670ustar00rootroot00000000000000import QtQuick 2.12 import QtQuick.Layouts 1.3 import QtQml.Models 2.12 import QtGraphicalEffects 1.12 import QtQuick.Controls 2.12 as Control import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 // ============================================================================= Control.RadioButton{ id: radio font.weight: checked ? RadioButtonStyle.selectedWeight : RadioButtonStyle.weight font.pointSize: RadioButtonStyle.pointSize spacing: 10 FontMetrics{id: fontMetrics} MouseArea{ anchors.fill:parent hoverEnabled: true acceptedButtons: Qt.NoButton cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor } indicator: Rectangle { height: fontMetrics.height - 5 width: height x: parent.leftPadding y: parent.height / 2 - height / 2 radius: width/2 border.color: RadioButtonStyle.color property bool checked: parent.checked Rectangle { width: parent.width - 8 height: width x: 4 y: 4 radius: width/2 color: RadioButtonStyle.color visible: parent.checked } } contentItem: Text{ text: parent.text font: parent.font width: parent.width - (parent.indicator.width + parent.spacing) height: implicitHeight y:0 // Override unwanted auto changes onYChanged: y = 0 onHeightChanged: height=implicitHeight //--------------------------------------- color: RadioButtonStyle.color verticalAlignment: Text.AlignVCenter leftPadding: parent.indicator.width + parent.spacing wrapMode: Text.WordWrap elide: Text.ElideRight } }linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/SearchBox.qml000066400000000000000000000102531434616504300262130ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import Common 1.0 import Utils 1.0 import Common.Styles 1.0 // ============================================================================= Item { id: searchBox // --------------------------------------------------------------------------- readonly property alias filter: searchField.text readonly property alias isOpen: searchBox._isOpen readonly property var view: _content[0] property alias entryHeight: menu.entryHeight property alias maxMenuHeight: menu.maxMenuHeight property alias placeholderText: searchField.placeholderText property alias tooltipText : tooltip.text property alias text : searchField.text property alias isMandatory: searchField.isMandatory default property alias _content: menu._content property bool _isOpen: false // --------------------------------------------------------------------------- signal menuClosed signal menuOpened signal menuRequested signal enterPressed // --------------------------------------------------------------------------- function closeMenu () { if (!_isOpen) { return } searchBox.state = '' _isOpen = false } function openMenu () { if (_isOpen) { return } searchBox.state = 'opened' _isOpen = true } function _filter (text) { var model = searchBox.view.model Utils.assert(model.setFilter != null, '`model.setFilter` must be defined.') model.setFilter(text) if(!searchField.activeFocus) searchField.focus = true if(!_isOpen){ searchBox.menuRequested() searchBox.openMenu() } } // --------------------------------------------------------------------------- implicitHeight: searchField.height Item { implicitHeight: searchField.height + menu.height width: parent.width TextField { id: searchField icon: text == '' ? SearchBoxStyle.searchIcon : SearchBoxStyle.cancelIcon overwriteColor: SearchBoxStyle.iconColor iconSize: height readOnly: !searchBox.enabled width: parent.width function enterButtonPressed(){ searchBox.enterPressed() searchBox.closeMenu() } Keys.onEscapePressed: searchBox.closeMenu() Keys.onEnterPressed: { enterButtonPressed() } Keys.onReturnPressed: { enterButtonPressed() } onActiveFocusChanged: { if (activeFocus && !_isOpen) { _filter(text) searchBox.menuRequested() searchBox.openMenu() } } onTextChanged: _filter(text) } // ------------------------------------------------------------------------- DropDownDynamicMenu { id: menu relativeTo: searchField relativeY: searchField.height // If the menu is focused, the main window loses the active status. // So It's necessary to map the keys events. Keys.forwardTo: searchField onClosed: searchBox.closeMenu() } Binding { target: searchBox.view property: 'width' value: searchField.width } Binding { target: searchBox.view property: 'headerPositioning' value: searchBox.view.header ? ListView.OverlayHeader : ListView.InlineFooter } } MouseArea {// Just take hover events and set popup to do its automatic close if mouse is not inside field/popup area anchors.fill: parent onContainsMouseChanged: menu.popup.closePolicy=(containsMouse?Controls.Popup.NoAutoClose:Controls.Popup.CloseOnEscape | Controls.Popup.CloseOnPressOutside) hoverEnabled:true preventStealing: true propagateComposedEvents:true onPressed: mouse.accepted=false onReleased: mouse.accepted=false onClicked: mouse.accepted=false onDoubleClicked: mouse.accepted=false onPressAndHold: mouse.accepted=false TooltipArea { id:tooltip visible: !isOpen && text !== '' isClickable:false } } // --------------------------------------------------------------------------- states: State { name: 'opened' } transitions: [ Transition { from: '' to: 'opened' ScriptAction { script: { menu.open() searchBox.menuOpened() } } }, Transition { from: 'opened' to: '' ScriptAction { script: { menu.close() searchField.focus = false searchField.text = '' searchBox.menuClosed() } } } ] } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Slider.qml000066400000000000000000000030511434616504300255550ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import Common.Styles 1.0 // ============================================================================= Controls.Slider { id: slider property alias backgroundImplicitHeight: backgroundItem.implicitHeight property alias backgroundImplicitWidth: backgroundItem.implicitWidth property alias handleImplicitHeight: handleItem.implicitHeight property alias handleImplicitWidth: handleItem.implicitWidth background: Rectangle { id: backgroundItem color: SliderStyle.background.color x: slider.leftPadding y: slider.topPadding + slider.availableHeight / 2 - height / 2 implicitHeight: SliderStyle.background.height implicitWidth: SliderStyle.background.width height: implicitHeight width: slider.availableWidth radius: SliderStyle.background.radius Rectangle { color: SliderStyle.background.content.color height: parent.height width: slider.visualPosition * parent.width radius: SliderStyle.background.content.radius } } handle: Rectangle { id: handleItem border.color: slider.pressed ? SliderStyle.handle.border.color.pressed : SliderStyle.handle.border.color.normal color: slider.pressed ? SliderStyle.handle.color.pressed : SliderStyle.handle.color.normal x: slider.leftPadding + slider.visualPosition * (slider.availableWidth - width) y: slider.topPadding + slider.availableHeight / 2 - height / 2 implicitWidth: SliderStyle.handle.width implicitHeight: SliderStyle.handle.height radius: SliderStyle.handle.radius } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/StackView.qml000066400000000000000000000027641434616504300262450ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.12 as Control import Common 1.0 import Common.Styles 1.0 import Utils 1.0 Control.StackView { id: stack clip:true property string viewsPath signal exit() readonly property alias nViews: stack.depth function pushView (view, properties) { stack.push(Utils.isString(view) ? viewsPath + view + '.qml' : view,properties) } function getView (index) { return stack.get(index) } function popView () { if( nViews <= 1 ) { stack.pop() stack.exit() }else stack.pop() } // ------------------------------------------------------------------------- popEnter: Transition { YAnimator { duration: StackViewStyle.stackAnimation.duration easing.type: Easing.OutBack from: stack.height + StackViewStyle.bottomMargin to: 0 } } popExit: Transition { XAnimator { duration: StackViewStyle.stackAnimation.duration easing.type: Easing.OutBack from: 0 to: stack.width + StackViewStyle.rightMargin } } pushEnter: Transition { XAnimator { duration: StackViewStyle.stackAnimation.duration easing.type: Easing.OutBack from: stack.width + StackViewStyle.rightMargin to: 0 } } pushExit: Transition { YAnimator { duration: StackViewStyle.stackAnimation.duration easing.type: Easing.OutBack from: 0 to: stack.height + StackViewStyle.bottomMargin } } }linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/StaticListForm.qml000066400000000000000000000030151434616504300272420ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common.Styles 1.0 // ============================================================================= // modelData : {placeholder, text} RowLayout { id: form property alias title: text.text property bool readOnly: false property var fields signal changed (int index, string value) // --------------------------------------------------------------------------- function _handleEditionFinished (index, text) { if (text !== fields[index].text) { form.changed(index, text) } } // --------------------------------------------------------------------------- spacing: 0 Text { id: text Layout.alignment: Qt.AlignTop Layout.leftMargin: ListFormStyle.titleArea.iconSize + ListFormStyle.titleArea.spacing Layout.preferredHeight: ListFormStyle.lineHeight Layout.preferredWidth: ListFormStyle.titleArea.text.width color: ListFormStyle.titleArea.text.color elide: Text.ElideRight font { bold: true pointSize: ListFormStyle.titleArea.text.pointSize } verticalAlignment: Text.AlignVCenter } ColumnLayout { Layout.fillWidth: true spacing: 0 Repeater { model: form.fields TransparentTextInput { Layout.fillWidth: true Layout.preferredHeight: ListFormStyle.lineHeight placeholder: modelData.placeholder || '' readOnly: form.readOnly text: modelData.text || '' onEditingFinished: _handleEditionFinished(index, text) } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Switch.qml000066400000000000000000000054221434616504300256000ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import Common 1.0 import Common.Styles 1.0 // ============================================================================= Controls.Switch { id: control // --------------------------------------------------------------------------- property bool enabled: true property QtObject indicatorStyle : SwitchStyle.normal onIndicatorStyleChanged: if( !indicatorStyle) indicatorStyle = SwitchStyle.normal // --------------------------------------------------------------------------- signal clicked // --------------------------------------------------------------------------- checked: false indicator: Rectangle { implicitHeight: indicatorStyle.indicator.height implicitWidth: indicatorStyle.indicator.width border.color: control.enabled ? ( control.checked ? indicatorStyle.indicator.border.color.checked : indicatorStyle.indicator.border.color.normal ) : indicatorStyle.indicator.border.color.disabled color: control.enabled ? ( control.checked ? indicatorStyle.indicator.color.checked : indicatorStyle.indicator.color.normal ) : indicatorStyle.indicator.color.disabled radius: indicatorStyle.indicator.radius x: control.leftPadding y: parent.height / 2 - height / 2 Rectangle { id: sphere height: indicatorStyle.sphere.size width: indicatorStyle.sphere.size anchors.verticalCenter: parent.verticalCenter border.color: control.enabled ? ( control.checked ? ( control.down ? indicatorStyle.sphere.border.color.pressed : indicatorStyle.sphere.border.color.checked ) : indicatorStyle.sphere.border.color.normal ) : indicatorStyle.sphere.border.color.disabled color: control.enabled ? ( control.down ? indicatorStyle.sphere.color.pressed : indicatorStyle.sphere.color.normal ) : indicatorStyle.sphere.color.disabled radius: width / 2 // ----------------------------------------------------------------------- states: State { when: control.checked PropertyChanges { target: sphere x: parent.width - width } } transitions: Transition { NumberAnimation { properties: 'x' duration: indicatorStyle.animation.duration easing.type: Easing.InOutQuad } } } } // --------------------------------------------------------------------------- MouseArea { anchors.fill: parent onClicked: control.enabled && control.clicked() onPressed: control.enabled && control.forceActiveFocus() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Tab/000077500000000000000000000000001434616504300243275ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Tab/TabBar.qml000066400000000000000000000002141434616504300261720ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 // ============================================================================= TabBar {} linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Tab/TabButton.qml000066400000000000000000000043601434616504300267470ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 // ============================================================================= Controls.TabButton { id: button // --------------------------------------------------------------------------- property int iconSize: TabButtonStyle.icon.size property string iconName readonly property bool _isSelected: parent.parent.currentItem === button // --------------------------------------------------------------------------- function _getBackgroundColor () { if (_isSelected) { return TabButtonStyle.backgroundColor.selected } return button.enabled ? ( button.down ? TabButtonStyle.backgroundColor.pressed : ( button.hovered ? TabButtonStyle.backgroundColor.hovered : TabButtonStyle.backgroundColor.normal ) ) : TabButtonStyle.backgroundColor.disabled } function _getTextColor () { if (_isSelected) { return TabButtonStyle.text.color.selected } return button.enabled ? ( button.down ? TabButtonStyle.text.color.pressed : ( button.hovered ? TabButtonStyle.text.color.hovered : TabButtonStyle.text.color.normal ) ) : TabButtonStyle.text.color.disabled } // --------------------------------------------------------------------------- background: Rectangle { color: _getBackgroundColor() implicitHeight: TabButtonStyle.text.height } contentItem: RowLayout { height: button.height width: button.width spacing: TabButtonStyle.spacing Icon { id: icon Layout.fillHeight: true Layout.leftMargin: TabButtonStyle.text.leftPadding overwriteColor: textItem.color icon: button.iconName iconSize: button.iconSize } Text { id: textItem Layout.fillWidth: true Layout.fillHeight: true color: _getTextColor() font { bold: true pointSize: TabButtonStyle.text.pointSize } elide: Text.ElideRight height: parent.height horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter rightPadding: TabButtonStyle.text.rightPadding text: button.text } } hoverEnabled: true } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/Tab/TabContainer.qml000066400000000000000000000024331434616504300274150ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 import Utils 1.0 // ============================================================================= Rectangle { default property alias _content: content.data color: TabContainerStyle.color ColumnLayout { anchors.fill: parent spacing: 0 Flickable { ScrollBar.vertical: ForceScrollBar { id: scrollBar } Layout.fillHeight: true Layout.fillWidth: true boundsBehavior: Flickable.StopAtBounds clip: true contentHeight: Utils.ensureArray(_content).reduce(function (acc, item) { return item.height }, 0, []) + TabContainerStyle.topMargin + TabContainerStyle.bottomMargin contentWidth: width Item { id: content anchors { left: parent.left leftMargin: TabContainerStyle.leftMargin right: parent.right rightMargin: TabContainerStyle.rightMargin top: parent.top topMargin: TabContainerStyle.topMargin } } } Rectangle { Layout.fillWidth: true Layout.preferredHeight: TabContainerStyle.separator.height color: TabContainerStyle.separator.color visible: scrollBar.visible } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Form/TransparentTextInput.qml000066400000000000000000000054561434616504300305340ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Common.Styles 1.0 // ============================================================================= // A editable text that has a background color on focus. // ============================================================================= Item { property alias color: textInput.color property alias font: textInput.font property alias inputMethodHints: textInput.inputMethodHints property alias placeholder: placeholder.text property alias readOnly: textInput.readOnly property alias text: textInput.text property bool forceFocus: false property bool isInvalid: false property int padding: TransparentTextInputStyle.padding // --------------------------------------------------------------------------- signal editingFinished // --------------------------------------------------------------------------- onActiveFocusChanged: { if (activeFocus) { textInput.focus = true } } Rectangle { id: background color: (textInput.activeFocus || parent.forceFocus) && !textInput.readOnly ? TransparentTextInputStyle.backgroundColor : // No Style constant, see component name. // It's a `transparent` TextInput. 'transparent' height: parent.height width: { var width = textInput.contentWidth + parent.padding * 2 return width < parent.width ? width : parent.width } InvertedMouseArea { anchors.fill: parent enabled: textInput.activeFocus onPressed: textInput.focus = false } } Icon { id: icon anchors.left: background.right height: background.height icon: 'generic_error' iconSize: TransparentTextInputStyle.iconSize visible: parent.isInvalid } Text { id: placeholder anchors.centerIn: parent color: TransparentTextInputStyle.placeholder.color elide: Text.ElideRight font { italic: true pointSize: TransparentTextInputStyle.placeholder.pointSize } height: textInput.height width: textInput.width verticalAlignment: Text.AlignVCenter visible: textInput.text.length === 0 && (!textInput.activeFocus || textInput.readOnly) } TextInput { id: textInput anchors.centerIn: parent height: parent.height width: parent.width - parent.padding * 2 clip: true color: activeFocus && !readOnly ? TransparentTextInputStyle.text.color.focused : TransparentTextInputStyle.text.color.normal font.pointSize: TransparentTextInputStyle.text.pointSize selectByMouse: true verticalAlignment: TextInput.AlignVCenter Keys.onEscapePressed: focus = false Keys.onEnterPressed: focus = false Keys.onReturnPressed: focus = false onEditingFinished: { cursorPosition = 0 if (!parent.readOnly) { parent.editingFinished() } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Helpers/000077500000000000000000000000001434616504300243205ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Helpers/DragBox.qml000066400000000000000000000053421434616504300263650ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 // ============================================================================= Item { id: dragBox // --------------------------------------------------------------------------- property var container property var draggable property var xPosition property var yPosition // --------------------------------------------------------------------------- property var _mouseArea: null // --------------------------------------------------------------------------- signal clicked (var mouse) signal doubleClicked (var mouse) signal pressed (var mouse) signal released (var mouse) signal wheel (var wheel) // --------------------------------------------------------------------------- function _create () { draggable.x = Qt.binding(xPosition) draggable.y = Qt.binding(yPosition) var drag = draggable.Drag drag.hotSpot.x = Qt.binding(function () { return draggable.width / 2 }) drag.hotSpot.y = Qt.binding(function () { return draggable.height / 2 }) if (_mouseArea == null) { _mouseArea = builder.createObject(draggable) drag.active = Qt.binding(function () { return _mouseArea && _mouseArea.held }) drag.source = Qt.binding(function () { return _mouseArea }) } } function _delete () { if (_mouseArea != null) { _mouseArea.destroy() _mouseArea = null } } function updateBoundaries(){ var container = dragBox.container if (parent.x < 0) { parent.x = 0 } else if (parent.x + parent.width >= container.width) { parent.x = container.width - parent.width } if (parent.y < 0) { parent.y = 0 } else if (parent.y + parent.height >= container.height) { parent.y = container.height - parent.height } } // --------------------------------------------------------------------------- Component.onCompleted: enabled && _create() Component.onDestruction: _delete() onEnabledChanged: { _delete() if (enabled) { _create() } } // --------------------------------------------------------------------------- Component { id: builder MouseArea { property bool held: false anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton drag { axis: Drag.XandYAxis target: parent } onDoubleClicked: dragBox.doubleClicked(mouse) onClicked:dragBox.clicked(mouse) onPressed: { dragBox.pressed(mouse) held = true } onReleased: { dragBox.released(mouse) held = false updateBoundaries() } onWheel: dragBox.wheel(wheel) } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.qml000066400000000000000000000062241434616504300304210ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Utils 1.0 // ============================================================================= // Helper to handle button click outside an item. // ============================================================================= Item { id: item // --------------------------------------------------------------------------- property bool _mouseAlwaysOutside property var _mouseArea: null // --------------------------------------------------------------------------- // When emitted, returns a function to test if the click // is on a specific item. It takes only a item parameter. signal pressed (var pointIsInItem) // --------------------------------------------------------------------------- function _createMouseArea () { var parent = Utils.getTopParent(item, true) if (_mouseArea == null) { _mouseArea = builder.createObject() } _mouseArea.parent = parent // Must be true if a fake parent is used and if `mouseArea` // is not in the same window that `item`. _mouseAlwaysOutside = _mouseArea.parent !== Utils.getTopParent(item) } function _deleteMouseArea () { if (_mouseArea != null) { _mouseArea.destroy() _mouseArea = null } } // --------------------------------------------------------------------------- // It's necessary to use a `enabled` variable. // See: http://doc.qt.io/qt-5/qml-qtqml-component.html#completed-signal // // The creation order of components in a view is undefined, // so the mouse area must be created only when the target component // was completed. Component.onCompleted: enabled && _createMouseArea() Component.onDestruction: _deleteMouseArea() onEnabledChanged: { _deleteMouseArea() if (enabled) { _createMouseArea() } } Component { id: builder MouseArea { cursorShape: Qt.ArrowCursor property var _timeout function _checkPosition (positionEvent) { // Propagate event. positionEvent.accepted = false // Click is outside or not. if ( _mouseAlwaysOutside || !Utils.pointIsInItem(this, item, positionEvent) ) { if (_timeout != null) { // Remove existing timeout to avoid the creation of // many children. Utils.clearTimeout(_timeout) } // Use a asynchronous call that is executed // after the propagated event. // // It's useful to ensure the window's context is not // modified with the positionEvent before the `onPressed` // call. // // The timeout is destroyed with the `MouseArea` component. _timeout = Utils.setTimeout(this, 0, item.pressed.bind( this, (function (point, item) { return Utils.pointIsInItem(this, item, point) }).bind(this, { x: positionEvent.x, y: positionEvent.y }) )) } } anchors.fill: parent propagateComposedEvents: true z: Constants.zMax onPressed: _checkPosition.call(this, mouse) onWheel: _checkPosition.call(this, wheel) } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.spec.qml000066400000000000000000000054311434616504300313510ustar00rootroot00000000000000import QtQuick 2.7 import QtTest 1.1 import Utils 1.0 import Common 1.0 // ============================================================================= Rectangle { id: root color: 'violet' height: 200 width: 300 Rectangle { id: item color: 'pink' height: 80 width: 100 x: 120 y: 100 InvertedMouseArea { id: invertedMouseArea anchors.fill: parent } } SignalSpy { id: spy signalName: 'pressed' target: invertedMouseArea } TestCase { when: windowShown function init () { spy.clear() } function test_randomClickInsideMouseArea () { Utils.times(100, function () { var x = Math.floor(Utils.genRandomNumber(item.x, item.x + width)) var y = Math.floor(Utils.genRandomNumber(item.y, item.y + height)) mouseClick(root, x, y) }) wait(100) compare(spy.count, 0, '`pressed` signal was emitted') } // ------------------------------------------------------------------------- function test_randomClickOutsideMouseArea () { Utils.times(50, function () { var x = Math.floor(Utils.genRandomNumberBetweenIntervals([ [ 0, item.x ], [ item.x + item.width, root.width ] ])) var y = Math.floor(Utils.genRandomNumberBetweenIntervals([ [ 0, item.y ], [ item.y + item.height, root.height ] ])) mouseClick(root, x, y) spy.wait(100) }) Utils.times(50, function () { var x = Math.floor(Utils.genRandomNumber(item.x, item.x + item.width)) var y = Math.floor(Utils.genRandomNumberBetweenIntervals([ [ 0, item.y ], [ item.y + item.height, root.height ] ])) mouseClick(root, x, y) spy.wait(100) }) } // ------------------------------------------------------------------------- function test_clickInsideMouseArea_data () { return [ { x: item.x, y: item.y }, { x: item.x + item.width - 1, y: item.y }, { x: item.x, y: item.y + item.height - 1}, { x: item.x + item.width - 1, y: item.y + item.height - 1 }, { } // (x, y) = item center. ] } function test_clickInsideMouseArea (data) { mouseClick(root, data.x, data.y) wait(100) compare(spy.count, 0, '`pressed` signal was emitted') } // ------------------------------------------------------------------------- function test_clickOutsideMouseArea_data () { return [ { x: item.x - 1, y: item.y - 1}, { x: item.x + item.width, y: item.y }, { x: item.x, y: item.y + item.height }, { x: item.x + item.width, y: item.y + item.height } ] } function test_clickOutsideMouseArea (data) { mouseClick(root, data.x, data.y) spy.wait(100) } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Image/000077500000000000000000000000001434616504300237405ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Image/Icon.qml000066400000000000000000000036711434616504300253520ustar00rootroot00000000000000import QtQuick 2.7 import QtGraphicalEffects 1.12 import Common 1.0 import Linphone 1.0 import Utils 1.0 import UtilsCpp 1.0 // ============================================================================= // An icon image properly resized. // ============================================================================= Item { id: mainItem property var iconSize // Required. property int iconHeight: 0 // Or this property int iconWidth: 0 // <-- too property string icon property color overwriteColor property alias horizontalAlignment: image.horizontalAlignment property alias verticalAlignment: image.verticalAlignment property alias fillMode: image.fillMode // Use this slot because of testing overwriteColor in layer doesn't seem to work onOverwriteColorChanged: if(overwriteColor) image.colorOverwriteEnabled = true else image.colorOverwriteEnabled = false height: iconHeight > 0 ? iconHeight : iconSize width: iconWidth > 0 ? iconWidth : iconSize Image { id:image anchors.fill: parent property bool colorOverwriteEnabled : false mipmap: SettingsModel.mipmapEnabled cache: Images.areReadOnlyImages asynchronous: true smooth: true antialiasing: false // Better quality is only available from Qt5.15 fillMode: !qtIsNewer_5_15_0 ? Image.PreserveAspectFit : Image.Stretch // Stretch is default from Qt's doc // Keep aspect ratio is done by ImagePovider that use directly SVG scalings (=no loss quality). source: width != 0 && height != 0 ? Utils.resolveImageUri(icon) : '' // Do not load image with unknown requested size sourceSize.width: qtIsNewer_5_15_0 ? fillMode == Image.TileHorizontally ? height : width : 0 sourceSize.height: qtIsNewer_5_15_0 ? fillMode == Image.TileVertically ? width : height : 0 layer { enabled: image.colorOverwriteEnabled effect: ColorOverlay { color: mainItem.overwriteColor } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Image/RoundedImage.qml000066400000000000000000000015531434616504300270220ustar00rootroot00000000000000import QtQuick 2.7 import QtGraphicalEffects 1.0 import Linphone 1.0 // ============================================================================= Item { id: item property alias source: image.source property color backgroundColor: '#00000000' property color foregroundColor: '#00000000' readonly property alias status: image.status Rectangle { id: backgroundArea anchors.fill: parent color: item.backgroundColor radius: width/2 } Image { id: image mipmap: SettingsModel.mipmapEnabled anchors.fill: parent fillMode: Image.PreserveAspectCrop sourceSize.width: parent.width sourceSize.height: parent.height layer.enabled: true layer.effect: OpacityMask { maskSource: backgroundArea } } Rectangle { id: foregroundArea anchors.fill: parent visible: color != 'transparent' color: item.foregroundColor radius: width/2 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Indicators/000077500000000000000000000000001434616504300250155ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Indicators/MediaProgressBar.qml000066400000000000000000000056421434616504300307300ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import Common 1.0 import Linphone 1.0 import Utils 1.0 import Units 1.0 import Common.Styles 1.0 // ============================================================================= ProgressBar { id: progressBar property bool stopAtEnd: true property bool resetAtEnd: false property int progressDuration // Max duration property int progressPosition // Position of progress bar in [0 ; progressDuration] property alias colorSet: progression.colorSet property alias backgroundColor: backgroundArea.color property alias durationTextColor: durationText.color property int waveLeftMargin: 0 function start(){ progressBar.value = 0 animationTest.start() } function resume(){ if(progressBar.value >= 100) progressBar.value = 0 animationTest.start() } function stop(){ animationTest.stop() } signal endReached() signal refreshPositionRequested() signal seekRequested(int ms) Timer{ id: animationTest repeat: true onTriggered: progressBar.refreshPositionRequested() interval: 5 } to: 101 value: 0 onValueChanged:{ if(value > 100){ if( progressBar.stopAtEnd) stop() if(progressBar.resetAtEnd) { progressBar.value = 0 progressPosition = 0 }else{ progressBar.value = 100// Stay at 100 progressPosition = progressDuration } progressBar.endReached() }else progression.percentageDisplayed = value } anchors.topMargin: 2 anchors.bottomMargin: 2 background: Rectangle { id: backgroundArea color: MediaProgressBarStyle.backgroundColor radius: 5 clip: false } contentItem: Rectangle{ anchors.fill: parent radius: 5 color: 'transparent' clip: false RowLayout{ anchors.fill: parent spacing: 0 ActionButton{ id: progression Layout.fillWidth: true Layout.fillHeight: true Layout.leftMargin: progressBar.waveLeftMargin backgroundRadius: 5 fillMode: Image.TileHorizontally verticalAlignment: Image.AlignLeft horizontalAlignment: Image.AlignLeft isCustom: true colorSet: MediaProgressBarStyle.progressionWave percentageDisplayed: 0 onClicked: progressBar.seekRequested(x * progressBar.progressDuration/width) } Text{ id: durationText Layout.fillHeight: true Layout.preferredWidth: implicitWidth Layout.leftMargin: 15 Layout.rightMargin: 6 horizontalAlignment: Qt.AlignRight verticalAlignment: Qt.AlignVCenter text: progressBar.progressPosition > 0 ? Utils.formatElapsedTime( progressBar.progressPosition / 1000 ) :( progressBar.progressPosition == 0 ? Utils.formatElapsedTime( progressBar.progressDuration / 1000) : '-') property font customFont : SettingsModel.textMessageFont font.family: customFont.family font.pointSize: Units.dp * (customFont.pointSize + 1) } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Indicators/RoundProgressBar.qml000066400000000000000000000036501434616504300307750ustar00rootroot00000000000000import QtQuick 2.12 import QtQuick.Controls 2.2 import QtQuick.Shapes 1.12 import Units 1.0 import Common.Styles 1.0 ProgressBar{ id: mainItem property string text: value + '%' implicitHeight: 35 implicitWidth: 35 to: 100 value: 0 background: Rectangle { color: RoundProgressBarStyle.backgroundColor radius: width } Timer{ id: animationTest repeat: true onTriggered: value = (value + 1) % to interval: 5 } contentItem: Item{ Shape { id: shape anchors.fill: parent anchors.margins: RoundProgressBarStyle.borderWidth property real progressionRadius : Math.min(shape.width / 2, shape.height / 2) - RoundProgressBarStyle.progressionWidth / 2 layer.enabled: true layer.samples: 8 layer.smooth: true vendorExtensionsEnabled: false ShapePath { id: pathDial strokeColor: RoundProgressBarStyle.progressRemainColor fillColor: 'transparent' strokeWidth: RoundProgressBarStyle.progressionWidth capStyle: Qt.RoundCap PathAngleArc { radiusX: shape.progressionRadius radiusY: shape.progressionRadius centerX: shape.width / 2 centerY: shape.height / 2 startAngle: -90 // top start sweepAngle: 360 } } ShapePath { id: pathProgress strokeColor: RoundProgressBarStyle.progressColor fillColor: 'transparent' strokeWidth: RoundProgressBarStyle.progressionWidth capStyle: Qt.RoundCap PathAngleArc { radiusX: shape.progressionRadius radiusY: shape.progressionRadius centerX: shape.width / 2 centerY: shape.height / 2 startAngle: -90 // top start sweepAngle: (360/ mainItem.to * mainItem.value) } } } Text{ anchors.centerIn: parent text: mainItem.text color: RoundProgressBarStyle.progressRemainColor font.pointSize: RoundProgressBarStyle.pointSize font.bold: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Indicators/VuMeter.qml000066400000000000000000000027671434616504300271330ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import Common.Styles 1.0 // ============================================================================= Column { id: vuMeter property bool enabled: true property double value: 0 height: VuMeterStyle.height width: VuMeterStyle.width // --------------------------------------------------------------------------- ProgressBar { id: high height: parent.height * 0.25 width: parent.width from: 0.75 to: 1.0 value: parent.value background: Rectangle { color: vuMeter.enabled ? VuMeterStyle.high.background.color.enabled : VuMeterStyle.high.background.color.disabled } contentItem: Item { Rectangle { anchors.bottom: parent.bottom color: VuMeterStyle.high.contentItem.color height: high.visualPosition * parent.height width: parent.width visible: vuMeter.enabled } } } ProgressBar { id: low height: parent.height * 0.75 width: parent.width from: 0 to: 0.75 value: parent.value background: Rectangle { color: vuMeter.enabled ? VuMeterStyle.low.background.color.enabled : VuMeterStyle.low.background.color.disabled } contentItem: Item { Rectangle { anchors.bottom: parent.bottom color: VuMeterStyle.low.contentItem.color height: low.visualPosition * parent.height width: parent.width visible: vuMeter.enabled } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Menus/000077500000000000000000000000001434616504300240055ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Menus/ApplicationMenu.qml000066400000000000000000000017611434616504300276150ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common.Styles 1.0 // ============================================================================= // Responsive flat menu with visual indicators. // ============================================================================= Rectangle { id: menu // --------------------------------------------------------------------------- property var defaultSelectedEntry: null property int entryHeight property int entryWidth property var _selected: defaultSelectedEntry default property alias _content: content.data // --------------------------------------------------------------------------- function resetSelectedEntry () { _selected = null } // --------------------------------------------------------------------------- color: ApplicationMenuStyle.backgroundColor implicitHeight: content.height width: entryWidth Column { id: content width: parent.width spacing: ApplicationMenuStyle.spacing } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Menus/ApplicationMenuEntry.qml000066400000000000000000000047041434616504300306370ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 // ============================================================================= Rectangle { id: entry // --------------------------------------------------------------------------- property string icon property alias name: text.text readonly property bool isSelected: parent.parent._selected === this property alias iconSize : mainIcon.iconSize property alias overwriteColor: mainIcon.overwriteColor // --------------------------------------------------------------------------- signal selected signal clicked // --------------------------------------------------------------------------- function select () { var menu = parent.parent if (menu._selected !== this) { menu._selected = this selected() } } function mouseClicked(){ var menu = parent.parent if (menu._selected !== this) { menu._selected = this selected() }else clicked() } // --------------------------------------------------------------------------- color: mouseArea.pressed ? ApplicationMenuStyle.entry.color.pressed : (isSelected ? ApplicationMenuStyle.entry.color.selected : (mouseArea.containsMouse ? ApplicationMenuStyle.entry.color.hovered : ApplicationMenuStyle.entry.color.normal ) ) height: parent.parent.entryHeight width: parent.parent.entryWidth RowLayout { anchors { left: parent.left leftMargin: ApplicationMenuStyle.entry.leftMargin right: parent.right rightMargin: ApplicationMenuStyle.entry.rightMargin verticalCenter: parent.verticalCenter } spacing: ApplicationMenuStyle.entry.spacing Icon { id:mainIcon icon: entry.icon iconSize: ApplicationMenuStyle.entry.iconSize } Text { id: text Layout.fillWidth: true color: entry.isSelected ? ApplicationMenuStyle.entry.text.color.selected : ApplicationMenuStyle.entry.text.color.normal font.pointSize: ApplicationMenuStyle.entry.text.pointSize font.weight: Font.DemiBold height: parent.height text: entry.name verticalAlignment: Text.AlignVCenter } } Rectangle { anchors { left: parent.left } height: parent.height color: entry.isSelected ? ApplicationMenuStyle.entry.indicator.color : 'transparent' width: ApplicationMenuStyle.entry.indicator.width } MouseArea { id: mouseArea anchors.fill: parent onClicked: entry.mouseClicked() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Menus/DropDownDynamicMenu.qml000066400000000000000000000037201434616504300304100ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Utils 1.0 // ============================================================================= Item { id: menu // --------------------------------------------------------------------------- property alias relativeTo: popup.relativeTo property alias relativeX: popup.relativeX property alias relativeY: popup.relativeY // Can be computed, but for performance usage, it must be given in attribute. property int entryHeight property int maxMenuHeight default property alias _content: menuContent.data property alias popup : popup // --------------------------------------------------------------------------- signal closed signal opened // --------------------------------------------------------------------------- function open () { popup.open() } function close () { popup.close() } // --------------------------------------------------------------------------- function _computeHeight () { Utils.assert(_content != null && _content.length > 0, '`_content` cannot be null and must exists.') var list = _content[0] Utils.assert(list != null, 'No list found.') var height = list.count * entryHeight if (list.headerPositioning === ListView.OverlayHeader) { // Workaround to force header layout. list.headerItem.z = Constants.zMax height += list.headerItem.height } return (maxMenuHeight !== undefined && maxMenuHeight != 0 && height > maxMenuHeight) ? maxMenuHeight : height } // --------------------------------------------------------------------------- visible: false // --------------------------------------------------------------------------- Popup { id: popup onOpened: menu.opened() onClosed: menu.closed() height: menu._computeHeight() width: menu._content[0].width Item { id: menuContent anchors.fill: parent } } Binding { property: 'height' target: menu._content[0] value: menuContent.height } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Menus/DropDownStaticMenu.qml000066400000000000000000000024101434616504300302460ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 // ============================================================================= Item { id: menu // --------------------------------------------------------------------------- property alias relativeTo: popup.relativeTo property alias relativeX: popup.relativeX property alias relativeY: popup.relativeY property alias entryHeight: content.entryHeight property alias entryWidth: content.entryWidth default property alias _content: content.data // --------------------------------------------------------------------------- signal closed signal opened // --------------------------------------------------------------------------- function open () { popup.open() } function close () { popup.close() } // --------------------------------------------------------------------------- visible: false // --------------------------------------------------------------------------- Popup { id: popup onOpened: menu.opened() onClosed: menu.closed() Column { id: content property int entryHeight property int entryWidth spacing: DropDownStaticMenuStyle.spacing width: menu.entryWidth } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Menus/DropDownStaticMenuEntry.qml000066400000000000000000000022511434616504300312730ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Common.Styles 1.0 // ============================================================================= Rectangle { id: entry property alias entryName: text.text signal clicked color: mouseArea.pressed ? DropDownStaticMenuStyle.entry.color.pressed : (mouseArea.containsMouse ? DropDownStaticMenuStyle.entry.color.hovered : DropDownStaticMenuStyle.entry.color.normal ) height: parent.entryHeight width: parent.entryWidth property int implicitWidth : text.implicitWidth + DropDownStaticMenuStyle.entry.leftMargin + DropDownStaticMenuStyle.entry.rightMargin + 5 // 5 = Elide width Text { id: text anchors { left: parent.left leftMargin: DropDownStaticMenuStyle.entry.leftMargin right: parent.right rightMargin: DropDownStaticMenuStyle.entry.rightMargin } color: DropDownStaticMenuStyle.entry.text.color elide: Text.ElideRight font.pointSize: DropDownStaticMenuStyle.entry.text.pointSize height: parent.height verticalAlignment: Text.AlignVCenter } MouseArea { id: mouseArea anchors.fill: parent onClicked: entry.clicked() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Menus/Menu.qml000066400000000000000000000011131434616504300254200ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.3 as Controls import Common 1.0 import Common.Styles 1.0 // ============================================================================= Controls.Menu { id: menu property var menuStyle : MenuStyle.normal width: menuStyle.width ? menuStyle.width : parent.width background: Rectangle { implicitWidth: menu.width color: menuStyle.color radius: menuStyle.radius border{ color:menuStyle.border.color width: menuStyle.border.width } layer { enabled: menuStyle.shadowEnabled effect: PopupShadow {} } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Menus/MenuItem.qml000066400000000000000000000056761434616504300262610ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.3 as Controls //2.3 for menu property (Qt 5.10) import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 // ============================================================================= // Using MenuItem.icon doesn't seem to work as of 07/2021 // Workaround : Implement own Icon Controls.MenuItem { id: button property alias iconMenu : iconArea.icon property alias iconSizeMenu: iconArea.iconSize property alias iconOverwriteColorMenu: iconArea.overwriteColor property alias iconLayoutDirection : rowArea.layoutDirection property var menuItemStyle : MenuItemStyle.normal onMenuItemStyleChanged: if(!menuItemStyle) menuItemStyle = MenuItemStyle.normal property alias textWeight : rowText.font.bold property int offsetTopMargin : 0 property int offsetBottomMargin : 0 height:visible?undefined:0 Component.onCompleted: menu.width = Math.max(menu.width, implicitWidth) background: Rectangle { color: button.down ? menuItemStyle.background.color.pressed : ( button.hovered ? menuItemStyle.background.color.hovered : menuItemStyle.background.color.normal ) implicitHeight: button.menuItemStyle.background.height } contentItem:RowLayout{ id:rowArea spacing:10 Item{ Layout.fillHeight: true Layout.preferredWidth: iconArea.icon?height/rowText.lineCount:0 Layout.leftMargin:(iconLayoutDirection == Qt.LeftToRight ? menuItemStyle.leftMargin : 0) Layout.rightMargin:(iconLayoutDirection == Qt.LeftToRight ? 0 : menuItemStyle.rightMargin) Icon{ id: iconArea visible: icon anchors.centerIn: parent iconSize: rowText.lineCount > 0 ? rowText.contentHeight/rowText.lineCount + 2 : 0 overwriteColor: button.enabled ? (button.down ? menuItemStyle.text.color.pressed : ( button.hovered ? menuItemStyle.text.color.hovered : menuItemStyle.text.color.normal )) : menuItemStyle.text.color.disabled } } Text { id:rowText Layout.fillWidth: true Layout.fillHeight: true Layout.leftMargin:(iconLayoutDirection == Qt.LeftToRight ? 0 : menuItemStyle.leftMargin) Layout.rightMargin:(iconLayoutDirection == Qt.LeftToRight ? menuItemStyle.rightMargin : 0) color: button.enabled ? (button.down ? menuItemStyle.text.color.pressed : ( button.hovered ? menuItemStyle.text.color.hovered : menuItemStyle.text.color.normal )) : menuItemStyle.text.color.disabled elide: Text.ElideRight font { weight: menuItemStyle.text.weight pointSize: menuItemStyle.text.pointSize } text: button.text horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignVCenter } } hoverEnabled: true MouseArea{ anchors.fill:parent propagateComposedEvents:true acceptedButtons: Qt.NoButton cursorShape: parent.hovered ? Qt.PointingHandCursor : Qt.ArrowCursor } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Misc/000077500000000000000000000000001434616504300236115ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Misc/Borders.qml000066400000000000000000000034311434616504300257250ustar00rootroot00000000000000import QtQuick 2.7 // ============================================================================= // Alternative to rectangle border which is a limited feature. // Allow the use of different borders (size, color...) for each // rectangle side. // ============================================================================= Rectangle { property var borderColor property var borderWidth property color bottomColor: 'transparent' property color leftColor: 'transparent' property color rightColor: 'transparent' property color topColor: 'transparent' property int bottomWidth: 0 property int leftWidth: 0 property int rightWidth: 0 property int topWidth: 0 default property alias _content: content.data color: 'transparent' Rectangle { id: bottomBorder anchors.bottom: parent.bottom color: borderColor != null ? borderColor : bottomColor height: borderWidth != null ? borderWidth : bottomWidth width: parent.width } Rectangle { id: leftBorder anchors.left: parent.left color: borderColor != null ? borderColor : leftColor height: parent.height width: borderWidth != null ? borderWidth : leftWidth } Rectangle { id: rightBorder anchors.right: parent.right color: borderColor != null ? borderColor : rightColor height: parent.height width: borderWidth != null ? borderWidth : rightWidth } Rectangle { id: topBorder anchors.top: parent.top color: borderColor != null ? borderColor : topColor height: borderWidth != null ? borderWidth : topWidth width: parent.width } Item { id: content anchors { fill: parent bottomMargin: bottomBorder.height leftMargin: leftBorder.width rightMargin: rightBorder.width topMargin: topBorder.height } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Misc/ForceScrollBar.qml000066400000000000000000000024321434616504300271670ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import Common.Styles 1.0 // ============================================================================= // A simple custom vertical scrollbar. // ============================================================================= ScrollBar { id: scrollBar property int contentSizeTarget property int sizeTarget onContentSizeTargetChanged: Qt.callLater( scrollBar.updatePolicy) onSizeTargetChanged: Qt.callLater( scrollBar.updatePolicy) policy: ScrollBar.AlwaysOff function updatePolicy(){ policy = contentSizeTarget > sizeTarget ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff } function delayPolicy(){ Qt.callLater( scrollBar.updatePolicy) } Component.onCompleted: updatePolicy() background: Rectangle { anchors.fill: parent color: ForceScrollBarStyle.background.color radius: ForceScrollBarStyle.background.radius } contentItem: Rectangle { color: scrollBar.pressed ? ForceScrollBarStyle.color.pressed : (scrollBar.hovered ? ForceScrollBarStyle.color.hovered : ForceScrollBarStyle.color.normal ) implicitHeight: ForceScrollBarStyle.contentItem.implicitHeight implicitWidth: ForceScrollBarStyle.contentItem.implicitWidth radius: ForceScrollBarStyle.contentItem.radius } hoverEnabled: true } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Misc/MessageBanner.qml000066400000000000000000000033411434616504300270370ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 import Utils 1.0 Rectangle{ id: mainItem property int fitHeight: visible && opacity > 0 ? 32 : 0 property string noticeBannerText property bool showIcon: true property alias pointSize: textItem.font.pointSize property alias textColor: textItem.color property alias icon: iconItem.icon property alias iconColor: iconItem.overwriteColor // = textColor by default onNoticeBannerTextChanged: if(noticeBannerText!='') mainItem.state = "showed" color: MessageBannerStyle.color radius: 10 state: "hidden" Timer{ id: hideNoticeBanner interval: 4000 repeat: false onTriggered: mainItem.state = "hidden" } RowLayout{ anchors.centerIn: parent spacing: 5 Icon{ id: iconItem icon: mainItem.showIcon ? MessageBannerStyle.copyTextIcon : '' overwriteColor: textItem.color iconSize: 20 visible: mainItem.showIcon } Text{ id: textItem Layout.fillHeight: true Layout.fillWidth: true text: mainItem.noticeBannerText font { pointSize: MessageBannerStyle.pointSize } color: MessageBannerStyle.textColor } } states: [ State { name: "hidden" PropertyChanges { target: mainItem; opacity: 0 } }, State { name: "showed" PropertyChanges { target: mainItem; opacity: 1 } } ] transitions: [ Transition { from: "*"; to: "showed" SequentialAnimation{ NumberAnimation{ properties: "opacity"; easing.type: Easing.OutBounce; duration: 500 } ScriptAction{ script: hideNoticeBanner.start()} } }, Transition { SequentialAnimation{ NumberAnimation{ properties: "opacity"; duration: 1000 } ScriptAction{ script: mainItem.noticeBannerText = '' } } } ] }// mainItemlinphone-desktop-5.0.2/linphone-app/ui/modules/Common/Misc/Paned.qml000066400000000000000000000177141434616504300253650ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Common.Styles 1.0 import Utils 1.0 // ============================================================================= // // Paned is one container divided in two areas. // The division between the areas can be modified with a handle. // // Each area can have a fixed or dynamic minimum width. // See `minimumLeftLimit` and `minimumRightLimit` attributes. // // Note: Paned supports too `maximumLeftLimit` and // `maximumRightLimit`. // // To create a dynamic minimum width, it's necessary to give // a percentage to `minmimumLeftLimit` or `minimumRightLimit` like: // `minimumLeftLimit: '66%'`. // The percentage is relative to the container width. // // ============================================================================= Item { id: container // --------------------------------------------------------------------------- property alias childA: contentA.data property alias childAItem: contentA property alias childB: contentB.data property bool defaultClosed: false property bool resizeAInPriority: false property int closingEdge: Qt.LeftEdge // `LeftEdge` or `RightEdge`. property int defaultChildAWidth property bool hideSplitter: false // User limits: string or int values. // By default: no limits. property var maximumLeftLimit property var maximumRightLimit property int minimumLeftLimit: 0 property int minimumRightLimit: 0 property bool _isClosed // Internal limits. property var _maximumLeftLimit property var _maximumRightLimit property var _minimumLeftLimit property var _minimumRightLimit // --------------------------------------------------------------------------- // Public functions. // --------------------------------------------------------------------------- function isClosed () { return _isClosed } function open () { if (_isClosed) { openingTransition.running = true } } function close () { if (!_isClosed) { _close() } } // --------------------------------------------------------------------------- // Private functions. // --------------------------------------------------------------------------- function _getLimitValue (limit) { if (limit == null) { return } return limit.isDynamic ? width * limit.value : limit.value } function _parseLimit (limit) { if (limit == null) { return } if (Utils.isString(limit)) { var arr = limit.split('%') if (arr[1] === '') { return { isDynamic: true, value: +arr[0] / 100 } } } return { value: limit } } function _applyLimits () { var maximumLeftLimit = _getLimitValue(_maximumLeftLimit) var maximumRightLimit = _getLimitValue(_maximumRightLimit) var minimumLeftLimit = _getLimitValue(_minimumLeftLimit) var minimumRightLimit = _getLimitValue(_minimumRightLimit) var theoreticalBWidth = container.width - contentA.width - handle.width // If closed, set correctly the handle position to left or right. if (_isClosed) { contentA.width = (closingEdge !== Qt.LeftEdge) ? container.width - handle.width : 0 } // width(A) < minimum width(A). else if (contentA.width < minimumLeftLimit) { contentA.width = minimumLeftLimit } // width(A) > maximum width(A). else if (maximumLeftLimit != null && contentA.width > maximumLeftLimit) { contentA.width = maximumLeftLimit } // width(B) < minimum width(B). else if (theoreticalBWidth < minimumRightLimit) { contentA.width = container.width - handle.width - minimumRightLimit } // width(B) > maximum width(B). else if (maximumRightLimit != null && theoreticalBWidth > maximumRightLimit) { contentA.width = container.width - handle.width - maximumRightLimit } else if (resizeAInPriority) { contentA.width = container.width - handle.width - contentB.width } } // --------------------------------------------------------------------------- function _applyLimitsOnUserMove (offset) { var minimumRightLimit = _getLimitValue(_minimumRightLimit) var minimumLeftLimit = _getLimitValue(_minimumLeftLimit) // One area is closed. if (_isClosed) { if (closingEdge === Qt.LeftEdge) { if (offset > minimumLeftLimit / 2) { _open() } } else { if (-offset > minimumRightLimit / 2) { _open() } } return } // Check limits. var maximumLeftLimit = _getLimitValue(_maximumLeftLimit) var maximumRightLimit = _getLimitValue(_maximumRightLimit) var theoreticalBWidth = container.width - offset - contentA.width - handle.width // width(A) < minimum width(A). if (contentA.width + offset < minimumLeftLimit) { contentA.width = minimumLeftLimit if (closingEdge === Qt.LeftEdge && -offset > minimumLeftLimit / 2) { if (_isClosed) { _open() } else { _close() } } } // width(A) > maximum width(A). else if (maximumLeftLimit != null && contentA.width + offset > maximumLeftLimit) { contentA.width = maximumLeftLimit } // width(B) < minimum width(B). else if (theoreticalBWidth < minimumRightLimit) { contentA.width = container.width - handle.width - minimumRightLimit if (closingEdge !== Qt.LeftEdge && offset > minimumRightLimit / 2) { if (_isClosed) { _open() } else { _close() } } } // width(B) > maximum width(B). else if (maximumRightLimit != null && theoreticalBWidth > maximumRightLimit) { contentA.width = container.width - handle.width - maximumRightLimit } // Resize A/B. else { contentA.width = contentA.width + offset } } // --------------------------------------------------------------------------- function _open () { _isClosed = false _applyLimits() } function _close () { _isClosed = true closingTransition.running = true } function _inverseClosingState () { if (!_isClosed) { // Save state and close. _close() } else { // Restore old state. openingTransition.running = true } } function _isVisible (edge) { return ( !_isClosed || openingTransition.running || closingTransition.running ) || closingEdge !== edge } // --------------------------------------------------------------------------- onWidthChanged: _applyLimits() Component.onCompleted: { // Unable to modify these properties after creation. // It's a desired choice. _maximumLeftLimit = _parseLimit(maximumLeftLimit) _maximumRightLimit = _parseLimit(maximumRightLimit) _minimumLeftLimit = _parseLimit(minimumLeftLimit) _minimumRightLimit = _parseLimit(minimumRightLimit) contentA.width = (defaultChildAWidth == null) ? _getLimitValue(_minimumLeftLimit) : defaultChildAWidth _isClosed = defaultClosed } Item { id: contentA height: parent.height visible: _isVisible(Qt.LeftEdge) } MouseArea { id: handle property int _mouseStart visible: !container.hideSplitter anchors.left: contentA.right cursorShape: Qt.SplitHCursor height: parent.height hoverEnabled: true width: visible ? PanedStyle.handle.width : 0 onDoubleClicked: _inverseClosingState() onMouseXChanged: pressed && _applyLimitsOnUserMove(mouseX - _mouseStart) onPressed: _mouseStart = mouseX Rectangle { anchors.fill: parent color: parent.pressed ? PanedStyle.handle.color.pressed : (parent.containsMouse ? PanedStyle.handle.color.hovered : PanedStyle.handle.color.normal ) } } Item { id: contentB anchors.left: handle.right height: parent.height visible: _isVisible(Qt.RightEdge) width: container.width - contentA.width - handle.width } PropertyAnimation { id: openingTransition duration: PanedStyle.transitionDuration property: 'width' target: contentA to: closingEdge === Qt.LeftEdge ? minimumLeftLimit : container.width - minimumRightLimit - handle.width onRunningChanged: !running && _open() } PropertyAnimation { id: closingTransition duration: PanedStyle.transitionDuration property: 'width' target: contentA to: closingEdge === Qt.LeftEdge ? 0 : container.width - handle.width } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Picker/000077500000000000000000000000001434616504300241335ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Picker/DatePicker.qml000066400000000000000000000124061434616504300266640ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 import Units 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils Item{ id: mainItem property bool allYears : false // if false : years from today property alias selectedDate: monthList.selectedDate property bool hideOldDates : false signal clicked(date date); RowLayout { id: headerRow anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 5 height: 30 Layout.alignment: Qt.AlignCenter ActionButton{ isCustom: true colorSet: DatePickerStyle.nextMonthButton rotation: 180 onClicked: --monthList.currentIndex visible: monthList.currentIndex > 0 } Text { // month year Layout.fillWidth: true Layout.alignment: Qt.AlignCenter horizontalAlignment: Qt.AlignCenter text: new Date(monthList.currentYear, monthList.currentMonth, 15).toLocaleString(Qt.locale(), 'MMMM yyyy')// 15 because of timezones that can change the date for localeString color: DatePickerStyle.title.color font.pointSize: DatePickerStyle.title.pointSize font.capitalization: Font.Capitalize } ActionButton{ isCustom: true colorSet: DatePickerStyle.nextMonthButton onClicked: ++monthList.currentIndex visible: monthList.currentIndex < monthList.count } } ListView { id: monthList anchors.top: headerRow.bottom anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 10 cacheBuffer:0 property int maxYears: 5 // Max years to be requested. function set(date) { selectedDate = new Date(date) var moveTo = (selectedDate.getFullYear()-minYear) * 12 + selectedDate.getMonth() currentIndex = moveTo } property date selectedDate: new Date() property int minYear: mainItem.allYears ? new Date(0,0,1).getFullYear() : new Date().getFullYear() snapMode: ListView.SnapOneItem orientation: Qt.Horizontal clip: true // One model per month model: (new Date().getFullYear()- minYear + maxYears) * 12 currentIndex: 0 property int currentYear: Math.floor(currentIndex / 12) + minYear property int currentMonth: currentIndex % 12 highlightFollowsCurrentItem: true highlightRangeMode: ListView.StrictlyEnforceRange highlightMoveDuration: 100 Component.onCompleted: monthList.set(mainItem.selectedDate) delegate: Item { width: monthList.width == 0 ? 100 : monthList.width height: monthList.height == 0 ? 100 : monthList.height property int year: Math.floor(index / 12) + monthList.minYear property int month: index % 12 property int firstDay: new Date(year, month, 1).getDay() GridLayout { // 1 month calender id: grid //width: monthList.width; height: monthList.height anchors.fill: parent property real cellWidth: width / columns; property real cellHeight: height / rows // width and height of each cell in the grid. property real cellMinSize: Math.min(cellHeight, cellWidth) columns: 7 // days rows: 8 Repeater { model: grid.columns * grid.rows // 49 cells per month delegate: Item{ id: cellItem property int day: index - 7 // 0 = top left below Sunday (-7 to 41) property int date: day - firstDay + 1 // 1-31 property date cellDate: new Date(year, month, date) property bool selected : text.text != '-' && Utils.equalDate(cellDate, monthList.selectedDate) && text.text && day >= 0 width: grid.cellMinSize height: width Rectangle { // index is 0 to 48 anchors.centerIn: parent width: Math.max(text.implicitWidth, text.implicitHeight) + 20 height: width //border.width: 0.3 * radius border.width: 2 border.color: cellItem.selected ? DatePickerStyle.cell.selectedBorderColor : 'transparent' // selected //radius: 0.02 * monthList.height radius: width/2 opacity: !mouseArea.pressed? 1: 0.3 // pressed state Text { id: text anchors.centerIn: parent color: DatePickerStyle.cell.color font.pixelSize: cellItem.selected ? DatePickerStyle.cell.selectedPointSize : cellItem.day < 0 ? DatePickerStyle.cell.dayHeaderPointSize : DatePickerStyle.cell.dayPointSize font.bold: cellItem.day < 0 || cellItem.selected || Utils.equalDate(cellItem.cellDate, new Date()) // today text: { if(cellItem.day < 0){ // Magic date to set day names in this order : 'S', 'M', 'T', 'W', 'T', 'F', 'S' in Locale return Utils.exactDate(new Date(2000,9,index+1)).toLocaleString(Qt.locale(), 'ddd')[0].toUpperCase() }else if(cellItem.cellDate.getMonth() == month && (!hideOldDates || new Date(year, month, cellItem.date+1) >= new Date())) // new Date use time too return cellItem.date else return '-' } } } MouseArea { id: mouseArea anchors.fill: parent enabled: text.text && text.text != '-' && cellItem.day >= 0 onClicked: { monthList.selectedDate = cellItem.cellDate mainItem.clicked(monthList.selectedDate) } } } } } } } }linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Picker/TimePicker.qml000066400000000000000000000106571434616504300267130ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 import Units 1.0 Item{ id: mainItem property date selectedTime property int border: 25 property int centerPosition: Math.min(height, width)/2 property int middleMinSize: centerPosition - border // Minus border signal newDate(date date) signal clicked(date date) onNewDate: selectedTime = date function getDate(hText, mText){ var d = new Date() if(hText || outer.currentItem) d.setHours(hText ? hText : outer.currentItem.text) if(mText || inner.currentItem) d.setMinutes(mText ? mText : inner.currentItem.text) d.setSeconds(0) return d; } PathView { id: outer model: 24 interactive: false highlightRangeMode: PathView.NoHighlightRange currentIndex: 0 Connections{// startX/Y begin from currentIndex. It must be set to 0 at first. target: mainItem onSelectedTimeChanged: outer.currentIndex = mainItem.selectedTime.getHours() % 24 } Component.onCompleted: currentIndex = mainItem.selectedTime.getHours() % 24 highlight: Rectangle { id: rect width: 30 * 1.5 height: width radius: width / 2 border.color: TimePickerStyle.hoursColor border.width: 3 } delegate: Item { id: del width: 30 height: 30 property bool currentItem: PathView.view.currentIndex == index property alias text : hourText.text Text { id: hourText anchors.centerIn: parent font.pointSize: Units.dp * 11 font.bold: currentItem text: index color: currentItem ? TimePickerStyle.selectedItemColor : TimePickerStyle.unselectedItemColor } MouseArea { anchors.fill: parent onClicked: mainItem.selectedTime = mainItem.getDate(parent.text, undefined) } } path: Path { id: outPath property int yStep: middleMinSize * Math.cos(2 * Math.PI / outer.count) startX: mainItem.centerPosition+10 startY: mainItem.centerPosition - outPath.yStep PathArc { x: mainItem.centerPosition+10; y: mainItem.centerPosition + outPath.yStep radiusX: 110; radiusY: 110 useLargeArc: false } PathArc { x: mainItem.centerPosition+10; y: mainItem.centerPosition - outPath.yStep radiusX: 110; radiusY: 110 useLargeArc: false } } } PathView { id: inner model: 12 interactive: false highlightRangeMode: PathView.NoHighlightRange currentIndex: 0 Connections{// startX/Y begin from currentIndex. It must be set to 0 at first. target: mainItem onSelectedTimeChanged: inner.currentIndex = mainItem.selectedTime.getMinutes() / 5 } Component.onCompleted: currentIndex = mainItem.selectedTime.getMinutes() / 5 highlight: Rectangle { width: 30 * 1.5 height: width radius: width / 2 border.color: TimePickerStyle.minutesColor border.width: 3 } delegate: Item { width: 30 height: 30 property bool currentItem: PathView.view.currentIndex == index property alias text : textMin.text Text { id: textMin anchors.centerIn: parent font.pointSize: Units.dp * 11 font.bold: currentItem text: index * 5 color: currentItem ? TimePickerStyle.selectedItemColor : TimePickerStyle.unselectedItemColor } MouseArea { anchors.fill: parent onClicked: mainItem.selectedTime = mainItem.getDate(undefined, parent.text) } } path: Path { id: innerPath property int yStep: (middleMinSize - 30 ) * Math.cos(2 * Math.PI / inner.count) startX: mainItem.centerPosition+10; startY: mainItem.centerPosition - innerPath.yStep PathArc { x: mainItem.centerPosition+10; y: mainItem.centerPosition + innerPath.yStep radiusX: 40; radiusY: 40 useLargeArc: false } PathArc { x: mainItem.centerPosition+10; y: mainItem.centerPosition - innerPath.yStep radiusX: 40; radiusY: 40 useLargeArc: false } } } RowLayout { id: selectedTimeArea anchors.centerIn: parent Text { id: h font.pointSize: Units.dp * 12 font.bold: true text: outer.currentItem.text.length < 2 ? '0' + outer.currentItem.text : outer.currentItem.text } Text { id: div font.pointSize: Units.dp * 12 font.bold: true text: ':' } Text { id: m font.pointSize: Units.dp * 12 font.bold: true text: inner.currentItem.text.length < 2 ? '0' + inner.currentItem.text : inner.currentItem.text } } MouseArea { anchors.fill: selectedTimeArea onClicked: { mainItem.selectedTime = mainItem.getDate() mainItem.clicked(mainItem.selectedTime) } } }linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Popup/000077500000000000000000000000001434616504300240215ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Popup/DesktopPopup.qml000066400000000000000000000045141434616504300271750ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Window 2.2 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Qt.labs.platform 1.0 import Common.Styles 1.0 // ============================================================================= Item { id: wrapper objectName: '__internalWrapper' // --------------------------------------------------------------------------- property alias popupX: window.x property alias popupY: window.y property bool requestActivate: false property int flags: Qt.SplashScreen readonly property alias popupWidth: window.width readonly property alias popupHeight: window.height default property alias _content: content.data property bool _isOpen: false signal isOpened() signal isClosed() signal dataChanged() on_ContentChanged: dataChanged(_content) // --------------------------------------------------------------------------- function open () { _isOpen = true; isOpened(); } function close () { _isOpen = false isClosed() } // --------------------------------------------------------------------------- // No size, no position. height: 0 width: 0 visible:true Window { id: window objectName: '__internalWindow' property bool isFrameLess : false; property bool showAsTool : false // Don't use Popup for flags : it could lead to error in geometry. On Mac, Using Tool ensure to have the Window on Top and fullscreen independant flags: Qt.BypassWindowManagerHint | (showAsTool?Qt.Tool:Qt.WindowStaysOnTopHint) | Qt.Window | Qt.FramelessWindowHint; opacity: 1.0 height: _content[0] != null ? _content[0].height : 0 width: _content[0] != null ? _content[0].width : 0 visible:true Item { id: content anchors.fill:parent property var $parent: wrapper } } // --------------------------------------------------------------------------- states: State { name: 'opening' when: _isOpen PropertyChanges { opacity: 1.0 target: window } } transitions: [ Transition { from: '' to: 'opening' ScriptAction { script: { if (wrapper.requestActivate) { window.requestActivate() } } } }, Transition { from: '*' to: '' ScriptAction { script: window.close() } } ] } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Popup/Popup.qml000066400000000000000000000045121434616504300256410ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 as Controls import Common.Styles 1.0 import Utils 1.0 // ============================================================================= Item { id: wrapper property bool isOpen : popup.visible || (!popup.visible && closeDelay.running) // This way, we prevent instant reopening on lost focus with clicked events property bool delayClosing : false // Optionnal parameters, set the position of popup relative to this item. property var relativeTo property int relativeX: 0 property int relativeY: 0 default property alias _content: popup.contentItem property alias closePolicy : popup.closePolicy property alias backgroundPopup: backgroundPopup.color property bool showShadow : true // --------------------------------------------------------------------------- signal closed signal opened // --------------------------------------------------------------------------- function open () { if (popup.visible) { return } if (relativeTo) { var parent = Utils.getTopParent(this) popup.x = Qt.binding(function () { return relativeTo ? relativeTo.mapToItem(null, relativeX, relativeY).x : 0 }) popup.y = Qt.binding(function () { return relativeTo ? relativeTo.mapToItem(null, relativeX, relativeY).y : 0 }) } else { popup.x = Qt.binding(function () { return x }) popup.y = Qt.binding(function () { return y }) } popup.open() } function close(){ if (!popup.visible) { return } popup.x = 0 popup.y = 0 popup.close() } // --------------------------------------------------------------------------- visible: false // --------------------------------------------------------------------------- Timer{// This way, we prevent instant reopening on lost focus with clicked events id: closeDelay interval: 100 } Controls.Popup { id: popup height: wrapper.height width: wrapper.width background: Rectangle { id: backgroundPopup color: PopupStyle.backgroundColor height: popup.height width: popup.width layer { enabled: wrapper.showShadow effect: PopupShadow {} } } padding: 0 Component.onCompleted: parent = Utils.getTopParent(this) onClosed: { if(wrapper.delayClosing) closeDelay.restart() wrapper.closed() } onOpened: wrapper.opened() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Popup/PopupShadow.qml000066400000000000000000000005551434616504300270120ustar00rootroot00000000000000import QtGraphicalEffects 1.0 import Common.Styles 1.0 // ============================================================================= DropShadow { color: PopupStyle.shadow.color horizontalOffset: PopupStyle.shadow.horizontalOffset radius: PopupStyle.shadow.radius samples: PopupStyle.shadow.samples verticalOffset: PopupStyle.shadow.verticalOffset } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/000077500000000000000000000000001434616504300242015ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Animations/000077500000000000000000000000001434616504300263035ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Animations/BusyIndicatorStyle.qml000066400000000000000000000006261434616504300326220ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Busy' property color color: ColorsList.add(sectionName+'_indicator', 'q').color property color alternateColor: ColorsList.add(sectionName+'_indicator_alt', 'i').color property int duration: 1250 property int nSpheres: 8 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Dialog/000077500000000000000000000000001434616504300254005ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Dialog/DateTimeDialogStyle.qml000066400000000000000000000040561434616504300317550ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'DateTimeDialog' property color color: ColorsList.add(sectionName, 'k').color property QtObject title: QtObject { property color lowGradient: ColorsList.add(sectionName+'_title_gradient_low', 'y').color property color highGradient: ColorsList.add(sectionName+'_title_gradient_high', 'z').color } property QtObject buttons: QtObject { property int bottomMargin: 25 property int leftMargin: 50 property int rightMargin: 50 property int spacing: 20 property int topMargin: 15 } property QtObject confirmDialog: QtObject { property int height: 200 property int width: 400 } property QtObject content: QtObject { property int leftMargin: 25 property int rightMargin: 25 property int topMargin: 10 property int bottomMargin: 25 } property QtObject description: QtObject { property color color: ColorsList.add(sectionName+'_description', 'j').color property int leftMargin: 50 property int pointSize: Units.dp * 11 property int rightMargin: 50 property int verticalMargin: 25 } property QtObject closeButton: QtObject { property int iconSize: 20 property string name : 'close' property string icon : 'close_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 'l_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Dialog/DialogStyle.qml000066400000000000000000000040461434616504300303370ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'Dialog' property color color: ColorsList.add(sectionName, 'k').color property QtObject title: QtObject { property color lowGradient: ColorsList.add(sectionName+'_title_gradient_low', 'y').color property color highGradient: ColorsList.add(sectionName+'_title_gradient_high', 'z').color } property QtObject buttons: QtObject { property int bottomMargin: 25 property int leftMargin: 50 property int rightMargin: 50 property int spacing: 20 property int topMargin: 15 } property QtObject confirmDialog: QtObject { property int height: 200 property int width: 400 } property QtObject content: QtObject { property int leftMargin: 25 property int rightMargin: 25 property int topMargin: 10 property int bottomMargin: 25 } property QtObject description: QtObject { property color color: ColorsList.add(sectionName+'_description', 'j').color property int leftMargin: 50 property int pointSize: Units.dp * 11 property int rightMargin: 50 property int verticalMargin: 25 } property QtObject closeButton: QtObject { property int iconSize: 20 property string name : 'close' property string icon : 'close_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 'l_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/000077500000000000000000000000001434616504300251045ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/ActionBarStyle.qml000066400000000000000000000002341434616504300305010ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 // ============================================================================= QtObject { property int spacing: 8 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/ActionSwitchStyle.qml000066400000000000000000000002021434616504300312310ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 // ============================================================================= QtObject { } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Buttons/000077500000000000000000000000001434616504300265425ustar00rootroot00000000000000AbstractTextButtonStyle.qml000066400000000000000000000005531434616504300340260ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Buttonspragma Singleton import QtQuick 2.7 import Units 1.0 // ============================================================================= QtObject { property QtObject background: QtObject { property int height: 30 property int radius: 4 property int width: 160 } property QtObject text: QtObject { property int pointSize: Units.dp * 9 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Buttons/ExclusiveButtonsStyle.qml000066400000000000000000000011321434616504300336210ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ExclusiveButtons' property int buttonsSpacing: 8 property QtObject button: QtObject { property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_h', 'n').color property color normal: ColorsList.add(sectionName+'_n', 'x').color property color pressed: ColorsList.add(sectionName+'_p', 'i').color property color selected: ColorsList.add(sectionName+'_c', 'g').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Buttons/FileChooserButtonStyle.qml000066400000000000000000000044311434616504300336760ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'FileChooser' property QtObject tools: QtObject { property int width: 30 property QtObject button: QtObject { property int iconSize: 16 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_h', 'c').color property color normal: ColorsList.add(sectionName+'_n', 'f').color property color pressed: ColorsList.add(sectionName+'_p', 'c').color } } } property QtObject file: QtObject { property int iconSize: 30 property string name : 'file' property string icon : 'file_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } property QtObject folder: QtObject { property int iconSize: 30 property string name : 'folder' property string icon : 'folder_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Buttons/SmallButtonStyle.qml000066400000000000000000000014251434616504300325440ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'SmallButton' property int leftPadding: 5 property int rightPadding: 5 property QtObject background: QtObject { property int height: 22 property int radius: 20 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_bg_h', 'c').color property color normal: ColorsList.add(sectionName+'_bg_n', 'f').color property color pressed: ColorsList.add(sectionName+'_bg_p', 'i').color } } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_text', 'q').color property int pointSize: Units.dp * 8 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Buttons/TextButtonAStyle.qml000066400000000000000000000017001434616504300325150ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 // TextButtonAStyle (Grey) import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'TextButtonA' property QtObject backgroundColor: QtObject { property color disabled: ColorsList.add(sectionName+'_bg_d', 'o').color property color hovered: ColorsList.add(sectionName+'_bg_h', 'j').color property color normal: ColorsList.add(sectionName+'_bg_n', 'r').color property color pressed: ColorsList.add(sectionName+'_bg_p', 'i').color } property QtObject textColor: QtObject { property color disabled: ColorsList.add(sectionName+'_text_d', 'q').color property color hovered: ColorsList.add(sectionName+'_text_h', 'q').color property color normal: ColorsList.add(sectionName+'_text_n', 'q').color property color pressed: ColorsList.add(sectionName+'_text_p', 'q').color } property QtObject borderColor : backgroundColor } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Buttons/TextButtonBStyle.qml000066400000000000000000000017041434616504300325220ustar00rootroot00000000000000// TextButtonBStyle (Primary) pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'TextButtonB' property QtObject backgroundColor: QtObject { property color disabled: ColorsList.add(sectionName+'_bg_d', 'i30').color property color hovered: ColorsList.add(sectionName+'_bg_h', 'b').color property color normal: ColorsList.add(sectionName+'_bg_n', 'i').color property color pressed: ColorsList.add(sectionName+'_bg_p', 'm').color } property QtObject textColor: QtObject { property color disabled: ColorsList.add(sectionName+'_text_d', 'q').color property color hovered: ColorsList.add(sectionName+'_text_h', 'q').color property color normal: ColorsList.add(sectionName+'_text_n', 'q').color property color pressed: ColorsList.add(sectionName+'_text_p', 'q').color } property QtObject borderColor : backgroundColor } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Buttons/TextButtonCStyle.qml000066400000000000000000000017271434616504300325300ustar00rootroot00000000000000// TextButtonCStyle (Green) pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'TextButtonC' property QtObject backgroundColor: QtObject { property color disabled: ColorsList.add(sectionName+'_bg_d', 'i30').color property color hovered: ColorsList.add(sectionName+'_bg_h', 'validation_h').color property color normal: ColorsList.add(sectionName+'_bg_n', 'validation').color property color pressed: ColorsList.add(sectionName+'_bg_p', 'i').color } property QtObject textColor: QtObject { property color disabled: ColorsList.add(sectionName+'_text_d', 'q').color property color hovered: ColorsList.add(sectionName+'_text_h', 'q').color property color normal: ColorsList.add(sectionName+'_text_n', 'q').color property color pressed: ColorsList.add(sectionName+'_text_p', 'q').color } property QtObject borderColor : backgroundColor } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/CheckBoxTextStyle.qml000066400000000000000000000011551434616504300311750ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CheckBox' property int pointSize: Units.dp * 10 property int radius: 3 property int size: 18 property QtObject color: QtObject { property color pressed: ColorsList.add(sectionName+'_p', 'i').color property color hovered: ColorsList.add(sectionName+'_h', 'h').color property color normal: ColorsList.add(sectionName+'_n', 'g').color property color selected: ColorsList.add(sectionName+'_u', 'i').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/ComboBoxStyle.qml000066400000000000000000000023531434616504300303530ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ComboBox' property QtObject indicator: QtObject{ property QtObject dropDown : QtObject{ property string icon: 'drop_down_custom' property int iconSize: 20 property color color: ColorsList.addImageColor(sectionName+'_indicator', icon, 'l_n_b_fg').color } } property QtObject background: QtObject { property int height: 36 property int iconSize: 10 property int radius: 4 property int width: 200 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_border_n', 'c').color property int width: 1 } property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_normal', 'q').color property color readOnly: ColorsList.add(sectionName+'_readonly', 'e').color } } property QtObject contentItem: QtObject { property int iconSize: 20 property int leftMargin: 10 property int spacing: 5 property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_text_n', 'd').color property int pointSize: Units.dp * 10 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/CommonItemDelegateStyle.qml000066400000000000000000000016701434616504300323460ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CommonItemDelegate' property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_h', 'o').color property color normal: ColorsList.add(sectionName+'_n', 'q').color } property QtObject contentItem: QtObject { property int iconSize: 20 property int spacing: 5 property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_text', 'd').color property int pointSize: Units.dp * 10 } } property QtObject indicator: QtObject { property color color: ColorsList.add(sectionName+'_indicator', 'i').color property int width: 5 } property QtObject separator: QtObject { property color color: ColorsList.add(sectionName+'_separator', 'c').color property int height: 1 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/DroppableTextAreaStyle.qml000066400000000000000000000107431434616504300322130ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'DroppableTextArea' property color backgroundColor: ColorsList.add(sectionName+'_Chat_bg', 'e').color property color outsideBackgroundColor: ColorsList.add(sectionName+'_Chat_outsideBackground', 'aa').color property QtObject ephemeralTimer: QtObject{ property string icon: 'timer_custom' property int iconSize : 30 property color timerColor: ColorsList.addImageColor(sectionName+'_ephemeralTimer', icon, 'ad').color } property QtObject fileChooserButton: QtObject { property int margins: 6 property int iconSize: 40 property string icon : 'attachment_custom' property string name : 'attachment' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'me_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'me_d_b_fg').color } property QtObject chatMicro: QtObject { property int margins: 6 property int iconSize: 40 property string name : 'micro' property string icon : 'chat_micro_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 'me_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'me_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 'me_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'me_d_b_fg').color } property QtObject send: QtObject { property int margins: 5 property int iconSize: 30 property string name : 'send' property string icon : 'send_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject hoverContent: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_Chat_hoverContent_bg', 'q').color property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_Chat_hoverContent_text', 'i').color property int pointSize: Units.dp * 11 } } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_Chat_text', 'd').color property int pointSize: Units.dp * 10 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Fields/000077500000000000000000000000001434616504300263125ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Fields/NumericFieldStyle.qml000066400000000000000000000012421434616504300324130ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'NumericField' property QtObject tools: QtObject { property int width: 20 property QtObject button: QtObject { property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_n', 'f').color property color pressed: ColorsList.add(sectionName+'_p', 'c').color } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_text', 'd').color property int pointSize: Units.dp * 9 } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Fields/TextAreaFieldStyle.qml000066400000000000000000000015431434616504300325320ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'TextAreaField' property QtObject background: QtObject { property int height: 36 property int width: 200 property int radius: 4 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_bg_border', 'c').color property int width: 1 } property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_bg_n', 'q').color property color readOnly: ColorsList.add(sectionName+'_bg_readOnly', 'e').color } } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_text', 'd').color property int pointSize: Units.dp * 10 property int padding: 8 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Fields/TextFieldStyle.qml000066400000000000000000000112101434616504300317310ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'TextField' property QtObject normal : QtObject { property QtObject background: QtObject { property int height: 36 property int width: 200 property int radius: 4 property QtObject border: QtObject { property QtObject color: QtObject { property color error: ColorsList.add(sectionName+'_n_bg_border_error', 'error').color property color normal: ColorsList.add(sectionName+'_n_bg_border_n', 'c').color property color selected: ColorsList.add(sectionName+'_n_bg_border_c', 'i').color } property int width: 1 } property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_n_bg_n', 'q').color property color readOnly: ColorsList.add(sectionName+'_n_bg_readonly', 'e').color } property QtObject mandatory: QtObject{ property color color: ColorsList.add(sectionName+'_required_text', 'g').color property real pointSize: Units.dp * 10 } } property QtObject text: QtObject { property color normal: ColorsList.add(sectionName+'_n_text', 'd').color property color readOnly: ColorsList.add(sectionName+'_n_text_readonly', 'd').color property int pointSize: Units.dp * 10 property int rightPadding: 5 } } property QtObject unbordered : QtObject { property QtObject background: QtObject { property int height: 36 property int width: 200 property int radius: 4 property QtObject border: QtObject { property QtObject color: QtObject { property color error: 'black' property color normal: 'black' property color selected: 'black' } property int width: 0 } property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_unbordered_bg_n', 'q').color property color readOnly: ColorsList.add(sectionName+'_unbordered_bg_readonly', 'e').color } property QtObject mandatory: QtObject{ property color color: ColorsList.add(sectionName+'_unbordered_required_text', 'g').color property real pointSize: Units.dp * 10 } } property QtObject text: QtObject { property color normal: ColorsList.add(sectionName+'_unbordered_text', 'd').color property color readOnly: ColorsList.add(sectionName+'_unbordered_text_readonly', 'd').color property int pointSize: Units.dp * 10 property int rightPadding: 5 } } property QtObject flat : QtObject { property QtObject background: QtObject { property int height: 36 property int width: 200 property int radius: 0 property QtObject border: QtObject { property QtObject color: QtObject { property color error: 'black' property color normal: 'black' property color selected: 'black' } property int width: 0 } property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_flat_bg_n', 'q').color property color readOnly: ColorsList.add(sectionName+'_flat_bg_readonly', 'e').color } property QtObject mandatory: QtObject{ property color color: ColorsList.add(sectionName+'_flat_required_text', 'g').color property real pointSize: Units.dp * 10 } } property QtObject text: QtObject { property color normal: ColorsList.add(sectionName+'_flat_text', 'd').color property color readonly: ColorsList.add(sectionName+'_flat_text_readonly', 'd').color property int pointSize: Units.dp * 10 property int rightPadding: 5 } } property QtObject flatInverse : QtObject { property QtObject background: QtObject { property int height: 36 property int width: 200 property int radius: 0 property QtObject border: QtObject { property QtObject color: QtObject { property color error: 'black' property color normal: 'black' property color selected: 'black' } property int width: 0 } property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_flat_inv_bg_n', 'q').color property color readOnly: ColorsList.add(sectionName+'_flat_inv_bg_readonly', 'q').color } property QtObject mandatory: QtObject{ property color color: ColorsList.add(sectionName+'_flat_inv_required_text', 'g').color property real pointSize: Units.dp * 10 } } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_flat_inv_text', 'd').color property color readOnly: ColorsList.add(sectionName+'_flat_inv_readonly', 'readonly_fg').color property int pointSize: Units.dp * 10 property int rightPadding: 5 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/ListFormStyle.qml000066400000000000000000000036061434616504300304040ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property int lineHeight: 35 property string sectionName : 'ListForm' property QtObject value: QtObject { property QtObject placeholder: QtObject { property color color: ColorsList.add(sectionName+'_placeholder', 'n').color property int pointSize: Units.dp * 10 } property QtObject text: QtObject { property int padding: 10 } } property QtObject titleArea: QtObject { property int spacing: 10 property int iconSize: 18 property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_text', 'j').color property int pointSize: Units.dp * 9 property int width: 130 } property QtObject add: QtObject { property int iconSize: 18 property string name : 'add' property string icon : 'add_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'l_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'l_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Placements/000077500000000000000000000000001434616504300271775ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Placements/FormHGroupStyle.qml000066400000000000000000000010101434616504300327530ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'FormHGroup' property int spacing: 20 property QtObject content: QtObject { property int maxWidth: 400 } property QtObject legend: QtObject { property color color: ColorsList.add(sectionName+'_legend', 'j').color property int pointSize: Units.dp * 10 property int height: 36 property int width: 200 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Placements/FormLineStyle.qml000066400000000000000000000002351434616504300324460ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 // ============================================================================= QtObject { property int spacing: 20 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Placements/FormStyle.qml000066400000000000000000000055561434616504300316510ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property int spacing: 10 property string sectionName : 'Form' property QtObject header: QtObject { property int bottomMargin: 5 property int spacing: 5 property QtObject separator: QtObject { property color color: ColorsList.add(sectionName+'_header_separator', 'i').color property int height: 2 } property QtObject title: QtObject { property color color: ColorsList.add(sectionName+'_header_title', 'i').color property int pointSize: Units.dp * 12 } property QtObject add: QtObject { property int iconSize: 38 property string name : 'add' property string icon : 'add_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'me_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'me_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject deleteAction: QtObject { property int iconSize: 38 property string name : 'delete' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'me_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'me_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Placements/FormTableLineStyle.qml000066400000000000000000000002351434616504300334160ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 // ============================================================================= QtObject { property int spacing: 20 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Placements/FormTableStyle.qml000066400000000000000000000010141434616504300326020ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'FormTable' property int spacing: 10 property QtObject entry: QtObject { property int height: 36 property int width: 200 property int maxWidth: 400 property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_entry_text', 'j').color property int pointSize: Units.dp * 10 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Placements/FormVGroupStyle.qml000066400000000000000000000012131434616504300327760ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'FormVGroup' property int spacing: 5 property QtObject content: QtObject { property int maxWidth: 400 } property QtObject error: QtObject { property color color: ColorsList.add(sectionName+'_error', 'error').color property int pointSize: Units.dp * 10 property int height: 11 } property QtObject legend: QtObject { property color color: ColorsList.add(sectionName+'_legend', 'j').color property int pointSize: Units.dp * 10 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/RadioButtonStyle.qml000066400000000000000000000010071434616504300310700ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'RadioButton' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'k').color property int height: 60 property color color: ColorsList.add(sectionName+'_fg', 'j').color property int weight: Font.Normal property int selectedWeight: Font.Bold property int pointSize: Units.dp * 12 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/SearchBoxStyle.qml000066400000000000000000000006631434616504300305230ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'SearchBox' property color shadowColor: ColorsList.add(sectionName+'_shadow', 'l').color property color iconColor: ColorsList.add(sectionName+'_icon', 'c').color property string searchIcon: 'search_custom' property string cancelIcon: 'close_custom' } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/SliderStyle.qml000066400000000000000000000021261434616504300300630ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Slider' property QtObject background: QtObject { property color color: ColorsList.add(sectionName+'_bg', 'c').color property int height: 4 property int radius: 2 property int width: 200 property QtObject content: QtObject { property color color: ColorsList.add(sectionName+'_content', 'm').color property int radius: 2 } } property QtObject handle: QtObject { property int height: 16 property int radius: 13 property int width: 16 property QtObject border: QtObject { property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_handle_border_n', 'c').color property color pressed: ColorsList.add(sectionName+'_handle_border_p', 'c').color } } property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_handle_n', 'e').color property color pressed: ColorsList.add(sectionName+'_handle_p', 'f').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/StackViewStyle.qml000066400000000000000000000006111434616504300305360ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'StackView' property int bottomMargin: 35 property int leftMargin: 90 property int rightMargin: 90 property int topMargin: 50 property QtObject stackAnimation: QtObject { property int duration: 400 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/SwitchStyle.qml000066400000000000000000000070271434616504300301070ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject{ property string sectionName: 'Switch' property QtObject normal :QtObject { property QtObject animation: QtObject { property int duration: 200 } property QtObject indicator: QtObject { property int height: 18 property int radius: 10 property int width: 48 property QtObject border: QtObject { property QtObject color: QtObject { property color checked: ColorsList.add(sectionName+'_n_indicator_border_c', 'i').color property color disabled: ColorsList.add(sectionName+'_n_indicator_border_d', 'c').color property color normal: ColorsList.add(sectionName+'_n_indicator_border_n', 'c').color } } property QtObject color: QtObject { property color checked: ColorsList.add(sectionName+'_n_indicator_c', 'i').color property color disabled: ColorsList.add(sectionName+'_n_indicator_d', 'e').color property color normal: ColorsList.add(sectionName+'_n_indicator_n', 'q').color } } property QtObject sphere: QtObject { property int size: 22 property QtObject border: QtObject { property QtObject color: QtObject { property color checked: ColorsList.add(sectionName+'_n_sphere_border_c', 'i').color property color disabled: ColorsList.add(sectionName+'_n_sphere_border_d', 'c').color property color normal: ColorsList.add(sectionName+'_n_sphere_border_n', 'n').color property color pressed: ColorsList.add(sectionName+'_n_sphere_border_p', 'n').color } } property QtObject color: QtObject { property color pressed: ColorsList.add(sectionName+'_n_sphere_p', 'c').color property color disabled: ColorsList.add(sectionName+'_n_sphere_d', 'e').color property color normal: ColorsList.add(sectionName+'_n_sphere_n', 'q').color } } } property QtObject aux : QtObject{ property QtObject animation: QtObject { property int duration: 200 } property QtObject indicator: QtObject { property int height: 18 property int radius: 10 property int width: 48 property QtObject border: QtObject { property QtObject color: QtObject { property color checked: ColorsList.add(sectionName+'_aux_indicator_border_c', 's').color property color disabled: ColorsList.add(sectionName+'_aux_indicator_border_d', 'c').color property color normal: ColorsList.add(sectionName+'_aux_indicator_border_n', 'c').color } } property QtObject color: QtObject { property color checked: ColorsList.add(sectionName+'_aux_indicator_c', 's').color property color disabled: ColorsList.add(sectionName+'_aux_indicator_d', 'e').color property color normal: ColorsList.add(sectionName+'_aux_indicator_n', 'q').color } } property QtObject sphere: QtObject { property int size: 22 property QtObject border: QtObject { property QtObject color: QtObject { property color checked: ColorsList.add(sectionName+'_aux_sphere_border_c', 's').color property color disabled: ColorsList.add(sectionName+'_aux_sphere_border_d', 'c').color property color normal: ColorsList.add(sectionName+'_aux_sphere_border_n', 'n').color property color pressed: ColorsList.add(sectionName+'_aux_sphere_border_p', 'n').color } } property QtObject color: QtObject { property color pressed: ColorsList.add(sectionName+'_aux_sphere_p', 'c').color property color disabled: ColorsList.add(sectionName+'_aux_sphere_d', 'e').color property color normal: ColorsList.add(sectionName+'_aux_sphere_n', 'q').color } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Tab/000077500000000000000000000000001434616504300256125ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Tab/TabButtonStyle.qml000066400000000000000000000031701434616504300312510ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'TabButton' property int spacing: 8 property QtObject backgroundColor: QtObject { property color disabled: ColorsList.add(sectionName+'_bg_d', 'i30').color property color hovered: ColorsList.add(sectionName+'_bg_h', 'b').color property color normal: ColorsList.add(sectionName+'_bg_n', 'i').color property color pressed: ColorsList.add(sectionName+'_bg_p', 'm').color property color selected: ColorsList.add(sectionName+'_bg_c', 'k').color } property QtObject icon: QtObject { property int size: 20 property string sipAccountsIcon: 'settings_sip_accounts_custom' property string audioIcon: 'settings_audio_custom' property string videoIcon: 'settings_video_custom' property string callIcon: 'settings_call_custom' property string networkIcon: 'settings_network_custom' property string advancedIcon: 'settings_advanced_custom' } property QtObject text: QtObject { property int pointSize: Units.dp * 9 property int height: 40 property int leftPadding: 10 property int rightPadding: 10 property QtObject color: QtObject { property color disabled: ColorsList.add(sectionName+'_text_d', 'q').color property color hovered: ColorsList.add(sectionName+'_text_h', 'q').color property color normal: ColorsList.add(sectionName+'_text_n', 'q').color property color pressed: ColorsList.add(sectionName+'_text_p', 'q').color property color selected: ColorsList.add(sectionName+'_text_c', 'i').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/Tab/TabContainerStyle.qml000066400000000000000000000010171434616504300317160ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'TabContainer' property color color: ColorsList.add(sectionName+'', 'k').color property int bottomMargin: 30 property int leftMargin: 30 property int rightMargin: 40 property int topMargin: 30 property QtObject separator: QtObject { property int height: 2 property color color: ColorsList.add(sectionName+'_separator', 'f').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Form/TransparentTextInputStyle.qml000066400000000000000000000014431434616504300330300ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'TransparentTextInput' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'f').color property int iconSize: 12 property int padding: 10 property QtObject placeholder: QtObject { property color color: ColorsList.add(sectionName+'_palceholder', 'n').color property int pointSize: Units.dp * 10 } property QtObject text: QtObject { property int pointSize: Units.dp * 10 property QtObject color: QtObject { property color focused: ColorsList.add(sectionName+'_text_focused', 'l').color property color normal: ColorsList.add(sectionName+'_text_n', 'd').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Indicators/000077500000000000000000000000001434616504300263005ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Indicators/MediaProgressBarStyle.qml000066400000000000000000000041351434616504300332300ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'MediaProgressBar' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'k').color property string gaugeIcon: 'chat_audio_soundwave_custom' property QtObject progressionWave: QtObject{ property int iconSize: 60 property int iconHeight: 60 property int iconWidth: 60 property string name : 'progression_soundwave' property string icon : 'chat_audio_soundwave_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'l_n_b_bg').color property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'l_h_b_bg').color property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'l_p_b_bg').color property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'l_n_b_fg').color property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'l_h_b_fg').color property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'l_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Indicators/RoundProgressBarStyle.qml000066400000000000000000000011331434616504300332730ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 import Units 1.0 // ============================================================================= QtObject { property string sectionName: 'RoundProgressBar' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'progress_bg').color property color progressRemainColor: ColorsList.add(sectionName+'_remaining_fg', 'progress_remaining_fg').color property color progressColor: ColorsList.add(sectionName+'_fg', 'i').color property int progressionWidth : 3 property int borderWidth: 2 property int pointSize: Units.dp * 7 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Indicators/VuMeterStyle.qml000066400000000000000000000021031434616504300314170ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'VuMeter' property int height: 40 property int width: 5 property QtObject high: QtObject { property QtObject background: QtObject { property QtObject color: QtObject { property color disabled: ColorsList.add(sectionName+'_bg_d', 'o').color property color enabled: ColorsList.add(sectionName+'_bg_enabled', 'n').color } } property QtObject contentItem: QtObject { property color color: ColorsList.add(sectionName+'_contentItem', 'b').color } } property QtObject low: QtObject { property QtObject background: QtObject { property QtObject color: QtObject { property color disabled: ColorsList.add(sectionName+'_low_bg_d', 'o').color property color enabled: ColorsList.add(sectionName+'_low_bg_enabled', 'n').color } } property QtObject contentItem: QtObject { property color color: ColorsList.add(sectionName+'_low_contentItem', 'j').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Menus/000077500000000000000000000000001434616504300252705ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Menus/ApplicationMenuStyle.qml000066400000000000000000000024171434616504300321200ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ApplicationMenu' property int spacing: 1 property color backgroundColor: ColorsList.add(sectionName+'_bg', 'n').color property QtObject entry: QtObject { property int iconSize: 24 property int leftMargin: 20 property int rightMargin: 20 property int spacing: 18 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_entry_h', 'h').color property color normal: ColorsList.add(sectionName+'_entry_n', 'g').color property color pressed: ColorsList.add(sectionName+'_entry_p', 'i').color property color selected: ColorsList.add(sectionName+'_entry_selected', 'j').color } property QtObject indicator: QtObject { property color color: ColorsList.add(sectionName+'_entry_indicator', 'i').color property int width: 5 } property QtObject text: QtObject { property int pointSize: Units.dp * 10 property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_entry_text_n', 'q').color property color selected: ColorsList.add(sectionName+'_entry_text_c', 'q').color } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Menus/DropDownStaticMenuStyle.qml000066400000000000000000000014211434616504300325530ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'DropDownStaticMenu' property int spacing: 1 property QtObject entry: QtObject { property int leftMargin: 18 property int rightMargin: 8 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_entry_h', 'j').color property color normal: ColorsList.add(sectionName+'_entry_n', 'g').color property color pressed: ColorsList.add(sectionName+'_entry_p', 'i').color } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_entry_text', 'q').color property int pointSize: Units.dp * 9 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Menus/MenuItemStyle.qml000066400000000000000000000143101434616504300305460ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.3 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'MenuItem' property QtObject speaker: QtObject { property int iconSize: 30 property string icon : 'speaker_on_custom' } property QtObject copy: QtObject { property int iconSize: 30 property string icon : 'menu_copy_text_custom' } property QtObject reply: QtObject { property int iconSize: 30 property string icon : 'menu_reply_custom' } property QtObject forward: QtObject { property int iconSize: 30 property string icon : 'menu_forward_custom' } property QtObject imdn: QtObject { property int iconSize: 30 property string icon : 'menu_imdn_info_custom' } property QtObject deleteEntry: QtObject { property int iconSize: 30 property string icon : 'delete_custom' } property QtObject info: QtObject { property int iconSize: 20 property string icon : 'menu_info_custom' } property QtObject devices: QtObject { property string icon : 'menu_devices_custom' } property QtObject ephemeral: QtObject { property string icon : 'menu_ephemeral_custom' } property QtObject scheduleMeeting: QtObject { property string icon : 'meetings_custom' } property QtObject contact: QtObject { property string add : 'contact_add_custom' property string view : 'contact_view_custom' } property QtObject normal : QtObject{ property int leftMargin: 5 property int rightMargin: 5 property QtObject background: QtObject { property int height: 30 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_normal_bg_h', 'o').color property color normal: ColorsList.add(sectionName+'_normal_bg_n', 'q').color property color pressed: ColorsList.add(sectionName+'_normal_bg_p', 'o').color } } property QtObject text: QtObject { property int pointSize: Units.dp * 10 property int weight : Font.Bold property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_n_text_h', 'j').color property color normal: ColorsList.add(sectionName+'_n_text_n', 'j').color property color pressed: ColorsList.add(sectionName+'_n_text_p', 'j').color property color disabled: ColorsList.add(sectionName+'_n_text_d', 'l50').color } } } property QtObject aux : QtObject{ property int leftMargin: 10 property int rightMargin: 10 property QtObject background: QtObject { property int height: 40 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_aux_bg_h', 'v').color property color normal: ColorsList.add(sectionName+'_aux_bg_n', 'a').color property color pressed: ColorsList.add(sectionName+'_aux_bg_p', 'v').color } } property QtObject text: QtObject { property int pointSize: Units.dp * 10 property int weight : Font.Normal property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_aux_text_h', 'j').color property color normal: ColorsList.add(sectionName+'_aux_text_n', 'j').color property color pressed: ColorsList.add(sectionName+'_aux_text_p', 'j').color property color disabled: ColorsList.add(sectionName+'_aux_text_d', 'l50').color } } } property QtObject auxError : QtObject{ property int leftMargin: 10 property int rightMargin: 10 property QtObject background: QtObject { property int height: 40 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_auxRed_bg_h', 'v').color property color normal: ColorsList.add(sectionName+'_auxRed_bg_n', 'a').color property color pressed: ColorsList.add(sectionName+'_auxRed_bg_p', 'v').color } } property QtObject text: QtObject { property int pointSize: Units.dp * 10 property int weight : Font.Normal property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_auxError_text_h', 'error').color property color normal: ColorsList.add(sectionName+'_auxError_text_n', 'error').color property color pressed: ColorsList.add(sectionName+'_auxError_text_p', 'error').color property color disabled: ColorsList.add(sectionName+'_auxError_text_d', 'l50').color } } } property QtObject aux2 : QtObject{ property int leftMargin: 10 property int rightMargin: 10 property QtObject background: QtObject { property int height: 50 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_aux2_bg_h', 'w').color property color normal: ColorsList.add(sectionName+'_aux2_bg_n', 'w').color property color pressed: ColorsList.add(sectionName+'_aux2_bg_p', 'v').color } } property QtObject text: QtObject { property int pointSize: Units.dp * 11 property int weight : Font.Normal property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_aux2_text_h', 'm').color property color normal: ColorsList.add(sectionName+'_aux2_text_n', 'j').color property color pressed: ColorsList.add(sectionName+'_aux2_text_p', 'm').color property color disabled: ColorsList.add(sectionName+'_aux2_text_d', 'l50').color } } } property QtObject aux2Error : QtObject{ property int leftMargin: 10 property int rightMargin: 10 property QtObject background: QtObject { property int height: 50 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_aux2Error_bg_h', 'w').color property color normal: ColorsList.add(sectionName+'_aux2Error_bg_n', 'w').color property color pressed: ColorsList.add(sectionName+'_aux2Error_bg_p', 'v').color } } property QtObject text: QtObject { property int pointSize: Units.dp * 11 property int weight : Font.Normal property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_aux2Error_text_h', 'error').color property color normal: ColorsList.add(sectionName+'_aux2Error_text_n', 'error').color property color pressed: ColorsList.add(sectionName+'_aux2Error_text_p', 'error').color property color disabled: ColorsList.add(sectionName+'_aux2Error_text_d', 'l50').color } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Menus/MenuStyle.qml000066400000000000000000000021721434616504300277320ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Menu' property QtObject normal : QtObject { property color color: ColorsList.add(sectionName+'_n', 'q').color property int width: 130 property bool shadowEnabled: true property int radius : 0 property QtObject border : QtObject { property color color: 'black' property int width: 0 } } property QtObject aux : QtObject { property color color: ColorsList.add(sectionName+'_aux', 'q').color property int width: 200 property bool shadowEnabled: false property int radius : 5 property QtObject border : QtObject { property color color: ColorsList.add(sectionName+'_aux_border', 'u').color property int width: 1 } } property QtObject aux2 : QtObject { property color color: ColorsList.add(sectionName+'_aux2', 'q').color property int width: 250 property bool shadowEnabled: false property int radius : 0 property QtObject border : QtObject { property color color: 'black' property int width: 0 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Misc/000077500000000000000000000000001434616504300250745ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Misc/ForceScrollBarStyle.qml000066400000000000000000000013401434616504300314700ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ForceScrollBar' property QtObject background : QtObject { property color color: ColorsList.add(sectionName+'_bg', 'g20').color property int radius : 10 } property QtObject contentItem: QtObject { property int implicitHeight: 8 property int implicitWidth: 8 property int radius: 10 } property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_h', 'h').color property color normal: ColorsList.add(sectionName+'_n', 'g20').color property color pressed: ColorsList.add(sectionName+'_p', 'd').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Misc/MessageBannerStyle.qml000066400000000000000000000010721434616504300313420ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'MessageBanner' property string copyTextIcon : 'menu_copy_text_custom' property color color: ColorsList.add(sectionName+'_message_banner', 'message_banner_bg', 'Background of message banner').color property color textColor: ColorsList.add(sectionName+'_message_banner_text', 'message_banner_fg', 'Text of message banner').color property int pointSize: Units.dp * 9 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Misc/PanedStyle.qml000066400000000000000000000010711434616504300276560ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Paned' property int transitionDuration: 200 property QtObject handle: QtObject { property int width: 5 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_hovered', 'h').color property color normal: ColorsList.add(sectionName+'_normal', 'c').color property color pressed: ColorsList.add(sectionName+'_pressed', 'd').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Picker/000077500000000000000000000000001434616504300254165ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Picker/DatePickerStyle.qml000066400000000000000000000030751434616504300311720ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'DatePicker' property QtObject title: QtObject{ property color color: ColorsList.add(sectionName+'_title_fg', 'g').color property real pointSize: Units.dp * 11 } property QtObject cell: QtObject{ property color color: ColorsList.add(sectionName+'_cell_fg', 'g').color property color selectedBorderColor: ColorsList.add(sectionName+'_selected', 'i').color property real selectedPointSize: Units.dp * 14 property real dayHeaderPointSize: Units.dp * 12 property real dayPointSize: Units.dp * 11 } property QtObject nextMonthButton: QtObject { property int iconSize: 20 property string name : 'nextMonth' property string icon : 'panel_arrow_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 'l_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Picker/TimePickerStyle.qml000066400000000000000000000010441434616504300312050ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'TimePicker' property color hoursColor: ColorsList.add(sectionName+'_hours', 'i').color property color minutesColor: ColorsList.add(sectionName+'_minutes', 'i').color property color selectedItemColor: ColorsList.add(sectionName+'_selected', 'l').color property color unselectedItemColor: ColorsList.add(sectionName+'_unselected', 'g').color } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Popup/000077500000000000000000000000001434616504300253045ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Popup/PopupStyle.qml000066400000000000000000000010061434616504300301400ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Popup' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'k').color property QtObject shadow: QtObject { property color color: ColorsList.add(sectionName+'_shadow', 'l').color property int horizontalOffset: 2 property int radius: 10 property int samples: 15 property int verticalOffset: 2 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Tooltip/000077500000000000000000000000001434616504300256335ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Tooltip/TooltipStyle.qml000066400000000000000000000010371434616504300310220ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Tooltip' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'g').color property color color: ColorsList.add(sectionName, 'q').color property int arrowSize: 8 property int delay: 1000 property int pointSize: Units.dp * 9 property int margins: 8 property int padding: 4 property int radius: 4 property int minWidth: 130 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Window/000077500000000000000000000000001434616504300254505ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/Window/WindowStyle.qml000066400000000000000000000005001434616504300304460ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Window' property QtObject transientWindow: QtObject { property color color: ColorsList.add(sectionName+'_transient', 'l80').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Styles/qmldir000066400000000000000000000067741434616504300254320ustar00rootroot00000000000000# See: https://wiki.qt.io/Qml_Styling module Common.Styles # Components styles ------------------------------------------------------------ singleton BusyIndicatorStyle 1.0 Animations/BusyIndicatorStyle.qml singleton DialogStyle 1.0 Dialog/DialogStyle.qml singleton DateTimeDialogStyle 1.0 Dialog/DateTimeDialogStyle.qml singleton ActionBarStyle 1.0 Form/ActionBarStyle.qml singleton ActionSwitchStyle 1.0 Form/ActionSwitchStyle.qml singleton CheckBoxTextStyle 1.0 Form/CheckBoxTextStyle.qml singleton ComboBoxStyle 1.0 Form/ComboBoxStyle.qml singleton DroppableTextAreaStyle 1.0 Form/DroppableTextAreaStyle.qml singleton ListFormStyle 1.0 Form/ListFormStyle.qml singleton RadioButtonStyle 1.0 Form/RadioButtonStyle.qml singleton SearchBoxStyle 1.0 Form/SearchBoxStyle.qml singleton SliderStyle 1.0 Form/SliderStyle.qml singleton StackViewStyle 1.0 Form/StackViewStyle.qml singleton SwitchStyle 1.0 Form/SwitchStyle.qml singleton CommonItemDelegateStyle 1.0 Form/CommonItemDelegateStyle.qml singleton TransparentTextInputStyle 1.0 Form/TransparentTextInputStyle.qml singleton AbstractTextButtonStyle 1.0 Form/Buttons/AbstractTextButtonStyle.qml singleton ExclusiveButtonsStyle 1.0 Form/Buttons/ExclusiveButtonsStyle.qml singleton FileChooserButtonStyle 1.0 Form/Buttons/FileChooserButtonStyle.qml singleton SmallButtonStyle 1.0 Form/Buttons/SmallButtonStyle.qml singleton TextButtonAStyle 1.0 Form/Buttons/TextButtonAStyle.qml singleton TextButtonBStyle 1.0 Form/Buttons/TextButtonBStyle.qml singleton TextButtonCStyle 1.0 Form/Buttons/TextButtonCStyle.qml singleton NumericFieldStyle 1.0 Form/Fields/NumericFieldStyle.qml singleton TextAreaFieldStyle 1.0 Form/Fields/TextAreaFieldStyle.qml singleton TextFieldStyle 1.0 Form/Fields/TextFieldStyle.qml singleton FormHGroupStyle 1.0 Form/Placements/FormHGroupStyle.qml singleton FormLineStyle 1.0 Form/Placements/FormLineStyle.qml singleton FormStyle 1.0 Form/Placements/FormStyle.qml singleton FormTableLineStyle 1.0 Form/Placements/FormTableLineStyle.qml singleton FormTableStyle 1.0 Form/Placements/FormTableStyle.qml singleton FormVGroupStyle 1.0 Form/Placements/FormVGroupStyle.qml singleton TabButtonStyle 1.0 Form/Tab/TabButtonStyle.qml singleton TabContainerStyle 1.0 Form/Tab/TabContainerStyle.qml singleton MediaProgressBarStyle 1.0 Indicators/MediaProgressBarStyle.qml singleton RoundProgressBarStyle 1.0 Indicators/RoundProgressBarStyle.qml singleton VuMeterStyle 1.0 Indicators/VuMeterStyle.qml singleton ApplicationMenuStyle 1.0 Menus/ApplicationMenuStyle.qml singleton DropDownStaticMenuStyle 1.0 Menus/DropDownStaticMenuStyle.qml singleton MenuItemStyle 1.0 Menus/MenuItemStyle.qml singleton MenuStyle 1.0 Menus/MenuStyle.qml singleton ForceScrollBarStyle 1.0 Misc/ForceScrollBarStyle.qml singleton MessageBannerStyle 1.0 Misc/MessageBannerStyle.qml singleton PanedStyle 1.0 Misc/PanedStyle.qml singleton DatePickerStyle 1.0 Picker/DatePickerStyle.qml singleton TimePickerStyle 1.0 Picker/TimePickerStyle.qml singleton PopupStyle 1.0 Popup/PopupStyle.qml singleton TooltipStyle 1.0 Tooltip/TooltipStyle.qml singleton WindowStyle 1.0 Window/WindowStyle.qml linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Text/000077500000000000000000000000001434616504300236425ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Text/Text.qml000066400000000000000000000011441434616504300253010ustar00rootroot00000000000000import QtQuick 2.7 as QtQuick import QtQuick.Controls 2.2 import Common.Styles 1.0 QtQuick.Text { id: mainItem property bool computeFitWidth: false // Avoid doing computations if not needed property int fitWidth: 0 // Fit Width computation onTextChanged:{ if(computeFitWidth) { var lines = text.split('\n') var totalWidth = 0 for(var index in lines){ metrics.text = lines[index] if( totalWidth < metrics.width) totalWidth = metrics.width } fitWidth = totalWidth } } QtQuick.TextMetrics{ id: metrics font: mainItem.font } //----------------------------------- } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Tooltip/000077500000000000000000000000001434616504300243505ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Tooltip/Tooltip.qml000066400000000000000000000075621434616504300265270ustar00rootroot00000000000000import QtQuick 2.7 as Core import QtQuick.Controls 2.2 as Core import QtGraphicalEffects 1.12 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Utils 1.0 // ============================================================================= Core.ToolTip { id: tooltip property var _edge: 'left' // --------------------------------------------------------------------------- function _getArrowHeightMargin () { Utils.assert( icon.height >= icon.implicitHeight, '`icon.height` must be lower than `icon.implicitHeight`.' ) return (icon.height - icon.implicitHeight) / 2 } function _getArrowWidthMargin () { Utils.assert( icon.width >= icon.implicitWidth, '`icon.width` must be lower than `icon.implicitWidth`.' ) return (icon.width - icon.implicitWidth) / 2 } function _getRelativeXArrowCenter () { return (tooltip.parent ? tooltip.parent.width / 2 - icon.width / 2 : 0) } function _getRelativeYArrowCenter () { return (tooltip.parent ? tooltip.parent.height / 2 - icon.height / 2 : 0) } function _setArrowEdge () { var a = container.mapToItem(null, 0, 0) var b = (tooltip.parent ?tooltip.parent.mapToItem(null, 0, 0) : {x:0,y:0}) if (a.x + container.width < b.x) { _edge = 'left' } else if (a.x > b.x + container.width) { _edge = 'right' } else if (a.y + container.height < b.y) { _edge = 'top' } else if (a.y > b.y + b.height) { _edge = 'bottom' } else { // Unable to get the tooltip arrow position. _edge = null } } // Called when new image is loaded. (When the is edge is updated.) function _setArrowPosition () { var a = container.mapToItem(null, 0, 0) var b = (tooltip.parent ?tooltip.parent.mapToItem(null, 0, 0) : {x:0,y:0}) if (_edge === 'left') { icon.x = container.width - TooltipStyle.margins - _getArrowWidthMargin() icon.y = b.y - a.y + _getRelativeYArrowCenter() } else if (_edge === 'right') { icon.x = container.width + TooltipStyle.margins + _getArrowWidthMargin() icon.y = b.y - a.y + _getRelativeYArrowCenter() } else if (_edge === 'top') { icon.x = b.x - a.x + _getRelativeXArrowCenter() icon.y = container.height - TooltipStyle.margins - _getArrowHeightMargin() } else if (_edge === 'bottom') { icon.x = b.x - a.x + _getRelativeXArrowCenter() icon.y = container.height + TooltipStyle.margins + _getArrowHeightMargin() } } // --------------------------------------------------------------------------- background: Core.Item { id: container layer { enabled: true effect: PopupShadow {} } Core.Rectangle { anchors { fill: parent margins: TooltipStyle.margins } color: TooltipStyle.backgroundColor radius: TooltipStyle.radius } // Do not use `Icon` component to access to `implicitHeight` // and `implicitWidth`. Core.Image { id: icon mipmap: SettingsModel.mipmapEnabled fillMode: Core.Image.PreserveAspectFit height: TooltipStyle.arrowSize source: _edge ? Utils.resolveImageUri('tooltip_arrow_' + _edge+'_custom') : '' sourceSize.height: height sourceSize.width: width layer { enabled: true effect: ColorOverlay { color: TooltipStyle.backgroundColor } } visible: tooltip.visible && _edge width: TooltipStyle.arrowSize z: Constants.zMax onStatusChanged: status === Core.Image.Ready && _setArrowPosition() } } contentItem: Core.Text { id: text color: TooltipStyle.color font.pointSize: TooltipStyle.pointSize padding: TooltipStyle.padding + TooltipStyle.margins text: tooltip.text wrapMode: Core.Text.WordWrap width:parent.width elide:Core.Text.ElideRight } delay: TooltipStyle.delay onVisibleChanged: visible && _setArrowEdge() } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Tooltip/TooltipArea.qml000066400000000000000000000032211434616504300273040ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Common.Styles 1.0 // ============================================================================= MouseArea { id: tooltipArea property alias text: tooltip.text property int delay: TooltipStyle.delay property bool force: false property var tooltipParent: parent property int maxWidth : window? window.width : tooltipParent.width property bool _visible: false property int hoveringCursor : Qt.PointingHandCursor property bool isClickable : false function show(){ if(isClickable){ if(tooltip.delay>0) { tooltip.oldDelay = tooltip.delay tooltip.delay = 0 } tooltip.show(text, -1); } } anchors.fill:parent hoverEnabled: true scrollGestureEnabled: true onContainsMouseChanged: _visible = containsMouse cursorShape: containsMouse ? hoveringCursor : Qt.ArrowCursor onPressed: { mouse.accepted = isClickable } onWheel: { _visible = false wheel.accepted = false } onClicked:{ show() mouse.accepted = false } Tooltip { id: tooltip property int oldDelay : 0 delay: tooltipArea.delay parent: tooltipParent visible: _visible || force width: Math.min(tooltip.implicitWidth, Math.max(tooltipArea.maxWidth, TooltipStyle.minWidth)) //tooltipParent.width>TooltipStyle.minWidth?tooltipParent.width:TooltipStyle.minWidth timeout: -1 // Workaround to always display tooltip. onVisibleChanged: { if (!visible && force) { tooltip.visible = true } } MouseArea{ anchors.fill:parent visible: tooltipArea.isClickable onClicked : { tooltip.hide() tooltip.delay = tooltip.oldDelay mouse.accepted = false } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/View/000077500000000000000000000000001434616504300236305ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/View/ScrollableListView.qml000066400000000000000000000042651434616504300301230ustar00rootroot00000000000000import QtQuick 2.12 //synchronousDrag import QtQuick.Controls 2.2 import Common 1.0 // ============================================================================= ListView { id: view property bool hideScrollBars: false property alias verticalScrollPolicy : vScrollBar.policy property alias horizontalScrollPolicy : hScrollBar.policy property bool fitCacheToContent: true function getVisibleIndex(checkMax) { var center_x = view.x + view.width / 2 var index = -1 var yCheck = 0 var direction = checkMax ? -1 : 1 var yStart = view.y + view.contentY + (checkMax ? view.height : 0) var yStep = 5 while(index<0 && yCheck < view.height){ index = indexAt( center_x, yStart + yCheck * direction) yCheck += yStep } return index } function getVisibleIndexRange() { return [getVisibleIndex(0), getVisibleIndex(1)] } function isIndexVisible(index){ return getVisibleIndex(0) <= index && index <= getVisibleIndex(1) } function isIndexAfter(index){ return getVisibleIndex(1) < index } // --------------------------------------------------------------------------- ScrollBar.vertical: ForceScrollBar { id: vScrollBar onPressedChanged: pressed ? view.movementStarted() : view.movementEnded() contentSizeTarget: view.contentHeight sizeTarget: view.height } ScrollBar.horizontal: ForceScrollBar { id: hScrollBar onPressedChanged: pressed ? view.movementStarted() : view.movementEnded() contentSizeTarget: view.contentWidth sizeTarget: view.width } // --------------------------------------------------------------------------- boundsMovement: Flickable.StopAtBounds boundsBehavior: Flickable.DragOverBounds clip: true contentWidth: width - (vScrollBar.visible?vScrollBar.width:0) contentHeight: height - (hScrollBar.visible?hScrollBar.height:0) spacing: 0 synchronousDrag: true onContentHeightChanged: { if(fitCacheToContent) cacheBuffer= (view.contentHeight > 0 ? view.contentHeight : 0) } cacheBuffer: height > 0 ? height : 0 // --------------------------------------------------------------------------- // TODO: Find a solution at this bug => // https://bugreports.qt.io/browse/QTBUG-31573 // https://bugreports.qt.io/browse/QTBUG-49989 } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Window/000077500000000000000000000000001434616504300241655ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Window/ApplicationWindow.qml000066400000000000000000000020001434616504300303230ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import 'Window.js' as Logic // ============================================================================= ApplicationWindow { id: window default property alias _content: content.data readonly property bool virtualWindowVisible: virtualWindow.active // --------------------------------------------------------------------------- signal attachedVirtualWindow signal detachedVirtualWindow // --------------------------------------------------------------------------- function attachVirtualWindow (component, properties, exitStatusHandler) { return Logic.attachVirtualWindow.call(this, component, properties, exitStatusHandler) } function detachVirtualWindow () { Logic.detachVirtualWindow() } // --------------------------------------------------------------------------- Item { anchors.fill: parent Rectangle { id: content anchors.fill: parent } VirtualWindow { id: virtualWindow } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Window/VirtualWindow.qml000066400000000000000000000075451434616504300275310ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.5 import Common 1.0 import Common.Styles 1.0 import 'Window.js' as Logic import Linphone 1.0 as M // ============================================================================= StackView{ id:stackView anchors.fill: parent property var active : !stackView.empty visible:!stackView.empty function setContent(url, properties, exitStatusHandler){ if(properties && properties.virtualWindowHash){ var haveItem = stackView.find(function(item, index) { return item.sourceProperties && item.sourceProperties.virtualWindowHash && item.sourceProperties.virtualWindowHash == properties.virtualWindowHash; }); if( haveItem == null ){//Push new push(page, {"sourceUrl":url, "sourceProperties":properties, "exitStatusHandler":exitStatusHandler, "setData":true, "active":true}); }else{//Update fields haveItem.sourceProperties = properties; haveItem.exitStatusHandler = exitStatusHandler; } }else{ push(page, {"sourceUrl":url, "sourceProperties":properties, "exitStatusHandler":exitStatusHandler, "setData":true, "active":true}); } return stackView.currentItem } function unsetContent () { if(stackView.depth == 1) clear(); else pop(); return stackView.empty; } Component{ id:page Loader { id:mainLoader active:false property var sourceUrl property var sourceProperties property var exitStatusHandler property bool setData : false // USe this flag to update source data // --------------------------------------------------------------------------- sourceComponent:Component{ id:mainComponent Item{ id:rootContent property alias contentLoader:content.contentLoader anchors.fill: parent MouseArea { anchors.fill: parent hoverEnabled: true onWheel: wheel.accepted = true cursorShape: Qt.ArrowCursor } Rectangle { id: content property alias contentLoader:contentLoader anchors.fill: parent color: WindowStyle.transientWindow.color Loader{ id:contentLoader anchors.centerIn: parent property var setSourceData : setData onSetSourceDataChanged: if( setData) {// SetData is true : assign source with properties using QML functions if(sourceProperties) setSource(sourceUrl, sourceProperties); else setSource(sourceUrl); }else{source=undefined}// SetData is false : clean memory active:mainLoader.active onLoaded:{// When loaded, attach handlers to content item.exitStatus.connect(Logic.detachVirtualWindow) if (exitStatusHandler) { item.exitStatus.connect(exitStatusHandler) } } } } } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Window/Window.js000066400000000000000000000030441434616504300257730ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // Windows (qml) Logic. // ============================================================================= .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= // Create a dynamic component hover the main content of one window. // The object parameter must have a `exitStatus` signal which is used // at item destruction. // // The exit status handler is optional. function attachVirtualWindow (component, properties, exitStatusHandler) { return virtualWindow.setContent(component, properties, exitStatusHandler); } function detachVirtualWindow () { if( virtualWindow.active) virtualWindow.unsetContent(); } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/Window/Window.qml000066400000000000000000000017761434616504300261620ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Window 2.2 import 'Window.js' as Logic // ============================================================================= Window { id: window default property alias _content: content.data readonly property bool virtualWindowVisible: virtualWindow.active //visible // --------------------------------------------------------------------------- signal attachedVirtualWindow signal detachedVirtualWindow // --------------------------------------------------------------------------- function attachVirtualWindow (component, properties, exitStatusHandler) { return Logic.attachVirtualWindow.call(this, component, properties, exitStatusHandler) } function detachVirtualWindow () { Logic.detachVirtualWindow() } // --------------------------------------------------------------------------- Item { anchors.fill: parent Rectangle { id: content anchors.fill: parent } VirtualWindow { id: virtualWindow } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Common/qmldir000066400000000000000000000105661434616504300241410ustar00rootroot00000000000000# ============================================================================== # Common's components to export. # ============================================================================== module Common # Constants -------------------------------------------------------------------- singleton Constants 1.0 Constants/Constants.qml # Components ------------------------------------------------------------------- BusyIndicator 1.0 Animations/BusyIndicator.qml DateTimeDialog 1.0 Dialog/DateTimeDialog.qml DialogDescription 1.0 Dialog/DialogDescription.qml ConfirmDialog 1.0 Dialog/ConfirmDialog.qml DialogPlus 1.0 Dialog/DialogPlus.qml ActionBar 1.0 Form/ActionBar.qml ActionButton 1.0 Form/ActionButton.qml ActionSwitch 1.0 Form/ActionSwitch.qml CheckBoxText 1.0 Form/CheckBoxText.qml ComboBox 1.0 Form/ComboBox.qml CommonItemDelegate 1.0 Form/CommonItemDelegate.qml DroppableTextArea 1.0 Form/DroppableTextArea.qml ListForm 1.0 Form/ListForm.qml ListItemSelector 1.0 Form/ListItemSelector.qml Mosaic 1.0 Form/Mosaic.qml MouseArea 1.0 Form/MouseArea.qml RadioButton 1.0 Form/RadioButton.qml SearchBox 1.0 Form/SearchBox.qml Slider 1.0 Form/Slider.qml StackView 1.0 Form/StackView.qml StaticListForm 1.0 Form/StaticListForm.qml Switch 1.0 Form/Switch.qml TransparentTextInput 1.0 Form/TransparentTextInput.qml ExclusiveButtons 1.0 Form/Buttons/ExclusiveButtons.qml FileChooserButton 1.0 Form/Buttons/FileChooserButton.qml TextButtonA 1.0 Form/Buttons/TextButtonA.qml TextButtonB 1.0 Form/Buttons/TextButtonB.qml TextButtonC 1.0 Form/Buttons/TextButtonC.qml HexField 1.0 Form/Fields/HexField.qml NumericField 1.0 Form/Fields/NumericField.qml PasswordField 1.0 Form/Fields/PasswordField.qml PortField 1.0 Form/Fields/PortField.qml ScrollableListViewField 1.0 Form/Fields/ScrollableListViewField.qml TextAreaField 1.0 Form/Fields/TextAreaField.qml TextField 1.0 Form/Fields/TextField.qml Form 1.0 Form/Placements/Form.qml FormEmptyLine 1.0 Form/Placements/FormEmptyLine.qml FormGroup 1.0 Form/Placements/FormGroup.qml FormHGroup 1.0 Form/Placements/FormHGroup.qml FormVGroup 1.0 Form/Placements/FormVGroup.qml FormLine 1.0 Form/Placements/FormLine.qml FormTable 1.0 Form/Placements/FormTable.qml FormTableEntry 1.0 Form/Placements/FormTableEntry.qml FormTableLine 1.0 Form/Placements/FormTableLine.qml TabBar 1.0 Form/Tab/TabBar.qml TabButton 1.0 Form/Tab/TabButton.qml TabContainer 1.0 Form/Tab/TabContainer.qml DragBox 1.0 Helpers/DragBox.qml InvertedMouseArea 1.0 Helpers/InvertedMouseArea.qml Icon 1.0 Image/Icon.qml RoundedImage 1.0 Image/RoundedImage.qml MediaProgressBar 1.0 Indicators/MediaProgressBar.qml RoundProgressBar 1.0 Indicators/RoundProgressBar.qml VuMeter 1.0 Indicators/VuMeter.qml ApplicationMenu 1.0 Menus/ApplicationMenu.qml ApplicationMenuEntry 1.0 Menus/ApplicationMenuEntry.qml DropDownDynamicMenu 1.0 Menus/DropDownDynamicMenu.qml DropDownStaticMenu 1.0 Menus/DropDownStaticMenu.qml DropDownStaticMenuEntry 1.0 Menus/DropDownStaticMenuEntry.qml Menu 1.0 Menus/Menu.qml MenuItem 1.0 Menus/MenuItem.qml Borders 1.0 Misc/Borders.qml ForceScrollBar 1.0 Misc/ForceScrollBar.qml MessageBanner 1.0 Misc/MessageBanner.qml Paned 1.0 Misc/Paned.qml DatePicker 1.0 Picker/DatePicker.qml TimePicker 1.0 Picker/TimePicker.qml DesktopPopup 1.0 Popup/DesktopPopup.qml Popup 1.0 Popup/Popup.qml PopupShadow 1.0 Popup/PopupShadow.qml Text 1.0 Text/Text.qml TooltipArea 1.0 Tooltip/TooltipArea.qml ToolTip 1.0 Tooltip/Tooltip.qml ScrollableListView 1.0 View/ScrollableListView.qml ApplicationWindow 1.0 Window/ApplicationWindow.qml Window 1.0 Window/Window.qml linphone-desktop-5.0.2/linphone-app/ui/modules/Konami/000077500000000000000000000000001434616504300227045ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Konami/Konami.qml000066400000000000000000000042421434616504300246370ustar00rootroot00000000000000/* * Konami.qml * Copyright 2017-2018 Ronan Abhamon (https://github.com/Wescoeur) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ import QtQuick 2.7 // ============================================================================= Item { id: konami property var code: [ Qt.Key_Up, Qt.Key_Up, Qt.Key_Down, Qt.Key_Down, Qt.Key_Left, Qt.Key_Right, Qt.Key_Left, Qt.Key_Right, Qt.Key_B, Qt.Key_A ] property int delay: 2000 signal triggered // --------------------------------------------------------------------------- Keys.forwardTo: core Timer { id: timer interval: konami.delay onTriggered: core.index = 0 } Item { id: core property int index: 0 anchors.fill: parent Keys.onPressed: { timer.stop() var code = konami.code if (event.key === code[index]) { // 1. Code OK. if (++index === code.length) { index = 0 konami.triggered() return } // 2. Actual key OK. if (delay > 0) { timer.start() } return } // 3. Wrong key. index = 0 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Konami/qmldir000066400000000000000000000001671434616504300241230ustar00rootroot00000000000000module Konami // ============================================================================= Konami 1.0 Konami.qml linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/000077500000000000000000000000001434616504300232425ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Account/000077500000000000000000000000001434616504300246365ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Account/AccountStatus.qml000066400000000000000000000063241434616504300301560ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 // ============================================================================= Item { id: accountStatus Layout.fillWidth: true Layout.fillHeight: true // --------------------------------------------------------------------------- signal clicked property alias cursorShape:mouseArea.cursorShape property alias betterIcon : presenceLevel.betterIcon // --------------------------------------------------------------------------- MouseArea { id:mouseArea anchors.fill:parent onClicked: accountStatus.clicked() } ColumnLayout { anchors.fill:parent spacing: AccountStatusStyle.verticalSpacing RowLayout { Layout.preferredHeight: parent.height / 2 Layout.maximumWidth: parent.width Layout.alignment: Qt.AlignBottom | Qt.AlignLeft spacing: AccountStatusStyle.horizontalSpacing Item { Layout.alignment: Qt.AlignBottom | Qt.AlignLeft Layout.bottomMargin: AccountStatusStyle.presenceLevel.bottomMargin Layout.preferredHeight: AccountStatusStyle.presenceLevel.size Layout.preferredWidth: AccountStatusStyle.presenceLevel.size PresenceLevel { id:presenceLevel anchors.fill:parent level: OwnPresenceModel.presenceStatus===Presence.Offline?Presence.White:( SettingsModel.rlsUriEnabled ? OwnPresenceModel.presenceLevel : Presence.Green) visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateRegistered } BusyIndicator { anchors.fill:parent running: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateInProgress color: AccountStatusStyle.busyColor } Icon { iconSize: parent.width icon: 'generic_error' visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateNotRegistered || AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateNoProxy TooltipArea{ text : 'Not Registered' } } } Text { id:username Layout.alignment: Qt.AlignBottom | Qt.AlignLeft color: AccountStatusStyle.username.color elide: Text.ElideRight font.bold: true font.pointSize: AccountStatusStyle.username.pointSize text: AccountSettingsModel.username verticalAlignment: Text.AlignBottom } Item { Layout.alignment: Qt.AlignBottom | Qt.AlignLeft Layout.bottomMargin: 5 Layout.preferredHeight: AccountStatusStyle.presenceLevel.size Layout.preferredWidth: AccountStatusStyle.presenceLevel.size MessageCounter { id: messageCounter anchors.fill: parent count: CoreManager.eventCount MouseArea{ anchors.fill: parent onClicked: window.setView('HistoryView') } } } Item{//Spacer Layout.fillHeight: true Layout.fillWidth: true } }//RowLayout Text { Layout.preferredHeight:parent.height / 2 Layout.preferredWidth:parent.width color: AccountStatusStyle.sipAddress.color elide: Text.ElideRight font.pointSize: AccountStatusStyle.sipAddress.pointSize text: AccountSettingsModel.sipAddress verticalAlignment: Text.AlignTop } }//ColumnLayout } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Blocks/000077500000000000000000000000001434616504300244575ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Blocks/CardBlock.qml000066400000000000000000000030321434616504300270140ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone.Styles 1.0 // ============================================================================= Column { default property alias _content: content.data property alias icon: icon.icon property alias title: title.text property alias description: description.text // --------------------------------------------------------------------------- spacing: CardBlockStyle.spacing width: CardBlockStyle.width Icon { id: icon iconSize: CardBlockStyle.icon.size height: CardBlockStyle.icon.size + CardBlockStyle.icon.bottomMargin width: parent.width } Column { spacing: CardBlockStyle.title.bottomMargin width: parent.width Text { id: title color: CardBlockStyle.title.color elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap font { bold: true pointSize: CardBlockStyle.title.pointSize } height: CardBlockStyle.title.height width: parent.width } Text { id: description color: CardBlockStyle.description.color elide: Text.ElideRight font.pointSize: CardBlockStyle.description.pointSize horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap height: CardBlockStyle.description.height width: parent.width } } // --------------------------------------------------------------------------- Item { id: content height: CardBlockStyle.content.height width: parent.width } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Blocks/RequestBlock.qml000066400000000000000000000024241434616504300275770ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone.Styles 1.0 // ============================================================================= Column { id: block property var action readonly property alias loading: block._loading property bool _loading: false // ---------------------------------------------------------------------------- function execute () { block._loading = true action() } function setText(txt){ errorBlock.text = txt } function stop (error) { errorBlock.text = error block._loading = false } // ---------------------------------------------------------------------------- TextEdit { id: errorBlock readOnly: true selectByMouse: true color: RequestBlockStyle.error.color font { italic: true pointSize: RequestBlockStyle.error.pointSize } height: visible ? undefined : 0 width: parent.width horizontalAlignment: Text.AlignHCenter padding: RequestBlockStyle.error.padding wrapMode: Text.WordWrap visible: !block.loading && errorBlock.text != '' } BusyIndicator { id: busy anchors { horizontalCenter: parent.horizontalCenter } height: visible ? RequestBlockStyle.loadingIndicator.height : 0 width: RequestBlockStyle.loadingIndicator.width running: block.loading } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Calls/000077500000000000000000000000001434616504300243005ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Calls/CallControls.qml000066400000000000000000000030721434616504300274140ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 // ============================================================================= Rectangle { id: callControls // --------------------------------------------------------------------------- default property alias _content: content.data property alias isDarkMode: contact.isDarkMode property alias signIcon: signIcon.icon property alias subtitleColor: contact.subtitleColor property alias titleColor: contact.titleColor property string peerAddress property string localAddress property string fullPeerAddress property string fullLocalAddress property var entry // --------------------------------------------------------------------------- signal clicked // --------------------------------------------------------------------------- color: CallControlsStyle.color height: CallControlsStyle.height MouseArea { anchors.fill: parent onClicked: callControls.clicked() } Icon { id: signIcon anchors { left: parent.left top: parent.top } iconSize: CallControlsStyle.signSize } RowLayout { anchors { fill: parent leftMargin: CallControlsStyle.leftMargin rightMargin: CallControlsStyle.rightMargin } spacing: 0 Contact { id: contact Layout.fillHeight: true Layout.fillWidth: true displayUnreadMessageCount: true entry: callControls.entry } Item { id: content Layout.fillHeight: true Layout.preferredWidth: callControls._content[0].width } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Calls/CallStatistics.qml000066400000000000000000000111101434616504300277330ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 // ============================================================================= Popup { id: callStatistics property var call backgroundPopup: CallStatisticsStyle.outsideColor showShadow: false // if true, we get a brownish/yollow color due to alphas // --------------------------------------------------------------------------- delayClosing: true Item{ height: callStatistics.height width: callStatistics.width MouseArea{ anchors.fill: parent onClicked: callStatistics.close() } Rectangle { color: CallStatisticsStyle.color anchors.fill: parent anchors.topMargin: CallStatisticsStyle.popup.topMargin anchors.bottomMargin: CallStatisticsStyle.popup.bottomMargin anchors.leftMargin: CallStatisticsStyle.popup.leftMargin anchors.rightMargin: CallStatisticsStyle.popup.rightMargin radius: 10 RowLayout { id: mainLayout anchors { fill: parent topMargin: CallStatisticsStyle.topMargin leftMargin: CallStatisticsStyle.leftMargin rightMargin: CallStatisticsStyle.rightMargin } Layout.alignment: Qt.AlignCenter Item{ Layout.preferredWidth: videoLoader.sourceComponent ? 0 : parent.width /7 Layout.fillHeight: true } Item{ Layout.fillWidth: true Layout.fillHeight: true Column{ anchors.fill: parent spacing: 30 Loader { property string $label: qsTr('audioStatsLabel') property var $data: callStatistics.call?callStatistics.call.audioStats:null property bool $fillLayout: !encryptionLoader.active sourceComponent: media width: parent.width } Loader { id: encryptionLoader //: 'Media encryption' : title in call statistics for the encryption section property string $label: qsTr('mediaEncryptionLabel') property var $data: callStatistics.call ? callStatistics.call.encryptionStats : null sourceComponent: callStatistics.call && callStatistics.call.isSecured ? media : undefined width: parent.width } } } Item{ Layout.fillWidth: videoLoader.sourceComponent Layout.fillHeight: true Loader { id: videoLoader property string $label: qsTr('videoStatsLabel') property var $data: callStatistics.call?callStatistics.call.videoStats:null sourceComponent: callStatistics.call && callStatistics.call.videoEnabled ? media : undefined width: sourceComponent ? parent.width : 0 } } Item{ Layout.preferredWidth: videoLoader.sourceComponent ? 0 : parent.width /7 Layout.fillHeight: true } } // ------------------------------------------------------------------------- // Line. // ------------------------------------------------------------------------- Component { id: line RowLayout { spacing: CallStatisticsStyle.spacing width: parent ? parent.width : undefined Text { Layout.preferredWidth: CallStatisticsStyle.key.width color: CallStatisticsStyle.key.color elide: Text.ElideRight font { pointSize: CallStatisticsStyle.key.pointSize bold: true } horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter text: modelData.key } Text { Layout.fillWidth: true color: CallStatisticsStyle.value.color elide: Text.ElideRight font.pointSize: CallStatisticsStyle.value.pointSize text: modelData.value } } } // ------------------------------------------------------------------------- // Media. // ------------------------------------------------------------------------- Component { id: media Column{ width: parent.width Text { color: CallStatisticsStyle.title.color font { bold: true pointSize: CallStatisticsStyle.title.pointSize } elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter text: $label height: contentHeight + CallStatisticsStyle.title.bottomMargin width: parent.width } Repeater { model: $data delegate: line } } } ActionButton{ id: closeButton anchors.top: parent.top anchors.right: parent.right anchors.topMargin: 10 anchors.rightMargin: 10 isCustom: true backgroundRadius: width/2 colorSet: CallStatisticsStyle.cancel onClicked: callStatistics.close() } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Calls/Calls.js000066400000000000000000000146111434616504300256770ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `Calls.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone // ============================================================================= // ----------------------------------------------------------------------------- // Helpers. // ----------------------------------------------------------------------------- function getParams (call) { if (!call) { return } var CallModel = Linphone.CallModel var status = call.status if (status === CallModel.CallStatusConnected) { var optActions = [] if (Linphone.SettingsModel.callPauseEnabled) { optActions.push({ handler: (function () { call.pausedByUser = true }), name: qsTr('callPause') }) } if (call.transferAddress !== '') { optActions.push({ handler: call.askForTransfer, //: 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. name: qsTr('attendedTransferComplete') }) } else { optActions.push({ handler: call.askForTransfer, name: qsTr('transferCall') }) optActions.push({ handler: call.askForAttendedTransfer, //: 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. name: qsTr('attendedTransferCall') }) } return { actions: optActions.concat([{ handler: call.terminate, name: qsTr('terminateCall') }]), component: callActions, string: 'connected' } } if (status === CallModel.CallStatusEnded) { return { string: 'ended' } } if (status === CallModel.CallStatusIncoming) { var optActions = [] if (Linphone.SettingsModel.videoSupported) { optActions.push({ handler: call.acceptWithVideo, name: qsTr('acceptVideoCall') }) } return { actions: [{ handler: (function () { call.accept() }), name: qsTr('acceptAudioCall') }].concat(optActions).concat([{ handler: call.terminate, name: qsTr('terminateCall') }]), component: callActions, string: 'incoming' } } if (status === CallModel.CallStatusOutgoing) { return { component: callAction, handler: call.terminate, useIcon: 1, string: 'outgoing' } } if (status === CallModel.CallStatusPaused) { var optActions = [] if (call.pausedByUser) { optActions.push({ handler: (function () { call.pausedByUser = false }), name: qsTr('resumeCall').toUpperCase() }) } else if (Linphone.SettingsModel.callPauseEnabled) { optActions.push({ handler: (function () { call.pausedByUser = true }), name: qsTr('callPause').toUpperCase() }) } if (call.transferAddress !== '') { optActions.push({ handler: call.askForTransfer, //: 'COMPLETE ATTENDED TRANSFER' : Title button, design is in uppercase. name: qsTr('attendedTransferComplete').toUpperCase() }) } else { optActions.push({ handler: call.askForTransfer, name: qsTr('transferCall').toUpperCase() }) optActions.push({ handler: call.askForAttendedTransfer, //: 'ATTENDED TRANSFER CALL' : Title button, design is in uppercase. name: qsTr('attendedTransferCall').toUpperCase() }) } return { actions: optActions.concat([{ handler: call.terminate, name: qsTr('terminateCall').toUpperCase() }]), component: callActions, string: 'paused' } } } function updateSelectedCall (call, index) { if(index != undefined){ calls._selectedCall = call ? call : null if (index != null) { calls.currentIndex = index } } } function resetSelectedCall () { updateSelectedCall(null, -1) } function setIndexWithCall (call) { var count = calls.count var model = calls.model for (var i = 0; i < count; i++) { if (call === model.data(model.index(i, 0))) { updateSelectedCall(call, i) return } } updateSelectedCall(call, -1) } // ----------------------------------------------------------------------------- // View handlers. // ----------------------------------------------------------------------------- function handleCountChanged (count) { if (count === 0) { return } var call = calls._selectedCall if (call == null) { if (calls.conferenceModel.count > 0) { return } var model = calls.model var index = count - 1 if(model){ var callModel = model.data(model.index(index, 0)) if( callModel.status === Linphone.CallModel.CallStatusConnected || callModel.isOutgoing ) updateSelectedCall(callModel, index) } } else { setIndexWithCall(call) } } // ----------------------------------------------------------------------------- // Model handlers. // ----------------------------------------------------------------------------- function handleCallRunning (call) { if (!call.isInConference) { setIndexWithCall(call) } } function handleRowsAboutToBeRemoved (_, first, last) { var index = calls.currentIndex if (index >= first && index <= last) { resetSelectedCall() } } function handleRowsInserted (_, first, last) { // The last inserted outgoing element become the selected call. var model = calls.model for (var index = last; index >= first; index--) { var call = model.data(model.index(index, 0)) if (call.isOutgoing && !call.isInConference) { updateSelectedCall(call) return } } // First received call. if (first === 0 && model.rowCount() === 1) { var call = model.data(model.index(0, 0)) if (!call.isInConference) { updateSelectedCall(model.data(model.index(0, 0))) } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Calls/Calls.qml000066400000000000000000000130721434616504300260540ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import ColorsList 1.0 import 'Calls.js' as Logic // ============================================================================= ListView { id: calls // --------------------------------------------------------------------------- readonly property CallModel selectedCall: calls._selectedCall property var conferenceModel property CallModel _selectedCall: null property var lastCall onSelectedCallChanged: if( selectedCall) lastCall = selectedCall // --------------------------------------------------------------------------- boundsBehavior: Flickable.StopAtBounds clip: true spacing: 0 // --------------------------------------------------------------------------- function refreshCall(){ Logic.resetSelectedCall() } function refreshLastCall(){ if(lastCall && lastCall.status === CallModel.CallStatusConnected) Logic.setIndexWithCall(lastCall) else{ for(var i = 0 ; i < model.rowCount() ; ++i){ var call = model.data(model.index(i, 0)) if( call && call.status === CallModel.CallStatusConnected){ Logic.updateSelectedCall(call, i) return; } } } } onCountChanged: Logic.handleCountChanged(count) Connections { target: model onCallRunning: Logic.handleCallRunning(callModel) onRowsAboutToBeRemoved: Logic.handleRowsAboutToBeRemoved(parent, first, last) onRowsInserted: Logic.handleRowsInserted(parent, first, last) } // --------------------------------------------------------------------------- Component { id: callAction ActionButton { isCustom: true backgroundRadius: 90 Component.onCompleted: params.useIcon == 1 ? colorSet = CallsStyle.entry.hangup : errorIcon.visible=true onClicked: params.handler() Icon{ id: errorIcon icon: 'generic_error' iconSize: CallsStyle.entry.iconActionSize visible: false } } } Component { id: callActions ActionButton { id: button property bool isSelected : calls.currentIndex === callId && call.status !== CallModel.CallStatusEnded isCustom: true backgroundRadius: 4 colorSet: isSelected ? CallsStyle.entry.selectedBurgerMenu : CallsStyle.entry.burgerMenu onClicked: menu.open() DropDownStaticMenu { id: menu relativeTo: callControls relativeX: callControls.width entryHeight: CallsStyle.entry.height entryWidth: maxWidth property int maxWidth : CallsStyle.entry.width Repeater { model: params ? params.actions : [] DropDownStaticMenuEntry { entryName: modelData.name Component.onCompleted: if( menu.maxWidth < implicitWidth ) menu.maxWidth = implicitWidth onClicked: { menu.close() params.actions[index].handler() } } } } } } // --------------------------------------------------------------------------- // Conference. // --------------------------------------------------------------------------- header: ConferenceControls { readonly property bool isSelected: calls.currentIndex === -1 && calls._selectedCall == null height: visible ? ConferenceControlsStyle.height : 0 width: parent.width visible: calls.conferenceModel.count > 0 color: isSelected ? CallsStyle.entry.color.selected : CallsStyle.entry.color.normal textColor: isSelected ? CallsStyle.entry.titleColor.selected : CallsStyle.entry.titleColor.normal onClicked: Logic.resetSelectedCall() onVisibleChanged: !visible && Logic.handleCountChanged(calls.count) } // --------------------------------------------------------------------------- // Calls. // --------------------------------------------------------------------------- delegate: CallControls { id: _callControls // ------------------------------------------------------------------------- isDarkMode: calls.currentIndex === index && $modelData!=undefined && $modelData.status!=undefined && $modelData.status !== CallModel.CallStatusEnded // ------------------------------------------------------------------------- color: isDarkMode ? CallsStyle.entry.color.selected : CallsStyle.entry.color.normal subtitleColor: isDarkMode ? CallsStyle.entry.subtitleColor.selected : CallsStyle.entry.subtitleColor.normal titleColor: isDarkMode ? CallsStyle.entry.titleColor.selected : CallsStyle.entry.titleColor.normal signIcon: { var params = loader.params return params ? 'call_sign_' + params.string : '' } entry: $modelData width: calls.width onClicked: { if ($modelData.status !== CallModel.CallStatusEnded) { Logic.updateSelectedCall($modelData, index) } } // ------------------------------------------------------------------------- Loader { id: loader readonly property int callId: index readonly property var call: $modelData readonly property var callControls: _callControls readonly property var params: Logic.getParams($modelData) anchors.centerIn: parent sourceComponent: params ? params.component : null } SequentialAnimation on color { loops: CallsStyle.entry.endCallAnimation.loops running: !$modelData || $modelData.status === CallModel.CallStatusEnded ColorAnimation { duration: CallsStyle.entry.endCallAnimation.duration from: CallsStyle.entry.color.normal to: CallsStyle.entry.endCallAnimation.blinkColor } ColorAnimation { duration: CallsStyle.entry.endCallAnimation.duration from: CallsStyle.entry.endCallAnimation.blinkColor to: CallsStyle.entry.color.normal } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Calls/ConferenceControls.qml000066400000000000000000000020551434616504300306100ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 // ============================================================================= Rectangle { id: callControls // --------------------------------------------------------------------------- property alias textColor: text.color // --------------------------------------------------------------------------- signal clicked // --------------------------------------------------------------------------- color: ConferenceControlsStyle.color height: ConferenceControlsStyle.height MouseArea { anchors.fill: parent onClicked: callControls.clicked() } RowLayout { anchors { fill: parent leftMargin: ConferenceControlsStyle.leftMargin rightMargin: ConferenceControlsStyle.rightMargin } spacing: 0 Text { id: text Layout.fillHeight: true Layout.fillWidth: true font.bold: true text: qsTr('conference') verticalAlignment: Text.AlignVCenter } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Calls/IncallAvatar.qml000066400000000000000000000037041434616504300273600ustar00rootroot00000000000000import QtQuick 2.7 import Linphone 1.0 import UtilsCpp 1.0 import App.Styles 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Avatar { id: mainItem property var call property var participantDeviceModel property ConferenceInfoModel conferenceInfoModel property bool isPreview: false property var _sipAddressObserver: participantDeviceModel ? SipAddressesModel.getSipAddressObserver(participantDeviceModel.address, '') : isPreview ? SipAddressesModel.getSipAddressObserver( AccountSettingsModel.fullSipAddress, AccountSettingsModel.fullSipAddress) : call ? SipAddressesModel.getSipAddressObserver( call.fullPeerAddress, call.fullLocalAddress) : null property var _username: conferenceInfoModel ? conferenceInfoModel.subject : _sipAddressObserver ? UtilsCpp.getDisplayName(_sipAddressObserver.peerAddress) : '' property bool isPaused: (call && (call.status === CallModel.CallStatusPaused)) || (participantDeviceModel && participantDeviceModel.isPaused) || false Component.onDestruction: _sipAddressObserver=null// Need to set it to null because of not calling destructor if not. backgroundColor: CallStyle.container.avatar.backgroundColor foregroundColor: mainItem.isPaused ? CallStyle.container.pause.color : 'transparent' image: { if (_sipAddressObserver) { var contact = _sipAddressObserver.contact return contact && contact.vcard.avatar }else return null; } username: _username Text { anchors.fill: parent color: CallStyle.container.pause.text.color // `|| 1` => `pointSize` must be greater than 0. font.pointSize: (width / CallStyle.container.pause.text.pointSizeFactor) || 1 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: '❙❙' textFormat: Text.RichText visible: mainItem.isPaused } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Camera/000077500000000000000000000000001434616504300244325ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Camera/CameraItem.qml000066400000000000000000000053771434616504300271700ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import App.Styles 1.0 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import UtilsCpp 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Item { id: container property bool isCameraFromDevice: true property ParticipantDeviceModel currentDevice property CallModel callModel property bool isPreview: (!callModel && !container.currentDevice) || ( container.currentDevice && container.currentDevice.isMe) property bool isFullscreen: false property bool hideCamera: false property bool isPaused: false property bool deactivateCamera: true property bool isVideoEnabled: !deactivateCamera && (!callModel || callModel.videoEnabled) && (!container.currentDevice || ( callModel && container.currentDevice && ( (!container.currentDevice.isMe && container.currentDevice.videoEnabled) || (container.currentDevice.isMe && callModel.cameraEnabled)))) property bool a : callModel && callModel.videoEnabled property bool b: container.currentDevice && container.currentDevice.videoEnabled property bool c: container.currentDevice && container.currentDevice.isMe property bool d : callModel && callModel.cameraEnabled property bool isReady: cameraLoader.item && cameraLoader.item.isReady property bool hadCall : false onCallModelChanged: if(callModel) hadCall = true signal videoDefinitionChanged() onCurrentDeviceChanged: {if(container.isCameraFromDevice) resetActive()} Component.onDestruction: if(!hadCall || (hadCall && callModel) ){isVideoEnabled=false} function resetActive(){ resetTimer.resetActive() } Loader { id: cameraLoader property bool resetActive: false anchors.fill: parent active: !resetActive && container.isVideoEnabled onActiveChanged: { console.log("QML Camera status : " + active) } sourceComponent: container.isVideoEnabled && !container.isPaused? camera : null Timer{ id: resetTimer interval: 100 repeat: false onTriggered: if(!cameraLoader.active){ cameraLoader.resetActive = false }else{ start() // Let some more time to propagate active event } function resetActive(){ start()// Do it first to avoid deleting caller while processing cameraLoader.resetActive = true } } Component { id: camera Camera { participantDeviceModel: container.currentDevice call: container.isCameraFromDevice ? null : container.callModel anchors.fill: parent isPreview: container.isPreview onRequestNewRenderer: {resetTimer.resetActive()} onVideoDefinitionChanged: container.videoDefinitionChanged() } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Camera/CameraView.qml000066400000000000000000000123241434616504300271720ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import App.Styles 1.0 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Item{ id: mainItem property alias currentDevice: camera.currentDevice property alias callModel: camera.callModel property alias hideCamera: camera.hideCamera property alias isPaused: camera.isPaused property alias isPreview: camera.isPreview property alias isFullscreen: camera.isFullscreen property alias isCameraFromDevice: camera.isCameraFromDevice property bool showCloseButton: false property bool showActiveSpeakerOverlay: true property color color : camera.isReady ? CameraViewStyle.cameraBackgroundColor : CameraViewStyle.outBackgroundColor property real avatarRatio : 2/3 signal closeRequested() signal videoDefinitionChanged() MouseArea{ anchors.fill: parent onClicked: camera.resetActive() } RectangularGlow { id: effect anchors.fill: backgroundArea glowRadius: 4 spread: 0.9 color: CameraViewStyle.border.color cornerRadius: backgroundArea.radius + glowRadius visible: mainItem.showActiveSpeakerOverlay && mainItem.currentDevice && mainItem.currentDevice.isSpeaking } Rectangle { id: backgroundArea color: mainItem.color anchors.fill: parent radius: CameraViewStyle.radius Component { id: avatar IncallAvatar { participantDeviceModel: mainItem.currentDevice call: participantDeviceModel ? undefined : camera.callModel height: Utils.computeAvatarSize(backgroundArea, CallStyle.container.avatar.maxSize, avatarRatio) width: height backgroundColor: CameraViewStyle.inAvatarBackgroundColor } } Loader { anchors.centerIn: parent active: (mainItem.callModel || mainItem.currentDevice) && !camera.isReady sourceComponent: avatar } } Rectangle{ id: showArea anchors.fill: parent radius: CameraViewStyle.radius visible: false color: 'red' } CameraItem{ id: camera anchors.centerIn: parent anchors.fill: parent visible: false onVideoDefinitionChanged: mainItem.videoDefinitionChanged() deactivateCamera: false } OpacityMask{ id: renderedCamera anchors.fill: parent source: camera maskSource: showArea invert:false visible: true /* In case we need transformations. property Matrix4x4 mirroredRotationMatrix : Matrix4x4 {// 180 rotation + mirror matrix: Qt.matrix4x4(-Math.cos(Math.PI), -Math.sin(Math.PI), 0, 0, Math.sin(Math.PI), Math.cos(Math.PI), 0, camera.height, 0, 0, 1, 0, 0, 0, 0, 1) } property Matrix4x4 rotationMatrix : Matrix4x4 {// 180 rotation only matrix: Qt.matrix4x4(Math.cos(Math.PI), -Math.sin(Math.PI), 0, camera.width, Math.sin(Math.PI), Math.cos(Math.PI), 0, camera.height, 0, 0, 1, 0, 0, 0, 0, 1) } //transform: ( camera.isPreview ? mirroredRotationMatrix : rotationMatrix) */ } Rectangle{ id: hideView anchors.fill: parent color: CameraViewStyle.pauseView.backgroundColor radius: CameraViewStyle.radius visible: mainItem.isPaused Rectangle{ anchors.centerIn: parent height: CameraViewStyle.pauseView.button.iconSize width: height radius: width/2 color: CameraViewStyle.pauseView.button.backgroundNormalColor Icon{ anchors.centerIn: parent icon: CameraViewStyle.pauseView.button.icon overwriteColor: CameraViewStyle.pauseView.button.foregroundNormalColor iconSize: CameraViewStyle.pauseView.button.iconSize } } } Text{ id: username visible: mainItem.currentDevice anchors.right: parent.right anchors.left: parent.left anchors.bottom: parent.bottom anchors.margins: 10 elide: Text.ElideRight maximumLineCount: 1 text: mainItem.currentDevice && mainItem.currentDevice.displayName + (mainItem.isPaused ? ' (en pause)' : '') font.pointSize: CameraViewStyle.contactDescription.pointSize font.weight: CameraViewStyle.contactDescription.weight color: CameraViewStyle.contactDescription.color } Glow { anchors.fill: username //spread: 1 radius: 12 samples: 25 color: "#80000000" source: username } ActionButton{ visible: mainItem.showCloseButton && camera.isPreview && mainItem.callModel && mainItem.callModel.videoEnabled anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 5 anchors.topMargin: 5 isCustom: true colorSet: CameraViewStyle.closePreview onClicked: mainItem.closeRequested() } Rectangle{ visible: mainItem.currentDevice && mainItem.currentDevice.isMuted anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 15 anchors.topMargin: 15 height: CameraViewStyle.isMuted.button.iconSize width: height radius: width/2 color: CameraViewStyle.isMuted.button.backgroundNormalColor Icon{ anchors.centerIn: parent icon: CameraViewStyle.isMuted.button.icon overwriteColor: CameraViewStyle.isMuted.button.foregroundNormalColor iconSize: CameraViewStyle.isMuted.button.iconSize } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/000077500000000000000000000000001434616504300241215ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/Chat.js000066400000000000000000000043251434616504300253420ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `Chat.qml` Logic. // ============================================================================= .import QtQuick 2.7 as QtQuick .import Linphone 1.0 as Linphone .import UtilsCpp 1.0 as UtilsCpp // ============================================================================= function initView () { chat.bindToEnd = true chat.positionViewAtEnd() if(chat.atYBeginning && !chat.loadingEntries){//Check if we are at beginning chat.displaying = true container.proxyModel.loadMoreEntriesAsync() } } function getComponentFromEntry (chatEntry) { if (chatEntry.type === Linphone.ChatRoomModel.CallEntry) { return 'Event.qml' } if (chatEntry.type === Linphone.ChatRoomModel.NoticeEntry) { return 'Notice.qml' } return chatEntry.isOutgoing ? 'OutgoingMessage.qml' : 'IncomingMessage.qml' } function handleFilesDropped (files) { chat.bindToEnd = true files.forEach(chatMessagePreview.addFile) } function handleMoreEntriesLoaded (n) { chat.positionViewAtIndex(n - 1, QtQuick.ListView.Beginning) } function handleMovementEnded () { if (chat.atYEnd) { chat.bindToEnd = true } } function handleMovementStarted () { chat.bindToEnd = false } function handleTextChanged (text) { container.proxyModel.compose(text) } function sendMessage (text) { textArea.text = '' chat.bindToEnd = true if(container.proxyModel) container.proxyModel.sendMessage(text) } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/Chat.qml000066400000000000000000000411141434616504300255140ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import Units 1.0 import 'Chat.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Rectangle { id: container property alias proxyModel: chat.model // ChatRoomProxyModel property alias tryingToLoadMoreEntries : chat.tryToLoadMoreEntries property alias noticeBannerText : messageBlock.noticeBannerText // When set, show a banner with text and hide after some time // --------------------------------------------------------------------------- signal messageToSend (string text) // --------------------------------------------------------------------------- color: ChatStyle.color clip: true Timer{// Let some time to have a better cell sizes id: repositionerDelay property int indexToMove interval: 100 onTriggered: chat.positionViewAtIndex(indexToMove, ListView.Center) } function positionViewAtIndex(index){ chat.bindToEnd = false chat.positionViewAtIndex(index, ListView.Center) repositionerDelay.indexToMove = index repositionerDelay.restart() } function goToMessage(message){ positionViewAtIndex(container.proxyModel.loadTillMessage(message)) } ColumnLayout { anchors.fill: parent spacing: 0 ScrollableListView { id: chat // ----------------------------------------------------------------------- property bool bindToEnd: false property bool displaying: false property bool loadingEntries: (container.proxyModel.chatRoomModel && container.proxyModel.chatRoomModel.entriesLoading) || displaying property bool tryToLoadMoreEntries: loadingEntries || remainingLoadersCount>0 property bool isMoving : false // replace moving read-only property to allow using movement signals. // Load optimizations property int remainingLoadersCount: 0 property int syncLoaderBatch: 50 // batch of simultaneous loaders on synchronous mode //------------------------------------ onLoadingEntriesChanged: { if( loadingEntries && !displaying) displaying = true } onBindToEndChanged: if( bindToEnd){ markAsReadTimer.start() } Timer{ id: markAsReadTimer interval: 5000 repeat: false running: false onTriggered: if(container.proxyModel.chatRoomModel) container.proxyModel.chatRoomModel.resetMessageCount() } Layout.fillHeight: true Layout.fillWidth: true clip: false highlightFollowsCurrentItem: false // Use moving event => this is a user action. onIsMovingChanged:{ if(!chat.isMoving && chat.atYBeginning && !chat.loadingEntries){// Moving has stopped. Check if we are at beginning chat.displaying = true container.proxyModel.loadMoreEntriesAsync() } } section { criteria: ViewSection.FullString delegate: sectionHeading property: '$sectionDate' } // ----------------------------------------------------------------------- Component.onCompleted: Logic.initView() onMovementStarted: {Logic.handleMovementStarted(); chat.isMoving = true} onMovementEnded: {Logic.handleMovementEnded(); chat.isMoving = false} // ----------------------------------------------------------------------- Connections { target: proxyModel // When the view is changed (for example `Calls` -> `Messages`), // the position is set at end and it can be possible to load // more entries. onEntryTypeFilterChanged: Logic.initView() onMoreEntriesLoaded: { Logic.handleMoreEntriesLoaded(n)// move view to n - 1 item chat.displaying = false } } // ----------------------------------------------------------------------- // Heading. // ----------------------------------------------------------------------- Component { id: sectionHeading Item { implicitHeight: container.height + ChatStyle.sectionHeading.bottomMargin width: parent.width clip: false Borders { id: container borderColor: ChatStyle.sectionHeading.border.color bottomWidth: ChatStyle.sectionHeading.border.width implicitHeight: text.contentHeight + ChatStyle.sectionHeading.padding * 2 + ChatStyle.sectionHeading.border.width * 2 topWidth: ChatStyle.sectionHeading.border.width width: parent.width Text { id: text anchors.fill: parent color: ChatStyle.sectionHeading.text.color font { bold: true pointSize: ChatStyle.sectionHeading.text.pointSize capitalization: Font.Capitalize } horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter // Cast section to integer because Qt converts the // sectionDate in string!!! text: new Date(section).toLocaleDateString( Qt.locale(App.locale) ) } } } } // ----------------------------------------------------------------------- // Message/Event renderer. // ----------------------------------------------------------------------- delegate: Rectangle { id: entry property bool isNotice : $chatEntry.type === ChatRoomModel.NoticeEntry property bool isCall : $chatEntry.type === ChatRoomModel.CallEntry property bool isMessage : $chatEntry.type === ChatRoomModel.MessageEntry function isHoverEntry () { return mouseArea.containsMouse } function removeEntry () { proxyModel.removeRow(index) } anchors { left: parent ? parent.left : undefined leftMargin: isNotice?0:ChatStyle.entry.leftMargin right: parent ? parent.right : undefined rightMargin: isNotice?0:ChatStyle.entry.deleteIconSize + ChatStyle.entry.message.extraContent.spacing + ChatStyle.entry.message.extraContent.rightMargin + ChatStyle.entry.message.extraContent.leftMargin + ChatStyle.entry.message.outgoing.areaSize } color: ChatStyle.color implicitHeight: layout.height + ChatStyle.entry.bottomMargin clip: false // --------------------------------------------------------------------- MouseArea { id: mouseArea cursorShape: Qt.ArrowCursor hoverEnabled: true implicitHeight: layout.height width: parent.width + parent.anchors.rightMargin clip: false acceptedButtons: Qt.NoButton ColumnLayout{ id: layout spacing: 0 width: entry.width Text{ id:authorName Layout.leftMargin: timeDisplay.width + 10 Layout.fillWidth: true text : $chatEntry.fromDisplayName ? $chatEntry.fromDisplayName : '' property var previousItem : { if(index >0) return proxyModel.getAt(index-1) else return null } color: ChatStyle.entry.event.text.color font.pointSize: ChatStyle.entry.event.text.pointSize visible: isMessage && $chatEntry != undefined && !$chatEntry.isOutgoing // Only outgoing && (!previousItem //No previous entry || previousItem.type != ChatRoomModel.MessageEntry // Previous entry is a message || previousItem.fromSipAddress != $chatEntry.fromSipAddress // Different user || (new Date(previousItem.timestamp)).setHours(0, 0, 0, 0) != (new Date($chatEntry.timestamp)).setHours(0, 0, 0, 0) // Same day == section ) } RowLayout { spacing: 0 width: entry.width // Display time. Text { id:timeDisplay Layout.alignment: Qt.AlignTop Layout.preferredHeight: ChatStyle.entry.lineHeight Layout.preferredWidth: ChatStyle.entry.time.width color: ChatStyle.entry.event.text.color font.pointSize: ChatStyle.entry.time.pointSize text: UtilsCpp.toTimeString($chatEntry.timestamp, 'hh:mm') verticalAlignment: Text.AlignVCenter TooltipArea { text: UtilsCpp.toDateTimeString($chatEntry.timestamp) } visible:!isNotice } // Display content. Loader { id: loader height: (item !== null && typeof(item)!== 'undefined')? item.height: 0 Layout.fillWidth: true source: Logic.getComponentFromEntry($chatEntry) property int loaderIndex: 0 // index of loader from remaining loaders property int remainingIndex : loaderIndex % ((chat.remainingLoadersCount) / chat.syncLoaderBatch) != 0 // Check loader index to remaining loader. onRemainingIndexChanged: if( remainingIndex == 0 && asynchronous) asynchronous = false asynchronous: true z:1 onStatusChanged: if( status == Loader.Ready) { remainingIndex = -1 // overwrite to remove signal changed. That way, there is no more binding loops. --chat.remainingLoadersCount // Loader is ready: remove one from remaining count. } Component.onCompleted: loaderIndex = ++chat.remainingLoadersCount // on new Loader : one more remaining Component.onDestruction: if( status != Loader.Ready) --chat.remainingLoadersCount // Remove remaining count if not loaded } Connections{ target: loader.item ignoreUnknownSignals: true //: "Copied to clipboard" : when a user copy a text from the menu, this message show up. onCopyAllDone: container.noticeBannerText = qsTr("allTextCopied") //: "Selection copied to clipboard" : when a user copy a text from the menu, this message show up. onCopySelectionDone: container.noticeBannerText = qsTr("selectedTextCopied") onReplyClicked: { proxyModel.chatRoomModel.reply = $chatEntry } onForwardClicked:{ window.attachVirtualWindow(Qt.resolvedUrl('../Dialog/SipAddressDialog.qml') //: 'Choose where to forward the message' : Dialog title for choosing where to forward the current message. , {title: qsTr('forwardDialogTitle'), addressSelectedCallback: function (sipAddress) { var chat = CallsListModel.createChatRoom( '', proxyModel.chatRoomModel.haveEncryption, [sipAddress], false ) if(chat){ chat.chatRoomModel.forwardMessage($chatEntry) TimelineListModel.select(chat.chatRoomModel) } }, chatRoomSelectedCallback: function (chatRoomModel){ if(chatRoomModel){ chatRoomModel.forwardMessage($chatEntry) TimelineListModel.select(chatRoomModel) } } }) } onGoToMessage:{ container.goToMessage(message) // sometimes, there is no access to chat id (maybe because of cleaning component while loading new items). Use a global intermediate. } onConferenceIcsCopied: container.noticeBannerText = qsTr('conferencesCopiedICS') } } } } } footer: Item{ implicitHeight: composersItem.implicitHeight width: parent.width clip: false Text { id: composersItem property var composers : container.proxyModel.chatRoomModel ? container.proxyModel.chatRoomModel.composers : undefined property int count : composers && composers.length ? composers.length : 0 color: ChatStyle.composingText.color font.pointSize: ChatStyle.composingText.pointSize height: visible ? undefined : 0 leftPadding: ChatStyle.composingText.leftPadding visible: count > 0 && ( (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled) || (proxyModel.chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled) ) wrapMode: Text.Wrap //: '%1 is typing...' indicate that someone is composing in chat text:(count==0?'': qsTr('chatTyping','',count).arg(container.proxyModel.getDisplayNameComposers())) } } ActionButton{ id: gotToBottomButton anchors.bottom: parent.bottom anchors.bottomMargin: 10 anchors.right: parent.right anchors.rightMargin: 35 visible: chat.isIndexAfter(chat.count-1) onVisibleChanged: updateMarkAsRead() Component.onCompleted: updateMarkAsRead() function updateMarkAsRead(){ if(!visible) container.proxyModel.markAsReadEnabled = true } Connections{ target: container.proxyModel onMarkAsReadEnabledChanged: if( !container.proxyModel.markAsReadEnabled) gotToBottomButton.updateMarkAsRead() } isCustom: true backgroundRadius: width/2 colorSet: ChatStyle.gotToBottom onClicked: { chat.bindToEnd = true } MessageCounter{ anchors.left: parent.right anchors.bottom: parent.top anchors.bottomMargin: 0 anchors.leftMargin: -14 count: container.proxyModel.chatRoomModel ? container.proxyModel.chatRoomModel.unreadMessagesCount : 0 showOnlyNumber: true iconSize: 15 pointSize: Units.dp * 7 } } } Rectangle { id: bottomChatBackground Layout.fillWidth: true Layout.preferredHeight: textAreaBorders.height + chatMessagePreview.height+messageBlock.height color: ChatStyle.sendArea.backgroundBorder.color visible: proxyModel.chatRoomModel && !proxyModel.chatRoomModel.isReadOnly && (!proxyModel.chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled || proxyModel.chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled) ColumnLayout{ anchors.fill: parent spacing: 0 MessageBanner{ id: messageBlock onHeightChanged: height = Layout.preferredHeight Layout.fillWidth: true Layout.preferredHeight: fitHeight Layout.leftMargin: ChatStyle.entry.leftMargin Layout.rightMargin: ChatStyle.entry.rightMargin noticeBannerText: '' } ChatMessagePreview{ id: chatMessagePreview Layout.fillWidth: true Layout.leftMargin: ChatStyle.sendArea.backgroundBorder.width maxHeight: container.height - textAreaBorders.height replyChatRoomModel: proxyModel.chatRoomModel replyRightMargin: textArea.textRightMargin replyLeftMargin: textArea.textLeftMargin } // ------------------------------------------------------------------------- // Send area. // ------------------------------------------------------------------------- Borders { id: textAreaBorders Layout.fillWidth: true Layout.preferredHeight: textArea.height Layout.leftMargin: ChatStyle.sendArea.backgroundBorder.width borderColor: ChatStyle.sendArea.border.color topWidth: ChatStyle.sendArea.border.width DroppableTextArea { id: textArea enabled:proxyModel && proxyModel.chatRoomModel ? !proxyModel.chatRoomModel.isReadOnly:false isEphemeral : proxyModel && proxyModel.chatRoomModel ? proxyModel.chatRoomModel.ephemeralEnabled:false anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom height: visible ? ChatStyle.sendArea.height + ChatStyle.sendArea.border.width : 0 minimumHeight:ChatStyle.sendArea.height + ChatStyle.sendArea.border.width maximumHeight:container.height/2 dropEnabled: SettingsModel.fileTransferUrl.length > 0 dropDisabledReason: qsTr('noFileTransferUrl') placeholderText: qsTr('newMessagePlaceholder') recordAudioToggled: RecorderManager.haveVocalRecorder && RecorderManager.getVocalRecorder().state != LinphoneEnums.RecorderStateClosed onDropped: Logic.handleFilesDropped(files) onTextChanged: Logic.handleTextChanged(text) onValidText: { textArea.text = '' chat.bindToEnd = true if(proxyModel.chatRoomModel) { proxyModel.sendMessage(text) }else{ proxyModel.chatRoomModel = CallsListModel.createChat(proxyModel.peerAddress) proxyModel.sendMessage(text) } } onAudioRecordRequest: RecorderManager.resetVocalRecorder() Component.onCompleted: {text = proxyModel.cachedText; cursorPosition=text.length} Rectangle{ anchors.fill:parent color:'white' opacity: 0.5 visible:!textArea.enabled } } }// Send Area }// ColumnLayout }// Bottom background } // --------------------------------------------------------------------------- // Scroll at end if necessary. // --------------------------------------------------------------------------- Timer { interval: 100 repeat: true running: true onTriggered: chat.bindToEnd && chat.positionViewAtEnd() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatAudioMessage.qml000066400000000000000000000073421434616504300300100ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import ColorsList 1.0 import 'Message.js' as Logic // ============================================================================= Loader{ id: mainItem property ContentModel contentModel property int maxWidth : parent.width property int fitWidth: active ? Math.max(maxWidth - ChatAudioMessageStyle.emptySpace, ChatAudioMessageStyle.minWidth) : 0 property int fitHeight: active ? 60 : 0 property font customFont : SettingsModel.textMessageFont property bool isOutgoing : contentModel && contentModel.chatMessageModel && (contentModel.chatMessageModel.isOutgoing || contentModel.chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle); property bool isActive: active active: contentModel && contentModel.isVoiceRecording() sourceComponent: Item{ id: loadedItem property bool isPlaying : vocalPlayer.item && vocalPlayer.item.playbackState === SoundPlayer.PlayingState onIsPlayingChanged: isPlaying ? mediaProgressBar.resume() : mediaProgressBar.stop() width: maxWidth < 0 || maxWidth > fitWidth ? fitWidth : maxWidth height: mainItem.fitHeight clip: false Loader { id: vocalPlayer active: false function play(){ if(!vocalPlayer.active) vocalPlayer.active = true else { if(loadedItem.isPlaying){// Pause the play vocalPlayer.item.pause() }else{// Play the audio vocalPlayer.item.play() } } } sourceComponent: SoundPlayer { source: mainItem.contentModel && mainItem.contentModel.filePath onStopped:{ mediaProgressBar.value = 101 } Component.onCompleted: { play()// This will open the file and allow seeking pause() mediaProgressBar.value = 0 mediaProgressBar.refresh() } } onStatusChanged: if (loader.status == Loader.Ready) play() } RowLayout{ id: lineLayout anchors.fill: parent spacing: 5 ActionButton{ id: playButton Layout.preferredHeight: iconSize Layout.preferredWidth: iconSize Layout.rightMargin: 5 Layout.leftMargin: 15 Layout.alignment: Qt.AlignVCenter isCustom: true backgroundRadius: width colorSet: (loadedItem.isPlaying ? ChatAudioMessageStyle.pauseAction : ChatAudioMessageStyle.playAction) onClicked:{ vocalPlayer.play() } } Item{ Layout.fillHeight: true Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter Layout.rightMargin: 10 Layout.topMargin: 10 Layout.bottomMargin: 10 MediaProgressBar{ id: mediaProgressBar anchors.fill: parent progressDuration: vocalPlayer.item ? vocalPlayer.item.duration : contentModel.getFileDuration() progressPosition: 0 value: 0 stopAtEnd: true resetAtEnd: false backgroundColor: ChatAudioMessageStyle.backgroundColor colorSet: ChatAudioMessageStyle.progressionWave durationTextColor: mainItem.isOutgoing ? ChatStyle.entry.message.outgoing.text.color : ChatStyle.entry.message.incoming.text.color function refresh(){ if( vocalPlayer.item){ progressPosition = vocalPlayer.item.getPosition() value = 100 * ( progressPosition / vocalPlayer.item.duration) } } onEndReached:{ if(vocalPlayer.item) vocalPlayer.item.stop() } onRefreshPositionRequested: refresh() onSeekRequested: if( vocalPlayer.item){ vocalPlayer.item.seek(ms) progressPosition = vocalPlayer.item.getPosition() value = 100 * (progressPosition / vocalPlayer.item.duration) } } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatAudioPreview.qml000066400000000000000000000122261434616504300300420ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import Units 1.0 import 'Chat.js' as Logic // ============================================================================= Rectangle{ id: audioPreviewBlock property bool haveRecorder: RecorderManager.haveVocalRecorder property RecorderModel vocalRecorder : (haveRecorder ? RecorderManager.getVocalRecorder() : null) property bool isRecording : (vocalRecorder ? vocalRecorder.state != LinphoneEnums.RecorderStateClosed : false) property bool isPlaying : vocalPlayer.item && vocalPlayer.item.playbackState === SoundPlayer.PlayingState onIsRecordingChanged: if(isRecording) { mediaProgressBar.resume() }else mediaProgressBar.stop() onIsPlayingChanged: isPlaying ? mediaProgressBar.resume() : mediaProgressBar.stop() Layout.preferredHeight: visible ? ChatAudioPreviewStyle.height : 0 color: ChatAudioPreviewStyle.backgroundColor radius: 0 state: haveRecorder ? 'showed' : 'hidden' clip: false Loader { id: vocalPlayer active: haveRecorder && vocalRecorder && !isRecording sourceComponent: SoundPlayer { source: (haveRecorder && vocalRecorder? vocalRecorder.file : '') onStopped:{ mediaProgressBar.value = 101 } Component.onCompleted: { play()// This will open the file and allow seeking pause() mediaProgressBar.value = 101 mediaProgressBar.refresh() } } } RowLayout{ id: lineLayout anchors.fill: parent spacing: 0 ActionButton{ Layout.preferredHeight: iconSize Layout.preferredWidth: iconSize Layout.leftMargin: 6 Layout.alignment: Qt.AlignVCenter isCustom: true colorSet: ChatAudioPreviewStyle.deleteAction onClicked: RecorderManager.clearVocalRecorder() } VuMeter { Layout.leftMargin: 6 Layout.rightMargin: 6 Timer { interval: 50 repeat: true running: audioPreviewBlock.isRecording onTriggered: parent.value = audioPreviewBlock.vocalRecorder.getCaptureVolume() } visible: audioPreviewBlock.isRecording } Item{ Layout.fillHeight: true Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter Layout.topMargin: 10 Layout.bottomMargin: 10 Layout.leftMargin: 6 MediaProgressBar{ id: mediaProgressBar anchors.fill: parent waveLeftMargin: !vocalPlayer.item && vocalRecorder ? 10 : 0 progressDuration: !vocalPlayer.item && vocalRecorder? vocalRecorder.getDuration() : 0 progressPosition: !vocalPlayer.item ? progressDuration : 0 value: !vocalPlayer.item ? 0.01 * progressDuration / 5 : 100 stopAtEnd: !audioPreviewBlock.isRecording resetAtEnd: false colorSet: isRecording ? ChatAudioPreviewStyle.recordingProgressionWave : ChatAudioPreviewStyle.progressionWave function progressComputation(t) { return 1 * Math.sqrt(1 - (t=t/1-1)*t); } function refresh(){ if( vocalPlayer.item){ progressPosition = vocalPlayer.item.getPosition() value = 100 * ( progressPosition / vocalPlayer.item.duration) }else{// Recording progressDuration = vocalRecorder.getDuration() progressPosition = progressDuration if( value == 0) value = 1 else value = value + Math.pow(value,-0.7) } } onEndReached:{ if(vocalPlayer.item) vocalPlayer.item.stop() } onRefreshPositionRequested: refresh() onSeekRequested: if( vocalPlayer.item){ vocalPlayer.item.seek(ms) progressPosition = vocalPlayer.item.getPosition() value = 100 * (progressPosition / vocalPlayer.item.duration) } } } ActionButton{ Layout.preferredHeight: iconSize Layout.preferredWidth: iconSize Layout.rightMargin: ChatStyle.rightButtonMargin Layout.leftMargin: ChatStyle.rightButtonLMargin Layout.alignment: Qt.AlignVCenter isCustom: true colorSet: audioPreviewBlock.isRecording ? ChatAudioPreviewStyle.stopAction : (audioPreviewBlock.isPlaying ? ChatAudioPreviewStyle.pauseAction : ChatAudioPreviewStyle.playAction) onClicked:{ if(audioPreviewBlock.isRecording){// Stop the record and save the file audioPreviewBlock.vocalRecorder.stop() mediaProgressBar.value = 0 }else if(audioPreviewBlock.isPlaying){// Pause the play vocalPlayer.item.pause() }else{// Play the audio vocalPlayer.item.play() } } } } states: [ State { name: "hidden" PropertyChanges { target: audioPreviewBlock; opacity: 0 ; visible: false } }, State { name: "showed" PropertyChanges { target: audioPreviewBlock; opacity: 1 ; visible: true } } ] transitions: [ Transition { from: "*"; to: "showed" SequentialAnimation{ ScriptAction{ script: audioPreviewBlock.visible = true } ScriptAction{ script: audioPreviewBlock.vocalRecorder.start() } NumberAnimation{ properties: "opacity"; easing.type: Easing.OutBounce; duration: 250 } } }, Transition { from: "*"; to: "hidden" SequentialAnimation{ NumberAnimation{ properties: "opacity"; duration: 250 } ScriptAction{ script: audioPreviewBlock.visible = false } } } ] } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatCalendarMessage.qml000066400000000000000000000331071434616504300304560ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import App 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import ColorsList 1.0 import 'Message.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Loader{ id: mainItem property ContentModel contentModel property ConferenceInfoModel conferenceInfoModel: contentModel ? contentModel.conferenceInfoModel : null property int maxWidth : parent.width property int fitHeight: active && item ? item.fitHeight : 0 property int fitWidth: active && item ? maxWidth/2 + ChatCalendarMessageStyle.widthMargin*2 : 0 property bool containsMouse: false property int gotoButtonMode: -1 //-1: hide, 0:goto, 1:MoreInfo property bool isExpanded : false property bool isCancelled: conferenceInfoModel && conferenceInfoModel.state == LinphoneEnums.ConferenceInfoStateCancelled signal expandToggle() signal conferenceUriCopied() signal conferenceRemoved() width: parent.width height: parent.height property font customFont : SettingsModel.textMessageFont active: mainItem.conferenceInfoModel sourceComponent: MouseArea{ id: loadedItem property int fitHeight: layout.fitHeight + ChatCalendarMessageStyle.topMargin+ChatCalendarMessageStyle.bottomMargin property int fitWidth: layout.fitWidth anchors.fill: parent anchors.leftMargin: ChatCalendarMessageStyle.widthMargin anchors.rightMargin: ChatCalendarMessageStyle.widthMargin anchors.topMargin: ChatCalendarMessageStyle.topMargin anchors.bottomMargin: ChatCalendarMessageStyle.bottomMargin clip: false hoverEnabled: true onClicked: mainItem.expandToggle() onHoveredChanged: mainItem.containsMouse = loadedItem.containsMouse ColumnLayout{ id: layout // Fix for binding loops property int participantsFitHeight: 0 property int expandedFitHeight: 0 function updateFitHeight(){ participantsFitHeight = participantsRow.implicitHeight expandedFitHeight = (expandedDescription.visible? expandedDescription.implicitHeight : 0) } property int fitHeight: dateRow.implicitHeight + statusLabel.implicitHeight + title.implicitHeight + participantsFitHeight + expandedFitHeight property int fitWidth: Layout.minimumWidth anchors.fill: parent spacing: 0 RowLayout { id: dateRow Layout.fillWidth: true //Layout.preferredWidth: parent.width // Need this because fillWidth is not enough... Layout.preferredHeight: ChatCalendarMessageStyle.lineHeight Layout.alignment: Qt.AlignTop Layout.topMargin: 5 spacing: 10 RowLayout { id: scheduleRow Layout.fillWidth: true Layout.preferredHeight: ChatCalendarMessageStyle.lineHeight Layout.leftMargin: 5 spacing: ChatCalendarMessageStyle.schedule.spacing Item{ Layout.preferredHeight: ChatCalendarMessageStyle.lineHeight Layout.preferredWidth: ChatCalendarMessageStyle.schedule.iconSize clip: false Icon{ anchors.centerIn: parent icon: ChatCalendarMessageStyle.schedule.icon iconSize: ChatCalendarMessageStyle.schedule.iconSize overwriteColor: ChatCalendarMessageStyle.schedule.color } } Text { id: conferenceTime Layout.fillWidth: true Layout.preferredHeight: ChatCalendarMessageStyle.lineHeight Layout.minimumWidth: implicitWidth verticalAlignment: Qt.AlignVCenter color: ChatCalendarMessageStyle.schedule.color elide: Text.ElideRight font.pointSize: ChatCalendarMessageStyle.schedule.pointSize // Reminder: QML use locale time (not system). Use UTC from C++ => convert it into QML => pass QML => convert it into UTC and apply our timezone. text: UtilsCpp.toTimeString(Utils.fromUTC(mainItem.conferenceInfoModel.dateTimeUtc), 'hh:mm') + (mainItem.conferenceInfoModel.duration > 0 ? ' (' +Utils.formatDuration(mainItem.conferenceInfoModel.duration * 60) + ')' : '') } } Text{ Layout.fillWidth: true Layout.preferredHeight: ChatCalendarMessageStyle.lineHeight Layout.rightMargin: 15 horizontalAlignment: Qt.AlignRight verticalAlignment: Qt.AlignVCenter color: ChatCalendarMessageStyle.schedule.color elide: Text.ElideRight font.pointSize: ChatCalendarMessageStyle.schedule.pointSize //: 'Organizer' : Label Title for the organizer. text: qsTr('icsOrganizer') +' : ' +UtilsCpp.getDisplayName(mainItem.conferenceInfoModel.organizer) } } Text{ id: statusLabel Layout.fillWidth: true Layout.preferredHeight: visible ? ChatCalendarMessageStyle.lineHeight : 0 Layout.alignment: Qt.AlignTop Layout.leftMargin: 10 visible: mainItem.isCancelled elide: Text.ElideRight color: ChatCalendarMessageStyle.type.cancelledColor font.pointSize: ChatCalendarMessageStyle.type.pointSize font.weight: Font.Bold //: 'Meeting has been cancelled' : ICS Title for cancelled meetings text:qsTr('icsCancelledMeetingInvite') } Text{ id: title Layout.fillWidth: true Layout.preferredHeight: ChatCalendarMessageStyle.lineHeight Layout.alignment: Qt.AlignTop Layout.leftMargin: 10 elide: Text.ElideRight color: ChatCalendarMessageStyle.subject.color font.pointSize: ChatCalendarMessageStyle.subject.pointSize font.weight: Font.Bold text: mainItem.conferenceInfoModel.subject } RowLayout { id: participantsRow Layout.fillWidth: true Layout.fillHeight: true Layout.minimumHeight: 4 + (mainItem.isExpanded ? expandedParticipantsList.minimumHeight : ChatCalendarMessageStyle.lineHeight) Layout.alignment: Qt.AlignTop Layout.leftMargin: 5 Layout.rightMargin: 10 spacing: ChatCalendarMessageStyle.participants.spacing property int participantLineHeight: participantsList.implicitHeight // Fix for binding loops onImplicitHeightChanged: Qt.callLater( layout.updateFitHeight) Item{ Layout.preferredHeight: parent.participantLineHeight Layout.preferredWidth: ChatCalendarMessageStyle.participants.iconSize Layout.alignment: Qt.AlignTop visible: mainItem.conferenceInfoModel.participantCount > 0 clip: false Icon{ anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter icon: ChatCalendarMessageStyle.participants.icon iconSize: ChatCalendarMessageStyle.participants.iconSize overwriteColor: ChatCalendarMessageStyle.participants.color } } Text { id: participantsList Layout.fillWidth: true Layout.preferredHeight: parent.participantLineHeight Layout.topMargin: 4 Layout.alignment: Qt.AlignTop visible: !mainItem.isExpanded color: ChatCalendarMessageStyle.participants.color elide: Text.ElideRight font.pointSize: ChatCalendarMessageStyle.participants.pointSize text: mainItem.conferenceInfoModel.displayNamesToString } ScrollableListView{ id: expandedParticipantsList property int minimumHeight: Math.min( count * parent.participantLineHeight, layout.height/(descriptionTitle.visible?3:2)) Layout.fillWidth: true Layout.topMargin: 4 Layout.minimumHeight: minimumHeight Layout.alignment: Qt.AlignTop spacing: 0 visible: mainItem.isExpanded onVisibleChanged: visible ? model= mainItem.conferenceInfoModel.getAllParticipants() : model = [] Connections{ target: mainItem.conferenceInfoModel onParticipantsChanged: if(expandedParticipantsList.visible) expandedParticipantsList.model = mainItem.conferenceInfoModel.getAllParticipants() } delegate: Row{ spacing: 5 width: expandedParticipantsList.contentWidth height: participantsRow.participantLineHeight Text{ id: displayName height: participantsRow.participantLineHeight text: modelData.displayName color: ChatCalendarMessageStyle.participants.color font.pointSize: ChatCalendarMessageStyle.participants.pointSize elide: Text.ElideRight } Text{ height: participantsRow.participantLineHeight width: expandedParticipantsList.contentWidth - displayName.width - parent.spacing // parent.width is not enough. Force width text: '('+modelData.address+')' color: ChatCalendarMessageStyle.participants.color font.pointSize: ChatCalendarMessageStyle.participants.pointSize elide: Text.ElideRight } } } Item{ Layout.preferredWidth: expandButton.iconSize Layout.preferredHeight: participantsRow.participantLineHeight Layout.alignment: Qt.AlignTop | Qt.AlignRight ActionButton{ id: expandButton visible: mainItem.gotoButtonMode >= 0 anchors.centerIn: parent anchors.verticalCenter: parent.verticalCenter isCustom: true colorSet: mainItem.gotoButtonMode == 0 ? ChatCalendarMessageStyle.gotoButton : ChatCalendarMessageStyle.infoButton iconSize: participantsRow.participantLineHeight backgroundRadius: width/2 toggled: mainItem.isExpanded onClicked: mainItem.expandToggle() } } } ColumnLayout{ id: expandedDescription Layout.fillWidth: true Layout.fillHeight: true Layout.alignment: Qt.AlignTop Layout.topMargin: 5 visible: mainItem.isExpanded spacing: 0 // Fix for binding loops onVisibleChanged: Qt.callLater( layout.updateFitHeight) onImplicitHeightChanged: Qt.callLater( layout.updateFitHeight) Text{ id: descriptionTitle Layout.fillWidth: true Layout.leftMargin: 10 Layout.topMargin: 5 color: ChatCalendarMessageStyle.subject.color font.pointSize: ChatCalendarMessageStyle.subject.pointSize font.weight: Font.Bold //: 'Description' : Title for the meeting description. text: qsTr('icsDescription') visible: description.text != '' } TextAreaField{ id: description Layout.fillWidth: true //Layout.fillHeight: true Layout.preferredHeight: visible ? implicitHeight : 0 Layout.leftMargin: 10 Layout.rightMargin: 10 padding: 0 color: 'transparent' readOnly: true textColor: ChatCalendarMessageStyle.description.color font.pointSize: ChatCalendarMessageStyle.description.pointSize border.width: 0 visible: description.text != '' text: mainItem.conferenceInfoModel.description } Text{ id: linkTitle Layout.fillWidth: true Layout.leftMargin: 10 Layout.topMargin: 5 color: ChatCalendarMessageStyle.subject.color font.pointSize: ChatCalendarMessageStyle.subject.pointSize font.weight: Font.Bold visible: !mainItem.isCancelled //: 'Meeting address' : Title for the meeting address. text: qsTr('icsconferenceAddressTitle') } RowLayout{ Layout.fillWidth: true Layout.fillHeight: true Layout.leftMargin: 10 Layout.rightMargin: 10 spacing: 10 visible: !mainItem.isCancelled TextField{ id: uriField readOnly: true Layout.fillWidth: true Layout.preferredHeight: ChatCalendarMessageStyle.copyLinkButton.iconSize textFieldStyle: TextFieldStyle.flatInverse text: mainItem.conferenceInfoModel.uri } ActionButton{ isCustom: true colorSet: ChatCalendarMessageStyle.copyLinkButton backgroundRadius: width/2 onClicked: { Clipboard.text = uriField.text mainItem.conferenceUriCopied() } } } RowLayout{ Layout.fillWidth: true Layout.topMargin: 10 Layout.bottomMargin: 5 Layout.rightMargin: 10 spacing: 10 Item{ Layout.fillWidth: true } TextButtonC{ addHeight: 20 //: 'Join' : Action button to join the meeting. text: qsTr('icsJoinButton').toUpperCase() onClicked: CallsListModel.prepareConferenceCall(mainItem.conferenceInfoModel) visible: !mainItem.isCancelled } ActionButton{ id: editButton isCustom: true colorSet: ChatCalendarMessageStyle.editButton backgroundRadius: width/2 visible: UtilsCpp.isMe(mainItem.conferenceInfoModel.organizer) && mainItem.conferenceInfoModel.endDateTime >= new Date() && !mainItem.isCancelled onClicked: { window.detachVirtualWindow() window.attachVirtualWindow(Utils.buildAppDialogUri('NewConference') ,{conferenceInfoModel: mainItem.conferenceInfoModel, forceSchedule: true}) } } ActionButton{ property bool isCancellable: editButton.visible isCustom: true colorSet: ChatCalendarMessageStyle.deleteButton backgroundRadius: width/2 onClicked: { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { //: 'Do you really want do cancel this meeting?' : Warning message to confirm the cancellation of a meeting. descriptionText: isCancellable ? qsTr('cancelConferenceInfo') //: 'Do you really want do delete this meeting?' : Warning message to confirm the deletion of a meeting. : qsTr('deleteConferenceInfo') , }, function (status) { if (status) { if( isCancellable) mainItem.conferenceInfoModel.cancelConference() else mainItem.conferenceInfoModel.deleteConferenceInfo() } }) } } Item{ Layout.fillWidth: mainItem.isCancelled } } } } } Connections{ target: conferenceInfoModel onRemoved: if(byUser) mainItem.conferenceRemoved() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatConferenceInvitationMessage.qml000066400000000000000000000237541434616504300330700ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import App 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import ColorsList 1.0 import 'Message.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Loader{ id: mainItem property ContentModel contentModel property ConferenceInfoModel conferenceInfoModel: contentModel ? contentModel.conferenceInfoModel : null property int maxWidth : parent.width property int fitHeight: active && item ? item.fitHeight : 0 // + (isExpanded? 200 : 0): 0 property int fitWidth: active && item ? Math.min(maxWidth > 0 ? maxWidth : 9999999, item.fitWidth + ChatCalendarMessageStyle.widthMargin*2) : 0 property bool containsMouse: false property int gotoButtonMode: -1 //-1: hide, 0:goto, 1:MoreInfo property bool isExpanded : false signal expandToggle() signal conferenceIcsCopied() width: parent.width height: parent.height property font customFont : SettingsModel.textMessageFont active: mainItem.conferenceInfoModel sourceComponent: MouseArea{ id: loadedItem property int fitHeight: layout.fitHeight + ChatCalendarMessageStyle.topMargin+ChatCalendarMessageStyle.bottomMargin property int fitWidth: layout.fitWidth + ChatCalendarMessageStyle.leftMargin+ChatCalendarMessageStyle.rightMargin anchors.fill: parent anchors.leftMargin: ChatCalendarMessageStyle.widthMargin anchors.rightMargin: ChatCalendarMessageStyle.widthMargin anchors.topMargin: ChatCalendarMessageStyle.topMargin anchors.bottomMargin: ChatCalendarMessageStyle.bottomMargin clip: false hoverEnabled: true onClicked: CallsListModel.prepareConferenceCall(mainItem.conferenceInfoModel) onHoveredChanged: mainItem.containsMouse = loadedItem.containsMouse ColumnLayout{ id: layout // Fix for binding loops property int minHeight: layout.implicitHeight function updateFitHeight(){ fitHeight = minHeight + (description.visible? description.implicitHeight : 0) } onMinHeightChanged: Qt.callLater( layout.updateFitHeight) property int fitHeight: 0 property int fitWidth: Math.max(titleItem.implicitWidth, Math.max(shareButton.width + joinButton.width, description.fitWidth)) anchors.fill: parent spacing: 0 Text{ id: titleItem Layout.fillWidth: true Layout.topMargin: 5 Layout.leftMargin: 5 Layout.alignment: Qt.AlignRight elide: Text.ElideRight color: mainItem.conferenceInfoModel.state == LinphoneEnums.ConferenceInfoStateUpdated ? ChatCalendarMessageStyle.type.updatedColor : mainItem.conferenceInfoModel.state == LinphoneEnums.ConferenceInfoStateCancelled ? ChatCalendarMessageStyle.type.cancelledColor : ChatCalendarMessageStyle.type.color font.pointSize: ChatCalendarMessageStyle.type.pointSize font.weight: Font.Bold text: (mainItem.conferenceInfoModel.state == LinphoneEnums.ConferenceInfoStateUpdated //: 'Meeting has been updated' : ICS title for an updated invitation. ? qsTr('icsUpdatedMeetingInvite') : mainItem.conferenceInfoModel.state == LinphoneEnums.ConferenceInfoStateCancelled //: 'Meeting has been cancelled' : ICS title for a cancelled invitation. ? qsTr('icsCancelledMeetingInvite') //: 'Meeting invite' : ICS title that is an invitation. : qsTr('icsMeetingInvite') ) +': '// + UtilsCpp.getDisplayName(mainItem.conferenceInfoModel.organizer) } Text{ id: title Layout.fillWidth: true Layout.leftMargin: 5 Layout.alignment: Qt.AlignRight elide: Text.ElideRight color: ChatCalendarMessageStyle.subject.color font.pointSize: ChatCalendarMessageStyle.subject.pointSize font.weight: Font.Bold text: mainItem.conferenceInfoModel.subject } RowLayout { id: participantsRow property int participantCount: mainItem.conferenceInfoModel.allParticipantCount Layout.fillWidth: true Layout.preferredHeight: ChatCalendarMessageStyle.participants.iconSize Layout.leftMargin: 5 Layout.rightMargin: 15 spacing: ChatCalendarMessageStyle.participants.spacing visible: participantsRow.participantCount > 0 Icon{ icon: ChatCalendarMessageStyle.participants.icon iconSize: ChatCalendarMessageStyle.participants.iconSize overwriteColor: ChatCalendarMessageStyle.participants.color } Text { id: participantsList Layout.fillWidth: true color: ChatCalendarMessageStyle.participants.color elide: Text.ElideRight font.pointSize: ChatCalendarMessageStyle.participants.pointSize //: '%1 participant' : number(=%1) of participant. text: qsTr('icsParticipants', '', participantsRow.participantCount).arg(participantsRow.participantCount) } } ColumnLayout{ Layout.fillWidth: true Layout.leftMargin: 5 Layout.rightMargin: 15 spacing: 0 RowLayout { id: dateRow Layout.fillWidth: true Layout.preferredHeight: conferenceDate.implicitHeight spacing: ChatCalendarMessageStyle.calendar.spacing Icon{ icon: ChatCalendarMessageStyle.calendar.icon iconSize: ChatCalendarMessageStyle.calendar.iconSize-2 overwriteColor: ChatCalendarMessageStyle.calendar.color } Text { id: conferenceDate Layout.fillWidth: true Layout.minimumWidth: implicitWidth verticalAlignment: Qt.AlignVCenter color: ChatCalendarMessageStyle.schedule.color elide: Text.ElideRight font.pointSize: Units.dp * 8 text: UtilsCpp.toDateString(Utils.fromUTC(mainItem.conferenceInfoModel.dateTimeUtc), 'yyyy/MM/dd') } } RowLayout { id: conferenceTimeRow Layout.fillWidth: true Layout.preferredHeight: conferenceTime.implicitHeight spacing: ChatCalendarMessageStyle.schedule.spacing Icon{ icon: ChatCalendarMessageStyle.schedule.icon iconSize: ChatCalendarMessageStyle.schedule.iconSize-2 overwriteColor: ChatCalendarMessageStyle.schedule.color } Text { id: conferenceTime Layout.fillWidth: true Layout.minimumWidth: implicitWidth verticalAlignment: Qt.AlignVCenter color: ChatCalendarMessageStyle.schedule.color elide: Text.ElideRight font.pointSize: Units.dp * 8 // Reminder: QML use locale time (not system). Use UTC from C++ => convert it into QML => pass QML => convert it into UTC and apply our timezone. text: UtilsCpp.toTimeString( Utils.fromUTC(mainItem.conferenceInfoModel.dateTimeUtc), 'hh:mm') + (mainItem.conferenceInfoModel.duration > 0 ? ' ('+Utils.formatDuration(mainItem.conferenceInfoModel.duration * 60) + ')' : '') } } } ColumnLayout{ id: expandedDescription Layout.fillWidth: true Layout.fillHeight: true Layout.topMargin: 5 visible: mainItem.isExpanded spacing: 0 ScrollableListView{ id: expandedParticipantsList Layout.fillWidth: true Layout.minimumHeight: Math.min( count * ChatCalendarMessageStyle.lineHeight, parent.height/(descriptionTitle.visible?3:2)) Layout.leftMargin: 10 spacing: 0 visible: mainItem.isExpanded onVisibleChanged: visible ? model= mainItem.conferenceInfoModel.getAllParticipants() : model = [] Connections{ target: mainItem.conferenceInfoModel onParticipantsChanged: if(expandedParticipantsList.visible) expandedParticipantsList.model = mainItem.conferenceInfoModel.getAllParticipants() } delegate: Row{ spacing: 5 width: expandedParticipantsList.width height: ChatCalendarMessageStyle.lineHeight Text{text: modelData.displayName color: ChatCalendarMessageStyle.description.color font.pointSize: ChatCalendarMessageStyle.description.pointSize elide: Text.ElideRight wrapMode: TextEdit.WordWrap } Text{text: '('+modelData.address+')' color: ChatCalendarMessageStyle.description.color font.pointSize: ChatCalendarMessageStyle.description.pointSize elide: Text.ElideRight wrapMode: TextEdit.WordWrap } } } } Text{ id: descriptionTitle Layout.fillWidth: true Layout.leftMargin: 5 Layout.topMargin: 5 color: ChatCalendarMessageStyle.subject.color font.pointSize: ChatCalendarMessageStyle.subject.pointSize font.weight: Font.Bold //: 'Description' : Title for the meeting description. text: qsTr('icsDescription') visible: description.text != '' } TextAreaField{ id: description Layout.fillWidth: true Layout.preferredHeight: visible ? implicitHeight : 0 Layout.leftMargin: 5 padding: 0 color: 'transparent' readOnly: true textColor: ChatCalendarMessageStyle.description.color font.pointSize: ChatCalendarMessageStyle.description.pointSize border.width: 0 visible: description.text != '' text: mainItem.conferenceInfoModel.description onImplicitHeightChanged: Qt.callLater( layout.updateFitHeight) onVisibleChanged: Qt.callLater( layout.updateFitHeight) } RowLayout{ Layout.fillWidth: true Layout.topMargin: 10 Layout.bottomMargin: 5 Layout.rightMargin: 10 spacing: 10 Item{ Layout.fillWidth: true } ActionButton{ id: shareButton visible: joinButton.visible iconSize: joinButton.height/2 isCustom: true colorSet: ChatCalendarMessageStyle.shareButton backgroundRadius: width/2 onClicked: { Clipboard.text = mainItem.conferenceInfoModel.getIcalendarString() mainItem.conferenceIcsCopied() } } TextButtonC{ id: joinButton addHeight: 20 visible: mainItem.conferenceInfoModel.state != LinphoneEnums.ConferenceInfoStateCancelled //: 'Join' : Action button to join the meeting. text: qsTr('icsJoinButton').toUpperCase() onClicked: CallsListModel.prepareConferenceCall(mainItem.conferenceInfoModel) } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatContent.qml000066400000000000000000000041031434616504300270440ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import ColorsList 1.0 // ============================================================================= Column{ id: mainItem property ContentModel contentModel property int fitHeight: calendarMessage.fitHeight + message.fitHeight + fileMessage.fitHeight + audioMessage.fitHeight property int fitWidth: calendarMessage.fitWidth + message.fitWidth + fileMessage.fitWidth + audioMessage.fitWidth property color backgroundColor property string lastTextSelected property alias textColor: message.color property alias textFont: message.font property alias fileIsHovering: fileMessage.isHovering signal rightClicked() signal conferenceIcsCopied() property int maxWidth height: fitHeight anchors.left: parent ? parent.left : undefined anchors.right: parent ? parent.right : undefined spacing: 0 property bool isOutgoing : contentModel && contentModel.chatMessageModel && (contentModel.chatMessageModel.isOutgoing || contentModel.chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle); z: message.visible ? 0 : 1 ChatConferenceInvitationMessage{ id: calendarMessage contentModel: mainItem.contentModel width: parent.width maxWidth: mainItem.maxWidth gotoButtonMode: 1 onExpandToggle: isExpanded=!isExpanded height: fitHeight z: 1 onConferenceIcsCopied:mainItem.conferenceIcsCopied() } ChatAudioMessage{ id: audioMessage contentModel: mainItem.contentModel visible: contentModel z: 1 } ChatFileMessage{ id: fileMessage contentModel: mainItem.contentModel width: parent.width z: 2 } ChatTextMessage { id: message contentModel: mainItem.contentModel onLastTextSelectedChanged: mainItem.lastTextSelected = lastTextSelected color: isOutgoing ? ChatStyle.entry.message.outgoing.text.color : ChatStyle.entry.message.incoming.text.color onRightClicked: mainItem.rightClicked() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatDeliveries.qml000066400000000000000000000054211434616504300275310ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import 'Message.js' as Logic // ============================================================================= Loader{ id: loader property ChatMessageModel chatMessageModel property ParticipantImdnStateProxyModel imdnStatesModel: ParticipantImdnStateProxyModel { chatMessageModel: loader.chatMessageModel } height: visible ? (ChatStyle.composingText.height-5)*(loader.imdnStatesModel.count/2 + 1) : 0 visible:false active: visible sourceComponent: GridView{ id: deliveryLayout cellWidth: parent.width/2; cellHeight: ChatStyle.composingText.height-5 interactive: false model: loader.imdnStatesModel function getText(state, displayName, stateChangeTime){ if(state == LinphoneEnums.ChatMessageStateDelivered) //: 'Send to %1 - %2' Little message to indicate the state of a message //~ Context %1 is someone, %2 is a date/time. The state is that the message has been sent but not received. return qsTr('deliveryDelivered').arg(displayName).arg(stateChangeTime) else if(state == LinphoneEnums.ChatMessageStateDeliveredToUser) //: 'Retrieved by %1 - %2' Little message to indicate the state of a message //~ Context %1 is someone, %2 is a date/time. The state is that the message has been retrieved return qsTr('deliveryDeliveredToUser').arg(displayName).arg(stateChangeTime) else if(state == LinphoneEnums.ChatMessageStateDisplayed) //: 'Read by %1 - %2' Little message to indicate the state of a message //~ Context %1 is someone, %2 is a date/time. The state that the message has been read. return qsTr('deliveryDisplayed').arg(displayName).arg(stateChangeTime) else if(state == LinphoneEnums.ChatMessageStateNotDelivered) //: "%1 have nothing received" Little message to indicate the state of a message //~ Context %1 is someone. The state is that the message hasn't been delivered. return qsTr('deliveryNotDelivered').arg(displayName) else if(state == LinphoneEnums.ChatMessageStateIdle) return '' else //: "Error while sending to %1" Little message to indicate the state of a message //~ Context %1 is someone. The state is that the message hasn't been delivered because of an error. return qsTr('deliveryError').arg(displayName) } delegate:Text{ height: ChatStyle.composingText.height-5 width: GridView.width text: deliveryLayout.getText($modelData.state, $modelData.displayName, UtilsCpp.toDateTimeString($modelData.stateChangeTime)) color: ChatStyle.entry.event.text.color font.pointSize: Units.dp * 8 elide: Text.ElideMiddle } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatFileMessage.qml000066400000000000000000000260471434616504300276310ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import Linphone.Styles 1.0 import Utils 1.0 import Units 1.0 import ColorsList 1.0 import UtilsCpp 1.0 // ============================================================================= // TODO : into Loader Row { id:mainRow property ChatMessageModel chatMessageModel: contentModel && contentModel.chatMessageModel property ContentModel contentModel property bool isOutgoing : chatMessageModel && ( chatMessageModel.isOutgoing || chatMessageModel.state == LinphoneEnums.ChatMessageStateIdle); property int fitWidth: visible ? Math.max( Math.max((thumbnailProvider.sourceComponent == extension ? thumbnailProvider.item.fitWidth : 0) , thumbnailProvider.width + 3*ChatStyle.entry.message.file.margins) , Math.max(ChatStyle.entry.message.file.width, ChatStyle.entry.message.outgoing.areaSize)) : 0 property int fitHeight: visible ? rectangle.height : 0 property bool isAnimatedImage : mainRow.contentModel && mainRow.contentModel.wasDownloaded && UtilsCpp.isAnimatedImage(mainRow.contentModel.filePath) property bool haveThumbnail: mainRow.contentModel && mainRow.contentModel.thumbnail property bool isHovering: thumbnailProvider.state == 'hovered' signal copyAllDone() signal copySelectionDone() signal forwardClicked() height: fitHeight visible: contentModel && !contentModel.isIcalendar() && (contentModel.isFile() || contentModel.isFileTransfer()) && !contentModel.isVoiceRecording() // --------------------------------------------------------------------------- // File message. // --------------------------------------------------------------------------- Item{ width: mainRow.width height:rectangle.height Rectangle { id: rectangle readonly property bool isError: chatMessageModel && Utils.includes([ LinphoneEnums.ChatMessageStateFileTransferError, LinphoneEnums.ChatMessageStateNotDelivered, ], chatMessageModel.state) readonly property bool isUploaded: chatMessageModel && chatMessageModel.state == LinphoneEnums.ChatMessageStateDelivered readonly property bool isDelivered: chatMessageModel && chatMessageModel.state == LinphoneEnums.ChatMessageStateDeliveredToUser readonly property bool isRead: chatMessageModel && chatMessageModel.state == LinphoneEnums.ChatMessageStateDisplayed readonly property bool isTransferring: chatMessageModel && (chatMessageModel.state == LinphoneEnums.ChatMessageStateFileTransferInProgress || chatMessageModel.state == LinphoneEnums.ChatMessageStateInProgress ) property string thumbnail : mainRow.contentModel ? mainRow.contentModel.thumbnail : '' color: 'transparent' anchors.left: parent.left anchors.top: parent.top anchors.leftMargin: ChatStyle.entry.message.file.margins anchors.topMargin: ChatStyle.entry.message.file.margins height: 2*ChatStyle.entry.message.file.margins + (mainRow.isAnimatedImage ? ChatStyle.entry.message.file.heightbetter : thumbnailProvider.sourceComponent == extension ? thumbnailProvider.item.fitHeight : ChatStyle.entry.message.file.height ) width: mainRow.width radius: ChatStyle.entry.message.radius // --------------------------------------------------------------------- // Thumbnail or extension. // --------------------------------------------------------------------- Component { id: thumbnailImage Image { id: thumbnailImageSource property real scaleAnimatorTo : ChatStyle.entry.message.file.animation.thumbnailTo mipmap: SettingsModel.mipmapEnabled source: mainRow.contentModel.thumbnail autoTransform: true fillMode: Image.PreserveAspectFit height: ChatStyle.entry.message.file.height width: height*4/3 Loader{ anchors.fill: parent sourceComponent: Image{// Better quality on zoom mipmap: SettingsModel.mipmapEnabled source:'image://external/'+mainRow.contentModel.filePath autoTransform: true fillMode: Image.PreserveAspectFit visible: status == Image.Ready } asynchronous: true active: thumbnailProvider.state == 'hovered' } } } Component { id: animatedImage AnimatedImage { id: animatedImageSource property real scaleAnimatorTo : ChatStyle.entry.message.file.animation.to mipmap: SettingsModel.mipmapEnabled source: 'file:/'+mainRow.contentModel.filePath autoTransform: true fillMode: Image.PreserveAspectFit height: ChatStyle.entry.message.file.heightbetter width: height*4/3 } } Component { id: extension Rectangle { property int fitWidth: Math.max(downloadText.implicitWidth, Math.max(fileName.visible ? fileName.implicitWidth : 0, fileIcon.iconSize)) + 20 property int fitHeight: fileIcon.iconSize + (fileName.visible ? fileName.implicitHeight + ChatStyle.entry.message.file.spacing : 0 ) + (downloadText.visible? downloadText.implicitHeight + ChatStyle.entry.message.file.spacing : 0) + 2*ChatStyle.entry.message.file.margins property real scaleAnimatorTo : ChatStyle.entry.message.file.animation.to height: fitHeight width: fitWidth color: ChatStyle.entry.message.file.extension.background.color radius: ChatStyle.entry.message.file.extension.radius ColumnLayout{ anchors.fill: parent anchors.topMargin: ChatStyle.entry.message.file.margins anchors.bottomMargin: ChatStyle.entry.message.file.margins spacing: ChatStyle.entry.message.file.spacing Icon{ id: fileIcon Layout.alignment: Qt.AlignCenter icon: extensionText.text != '' ? ChatStyle.entry.message.file.extension.icon : ChatStyle.entry.message.file.extension.unknownIcon iconSize: ChatStyle.entry.message.file.extension.iconSize Layout.preferredHeight: iconSize Layout.preferredWidth: iconSize Text { id: extensionText anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter anchors.bottomMargin: ChatStyle.entry.message.file.spacing width: parent.width - 2*ChatStyle.entry.message.file.spacing color: ChatStyle.entry.message.file.extension.text.color font.bold: true font.pointSize: ChatStyle.entry.message.file.extension.text.pointSize clip: true text: (mainRow.contentModel?Utils.getExtension(mainRow.contentModel.name).toUpperCase():'') horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } RoundProgressBar { id: progressBar anchors.centerIn: parent property int fileSize: mainRow.contentModel ? mainRow.contentModel.fileSize : 0 to: 100 value: mainRow.contentModel ? (fileSize>0 ? Math.floor(100 * mainRow.contentModel.fileOffset / fileSize) : 0) : to visible: rectangle.isTransferring && value != 0 /* Change format? Current is % text: if(mainRow.contentModel){ var fileSize = Utils.formatSize(mainRow.contentModel.fileSize) return progressBar.visible ? Utils.formatSize(mainRow.contentModel.fileOffset) + '/' + fileSize : fileSize }else return '' */ } } Text { id: fileName Layout.fillWidth: true Layout.fillHeight: true visible: mainRow.contentModel && !mainRow.isAnimatedImage && !mainRow.haveThumbnail color: ChatStyle.entry.message.file.extension.text.color elide: Text.ElideRight font.pointSize: ChatStyle.entry.message.file.name.pointSize wrapMode: Text.WrapAnywhere horizontalAlignment: Qt.AlignCenter text: (mainRow.contentModel ? mainRow.contentModel.name : '') } Text{ id: downloadText Layout.fillWidth: true Layout.fillHeight: true Layout.preferredHeight: visible ? ChatStyle.entry.message.file.download.height : 0 //: 'Cancel' : Message link to cancel a transfer (upload/download) text: mainRow.contentModel ? rectangle.isTransferring ? qsTr('fileTransferCancel') //: 'Download' : Message link to download a file : qsTr('fileTransferDownload') +' ('+Utils.formatSize(mainRow.contentModel.fileSize)+')' : '' font.underline: true font.pointSize: ChatStyle.entry.message.file.download.pointSize color:ChatStyle.entry.message.file.extension.text.color visible: (mainRow.contentModel? (!mainItem.isOutgoing && !mainRow.contentModel.wasDownloaded) || rectangle.isTransferring : false) horizontalAlignment: Qt.AlignCenter verticalAlignment: Qt.AlignCenter } } } } Loader { id: thumbnailProvider sourceComponent: (mainRow.contentModel ? (mainRow.isAnimatedImage ? animatedImage : (mainRow.haveThumbnail ? thumbnailImage : extension ) ) : undefined) ScaleAnimator { id: thumbnailProviderAnimator target: thumbnailProvider duration: ChatStyle.entry.message.file.animation.duration easing.type: Easing.InOutQuad from: 1.0 } states: State { name: 'hovered' } transitions: [ Transition { from: '' to: 'hovered' ScriptAction { script: { if(thumbnailProvider.sourceComponent != extension){ if (thumbnailProviderAnimator.running) { thumbnailProviderAnimator.running = false } thumbnailProvider.z = Constants.zPopup thumbnailProviderAnimator.to = thumbnailProvider.item.scaleAnimatorTo thumbnailProviderAnimator.running = true } } } }, Transition { from: 'hovered' to: '' ScriptAction { script: { if(thumbnailProvider.sourceComponent != extension){ if (thumbnailProviderAnimator.running) { thumbnailProviderAnimator.running = false } thumbnailProviderAnimator.to = 1.0 thumbnailProviderAnimator.running = true thumbnailProvider.z = 0 } } } } ] } } MouseArea { function handleMouseMove (mouse) { thumbnailProvider.state = Utils.pointIsInItem(this, thumbnailProvider, mouse) ? 'hovered' : '' } anchors.fill: parent visible: true onClicked: { if(rectangle.isTransferring) mainRow.contentModel.cancelDownloadFile() else if (Utils.pointIsInItem(this, thumbnailProvider, mouse)) { mainRow.contentModel.openFile() } else if (mainRow.contentModel && mainRow.contentModel.wasDownloaded) { mainRow.contentModel.openFile(true)// Show directory thumbnailProvider.state = '' } else { mainRow.contentModel.downloadFile() thumbnailProvider.state = '' } } onExited: thumbnailProvider.state = '' onMouseXChanged: handleMouseMove.call(this, mouse) onMouseYChanged: handleMouseMove.call(this, mouse) } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatFilePreview.qml000066400000000000000000000041411434616504300276550ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 import Units 1.0 import 'Chat.js' as Logic // ============================================================================= Item{ visible: mainListView.count > 0 Layout.preferredHeight: visible ? ChatFilePreviewStyle.height : 0 function addFile(path){ contents.addFile(path) } ScrollableListView{ id: mainListView spacing: ChatFilePreviewStyle.filePreview.closeButton.iconSize anchors.fill: parent anchors.rightMargin: ChatStyle.rightButtonMargin + ChatStyle.rightButtonLMargin + ChatStyle.rightButtonSize orientation: Qt.Horizontal model: ContentProxyModel{ id: contents } header:Component{ Item{ width: ChatFilePreviewStyle.filePreview.closeButton.iconSize/2 height:mainListView.height } } footer: Component{ Item{ width: ChatFilePreviewStyle.filePreview.closeButton.iconSize height:mainListView.height } } delegate: FileView{ height:mainListView.height-ChatFilePreviewStyle.filePreview.heightMargins width: height * ChatFilePreviewStyle.filePreview.format anchors.verticalCenter: parent ? parent.verticalCenter : ScrollableListView.verticalCenter anchors.verticalCenterOffset: 7 thumbnail: $modelData.thumbnail name: $modelData.name animationScale: 1.1 onClickOnFile: { $modelData.openFile() } ActionButton{ anchors.bottom: parent.top anchors.bottomMargin: -height/2 anchors.left: parent.right anchors.leftMargin: -width/2 isCustom: true backgroundRadius: width colorSet: ChatFilePreviewStyle.filePreview.removeButton z: parent.z+1 onClicked:{ contents.remove($modelData) } } } } ActionButton{ anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: ChatStyle.rightButtonMargin isCustom: true backgroundRadius: width colorSet: ChatFilePreviewStyle.filePreview.closeButton z: parent.z+1 onClicked:{ contents.clear() } } }linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatForwardMessage.qml000066400000000000000000000035531434616504300303530ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import ColorsList 1.0 import 'Message.js' as Logic // ============================================================================= Item { id: mainItem property ChatMessageModel mainChatMessageModel property int maxWidth : parent.width property int fitWidth: visible ? headerArea.fitWidth + 7 + ChatForwardMessageStyle.padding * 2 : 0 property int fitHeight: visible ? icon.height : 0 property font customFont : SettingsModel.textMessageFont visible: mainChatMessageModel && mainChatMessageModel.isForward width: maxWidth > fitWidth ? fitWidth : maxWidth height: fitHeight ColumnLayout{ anchors.fill: parent spacing: 5 Row{ id: headerArea property int fitWidth: icon.width + headerText.implicitWidth Layout.fillHeight: true Layout.topMargin: 5 Icon{ id: icon icon: ChatForwardMessageStyle.header.forwardIcon.icon iconSize: ChatForwardMessageStyle.header.forwardIcon.iconSize height: iconSize overwriteColor: ChatForwardMessageStyle.header.color } Text{ id: headerText height: icon.height verticalAlignment: Qt.AlignVCenter // Anonymized forward : do not get display name, this is wanted. //property string forwardInfo: mainChatMessageModel ? mainChatMessageModel.getForwardInfoDisplayName : '' //: 'Forwarded' : Header on a message that contains a forward. text: qsTr('Forwarded')// + (forwardInfo ? ' : ' +forwardInfo : '') font.family: mainItem.customFont.family font.pointSize: Units.dp * (mainItem.customFont.pointSize + ChatForwardMessageStyle.header.pointSizeOffset) color: ChatForwardMessageStyle.header.color } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatMenu.qml000066400000000000000000000066371434616504300263540ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import 'Message.js' as Logic // ============================================================================= // ChatMenu Item { id: container property string lastTextSelected property ChatMessageModel chatMessageModel property int deliveryCount : 0 property bool deliveryVisible: false property bool isCallEvent: false // the menu point to an event signal deliveryStatusClicked() signal removeEntryRequested() signal copyAllDone() signal copySelectionDone() signal replyClicked() signal forwardClicked() function open(){ messageMenu.popup() } property string chatTextContent: chatMessageModel && chatMessageModel.content Menu { id: messageMenu menuStyle : MenuStyle.aux MenuItem { //: 'Copy all' : Text menu to copy all message text into clipboard text: (container.lastTextSelected == '' ? qsTr('menuCopyAll') //: 'Copy' : Text menu to copy selected text in message into clipboard : qsTr('menuCopy')) iconMenu: MenuItemStyle.copy.icon iconSizeMenu: MenuItemStyle.copy.iconSize iconLayoutDirection: Qt.RightToLeft menuItemStyle : MenuItemStyle.aux onTriggered: { if( container.lastTextSelected == ''){ Clipboard.text = container.chatTextContent container.copyAllDone(); }else{ Clipboard.text = container.lastTextSelected container.copySelectionDone() } } visible: chatTextContent != '' } MenuItem { enabled: TextToSpeech.available text: qsTr('menuPlayMe') iconMenu: MenuItemStyle.speaker.icon iconSizeMenu: MenuItemStyle.speaker.iconSize iconLayoutDirection: Qt.RightToLeft menuItemStyle : MenuItemStyle.aux onTriggered: TextToSpeech.say(container.chatTextContent) visible: chatTextContent != '' } MenuItem { //: 'Forward' : Forward a message from menu text: qsTr('menuForward') iconMenu: MenuItemStyle.forward.icon iconSizeMenu: MenuItemStyle.forward.iconSize iconLayoutDirection: Qt.RightToLeft menuItemStyle : MenuItemStyle.aux onTriggered: container.forwardClicked() visible: !isCallEvent } MenuItem { //: 'Reply' : Reply to a message from menu text: qsTr('menuReply') iconMenu: MenuItemStyle.reply.icon iconSizeMenu: MenuItemStyle.reply.iconSize iconLayoutDirection: Qt.RightToLeft menuItemStyle : MenuItemStyle.aux onTriggered: container.replyClicked() visible: !isCallEvent } MenuItem { //: 'Hide delivery status' : Item menu that lead to IMDN of a message text: (deliveryVisible ? qsTr('menuHideDeliveryStatus') //: 'Delivery status' : Item menu that lead to IMDN of a message : qsTr('menuDeliveryStatus') ) iconMenu: MenuItemStyle.imdn.icon iconSizeMenu: MenuItemStyle.imdn.iconSize iconLayoutDirection: Qt.RightToLeft menuItemStyle : MenuItemStyle.aux visible: container.deliveryCount > 0 onTriggered: container.deliveryStatusClicked() } MenuItem { //: 'Delete' : Item menu to delete a message text: qsTr('menuDelete') iconMenu: MenuItemStyle.deleteEntry.icon iconSizeMenu: MenuItemStyle.deleteEntry.iconSize iconLayoutDirection: Qt.RightToLeft menuItemStyle : MenuItemStyle.auxError onTriggered: container.removeEntryRequested() } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatMessagePreview.qml000066400000000000000000000035271434616504300303710ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 import Units 1.0 import 'Chat.js' as Logic // ============================================================================= ColumnLayout{ property alias replyChatRoomModel : replyPreview.chatRoomModel property int maxHeight: parent.height property int fitHeight: (replyPreview.visible ? replyPreview.height + replySeparator.height: 0 ) + (audioPreview.visible ? audioPreview.height + audioSeparator.height: 0) + (filesPreview.visible ? filesPreview.height + filesSeparator.height: 0) property alias replyRightMargin: replyPreview.rightMargin property alias replyLeftMargin: replyPreview.leftMargin spacing: 0 Layout.preferredHeight: fitHeight Layout.maximumHeight: fitHeight> maxHeight ? maxHeight : fitHeight // ?? just using maxHeight doesn't work. function hide(){ } function addFile(path){ filesPreview.addFile(path) } ChatReplyPreview{ id: replyPreview Layout.fillWidth: true maxHeight: parent.maxHeight - (audioPreview.visible ? audioPreview.height + audioSeparator.height: 0) - (filesPreview.visible ? filesPreview.height + filesSeparator.height: 0) } Item{ id: replySeparator visible: replyPreview.visible Layout.preferredHeight: visible ? ChatStyle.separatorHeight : 0 Layout.fillWidth: true } ChatAudioPreview{ id: audioPreview Layout.fillWidth: true } Item{ id: audioSeparator visible: audioPreview.visible Layout.preferredHeight: visible ? ChatStyle.separatorHeight : 0 Layout.fillWidth: true } ChatFilePreview{ id: filesPreview Layout.fillWidth: true } Item{ id: filesSeparator visible: filesPreview.visible Layout.preferredHeight: visible ? ChatStyle.separatorHeight : 0 Layout.fillWidth: true } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatReplyMessage.qml000066400000000000000000000115411434616504300300360ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import ColorsList 1.0 import 'Message.js' as Logic // ============================================================================= Item { id: mainItem property ChatMessageModel chatMessageModel property ChatMessageModel mainChatMessageModel property int maxWidth : parent.width property int headerHeight: ChatReplyMessageStyle.header.replyIcon.iconSize property int replyHeight: (chatMessageModel ? replyMessage.height + usernameReplied.implicitHeight + ChatStyle.entry.message.padding * 3 + 3 : 0) property int fitWidth: visible ? Math.max(usernameReplied.implicitWidth + replyMessage.fitWidth , headerArea.fitWidth) + 7 + ChatReplyMessageStyle.padding * 2 : 0 property int fitHeight: visible ? headerHeight + replyHeight : 0 property font customFont : SettingsModel.textMessageFont visible: mainChatMessageModel && mainChatMessageModel.isReply width: maxWidth < 0 || maxWidth > fitWidth ? fitWidth : maxWidth height: fitHeight onMainChatMessageModelChanged: if( mainChatMessageModel && mainChatMessageModel.replyChatMessageModel) chatMessageModel = mainChatMessageModel.replyChatMessageModel signal goToMessage(ChatMessageModel message) ColumnLayout{ anchors.fill: parent spacing: 5 Row{ id: headerArea property int fitWidth: icon.width + headerText.implicitWidth Layout.preferredHeight: headerHeight Layout.topMargin: 5 Icon{ id: icon icon: ChatReplyMessageStyle.header.replyIcon.icon iconSize: ChatReplyMessageStyle.header.replyIcon.iconSize height: iconSize overwriteColor: ChatReplyMessageStyle.header.color MouseArea{ anchors.fill: parent onClicked: mainItem.goToMessage(mainItem.chatMessageModel) } } Text{ id: headerText height: parent.height verticalAlignment: Qt.AlignVCenter //: 'Reply' : Header on a message that contains a reply. text: qsTr('headerReply') + (chatMessageModel || !mainChatMessageModel? '' : ' - ' + mainChatMessageModel.fromDisplayNameReplyMessage) font.family: mainItem.customFont.family font.pointSize: Units.dp * (mainItem.customFont.pointSize + ChatReplyMessageStyle.header.pointSizeOffset) color: ChatReplyMessageStyle.header.color MouseArea{ anchors.fill: parent onClicked: mainItem.goToMessage(mainItem.chatMessageModel) } } } Rectangle{ id: replyArea Layout.fillWidth: true Layout.fillHeight: true Layout.bottomMargin: ChatStyle.entry.message.padding Layout.leftMargin: 10 Layout.rightMargin: 10 Rectangle{ anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom width: 7 color: chatMessageModel && chatMessageModel.isOutgoing ? ChatReplyMessageStyle.replyArea.outgoingMarkColor : ChatReplyMessageStyle.replyArea.incomingMarkColor } radius: 5 color: ChatReplyMessageStyle.replyArea.backgroundColor visible: chatMessageModel != undefined Text{ id: usernameReplied anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 3 leftPadding: 2 * ChatStyle.entry.message.padding text: mainChatMessageModel && mainChatMessageModel.fromDisplayNameReplyMessage font.family: mainItem.customFont.family font.pointSize: Units.dp * (mainItem.customFont.pointSize + ChatReplyMessageStyle.replyArea.usernamePointSizeOffset) font.weight: Font.Bold color: ChatReplyMessageStyle.replyArea.foregroundColor } ScrollableListView { id: replyMessage property int fitWidth : 0 hideScrollBars: true anchors.top: usernameReplied.bottom anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 3 anchors.leftMargin: 5 interactive: false clip: false function updateWidth(){ var maxWidth = 0 for(var child in replyMessage.contentItem.children) { var a = replyMessage.contentItem.children[child].fitWidth if(a) maxWidth = Math.max(maxWidth,a) } fitWidth = maxWidth } model: ContentProxyModel{ chatMessageModel: mainItem.chatMessageModel } onContentHeightChanged: Qt.callLater( function(){replyMessage.height = replyMessage.contentHeight}) delegate: ChatContent{ contentModel: $modelData textColor: ChatReplyMessageStyle.replyArea.foregroundColor onFitWidthChanged:{ replyMessage.updateWidth() } Rectangle{ anchors.left: parent.left anchors.right: parent.right color: ChatStyle.entry.separator.color height: visible ? ChatStyle.entry.separator.width : 0 visible: (index !== (replyMessage.count - 1)) } } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatReplyPreview.qml000066400000000000000000000077341434616504300301040ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 import Units 1.0 import 'Chat.js' as Logic // ============================================================================= Rectangle{ id: replyPreviewBlock property ChatRoomModel chatRoomModel property int maxHeight : parent.maxHeight Layout.preferredHeight: visible ? Math.min(messageContentsList.height + replyPreviewHeaderArea.implicitHeight + 15, replyPreviewBlock.maxHeight) : 0 property int leftMargin: 10 property int rightMargin: 10 color: ChatStyle.replyPreview.backgroundColor radius: 10 state: chatRoomModel && chatRoomModel.reply ? 'showed' : 'hidden' // Remove bottom corners function hide(){ state = 'hidden' } MouseArea{// Block mouse events anchors.fill: parent } Rectangle{ anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right height: parent.radius color: parent.color } //------------------------- ColumnLayout{ anchors.fill: parent anchors.leftMargin: replyPreviewBlock.leftMargin anchors.rightMargin: replyPreviewBlock.rightMargin anchors.bottomMargin: 5 spacing: 0 RowLayout{ id: replyPreviewHeaderArea Layout.fillWidth: true Layout.preferredHeight: replyPreviewTitleText.implicitHeight Layout.topMargin: 10 spacing: 5 Icon{ icon: ChatStyle.replyPreview.icon overwriteColor: ChatStyle.replyPreview.iconColor iconSize: 20 } Text{ id: replyPreviewTitleText Layout.fillWidth: true Layout.preferredHeight: implicitHeight //: 'Reply to %1' : Title for a reply preview to know who said what. text: replyPreviewBlock.chatRoomModel && replyPreviewBlock.chatRoomModel.reply ? qsTr('titleReply').arg(replyPreviewBlock.chatRoomModel.reply.fromDisplayName) : '' font.pointSize: ChatStyle.replyPreview.headerPointSize font.weight: Font.Bold color: ChatStyle.replyPreview.headerTextColor } } Flickable { id: replyPreviewTextArea ScrollBar.vertical: ForceScrollBar {visible: replyPreviewTextArea.height < messageContentsList.height} boundsBehavior: Flickable.StopAtBounds contentHeight: messageContentsList.height contentWidth: width - ScrollBar.vertical.width flickableDirection: Flickable.VerticalFlick clip: true Layout.fillHeight: true Layout.fillWidth: true ListView { id: messageContentsList anchors.left: parent.left anchors.right: parent.right model: ContentProxyModel{ chatMessageModel: replyPreviewBlock.chatRoomModel && replyPreviewBlock.chatRoomModel.reply } height: contentHeight clip: true delegate: ChatContent{ contentModel: $modelData Rectangle{ anchors.left: parent.left anchors.right: parent.right color: ChatStyle.entry.separator.color height: visible ? ChatStyle.entry.separator.width : 0 visible: (index !== (messageContentsList.count - 1)) } } } } } ActionButton{ anchors.right:parent.right anchors.rightMargin: ChatStyle.rightButtonMargin anchors.verticalCenter: parent.verticalCenter height: ChatStyle.replyPreview.closeButton.iconSize isCustom: true backgroundRadius: 90 colorSet: ChatStyle.replyPreview.closeButton onClicked: chatRoomModel.reply = null } states: [ State { name: "hidden" PropertyChanges { target: replyPreviewBlock; opacity: 0 ; visible: false } }, State { name: "showed" PropertyChanges { target: replyPreviewBlock; opacity: 1 ; visible: true} } ] transitions: [ Transition { from: "*"; to: "showed" SequentialAnimation{ ScriptAction{ script: replyPreviewBlock.visible = true } NumberAnimation{ properties: "opacity"; easing.type: Easing.OutBounce; duration: 500 } } }, Transition { from: "*"; to: "hidden" SequentialAnimation{ NumberAnimation{ properties: "opacity"; duration: 250 } ScriptAction{ script: replyPreviewBlock.visible = false } } } ] } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/ChatTextMessage.qml000066400000000000000000000052331434616504300276700ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import ColorsList 1.0 import 'Message.js' as Logic // TODO : into Loader // ============================================================================= TextEdit { id: message property ContentModel contentModel property string lastTextSelected : '' property font customFont : SettingsModel.textMessageFont property int fitHeight: visible ? contentHeight + padding + 8 : 0 property int fitWidth: visible ? implicitWidth + 2: 0 // add 2 because there is a bug on border that lead to not fit text exactly signal rightClicked() property int removeWarningFromBindingLoop : implicitWidth // Just a dummy variable to remove meaningless binding loop on implicitWidth height: fitHeight width: parent.width visible: contentModel && contentModel.isText() clip: false padding: ChatStyle.entry.message.padding textMargin: 0 readOnly: true selectByMouse: true font.family: customFont.family font.pointSize: Units.dp * customFont.pointSize text: visible ? UtilsCpp.encodeTextToQmlRichFormat(contentModel.text, { imagesHeight: ChatStyle.entry.message.images.height, imagesWidth: ChatStyle.entry.message.images.width }) : '' // See http://doc.qt.io/qt-5/qml-qtquick-text.html#textFormat-prop // and http://doc.qt.io/qt-5/richtext-html-subset.html textFormat: Text.RichText // To supports links and imgs. wrapMode: TextEdit.Wrap onLinkActivated: Qt.openUrlExternally(link) onSelectedTextChanged:{ if(selectedText != '') lastTextSelected = selectedText else { if( mouseArea.keepLastSelection) { mouseArea.keepLastSelection = false select(mouseArea.lastStartSelection, mouseArea.lastEndSelection) } } } onActiveFocusChanged: { if(activeFocus) { lastTextSelected = '' mouseArea.keepLastSelection = false } deselect() } MouseArea { id: mouseArea property bool keepLastSelection: false property int lastStartSelection:0 property int lastEndSelection:0 anchors.fill: parent propagateComposedEvents: true hoverEnabled: false scrollGestureEnabled: false cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor acceptedButtons: Qt.RightButton onClicked: { if(!keepLastSelection) { lastStartSelection = parent.selectionStart lastEndSelection = parent.selectionEnd } keepLastSelection = true message.rightClicked() mouse.accepted = true } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/Event.qml000066400000000000000000000102411434616504300257130ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import Linphone.Styles 1.0 import Utils 1.0 // ============================================================================= Row { id: mainItem property QtObject iconData property string translation Component.onCompleted: { if ($chatEntry.status == LinphoneEnums.CallStatusSuccess) { if(!$chatEntry.isStart){ iconData = ChatStyle.entry.event.endedCall translation ='endedCall' }else if($chatEntry.isOutgoing ){ iconData = ChatStyle.entry.event.outgoingCall translation ='outgoingCall' }else{ iconData = ChatStyle.entry.event.incomingCall translation ='incomingCall' } }else if($chatEntry.status == LinphoneEnums.CallStatusDeclined) { if($chatEntry.isOutgoing ){ iconData = ChatStyle.entry.event.declinedOutgoingCall translation ='declinedOutgoingCall' }else{ iconData = ChatStyle.entry.event.declinedIncomingCall translation ='declinedIncomingCall' } }else if($chatEntry.status == LinphoneEnums.CallStatusMissed) { if($chatEntry.isOutgoing ){ iconData = ChatStyle.entry.event.missedOutgoingCall translation ='missedOutgoingCall' }else{ iconData = ChatStyle.entry.event.missedIncomingCall translation ='missedIncomingCall' } }else if($chatEntry.status == LinphoneEnums.CallStatusAborted) { if($chatEntry.isOutgoing ){ iconData = ChatStyle.entry.event.outgoingCall translation ='outgoingCall' }else{ iconData = ChatStyle.entry.event.incomingCall translation ='incomingCall' } }else if($chatEntry.status == LinphoneEnums.CallStatusDeclined) { if($chatEntry.isOutgoing ){ iconData = ChatStyle.entry.event.declinedOutgoingCall translation ='declinedOutgoingCall' }else{ iconData = ChatStyle.entry.event.declinedIncomingCall translation ='declinedIncomingCall' } }else if($chatEntry.status == LinphoneEnums.CallStatusEarlyAborted) { if($chatEntry.isOutgoing ){ iconData = ChatStyle.entry.event.missedOutgoingCall translation ='missedOutgoingCall' }else{ iconData = ChatStyle.entry.event.missedIncomingCall translation ='missedIncomingCall' } }else if($chatEntry.status == LinphoneEnums.CallStatusAcceptedElsewhere) { if($chatEntry.isOutgoing ){ iconData = ChatStyle.entry.event.outgoingCall translation ='outgoingCall' }else{ iconData = ChatStyle.entry.event.incomingCall translation ='incomingCall' } }else if($chatEntry.status == LinphoneEnums.CallStatusDeclinedElsewhere) { if($chatEntry.isOutgoing ){ iconData = ChatStyle.entry.event.declinedOutgoingCall translation ='declinedOutgoingCall' }else{ iconData = ChatStyle.entry.event.declinedIncomingCall translation ='declinedIncomingCall' } }else { iconData = ChatStyle.entry.event.unknownCallEvent translation = 'unknownCallEvent' } } height: ChatStyle.entry.lineHeight spacing: ChatStyle.entry.message.extraContent.spacing Icon { height: parent.height icon: mainItem.iconData ? mainItem.iconData.icon : null overwriteColor: mainItem.iconData ? mainItem.iconData.color: null iconSize: ChatStyle.entry.event.iconSize width: ChatStyle.entry.metaWidth } Text { id:textArea Component { // Never created. // Private data for `lupdate`. Item { property var i18n: [ QT_TR_NOOP('declinedIncomingCall'), QT_TR_NOOP('declinedOutgoingCall'), QT_TR_NOOP('endedCall'), QT_TR_NOOP('incomingCall'), QT_TR_NOOP('missedIncomingCall'), QT_TR_NOOP('missedOutgoingCall'), QT_TR_NOOP('outgoingCall') ] } } color: ChatStyle.entry.event.text.color font { bold: true pointSize: ChatStyle.entry.event.text.pointSize } height: parent.height text: mainItem.translation ? qsTr(mainItem.translation) : '' verticalAlignment: Text.AlignVCenter ChatMenu{ id:chatMenu height: parent.height width: textArea.width deliveryCount: 0 isCallEvent: true onRemoveEntryRequested: removeEntry() } } ActionButton { height: ChatStyle.entry.menu.iconSize isCustom: true backgroundRadius: 8 colorSet: ChatStyle.entry.menu visible: isHoverEntry() onClicked: chatMenu.open() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/IncomingMessage.qml000066400000000000000000000045311434616504300277070ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 // ============================================================================= RowLayout { id:mainRow Layout.fillWidth: true signal copyAllDone() signal copySelectionDone() signal replyClicked() signal forwardClicked() signal goToMessage(ChatMessageModel message) signal conferenceIcsCopied() implicitHeight: message.height spacing: 0 Item { Layout.alignment: Qt.AlignTop Layout.preferredHeight: ChatStyle.entry.lineHeight Layout.preferredWidth: ChatStyle.entry.metaWidth Avatar { id:avatar anchors.centerIn: parent height: ChatStyle.entry.message.incoming.avatarSize image: $chatEntry.contactModel? $chatEntry.contactModel.vcard.avatar : '' //chat.sipAddressObserver.contact ? chat.sipAddressObserver.contact.vcard.avatar : '' username: $chatEntry.fromDisplayName width: ChatStyle.entry.message.incoming.avatarSize // The avatar is only visible for the first message of a incoming messages sequence. visible: { if (index <= 0) { return true // 1. First message, so visible. } var previousEntry = proxyModel.getAt(index - 1) return !$chatEntry.isOutgoing && (// Only outgoing !previousEntry //No previous entry || previousEntry.type != ChatRoomModel.MessageEntry // Previous entry is a message || previousEntry.fromSipAddress != $chatEntry.fromSipAddress // Different user || (new Date(previousEntry.timestamp)).setHours(0, 0, 0, 0) != (new Date($chatEntry.timestamp)).setHours(0, 0, 0, 0) // Same day == section ) } TooltipArea{ delay:0 text:avatar.username+'\n'+$chatEntry.fromSipAddress tooltipParent:mainRow isClickable: true onDoubleClicked: { window.mainSearchBar.text = $chatEntry.fromSipAddress } } } } Message { id: message onCopyAllDone: parent.copyAllDone() onCopySelectionDone: parent.copySelectionDone() onReplyClicked: parent.replyClicked() onForwardClicked: parent.forwardClicked() onGoToMessage: parent.goToMessage(message) onConferenceIcsCopied: parent.conferenceIcsCopied() Layout.fillWidth: true // Not a style. Workaround to avoid a 0 width. // Arbitrary value. Layout.minimumWidth: 1 backgroundColor: ChatStyle.entry.message.incoming.backgroundColor } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/Message.js000066400000000000000000000031111434616504300260370ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `Message.qml` Logic. // ============================================================================= // See: `ensureVisible` on http://doc.qt.io/qt-5/qml-qtquick-textedit.html function ensureVisible (cursor) { // Case 1: No focused. if (!message.activeFocus) { return } // Case 2: Scroll up. var contentItem = chat.contentItem var contentY = chat.contentY var messageY = message.mapToItem(contentItem, 0, 0).y + cursor.y if (contentY >= messageY) { chat.contentY = messageY return } // Case 3: Scroll down. var chatHeight = chat.height var cursorHeight = cursor.height if (contentY + chatHeight <= messageY + cursorHeight) { chat.contentY = messageY + cursorHeight - chatHeight } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/Message.qml000066400000000000000000000136631434616504300262310ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Linphone 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import TextToSpeech 1.0 import Utils 1.0 import Units 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import ColorsList 1.0 import 'Message.js' as Logic // ============================================================================= Item { id: container // --------------------------------------------------------------------------- property alias backgroundColor: rectangle.color default property alias _content: content.data // --------------------------------------------------------------------------- signal copyAllDone() signal copySelectionDone() signal replyClicked() signal forwardClicked() signal goToMessage(ChatMessageModel message) signal conferenceIcsCopied() // --------------------------------------------------------------------------- property string lastTextSelected implicitHeight: (deliveryLayout.visible? deliveryLayout.height : 0) +(ephemeralTimerRow.visible? 16 : 0) + messageData.height Rectangle { id: rectangle property int maxWidth: parent.width property int dataWidth: maxWidth property bool ephemeral : $chatEntry.isEphemeral function updateWidth(){ var maxWidth = Math.max(forwardMessage.fitWidth, replyMessage.fitWidth) for(var child in messageContentsList.contentItem.children) { var a = messageContentsList.contentItem.children[child].fitWidth if(a) maxWidth = Math.max(maxWidth,a) } rectangle.dataWidth = maxWidth } height: parent.height - (deliveryLayout.visible? deliveryLayout.height : 0) radius: ChatStyle.entry.message.radius clip: false width: ( ephemeralTimerRow.visible && dataWidth < ephemeralTimerRow.width + 2*ChatStyle.entry.message.padding ? ephemeralTimerRow.width + 2*ChatStyle.entry.message.padding : Math.min(dataWidth, maxWidth) ) // --------------------------------------------------------------------------- // Message. // --------------------------------------------------------------------------- Column{ id: messageData anchors.left: parent.left anchors.right: parent.right spacing: 0 ChatForwardMessage{ id: forwardMessage mainChatMessageModel: $chatEntry visible: $chatEntry.isForward maxWidth: container.width onFitWidthChanged:{ rectangle.updateWidth() } } ChatReplyMessage{ id: replyMessage z: 1 mainChatMessageModel: $chatEntry visible: $chatEntry.isReply maxWidth: container.width onFitWidthChanged:{ rectangle.updateWidth() } onGoToMessage: container.goToMessage(message) } ListView { id: messageContentsList anchors.left: parent.left anchors.right: parent.right visible: count > 0 spacing: 0 clip: false model: ContentProxyModel{ chatMessageModel: $chatEntry } height: contentHeight boundsBehavior: Flickable.StopAtBounds interactive: false delegate: ChatContent{ maxWidth: container.width contentModel: $modelData onFitWidthChanged:{ rectangle.updateWidth() } onLastTextSelectedChanged: container.lastTextSelected= lastTextSelected onRightClicked: chatMenu.open() onConferenceIcsCopied: container.conferenceIcsCopied() onFileIsHoveringChanged: menuButton.visible = !fileIsHovering Rectangle{ anchors.left: parent.left anchors.right: parent.right color: ChatStyle.entry.separator.color height: visible ? ChatStyle.entry.separator.width : 0 visible: (index !== (messageContentsList.count - 1)) } } } } Row{ id:ephemeralTimerRow anchors.right:parent.right anchors.bottom:parent.bottom anchors.rightMargin : 5 visible:$chatEntry.isEphemeral Text{ id: ephemeralText anchors.bottom: parent.bottom anchors.bottomMargin: 5 text: $chatEntry.ephemeralExpireTime > 0 ? Utils.formatElapsedTime($chatEntry.ephemeralExpireTime) : Utils.formatElapsedTime($chatEntry.ephemeralLifetime) color: ChatStyle.ephemeralTimer.timerColor font.pointSize: Units.dp * 8 Timer{ running:parent.visible interval: 1000 repeat:true onTriggered: if($chatEntry && $chatEntry.getEphemeralExpireTime() > 0 ) parent.text = Utils.formatElapsedTime($chatEntry.getEphemeralExpireTime())// Use the function } } Icon{ anchors.verticalCenter: ephemeralText.verticalCenter icon: ChatStyle.ephemeralTimer.icon overwriteColor: ChatStyle.ephemeralTimer.timerColor iconSize: ChatStyle.ephemeralTimer.iconSize } } } // --------------------------------------------------------------------------- // Extra content. // --------------------------------------------------------------------------- Item { id: content anchors { left: rectangle.right leftMargin: ChatStyle.entry.message.extraContent.leftMargin } } ChatDeliveries{ id: deliveryLayout anchors.top:rectangle.bottom anchors.left:parent.left anchors.right:parent.right anchors.rightMargin: 50 chatMessageModel: $chatEntry } ActionButton { id: menuButton anchors.left:rectangle.right anchors.leftMargin: -10 anchors.top:rectangle.top anchors.topMargin: 5 height: ChatStyle.entry.menu.iconSize isCustom: true backgroundRadius: 8 colorSet : ChatStyle.entry.menu visible: isHoverEntry() onClicked: chatMenu.open() } ChatMenu{ id:chatMenu height: parent.height width: rectangle.width chatMessageModel: $chatEntry lastTextSelected: container.lastTextSelected deliveryCount: deliveryLayout.imdnStatesModel.count onDeliveryStatusClicked: deliveryLayout.visible = !deliveryLayout.visible onRemoveEntryRequested: removeEntry() deliveryVisible: deliveryLayout.visible onCopyAllDone: container.copyAllDone() onCopySelectionDone: container.copySelectionDone() onReplyClicked: container.replyClicked() onForwardClicked: container.forwardClicked() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/Notice.qml000066400000000000000000000123071434616504300260600ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import Units 1.0 import ColorsList 1.0 // ============================================================================= RowLayout{ id: mainLayout property string _type: { var status = $chatEntry.eventLogType var type = $chatEntry.status if(type == ChatNoticeModel.NoticeUnreadMessages) //: '%1 unread messages' : Little message to show on an event where unread messages begin. return qsTr('unreadMessageNotice', '', parseInt($chatEntry.name)) if (status == LinphoneEnums.EventLogTypeConferenceCreated) { //: 'You have joined the group' : Little message to show on the event when the user join the chat group. return qsTr('conferenceCreatedEvent'); } if (status == LinphoneEnums.EventLogTypeConferenceTerminated) { //: 'You have left the group' : Little message to show on the event when the user leave the chat group. return qsTr('conferenceCreatedTerminated'); } if (status == LinphoneEnums.EventLogTypeConferenceCallStarted) { return 'EventLogTypeConferenceCallStarted'; } if (status == LinphoneEnums.EventLogTypeConferenceCallEnded) { return 'EventLogTypeConferenceCallEnded'; } if (status == LinphoneEnums.EventLogTypeConferenceChatMessage) { return 'EventLogTypeConferenceChatMessage'; } if (status == LinphoneEnums.EventLogTypeConferenceParticipantAdded) { //: '%1 has joined' : Little message to show on the event when someone join the chat group. return qsTr('conferenceParticipantAddedEvent'); } if (status == LinphoneEnums.EventLogTypeConferenceParticipantRemoved) { //: '%1 has left' : Little message to show on the event when someone leave the chat group return qsTr('conferenceParticipantRemovedEvent'); } if (status == LinphoneEnums.EventLogTypeConferenceParticipantSetAdmin) { //: '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody return qsTr('conferenceParticipantSetAdminEvent'); } if (status == LinphoneEnums.EventLogTypeConferenceParticipantUnsetAdmin) { //: '%1 is no more an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody return qsTr('conferencePArticipantUnsetAdminEvent'); } if (status == LinphoneEnums.EventLogTypeConferenceParticipantDeviceAdded) { return 'EventLogTypeConferenceParticipantDeviceAdded'; } if (status == LinphoneEnums.EventLogTypeConferenceParticipantDeviceRemoved) { return 'EventLogTypeConferenceParticipantDeviceRemoved'; } //if (status == LinphoneEnums.EventLogTypeConferenceParticipantDeviceMediaChanged) { //return 'EventLogTypeConferenceParticipantDeviceMediaChanged'; //} if (status == LinphoneEnums.EventLogTypeConferenceAvailableMediaChanged) { return 'EventLogTypeConferenceAvailableMediaChanged'; } if (status == LinphoneEnums.EventLogTypeConferenceSecurityEvent) { //: 'Security level degraded by %1': Little message to show on the event when a security level has been lost. return qsTr('conferenceSecurityEvent'); } if (status == LinphoneEnums.EventLogTypeConferenceEphemeralMessageLifetimeChanged) { //: 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time return qsTr('conferenceEphemeralMessageLifetimeChangedEvent'); } if (status == LinphoneEnums.EventLogTypeConferenceEphemeralMessageEnabled) { //: 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time return qsTr('conferenceEphemeralMessageEnabledEvent'); } if (status == LinphoneEnums.EventLogTypeConferenceEphemeralMessageDisabled) { //: 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. return qsTr('conferenceEphemeralMessageDisabledEvent'); } if (status == LinphoneEnums.EventLogTypeConferenceSubjectChanged) { //: 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. return qsTr('conferenceSubjectChangedEvent'); } return 'unknown_notice' } property bool isImportant: $chatEntry.eventLogType == LinphoneEnums.EventLogTypeConferenceTerminated property bool isError: $chatEntry.status == ChatNoticeModel.NoticeError property color eventColor : (isError ? ChatStyle.entry.event.notice.errorColor : ( isImportant ? ChatStyle.entry.event.notice.importantColor : ChatStyle.entry.event.notice.color )) Layout.preferredHeight: ChatStyle.entry.lineHeight spacing: ChatStyle.entry.message.extraContent.spacing Rectangle{ height:1 Layout.fillWidth: true color: mainLayout.eventColor } Text { id:message Layout.preferredWidth: contentWidth color: mainLayout.eventColor font { pointSize: Units.dp * 7 } height: parent.height text: $chatEntry.name?_type.arg($chatEntry.name):_type verticalAlignment: Text.AlignVCenter TooltipArea { text: UtilsCpp.toDateTimeString($chatEntry.timestamp) } } Rectangle{ height:1 Layout.fillWidth: true color: mainLayout.eventColor } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Chat/OutgoingMessage.qml000066400000000000000000000062441434616504300277420ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import Linphone.Styles 1.0 import Utils 1.0 // ============================================================================= Item { implicitHeight: message.height //width: parent.width Layout.fillWidth: true signal copyAllDone() signal copySelectionDone() signal replyClicked() signal forwardClicked() signal goToMessage(ChatMessageModel message) signal conferenceIcsCopied() Message { id: message onCopyAllDone: parent.copyAllDone() onCopySelectionDone: parent.copySelectionDone() onReplyClicked: parent.replyClicked() onForwardClicked: parent.forwardClicked() onGoToMessage: parent.goToMessage(message) onConferenceIcsCopied: parent.conferenceIcsCopied() anchors { left: parent.left leftMargin: ChatStyle.entry.metaWidth right: parent.right } backgroundColor: ChatStyle.entry.message.outgoing.backgroundColor width: parent.width Row { spacing: ChatStyle.entry.message.extraContent.spacing Component { id: iconComponent Icon { id: iconId readonly property var isError: Utils.includes([ LinphoneEnums.ChatMessageStateFileTransferError, LinphoneEnums.ChatMessageStateNotDelivered, ], $chatEntry.state) readonly property bool isUploaded: $chatEntry.state == LinphoneEnums.ChatMessageStateDelivered readonly property bool isDelivered: $chatEntry.state == LinphoneEnums.ChatMessageStateDeliveredToUser readonly property bool isRead: $chatEntry.state == LinphoneEnums.ChatMessageStateDisplayed icon: iconId.isError ? 'chat_error' : (iconId.isRead ? 'chat_read' : (iconId.isDelivered ? 'chat_delivered' : '' ) ) iconSize: ChatStyle.entry.message.outgoing.sendIconSize MouseArea { id:retryAction anchors.fill: parent visible: iconId.isError || $chatEntry.state == LinphoneEnums.ChatMessageStateIdle onClicked: $chatEntry.resendMessage() } TooltipArea { id:tooltip visible: text != '' text: iconId.isError ? qsTr('messageError') : (iconId.isRead ? qsTr('messageRead') : (isDelivered ? qsTr('messageDelivered') : '')) hoveringCursor : retryAction.visible?Qt.PointingHandCursor:Qt.ArrowCursor } } } Component { id: indicator Item { anchors.fill: parent BusyIndicator { anchors.centerIn: parent height: ChatStyle.entry.message.outgoing.busyIndicatorSize width: ChatStyle.entry.message.outgoing.busyIndicatorSize } } } Loader { height: ChatStyle.entry.lineHeight width: ChatStyle.entry.message.outgoing.areaSize sourceComponent: $chatEntry.state == LinphoneEnums.ChatMessageStateInProgress || $chatEntry.state == LinphoneEnums.ChatMessageStateFileTransferInProgress ? indicator : iconComponent } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Codecs/000077500000000000000000000000001434616504300244425ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Codecs/CodecAttribute.qml000066400000000000000000000004211434616504300300530ustar00rootroot00000000000000import QtQuick 2.7 import Linphone.Styles 1.0 // ============================================================================= Text { color: CodecsViewerStyle.attribute.text.color elide: Text.ElideRight font.pointSize: CodecsViewerStyle.attribute.text.pointSize } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Codecs/CodecLegend.qml000066400000000000000000000004331434616504300273110ustar00rootroot00000000000000import QtQuick 2.7 import Linphone.Styles 1.0 // ============================================================================= Text { color: CodecsViewerStyle.legend.color elide: Text.ElideRight font { bold: true pointSize: CodecsViewerStyle.legend.pointSize } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Codecs/CodecsViewer.qml000066400000000000000000000130551434616504300275430ustar00rootroot00000000000000import QtQml.Models 2.2 import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone.Styles 1.0 // ============================================================================= Column { id: codecsViewer // --------------------------------------------------------------------------- property alias model: view.model // --------------------------------------------------------------------------- signal downloadRequested (var codecInfo) // --------------------------------------------------------------------------- // Header. // --------------------------------------------------------------------------- RowLayout { anchors { left: parent.left leftMargin: CodecsViewerStyle.leftMargin right: parent.right } height: CodecsViewerStyle.legend.height spacing: CodecsViewerStyle.column.spacing CodecLegend { Layout.preferredWidth: CodecsViewerStyle.column.mimeWidth text: qsTr('codecMime') } CodecLegend { Layout.preferredWidth: CodecsViewerStyle.column.encoderDescriptionWidth text: qsTr('codecEncoderDescription') } CodecLegend { Layout.preferredWidth: CodecsViewerStyle.column.clockRateWidth text: qsTr('codecEncoderClockRate') } CodecLegend { Layout.preferredWidth: CodecsViewerStyle.column.bitrateWidth text: qsTr('codecBitrate') } CodecLegend { Layout.preferredWidth: CodecsViewerStyle.column.recvFmtpWidth text: qsTr('codecRecvFmtp') } CodecLegend { Layout.fillWidth: true Layout.leftMargin: 10 text: qsTr('codecStatus') } } // --------------------------------------------------------------------------- // Codecs. // --------------------------------------------------------------------------- ListView { id: view boundsBehavior: Flickable.StopAtBounds clip: true spacing: 0 anchors { left: parent.left right: parent.right } height: count * CodecsViewerStyle.attribute.height // ------------------------------------------------------------------------- // One codec. // ------------------------------------------------------------------------- delegate: MouseArea { id: dragArea cursorShape: Qt.ArrowCursor property bool held: false width: view.width drag { axis: Drag.YAxis maximumY: (view.count - index) * height - height / 2 minimumY: -index * height - height / 2 target: held ? content : undefined } height: CodecsViewerStyle.attribute.height onPressed: held = true onReleased: { held = false view.model.moveCodec(index, index + 1 + content.y / height) content.y = 0 } Rectangle { id: content readonly property bool isDownloadable: Boolean($modelData.downloadUrl) Drag.active: dragArea.held Drag.source: dragArea Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 anchors { left: parent.left right: parent.right } color: CodecsViewerStyle.attribute.background.color.normal height: dragArea.height RowLayout { anchors { fill: parent leftMargin: CodecsViewerStyle.leftMargin } spacing: CodecsViewerStyle.column.spacing CodecAttribute { Layout.preferredWidth: CodecsViewerStyle.column.mimeWidth text: $modelData.mime } CodecAttribute { Layout.preferredWidth: CodecsViewerStyle.column.encoderDescriptionWidth text: $modelData.encoderDescription || '' } CodecAttribute { Layout.preferredWidth: CodecsViewerStyle.column.clockRateWidth text: $modelData.clockRate || '' } NumericField { Layout.preferredWidth: CodecsViewerStyle.column.bitrateWidth readOnly: content.isDownloadable || !$modelData.isVbr text: $modelData.bitrate || '' onEditingFinished: view.model.setBitrate(index, text) } TextField { Layout.preferredWidth: CodecsViewerStyle.column.recvFmtpWidth readOnly: content.isDownloadable text: $modelData.recvFmtp || '' onEditingFinished: view.model.setRecvFmtp(index, text) } Switch { Layout.fillWidth: true checked: Boolean($modelData.enabled) onClicked: !checked && content.isDownloadable ? downloadRequested($modelData) : view.model.enableCodec(index, !checked) } } } MouseArea { id: mouseArea anchors.fill: parent cursorShape: Qt.ArrowCursor onPressed: mouse.accepted = false } // --------------------------------------------------------------------- // Animations/States codec. // --------------------------------------------------------------------- states: [ State { when: mouseArea.containsMouse && !dragArea.held PropertyChanges { target: content color: CodecsViewerStyle.attribute.background.color.hovered } }, State { when: dragArea.held PropertyChanges { target: content color: CodecsViewerStyle.attribute.background.color.hovered } PropertyChanges { target: dragArea opacity: 0.5 z: Constants.zMax } } ] } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Contact/000077500000000000000000000000001434616504300246355ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Contact/Avatar.qml000066400000000000000000000051101434616504300265630ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 // ============================================================================= Item { id: avatar // --------------------------------------------------------------------------- property alias presenceLevel: presenceLevelIcon.level property bool isDarkMode: false property color backgroundColor: isDarkMode ? AvatarStyle.backgroundDarkModeColor : AvatarStyle.backgroundColor property color foregroundColor: 'transparent' property string username property var image property bool isOneToOne: true property var _initialsRegex: /^\s*([^\s\.]+)(?:[\s\.]+([^\s\.]+))?/ property bool isPhoneNumber: UtilsCpp.isPhoneNumber(username) // --------------------------------------------------------------------------- function isLoaded () { return roundedImage.status === Image.Ready } function _computeInitials () { var result = username.match(_initialsRegex) if (!result) { return username.length > 0 ? username.charAt(0).toUpperCase() : '' } return result[1].charAt(0).toUpperCase() + ( result[2] != null ? result[2].charAt(0).toUpperCase() : '' ) } // --------------------------------------------------------------------------- RoundedImage { id: roundedImage anchors.fill: parent backgroundColor: avatar.backgroundColor foregroundColor: avatar.foregroundColor source: avatar.image || '' Icon{ anchors.fill: parent icon: AvatarStyle.personImage visible: parent.source == '' && avatar.isPhoneNumber overwriteColor: AvatarStyle.initials.color } } Text { id: initialsText anchors.centerIn: parent color: isDarkMode ? AvatarStyle.initials.darkModeColor : AvatarStyle.initials.color font.pointSize: { var width if (parent.width > 0) { width = parent.width / AvatarStyle.initials.ratio } return AvatarStyle.initials.pointSize * (width || 1) } text: _computeInitials() visible: roundedImage.status !== Image.Ready && !avatar.isPhoneNumber && avatar.isOneToOne } Icon { anchors.fill: parent icon: ContactStyle.groupChat.icon overwriteColor: isDarkMode ? ContactStyle.groupChat.avatarDarkModeColor : ContactStyle.groupChat.avatarColor iconSize: avatar.width //visible: entry!=undefined && entry.isOneToOne!=undefined && !entry.isOneToOne visible: !avatar.isOneToOne } PresenceLevel { id: presenceLevelIcon visible: level >= 0 anchors { bottom: parent.bottom right: parent.right } height: parent.height / 4 width: parent.width / 4 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Contact/Contact.qml000066400000000000000000000111201434616504300267360ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import Linphone 1.0 import Linphone.Styles 1.0 import Common 1.0 import UtilsCpp 1.0 // ============================================================================= Rectangle { id: item // --------------------------------------------------------------------------- // An entry from `SipAddressesModel`, an `SipAddressObserver` or a ChatRoomModel property var entry // entry should have these functions : presenceStatus, sipAddress, username, avatar (image) property alias subtitleColor: description.subtitleColor property alias titleColor: description.titleColor property alias statusText : description.statusText property alias isDarkMode: avatar.isDarkMode property bool displayUnreadMessageCount: false property bool showSubtitle : true property bool showBusyIndicator: false property string subtitle: '' property string subject: (entry && entry.conferenceInfoModel && entry.conferenceInfoModel.subject ? entry.conferenceInfoModel.subject : '') property string username: entry ? entry.username ? entry.username : entry.contactModel ? entry.contactModel.vcard.username : UtilsCpp.getDisplayName(entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '') : '' property string organizer: entry && entry.conferenceInfoModel ? UtilsCpp.getDisplayName(entry.conferenceInfoModel.organizer) : '' signal avatarClicked(var mouse) // --------------------------------------------------------------------------- color: 'transparent' // No color by default. height: ContactStyle.height RowLayout { anchors { fill: parent leftMargin: ContactStyle.leftMargin rightMargin: ContactStyle.rightMargin } spacing: 0 Avatar { id: avatar Layout.preferredHeight: ContactStyle.contentHeight Layout.preferredWidth: ContactStyle.contentHeight //image: _contact && _contact.vcard.avatar image: entry?(entry.avatar ? entry.avatar : entry.contactModel ? entry.contactModel.vcard.avatar : '') :'' presenceLevel: entry?(entry.contactModel ? (entry.contactModel.presenceStatus >= 0 ? Presence.getPresenceLevel(entry.contactModel.presenceStatus) : -1) : (entry.presenceStatus >= 0 ? Presence.getPresenceLevel(entry.presenceStatus) : -1) ) :-1 username: entry!=undefined ? entry.conferenceInfoModel ? item.organizer : entry.isOneToOne!=undefined && !entry.isOneToOne ? '' : item.username : item.username isOneToOne: entry==undefined || entry.isOneToOne==undefined || entry.isOneToOne Icon{ anchors.top:parent.top anchors.horizontalCenter: parent.right visible: entry!=undefined && entry.haveEncryption != undefined && entry.haveEncryption icon: entry?(entry.securityLevel === 2?'secure_level_1': entry.securityLevel===3? 'secure_level_2' : 'secure_level_unsafe'):'secure_level_unsafe' iconSize: parent.height/2 } MouseArea{ anchors.fill: parent onClicked: item.avatarClicked(mouse) } Loader{ id: busyLoader anchors.fill: parent anchors.margins: 5 active: item.showBusyIndicator sourceComponent: Component{ BusyIndicator{// Joining spinner id: joiningSpinner running: false Timer{// Delay starting spinner (Qt bug) id: indicatorDelay interval: 100 onTriggered: joiningSpinner.running = true } Component.onCompleted: indicatorDelay.start() } } } } ContactDescription { id: description Layout.fillHeight: true Layout.fillWidth: true Layout.leftMargin: ContactStyle.spacing titleText: item.subject ? item.subject : item.username subtitleText: entry && item.showSubtitle ? item.subtitle ? item.subtitle : (entry.isOneToOne == undefined || entry.isOneToOne) && (entry.haveEncryption == undefined || !entry.haveEncryption) ? item.organizer ? item.organizer : entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '' : entry.participants ? entry.participants.addressesToString : '' : '' } ContactMessageCounter { Layout.alignment: Qt.AlignTop count: entry?Number(entry.unreadMessagesCount) + Number(entry.missedCallsCount):0 isComposing: Boolean(entry && entry.composers && entry.composers.length > 0) visible: entry?(entry.unreadMessagesCount !== null || entry.missedCallsCount !== null) && item.displayUnreadMessageCount:false } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Contact/ContactDescription.qml000066400000000000000000000072231434616504300311530ustar00rootroot00000000000000import QtQuick 2.7 import Linphone 1.0 import Linphone.Styles 1.0 import Common 1.0 // ============================================================================= Column { id:mainItem property alias titleText: title.fullText property alias subtitleText: subtitle.fullText property string sipAddress property alias statusText : status.text property var contactDescriptionStyle : ContactDescriptionStyle property color subtitleColor: contactDescriptionStyle.subtitle.color property color titleColor: contactDescriptionStyle.title.color property int horizontalTextAlignment property int contentWidth : Math.max(titleImplicitWidthWorkaround.implicitWidth, subtitleImplicitWidthWorkaround.implicitWidth) +10 +statusWidth property int contentHeight : Math.max(title.implicitHeight, subtitle.implicitHeight)+10 readonly property int statusWidth : (status.visible ? status.width + 5 : 0) property bool titleClickable: false signal titleClicked() // --------------------------------------------------------------------------- TextEdit { id: title property string fullText anchors.horizontalCenter: (horizontalTextAlignment == Text.AlignHCenter ? parent.horizontalCenter : undefined) color: titleColor font.weight: contactDescriptionStyle.title.weight font.pointSize: contactDescriptionStyle.title.pointSize horizontalAlignment: horizontalTextAlignment verticalAlignment: (subtitle.visible?Text.AlignBottom:Text.AlignVCenter) width: Math.min(parent.width-statusWidth, titleImplicitWidthWorkaround.implicitWidth) height: (parent.height-parent.topPadding-parent.bottomPadding)/parent.visibleChildren.length text: metrics.elidedText onActiveFocusChanged: deselect(); readOnly: true selectByMouse: true Text{// Workaround to get implicitWidth from text without eliding id: titleImplicitWidthWorkaround text: title.fullText font.weight: title.font.weight font.pointSize: title.font.pointSize visible: false } TextMetrics { id: metrics font: title.font text: title.fullText elideWidth: title.width elide: Qt.ElideRight } Text{ id:status anchors.top:parent.top anchors.bottom : parent.bottom anchors.left:parent.right anchors.leftMargin:5 verticalAlignment: Text.AlignVCenter visible: text != '' text : '' color: contactDescriptionStyle.title.status.color font.pointSize: contactDescriptionStyle.title.status.pointSize font.italic : true } MouseArea{ anchors.fill:parent visible: titleClickable onClicked: titleClicked() } } TextEdit { id:subtitle property string fullText anchors.horizontalCenter: (horizontalTextAlignment == Text.AlignHCenter ? parent.horizontalCenter : undefined) color: subtitleColor font.weight: contactDescriptionStyle.subtitle.weight font.pointSize: contactDescriptionStyle.subtitle.pointSize horizontalAlignment: horizontalTextAlignment verticalAlignment: (title.visible?Text.AlignTop:Text.AlignVCenter) width: Math.min(parent.width-statusWidth, subtitleImplicitWidthWorkaround.implicitWidth) height: (parent.height-parent.topPadding-parent.bottomPadding)/parent.visibleChildren.length visible: text != '' text: subtitleMetrics.elidedText onActiveFocusChanged: deselect(); readOnly: true selectByMouse: true Text{// Workaround to get implicitWidth from text without eliding id: subtitleImplicitWidthWorkaround text: subtitle.fullText font.weight: subtitle.font.weight font.pointSize: subtitle.font.pointSize visible: false } TextMetrics { id: subtitleMetrics font: subtitle.font text: subtitle.fullText elideWidth: subtitle.width elide: Qt.ElideRight } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Contact/ContactMessageCounter.qml000066400000000000000000000020451434616504300316110ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 // ============================================================================= Item { id: messageCounter property alias count: counterIcon.count property bool isComposing implicitHeight: counterIcon.height + ContactMessageCounterStyle.verticalMargins * 2 implicitWidth: counterIcon.width + ContactMessageCounterStyle.horizontalMargins * 2 MessageCounter { id: counterIcon property int composingIndex: 0 anchors.centerIn: parent icon: messageCounter.isComposing ? ('chat_is_composing_' + counterIcon.composingIndex) : 'chat_count' visible: messageCounter.count > 0 || messageCounter.isComposing Timer { interval: 500 repeat: true running: messageCounter.isComposing onRunningChanged: { if (running) { counterIcon.composingIndex = 0 } } onTriggered: counterIcon.composingIndex = (counterIcon.composingIndex + 1) % 4 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Dialog/000077500000000000000000000000001434616504300244415ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Dialog/MultimediaParametersDialog.qml000066400000000000000000000134171434616504300324200ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import App.Styles 1.0 // ============================================================================= DialogPlus { property var call property bool fixedSize : true property int fitHeight: MultimediaParametersDialogStyle.height+30 property int fitWidth: MultimediaParametersDialogStyle.width // --------------------------------------------------------------------------- buttons: [ TextButtonB { text: qsTr('ok') onClicked: { if(call) call.updateStreams() exit(0) } } ] buttonsAlignment: Qt.AlignCenter onVisibleChanged: if(visible) {SettingsModel.reloadDevices()} Component.onCompleted: { SettingsModel.stopCaptureGraph() SettingsModel.reloadDevices() SettingsModel.startCaptureGraph() if( fixedSize){ height = fitHeight width = fitWidth } } Component.onDestruction: SettingsModel.stopCaptureGraph() onCallChanged: !call && exit(0) //: 'Multimedia parameters' : Menu title to show multimedia devices configuration. title: qsTr('menuMultimedia') // --------------------------------------------------------------------------- Column { anchors.fill: parent anchors.topMargin: MultimediaParametersDialogStyle.column.spacing spacing: MultimediaParametersDialogStyle.column.spacing RowLayout { spacing: MultimediaParametersDialogStyle.column.entry.spacing width: parent.width Icon { Layout.alignment: Qt.AlignTop Layout.preferredHeight: ComboBoxStyle.background.height icon: MultimediaParametersDialogStyle.column.entry.speaker.icon overwriteColor: MultimediaParametersDialogStyle.column.entry.speaker.color iconSize: MultimediaParametersDialogStyle.column.entry.speaker.iconSize } Column { Layout.fillWidth: true spacing: MultimediaParametersDialogStyle.column.entry.spacing2 ComboBox { currentIndex: Utils.findIndex(model, function (device) { return device === SettingsModel.playbackDevice }) model: SettingsModel.playbackDevices width: parent.width onActivated: SettingsModel.playbackDevice = model[index] } Slider { id: playbackSlider width: parent.width property bool initialized: false value: call ? call.speakerVolumeGain : SettingsModel.playbackGain onPositionChanged: { if( initialized){ if(call) call.speakerVolumeGain = position else SettingsModel.playbackGain = position } } Component.onCompleted: initialized = true ToolTip { parent: playbackSlider.handle visible: playbackSlider.pressed text: (playbackSlider.value * 100).toFixed(0) + " %" } } } } RowLayout { spacing: MultimediaParametersDialogStyle.column.entry.spacing width: parent.width Icon { Layout.alignment: Qt.AlignTop Layout.preferredHeight: ComboBoxStyle.background.height icon: MultimediaParametersDialogStyle.column.entry.micro.icon overwriteColor: MultimediaParametersDialogStyle.column.entry.micro.color iconSize: MultimediaParametersDialogStyle.column.entry.micro.iconSize } Column { Layout.fillWidth: true spacing: MultimediaParametersDialogStyle.column.entry.spacing2 ComboBox { currentIndex: Utils.findIndex(model, function (device) { return device === SettingsModel.captureDevice }) model: SettingsModel.captureDevices width: parent.width onActivated: SettingsModel.captureDevice = model[index] } Slider { id: captureSlider width: parent.width value: call ? call.microVolumeGain : SettingsModel.captureGain property bool initialized: false onPositionChanged: if(initialized){ if(call) call.microVolumeGain = position else SettingsModel.captureGain = position } Component.onCompleted: initialized = true ToolTip { parent: captureSlider.handle visible: captureSlider.pressed text: "+ " + (captureSlider.value * 100).toFixed(0) + " %" } } Slider { id: audioTestSlider enabled: false width: parent.width height: 8 background: Rectangle { x: audioTestSlider.leftPadding y: audioTestSlider.topPadding + audioTestSlider.availableHeight / 2 - height / 2 implicitWidth: 200 implicitHeight: 8 width: audioTestSlider.availableWidth height: implicitHeight radius: 2 color: SettingsAudioStyle.sliderBackgroundColor Rectangle { width: audioTestSlider.visualPosition * parent.width height: parent.height color: audioTestSlider.value > 0.8 ? SettingsAudioStyle.sliderHighColor : SettingsAudioStyle.sliderLowColor radius: 2 } } //Empty slider handle handle: Text {text: ''; visible: false } Timer { interval: 50 repeat: true running: SettingsModel.captureGraphRunning || call || false onTriggered: call ? parent.value = call.microVu : parent.value = SettingsModel.getMicVolume() } } } } RowLayout { spacing: MultimediaParametersDialogStyle.column.entry.spacing width: parent.width Icon { icon: MultimediaParametersDialogStyle.column.entry.camera.icon overwriteColor: MultimediaParametersDialogStyle.column.entry.camera.color iconSize: MultimediaParametersDialogStyle.column.entry.speaker.iconSize } ComboBox { Layout.fillWidth: true currentIndex: Number(Utils.findIndex(model, function (device) { return device === SettingsModel.videoDevice })) model: SettingsModel.videoDevices onActivated: SettingsModel.videoDevice = model[index] } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml000066400000000000000000000100501434616504300313720ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 // ============================================================================= DialogPlus { id: dialog // --------------------------------------------------------------------------- property alias downloadUrl: fileDownloader.url property alias installFolder: fileDownloader.downloadFolder property alias checksum: fileDownloader.checksum property string installName // Right install name. property string mime // Human readable name. property bool _installing: false property int _exitStatus: -1 // Not downloaded for the moment. // --------------------------------------------------------------------------- function install () { dialog._installing = true fileDownloader.download() } function _endInstall (exitStatus) { if (exitStatus === 1) { Utils.write(installFolder + mime + '.txt', downloadUrl) } dialog._exitStatus = exitStatus dialog._installing = false } // --------------------------------------------------------------------------- // TODO: Improve one day. Do not launch download directly. // Provide a download function (window.attachVirtualWindow cannot call // function after creation at this moment). Component.onCompleted: dialog.install() // --------------------------------------------------------------------------- buttons: [ // TODO: Add a retry button??? TextButtonB { enabled: !dialog._installing && !fileDownloader.downloading && !fileExtractor.extracting text: qsTr('confirm') onClicked: exit(1) } ] buttonsAlignment: Qt.AlignCenter descriptionText: { var str if (dialog.extracting) { str = qsTr('onlineInstallerExtractingDescription') } else if (dialog._installing) { str = qsTr('onlineInstallerDownloadingDescription') } else if (dialog._exitStatus > 0) { str = qsTr('onlineInstallerFinishedDescription') } else { str = qsTr('onlineInstallerFailedDescription') } return str.replace('%1', dialog.mime) } height: OnlineInstallerDialogStyle.height + 30 width: OnlineInstallerDialogStyle.width Column { anchors.verticalCenter: parent.verticalCenter width: parent.width spacing: OnlineInstallerDialogStyle.column.spacing ProgressBar { id: progressBar property var target: fileDownloader height: OnlineInstallerDialogStyle.column.bar.height width: parent.width to: target.totalBytes value: target.readBytes indeterminate : target.totalBytes == 0 background: Rectangle { color: OnlineInstallerDialogStyle.column.bar.background.color radius: OnlineInstallerDialogStyle.column.bar.radius } contentItem: Item { Rectangle { color: dialog._exitStatus ? OnlineInstallerDialogStyle.column.bar.contentItem.color.normal : OnlineInstallerDialogStyle.column.bar.contentItem.color.failed height: parent.height radius: OnlineInstallerDialogStyle.column.bar.radius width: progressBar.visualPosition * parent.width } } } Text { anchors.right: parent.right color: OnlineInstallerDialogStyle.column.text.color font.pointSize: OnlineInstallerDialogStyle.column.text.pointSize text: { var target = progressBar.target var fileSize = Utils.formatSize(target.totalBytes) return Utils.formatSize(target.readBytes) + '/' + fileSize } } FileDownloader { id: fileDownloader onDownloadFailed: dialog._endInstall(0) onDownloadFinished: { fileExtractor.file = filePath progressBar.target = fileExtractor fileExtractor.extract() } } FileExtractor { id: fileExtractor extractFolder: dialog.installFolder extractName: dialog.installName onExtractFailed: {fileDownloader.remove(); dialog._endInstall(0)} onExtractFinished: {fileDownloader.remove(); dialog._endInstall(1)} } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Dialog/SipAddressDialog.qml000066400000000000000000000050021434616504300303320ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 import Linphone.Styles 1.0 // ============================================================================= DialogPlus { id: mainItem property var addressSelectedCallback property var chatRoomSelectedCallback buttons: [ TextButtonA { text: qsTr('cancel') onClicked: exit(0) } ] buttonsAlignment: Qt.AlignCenter height: SipAddressDialogStyle.height + 30 width: SipAddressDialogStyle.width // --------------------------------------------------------------------------- ColumnLayout { anchors.fill: parent spacing: SipAddressDialogStyle.spacing SmartSearchBar { id: smartSearchBar Layout.fillWidth: true Layout.topMargin: SipAddressDialogStyle.spacing visible: !timeline.isFilterVisible showHeader:false maxMenuHeight: MainWindowStyle.searchBox.maxHeight //: 'Search in contacts' : Placeholder for a search a contact placeholderText: qsTr('contactsSearchPlaceholder') //: 'Search an address in your contacts or use a custom one.' : tooltip tooltipText: qsTr('contactsSearchTooltip') actions:[{ colorSet: SipAddressDialogStyle.select, secure: 0, visible: true, secureIconVisibleHandler : function(entry) { return UtilsCpp.hasCapability(entry.sipAddress, LinphoneEnums.FriendCapabilityLimeX3Dh) }, handler: function (entry) { console.debug("Call selected: " +entry + "/"+entry.sipAddress) mainItem.addressSelectedCallback(entry.sipAddress) exit(1) }, }] onEntryClicked: { console.debug("Call selected: " +entry + "/"+entry.sipAddress) mainItem.addressSelectedCallback(entry.sipAddress) exit(1) } } Text { id: description Layout.fillWidth: true color: SipAddressDialogStyle.list.color font.pointSize: SipAddressDialogStyle.list.pointSize horizontalAlignment: Qt.AlignLeft verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap //: 'Conversations' : header for a selection in conversation list text: qsTr('timelineSelectionHeader') } ScrollableListViewField { Layout.fillHeight: true Layout.fillWidth: true Timeline { id: timeline showHistoryButton: false anchors.fill: parent model: TimelineProxyModel{ listSource: TimelineProxyModel.Copy } onEntrySelected:{ if( entry) { mainItem.chatRoomSelectedCallback(entry.chatRoomModel) exit(1) } } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Dialog/ZrtpTokenAuthenticationDialog.qml000066400000000000000000000104171434616504300331370ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 // ============================================================================= DialogPlus { id: mainItem property var addressSelectedCallback property var chatRoomSelectedCallback property var call property alias localSas: localSasText.text property alias remoteSas : remoteSasText.text buttons: [ TextButtonA { //: 'Later' : Button label to do something in another time. text: qsTr('Later') onClicked: { if(mainItem.call) mainItem.call.verifyAuthenticationToken(false) mainItem.exit(0) } }, TextButtonC { //: 'Correct' : Button label to confirm a code. text: qsTr('Correct') onClicked: { if(mainItem.call) mainItem.call.verifyAuthenticationToken(true) mainItem.exit(1) } } ] buttonsAlignment: Qt.AlignCenter height: 400 width: 350 radius: 10 onCallChanged: if(!call) exit(0) Component.onCompleted: if( !localSas || !remoteSas) mainItem.exit(0) ColumnLayout { id:columnLayout // --------------------------------------------------------------------------- anchors.fill: parent Layout.fillWidth: true Icon{ Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: 5 icon: mainItem.call.isPQZrtp === CallModel.CallPQStateOn ? ZrtpTokenAuthenticationDialogStyle.pqIcon : mainItem.call.isPQZrtp === CallModel.CallPQStateOff ? ZrtpTokenAuthenticationDialogStyle.icon : ZrtpTokenAuthenticationDialogStyle.secureIcon iconSize: ZrtpTokenAuthenticationDialogStyle.iconSize } // --------------------------------------------------------------------------- // Main text. // --------------------------------------------------------------------------- Text { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter //: 'Communication security' : Title of popup for ZRTP confirmation. text: qsTr('title') color: ZrtpTokenAuthenticationDialogStyle.text.colorA wrapMode: Text.WordWrap font { bold: true pointSize: ZrtpTokenAuthenticationDialogStyle.text.titlePointSize } } Text { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter //: 'To raise the security level, you can check the following codes with your correspondent.' : Explanation to do a security check. text: qsTr('confirmSas') color: ZrtpTokenAuthenticationDialogStyle.text.colorA wrapMode: Text.WordWrap font.pointSize: ZrtpTokenAuthenticationDialogStyle.text.pointSize } // --------------------------------------------------------------------------- // Rules. // --------------------------------------------------------------------------- ColumnLayout { Layout.fillHeight: true Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter spacing: ZrtpTokenAuthenticationDialogStyle.text.wordsSpacing Text { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: ZrtpTokenAuthenticationDialogStyle.text.colorA font.pointSize: ZrtpTokenAuthenticationDialogStyle.text.pointSize text: qsTr('codeA') } Text { id: localSasText Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: ZrtpTokenAuthenticationDialogStyle.text.colorB font { bold: true pointSize: ZrtpTokenAuthenticationDialogStyle.text.sasPointSize } text: mainItem.call?mainItem.call.localSas:'' } Text { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: ZrtpTokenAuthenticationDialogStyle.text.colorA font.pointSize: ZrtpTokenAuthenticationDialogStyle.text.pointSize text: qsTr('codeB') } Text { id: remoteSasText Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: ZrtpTokenAuthenticationDialogStyle.text.colorB font { bold: true pointSize: ZrtpTokenAuthenticationDialogStyle.text.sasPointSize } text: mainItem.call?mainItem.call.remoteSas:'' } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/File/000077500000000000000000000000001434616504300241215ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/File/FileView.qml000066400000000000000000000054621434616504300263550ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import Linphone.Styles 1.0 import Utils 1.0 import Units 1.0 import ColorsList 1.0 // ============================================================================= Item { id: mainItem property string thumbnail property string name property bool active: true property real animationScale : ChatStyle.entry.message.file.animation.to property alias imageScale: thumbnailProvider.scale signal clickOnFile() // --------------------------------------------------------------------- // Thumbnail or extension. // --------------------------------------------------------------------- Component { id: thumbnailImage Image { id: thumbnailImageSource mipmap: SettingsModel.mipmapEnabled source: mainItem.thumbnail fillMode: Image.PreserveAspectFit } } Component { id: extension Rectangle { color: ChatStyle.entry.message.file.extension.background.color Text { anchors.fill: parent color: ChatStyle.entry.message.file.extension.text.color font.bold: true elide: Text.ElideRight text: Utils.getExtension(mainItem.name).toUpperCase() horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } } Loader { id: thumbnailProvider anchors.fill: parent sourceComponent: (mainItem.active ? (mainItem.thumbnail ? thumbnailImage : extension ): undefined) ScaleAnimator { id: thumbnailProviderAnimator target: mainItem duration: ChatStyle.entry.message.file.animation.duration easing.type: Easing.InOutQuad from: 1.0 } states: State { name: 'hovered' } transitions: [ Transition { from: '' to: 'hovered' ScriptAction { script: { if (thumbnailProviderAnimator.running) { thumbnailProviderAnimator.running = false } mainItem.z = Constants.zPopup thumbnailProviderAnimator.to = mainItem.animationScale thumbnailProviderAnimator.running = true } } }, Transition { from: 'hovered' to: '' ScriptAction { script: { if (thumbnailProviderAnimator.running) { thumbnailProviderAnimator.running = false } thumbnailProviderAnimator.to = 1.0 thumbnailProviderAnimator.running = true mainItem.z = 0 } } } ] } MouseArea { function handleMouseMove (mouse) { thumbnailProvider.state = Utils.pointIsInItem(this, thumbnailProvider, mouse) ? 'hovered' : '' } anchors.fill: parent onClicked: { clickOnFile() thumbnailProvider.state = '' } onExited: thumbnailProvider.state = '' onMouseXChanged: handleMouseMove.call(this, mouse) onMouseYChanged: handleMouseMove.call(this, mouse) } }linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/History/000077500000000000000000000000001434616504300247035ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/History/Event.qml000066400000000000000000000117231434616504300265030ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 // ============================================================================= Row { id: mainItem signal entryClicked(var entry) property var _sipAddressObserver: $historyEntry.sipAddress ? SipAddressesModel.getSipAddressObserver($historyEntry.sipAddress, '') : $historyEntry.title property QtObject iconData property string translation Component.onDestruction: _sipAddressObserver=null// Need to set it to null because of not calling destructor if not. Component.onCompleted: { if ($historyEntry.status == LinphoneEnums.CallStatusSuccess) { if(!$historyEntry.isStart){ iconData = HistoryStyle.entry.event.endedCall translation ='endedCall' }else if($historyEntry.isOutgoing ){ iconData = HistoryStyle.entry.event.outgoingCall translation ='outgoingCall' }else{ iconData = HistoryStyle.entry.event.incomingCall translation ='incomingCall' } }else if($historyEntry.status == LinphoneEnums.CallStatusDeclined) { if($historyEntry.isOutgoing ){ iconData = HistoryStyle.entry.event.declinedOutgoingCall translation ='declinedOutgoingCall' }else{ iconData = HistoryStyle.entry.event.declinedIncomingCall translation ='declinedIncomingCall' } }else if($historyEntry.status == LinphoneEnums.CallStatusMissed) { if($historyEntry.isOutgoing ){ iconData = HistoryStyle.entry.event.missedOutgoingCall translation ='missedOutgoingCall' }else{ iconData = HistoryStyle.entry.event.missedIncomingCall translation ='missedIncomingCall' } }else if($historyEntry.status == LinphoneEnums.CallStatusAborted) { if($historyEntry.isOutgoing ){ iconData = HistoryStyle.entry.event.outgoingCall translation ='outgoingCall' }else{ iconData = HistoryStyle.entry.event.incomingCall translation ='incomingCall' } }else if($historyEntry.status == LinphoneEnums.CallStatusDeclined) { if($historyEntry.isOutgoing ){ iconData = HistoryStyle.entry.event.declinedOutgoingCall translation ='declinedOutgoingCall' }else{ iconData = HistoryStyle.entry.event.declinedIncomingCall translation ='declinedIncomingCall' } }else if($historyEntry.status == LinphoneEnums.CallStatusEarlyAborted) { if($historyEntry.isOutgoing ){ iconData = HistoryStyle.entry.event.missedOutgoingCall translation ='missedOutgoingCall' }else{ iconData = HistoryStyle.entry.event.missedIncomingCall translation ='missedIncomingCall' } }else if($historyEntry.status == LinphoneEnums.CallStatusAcceptedElsewhere) { if($historyEntry.isOutgoing ){ iconData = HistoryStyle.entry.event.outgoingCall translation ='outgoingCall' }else{ iconData = HistoryStyle.entry.event.incomingCall translation ='incomingCall' } }else if($historyEntry.status == LinphoneEnums.CallStatusDeclinedElsewhere) { if($historyEntry.isOutgoing ){ iconData = HistoryStyle.entry.event.declinedOutgoingCall translation ='declinedOutgoingCall' }else{ iconData = HistoryStyle.entry.event.declinedIncomingCall translation ='declinedIncomingCall' } }else { iconData = HistoryStyle.entry.event.unknownCallEvent translation = 'unknownCallEvent' } } height: HistoryStyle.entry.lineHeight spacing: HistoryStyle.entry.message.extraContent.spacing Icon { height: parent.height icon: mainItem.iconData ? mainItem.iconData.icon : null overwriteColor: mainItem.iconData ? mainItem.iconData.color: null iconSize: HistoryStyle.entry.event.iconSize width: HistoryStyle.entry.metaWidth } Text { Component { // Never created. // Private data for `lupdate`. Item { property var i18n: [ QT_TR_NOOP('declinedIncomingCall'), QT_TR_NOOP('declinedOutgoingCall'), QT_TR_NOOP('endedCall'), QT_TR_NOOP('incomingCall'), QT_TR_NOOP('missedIncomingCall'), QT_TR_NOOP('missedOutgoingCall'), QT_TR_NOOP('outgoingCall') ] } } color: HistoryStyle.entry.event.text.color font { bold: true pointSize: HistoryStyle.entry.event.text.pointSize } height: parent.height text: mainItem.translation ? qsTr(mainItem.translation) +' - ' : ' - ' verticalAlignment: Text.AlignVCenter MouseArea{ anchors.fill:parent onClicked: entryClicked($historyEntry) } } Text { color: HistoryStyle.entry.event.text.color font { bold: true pointSize: HistoryStyle.entry.event.text.pointSize } height: parent.height text: $historyEntry.title ? $historyEntry.title : _sipAddressObserver ? ( UtilsCpp.getDisplayName(_sipAddressObserver.peerAddress || $historyEntry.sipAddress) || _sipAddressObserver) : '' verticalAlignment: Text.AlignVCenter MouseArea{ anchors.fill:parent onClicked: entryClicked($historyEntry) } } ActionButton { isCustom: true backgroundRadius: 8 colorSet: HistoryStyle.entry.deleteAction visible: isHoverEntry() onClicked: removeEntry() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/History/History.js000066400000000000000000000035531434616504300267100ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `Chat.qml` Logic. // ============================================================================= .import QtQuick 2.7 as QtQuick .import Linphone 1.0 as Linphone // ============================================================================= function initView () { history.tryToLoadMoreEntries = false history.bindToEnd = true } function loadMoreEntries () { if (history.atYBeginning && !history.tryToLoadMoreEntries) { history.tryToLoadMoreEntries = true history.positionViewAtBeginning() container.proxyModel.loadMoreEntries() } } function getComponentFromEntry (historyEntry) { if (historyEntry.type === Linphone.HistoryModel.CallEntry) { return 'Event.qml' } return '' } function handleMoreEntriesLoaded (n) { history.positionViewAtIndex(n - 1, QtQuick.ListView.Beginning) history.tryToLoadMoreEntries = false } function handleMovementEnded () { if (history.atYEnd) { history.bindToEnd = true } } function handleMovementStarted () { history.bindToEnd = false } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/History/History.qml000066400000000000000000000134561434616504300270700ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import UtilsCpp 1.0 import 'History.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Rectangle { id: container property alias proxyModel: history.model signal entryClicked(var entry) // --------------------------------------------------------------------------- color: HistoryStyle.color ColumnLayout { anchors.fill: parent spacing: 0 ScrollableListView { id: history // ----------------------------------------------------------------------- property bool bindToEnd: false property bool tryToLoadMoreEntries: true // ----------------------------------------------------------------------- Layout.fillHeight: true Layout.fillWidth: true highlightFollowsCurrentItem: false section { criteria: ViewSection.FullString delegate: sectionHeading property: '$sectionDate' } // ----------------------------------------------------------------------- Component.onCompleted: Logic.initView() onContentYChanged: Logic.loadMoreEntries() onMovementEnded: Logic.handleMovementEnded() onMovementStarted: Logic.handleMovementStarted() // ----------------------------------------------------------------------- Connections { target: proxyModel // When the view is changed (for example `Calls` -> `Messages`), // the position is set at end and it can be possible to load // more entries. onEntryTypeFilterChanged: Logic.initView() onMoreEntriesLoaded: Logic.handleMoreEntriesLoaded(n) } // ----------------------------------------------------------------------- // Heading. // ----------------------------------------------------------------------- Component { id: sectionHeading Item { implicitHeight: container.height + HistoryStyle.sectionHeading.bottomMargin width: parent.width Borders { id: container borderColor: HistoryStyle.sectionHeading.border.color bottomWidth: HistoryStyle.sectionHeading.border.width implicitHeight: text.contentHeight + HistoryStyle.sectionHeading.padding * 2 + HistoryStyle.sectionHeading.border.width * 2 topWidth: HistoryStyle.sectionHeading.border.width width: parent.width Text { id: text anchors.fill: parent color: HistoryStyle.sectionHeading.text.color font { bold: true pointSize: HistoryStyle.sectionHeading.text.pointSize } horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter // Cast section to integer because Qt converts the // sectionDate in string!!! text: new Date(section).toLocaleDateString( Qt.locale(App.locale) ) } } } } // ----------------------------------------------------------------------- // Message/Event renderer. // ----------------------------------------------------------------------- delegate: Rectangle { id: entry function isHoverEntry () { return mouseArea.containsMouse } function removeEntry () { proxyModel.removeEntry(index) } anchors { left: parent ? parent.left : undefined leftMargin: HistoryStyle.entry.leftMargin right: parent ? parent.right : undefined rightMargin: HistoryStyle.entry.deleteAction.iconSize + HistoryStyle.entry.message.extraContent.spacing + HistoryStyle.entry.message.extraContent.rightMargin + HistoryStyle.entry.message.extraContent.leftMargin } color: HistoryStyle.color implicitHeight: layout.height + HistoryStyle.entry.bottomMargin // --------------------------------------------------------------------- MouseArea { id: mouseArea cursorShape: Qt.ArrowCursor hoverEnabled: true implicitHeight: layout.height width: parent.width + parent.anchors.rightMargin RowLayout { id: layout spacing: 0 width: entry.width // Display time. Text { Layout.alignment: Qt.AlignTop Layout.preferredHeight: HistoryStyle.entry.lineHeight Layout.preferredWidth: HistoryStyle.entry.time.width color: HistoryStyle.entry.time.color font.pointSize: HistoryStyle.entry.time.pointSize text: UtilsCpp.toTimeString($historyEntry.timestamp, 'hh:mm') verticalAlignment: Text.AlignVCenter TooltipArea { text: UtilsCpp.toDateTimeString($historyEntry.timestamp) } } // Display content. Loader { id:entryLoader Layout.fillWidth: true source: Logic.getComponentFromEntry($historyEntry) } Connections{ target:entryLoader.item onEntryClicked:{entryClicked(entry)} } } } } } } // --------------------------------------------------------------------------- // Scroll at end if necessary. // --------------------------------------------------------------------------- Timer { interval: 100 repeat: true running: true onTriggered: history.bindToEnd && history.positionViewAtEnd() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Menus/000077500000000000000000000000001434616504300243315ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Menus/IncallMenu.qml000066400000000000000000000251461434616504300271030ustar00rootroot00000000000000import QtQuick 2.12 import QtQuick.Layouts 1.3 import QtQml.Models 2.12 import QtGraphicalEffects 1.12 import QtQuick.Controls 2.12 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import LinphoneEnums 1.0 import UtilsCpp 1.0 import App.Styles 1.0 // ============================================================================= Rectangle{ id: mainItem property CallModel callModel property ConferenceModel conferenceModel: callModel.conferenceModel property ParticipantModel me: conferenceModel ? conferenceModel.localParticipant : null property bool isMeAdmin: me && me.adminStatus property bool isParticipantsMenu: false signal close() signal layoutChanging(int layoutMode) height: 500 width: 400 color: "white" radius: IncallMenuStyle.radius // List of title texts in order to allow bindings between all components property var menuTitles: [ //: 'Multimedia parameters' : Menu title to show multimedia devices configuration. qsTr('incallMenuMultimedia'), //: 'Change layout' : Menu title to change the conference layout. qsTr('incallMenuLayout'), //: 'Invite participants' : Menu title to invite participants in admin mode. mainItem.isMeAdmin ? qsTr('incallMenuInvite') //: 'Participants list' : Menu title to show participants in non-admin mode. : qsTr('incallMenuParticipants') ] function showParticipantsMenu(){ contentsStack.push(participantsMenu, {title:Qt.binding(function() { return mainItem.menuTitles[2]})}) visible = true } onVisibleChanged: if(!visible && contentsStack.nViews > 1) { contentsStack.pop() } property bool _activateCamera: false Connections{// Enable camera only when status is ok target: mainItem.callModel onStatusChanged: if( mainItem._activateCamera && (status == LinphoneEnums.CallStatusConnected || status == LinphoneEnums.CallStatusIdle)){ camera._activateCamera = false callModel.cameraEnabled = true } } ButtonGroup{id: modeGroup} ColumnLayout{ anchors.fill: parent // HEADER Borders{ Layout.fillWidth: true Layout.preferredHeight: Math.max(IncallMenuStyle.header.height, titleMenu.implicitHeight+20) bottomColor: IncallMenuStyle.list.border.color bottomWidth: IncallMenuStyle.list.border.width RowLayout{ anchors.fill: parent ActionButton{ backgroundRadius: width/2 isCustom: true colorSet: IncallMenuStyle.buttons.back onClicked: contentsStack.pop() visible: contentsStack.nViews > 1 } Text{ id: titleMenu text: contentsStack.currentItem.title Layout.fillWidth: true Layout.preferredHeight: implicitHeight horizontalAlignment: Qt.AlignCenter color: IncallMenuStyle.header.color font.pointSize: IncallMenuStyle.header.pointSize font.weight: IncallMenuStyle.header.weight wrapMode: Text.WordWrap elide: Text.ElideRight } ActionButton{ Layout.rightMargin: 10 backgroundRadius: width/2 isCustom: true colorSet: IncallMenuStyle.buttons.close onClicked: mainItem.close() } } } // CONTENT StackView{ id: contentsStack initialItem: settingsMenuComponent Layout.fillHeight: true Layout.fillWidth: true } Component{ id: settingsMenuComponent ColumnLayout{ property string objectName: 'settingsMenu' //: 'Settings' : Main menu title for settings. property string title: qsTr('incallMenuTitle') Layout.fillHeight: true Layout.fillWidth: true Repeater{ model: [ {titleIndex: 0 ,icon: IncallMenuStyle.settingsIcons.mediaIcon , nextPage:mediaMenu , visible: true}, {titleIndex: 1 , icon: (mainItem.callModel ? mainItem.callModel.conferenceVideoLayout == LinphoneEnums.ConferenceLayoutAudioOnly ? IncallMenuStyle.settingsIcons.audioOnlyIcon : mainItem.callModel.conferenceVideoLayout == LinphoneEnums.ConferenceLayoutGrid ? IncallMenuStyle.settingsIcons.gridIcon : IncallMenuStyle.settingsIcons.activeSpeakerIcon : IncallMenuStyle.settingsIcons.audioOnlyIcon) , nextPage:layoutMenu , visible: mainItem.callModel && mainItem.callModel.isConference}, { titleIndex: 2 , icon: IncallMenuStyle.settingsIcons.participantsIcon , nextPage:participantsMenu , visible: mainItem.callModel && mainItem.callModel.isConference} ] delegate: Borders{ bottomColor: IncallMenuStyle.list.border.color bottomWidth: IncallMenuStyle.list.border.width Layout.preferredHeight: Math.max(settingIcon.height, settingsDescription.implicitHeight) + 20 Layout.fillWidth: true visible: modelData.visible RowLayout{ anchors.fill: parent Icon{ id: settingIcon Layout.minimumWidth: iconWidth Layout.leftMargin: 15 Layout.alignment: Qt.AlignVCenter icon: modelData.icon overwriteColor: IncallMenuStyle.list.color iconWidth: IncallMenuStyle.settingsIcons.width iconHeight: IncallMenuStyle.settingsIcons.height } Text{ id: settingsDescription Layout.fillWidth: true height: implicitHeight wrapMode: Text.WordWrap elide: Text.ElideRight text: mainItem.menuTitles[modelData.titleIndex] font.pointSize: IncallMenuStyle.list.pointSize color: IncallMenuStyle.list.color } Icon{ Layout.minimumWidth: iconWidth Layout.rightMargin: 10 Layout.alignment: Qt.AlignVCenter //backgroundRadius: width/2 icon: IncallMenuStyle.buttons.next.icon overwriteColor: IncallMenuStyle.buttons.next.backgroundNormalColor iconWidth: IncallMenuStyle.buttons.next.iconSize iconHeight: IncallMenuStyle.buttons.next.iconSize } } MouseArea{ anchors.fill: parent onClicked: { contentsStack.push(modelData.nextPage, {title:Qt.binding(function() { return settingsDescription.text})}) } } } } Item{// Spacer Layout.fillWidth: true Layout.fillHeight: true } } } //----------------------------------------------------------------------------------------------------------------------------- Component{ id: mediaMenu ColumnLayout{ property string title Layout.fillHeight: true Layout.fillWidth: true MultimediaParametersDialog{ Layout.fillHeight: true Layout.fillWidth: true Layout.minimumHeight: fitHeight call: conference.callModel flat: true showMargins: true expandHeight: false fixedSize: false showTitleBar: false onExitStatus: contentsStack.pop() } Item{// Spacer Layout.fillWidth: true Layout.fillHeight: true } } } //----------------------------------------------------------------------------------------------------------------------------- Component{ id: layoutMenu ColumnLayout{ property string title Layout.fillHeight: true Layout.fillWidth: true Repeater{ //: 'Mosaic mode' : Grid layout for video conference. model: [{text: qsTr('incallMenuGridLayout'), icon: IncallMenuStyle.modeIcons.gridIcon, value:LinphoneEnums.ConferenceLayoutGrid} //: 'Active speaker mode' : Active speaker layout for video conference. , {text: qsTr('incallMenuActiveSpeakerLayout'), icon: IncallMenuStyle.modeIcons.activeSpeakerIcon, value:LinphoneEnums.ConferenceLayoutActiveSpeaker} //: 'Audio only mode' : Audio only layout for video conference. , {text: qsTr('incallMenuAudioLayout'), icon: IncallMenuStyle.modeIcons.audioOnlyIcon, value:LinphoneEnums.ConferenceLayoutAudioOnly} ] delegate: Borders{ bottomColor: IncallMenuStyle.list.border.color bottomWidth: IncallMenuStyle.list.border.width Layout.preferredHeight: Math.max(layoutIcon.height, radio.contentItem.implicitHeight) + 20 Layout.fillWidth: true enabled: mainItem.callModel && !mainItem.callModel.updating MouseArea{ anchors.fill: parent onClicked: radio.clicked() } RowLayout{ anchors.fill: parent RadioButton{ id: radio Layout.fillWidth: true Layout.leftMargin: 15 Layout.preferredHeight: contentItem.implicitHeight Layout.alignment: Qt.AlignVCenter ButtonGroup.group: modeGroup text: modelData.text property bool isInternallyChecked: mainItem.callModel ? (mainItem.callModel.localVideoEnabled && modelData.value == mainItem.callModel.conferenceVideoLayout) || (!mainItem.callModel.localVideoEnabled && modelData.value == LinphoneEnums.ConferenceLayoutAudioOnly) : false // break bind. Radiobutton checked itself without taking care of custom binding. This workaound works as long as we don't really need the binding. onIsInternallyCheckedChanged: checked = isInternallyChecked Component.onCompleted: checked = isInternallyChecked onClicked: mainItem.layoutChanging(modelData.value) } Icon{ id: layoutIcon Layout.minimumWidth: iconWidth Layout.rightMargin: 10 Layout.alignment: Qt.AlignVCenter icon: modelData.icon iconWidth: IncallMenuStyle.modeIcons.width iconHeight: IncallMenuStyle.modeIcons.height } } } } Item{// Spacer Layout.fillWidth: true Layout.fillHeight: true } } } //----------------------------------------------------------------------------------------------------------------------------- Component{ id: participantsMenu ColumnLayout{ property string title Layout.fillHeight: true Layout.fillWidth: true ParticipantsListView{ Layout.fillHeight: true Layout.fillWidth: true Layout.leftMargin: 10 Layout.rightMargin: 10 conferenceModel: mainItem.conferenceModel isAdmin: mainItem.isMeAdmin Text{ //: 'Your are currently alone in this meeting' : Message to warn the user when there is no other participant. text: qsTr('incallMenuParticipantsAlone') visible: parent.count <= 1 font.pointSize: IncallMenuStyle.list.pointSize color: IncallMenuStyle.list.color } } Item{// Spacer Layout.fillWidth: true Layout.fillHeight: true } Component.onCompleted: mainItem.isParticipantsMenu = true Component.onDestruction: mainItem.isParticipantsMenu = false } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Menus/SipAddressesMenu.qml000066400000000000000000000056561434616504300302760ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone.Styles 1.0 // ============================================================================= // SipAddressesMenu Item { id: sipAddressesMenu // --------------------------------------------------------------------------- property alias relativeTo: menu.relativeTo property alias relativeX: menu.relativeX property alias relativeY: menu.relativeY property var sipAddresses: [] // --------------------------------------------------------------------------- function open (callback) { var length = sipAddresses.length if (!length) { return } if (length === 1) { if(callback) return callback(sipAddresses[0]) else return sipAddressesMenu.sipAddressClicked(sipAddresses[0]) } menu.callback = callback menu.open() } function _fillModel () { model.clear() sipAddresses.forEach(function (sipAddress) { model.append({ $modelData: sipAddress }) }) } // --------------------------------------------------------------------------- signal sipAddressClicked (string sipAddress) // --------------------------------------------------------------------------- onSipAddressesChanged: _fillModel() // --------------------------------------------------------------------------- DropDownDynamicMenu { id: menu property var callback parent: sipAddressesMenu.parent relativeTo: sipAddressesMenu.parent relativeY: sipAddressesMenu.parent.height entryHeight: SipAddressesMenuStyle.entry.height maxMenuHeight: SipAddressesMenuStyle.maxHeight ScrollableListView { id: list spacing: SipAddressesMenuStyle.spacing width: SipAddressesMenuStyle.entry.width model: ListModel { id: model Component.onCompleted: _fillModel() } delegate: Rectangle { height: menu.entryHeight width: list.width color: mouseArea.pressed ? SipAddressesMenuStyle.entry.color.pressed : ( mouseArea.containsMouse ? SipAddressesMenuStyle.entry.color.hovered : SipAddressesMenuStyle.entry.color.normal ) Text { anchors { left: parent.left leftMargin: SipAddressesMenuStyle.entry.leftMargin right: parent.right rightMargin: SipAddressesMenuStyle.entry.rightMargin } color: SipAddressesMenuStyle.entry.text.color elide: Text.ElideRight font.pointSize: SipAddressesMenuStyle.entry.text.pointSize height: parent.height text: $modelData verticalAlignment: Text.AlignVCenter } MouseArea { id: mouseArea anchors.fill: parent onClicked: { menu.close() if( menu.callback) menu.callback($modelData) else sipAddressesMenu.sipAddressClicked($modelData) } } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Misc/000077500000000000000000000000001434616504300241355ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Misc/MessageCounter.qml000066400000000000000000000021331434616504300275730ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone.Styles 1.0 // ============================================================================= Item{ id: messageCounter property bool showOnlyNumber: false property int count: 0 property alias icon: backgroundIcon.icon property alias iconSize: amountIcon.iconSize property int pointSize: MessageCounterStyle.text.pointSize visible: messageCounter.count > 0 height: showOnlyNumber ? amountIcon.height : backgroundIcon.height width: showOnlyNumber ? amountIcon.width : backgroundIcon.width Icon { id: backgroundIcon icon: 'chat_count' iconSize: MessageCounterStyle.iconSize.message visible: !messageCounter.showOnlyNumber } Icon { id: amountIcon anchors { horizontalCenter: parent.right verticalCenter: parent.bottom } icon: 'chat_amount' iconSize: MessageCounterStyle.iconSize.amount visible: messageCounter.count > 0 Text { anchors.centerIn: parent color: MessageCounterStyle.text.color font.pointSize: messageCounter.pointSize text: (messageCounter.count>99 ? '+' : messageCounter.count) } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Notifications/000077500000000000000000000000001434616504300260535ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Notifications/Notification.qml000066400000000000000000000022641434616504300312200ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone.Styles 1.0 // ============================================================================= DesktopPopup { id: notification property alias icon: iconSign.icon property var notificationData: ({ timelineModel : null }) property int overrodeHeight default property alias _content: content.data signal deleteNotification (var notification) // Use as an intermediate between signal/slot without propagate the notification var : last signal parameter will be the last notification instance function deleteNotificationSlot(){ deleteNotification(notification) } function _close (cb) { if (cb) { cb() } deleteNotificationSlot(); } Rectangle { color: NotificationStyle.color height: overrodeHeight || NotificationStyle.height width: NotificationStyle.width border { color: NotificationStyle.border.color width: NotificationStyle.border.width } Item { id: content anchors.fill: parent } Icon { id: iconSign anchors { left: parent.left top: parent.top } iconSize: NotificationStyle.iconSize } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Notifications/Notification.spec.qml000066400000000000000000000021131434616504300321420ustar00rootroot00000000000000import QtTest 1.1 import Linphone 1.0 import Utils 1.0 // ============================================================================= // Check defined properties/methods used in `Notifier.cpp`. TestCase { Notification { id: notification } function test_notificationDataProperty () { compare(Utils.isObject(notification.notificationData), true) } function test_notificationPopupX () { compare(Utils.isInteger(notification.popupX), true) } function test_notificationPopupY () { compare(Utils.isInteger(notification.popupY), true) } function test_notificationPopupHeight () { compare(Utils.isInteger(notification.popupHeight), true) } function test_notificationPopupWidth () { compare(Utils.isInteger(notification.popupWidth), true) } function test_notificationOpenMethod () { compare(Utils.isFunction(notification.open), true) } function test_childWindow () { var window = notification.data[0] compare(Utils.qmlTypeof(window, 'QQuickWindowQmlImpl'), true) compare(window.objectName === '__internalWindow', true) } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Notifications/NotificationBasic.qml000066400000000000000000000017371434616504300321660ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone.Styles 1.0 // ============================================================================= Notification { id: notification property string message property var handler: (function () {}) overrodeHeight: NotificationBasicStyle.overrodeHeight // --------------------------------------------------------------------------- Loader { active: Boolean(notification.message) anchors { fill: parent leftMargin: NotificationBasicStyle.leftMargin rightMargin: NotificationBasicStyle.rightMargin } sourceComponent: Text { anchors.fill: parent color: NotificationBasicStyle.message.color font.pointSize: NotificationBasicStyle.message.pointSize text: notification.message verticalAlignment: Text.AlignVCenter wrapMode: Text.Wrap MouseArea { anchors.fill: parent onClicked: notification._close(notification.handler) } } } } NotificationNewVersionAvailable.qml000066400000000000000000000002721434616504300347570ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/NotificationsNotificationBasic { icon: 'update_sign' message: notificationData.message?notificationData.message:'' handler: (function () { Qt.openUrlExternally(notificationData.url) }) } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedCall.qml000066400000000000000000000053741434616504300334700ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 // ============================================================================= Notification { id: notification icon: 'call_sign_incoming' // --------------------------------------------------------------------------- readonly property var call: notificationData && notificationData.call // --------------------------------------------------------------------------- Loader { active: Boolean(notification.call) anchors { fill: parent leftMargin: NotificationReceivedCallStyle.leftMargin rightMargin: NotificationReceivedCallStyle.rightMargin bottomMargin: NotificationReceivedCallStyle.bottomMargin } sourceComponent: ColumnLayout { spacing: NotificationReceivedCallStyle.spacing Contact { Layout.fillWidth: true /* property var peerAddress: notification.call ? notification.call.fullPeerAddress : '' onPeerAddressChanged: { entry=SipAddressesModel.getSipAddressObserver(peerAddress, notification.call ? notification.call.fullLocalAddress : '') } entry: SipAddressesModel.getSipAddressObserver(peerAddress, notification.call ? notification.call.fullLocalAddress : '') */ entry: notification.call Component.onDestruction: entry=null// Need to set it to null because of not calling destructor if not. } // --------------------------------------------------------------------- // Action buttons. // --------------------------------------------------------------------- Item { Layout.fillHeight: true Layout.fillWidth: true ActionBar { id: acceptActionBar anchors.centerIn: parent iconSize: NotificationReceivedCallStyle.actionArea.iconSize ActionButton { isCustom: true backgroundRadius: 90 colorSet: NotificationReceivedCallStyle.acceptVideoCall visible: SettingsModel.videoSupported && notification.call.getRemoteVideoEnabled() onClicked: notification._close(notification.call.acceptWithVideo) } ActionButton { isCustom: true backgroundRadius: 90 colorSet: NotificationReceivedCallStyle.acceptCall onClicked: notification._close(notification.call.accept) } } ActionBar { anchors { right: parent.right rightMargin: NotificationReceivedCallStyle.actionArea.rightButtonsGroupMargin verticalCenter: parent.verticalCenter } iconSize: NotificationReceivedCallStyle.actionArea.iconSize ActionButton { isCustom: true backgroundRadius: 90 colorSet: NotificationReceivedCallStyle.hangup onClicked: notification._close(notification.call.terminate) } } } } } } NotificationReceivedFileMessage.qml000066400000000000000000000053001434616504300347070ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Notificationsimport QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import UtilsCpp 1.0 // ============================================================================= Notification { id: notification icon: 'file_sign' overrodeHeight: NotificationReceivedFileMessageStyle.overrodeHeight // --------------------------------------------------------------------------- readonly property string fileUri: notificationData && notificationData.fileUri || '' readonly property string imageUri: notificationData && notificationData.imageUri || '' property string systemFileUri: Utils.getUriFromSystemPath(notification.fileUri) // --------------------------------------------------------------------------- Loader { active: Boolean(notification.fileUri) anchors { fill: parent leftMargin: NotificationReceivedFileMessageStyle.leftMargin rightMargin: NotificationReceivedFileMessageStyle.rightMargin } sourceComponent: RowLayout { anchors.fill: parent spacing: NotificationReceivedFileMessageStyle.spacing Text { Layout.fillWidth: true color: NotificationReceivedFileMessageStyle.fileName.color elide: Text.ElideRight font.pointSize: NotificationReceivedFileMessageStyle.fileName.pointSize text: Utils.basename(notification.fileUri) visible:!normalImage.visible && !animatedImage.visible } Loader{ Layout.fillHeight: true Layout.fillWidth: true sourceComponent: notification.fileUri && UtilsCpp.isAnimatedImage(notification.fileUri) ? animatedImage : normalImage active: fileUri || imageUri Component{ id: normalImage Image{ id:image mipmap: SettingsModel.mipmapEnabled fillMode: Image.PreserveAspectFit source: (imageUri ?"image://external/"+notification.imageUri : '') visible: image.status == Image.Ready } } Component{ id: animatedImage AnimatedImage{ id:image mipmap: SettingsModel.mipmapEnabled fillMode: Image.PreserveAspectFit source: (systemFileUri ? systemFileUri: '') } } } Text { Layout.preferredWidth: NotificationReceivedFileMessageStyle.fileSize.width color: NotificationReceivedFileMessageStyle.fileSize.color elide: Text.ElideRight font.pointSize: NotificationReceivedFileMessageStyle.fileSize.pointSize horizontalAlignment: Text.AlignRight text: Utils.formatSize(notification.notificationData.fileSize) } } MouseArea { anchors.fill: parent onClicked: notification._close(function () { if (!Qt.openUrlExternally(systemFileUri)) { Qt.openUrlExternally(Utils.dirname(systemFileUri)) } }) } } } NotificationReceivedMessage.qml000066400000000000000000000062471434616504300341220ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Notificationsimport QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import UtilsCpp 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Notification { id: notification icon: 'message_sign' // --------------------------------------------------------------------------- readonly property TimelineModel timelineModel: notificationData && notificationData.timelineModel readonly property string peerAddress: notificationData && notificationData.peerAddress || '' readonly property string localAddress: notificationData && notificationData.localAddress || '' readonly property string fullPeerAddress: notificationData && notificationData.fullPeerAddress || '' readonly property string fullLocalAddress: notificationData && notificationData.fullLocalAddress || '' // --------------------------------------------------------------------------- Loader { active: timelineModel//Boolean(notification.peerAddress) && Boolean(notification.localAddress) anchors { fill: parent leftMargin: NotificationReceivedMessageStyle.leftMargin rightMargin: NotificationReceivedMessageStyle.rightMargin bottomMargin: NotificationReceivedMessageStyle.bottomMargin } sourceComponent: ColumnLayout { spacing: NotificationReceivedMessageStyle.spacing Contact { Layout.fillWidth: true property ChatRoomModel chatRoomModel : notification.timelineModel.getChatRoomModel() property var sipObserver: SipAddressesModel.getSipAddressObserver(notification.fullPeerAddress, notification.fullLocalAddress) subtitle: chatRoomModel.isOneToOne ? SipAddressesModel.cleanSipAddress(notification.fullPeerAddress) : UtilsCpp.getDisplayName(notification.fullPeerAddress) entry: chatRoomModel ? chatRoomModel : sipObserver Component.onDestruction: sipObserver=null// Need to set it to null because of not calling destructor if not. } Rectangle { Layout.fillHeight: true Layout.fillWidth: true color: NotificationReceivedMessageStyle.messageContainer.color radius: NotificationReceivedMessageStyle.messageContainer.radius Text { anchors { fill: parent margins: NotificationReceivedMessageStyle.messageContainer.margins } color: NotificationReceivedMessageStyle.messageContainer.text.color elide: Text.ElideRight font { italic: true pointSize: NotificationReceivedMessageStyle.messageContainer.text.pointSize } verticalAlignment: Text.AlignVCenter text: notification.notificationData.message wrapMode: Text.Wrap } } } } MouseArea { anchors.fill: parent onClicked: notification._close(function () { AccountSettingsModel.setDefaultAccountFromSipAddress(notification.localAddress) notification.timelineModel.selected = true console.debug("Load conversation from notification") notification.notificationData.window.setView('Conversation', { chatRoomModel:notification.timelineModel.getChatRoomModel() }) App.smartShowWindow(notification.notificationData.window) }) } } NotificationRecordingCompleted.qml000066400000000000000000000005221434616504300346260ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Notificationsimport Utils 1.0 // ============================================================================= NotificationBasic { icon: 'recording_sign' message: Utils.basename(notificationData.filePath) handler: (function () { Qt.openUrlExternally(Utils.dirname( Utils.getUriFromSystemPath(notificationData.filePath) )) }) } NotificationSnapshotWasTaken.qml000066400000000000000000000005211434616504300343110ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Notificationsimport Utils 1.0 // ============================================================================= NotificationBasic { icon: 'snapshot_sign' message: Utils.basename(notificationData.filePath) handler: (function () { Qt.openUrlExternally(Utils.dirname( Utils.getUriFromSystemPath(notificationData.filePath) )) }) } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Presence/000077500000000000000000000000001434616504300250065ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Presence/PresenceLevel.qml000066400000000000000000000010361434616504300302550ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 // ============================================================================= // Wrapper to use `icon` property. Item { property var level: null property bool betterIcon : false Icon { anchors.centerIn: parent icon: (level !== -1 && level != null) ? (betterIcon? Presence.getBetterPresenceLevelIconName(level) : Presence.getPresenceLevelIconName(level)) : '' iconSize: parent.height > parent.width ? parent.width : parent.height } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/SmartSearchBar/000077500000000000000000000000001434616504300261035ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml000066400000000000000000000067011434616504300314630ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import Linphone.Styles 1.0 // ============================================================================= SearchBox { id: searchBox // --------------------------------------------------------------------------- readonly property alias isOpen: searchBox._isOpen property alias header : view.headerItem property alias actions : view.actions property alias showHeader : view.showHeader property string previousText: text onTextChanged: if( text != '') previousText = text; property alias participantListModel : searchModel.participantListModel function addAddressToIgnore(entry){ searchModel.addAddressToIgnore(entry) } function removeAddressToIgnore(entry){ searchModel.removeAddressToIgnore(entry) } function isIgnored(address){ return searchModel.isIgnored(address) } property var resultExceptions : [] // --------------------------------------------------------------------------- signal addContact (string sipAddress) signal launchChat (string sipAddress) signal launchSecureChat (string sipAddress) signal launchCall (string sipAddress) signal launchVideoCall (string sipAddress) signal entryClicked (var entry) // --------------------------------------------------------------------------- entryHeight: SipAddressesViewStyle.entry.height // --------------------------------------------------------------------------- onEnterPressed: { var sipAddress = view.interpretableSipAddress return sipAddress.length > 0 && SettingsModel.outgoingCallsEnabled && searchBox.launchCall(sipAddress) } // --------------------------------------------------------------------------- SipAddressesView { id: view actions: [{ colorSet: SipAddressesViewStyle.videoCall, secure: 0, visible: true, handler: function (entry) { searchBox.closeMenu() searchBox.launchVideoCall(entry.sipAddress) }, visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.showStartVideoCallButton }, { colorSet: SipAddressesViewStyle.call, secure: 0, visible: true, handler: function (entry) { searchBox.closeMenu() searchBox.launchCall(entry.sipAddress) }, visible: SettingsModel.outgoingCallsEnabled }, { colorSet: SettingsModel.getShowStartChatButton() ? SipAddressesViewStyle.chat : SipAddressesViewStyle.history, secure: 0, handler: function (entry) { searchBox.closeMenu() searchBox.launchChat(entry.sipAddress) }, visible: SettingsModel.standardChatEnabled, zz: 'toto' }, { colorSet: SettingsModel.getShowStartChatButton() ? SipAddressesViewStyle.chat : SipAddressesViewStyle.history, secure: 1, visible: SettingsModel.secureChatEnabled && AccountSettingsModel.conferenceUri != '', handler: function (entry) { searchBox.closeMenu() searchBox.launchSecureChat(entry.sipAddress) } } ] headerButtonDescription: qsTr('addContact') headerButtonIcon: 'contact_add_custom' headerButtonOverwriteColor: SipAddressesViewStyle.header.button.color headerButtonAction: SettingsModel.contactsEnabled && (function (sipAddress) { searchBox.closeMenu() searchBox.addContact(sipAddress) }) genSipAddress: searchBox.filter model: SearchSipAddressesProxyModel { id:searchModel } onEntryClicked: { searchBox.closeMenu() searchBox.entryClicked(entry) } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Sticker/000077500000000000000000000000001434616504300246465ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Sticker/AvatarSticker.qml000066400000000000000000000036551434616504300301350ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import App.Styles 1.0 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= DecorationSticker { id:mainItem property ParticipantDeviceModel currentDevice property CallModel callModel property alias isPaused: avatar.isPaused property bool showCloseButton: false property bool showActiveSpeakerOverlay: true property real avatarRatio : 2/3 property color color : AvatarStickerStyle.stickerBackgroundColor property alias image: avatar.image property alias avatarBackgroundColor: avatar.backgroundColor property alias avatarUsername: avatar.username property alias conferenceInfoModel: avatar.conferenceInfoModel property alias isPreview: avatar.isPreview property bool showAvatarBorder: false property alias showCustomButton: mainItem._showCustomButton property alias customButtonToggled: mainItem._customButtonToggled property alias customButtonColorSet: mainItem._customButtonColorSet _currentDevice: currentDevice _callModel: callModel _isPaused: isPaused _isPreview: isPreview _showCloseButton: showCloseButton _showActiveSpeakerOverlay: showActiveSpeakerOverlay username: avatarUsername clip:false radius: AvatarStickerStyle.radius _content: Rectangle{ anchors.fill: parent color: mainItem.color radius: mainItem.radius border.color: '#40000000' border.width: mainItem.showAvatarBorder && !mainItem.speakingOverlayDisplayed? 1 : 0 IncallAvatar { id: avatar anchors.centerIn: parent participantDeviceModel: mainItem.currentDevice call: participantDeviceModel ? undefined : mainItem.callModel height: Utils.computeAvatarSize(mainItem, mainItem.width, avatarRatio) width: height backgroundColor: AvatarStickerStyle.inBackgroundColor } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Sticker/CameraSticker.qml000066400000000000000000000056401434616504300301030ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import App.Styles 1.0 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= DecorationSticker{ id: mainItem property alias currentDevice: camera.currentDevice property alias callModel: camera.callModel property alias deactivateCamera: camera.deactivateCamera property alias hideCamera: camera.hideCamera property alias isPaused: camera.isPaused property alias isPreview: camera.isPreview property alias isFullscreen: camera.isFullscreen property alias isCameraFromDevice: camera.isCameraFromDevice property alias isReady: camera.isReady property alias isVideoEnabled: camera.isVideoEnabled property bool showCloseButton: false property bool showActiveSpeakerOverlay: true property color color : CameraStickerStyle.cameraBackgroundColor property alias showCustomButton: mainItem._showCustomButton property alias customButtonToggled: mainItem._customButtonToggled property alias customButtonColorSet: mainItem._customButtonColorSet signal videoDefinitionChanged() onBackgroundClicked: camera.resetActive() onDeactivateCameraChanged: if( deactivateCamera) camera.resetActive() function resetCamera(){ camera.resetActive(); } _currentDevice: currentDevice _callModel: callModel _isPaused: isPaused _isPreview: isPreview _showCloseButton: showCloseButton _showActiveSpeakerOverlay: showActiveSpeakerOverlay clip:false radius: CameraStickerStyle.radius _content: Rectangle{ anchors.fill: parent color: mainItem.color radius: CameraStickerStyle.radius Rectangle{ id: showArea anchors.fill: parent radius: mainItem.radius visible: false color: 'red' } CameraItem{ id: camera callModel: mainItem.callModel anchors.centerIn: parent anchors.fill: parent visible: false onVideoDefinitionChanged: mainItem.videoDefinitionChanged() } OpacityMask{ id: renderedCamera anchors.fill: parent source: camera maskSource: showArea invert:false visible: true /* In case we need transformations. property Matrix4x4 mirroredRotationMatrix : Matrix4x4 {// 180 rotation + mirror matrix: Qt.matrix4x4(-Math.cos(Math.PI), -Math.sin(Math.PI), 0, 0, Math.sin(Math.PI), Math.cos(Math.PI), 0, camera.height, 0, 0, 1, 0, 0, 0, 0, 1) } property Matrix4x4 rotationMatrix : Matrix4x4 {// 180 rotation only matrix: Qt.matrix4x4(Math.cos(Math.PI), -Math.sin(Math.PI), 0, camera.width, Math.sin(Math.PI), Math.cos(Math.PI), 0, camera.height, 0, 0, 1, 0, 0, 0, 0, 1) } //transform: ( camera.isPreview ? mirroredRotationMatrix : rotationMatrix) */ } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Sticker/DecorationSticker.qml000066400000000000000000000120361434616504300307770ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import App.Styles 1.0 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import Linphone.Styles 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // Decoration is used to be inherited // Variables in '_' allow to use alias in inheritance. These variables should not be change inside DecorationSticker // ============================================================================= Item{ id: mainItem default property alias _content: content.data property alias speakingOverlayDisplayed: effect.visible property string username: mainItem._currentDevice ? mainItem._currentDevice.displayName : '' property bool showUsername: true property ParticipantDeviceModel _currentDevice property CallModel _callModel property bool _isPaused property bool _isPreview property bool _showCloseButton: false property bool _showActiveSpeakerOverlay: true property bool _showCustomButton: false property bool _customButtonToggled: false property alias _customButtonColorSet : customButton.colorSet property int radius signal closeRequested() signal backgroundClicked() signal customButtonClicked() MouseArea{ anchors.fill: parent onClicked: mainItem.backgroundClicked() } RectangularGlow { id: effect anchors.fill: content glowRadius: 4 spread: 0.9 color: DecorationStickerStyle.border.color cornerRadius: (mainItem.radius? mainItem.radius : 0) + glowRadius visible: mainItem._showActiveSpeakerOverlay && mainItem._currentDevice && mainItem._currentDevice.isSpeaking } Item{ id: content anchors.fill: parent } Rectangle{ id: hideView anchors.fill: parent color: DecorationStickerStyle.pauseView.backgroundColor radius: DecorationStickerStyle.radius visible: mainItem._isPaused Rectangle{ anchors.centerIn: parent height: DecorationStickerStyle.pauseView.button.iconSize width: height radius: width/2 color: DecorationStickerStyle.pauseView.button.backgroundNormalColor Icon{ anchors.centerIn: parent icon: DecorationStickerStyle.pauseView.button.icon overwriteColor: DecorationStickerStyle.pauseView.button.foregroundNormalColor iconSize: DecorationStickerStyle.pauseView.button.iconSize } } } Text{ id: usernameItem visible: mainItem.showUsername && mainItem._currentDevice anchors.right: parent.right anchors.left: parent.left anchors.bottom: parent.bottom anchors.margins: 10 elide: Text.ElideRight maximumLineCount: 1 //: 'paused' : Pause state on sticker, next to username. text: mainItem.username + (mainItem._isPaused ? ' ('+qsTr('paused')+')' : '') font.pointSize: DecorationStickerStyle.contactDescription.pointSize font.weight: DecorationStickerStyle.contactDescription.weight color: DecorationStickerStyle.contactDescription.color } Glow { anchors.fill: usernameItem visible: usernameItem.visible //spread: 1 radius: 12 samples: 25 color: "#80000000" source: usernameItem } Loader{ id: closeLoader anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 5 anchors.topMargin: 5 active: mainItem._showCloseButton && mainItem._isPreview && mainItem._callModel && mainItem._callModel.videoEnabled sourceComponent: Component{ ActionButton{ isCustom: true colorSet: DecorationStickerStyle.closePreview onClicked: mainItem.closeRequested() } } } ColumnLayout{ anchors.top: parent.top anchors.right: parent.right anchors.topMargin: 10 anchors.rightMargin: 10 ActionButton{// Custom action id: customButton visible: mainItem._showCustomButton isCustom: true backgroundRadius: width/2 toggled: mainItem._customButtonToggled onClicked: mainItem.customButtonClicked() } Rectangle{// Mute visible: mainItem._currentDevice && mainItem._currentDevice.isMuted Layout.preferredHeight: DecorationStickerStyle.isMuted.button.iconSize Layout.preferredWidth: DecorationStickerStyle.isMuted.button.iconSize radius: width/2 color: DecorationStickerStyle.isMuted.button.backgroundNormalColor Icon{ anchors.centerIn: parent icon: DecorationStickerStyle.isMuted.button.icon overwriteColor: DecorationStickerStyle.isMuted.button.foregroundNormalColor iconSize: DecorationStickerStyle.isMuted.button.iconSize } } Loader{ id: busyLoader Layout.preferredHeight: 20 Layout.preferredWidth: 20 active: mainItem._currentDevice && (mainItem._currentDevice.state == LinphoneEnums.ParticipantDeviceStateJoining || mainItem._currentDevice.state == LinphoneEnums.ParticipantDeviceStateScheduledForJoining || mainItem._currentDevice.state == LinphoneEnums.ParticipantDeviceStateAlerting) sourceComponent: Component{ BusyIndicator{// Joining spinner id: joiningSpinner running: false Timer{// Delay starting spinner (Qt bug) id: indicatorDelay interval: 100 onTriggered: joiningSpinner.running = true } Component.onCompleted: indicatorDelay.start() } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Sticker/Sticker.qml000066400000000000000000000065351434616504300267760ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import App.Styles 1.0 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils import UtilsCpp 1.0 // A sticker display the avatar or its camera view // ============================================================================= Item{ id: mainItem property bool flipped : !deactivateCamera && camera.isReady property bool showCustomButton: false property bool showUsername: true property bool customButtonToggled: false property QtObject customButtonColorSet: StickerStyle.custom property alias currentDevice: camera.currentDevice property alias callModel: camera.callModel property alias isPaused: camera.isPaused property alias isPreview: camera.isPreview property alias showCloseButton: camera.showCloseButton property alias showActiveSpeakerOverlay: camera.showActiveSpeakerOverlay property alias isCameraFromDevice: camera.isCameraFromDevice property alias deactivateCamera: camera.deactivateCamera readonly property alias isVideoEnabled: camera.isVideoEnabled property alias image: avatar.image property alias avatarBackgroundColor: avatar.avatarBackgroundColor property alias avatarStickerBackgroundColor: avatar.color property alias avatarRatio: avatar.avatarRatio property alias showAvatarBorder: avatar.showAvatarBorder property alias avatarUsername: avatar.avatarUsername property alias conferenceInfoModel: avatar.conferenceInfoModel signal videoDefinitionChanged() signal customButtonClicked() property alias username: avatar.username function resetCamera(){ camera.resetCamera() } clip:false state: mainItem.flipped ? 'back' : 'front' states: [State { name: "front" }, State { name: "back" } ] property bool quickTransition : false property alias cameraOpacity: camera.opacity transitions: [Transition { from: 'front' to: 'back' SequentialAnimation { NumberAnimation { target: mainItem; duration: quickTransition ? 0 : 400 } NumberAnimation { target: camera; property: 'opacity'; to:1.0; duration: 100;} } }, Transition { from: 'back' to: 'front' SequentialAnimation { NumberAnimation { target: mainItem; duration: 0 } NumberAnimation { target: camera; property: 'opacity'; to:0.0; duration: 100;} } } ] AvatarSticker{ id: avatar currentDevice: mainItem.currentDevice callModel: mainItem.callModel isPaused: mainItem.isPaused isPreview: mainItem.isPreview showCloseButton: mainItem.showCloseButton showActiveSpeakerOverlay: mainItem.showActiveSpeakerOverlay showUsername: mainItem.showUsername showCustomButton: mainItem.showCustomButton customButtonToggled: mainItem.customButtonToggled customButtonColorSet: mainItem.customButtonColorSet height: mainItem.height width: mainItem.width onCustomButtonClicked: mainItem.customButtonClicked() } CameraSticker{ id: camera height: mainItem.height width: mainItem.width opacity: 0.0 username: mainItem.username showUsername: mainItem.showUsername showCustomButton: mainItem.showCustomButton customButtonToggled: mainItem.customButtonToggled customButtonColorSet: mainItem.customButtonColorSet onVideoDefinitionChanged: mainItem.videoDefinitionChanged() onCustomButtonClicked: mainItem.customButtonClicked() } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/000077500000000000000000000000001434616504300245255ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Account/000077500000000000000000000000001434616504300261215ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Account/AccountStatusStyle.qml000066400000000000000000000015761434616504300324660ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'AccountStatus' property int horizontalSpacing: 8 property int verticalSpacing: 2 property color busyColor: ColorsList.add(sectionName+'_spinner', 'i').color property QtObject presenceLevel: QtObject { property int bottomMargin: 1 property int size: 16 } property QtObject sipAddress: QtObject { property color color: ColorsList.add(sectionName+'_sipAddress', 'g').color property int pointSize: Units.dp * 10 } property QtObject username: QtObject { property color color: ColorsList.add(sectionName+'_username', 'j').color property int pointSize: Units.dp * 11 } property QtObject messageCounter: QtObject { property int bottomMargin: 4 property int size: 16 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Blocks/000077500000000000000000000000001434616504300257425ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Blocks/CardBlockStyle.qml000066400000000000000000000015031434616504300313210ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CardBlock' property int spacing: 30 property int width: 200 property QtObject content: QtObject { property int height: 40 } property QtObject description: QtObject { property color color: ColorsList.add(sectionName+'_description', 'n').color property int pointSize: Units.dp * 10 property int height: 40 } property QtObject icon: QtObject { property int bottomMargin: 20 property int size: 148 } property QtObject title: QtObject { property color color: ColorsList.add(sectionName+'_title', 'j').color property int bottomMargin: 10 property int pointSize: Units.dp * 10 property int height: 20 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Blocks/RequestBlockStyle.qml000066400000000000000000000010201434616504300320720ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'RequestBlock' property int height: 80 property QtObject error: QtObject { property color color: ColorsList.add(sectionName+'_error', 'error').color property int pointSize: Units.dp * 11 property int padding: 4 } property QtObject loadingIndicator: QtObject { property int height: 20 property int width: 20 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Calls/000077500000000000000000000000001434616504300255635ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Calls/CallControlsStyle.qml000066400000000000000000000005621434616504300317210ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CallControl' property color color: ColorsList.add(sectionName, 'e').color property int height: 60 property int leftMargin: 12 property int rightMargin: 12 property int signSize: 40 } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Calls/CallStatisticsStyle.qml000066400000000000000000000040511434616504300322450ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CallStats' property color outsideColor: ColorsList.add(sectionName+'_outside_bg', 'j50').color property color color: ColorsList.add(sectionName+'_bg', 'j90').color property int height: 280 property int leftMargin: 12 property int rightMargin: 12 property int topMargin: 40 property int spacing: 8 property QtObject popup: QtObject{ property int topMargin: 60 property int bottomMargin: 100 property int leftMargin: 110 property int rightMargin: 110 property int radius: 10 } property QtObject title: QtObject { property color color: ColorsList.add(sectionName+'_title', 'q').color property int bottomMargin: 20 property int pointSize: Units.dp * 16 } property QtObject key: QtObject { property color color: ColorsList.add(sectionName+'_key', 'q').color property int pointSize: Units.dp * 10 property int width: 200 } property QtObject value: QtObject { property color color: ColorsList.add(sectionName+'_value', 'q').color property int pointSize: Units.dp * 10 } property QtObject cancel: QtObject { property int iconSize: 40 property string icon : 'cancel_custom' property string name : 'cancel' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_inv_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Calls/CallsStyle.qml000066400000000000000000000074361434616504300303670ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'Calls' property QtObject entry: QtObject { property int iconActionSize: 35 property int iconMenuSize: 35 property int height: 30 property int width: 200 property QtObject color: QtObject { property color normal: ColorsList.add('Calls_entry_n', 'e').color property color selected: ColorsList.add('Calls_entry_c', 'j').color } property QtObject burgerMenu: QtObject { property string name : 'burgerMenu' property string icon : 'menu_vdots_custom' property int iconSize: 35 property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject selectedBurgerMenu: QtObject { property string name : 'selectedBurgerMenu' property string icon : 'menu_vdots_custom' property int iconSize: 35 property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_inv_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_inv_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_inv_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_inv_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_inv_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_inv_fg').color } property QtObject hangup: QtObject { property int iconSize: 35 property string icon : 'hangup_custom' property string name : 'hangup' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'r_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'r_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'r_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'r_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'r_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'r_p_b_fg').color } property QtObject endCallAnimation: QtObject { property color blinkColor: ColorsList.add('Calls_entry_end_blink', 'i').color property int duration: 300 property int loops: 3 } property QtObject subtitleColor: QtObject { property color normal: ColorsList.add('Calls_entry_subtitle_n', 'n').color property color selected: ColorsList.add('Calls_entry_subtitle_selected', 'q').color } property QtObject titleColor: QtObject { property color normal: ColorsList.add('Calls_entry_title_n', 'j').color property color selected: ColorsList.add('Calls_entry_title_selected', 'q').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Calls/ConferenceControlsStyle.qml000066400000000000000000000005361434616504300331160ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ConferenceControls' property color color: ColorsList.add(sectionName, 'e').color property int height: 60 property int leftMargin: 12 property int rightMargin: 12 } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Camera/000077500000000000000000000000001434616504300257155ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Camera/CameraViewStyle.qml000066400000000000000000000070051434616504300314760ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CameraView' property color outBackgroundColor: ColorsList.add(sectionName+'_out_bg', 'conference_out_avatar_bg').color property color inAvatarBackgroundColor: ColorsList.add(sectionName+'_in_bg', 'conference_bg').color property color cameraBackgroundColor: ColorsList.add(sectionName+'_camera_bg', 'fullscreen_conference_bg').color property int radius : 10 property QtObject contactDescription: QtObject { property color color: ColorsList.add(sectionName+'_username', 'q').color property int pointSize: Units.dp * 12 property int weight: Font.Bold } property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_border', 'b').color property int width: 2 } //------------------------------------------------------------------------------ property QtObject closePreview: QtObject { property int iconSize: 40 property string icon : 'close_custom' property string name : 'close_preview' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_inv_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_inv_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_inv_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_inv_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_inv_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_inv_fg').color } //------------------------------------------------------------------------------ property QtObject pauseView: QtObject{ property color backgroundColor : ColorsList.add(sectionName+'_pauseView_bg_n', 'l').color property QtObject button: QtObject { property int iconSize: 80 property string icon : 'pause_custom' property string name : 'pause' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg', icon, 's_n_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg', icon, 's_n_b_fg').color } } property QtObject isMuted: QtObject{ property color backgroundColor : ColorsList.add(sectionName+'_isMuted_bg', 'j').color property QtObject button: QtObject { property int iconSize: 30 property string icon : 'micro_off_custom' property string name : 'isMuted' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg', icon, 's_d_b_fg').color } } property QtObject isAudioOnly: QtObject{ property color backgroundColor : ColorsList.add(sectionName+'_isAudioOnly_bg', 'j').color property QtObject button: QtObject { property int iconSize: 30 property string icon : 'conference_audio_only_custom' property string name : 'isAudioOnly' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg', icon, 's_d_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Chat/000077500000000000000000000000001434616504300254045ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Chat/ChatAudioMessageStyle.qml000066400000000000000000000076031434616504300323140ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'ChatAudioMessage' property int minWidth: 500 property int emptySpace: 10 property color color: ColorsList.add(sectionName, 'q').color property color backgroundColor: ColorsList.add(sectionName+'_bg', 'a').color property QtObject pauseAction: QtObject { property int iconSize: 25 property string name : 'pause' property string icon : 'chat_audio_pause_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'q').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'q').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'q').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject playAction: QtObject { property int iconSize: 25 property string name : 'play' property string icon : 'chat_audio_play_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'q').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'q').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'q').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject progressionWave: QtObject{ property int iconSize: 60 property int iconHeight: 60 property int iconWidth: 60 property string name : 'progression_soundwave' property string icon : 'chat_audio_soundwave_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'w_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'w_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'w_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'w_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'w_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'w_p_b_fg').color property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'l_n_b_bg').color property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'l_h_b_bg').color property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'l_p_b_bg').color property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'l_n_b_fg').color property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'l_h_b_fg').color property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'l_p_b_fg').color } property int padding: 8 } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Chat/ChatAudioPreviewStyle.qml000066400000000000000000000202331434616504300323430ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'ChatAudioPreview' property color color: ColorsList.add(sectionName, 'q').color property int height: 70 property QtObject header: QtObject{ property color color: ColorsList.add(sectionName+'_header', 'h').color property int pointSizeOffset: -3 property QtObject replyIcon: QtObject{ property string icon : 'menu_reply_custom' property int iconSize: 22 } } property color backgroundColor: ColorsList.add(sectionName+'_bg', 'aa').color property QtObject audioArea: QtObject{ property color outgoingMarkColor: ColorsList.add(sectionName+'_reply_outgoing_mark', 'm').color property color incomingMarkColor: ColorsList.add(sectionName+'_reply_incoming_mark', 'r').color property color backgroundColor: ColorsList.add(sectionName+'_reply_bg', 'q').color property color foregroundColor: ColorsList.add(sectionName+'_reply_fg', 'h').color property int usernamePointSizeOffset: -2 property int pointSizeOffset: -2 } property QtObject deleteAction: QtObject { property int iconSize: 40 property string name : 'delete' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject stopAction: QtObject { property int iconSize: 30 property string name : 'stop' property string icon : 'chat_audio_preview_stop_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject pauseAction: QtObject { property int iconSize: 30 property string name : 'pause' property string icon : 'chat_audio_preview_pause_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject playAction: QtObject { property int iconSize: 30 property string name : 'play' property string icon : 'chat_audio_preview_play_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject progressionWave: QtObject{ property int iconSize: 60 property int iconHeight: 60 property int iconWidth: 60 property string name : 'progression_soundwave' property string icon : 'chat_audio_soundwave_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'l_h_b_bg').color property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'l_n_b_bg').color property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'l_p_b_bg').color property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'l_h_b_fg').color property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'l_n_b_fg').color property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'l_p_b_fg').color } property QtObject recordingProgressionWave: QtObject{ property int iconSize: 60 property int iconHeight: 60 property int iconWidth: 60 property string name : 'recording_progression_soundwave' property string icon : 'chat_audio_soundwave_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'wr_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'wr_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'wr_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'wr_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'wr_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'wr_p_b_fg').color // Old color: l_n_b_bg property color backgroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_n', icon, 'ai').color property color backgroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_h', icon, 'ai').color property color backgroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_bg_p', icon, 'ai').color property color foregroundHiddenPartNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_n', icon, 'ai').color property color foregroundHiddenPartHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_h', icon, 'ai').color property color foregroundHiddenPartPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_hidden_fg_p', icon, 'ai').color } property int padding: 8 } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Chat/ChatCalendarMessageStyle.qml000066400000000000000000000210271434616504300327600ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'ChatCalendarMessage' property int topMargin: 0 property int widthMargin: 5 property int minWidth: 300 property int actionButtonsSize: 36 property int avatarSize: 30 property int deleteButtonSize: 22 property int height: 50 property int leftMargin: 40 property int bottomMargin: 10 property int presenceLevelSize: 12 property int rightMargin: 25 property int spacing: 15 property int lineHeight: 20 //property color : ColorsList.add(sectionName+'_conference_bg_n', 'conference_entry_bg').color property QtObject backgroundColor: QtObject { property color normal: ColorsList.add(sectionName+'_conference_bg_n', 'conference_entry_bg').color property color hovered: ColorsList.add(sectionName+'_conference_bg_h', 'g10').color } property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_conference_border', 'f').color property int width: 1 } property QtObject indicator: QtObject { property color color: ColorsList.add(sectionName+'_conference_indicator', 'i').color property int width: 5 } property QtObject calendar: QtObject { property int spacing: 5 property int pointSize: Units.dp * 9 property string icon : 'calendar_custom' property int iconSize: 30 property color color: ColorsList.add(sectionName+'_schedule', 'j').color } property QtObject schedule: QtObject { property int spacing: 5 property int pointSize: Units.dp * 9 property string icon : 'schedule_custom' property int iconSize: 30 property color color: ColorsList.add(sectionName+'_schedule', 'j').color } property QtObject type: QtObject { property int spacing: 5 property int pointSize: Units.dp * 10 property color updatedColor: ColorsList.add(sectionName+'_updated_subject', 'updated_ics_fg').color property color cancelledColor: ColorsList.add(sectionName+'_cancelled_subject', 'cancelled_ics_fg').color property color color: ColorsList.add(sectionName+'_subject', 'ics_fg').color } property QtObject subject: QtObject { property int spacing: 5 property int pointSize: Units.dp * 11 property color color: ColorsList.add(sectionName+'_subject', 'j').color } property QtObject description: QtObject { property int spacing: 5 property int pointSize: Units.dp * 9 property color color: ColorsList.add(sectionName+'_description', 'j').color } property QtObject participants: QtObject { property int spacing: 5 property int pointSize: Units.dp * 9 property string icon : 'calendar_participants_custom' property int iconSize: 25 property color color: ColorsList.add(sectionName+'_participants', 'j').color } property QtObject gotoButton: QtObject{ property int iconSize: 20 property string name : 'goto' property string icon : 'transfer_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_c', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_c', icon, 's_p_b_fg').color } property QtObject infoButton: QtObject{ property int iconSize: 25 property string name : 'info' property string icon : 'menu_info_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 'me_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_c', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 'me_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_c', icon, 'me_p_b_fg').color } property QtObject organizer: QtObject { property color color: ColorsList.add(sectionName+'_conference_organizer', 'j').color property int pointSize: Units.dp * 9 property int width: 220 } property QtObject copyLinkButton: QtObject{ property int iconSize: 40 property string name : 'copy' property string icon : 'menu_copy_text_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 's_p_b_fg').color } property QtObject shareButton: QtObject{ property int iconSize: 40 property string name : 'share' property string icon : 'settings_network_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 'me_p_b_fg').color } property QtObject editButton: QtObject{ property int iconSize: 40 property string name : 'edit' property string icon : 'ics_edit_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 's_p_b_fg').color } property QtObject deleteButton: QtObject{ property int iconSize: 40 property string name : 'delete' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 's_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Chat/ChatFilePreviewStyle.qml000066400000000000000000000056411434616504300321670ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'ChatFilePreview' property int height: 160 property QtObject filePreview: QtObject{ id: filePreviewObject property int heightMargins: 60 property real format: 16/9 property string name: 'filePreview' property string icon: 'menu_reply_custom' property color backgroundColor: ColorsList.add(sectionName+'_'+name+'_bg', 'e').color property color headerTextColor: ColorsList.add(sectionName+'_'+name+'_header_fg', 'i').color property color iconColor: ColorsList.add(sectionName+'_'+name+'_header_fg', 'i').color property color textColor: ColorsList.add(sectionName+'_'+name+'_fg', 'd').color property int pointSize: Units.dp * 9 property int headerPointSize: Units.dp * 9 property QtObject removeButton: QtObject{ property int iconSize: 30 property string name : 'remove' property string icon : 'close_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_p', icon, 's_p_b_fg').color } property QtObject closeButton: QtObject{ property int iconSize: 30 property string name : 'close' property string icon : 'close_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_b_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+filePreviewObject.name+'_'+name+'_f_p', icon, 'l_p_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Chat/ChatForwardMessageStyle.qml000066400000000000000000000011101434616504300326420ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'ChatReplyMessage' property color color: ColorsList.add(sectionName, 'q').color property QtObject header: QtObject{ property color color: ColorsList.add(sectionName+'_header', 'h').color property int pointSizeOffset: -3 property QtObject forwardIcon: QtObject{ property string icon : 'menu_forward_custom' property int iconSize: 22 } } property int padding: 8 } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Chat/ChatReplyMessageStyle.qml000066400000000000000000000020521434616504300323370ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'ChatReplyMessage' property color color: ColorsList.add(sectionName, 'q').color property QtObject header: QtObject{ property color color: ColorsList.add(sectionName+'_header', 'h').color property int pointSizeOffset: -3 property QtObject replyIcon: QtObject{ property string icon : 'menu_reply_custom' property int iconSize: 22 } } property QtObject replyArea: QtObject{ property color outgoingMarkColor: ColorsList.add(sectionName+'_reply_outgoing_mark', 'm').color property color incomingMarkColor: ColorsList.add(sectionName+'_reply_incoming_mark', 'r').color property color backgroundColor: ColorsList.add(sectionName+'_reply_bg', 'q').color property color foregroundColor: ColorsList.add(sectionName+'_reply_fg', 'h').color property int usernamePointSizeOffset: -2 property int pointSizeOffset: -2 } property int padding: 8 } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Chat/ChatStyle.qml000066400000000000000000000274401434616504300300260ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'Chat' property color color: ColorsList.add(sectionName, 'q').color property string copyTextIcon : 'menu_copy_text_custom' property int rightButtonMargin: 15 property int rightButtonSize: 30 property int rightButtonLMargin: 10 property int separatorHeight: 2 property QtObject sectionHeading: QtObject { property int padding: 5 property int bottomMargin: 20 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_section_border', 'g10').color property int width: 1 } property QtObject text: QtObject { property int pointSize: Units.dp * 10 property color color: ColorsList.add(sectionName+'_section_text', 'ab').color } } property QtObject gotToBottom: QtObject{ property string name: 'goToBottom' property string icon: 'move_to_bottom_custom' property int iconSize: 30 property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 's_p_b_fg').color } property QtObject sendArea: QtObject { property int height: 80 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_send_border', 'f').color property int width: 1 } property QtObject backgroundBorder: QtObject { property color color: ColorsList.add(sectionName+'_send_background_border', 'ag').color property int width: 2 } } property QtObject composingText: QtObject { property color color: ColorsList.add(sectionName+'_composing_text', 'd').color property int height: 25 property int leftPadding: 20 property int pointSize: Units.dp * 9 } property QtObject replyPreview: QtObject { id: replyPreviewObject property string name: 'replyPreview' property string icon: 'menu_reply_custom' property color backgroundColor: ColorsList.add(sectionName+'_'+name+'_bg', 'e').color property color headerTextColor: ColorsList.add(sectionName+'_'+name+'_header_fg', 'i').color property color iconColor: ColorsList.add(sectionName+'_'+name+'_header_fg', 'i').color property color textColor: ColorsList.add(sectionName+'_'+name+'_fg', 'd').color property int pointSize: Units.dp * 9 property int headerPointSize: Units.dp * 9 property QtObject closeButton: QtObject{ property int iconSize: rightButtonSize property string name : 'close' property string icon : 'close_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+replyPreviewObject.name+'_'+name+'_b_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+replyPreviewObject.name+'_'+name+'_b_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+replyPreviewObject.name+'_'+name+'_b_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+replyPreviewObject.name+'_'+name+'_f_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+replyPreviewObject.name+'_'+name+'_f_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+replyPreviewObject.name+'_'+name+'_f_p', icon, 'l_p_b_fg').color } } property QtObject ephemeralTimer: QtObject{ property string icon: 'timer_custom' property int iconSize : 25 property color timerColor: ColorsList.addImageColor(sectionName+'_ephemeralTimer', icon, 'ad').color } property QtObject entry: QtObject { property int bottomMargin: 10 property int deleteIconSize: 22 property int leftMargin: 18 property int rightMargin: 18 property int lineHeight: 30 property int metaWidth: 40 property QtObject separator: QtObject { property color color: ColorsList.add(sectionName+'_separator_border', 'g10').color property int width: 2 } property QtObject menu: QtObject { property int iconSize: 22 property string name : 'menu' property string icon : 'chat_menu_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, '','','#DEDEDE').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, '','','#DEDEDE').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, '','','#A1A1A1').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, '', '', '#595759').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, '', '', '#595759').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, '', '', '#595759').color } property QtObject deleteAction: QtObject { property int iconSize: 22 property string name : 'delete' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, '','','#DEDEDE').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, '','','#DEDEDE').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, '','','#A1A1A1').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, '', '', '#595759').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, '', '', '#595759').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, '', '', '#595759').color } property QtObject event: QtObject { property int iconSize: 30 property QtObject notice: QtObject{ property color color: ColorsList.add(sectionName+'_entry_notice', 'ab').color property color errorColor: ColorsList.add(sectionName+'_entry_notice_error', 'error').color property color importantColor: ColorsList.add(sectionName+'_entry_notice_important', 'ae').color property int pointSize: Units.dp * 10 } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_entry_text', 'ac').color property int pointSize: Units.dp * 10 } property QtObject declinedIncomingCall: QtObject{ property string icon: 'declined_incoming_call_custom' property color color: ColorsList.addImageColor(sectionName+'_declinedIncomingCall', icon, 'event_bad').color } property QtObject declinedOutgoingCall: QtObject{ property string icon: 'declined_outgoing_call_custom' property color color: ColorsList.addImageColor(sectionName+'_declinedOutgoingCall', icon, 'event_bad').color } property QtObject endedCall: QtObject{ property string icon: 'ended_call_custom' property color color: ColorsList.addImageColor(sectionName+'_endedCall', icon, 'event_neutral').color } property QtObject incomingCall: QtObject{ property string icon: 'incoming_call_custom' property color color: ColorsList.addImageColor(sectionName+'_incomingCall', icon, 'event_in').color } property QtObject outgoingCall: QtObject{ property string icon: 'outgoing_call_custom' property color color: ColorsList.addImageColor(sectionName+'_outgoingCall', icon, 'event_out').color } property QtObject missedIncomingCall: QtObject{ property string icon: 'missed_incoming_call_custom' property color color: ColorsList.addImageColor(sectionName+'_missedIncominCall', icon, 'event_bad').color } property QtObject missedOutgoingCall: QtObject{ property string icon: 'missed_outgoing_call_custom' property color color: ColorsList.addImageColor(sectionName+'_missedOutgoingCall', icon, 'event_bad').color } property QtObject unknownCallEvent: QtObject{ property string icon: 'unknown_call_event' property color color: ColorsList.addImageColor(sectionName+'_unknownCallEvent', icon, 'event_bad').color } } property QtObject message: QtObject { property int padding: 8 property int radius: 4 property QtObject extraContent: QtObject { property int leftMargin: 10 property int spacing: 5 property int rightMargin: 5 } property QtObject file: QtObject { property int height: 80 property int heightbetter: 200 property int iconSize: 18 property int margins: 8 property int spacing: 8 property int width: 100 property QtObject name: QtObject{ property int pointSize: Units.dp * 7 } property QtObject download: QtObject{ property string icon: 'download_custom' property int height: 20 property int pointSize: Units.dp * 8 property int iconSize: 30 property color outgoingColor: ColorsList.addImageColor(sectionName+'_download_out', icon, 'g').color property color incomingColor: ColorsList.addImageColor(sectionName+'_download_in', icon, 'q').color } property QtObject animation: QtObject { property int duration: 300 property real to: 1.7 property real thumbnailTo: 2 } property QtObject extension: QtObject { property string icon: 'file_extension_custom' property string unknownIcon: 'file_unknown_custom' property int iconSize: 60 property int radius: 5 property QtObject background: QtObject { property color color: ColorsList.add(sectionName+'_file_extension_bg', 'q').color } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_file_extension_text', 'd').color property int pointSize: Units.dp * 9 } } property QtObject status: QtObject { property int spacing: 4 property QtObject bar: QtObject { property int height: 6 property int radius: 3 property QtObject background: QtObject { property color color: ColorsList.add(sectionName+'_file_statusbar_bg', 'f').color } property QtObject contentItem: QtObject { property color color: ColorsList.add(sectionName+'_file_statusbar_content', 'p').color } } } } property QtObject images: QtObject { property int height: 240 property int width: 240 } property QtObject incoming: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_incoming_bg', 'incoming_bg').color property int avatarSize: 20 property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_incoming_text', 'd').color property int pointSize: Units.dp * 10 } } property QtObject outgoing: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_outgoing_bg', 'outgoing_bg').color property int areaSize: 12 property int busyIndicatorSize: 12 property int sendIconSize: 60 property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_outgoing_text', 'd').color property int pointSize: Units.dp * 10 } } } property QtObject time: QtObject { property color color: ColorsList.add(sectionName+'_time', 'd').color property int pointSize: Units.dp * 10 property int width: 44 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Codecs/000077500000000000000000000000001434616504300257255ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Codecs/CodecsViewerStyle.qml000066400000000000000000000022751434616504300320510ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CodecsViewer' property int leftMargin: 10 property QtObject attribute: QtObject { property int height: 40 property QtObject background: QtObject { property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_n', 'a').color property color hovered: ColorsList.add(sectionName+'_h', 'o').color } } property QtObject dropArea: QtObject { property int margins: 5 } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_text', 'j').color property int pointSize: Units.dp * 10 } } property QtObject column: QtObject { property int bitrateWidth: 120 property int clockRateWidth: 100 property int encoderDescriptionWidth: 280 property int mimeWidth: 100 property int recvFmtpWidth: 200 property int spacing: 10 } property QtObject legend: QtObject { property color color: ColorsList.add(sectionName+'_legend', 'j').color property int pointSize: Units.dp * 10 property int height: 50 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Contact/000077500000000000000000000000001434616504300261205ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Contact/AvatarStyle.qml000066400000000000000000000012771434616504300311010ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Avatar' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'd').color property color backgroundDarkModeColor: ColorsList.add(sectionName+'_dark_bg', 'q').color property string personImage : 'contact_custom' property QtObject initials: QtObject { property color color: ColorsList.add(sectionName+'_initials', 'q').color property color darkModeColor: ColorsList.add(sectionName+'_dark_initials', 'd').color property int pointSize: Units.dp * 10 property int ratio: 30 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Contact/ContactDescriptionStyle.qml000066400000000000000000000014151434616504300334540ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ContactDescription' property QtObject subtitle: QtObject { property color color: ColorsList.add(sectionName+'_subtitle', 'n').color property int pointSize: Units.dp * 10 property int weight: Font.Normal } property QtObject title: QtObject { property color color: ColorsList.add(sectionName+'_title', 'j').color property int pointSize: Units.dp * 11 property int weight: Font.Bold property QtObject status : QtObject{ property color color : ColorsList.add(sectionName+'_status', 'g').color property int pointSize : Units.dp * 9 } } } ContactMessageCounterStyle.qml000066400000000000000000000003121434616504300340310ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Contactpragma Singleton import QtQml 2.2 // ============================================================================= QtObject { property int horizontalMargins: 0 property int verticalMargins: 10 } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Contact/ContactStyle.qml000066400000000000000000000013461434616504300312530ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Contact' property int contentHeight: 32 property int height: 50 property int leftMargin: 14 property int rightMargin: 14 property int spacing: 14 property QtObject groupChat: QtObject { property string icon: 'chat_room_custom' property color color: ColorsList.addImageColor(sectionName+'_groupChat', icon, 'g').color property color avatarColor: ColorsList.addImageColor(sectionName+'_groupChat_onAvatar', icon, 'q').color property color avatarDarkModeColor: ColorsList.addImageColor(sectionName+'_groupChat_dark_onAvatar', icon, 'd').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Dialog/000077500000000000000000000000001434616504300257245ustar00rootroot00000000000000MultimediaParametersDialogStyle.qml000066400000000000000000000022631434616504300346420ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Dialogpragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'MultimediaParamsDialog' property int height: 350 property int width: 450 property QtObject column: QtObject { property int spacing: 15 property QtObject entry: QtObject { property int iconSize: 24 property int spacing: 10 property int spacing2: 5 property QtObject speaker: QtObject { property int iconSize: 30 property string icon : 'speaker_on_custom' property string name : 'speaker' property color color : ColorsList.addImageColor(sectionName+'_'+name, icon, 'g').color } property QtObject micro: QtObject { property int iconSize: 30 property string icon : 'micro_on_custom' property string name : 'micro' property color color : ColorsList.addImageColor(sectionName+'_'+name, icon, 'g').color } property QtObject camera: QtObject { property int iconSize: 30 property string icon : 'camera_on_custom' property string name : 'camera' property color color : ColorsList.addImageColor(sectionName+'_'+name, icon, 'g').color } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Dialog/OnlineInstallerDialogStyle.qml000066400000000000000000000017521434616504300337070ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'OnlineInstallerDialog' property int height: 200 property int width: 400 property QtObject column: QtObject { property int spacing: 6 property QtObject bar: QtObject { property int height: 20 property int radius: 6 property QtObject background: QtObject { property color color: ColorsList.add(sectionName+'_bar_bg', 'f').color } property QtObject contentItem: QtObject { property QtObject color: QtObject { property color failed: ColorsList.add(sectionName+'_bar_content_failed', 'error').color property color normal: ColorsList.add(sectionName+'_bar_content_n', 'p').color } } } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_text', 'd').color property int pointSize: Units.dp * 11 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Dialog/SipAddressDialogStyle.qml000066400000000000000000000026471434616504300326520ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 import Units 1.0 // ============================================================================= QtObject { property string sectionName : 'SipAddressDialog' property int height: 420 property int spacing: 10 property int width: 450 property QtObject select: QtObject { property int iconSize: 36 property string icon : 'transfer_custom' property string name : 'select' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject searchField: QtObject { property color color: ColorsList.add(sectionName+'_searchField', 'c').color } property QtObject list: QtObject { property color color: ColorsList.add(sectionName+'_list_title', 'g').color property int pointSize: Units.dp * 11 } } ZrtpTokenAuthenticationDialogStyle.qml000066400000000000000000000015111434616504300353570ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Dialogpragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'ZrtpTokenAuthenticationDialog' property int height: 50 property string pqIcon: 'secure_pq_zrtp' property string secureIcon: 'secure_on' property string icon: 'secure_level_2' property int iconSize: 60 property QtObject buttons: QtObject { property int spacing: 10 } property QtObject text: QtObject { property color colorA: ColorsList.add(sectionName+'_zrtp_text_a', 'j').color property color colorB: ColorsList.add(sectionName+'_zrtp_text_b', 's').color property int pointSize: Units.dp * 10 property int titlePointSize: Units.dp * 12 property int sasPointSize: Units.dp * 13 property int wordsSpacing: 5 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/History/000077500000000000000000000000001434616504300261665ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/History/HistoryStyle.qml000066400000000000000000000100651434616504300313650ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'History' property color color: ColorsList.add(sectionName, 'q').color property QtObject sectionHeading: QtObject { property int padding: 5 property int bottomMargin: 20 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_section_border', 'g10').color property int width: 1 } property QtObject text: QtObject { property int pointSize: Units.dp * 10 property color color: ColorsList.add(sectionName+'_section_text', 'g').color } } property QtObject entry: QtObject { property int bottomMargin: 10 property int leftMargin: 18 property int lineHeight: 30 property int metaWidth: 40 property QtObject deleteAction: QtObject { property int iconSize: 30 property string name : 'delete' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } property QtObject event: QtObject { property int iconSize: 30 property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_event_text', 'd').color property int pointSize: Units.dp * 10 } property QtObject declinedIncomingCall: QtObject{ property string icon: 'declined_incoming_call_custom' property color color: ColorsList.addImageColor(sectionName+'_declinedIncomingCall', icon, 'event_bad').color } property QtObject declinedOutgoingCall: QtObject{ property string icon: 'declined_outgoing_call_custom' property color color: ColorsList.addImageColor(sectionName+'_declinedOutgoingCall', icon, 'event_bad').color } property QtObject endedCall: QtObject{ property string icon: 'ended_call_custom' property color color: ColorsList.addImageColor(sectionName+'_endedCall', icon, 'event_neutral').color } property QtObject incomingCall: QtObject{ property string icon: 'incoming_call_custom' property color color: ColorsList.addImageColor(sectionName+'_incomingCall', icon, 'event_in').color } property QtObject outgoingCall: QtObject{ property string icon: 'outgoing_call_custom' property color color: ColorsList.addImageColor(sectionName+'_outgoingCall', icon, 'event_out').color } property QtObject missedIncomingCall: QtObject{ property string icon: 'missed_incoming_call_custom' property color color: ColorsList.addImageColor(sectionName+'_missedIncominCall', icon, 'event_bad').color } property QtObject missedOutgoingCall: QtObject{ property string icon: 'missed_outgoing_call_custom' property color color: ColorsList.addImageColor(sectionName+'_missedOutgoingCall', icon, 'event_bad').color } property QtObject unknownCallEvent: QtObject{ property string icon: 'unknown_call_event' property color color: ColorsList.addImageColor(sectionName+'_unknownCallEvent', icon, 'event_bad').color } } property QtObject message: QtObject { property int padding: 8 property int radius: 4 property QtObject extraContent: QtObject { property int leftMargin: 10 property int spacing: 5 property int rightMargin: 5 } } property QtObject time: QtObject { property color color: ColorsList.add(sectionName+'_time', 'd').color property int pointSize: Units.dp * 10 property int width: 44 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Menus/000077500000000000000000000000001434616504300256145ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Menus/IncallMenuStyle.qml000066400000000000000000000107571434616504300314110ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'IncallMenu' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'k').color property int radius: 8 property QtObject header: QtObject{ property string name: 'header' property int height: 60 property color color: ColorsList.add(sectionName+'_'+name+'_fg', 'j').color property int weight: Font.Bold property int pointSize: Units.dp * 14 } property QtObject list : QtObject{ property string name: 'list' property int height: 60 property color color: ColorsList.add(sectionName+'_'+name+'_fg', 'j').color property int weight: Font.Normal property int selectedWeight: Font.Bold property int pointSize: Units.dp * 12 property QtObject border: QtObject{ property color color: ColorsList.add(sectionName+'_list_border', 'f').color property int width: 2 } } property QtObject modeIcons: QtObject{ property string gridIcon: 'conference_layout_grid_custom' property string activeSpeakerIcon: 'conference_layout_active_speaker_custom' property string audioOnlyIcon: 'conference_audio_only_custom' property int width: 40 property int height: 40 } property QtObject settingsIcons: QtObject{ property string gridIcon: 'conference_layout_grid_custom' property string activeSpeakerIcon: 'conference_layout_active_speaker_custom' property string audioOnlyIcon: 'conference_audio_only_custom' property string mediaIcon: 'micro_on_custom' property string participantsIcon: 'participants_custom' property int width: 40 property int height: 40 } //------------------------------------------------------------------------------ property QtObject buttons: QtObject{ property QtObject close: QtObject { property int iconSize: 40 property string icon : 'close_custom' property string name : 'close' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject back: QtObject { property int iconSize: 40 property string icon : 'back_custom' property string name : 'back' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject next: QtObject { property int iconSize: 40 property string icon : 'panel_arrow_custom' property string name : 'next' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } } //------------------------------------------------------------------------------ } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Menus/SipAddressesMenuStyle.qml000066400000000000000000000015421434616504300325700ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'SipAddressesMenu' property int spacing: 1 property int maxHeight: 164 property QtObject entry: QtObject { property int leftMargin: 18 property int rightMargin: 8 property int height: 40 property int width: 300 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_entry_h', 'j').color property color normal: ColorsList.add(sectionName+'_entry_n', 'g').color property color pressed: ColorsList.add(sectionName+'_entry_p', 'i').color } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_entry_text', 'q').color property int pointSize: Units.dp * 10 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Misc/000077500000000000000000000000001434616504300254205ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Misc/MessageCounterStyle.qml000066400000000000000000000007211434616504300321000ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'MessageCounter' property QtObject iconSize: QtObject { property int amount: 12 property int message: 18 } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_text', 'q').color property int pointSize: Units.dp * 6 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Notifications/000077500000000000000000000000001434616504300273365ustar00rootroot00000000000000NotificationBasicStyle.qml000066400000000000000000000007261434616504300344100ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Notificationspragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'NotificationBasic' property int leftMargin: 30 property int rightMargin: 15 property int overrodeHeight: 55 property QtObject message: QtObject { property color color: ColorsList.add(sectionName+'_message', 'h').color property int pointSize: Units.dp * 10 } } NotificationReceivedCallStyle.qml000066400000000000000000000060171434616504300357100ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Notificationspragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'IncomingCallNotification' property int spacing: 0 property int bottomMargin: 15 property int leftMargin: 15 property int rightMargin: 15 property QtObject actionArea: QtObject { property int iconSize: 40 property int rightButtonsGroupMargin: 15 } property QtObject hangup: QtObject { property int iconSize: 40 property string icon : 'hangup_custom' property string name : 'hangup' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'r_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'r_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'r_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'r_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'r_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'r_p_b_fg').color } property QtObject acceptVideoCall: QtObject { property int iconSize: 40 property string icon : 'video_call_accept_custom' property string name : 'videoCallAccept' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color } property QtObject acceptCall: QtObject { property int iconSize: 40 property string icon : 'call_accept_custom' property string name : 'callAccept' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color } } NotificationReceivedFileMessageStyle.qml000066400000000000000000000013661434616504300372230ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Notificationspragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'NotificationReceived' property color color: ColorsList.add(sectionName+'_file', 'k').color property int leftMargin: 25 property int overrodeHeight: 55 property int rightMargin: 15 property int spacing: 10 property QtObject fileName: QtObject { property color color: ColorsList.add(sectionName+'_file_name', 'h').color property int pointSize: Units.dp * 10 } property QtObject fileSize: QtObject { property color color: ColorsList.add(sectionName+'_file_size', 'h').color property int pointSize: Units.dp * 9 property int width: 100 } } NotificationReceivedMessageStyle.qml000066400000000000000000000014561434616504300364230ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Notificationspragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'NotificationReceivedMessage' property color color: ColorsList.add(sectionName+'_message', 'k').color property int bottomMargin: 15 property int leftMargin: 15 property int overrodeHeight: 55 property int rightMargin: 15 property int spacing: 0 property QtObject messageContainer: QtObject { property color color: ColorsList.add(sectionName+'_message_container', 'o').color property int radius: 6 property int margins: 10 property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_message_container_text', 'l').color property int pointSize: Units.dp * 9 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Notifications/NotificationStyle.qml000066400000000000000000000007361434616504300335260ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Notification' property color color: ColorsList.add(sectionName, 'k').color property int height: 120 property int iconSize: 40 property int width: 300 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_border', 'n').color property int width: 1 } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Sticker/000077500000000000000000000000001434616504300261315ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Sticker/AvatarStickerStyle.qml000066400000000000000000000007341434616504300324340ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'AvatarSticker' property color stickerBackgroundColor: ColorsList.add(sectionName+'_out_bg', 'avatar_initials_sticker_bg').color property color inBackgroundColor: ColorsList.add(sectionName+'_in_bg', 'avatar_initials_bg').color property int radius : 10 } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Sticker/CameraStickerStyle.qml000066400000000000000000000005701434616504300324040ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CameraSticker' property color cameraBackgroundColor: ColorsList.add(sectionName+'_camera_bg', 'fullscreen_conference_bg').color property int radius : 10 } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Sticker/DecorationStickerStyle.qml000066400000000000000000000050071434616504300333030ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'DecorationSticker' property int radius : 10 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_border', 'b').color property int width: 2 } property QtObject pauseView: QtObject{ property color backgroundColor : ColorsList.add(sectionName+'_pauseView_bg_n', 'l').color property QtObject button: QtObject { property int iconSize: 80 property string icon : 'pause_custom' property string name : 'pause' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg', icon, 's_n_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg', icon, 's_n_b_fg').color } } property QtObject contactDescription: QtObject { property color color: ColorsList.add(sectionName+'_username', 'q').color property int pointSize: Units.dp * 12 property int weight: Font.Bold } property QtObject closePreview: QtObject { property int iconSize: 40 property string icon : 'close_custom' property string name : 'close_preview' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_inv_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_inv_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_inv_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_inv_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_inv_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_inv_fg').color } property QtObject isMuted: QtObject{ property color backgroundColor : ColorsList.add(sectionName+'_isMuted_bg', 'j').color property QtObject button: QtObject { property int iconSize: 30 property string icon : 'micro_off_custom' property string name : 'isMuted' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg', icon, 's_d_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Sticker/StickerStyle.qml000066400000000000000000000025041434616504300312720ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Sticker' property QtObject custom: QtObject { property int iconSize: 40 property string icon : 'menu_vdots_custom' property string name : 'custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 's_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/TelKeypad/000077500000000000000000000000001434616504300264075ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/TelKeypad/TelKeypadStyle.qml000066400000000000000000000043331434616504300320300ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'TelKeypad' property int columnSpacing: 12 property int height: 240 property int rowSpacing: 12 property int width: 240 property color color: ColorsList.add(sectionName+'', 'telkeypad_bg').color property color selectedColor : ColorsList.add(sectionName+'_c', 'm').color property int selectedBorderWidth: 2 property real radius : 20 property QtObject voicemail: QtObject{ property string icon: 'tel_keypad_voicemail_custom' property int iconSize: 20 } property QtObject button: QtObject { property QtObject colorSet: QtObject{ property int iconSize: 0 property string name : 'telButton' property string icon : '' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'telkeypad_fg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'telkeypad_h').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'i').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 'i').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'transparent').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'transparent').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'transparent').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 'transparent').color } property QtObject line: QtObject { property color color: ColorsList.add(sectionName+'_b_line', 'l50').color property int bottomMargin: 4 property int height: 2 property int leftMargin: 8 property int rightMargin: 8 property int topMargin: 0 } property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_b_text', 'telkeypad_bg').color property int pointSize: Units.dp * 14 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Timeline/000077500000000000000000000000001434616504300262735ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml000066400000000000000000000102661434616504300316020ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Timeline' property color color: ColorsList.add(sectionName+'_bg', 'q').color property QtObject ephemeralTimer: QtObject{ property string icon: 'timer_custom' property int iconSize : 30 property color timerColor: ColorsList.addImageColor(sectionName+'_ephemeralTimer', icon, 'ad').color property color selectedTimerColor: ColorsList.addImageColor(sectionName+'_ephemeralTimer_c', icon, 'q').color } property QtObject contact: QtObject { property int height: 60 property QtObject backgroundColor: QtObject { property color a: ColorsList.add(sectionName+'_contact_bg_a', 'timeline_bg_1').color property color b: ColorsList.add(sectionName+'_contact_bg_b', 'timeline_bg_2').color property color selected: ColorsList.add(sectionName+'_contact_bg_c', 'i').color } property QtObject subtitle: QtObject { property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_contact_subtitle_n', 'n').color property color selected: ColorsList.add(sectionName+'_contact_subtitle_c', 'q').color } } property QtObject title: QtObject { property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_contact_title_n', 'j').color property color selected: ColorsList.add(sectionName+'_contact_title_c', 'q').color } } } property QtObject legend: QtObject { property QtObject backgroundColor: QtObject { property color normal: ColorsList.add(sectionName+'_legend_bg_n', 'f').color property color hovered: ColorsList.add(sectionName+'_legend_bg_h', 'c').color } property color color: ColorsList.add(sectionName+'_legend', 'd').color property int pointSize: Units.dp * 10 property int height: 30 property int iconSize: 28 property int leftMargin: 17 property int rightMargin: 17 property int lastRightMargin: 5 property int spacing: 1 } property QtObject filterField: QtObject { property color borderColor: ColorsList.add(sectionName+'_filter_border', 'border').color } property QtObject searchField: QtObject { property color color: ColorsList.add(sectionName+'_searchField', 'c').color property color borderColor: ColorsList.add(sectionName+'_searchField_border', 'border').color property int pointSize: Units.dp * 9 } property QtObject selectedDeleteAction: QtObject { property int iconSize: 40 property string name : 'delete_on_selected' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_inv_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_inv_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_inv_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_inv_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_inv_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_inv_fg').color } property QtObject deleteAction: QtObject { property int iconSize: 40 property string name : 'delete' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/View/000077500000000000000000000000001434616504300254375ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/View/ParticipantsListViewStyle.qml000066400000000000000000000100161434616504300333210ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ParticipantsListView' property int height: 500 property int width: 450 property QtObject mainLayout: QtObject { property int topMargin: 15 property int leftMargin: 25 property int rightMargin: 25 property int spacing: 7 } property QtObject searchBar : QtObject{ property int topMargin : 10 } property QtObject results : QtObject{ property int topMargin : 10 property color color : ColorsList.add(sectionName+'_results', 'g').color property QtObject title : QtObject{ property int topMargin: 10 property int leftMargin: 20 property color color: ColorsList.add(sectionName+'_results_title', 'j').color property int pointSize : Units.dp * 11 property int weight : Font.DemiBold } property QtObject header: QtObject{ property int rightMargin: 55 property color color: Colors.t.color property int weight : Font.Light property int pointSize : Units.dp * 10 } } property QtObject leaveButton : QtObject { property QtObject backgroundColor: QtObject { property color disabled: ColorsList.add(sectionName+'_leave_bg_d', 'o').color property color hovered: ColorsList.add(sectionName+'_leave_bg_h', 'j').color property color normal: ColorsList.add(sectionName+'_leave_bg_n', 'k').color property color pressed: ColorsList.add(sectionName+'_leave_bg_p', 'i').color } property QtObject textColor: QtObject { property color disabled: ColorsList.add(sectionName+'_leave_text_d', 'q').color property color hovered: ColorsList.add(sectionName+'_leave_text_h', 'q').color property color normal: ColorsList.add(sectionName+'_leave_text_n', 'i').color property color pressed: ColorsList.add(sectionName+'_leave_text_p', 'q').color } property QtObject borderColor : QtObject{ property color disabled: ColorsList.add(sectionName+'_leave_border_d', 'q').color property color hovered: ColorsList.add(sectionName+'_leave_border_h', 'q').color property color normal: ColorsList.add(sectionName+'_leave_border_n', 'i').color property color pressed: ColorsList.add(sectionName+'_leave_border_p', 'q').color } } property QtObject addParticipant: QtObject { property int iconSize: 30 property string name : 'addParticipant' property string icon : 'add_participant_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } property QtObject removeParticipant: QtObject { property int iconSize: 30 property string name : 'removeParticipant' property string icon : 'remove_participant_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/View/ParticipantsViewStyle.qml000066400000000000000000000006531434616504300324730ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ParticipantsView' property QtObject entry: QtObject { property QtObject status: QtObject { property color color : ColorsList.add(sectionName+'_entry_status', 'g').color property int pointSize : Units.dp * 8 } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/View/SipAddressesViewStyle.qml000066400000000000000000000117751434616504300324320ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'SipAddressesView' property QtObject entry: QtObject { property int height: 50 property int iconSize: 36 property int rightMargin: 10 property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_entry_h', 'o').color property color normal: ColorsList.add(sectionName+'_entry_n', 'q').color } property QtObject indicator: QtObject { property color color: ColorsList.add(sectionName+'_entry_indicator', 'i').color property int width: 5 } property QtObject separator: QtObject { property color color: ColorsList.add(sectionName+'_entry_separator', 'c').color property int height: 1 } } property QtObject header: QtObject { property int iconSize: 40 property int leftMargin: 20 property int rightMargin: 10 property QtObject button: QtObject { property int height: 40 property color color: ColorsList.add(sectionName+'_header_button', 'q').color } property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_header_n', 'j').color property color pressed: ColorsList.add(sectionName+'_header_p', 'i').color } property QtObject text: QtObject { property int pointSize: Units.dp * 9 property QtObject color: QtObject { property color normal: ColorsList.add(sectionName+'_header_text_n', 'q').color property color pressed: ColorsList.add(sectionName+'_header_text_p', 'q').color } } } property QtObject videoCall: QtObject { property int iconSize: 36 property string name : 'videoCall' property string icon : 'video_call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject call: QtObject { property int iconSize: 36 property string name : 'call' property string icon : 'call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject chat: QtObject { property int iconSize: 36 property string name : 'chat' property string icon : 'chat_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject history: QtObject { property int iconSize: 36 property string icon : 'history_custom' property string name : 'history' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Styles/qmldir000066400000000000000000000071261434616504300257460ustar00rootroot00000000000000# See: https://wiki.qt.io/Qml_Styling module Linphone.Styles # Components styles ------------------------------------------------------------ singleton AccountStatusStyle 1.0 Account/AccountStatusStyle.qml singleton CardBlockStyle 1.0 Blocks/CardBlockStyle.qml singleton RequestBlockStyle 1.0 Blocks/RequestBlockStyle.qml singleton CameraViewStyle 1.0 Camera/CameraViewStyle.qml singleton ChatStyle 1.0 Chat/ChatStyle.qml singleton ChatAudioMessageStyle 1.0 Chat/ChatAudioMessageStyle.qml singleton ChatAudioPreviewStyle 1.0 Chat/ChatAudioPreviewStyle.qml singleton ChatFilePreviewStyle 1.0 Chat/ChatFilePreviewStyle.qml singleton ChatCalendarMessageStyle 1.0 Chat/ChatCalendarMessageStyle.qml singleton ChatForwardMessageStyle 1.0 Chat/ChatForwardMessageStyle.qml singleton ChatReplyMessageStyle 1.0 Chat/ChatReplyMessageStyle.qml singleton CallControlsStyle 1.0 Calls/CallControlsStyle.qml singleton CallsStyle 1.0 Calls/CallsStyle.qml singleton CallStatisticsStyle 1.0 Calls/CallStatisticsStyle.qml singleton ConferenceControlsStyle 1.0 Calls/ConferenceControlsStyle.qml singleton CodecsViewerStyle 1.0 Codecs/CodecsViewerStyle.qml singleton AvatarStyle 1.0 Contact/AvatarStyle.qml singleton ContactDescriptionStyle 1.0 Contact/ContactDescriptionStyle.qml singleton ContactMessageCounterStyle 1.0 Contact/ContactMessageCounterStyle.qml singleton ContactStyle 1.0 Contact/ContactStyle.qml singleton MultimediaParametersDialogStyle 1.0 Dialog/MultimediaParametersDialogStyle.qml singleton OnlineInstallerDialogStyle 1.0 Dialog/OnlineInstallerDialogStyle.qml singleton SipAddressDialogStyle 1.0 Dialog/SipAddressDialogStyle.qml singleton ZrtpTokenAuthenticationDialogStyle 1.0 Dialog/ZrtpTokenAuthenticationDialogStyle.qml singleton HistoryStyle 1.0 History/HistoryStyle.qml singleton SipAddressesMenuStyle 1.0 Menus/SipAddressesMenuStyle.qml singleton IncallMenuStyle 1.0 Menus/IncallMenuStyle.qml singleton MessageCounterStyle 1.0 Misc/MessageCounterStyle.qml singleton NotificationBasicStyle 1.0 Notifications/NotificationBasicStyle.qml singleton NotificationReceivedCallStyle 1.0 Notifications/NotificationReceivedCallStyle.qml singleton NotificationReceivedFileMessageStyle 1.0 Notifications/NotificationReceivedFileMessageStyle.qml singleton NotificationReceivedMessageStyle 1.0 Notifications/NotificationReceivedMessageStyle.qml singleton NotificationStyle 1.0 Notifications/NotificationStyle.qml singleton AvatarStickerStyle 1.0 Sticker/AvatarStickerStyle.qml singleton CameraStickerStyle 1.0 Sticker/CameraStickerStyle.qml singleton DecorationStickerStyle 1.0 Sticker/DecorationStickerStyle.qml singleton StickerStyle 1.0 Sticker/StickerStyle.qml singleton TelKeypadStyle 1.0 TelKeypad/TelKeypadStyle.qml singleton TimelineStyle 1.0 Timeline/TimelineStyle.qml singleton ParticipantsListViewStyle 1.0 View/ParticipantsListViewStyle.qml singleton ParticipantsViewStyle 1.0 View/ParticipantsViewStyle.qml singleton SipAddressesViewStyle 1.0 View/SipAddressesViewStyle.qml linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/TelKeypad/000077500000000000000000000000001434616504300251245ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/TelKeypad/TelKeypad.js000066400000000000000000000036361434616504300273540ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `TelKeypad.qml` Logic. // ============================================================================= .import Linphone.Styles 1.0 as LinphoneStyles .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function mapKeyToButtonIndex (key) { // Digits. if (key === 48) { return 13 } if (key >= 49 && key <= 57) { return (key - 49) + parseInt((key - 49) / 3) } // A, B, C and D. if (key >= 65 && key <= 68) { return (key - 65) * 4 + 3 } // * if (key === 42) { return 12 } // # if (key === 35) { return 14 } } function sendDtmf (index) { var children = grid.children[index] children.color = LinphoneStyles.TelKeypadStyle.button.color.pressed children.clicked() var timeout = children._timeout if (timeout) { Utils.clearTimeout(timeout) } children._timeout = Utils.setTimeout(dragBox, 100, (function (index) { grid.children[index].color = LinphoneStyles.TelKeypadStyle.button.color.normal }).bind(dragBox, index)) } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/TelKeypad/TelKeypad.qml000066400000000000000000000057301434616504300275260ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone.Styles 1.0 import 'TelKeypad.js' as Logic // ============================================================================= Rectangle { id: telKeypad property bool showHistory: false property var container: parent property var call signal sendDtmf(var dtmf) signal keyPressed(var event) color: TelKeypadStyle.color // useless as it is overridden by buttons color, but keep it if buttons are transparent onActiveFocusChanged: {if(activeFocus) selectedArea.border.width=TelKeypadStyle.selectedBorderWidth; else selectedArea.border.width=0} Keys.onPressed: keyPressed(event) layer { effect: PopupShadow {} enabled: true } height: TelKeypadStyle.height + (showHistory ? history.contentHeight : 0) width: TelKeypadStyle.width radius:TelKeypadStyle.radius+1.0 // +1 for avoid mixing color with border slection (some pixels can be print after the line) onVisibleChanged: if(visible) telKeypad.forceActiveFocus() // --------------------------------------------------------------------------- MouseArea{ anchors.fill:parent onClicked: telKeypad.forceActiveFocus() } ColumnLayout { anchors.fill: parent anchors.topMargin: TelKeypadStyle.rowSpacing+5 anchors.bottomMargin: TelKeypadStyle.rowSpacing+5 anchors.leftMargin: TelKeypadStyle.columnSpacing+5 anchors.rightMargin: TelKeypadStyle.columnSpacing+5 Text{ id: history Layout.preferredWidth: parent.width Layout.preferredHeight: implicitHeight color: 'white' font { bold: true pointSize: TelKeypadStyle.button.text.pointSize } wrapMode: Text.WrapAnywhere horizontalAlignment: Qt.AlignCenter visible: showHistory MouseArea{ anchors.fill: parent onClicked: parent.text = '' } } GridLayout { id: grid Layout.topMargin: TelKeypadStyle.rowSpacing+5 columns: 4 // Not a style. rows: 4 // Same idea. columnSpacing: TelKeypadStyle.columnSpacing rowSpacing: TelKeypadStyle.rowSpacing Repeater { model: [ '1', '2', '3', 'A', '4', '5', '6', 'B', '7', '8', '9', 'C', '*', '0', '#', 'D', ] TelKeypadButton { id: telKeypadButton property var _timeout showVoicemail: index === 0 auxSymbol: index === 13 ? '+' : '' Layout.fillHeight: true Layout.fillWidth: true text: modelData onSendDtmf: { telKeypad.forceActiveFocus() if(telKeypad.call) telKeypad.call.sendDtmf(dtmf) telKeypad.sendDtmf(dtmf) if(showHistory) history.text += dtmf } Connections{ target: telKeypad onKeyPressed: telKeypadButton.activateEvent(event.key) } } } } } // --------------------------------------------------------------------------- Rectangle{ id: selectedArea anchors.fill:parent color:"transparent" border.color:TelKeypadStyle.selectedColor border.width:0 focus:false enabled:false radius:TelKeypadStyle.radius } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/TelKeypad/TelKeypadButton.qml000066400000000000000000000051761434616504300307260ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone.Styles 1.0 // ============================================================================= Item { id: button property string text: '' property bool showVoicemail: false property string auxSymbol: '' signal sendDtmf(var dtmf) function activateEvent(key){ var keyString = String.fromCharCode(key) if( keyString == text || keyString == auxSymbol){ actionButton.toggled = true; sendDtmf(keyString) } } // --------------------------------------------------------------------------- Timer{ id: untoggledTimer interval: 100 repeat: false onTriggered: actionButton.toggled = false } ActionButton { id: actionButton onToggledChanged: if(toggled) untoggledTimer.start() anchors.fill: parent colorSet: TelKeypadStyle.button.colorSet backgroundRadius: width/2 isCustom: true property bool doNotSend: false onPressed: {actionButton.doNotSend = false} onReleased: { if(actionButton.doNotSend) actionButton.doNotSend = false else { actionButton.doNotSend = true; button.sendDtmf(button.text) } } onLongPressed:{ if(actionButton.doNotSend){ actionButton.doNotSend = false }else{ if( button.auxSymbol != '' && actionButton.hovered) { actionButton.doNotSend = true; button.sendDtmf(button.auxSymbol) } } } ColumnLayout { anchors.fill: parent spacing: 0 anchors.topMargin: 5 anchors.bottomMargin: 5 RowLayout{ Layout.fillHeight: true Layout.fillWidth: true Layout.alignment: Qt.AlignCenter Layout.leftMargin: 5 Layout.rightMargin: 5 spacing: 0 Text { id: charText Layout.fillHeight: true Layout.fillWidth: true color: TelKeypadStyle.button.text.color elide: Text.ElideRight font { bold: true pointSize: TelKeypadStyle.button.text.pointSize } text: button.text horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } Icon{ icon: TelKeypadStyle.voicemail.icon iconSize: charText.height/2 overwriteColor: TelKeypadStyle.button.text.color visible: button.showVoicemail } } Text { visible: text != '' Layout.fillHeight: true Layout.fillWidth: true color: TelKeypadStyle.button.text.color elide: Text.ElideRight font { bold: true pointSize: TelKeypadStyle.button.text.pointSize } text: auxSymbol horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Timeline/000077500000000000000000000000001434616504300250105ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Timeline/Timeline.js000066400000000000000000000045131434616504300271170ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `Timeline.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone // ============================================================================= function setSelectedEntry (peerAddress, localAddress) { /* if (localAddress != null && localAddress !== Linphone.AccountSettingsModel.sipAddress) { resetSelectedEntry() return } var model = timeline.model var n = view.count timeline._selectedSipAddress = peerAddress for (var i = 0; i < n; i++) { if (peerAddress === model.data(model.index(i, 0)).sipAddress) { view.currentIndex = i return } } */ } function resetSelectedEntry () { /* view.currentIndex = -1 timeline._selectedSipAddress = ''*/ } // ----------------------------------------------------------------------------- function handleDataChanged (topLeft, bottomRight, roles) { /* var index = view.currentIndex var model = timeline.model var sipAddress = timeline._selectedSipAddress if ( index !== -1 && sipAddress !== model.data(model.index(index, 0)).sipAddress ) { setSelectedEntry(sipAddress) } */ } function handleRowsAboutToBeRemoved (parent, first, last) { /* var index = view.currentIndex if (index >= first && index <= last) { view.currentIndex = -1 } */ } function handleCountChanged (_) { /* var sipAddress = timeline._selectedSipAddress if (sipAddress.length > 0) { setSelectedEntry(sipAddress) }*/ } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml000066400000000000000000000301341434616504300272720ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.5 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import ColorsList 1.0 import UtilsCpp 1.0 import 'Timeline.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Rectangle { id: timeline // --------------------------------------------------------------------------- property alias model: view.model property string _selectedSipAddress property bool showHistoryButton : CoreManager.callLogsCount property bool isFilterVisible: searchView.visible || showFilterView property bool showFiltersButtons: view.count > 0 || timeline.isFilterVisible || timeline.model.filterFlags > 0 // --------------------------------------------------------------------------- signal entrySelected (TimelineModel entry) signal showHistoryRequest() // --------------------------------------------------------------------------- property bool showFilterView : false color: TimelineStyle.color ColumnLayout { anchors.fill: parent spacing: 0 // ------------------------------------------------------------------------- Connections { target: model onSelectedCountChanged:{ if(selectedCount<=0) { view.currentIndex = -1 timeline.entrySelected('') } } onSelectedChanged : if(timelineModel) timeline.entrySelected(timelineModel) } // ------------------------------------------------------------------------- // Legend. // ------------------------------------------------------------------------- Rectangle { id: legendArea Layout.fillWidth: true Layout.preferredHeight: TimelineStyle.legend.height Layout.alignment: Qt.AlignTop color: showHistory.containsMouse?TimelineStyle.legend.backgroundColor.hovered:TimelineStyle.legend.backgroundColor.normal visible: showHistoryButton || showFiltersButtons MouseArea{// no more showing history id:showHistory anchors.fill:parent visible: showFiltersButtons onClicked: { timeline.showFilterView = !timeline.showFilterView } } RowLayout{ anchors.fill:parent spacing:TimelineStyle.legend.spacing Text { Layout.preferredHeight: parent.height Layout.fillWidth: true Layout.leftMargin: TimelineStyle.legend.leftMargin visible: showFiltersButtons color: TimelineStyle.legend.color font.pointSize: TimelineStyle.legend.pointSize //: A title for filtering mode. text: qsTr('timelineFilter')+' : ' +(timeline.model.filterFlags == 0 || timeline.model.filterFlags == TimelineProxyModel.AllChatRooms //: 'All' The mode for timelines filtering. ? qsTr('timelineFilterAll') //: 'Custom' The mode for timelines filtering. : qsTr('timelineFilterCustom')) verticalAlignment: Text.AlignVCenter } Icon { id:filterButton Layout.alignment: Qt.AlignRight icon: 'filter_params_custom' iconSize: TimelineStyle.legend.iconSize overwriteColor: TimelineStyle.legend.color visible: showFiltersButtons MouseArea{ anchors.fill:parent onClicked:{ timeline.showFilterView = !timeline.showFilterView } } } MouseArea{ Layout.alignment: Qt.AlignRight Layout.fillHeight: true Layout.preferredWidth: TimelineStyle.legend.iconSize visible: showFiltersButtons onClicked:{ searchView.visible = !searchView.visible } Icon { id:searchButton anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter property bool searching: searchView.visible icon: (searchView.visible? 'close_custom': 'search_custom') iconSize: TimelineStyle.legend.iconSize overwriteColor: TimelineStyle.legend.color } } MouseArea{ Layout.alignment: Qt.AlignRight Layout.rightMargin: TimelineStyle.legend.lastRightMargin Layout.fillHeight: true Layout.preferredWidth: TimelineStyle.legend.iconSize visible: timeline.showHistoryButton onClicked:{ showHistoryRequest() } Icon { id:callHistoryButton anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter property bool searching: searchView.visible icon: 'call_history_custom' iconSize: TimelineStyle.legend.iconSize overwriteColor: TimelineStyle.legend.color } } } } // ------------------------------------------------------------------------- // Filter. // ------------------------------------------------------------------------- Rectangle{ id:exhaustiveFilterView Layout.fillWidth: true Layout.preferredHeight: filterChoices.height Layout.alignment: Qt.AlignCenter border.color: TimelineStyle.filterField.borderColor border.width: 2 visible: timeline.showFilterView && !SettingsModel.useMinimalTimelineFilter ColumnLayout{ id:filterChoices anchors.leftMargin: 20 anchors.left:parent.left anchors.right:parent.right spacing:-4 function getFilterFlags(){ return secureFilter.model.get(secureFilter.currentIndex).value | groupFilter.model.get(groupFilter.currentIndex).value | ephemeralsFilter.model.get(ephemeralsFilter.currentIndex).value; } ComboBox { Layout.fillWidth: true id:secureFilter currentIndex: 0 onCurrentIndexChanged: timeline.model.filterFlags = filterChoices.getFilterFlags() textRole: "key" model: ListModel { ListElement { //: 'All security levels' : Filter item. Selecting it will not do any filter on security level. key: qsTr('timelineFilterAllSecureLevelRooms'); value: 0} ListElement { //: 'Standard rooms' : Filter item. Selecting it will show all simple rooms. key: qsTr('timelineFilterStandardRooms'); value: TimelineProxyModel.StandardChatRoom} ListElement { //: 'Secure rooms' : Filter item. Selecting it will show all secure rooms. key: qsTr('timelineFilterSecureRooms'); value: TimelineProxyModel.SecureChatRoom} } haveBorder: false haveMargin: false backgroundColor: 'transparent' visible: SettingsModel.secureChatEnabled && SettingsModel.standardChatEnabled } ComboBox { Layout.fillWidth: true id:groupFilter currentIndex: 0 onCurrentIndexChanged: timeline.model.filterFlags = filterChoices.getFilterFlags() textRole: "key" model: ListModel { ListElement { //: 'Any conversations' : Filter item. Selecting it will not do any filter on the type of conversations. key: qsTr('timelineFilterAnyChatRooms'); value: 0} ListElement { //: 'Simple rooms' : Filter item. Selecting it will show all secure chat groups (with more than one participant). key: qsTr('timelineFilterSimpleRooms'); value: TimelineProxyModel.SimpleChatRoom} ListElement { //: 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). key: qsTr('timelineFilterChatGroups'); value: TimelineProxyModel.GroupChatRoom} } haveBorder: false haveMargin: false backgroundColor: 'transparent' visible: SettingsModel.secureChatEnabled || SettingsModel.standardChatEnabled } ComboBox { Layout.fillWidth: true id:ephemeralsFilter currentIndex: 0 onCurrentIndexChanged: timeline.model.filterFlags = filterChoices.getFilterFlags() textRole: "key" model: ListModel { ListElement { //: 'Ephemerals on/off' : Filter item. Selecting it will not do any filter on ephemerals activation. key: qsTr('timelineFilterAnyEphemerals'); value: 0} ListElement { //: 'No Ephemerals' : Filter item. Selecting it will hide all chat rooms where the ephemeral mode has been enabled. key: qsTr('timelineFilterNoEphemerals'); value: TimelineProxyModel.NoEphemeralChatRoom} ListElement { //: 'Ephemerals' : Filter item. Selecting it will show all chat rooms where the ephemeral mode has been enabled. key: qsTr('timelineFilterEphemerals'); value: TimelineProxyModel.EphemeralChatRoom} } haveBorder: false haveMargin: false backgroundColor: 'transparent' visible: SettingsModel.secureChatEnabled || SettingsModel.standardChatEnabled } } } Rectangle{ id:minimalFilterView Layout.fillWidth: true Layout.preferredHeight: minimalFilterChoices.height Layout.alignment: Qt.AlignCenter border.color: TimelineStyle.filterField.borderColor border.width: 2 visible: timeline.showFilterView && SettingsModel.useMinimalTimelineFilter ColumnLayout{ id:minimalFilterChoices anchors.leftMargin: 20 anchors.left:parent.left anchors.right:parent.right spacing:-4 function getFilterFlags(){ return securedCheckBox.getValue() | groupCheckBox.getValue() | conferenceCheckBox.getValue(); } CheckBoxText { id: securedCheckBox Layout.fillWidth: true visible: SettingsModel.secureChatEnabled && SettingsModel.standardChatEnabled //: 'Secure rooms' : Filter item. Selecting it will show all secure rooms. text: qsTr('timelineFilterSecureRooms') onClicked: { timeline.model.filterFlags = minimalFilterChoices.getFilterFlags() } function getValue(){ if( checked) return TimelineProxyModel.SecureChatRoom else return 0 } } CheckBoxText { id: groupCheckBox Layout.fillWidth: true visible: SettingsModel.secureChatEnabled || SettingsModel.standardChatEnabled //: 'Chat groups' : Filter item. Selecting it will show all chat groups (with more than one participant). text: qsTr('timelineFilterChatGroups') onClicked: { timeline.model.filterFlags = minimalFilterChoices.getFilterFlags() } function getValue(){ if( checked) return TimelineProxyModel.GroupChatRoom else return 0 } } CheckBoxText { id: conferenceCheckBox Layout.fillWidth: true visible: false //: 'Conferences' : Filter item. Selecting it will show all conferences. text: qsTr('timelineFilterConferences') onClicked: { timeline.model.filterFlags = minimalFilterChoices.getFilterFlags() } function getValue(){ return 0 } } } } // ------------------------------------------------------------------------- // Search. // ------------------------------------------------------------------------- Rectangle{ id:searchView Layout.fillWidth: true Layout.preferredHeight: 40 Layout.alignment: Qt.AlignCenter border.color: TimelineStyle.searchField.borderColor border.width: 2 visible:false onVisibleChanged: if(visible){ timeline.model.filterText = searchBar.text searchBar.forceActiveFocus() }else{ timeline.model.filterText = '' } TextField { id:searchBar anchors.fill: parent anchors.rightMargin: 7 anchors.leftMargin: 7 anchors.topMargin: 5 anchors.bottomMargin: 5 width: parent.width - 14 icon: text == '' ? 'search_custom' : 'close_custom' iconSize: 30 overwriteColor: TimelineStyle.searchField.color //: 'Search in the list' : ths is a placeholder when searching something in the timeline list placeholderText: qsTr('timelineSearchPlaceholderText') onTextChanged: searchDelay.restart() font.pointSize: TimelineStyle.searchField.pointSize Timer{ id: searchDelay interval: 600 repeat: false onTriggered: timeline.model.filterText = searchBar.text } } } // ------------------------------------------------------------------------- // History. // ------------------------------------------------------------------------- ScrollableListView { id: view Layout.fillHeight: true Layout.fillWidth: true currentIndex: -1 delegate: TimelineItem{ timelineModel: $modelData modelIndex: index Connections{ target: $modelData onSelectedChanged:{ if(selected) { view.currentIndex = index; } } } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/Timeline/TimelineItem.qml000066400000000000000000000115661434616504300301210ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.5 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import ColorsList 1.0 import UtilsCpp 1.0 import 'Timeline.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Item { id: mainItem property TimelineModel timelineModel property bool optionsToggled: false property int modelIndex: 0 height: TimelineStyle.contact.height width: parent ? parent.width : 0 state: optionsToggled ? 'options' : 'normal' states: [State { name: "normal" }, State { name: "options" } ] transitions: [Transition { from: 'normal' to: 'options' //NumberAnimation { target: contactView; property: 'x'; to:-contactView.width; duration: 200;} NumberAnimation { target: optionsView; property: 'x'; to:0; duration: 200;} }, Transition { from: 'options' to: 'normal' //NumberAnimation { target: contactView; property: 'x'; to:0; duration: 200;} NumberAnimation { target: optionsView; property: 'x'; to:optionsView.width; duration: 200;} } ] enabled: !contactView.showBusyIndicator Contact { id: contactView property bool isSelected: mainItem.timelineModel != undefined && mainItem.timelineModel.selected //view.currentIndex === index height: mainItem.height width: mainItem.width color: isSelected ? TimelineStyle.contact.backgroundColor.selected : ( mainItem.modelIndex % 2 == 0 ? TimelineStyle.contact.backgroundColor.a : TimelineStyle.contact.backgroundColor.b ) displayUnreadMessageCount: SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled entry: mainItem.timelineModel && mainItem.timelineModel.chatRoomModel subtitleColor: isSelected ? TimelineStyle.contact.subtitle.color.selected : TimelineStyle.contact.subtitle.color.normal titleColor: isSelected ? TimelineStyle.contact.title.color.selected : TimelineStyle.contact.title.color.normal showSubtitle: mainItem.timelineModel && (mainItem.timelineModel.chatRoomModel && (mainItem.timelineModel.chatRoomModel.isOneToOne || !mainItem.timelineModel.chatRoomModel.isConference)) showBusyIndicator: mainItem.timelineModel && mainItem.timelineModel.updating TooltipArea { id: contactTooltip text: mainItem.timelineModel && UtilsCpp.toDateTimeString(mainItem.timelineModel.chatRoomModel.lastUpdateTime) isClickable: true } Icon{ icon: TimelineStyle.ephemeralTimer.icon iconSize: TimelineStyle.ephemeralTimer.iconSize overwriteColor: mainItem.timelineModel && mainItem.timelineModel.selected ? TimelineStyle.ephemeralTimer.selectedTimerColor : TimelineStyle.ephemeralTimer.timerColor anchors.right:parent.right anchors.bottom:parent.bottom anchors.bottomMargin: 7 anchors.rightMargin: 7 visible: mainItem.timelineModel && mainItem.timelineModel.chatRoomModel.ephemeralEnabled } MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton propagateComposedEvents: true preventStealing: false onClicked: { if(mouse.button == Qt.LeftButton){ mainItem.timelineModel.selected = true }else{ mainItem.optionsToggled = !mainItem.optionsToggled } } } } Item{ id: optionsView height: mainItem.height width: mainItem.width x:width visible: x!=width RowLayout{ anchors.fill: parent MouseArea { Layout.fillHeight: true Layout.fillWidth: true onClicked: { mainItem.optionsToggled = !mainItem.optionsToggled } } Rectangle{ Layout.fillHeight: true Layout.preferredWidth: optionsLayout.width color: contactView.color MouseArea {// Grabber anchors.fill: parent cursorShape: Qt.ArrowCursor } RowLayout{ id: optionsLayout anchors.centerIn: parent /* TODO CheckBoxText { id: securedCheckBox Layout.alignment: Qt.AlignCenter text: '' onClicked: { } }*/ ActionButton{ id: deleteButton Layout.alignment: Qt.AlignCenter Layout.rightMargin: 6 isCustom: true colorSet: contactView.isSelected ? TimelineStyle.selectedDeleteAction : TimelineStyle.deleteAction onClicked: window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { //: 'Are you sure you want to delete and leave this timeline?' descriptionText: qsTr('deleteTimeline'), }, function (status) { if (status) { mainItem.timelineModel.chatRoomModel.deleteChatRoom() } }) TooltipArea { //: 'After confirmation, it will erase all history, leave the chat room if it is a group chat and delete it in database.' text: qsTr('deleteTimelineTooltip') } } } } } } }linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/View/000077500000000000000000000000001434616504300241545ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/View/ParticipantsListView.qml000066400000000000000000000112201434616504300310130ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 import Common.Styles 1.0 import Linphone.Styles 1.0 import Units 1.0 import UtilsCpp 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= ColumnLayout { id:mainLayout property ChatRoomModel chatRoomModel property ConferenceModel conferenceModel property ParticipantModel me: conferenceModel && conferenceModel.localParticipant property int count: selectedParticipants.count property bool isAdmin : (chatRoomModel && chatRoomModel.isMeAdmin && !chatRoomModel.isReadOnly) || (me && me.adminStatus) property bool canHandleParticipants : isAdmin && ( (chatRoomModel && chatRoomModel.canHandleParticipants) || conferenceModel) property bool haveEncryption: chatRoomModel && chatRoomModel.haveEncryption spacing: ParticipantsListViewStyle.mainLayout.spacing SmartSearchBar { id: smartSearchBar Layout.fillWidth: true Layout.topMargin: ParticipantsListViewStyle.searchBar.topMargin showHeader:false visible: mainLayout.canHandleParticipants maxMenuHeight: MainWindowStyle.searchBox.maxHeight //: 'Add Participants' : Placeholder in a search bar for adding participant to the chat room placeholderText: qsTr('addParticipantPlaceholder') //: 'Search participants in your contact list in order to invite them into the chat room.' //~ Tooltip Explanation for inviting the selected participants into chat room tooltipText: qsTr('addParticipantTooltip') actions:[{ colorSet: ParticipantsListViewStyle.addParticipant, secure: mainLayout.haveEncryption, visible: true, secureIconVisibleHandler : function(entry) { return entry.sipAddress && mainLayout.haveEncryption && UtilsCpp.hasCapability(entry.sipAddress, LinphoneEnums.FriendCapabilityLimeX3Dh); }, handler: function (entry) { selectedParticipants.addAddress(entry.sipAddress) }, }] participantListModel: selectedParticipants.participantListModel onEntryClicked: { selectedParticipants.addAddress(entry.sipAddress) } } ScrollableListViewField { Layout.fillHeight: true Layout.fillWidth: true Layout.bottomMargin: 5 textFieldStyle: TextFieldStyle.unbordered ColumnLayout{ anchors.fill:parent spacing:0 Text{ Layout.topMargin: ParticipantsListViewStyle.results.title.topMargin Layout.leftMargin: ParticipantsListViewStyle.results.title.leftMargin //: 'Participant list' text:qsTr('participantList') color: ParticipantsListViewStyle.results.title.color font.pointSize:ParticipantsListViewStyle.results.title.pointSize font.weight: ParticipantsListViewStyle.results.title.weight } Text{ Layout.preferredHeight: implicitHeight Layout.rightMargin: ParticipantsListViewStyle.results.header.rightMargin Layout.alignment: Qt.AlignRight | Qt.AlignBottom //Layout.topMargin: ParticipantsListViewStyle.results.topMargin //: 'Admin' : Admin(istrator) //~ one word for admin status text : qsTr('adminStatus') color: ParticipantsListViewStyle.results.header.color font.pointSize: ParticipantsListViewStyle.results.header.pointSize font.weight: ParticipantsListViewStyle.results.header.weight visible: mainLayout.isAdmin && participantView.count > 0 } ParticipantsView { id: participantView Layout.fillHeight: true Layout.fillWidth: true showSubtitle:false showSwitch : mainLayout.isAdmin showSeparator: false showAdminStatus:!mainLayout.isAdmin isSelectable: false hoveredCursor:Qt.WhatsThisCursor actions: mainLayout.isAdmin ? [{ colorSet: ParticipantsListViewStyle.removeParticipant, secure:0, visible:true, visibleHandler: function(entry){ return !UtilsCpp.isMe(entry.sipAddress) }, //: 'Remove this participant from the list' : Tootltip to explain that the action will lead to remove the participant. tooltipText: qsTr('participantsListRemoveTooltip'), handler: function (entry) { selectedParticipants.removeModel(entry) } }] : [] genSipAddress: '' model: ParticipantProxyModel { id:selectedParticipants chatRoomModel: mainLayout.chatRoomModel conferenceModel: mainLayout.conferenceModel onAddressAdded: smartSearchBar.addAddressToIgnore(sipAddress) onAddressRemoved: smartSearchBar.removeAddressToIgnore(sipAddress) showMe: true } onEntryClicked: { contactItem.showSubtitle = !contactItem.showSubtitle } } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/View/ParticipantsView.qml000066400000000000000000000240261434616504300301670ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Common.Styles 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= ScrollableListView { id: sipAddressesView // --------------------------------------------------------------------------- // Contains a list of: { // icon: 'string', // handler: function () { ... } // } property var actions: [] property string genSipAddress property string headerButtonDescription property string headerButtonIcon property var headerButtonAction property bool showHeader : true property bool showSubtitle : true property bool showSwitch : false property bool showSeparator : true property bool showAdminStatus : false property bool isSelectable : true property bool showInvitingIndicator : true property int hoveredCursor : Qt.PointingHandCursor property var switchHandler : function(checked, index){ } readonly property string interpretableSipAddress: SipAddressesModel.interpretSipAddress( genSipAddress, false ) // --------------------------------------------------------------------------- signal entryClicked (var entry, var index, var contactItem) // --------------------------------------------------------------------------- // Header. // --------------------------------------------------------------------------- header: MouseArea { height: { var height = headerButton.visible ? SipAddressesViewStyle.header.button.height : 0 if (defaultContact.visible) { height += SipAddressesViewStyle.entry.height } return height } width: parent.width // Workaround to handle mouse. // Without it, the mouse can be given to items list when mouse is hover header. hoverEnabled: true cursorShape: Qt.ArrowCursor Column { anchors.fill: parent spacing: 0 // ----------------------------------------------------------------------- // Default contact. // ----------------------------------------------------------------------- Loader { id: defaultContact height: SipAddressesViewStyle.entry.height width: parent.width visible: sipAddressesView.interpretableSipAddress.length > 0 sourceComponent: Rectangle { anchors.fill: parent color: SipAddressesViewStyle.entry.color.normal RowLayout { anchors { fill: parent rightMargin: SipAddressesViewStyle.entry.rightMargin } spacing: 0 Contact { id: contact Layout.fillHeight: true Layout.fillWidth: true entry: ({ sipAddress: sipAddressesView.interpretableSipAddress, isOneToOne:true, haveEncryption:false, securityLevel:1 }) } ActionBar { id: defaultContactActionBar iconSize: SipAddressesViewStyle.entry.iconSize Repeater { model: sipAddressesView.actions ActionButton { isCustom: true backgroundRadius: 90 colorSet: modelData.colorSet visible: sipAddressesView.actions[index].visible && (!sipAddressesView.actions[index].visibleHandler || sipAddressesView.actions[index].visibleHandler({ sipAddress: sipAddressesView.interpretableSipAddress })) onClicked: sipAddressesView.actions[index].handler({ sipAddress: sipAddressesView.interpretableSipAddress }) Icon{ visible: modelData.secure>0 && (sipAddressesView.actions[index].secureIconVisibleHandler ? sipAddressesView.actions[index].secureIconVisibleHandler({sipAddres:$modelData}) : true) icon: modelData.secure === 2?'secure_level_2':'secure_level_1' iconSize:parent.height/2 anchors.top:parent.top anchors.horizontalCenter: parent.right } } } } } } } // ----------------------------------------------------------------------- // Header button. // ----------------------------------------------------------------------- MouseArea { id: headerButton height: SipAddressesViewStyle.header.button.height width: parent.width visible: sipAddressesView.showHeader && !!sipAddressesView.headerButtonAction onClicked: sipAddressesView.headerButtonAction(sipAddressesView.interpretableSipAddress) Rectangle { anchors.fill: parent color: parent.pressed ? SipAddressesViewStyle.header.color.pressed : SipAddressesViewStyle.header.color.normal Text { anchors { left: parent.left leftMargin: SipAddressesViewStyle.header.leftMargin verticalCenter: parent.verticalCenter } font { bold: true pointSize: SipAddressesViewStyle.header.text.pointSize } color: headerButton.pressed ? SipAddressesViewStyle.header.text.color.pressed : SipAddressesViewStyle.header.text.color.normal text: sipAddressesView.headerButtonDescription } Icon { anchors { right: parent.right rightMargin: SipAddressesViewStyle.header.rightMargin verticalCenter: parent.verticalCenter } icon: sipAddressesView.headerButtonIcon iconSize: parent.height visible: icon.length > 0 } } } } } // --------------------------------------------------------------------------- // Entries. // --------------------------------------------------------------------------- delegate: Rectangle { id: sipAddressEntry color: SipAddressesViewStyle.entry.color.normal height: SipAddressesViewStyle.entry.height width: parent ? parent.width : 0 Rectangle { id: indicator anchors.left: parent.left color: 'transparent' height: parent.height width: SipAddressesViewStyle.entry.indicator.width } MouseArea { id: mouseArea anchors.fill: parent cursorShape: Qt.ArrowCursor RowLayout { anchors { fill: parent rightMargin: SipAddressesViewStyle.entry.rightMargin } spacing: 0 // --------------------------------------------------------------------- // Contact or address info. // --------------------------------------------------------------------- Contact { id:contactView Layout.fillHeight: true Layout.fillWidth: true showSubtitle: sipAddressesView.showSubtitle function getStatus(){ var count = 0; var txt = '' if( $modelData.adminStatus) { //: '(Admin)' : One word for Admin(istrator) //~ Context Little Header in one word for a column in participant txt += qsTr('participantsAdminHeader') ++count } if( $modelData.isMe) //: 'Me' : One word for myself. txt += (count++ > 0 ? ' - ' : '') + qsTr('participantsMe') return txt } statusText : showAdminStatus ? getStatus() : '' entry: $modelData onAvatarClicked: sipAddressesView.entryClicked(parent.entry, index, contactView) } // --------------------------------------------------------------------- // Actions // --------------------------------------------------------------------- ActionBar { iconSize: SipAddressesViewStyle.entry.iconSize Switch{ anchors.verticalCenter: parent.verticalCenter width:50 indicatorStyle: SwitchStyle.aux visible: sipAddressesView.showSwitch enabled:true checked: $modelData.adminStatus onClicked: { $modelData.adminStatus = !checked } } Repeater { id: actionsRepeater model: sipAddressesView.actions Item{ height: buttonAction.height width: buttonAction.width anchors.verticalCenter: parent.verticalCenter ActionButton { id: buttonAction isCustom: true backgroundRadius: 90 colorSet: modelData.colorSet anchors.verticalCenter: parent.verticalCenter tooltipText: modelData.tooltipText? modelData.tooltipText:'' visible: sipAddressesView.actions[index].visible && (!sipAddressesView.actions[index].visibleHandler || sipAddressesView.actions[index].visibleHandler(contactView.entry)) onClicked: { sipAddressesView.actions[index].handler(contactView.entry) } Icon{ visible: modelData.secure>0 && (sipAddressesView.actions[index].secureIconVisibleHandler ? sipAddressesView.actions[index].secureIconVisibleHandler({sipAddres:$modelData}) : true) icon: modelData.secure === 2?'secure_level_2':'secure_level_1' iconSize: parent.height/2 anchors.top:parent.top anchors.horizontalCenter: parent.right } Loader{ anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter height: parent.height - 2 width: height active: index == actionsRepeater.count -1 && sipAddressesView.showInvitingIndicator && contactView.entry && contactView.entry.inviting sourceComponent: Component{ BusyIndicator{ color: BusyIndicatorStyle.alternateColor running: true } } } } } } } } } // Separator. Rectangle { color: SipAddressesViewStyle.entry.separator.color height: SipAddressesViewStyle.entry.separator.height width: parent.width visible: sipAddressesView.showSeparator } // ------------------------------------------------------------------------- states: State { when: mouseArea.containsMouse && sipAddressesView.isSelectable PropertyChanges { color: SipAddressesViewStyle.entry.color.hovered target: sipAddressEntry } PropertyChanges { color: SipAddressesViewStyle.entry.indicator.color target: indicator } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/View/SipAddressesView.qml000066400000000000000000000220541434616504300301160ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import UtilsCpp 1.0 import App.Styles 1.0 import Linphone.Styles 1.0 import Common.Styles 1.0 // ============================================================================= ScrollableListView { id: sipAddressesView // --------------------------------------------------------------------------- // Contains a list of: { // icon: 'string', // handler: function () { ... } // } property var actions: [] property string genSipAddress // Optional parameters. property string headerButtonDescription property string headerButtonIcon property color headerButtonOverwriteColor property var headerButtonAction property bool showHeader : true property bool showSubtitle : true property bool showSwitch : false property bool showSeparator : true property bool isSelectable : true property var switchHandler : function(checked, index){ } readonly property string interpretableSipAddress: SipAddressesModel.interpretSipAddress( genSipAddress, false ) // --------------------------------------------------------------------------- signal entryClicked (var entry, var index) // --------------------------------------------------------------------------- // Header. // --------------------------------------------------------------------------- header: MouseArea { height: { var height = headerButton.visible ? SipAddressesViewStyle.header.button.height : 0 if (defaultContact.visible) { height += SipAddressesViewStyle.entry.height } return height } width: parent.width // Workaround to handle mouse. // Without it, the mouse can be given to items list when mouse is hover header. hoverEnabled: true cursorShape: Qt.ArrowCursor Column { anchors.fill: parent spacing: 0 // ----------------------------------------------------------------------- // Default contact. // ----------------------------------------------------------------------- Loader { id: defaultContact height: SipAddressesViewStyle.entry.height width: parent.width visible: sipAddressesView.interpretableSipAddress.length > 0 sourceComponent: Rectangle { anchors.fill: parent color: SipAddressesViewStyle.entry.color.normal RowLayout { anchors { fill: parent rightMargin: SipAddressesViewStyle.entry.rightMargin } spacing: 0 Contact { id: contact Layout.fillHeight: true Layout.fillWidth: true entry: ({ sipAddress: sipAddressesView.interpretableSipAddress, isOneToOne:true, haveEncryption:false, securityLevel:1 }) } ActionBar { id: defaultContactActionBar iconSize: SipAddressesViewStyle.entry.iconSize Repeater { model: sipAddressesView.actions ActionButton { isCustom: true backgroundRadius: 90 colorSet: modelData.colorSet visible: modelData.visible onClicked: { sipAddressesView.actions[index].handler({ // Do not use modelData on functions : Qt bug sipAddress: sipAddressesView.interpretableSipAddress }) } Icon{ visible: modelData.secure>0 && // Do not use modelData on functions : Qt bug (sipAddressesView.actions[index].secureIconVisibleHandler ? sipAddressesView.actions[index].secureIconVisibleHandler({ sipAddress : sipAddressesView.interpretableSipAddress}) : true) icon: 'secure_on' iconSize: parent.height/2 anchors.top:parent.top anchors.horizontalCenter: parent.right } } } } } } } // ----------------------------------------------------------------------- // Header button. // ----------------------------------------------------------------------- MouseArea { id: headerButton height: SipAddressesViewStyle.header.button.height width: parent.width visible: sipAddressesView.showHeader && !!sipAddressesView.headerButtonAction onClicked: sipAddressesView.headerButtonAction(sipAddressesView.interpretableSipAddress) Rectangle { anchors.fill: parent color: parent.pressed ? SipAddressesViewStyle.header.color.pressed : SipAddressesViewStyle.header.color.normal Text { anchors { left: parent.left leftMargin: SipAddressesViewStyle.header.leftMargin verticalCenter: parent.verticalCenter } font { bold: true pointSize: SipAddressesViewStyle.header.text.pointSize } color: headerButton.pressed ? SipAddressesViewStyle.header.text.color.pressed : SipAddressesViewStyle.header.text.color.normal text: sipAddressesView.headerButtonDescription } Icon { anchors { right: parent.right rightMargin: SipAddressesViewStyle.header.rightMargin verticalCenter: parent.verticalCenter } icon: sipAddressesView.headerButtonIcon iconSize: parent.height overwriteColor: sipAddressesView.headerButtonOverwriteColor visible: icon.length > 0 } } } } } // --------------------------------------------------------------------------- // Entries. // --------------------------------------------------------------------------- delegate: Rectangle { id: sipAddressEntry property var entry: $modelData color: SipAddressesViewStyle.entry.color.normal height: SipAddressesViewStyle.entry.height width: parent ? parent.width : 0 Rectangle { id: indicator anchors.left: parent.left color: 'transparent' height: parent.height width: SipAddressesViewStyle.entry.indicator.width } MouseArea { id: mouseArea anchors.fill: parent cursorShape: Qt.ArrowCursor RowLayout { anchors { fill: parent rightMargin: SipAddressesViewStyle.entry.rightMargin } spacing: 0 // --------------------------------------------------------------------- // Contact or address info. // --------------------------------------------------------------------- Contact { Layout.fillHeight: true Layout.fillWidth: true showSubtitle: sipAddressesView.showSubtitle entry: $modelData MouseArea { anchors.fill: parent onClicked: sipAddressesView.entryClicked($modelData, index) } } // --------------------------------------------------------------------- // Actions // --------------------------------------------------------------------- ActionBar { iconSize: SipAddressesViewStyle.entry.iconSize Loader { anchors.verticalCenter: parent.verticalCenter width:50 sourceComponent: Component{ Switch{ anchors.verticalCenter: parent.verticalCenter width:50 //Layout.preferredWidth: 50 indicatorStyle: SwitchStyle.aux visible: sipAddressesView.showSwitch enabled:true checked: false onClicked: { //checked = !checked switchHandler(!checked, index) } } } active: sipAddressesView.showSwitch // Resolve a random Qt crash from using indicator on switch. This way, switch is not loaded. // https://bugreports.qt.io/browse/QTBUG-82285. } Repeater { model: sipAddressesView.actions ActionButton { isCustom: true backgroundRadius: 90 colorSet: sipAddressesView.actions[index].colorSet tooltipText:$modelData.tooltipText?$modelData.tooltipText:'' visible: sipAddressesView.actions[index].visible onClicked: {// Do not use $modelData on functions : Qt bug sipAddressesView.actions[index].handler(sipAddressEntry.entry) } Icon{ visible: sipAddressesView.actions[index].secure>0 && // Do not use $modelData on functions : Qt bug (sipAddressesView.actions[index].secureIconVisibleHandler ? sipAddressesView.actions[index].secureIconVisibleHandler(sipAddressEntry.entry) : true) icon: 'secure_on' iconSize: parent.height/2 anchors.top:parent.top anchors.horizontalCenter: parent.right } } } } } } // Separator. Rectangle { color: SipAddressesViewStyle.entry.separator.color height: SipAddressesViewStyle.entry.separator.height width: parent.width visible: sipAddressesView.showSeparator } // ------------------------------------------------------------------------- states: State { when: mouseArea.containsMouse && sipAddressesView.isSelectable PropertyChanges { color: SipAddressesViewStyle.entry.color.hovered target: sipAddressEntry } PropertyChanges { color: SipAddressesViewStyle.entry.indicator.color target: indicator } } } } linphone-desktop-5.0.2/linphone-app/ui/modules/Linphone/qmldir000066400000000000000000000053121434616504300244560ustar00rootroot00000000000000# ============================================================================== # Linphone's components to export. # ============================================================================== module Linphone # Components ------------------------------------------------------------------- AccountStatus 1.0 Account/AccountStatus.qml CardBlock 1.0 Blocks/CardBlock.qml RequestBlock 1.0 Blocks/RequestBlock.qml Calls 1.0 Calls/Calls.qml CallStatistics 1.0 Calls/CallStatistics.qml IncallAvatar 1.0 Calls/IncallAvatar.qml CameraItem 1.0 Camera/CameraItem.qml CameraView 1.0 Camera/CameraView.qml Chat 1.0 Chat/Chat.qml ChatAudioMessage 1.0 Chat/ChatAudioMessage.qml ChatAudioPreview 1.0 Chat/ChatAudioPreview.qml ChatCalendarMessage 1.0 Chat/ChatCalendarMessage.qml ChatConferenceInvitationMessage 1.0 Chat/ChatConferenceInvitationMessage.qml ChatMessagePreview 1.0 Chat/ChatMessagePreview.qml ChatForwardMessage 1.0 Chat/ChatForwardMessage.qml ChatReplyMessage 1.0 Chat/ChatReplyMessage.qml ChatReplyPreview 1.0 Chat/ChatReplyPreview.qml CodecsViewer 1.0 Codecs/CodecsViewer.qml Avatar 1.0 Contact/Avatar.qml Contact 1.0 Contact/Contact.qml ContactDescription 1.0 Contact/ContactDescription.qml SipAddressDialog 1.0 Dialog/SipAddressDialog.qml MultimediaParametersDialog 1.0 Dialog/MultimediaParametersDialog.qml ZrtpTokenAuthenticationDialog 1.0 Dialog/ZrtpTokenAuthenticationDialog.qml FileView 1.0 File/FileView.qml History 1.0 History/History.qml SipAddressesMenu 1.0 Menus/SipAddressesMenu.qml IncallMenu 1.0 Menus/IncallMenu.qml MessageCounter 1.0 Misc/MessageCounter.qml PresenceLevel 1.0 Presence/PresenceLevel.qml SmartSearchBar 1.0 SmartSearchBar/SmartSearchBar.qml AvatarSticker 1.0 Sticker/AvatarSticker.qml CameraSticker 1.0 Sticker/CameraSticker.qml DecorationSticker 1.0 Sticker/DecorationSticker.qml Sticker 1.0 Sticker/Sticker.qml TelKeypad 1.0 TelKeypad/TelKeypad.qml Timeline 1.0 Timeline/Timeline.qml TimelineItem 1.0 Timeline/TimelineItem.qml ParticipantsListView 1.0 View/ParticipantsListView.qml ParticipantsView 1.0 View/ParticipantsView.qml SipAddressesView 1.0 View/SipAddressesView.qml linphone-desktop-5.0.2/linphone-app/ui/scripts/000077500000000000000000000000001434616504300215055ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/scripts/Utils/000077500000000000000000000000001434616504300226055ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/scripts/Utils/port-tools.js000066400000000000000000000026051434616504300252700ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // Library to deal with Ports. // ============================================================================= .pragma library // ----------------------------------------------------------------------------- var PORT = '([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])' var PORT_RANGE = PORT + '(?::' + PORT + ')?' // ----------------------------------------------------------------------------- var PORT_REGEX = new RegExp('^' + PORT + '$') var PORT_RANGE_REGEX = new RegExp('^' + PORT_RANGE + '$') linphone-desktop-5.0.2/linphone-app/ui/scripts/Utils/qmldir000066400000000000000000000000411434616504300240130ustar00rootroot00000000000000module Utils Utils 1.0 utils.js linphone-desktop-5.0.2/linphone-app/ui/scripts/Utils/uri-tools.spec.qml000066400000000000000000000120301434616504300262020ustar00rootroot00000000000000import QtTest 1.1 import 'uri-tools.js' as UriTools // ============================================================================= TestCase { function test_regexExists () { compare( UriTools.URI_REGEX instanceof RegExp, true, '`URI_REGEX` is not a `RegExp` or is undefined.' ) } function test_urlSupport () { compare( typeof UriTools.SUPPORTS_URL, 'boolean', '`SUPPORTS_URL` is not a `Boolean` or is undefined.' ) } function test_matchUri_data () { return [ // ======================================================================= // Must match. // ======================================================================= { input: 'http://www.LaRmInA.com/', output: [ 'http://www.LaRmInA.com/' ] }, { input: 'http://foob%3Dbar@baz.fr', output: [ 'http://foob%3Dbar@baz.fr' ] }, { input: 'file://a/b/c/;d', output: [ 'file://a/b/c/;d' ] }, { input: 'ftp://0/', output: [ 'ftp://0/' ] }, { input: 'mailto://valentin.cognito@domain.unknown', output: [ 'mailto://valentin.cognito@domain.unknown' ] }, { input: 'mailto://sLimAne@egypt', output: [ 'mailto://sLimAne@egypt' ] }, { input: 'file://beetlejuice-beetlejuice-beetlejui...', output: [ 'file://beetlejuice-beetlejuice-beetlejui...' ] }, { input: 'https://gitlab@localhost', output: [ 'https://gitlab@localhost' ] }, { input: 'xmpp:von.zimmel@reich.org', output: [ 'xmpp:von.zimmel@reich.org' ] }, { input: 'dot.dot://dot.dot.dot@dot.dot.dot', output: [ 'dot.dot://dot.dot.dot@dot.dot.dot' ] }, { input: 'A:B', output: [ 'A:B' ] }, { input: 'foo://a=B.7z*+9aZb;$.!,!,!_(~_~)_-&\':', output: [ 'foo://a=B.7z*+9aZb;$.!,!,!_(~_~)_-&\':' ] }, { input: 'foo+bar+baz://hey:1800/it-s-me?a&b=12', output: [ 'foo+bar+baz://hey:1800/it-s-me?a&b=12' ] }, { input: 'nsa://localhost:666', output: [ 'nsa://localhost:666' ] }, { input: 'protocol://U$3r:p@sswd/WwW.L33t.sp3', output: [ 'protocol://U$3r:p@sswd/WwW.L33t.sp3' ] }, { input: 'foo://username:password@www.example.com:123/hello/world/there.html?name=ferret#foo', output: [ 'foo://username:password@www.example.com:123/hello/world/there.html?name=ferret#foo' ] }, { input: 'lalala://lololo.titi/tata_(tutu)#riri-0', output: [ 'lalala://lololo.titi/tata_(tutu)#riri-0' ] }, { input: 'dest://007@uk.en:8080/', output: [ 'dest://007@uk.en:8080/' ] }, { input: 'fefe://zef.sfdfzfds.vfs/zrefz/?vsfezzef=afzfefg&zfefezfze=7275&grgr', output: [ 'fefe://zef.sfdfzfds.vfs/zrefz/?vsfezzef=afzfefg&zfefezfze=7275&grgr' ] }, { input: 'fefe://xcv.zefe/(fzfff)?zefezef=fzefzef', output: [ 'fefe://xcv.zefe/(fzfff)?zefezef=fzefzef' ] }, { input: 'feeffsd://vccvx.zzef.dfs/xcvvcx/#&zfe=zfe', output: [ 'feeffsd://vccvx.zzef.dfs/xcvvcx/#&zfe=zfe' ] }, { input: 'http://256.1.1.1', output: [ 'http://256.1.1.1' ] // Valid URI. Invalid URL. }, { input: 'http://0.0.0.0', output: [ 'http://0.0.0.0' ] // Same idea. }, { input: 'http://a.b--c.de/', output: [ 'http://a.b--c.de/' ] // And again. }, { input: 'http://1.0.1.0.1.0', output: [ 'http://1.0.1.0.1.0' ] // AND AGAIN. }, { input: 'http://abc.c.d.', output: [ 'http://abc.c.d.' ] // AAAAND AAAGAAAIIIIIN. }, { input: 'https://a.b-c.de/', output: [ 'https://a.b-c.de/' ] }, { input: 'http://a/B/c?a&b&c', output: [ 'http://a/B/c?a&b&c' ] }, { input: '1http://www.linphone.org', output: [ 'http://www.linphone.org' ] }, { input: 'http://255.254.255.254', output: [ 'http://255.254.255.254' ] }, { input: 'http://12.42.1.10/', output: [ 'http://12.42.1.10/' ] }, // ======================================================================= // Partial or no match. // ======================================================================= { input: '://www.linphone.org', output: UriTools.SUPPORTS_URL ? [ 'www.linphone.org' ] : null }, { input: 'http', output: null }, { input: '/path/', output: null }, { input: 'http://✪dragooooonnnn✪ball✪z✪z✪z.goku/4', output: [ 'http://' ] }, { input: 'http:// iamafail.fr', output: [ 'http://' ] }, { input: 'isaac://石村.jp', output: [ 'isaac://' ] }, { input: ':// not good', output: null }, { input: 'http://☺.☺.☺', output: [ 'http://' ] } ] } function test_matchUri (data) { compare(data.input.match(UriTools.URI_REGEX), data.output) } } linphone-desktop-5.0.2/linphone-app/ui/scripts/Utils/utils.js000066400000000000000000000455701434616504300243160ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // Contains many common helpers. // ============================================================================= .pragma library .import QtQuick 2.7 as QtQuick .import Linphone 1.0 as Linphone .import 'port-tools.js' as PortTools // ============================================================================= // Constants. // ============================================================================= var PORT_REGEX = PortTools.PORT_REGEX var PORT_RANGE_REGEX = PortTools.PORT_RANGE_REGEX var SCHEME_REGEX = new RegExp('^[^:]+:') // ============================================================================= // QML helpers. // ============================================================================= function buildCommonDialogUri (component) { return 'qrc:/ui/modules/Common/Dialog/' + component + '.qml' } function buildLinphoneDialogUri (component) { return 'qrc:/ui/modules/Linphone/Dialog/' + component + '.qml' } function buildAppDialogUri (component) { return 'qrc:/ui/views/App/Dialog/' + component + '.qml' } // ----------------------------------------------------------------------------- // Destroy timeout. function clearTimeout (timer) { timer.stop() // NECESSARY. timer.destroy() } // ----------------------------------------------------------------------------- function createObject (source, parent, options) { if (options && options.isString) { var object = Qt.createQmlObject(source, parent) var properties = options && options.properties if (properties) { for (var key in properties) { object[key] = properties[key] } } return object } var component = Qt.createComponent(source) if (component.status !== QtQuick.Component.Ready) { console.debug('Component not ready.') if (component.status === QtQuick.Component.Error) { console.debug('Error: ' + component.errorString()) } return // Error. } var object = component.createObject(parent, (options && options.properties) || {}) if (!object) { console.debug('Error: unable to create dynamic object.') } return object } // ----------------------------------------------------------------------------- function getSystemPathFromUri (uri) { var str = uri.toString() if (startsWith(str, 'file://')) { str = str.substring(7) // Absolute path. if (str.charAt(0) === '/') { return runOnWindows() ? str.substring(1) : str } } return str } function getUriFromSystemPath (path) { if (path.startsWith('file://')) { return path } if (runOnWindows()) { return 'file://' + (/^\w:/.exec(path) ? '/' : '') + path } return 'file://' + path } // ----------------------------------------------------------------------------- // Returns the top (root) parent of one object. function getTopParent (object, useFakeParent) { function _getTopParent (object, useFakeParent) { return (useFakeParent && object.$parent) || object.parent } var parent = _getTopParent(object, useFakeParent) var p while ((p = _getTopParent(parent, useFakeParent)) != null) { parent = p } return parent } // ----------------------------------------------------------------------------- // Load by default a window in the ui/views folder. // If options.isString is equals to true, a marshalling component can // be used. // // Supported options: isString, exitHandler, properties. // // If exitHandler is used, window must implement exitStatus signal. function openWindow (window, parent, options, fullscreen) { var object = createObject(window, parent, options) object.closing.connect(object.destroy.bind(object)) if (options && options.exitHandler) { object.exitStatus.connect( // Bind to access parent properties. options.exitHandler.bind(parent) ) } if( runOnWindows()){ object.show() // Needed for Windows : Show the window in all case. Allow to graphically locate the window before going to fullscreen. if(fullscreen) object.showFullScreen()// Should be equivalent to changing visibility }else if(fullscreen) object.showFullScreen()// Should be equivalent to changing visibility else object.show() return object } // ----------------------------------------------------------------------------- function resolveImageUri (name) { return name //? 'image://internal/' + removeScheme(Qt.resolvedUrl('/assets/images/' + name + '.svg')) ? 'image://internal/' + name : '' } // ----------------------------------------------------------------------------- function runOnWindows () { var os = Qt.platform.os return os === 'windows' || os === 'winrt' } // ----------------------------------------------------------------------------- // Test if a point is in a item. // // `source` is the item that generated the point. // `target` is the item to test. // `point` is the point to test. function pointIsInItem (source, target, point) { point = source.mapToItem(target.parent, point.x, point.y) return ( point.x >= target.x && point.y >= target.y && point.x < target.x + target.width && point.y < target.y + target.height ) } // ----------------------------------------------------------------------------- // Test the type of a qml object. // Warning: this function is probably not portable // on new versions of Qt. // // So, if you want to use it on a specific `className`, please to add // a test in `test_qmlTypeof_data` of `utils.spec.qml`. function qmlTypeof (object, className) { var str = object.toString() return ( str.indexOf(className + '(') == 0 || str.indexOf(className + '_QML') == 0 ) } // ----------------------------------------------------------------------------- function removeScheme (url) { return url.toString().replace(SCHEME_REGEX, '') } // ----------------------------------------------------------------------------- // A copy of `Window.setTimeout` from js. // delay is in milliseconds. function setTimeout (parent, delay, cb) { var timer = new (function (parent) { return Qt.createQmlObject('import QtQml 2.2; Timer { }', parent) })(parent) timer.interval = delay timer.repeat = false timer.triggered.connect(cb) timer.start() return timer } // ============================================================================= // GENERIC. // ============================================================================= function _computeOptimizedCb (func, context) { return context ? (function () { return func.apply(context, arguments) }) : func } function _indexFinder (array, cb, context) { var length = array.length for (var i = 0; i < length; i++) { if (cb(array[i], i, array)) { return i } } return -1 } function _keyFinder (obj, cb, context) { var keys = Object.keys(obj) var length = keys.length for (var i = 0; i < length; i++) { var key = keys[i] if (cb(obj[key], key, obj)) { return key } } } // ----------------------------------------------------------------------------- // Basic assert function. function assert (condition, message) { if (!condition) { throw new Error('Assert: ' + message) } } // ----------------------------------------------------------------------------- function basename (str) { if (!str) { return '' } if (runOnWindows()) { str = str.replace(/\\/g, '/') } var str2 = str var length = str2.length - 1 if (str2[length] === '/') { str2 = str2.substring(0, length) } return str2.slice(str2.lastIndexOf('/') + 1) } // ----------------------------------------------------------------------------- function capitalizeFirstLetter (str) { return str.charAt(0).toUpperCase() + str.slice(1) } // ----------------------------------------------------------------------------- function dirname (str) { if (!str) { return '' } if (runOnWindows()) { str = str.replace(/\\/g, '/') } var str2 = str var length = str2.length - 1 if (str2[length] === '/') { str2 = str2.substring(0, length) } return str2.slice(0, str2.lastIndexOf('/') + 1) } // ----------------------------------------------------------------------------- function extractProperties (obj, pattern) { if (!pattern) { return {} } var obj2 = {} pattern.forEach(function (property) { obj2[property] = obj[property] }) return obj2 } // ----------------------------------------------------------------------------- // Returns an array from an `object` or `array` argument. function ensureArray (obj) { if (isArray(obj)) { return obj } var keys = Object.keys(obj) var length = keys.length var values = Array(length) for (var i = 0; i < length; i++) { values[i] = obj[keys[i]] } return values } // ----------------------------------------------------------------------------- function escapeQuotes (str) { return str != null ? str.replace(/([^'\\]*(?:\\.[^'\\]*)*)'/g, '$1\\\'') : '' } function decode(str){ return decodeURIComponent(escape(str.replace(/%([0-9A-Fa-f]{2})/g, function() { return String.fromCharCode(parseInt(arguments[1], 16)); }))); } // ----------------------------------------------------------------------------- // Get the first matching value in a array or object. // The matching value is obtained if `cb` returns true. function find (obj, cb, context) { cb = _computeOptimizedCb(cb, context) var finder = isArray(obj) ? _indexFinder : _keyFinder var key = finder(obj, cb, context) return key != null && key !== -1 ? obj[key] : null } // ----------------------------------------------------------------------------- function findIndex (array, cb, context) { cb = _computeOptimizedCb(cb, context) var key = _indexFinder(array, cb, context) return key != null ? key : -1 } // ----------------------------------------------------------------------------- function formatElapsedTime (seconds) { seconds = parseInt(seconds, 10) //s, m, h, d, W, M, Y //1, 60, 3600, 86400, 604800, 2592000, 31104000 var y = Math.floor(seconds / 31104000) if(y > 0) return y+ ' years' var M = Math.floor(seconds / 2592000) if(M > 0) return M+' months' var w = Math.floor(seconds / 604800) if(w>0) return w+' week'; var d = Math.floor(seconds / 86400) if(d>0) return d+' days' var h = Math.floor(seconds / 3600) var m = Math.floor((seconds - h * 3600) / 60) var s = seconds - h * 3600 - m * 60 if (h < 10 && h > 0) { h = '0' + h } if (m < 10) { m = '0' + m } if (s < 10) { s = '0' + s } return (h === 0 ? '' : h + ':') + m + ':' + s } function formatDuration (seconds) { seconds = parseInt(seconds, 10) //s, m, h, d, W, M, Y //1, 60, 3600, 86400, 604800, 2592000, 31104000 var y = Math.floor(seconds / 31104000) if(y > 0) //: '%1 year' return qsTr('formatYears', '', y).arg(y) var M = Math.floor(seconds / 2592000) if(M > 0) //: '%1 month' return qsTr('formatMonths', '', M).arg(M) var w = Math.floor(seconds / 604800) if(w>0) //: '%1 week' return qsTr('formatWeeks', '', w).arg(w) var d = Math.floor(seconds / 86400) if(d>0) //: '%1 day' return qsTr('formatDays', '', d).arg(d) var h = Math.floor(seconds / 3600) var m = Math.floor((seconds - h * 3600) / 60) var s = seconds - h * 3600 - m * 60 //: '%1 hour' return (h > 0 ? qsTr('formatHours', '', h).arg(h): '') //: '%1 minute' + (m > 0 ? (h > 0 ? ', ' : '') +qsTr('formatMinutes', '', m).arg(m): '') //: '%1 second' + (s > 0 ? (h> 0 || m > 0 ? ', ' : '') +qsTr('formatSeconds', '', s).arg(s): '') } function buildDate(date, time){ var dateTime = new Date() dateTime.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()) dateTime.setHours(time.getHours()) dateTime.setMinutes(time.getMinutes()) dateTime.setSeconds(time.getSeconds()) return dateTime } function equalDate(date1, date2){ return date1.getFullYear() == date2.getFullYear() && date1.getMonth() == date2.getMonth() && date1.getDate() == date2.getDate() } function fromUTC(date){ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds())); } // return EXACTLY what date has been set (not take account of Locale timezones, eg. Date(2000,0,1) will print january and not december if timezone lead there.) // Use this function for toLocaleString/toLocaleDateString or other function exactDate(date) { var timeOffset = date.getTimezoneOffset() * 60000 var exactDate = new Date(date.valueOf() - timeOffset) return exactDate } // ----------------------------------------------------------------------------- function formatSize (size) { var units = ['KB', 'MB', 'GB', 'TB'] var unit = 'B' if (size == null) { size = 0 } var length = units.length for (var i = 0; size >= 1024 && i < length; i++) { unit = units[i] size /= 1024 } return parseFloat(size.toFixed(2)) + unit } // ----------------------------------------------------------------------------- // Generate a random number in the [min, max[ interval. // Uniform distrib. function genRandomNumber (min, max) { return Math.random() * (max - min) + min } // ----------------------------------------------------------------------------- // Generate a random number between a set of intervals. // The `intervals` param must be orderer like this: // `[ [ 1, 4 ], [ 8, 16 ], [ 22, 25 ] ]` function genRandomNumberBetweenIntervals (intervals) { if (intervals.length === 1) { return genRandomNumber(intervals[0][0], intervals[0][1]) } // Compute the intervals size. var size = 0 intervals.forEach(function (interval) { size += interval[1] - interval[0] }) // Generate a value in the interval: `[0, size[` var n = genRandomNumber(0, size) // Map the value in the right interval. n += intervals[0][0] for (var i = 0; i < intervals.length - 1; i++) { if (n < intervals[i][1]) { break } n += intervals[i + 1][0] - intervals[i][1] } return n } // ----------------------------------------------------------------------------- // Returns the extension of a filename. function getExtension (str) { var index = str.lastIndexOf('.') if (index === -1) { return '' } return str.slice(index + 1) } // ----------------------------------------------------------------------------- // Test if a value is included in an array or object. function includes (obj, value, startIndex) { obj = ensureArray(obj) if (startIndex == null) { startIndex = 0 } var length = obj.length for (var i = startIndex; i < length; i++) { if ( value === obj[i] || // Check `NaN`. (value !== value && obj[i] !== obj[i]) ) { return true } } return false } // ----------------------------------------------------------------------------- function isArray (array) { return (array instanceof Array) } // ----------------------------------------------------------------------------- function isFunction (func) { return typeof func === 'function' } // ----------------------------------------------------------------------------- function isInteger (integer) { return integer === parseInt(integer, 10) } // ----------------------------------------------------------------------------- function isObject (object) { return object !== null && typeof object === 'object' } // ----------------------------------------------------------------------------- function isString (string) { return typeof string === 'string' || string instanceof String } // ----------------------------------------------------------------------------- // Convert a snake_case string to a lowerCamelCase string. function snakeToCamel (s) { return s.replace(/(\_\w)/g, function (matches) { return matches[1].toUpperCase() }) } // ----------------------------------------------------------------------------- // Test if a string starts by a given string. function startsWith (str, searchStr) { if (searchStr == null) { searchStr = '' } return str.slice(0, searchStr.length) === searchStr } // ----------------------------------------------------------------------------- // Invoke a `cb` function with each value of the interval: `[0, n[`. // Return a mapped array created with the returned values of `cb`. function times (n, cb, context) { var arr = Array(Math.max(0, n)) cb = _computeOptimizedCb(cb, context, 1) for (var i = 0; i < n; i++) { arr[i] = cb(i) } return arr } // ----------------------------------------------------------------------------- function unscapeHtml (str) { return str.replace(/&/g, '&') .replace(//g, '\u2063>') .replace(/"/g, '"') .replace(/'/g, ''') } // ----------------------------------------------------------------------------- function write (fileName, text) { // TODO: Deal with async. var request = new XMLHttpRequest() request.open('PUT', getUriFromSystemPath(fileName), false) request.send(text) } function computeAvatarSize (container, maxSize, ratio) { var height = container.height * ratio var width = container.width * ratio var size = height < maxSize && height > 0 ? height : maxSize return size < width ? size : width } // ----------------------------------------------------------------------------- function openCodecOnlineInstallerDialog (window, codecInfo, cb) { var VideoCodecsModel = Linphone.VideoCodecsModel window.attachVirtualWindow(buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('downloadCodecDescription') .replace('%1', codecInfo.mime) .replace('%2', codecInfo.encoderDescription) }, function (status) { if (status) { window.attachVirtualWindow(buildLinphoneDialogUri('OnlineInstallerDialog'), { downloadUrl: codecInfo.downloadUrl, extract: true, installFolder: VideoCodecsModel.codecsFolder, installName: codecInfo.installName, mime: codecInfo.mime, checksum: codecInfo.checksum }, function (status) { if (status) { VideoCodecsModel.reload() } if (cb) { cb(window) } }) } else if (cb) { cb(window) } }) } function printObject(o) { var out = ''; for (var p in o) { out += p + ': ' + o[p] + '\n'; } return out; } linphone-desktop-5.0.2/linphone-app/ui/scripts/Utils/utils.spec.qml000066400000000000000000000261501434616504300254150ustar00rootroot00000000000000import QtQuick 2.7 import QtTest 1.1 // Explicit import, `utils.js` is not accessible in resources file // when tests are executed. import './utils.js' as Utils // ============================================================================= TestCase { id: testCase // =========================================================================== // QML helpers. // =========================================================================== function test_clearTimeout_data () { return [ { time: 0 }, { time: 100 } ] } function test_clearTimeout (data) { var failed = false var timeout = Utils.setTimeout(testCase, data.time, function () { failed = true }) if (failed) { fail('`setTimeout` callback was called before `wait`.') } Utils.clearTimeout(timeout) wait(100) if (failed) { fail('`setTimeout` callback was called after `wait`.') } } // --------------------------------------------------------------------------- function test_qmlTypeof_data () { return [ { component: 'import QtQuick 2.7; ListModel {}', result: true, type: 'QQmlListModel' }, { component: 'import QtQuick 2.7; ListView {}', result: true, type: 'QQuickListView' }, { component: 'import QtQuick 2.7; MouseArea {}', result: true, type: 'QQuickMouseArea' }, { component: 'import QtQuick 2.7; import QtQuick.Window 2.2; Window {}', result: true, type: 'QQuickWindowQmlImpl' } ] } function test_qmlTypeof (data) { var object = Qt.createQmlObject(data.component, testCase) verify(object) compare(Utils.qmlTypeof(object, data.type), data.result) } // --------------------------------------------------------------------------- function test_setTimeoutWithoutParent () { try { Utils.setTimeout(null, 0, function () { fail('`setTimeout` was called without parent.') }) } catch (e) { compare(e, 'Error: Qt.createQmlObject(): Missing parent object') } } function test_setTimeout_data () { return [ { time: 0 }, { time: 100 } ] } function test_setTimeout (data) { var failed = true Utils.setTimeout(testCase, data.time, function () { failed = false }) if (!failed) { fail('`setTimeout` callback was called before `wait`.') } wait(200) if (failed) { fail('`setTimeout` failed because callback it was not called in due course.') } } // =========================================================================== // GENERIC. // =========================================================================== function test_ensureArray_data () { return [ { input: [ 1, 2, 3 ], output: [ 1, 2, 3 ] }, { input: { toto: 4, ro: 5 }, output: [ 4, 5 ] }, { input: new Object(), output: [] }, { input: new Array(), output: [] }, { input: { a: 0, b: 1, c: 0 }, output: [ 0, 1, 0 ] } ] } function test_ensureArray (data) { // Use `sort` because transform a object in array hasn't a // guarantee ordering. compare(Utils.ensureArray(data.input).sort(), data.output.sort()) } // --------------------------------------------------------------------------- function test_execAll_data () { return [ { cb: function () { return 'failed' }, regex: /x/g, text: '', output: [] }, { cb: function (c, valid) { return !valid && c }, regex: /x/g, text: 'n', output: [ 'n' ] }, { cb: function (c, valid) { return valid && String.fromCharCode(c.charCodeAt(0) + 1) }, regex: /[a-z]/g, text: 'abcdefgh', output: [ 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ] }, { cb: function (n, valid) { return !valid ? '*' : Math.abs(n) }, regex: /\d+/g, text: 'abc 5 def -4 ghi 452-12', output: [ '*', 5, '*', 4, '*', 452, '*', 12 ] }, { regex: /candy-shop/g, text: 'candy-hop candy-shop cndy-shop candyshop', output: [ 'candy-hop ', 'candy-shop', ' cndy-shop candyshop' ] }, { cb: function (text, valid) { return valid ? text + ' Batman!' : text }, regex: /(?:na)+\.\.\./g, text: 'Nananana. Nanananananananana... Nanananananananana... Nananananananananananana...', output: [ 'Nananana. Na', 'nananananananana... Batman!', ' Na', 'nananananananana... Batman!', ' Na', 'nanananananananananana... Batman!' ] } ] } function test_execAll (data) { compare(Utils.execAll(data.regex, data.text, data.cb), data.output) } // --------------------------------------------------------------------------- function test_genRandomNumber_data () { return [ { min: 42, max: 3600 }, { min: 10, max: 100 }, { min: 58, max: 61 }, { min: 2, max: 3 } ] } function test_genRandomNumber (data) { Utils.times(10, function () { var n = Utils.genRandomNumber(data.min, data.max) compare(n >= data.min && n < data.max, true) }) } function test_genRandomNumberId () { compare(Utils.genRandomNumber(42, 42), 42) } // --------------------------------------------------------------------------- function test_genRandomNumberBetweenIntervals_data () { return [ { intervals: [ [ 1, 5 ] ] }, { intervals: [ [ 8, 9 ], [ 10, 15 ] ] }, { intervals: [ [ 1, 4 ], [ 8, 16 ], [ 22, 25 ] ] }, { intervals: [ [ 11, 12 ], [ 50, 80 ], [ 92, 93 ], [ 1000, 1100 ] ] }, { intervals: [ [ -5, -2 ] ] }, { intervals: [ [ -5, -2 ], [ 12, 14 ] ] }, { intervals: [ [ -127, -111 ], [ -35, -14 ], [ 1256, 1270 ], [ 10000, 10020 ] ] } ] } function test_genRandomNumberBetweenIntervals (data) { var intervals = data.intervals Utils.times(10, function () { var n = Utils.genRandomNumberBetweenIntervals(intervals) var soFarSoGood = false for (var i = 0; i < intervals.length; i++) { if (n >= intervals[i][0] && n < intervals[i][1]) { soFarSoGood = true break } } compare( soFarSoGood, true, 'The generated number cannot be found in a interval.' ) }) } // --------------------------------------------------------------------------- function test_getExtension_data () { return [ { input: 'foobar.baz', output: 'baz' }, { input: 'classe.george.abitbol', output: 'abitbol' }, { input: '', output: '' }, { input: 'cotcotcot', output: '' } ] } function test_getExtension (data) { compare(Utils.getExtension(data.input), data.output) } // --------------------------------------------------------------------------- function test_includes_data () { return [ // Test arrays. { input: [], value: 0, output: false }, { input: [ 1, 2, 3 ], value: 4, output: false }, { input: [ 6 ], value: 6, output: true }, { input: [ 4, 8, 'foo' ], value: 8, output: true }, { input: [ 12, NaN, 47 ], value: NaN, output: true }, { input: Array(1), value: undefined, output: true }, { input: [ 'a', 'b', 'c' ], startIndex: 1, value: 'a', output: false }, { input: [ 6, 5, 4, 9 ], startIndex: 3, value: 9, output: true }, // Test objects. { input: {}, value: 0, output: false }, { input: { a: 1, b: 2, c: 3 }, value: 4, output: false }, { input: { a: 6 }, value: 6, output: true }, { input: { a: 4, b: 8, c: 'foo' }, value: 8, output: true }, { input: { a: 12, b: NaN, c: 47 }, value: NaN, output: true }, { input: new Object(), value: undefined, output: false }, { input: { a: 'a', b: 'b', c: 'c' }, startIndex: 1, value: 'a', output: false }, { input: { a: 6, b: 5, c: 4, d: 9 }, startIndex: 3, value: 9, output: true }, ] } function test_includes (data) { compare( Utils.includes(data.input, data.value, data.startIndex), data.output ) } // --------------------------------------------------------------------------- function test_isArray_data () { return [ { input: [], output: true }, { input: {}, output: false }, { input: [ 6 ], output: true }, { input: /bar/, output: false }, { input: new Error, output: false }, { input: true, output: false }, { input: 42, output: false }, { input: new Array(), output: true }, { input: [ 15, new Date(), 'ij' ], output: true } ] } function test_isArray (data) { compare(Utils.isArray(data.input), data.output) } // --------------------------------------------------------------------------- function test_isString_data () { return [ { input: 'foo', output: true }, { input: Object('bar'), output: true }, { input: [ 0 ], output: false }, { input: /baz/, output: false }, { input: new Error, output: false }, { input: true, output: false }, { input: 42, output: false } ] } function test_isString (data) { compare(Utils.isString(data.input), data.output) } // --------------------------------------------------------------------------- function test_snakeToCamel_data () { return [ { input: 'foo_bar', output: 'fooBar' }, { input: 'george_abitbol', output: 'georgeAbitbol' }, { input: 'billTremendousAndHubert', output: 'billTremendousAndHubert' }, { input: 'foo_bAr_BAZ', output: 'fooBArBAZ' } ] } function test_snakeToCamel (data) { compare(Utils.snakeToCamel(data.input), data.output) } // --------------------------------------------------------------------------- function test_startsWith_data () { return [ { string: 'foo', searchStr: undefined, result: true }, { string: 'bar', searchStr: null, result: true }, { string: 'abitbol', searchStr: 'abitboll', result: false }, { string: '', searchStr: NaN, result: false }, { string: '', searchStr: '', result: true }, { string: '', searchStr: Infinity, result: false }, { string: '', searchStr: 0, result: false }, { string: 'george', searchStr: 'geo', result: true }, { string: 'george', searchStr: 'george', result: true }, { string: 'george', searchStr: 'georg', result: true }, { string: 'ruby', searchStr: '', result: true } ] } function test_startsWith (data) { compare( Utils.startsWith(data.string, data.searchStr), data.result ) } // --------------------------------------------------------------------------- function test_times1_data () { return [ { cb: function (n) { return n * 2 }, n: 10, output: [ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18 ] }, { cb: function (n) { return n % 2 }, n: 6, output: [ 0, 1, 0, 1, 0, 1 ] } ] } function test_times1 (data) { compare(Utils.times(data.n, data.cb), data.output) } function test_times2 () { var sum = 0 Utils.times(5, function (i) { sum += (i + 1) }) compare(sum, 15) } } linphone-desktop-5.0.2/linphone-app/ui/views/000077500000000000000000000000001434616504300211535ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/000077500000000000000000000000001434616504300216735ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/000077500000000000000000000000001434616504300227315ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/AbstractStartingCall.qml000066400000000000000000000071251434616504300275240ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import UtilsCpp 1.0 import App.Styles 1.0 // ============================================================================= Rectangle { property var call default property alias _actionArea: actionArea.data property var _sipAddressObserver: SipAddressesModel.getSipAddressObserver(call.fullPeerAddress, call.fullLocalAddress) property alias showKeypad :telKeypadButton.visible Component.onDestruction: _sipAddressObserver=null// Need to set it to null because of not calling destructor if not. // --------------------------------------------------------------------------- color: CallStyle.backgroundColor ColumnLayout { anchors { fill: parent topMargin: CallStyle.header.topMargin } spacing: 0 // ------------------------------------------------------------------------- // Contact & Call type (animation). // ------------------------------------------------------------------------- Column { Layout.fillWidth: true spacing: CallStyle.header.spacing ContactDescription { id: contactDescription anchors.horizontalCenter: parent.horizontalCenter height: CallStyle.header.contactDescription.height horizontalTextAlignment: Text.AlignHCenter subtitleText: SipAddressesModel.cleanSipAddress(call.peerAddress) titleText: _sipAddressObserver ? UtilsCpp.getDisplayName(_sipAddressObserver.peerAddress) : '' width: contentWidth } BusyIndicator { anchors.horizontalCenter: parent.horizontalCenter color: CallStyle.header.busyIndicator.color height: CallStyle.header.busyIndicator.height width: CallStyle.header.busyIndicator.width visible: call.isOutgoing } } // ------------------------------------------------------------------------- // Contact visual. // ------------------------------------------------------------------------- Item { id: container Layout.fillHeight: true Layout.fillWidth: true Layout.margins: CallStyle.container.margins Avatar { id: avatar function _computeAvatarSize () { var height = container.height var width = container.width var size = height < CallStyle.container.avatar.maxSize && height > 0 ? height : CallStyle.container.avatar.maxSize return size < width ? size : width } anchors.centerIn: parent backgroundColor: CallStyle.container.avatar.backgroundColor image: _sipAddressObserver && _sipAddressObserver.contact && _sipAddressObserver.contact.vcard.avatar username: contactDescription.username height: _computeAvatarSize() width: height } } // ------------------------------------------------------------------------- // Buttons. // ------------------------------------------------------------------------- Item { id: actionArea Layout.fillWidth: true Layout.preferredHeight: CallStyle.actionArea.height } } ActionButton { id:telKeypadButton isCustom: true backgroundRadius: 90 colorSet : CallStyle.buttons.telKeyad visible:false onClicked: telKeypad.visible = !telKeypad.visible anchors.left:parent.left anchors.top:parent.top anchors.leftMargin: CallStyle.header.leftMargin anchors.topMargin: CallStyle.header.topMargin } // --------------------------------------------------------------------------- // TelKeypad. // --------------------------------------------------------------------------- TelKeypad { id: telKeypad showHistory:true call: parent.call visible: SettingsModel.showTelKeypadAutomatically y: 50 } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/CallsWindow.js000066400000000000000000000106651434616504300255250ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `CallsWindow.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function handleClosing (close) { var callsList = Linphone.CallsListModel window.detachVirtualWindow() if (callsList.getRunningCallsNumber() === 0) { return } window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('acceptClosingDescription') }, function (status) { if (status) { callsList.terminateAllCalls() window.close() } }) close.accepted = false } // ----------------------------------------------------------------------------- function openCallSipAddress () { window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/CallSipAddress.qml')) } function openConferenceManager (params, exitHandler) { window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ConferenceManager.qml'), params, exitHandler) } function openWaitingRoom(model){ calls.refreshCall() if(window.conferenceInfoModel && middlePane.sourceComponent == waitingRoom) middlePane.item.reset() window.conferenceInfoModel = model } // ----------------------------------------------------------------------------- // Used to get Component based from Call Status function getContent (call, conferenceInfoModel) { if (call == null) { if(conferenceInfoModel) { return waitingRoom } else{ return null } } var status = call.status if (status == null) { return calls.conferenceModel.count > 0 ? conference : null } var CallModel = Linphone.CallModel if (status === CallModel.CallStatusIncoming) { return null; } if( window.conferenceInfoModel != call.conferenceInfoModel) { Qt.callLater(function(){window.conferenceInfoModel = call.conferenceInfoModel}) return middlePane.sourceComponent // unchange. Wait for later decision on conference model (avoid binding loop on sourceComponent) }else{ if(call.isConference){ return incall } if (status === CallModel.CallStatusOutgoing || (status === CallModel.CallStatusEnded && call.callError != '' )) { return waitingRoom } return incall } } // ----------------------------------------------------------------------------- function handleCallTransferAsked (call) { if (!call) { return } if (call.transferAddress !== '') { console.debug('Attended transfer to call ' + call.transferAddress) call.transferToAnother(call.transferAddress) return } window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/CallTransfer.qml'), { call: call, attended: false }) } function handleCallAttendedTransferAsked (call) { if (!call) { return } if (call.transferAddress !== '') { console.debug('Attended transfer to call ' + call.transferAddress) call.transferToAnother(call.transferAddress) return } window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/CallTransfer.qml'), { call: call, attended: true }) } function windowMustBeClosed () { return Linphone.CallsListModel.rowCount() === 0 && !window.virtualWindowVisible && middlePane.sourceComponent != waitingRoom } function tryToCloseWindow () { if (windowMustBeClosed()) { // Workaround, it's necessary to use a timeout because at last call termination // a segfault is emit in `QOpenGLContext::functions() const ()`. Utils.setTimeout(window, 0, function () { windowMustBeClosed() && window.close() }) } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/CallsWindow.qml000066400000000000000000000217051434616504300256770ustar00rootroot00000000000000import QtGraphicalEffects 1.0 import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 import 'CallsWindow.js' as Logic // ============================================================================= Window { id: window // --------------------------------------------------------------------------- // `{}` is a workaround to avoid `TypeError: Cannot read property...` when calls list is empty property CallModel call: calls.selectedCall onCallChanged: if(!call && conferenceInfoModel) {conferenceInfoModel = null} /* ?calls.selectedCall:{ callError: '', isOutgoing: true, recording: false, localSas: '', peerAddress: '', localAddress: '', type: false, updating: true, videoEnabled: false, chatRoomModel:null }); */ property ConferenceInfoModel conferenceInfoModel readonly property bool chatIsOpened: !rightPaned.isClosed() readonly property bool callsIsOpened: !mainPaned.isClosed() readonly property bool haveChat: rightPane.sourceComponent // --------------------------------------------------------------------------- function openChat () { rightPaned.open() } function closeChat () { rightPaned.close() } function endOfProcess(exitValue){ window.detachVirtualWindow(); if(exitValue == 0 && calls.count == 0 && middlePane.sourceComponent != waitingRoom) { close(); } } function openConferenceManager (params) { Logic.openConferenceManager(params, endOfProcess) } function setHeight (height) { window.height = (Window.screen && height > Window.screen.desktopAvailableHeight) ? Window.screen.desktopAvailableHeight : height } // --------------------------------------------------------------------------- minimumHeight: CallsWindowStyle.minimumHeight minimumWidth: CallsWindowStyle.minimumWidth title: qsTr('callsTitle') // --------------------------------------------------------------------------- onClosing: Logic.handleClosing(close) onDetachedVirtualWindow: Logic.tryToCloseWindow() // --------------------------------------------------------------------------- Paned { id: mainPaned anchors.fill: parent defaultChildAWidth: CallsWindowStyle.callsList.defaultWidth defaultClosed: true maximumLeftLimit: CallsWindowStyle.callsList.maximumWidth minimumLeftLimit: CallsWindowStyle.callsList.minimumWidth hideSplitter: !window.callsIsOpened && middlePane.sourceComponent == incall || middlePane.sourceComponent == waitingRoom // ------------------------------------------------------------------------- // Calls list. // ------------------------------------------------------------------------- childA: Rectangle { id: leftPaned anchors.fill: parent color: CallsWindowStyle.callsList.color ColumnLayout { anchors.fill: parent spacing: 0 Item { Layout.fillWidth: true Layout.preferredHeight: CallsWindowStyle.callsList.header.height visible: SettingsModel.outgoingCallsEnabled || SettingsModel.conferenceEnabled LinearGradient { anchors.fill: parent start: Qt.point(0, 0) end: Qt.point(0, height) gradient: Gradient { GradientStop { position: 0.0; color: CallsWindowStyle.callsList.header.color1 } GradientStop { position: 1.0; color: CallsWindowStyle.callsList.header.color2 } } } RowLayout{ anchors.fill: parent ActionBar { Layout.leftMargin: CallsWindowStyle.callsList.header.leftMargin Layout.alignment: Qt.AlignVCenter iconSize: CallsWindowStyle.callsList.header.iconSize ActionButton { isCustom: true backgroundRadius: 4 colorSet: CallsWindowStyle.callsList.newCall visible: SettingsModel.outgoingCallsEnabled onClicked: Logic.openCallSipAddress() } ActionButton { isCustom: true backgroundRadius: 4 colorSet: CallsWindowStyle.callsList.mergeConference visible: SettingsModel.conferenceEnabled enabled: CallsListModel.canMergeCalls onClicked: { CallsListModel.mergeAll() } } } Item{// Spacer Layout.fillWidth: true } ActionButton { Layout.alignment: Qt.AlignVCenter Layout.rightMargin: 15 isCustom: true backgroundRadius: 4 colorSet: CallsWindowStyle.callsList.closeButton onClicked: mainPaned.close() } } } Calls { id: calls Layout.fillHeight: true Layout.fillWidth: true conferenceModel: ConferenceProxyModel {} model: CallsListProxyModel {} } } } // ------------------------------------------------------------------------- // Content. // ------------------------------------------------------------------------- childB: Paned { id: rightPaned anchors.fill: parent closingEdge: Qt.RightEdge defaultClosed: true minimumLeftLimit: CallsWindowStyle.call.minimumWidth minimumRightLimit: CallsWindowStyle.chat.minimumWidth resizeAInPriority: true hideSplitter: !window.chatIsOpened && (!middlePane.sourceComponent || middlePane.sourceComponent == incall || !rightPane.sourceComponent) // ----------------------------------------------------------------------- Component { id: incomingCall IncomingCall { call: window.call } } Component { id: chat Chat { anchors.fill: parent proxyModel: ChatRoomProxyModel { Component.onCompleted: { if (chatRoomModel && (!chatRoomModel.haveEncryption && !SettingsModel.standardChatEnabled || chatRoomModel.haveEncryption && !SettingsModel.secureChatEnabled)) { setEntryTypeFilter(ChatRoomModel.CallEntry | ChatRoomModel.NoticeEntry) } } chatRoomModel: window.call.chatRoomModel peerAddress: window.call.peerAddress fullPeerAddress: window.call.fullPeerAddress fullLocalAddress: window.call.fullLocalAddress localAddress: window.call.localAddress isCall: true // Used for cleaning data if there are no call associated to this chat room. } Connections { target: SettingsModel onStandardChatEnabledChanged: if(!chatRoomModel.haveEncryption) proxyModel.setEntryTypeFilter(status ? ChatRoomModel.GenericEntry : ChatRoomModel.CallEntry | ChatRoomModel.NoticeEntry) onSecureChatEnabledChanged: if(chatRoomModel.haveEncryption) proxyModel.setEntryTypeFilter(SettingsModel.secureChatEnabled ? ChatRoomModel.GenericEntry : ChatRoomModel.CallEntry | ChatRoomModel.NoticeEntry) } } } Component { id: conference Conference { conferenceModel: calls.conferenceModel } } Component { id: waitingRoom WaitingRoom{ conferenceInfoModel: window.conferenceInfoModel onCancel: { endOfProcess(0) window.conferenceInfoModel = null calls.refreshLastCall() } enabled: window.visible callModel: window.call } } Component { id: incall Incall { callModel: window.call enabled: window.visible listCallsOpened: window.callsIsOpened onOpenListCallsRequest: mainPaned.open() onIsFullScreenChanged: if(isFullScreen){ window.hide() }else{ window.show() } } } // ----------------------------------------------------------------------- childA: Loader { id: middlePane anchors.fill: parent sourceComponent: Logic.getContent(window.call, window.conferenceInfoModel) property var lastComponent: null onSourceComponentChanged: { if(lastComponent != sourceComponent){ if( sourceComponent == waitingRoom) mainPaned.close() rightPaned.childAItem.update() if(!sourceComponent && calls.count == 0) window.close() lastComponent = sourceComponent } }// Force update when loading a new Content. It's just to be sure active: window.call || window.conferenceInfoModel } childB: Loader { id: rightPane anchors.fill: parent sourceComponent: window.call && window.call.chatRoomModel ? chat : null onSourceComponentChanged: if(!sourceComponent) window.closeChat() } } } // --------------------------------------------------------------------------- // Handle transfer. // Handle count changed. Not on proxy model!!! // --------------------------------------------------------------------------- Connections { target: CallsListModel onCallTransferAsked: Logic.handleCallTransferAsked(callModel) onCallAttendedTransferAsked: Logic.handleCallAttendedTransferAsked(callModel) onCallConferenceAsked: Logic.openWaitingRoom(conferenceInfoModel) onRowsRemoved: Logic.tryToCloseWindow() } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/Conference.qml000066400000000000000000000157071434616504300255250ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import UtilsCpp 1.0 import App.Styles 1.0 // ============================================================================= Rectangle { id: conference property var conferenceModel // --------------------------------------------------------------------------- color: CallStyle.backgroundColor // --------------------------------------------------------------------------- ColumnLayout { anchors { fill: parent topMargin: CallStyle.header.topMargin } spacing: 0 // ------------------------------------------------------------------------- // Conference info. // ------------------------------------------------------------------------- Item { id: info Layout.fillWidth: true Layout.leftMargin: CallStyle.header.leftMargin Layout.rightMargin: CallStyle.header.rightMargin Layout.preferredHeight: ConferenceStyle.description.height ActionBar { id: leftActions anchors.left: parent.left iconSize: CallStyle.header.iconSize } Text { id: conferenceDescription anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter text: qsTr('conferenceTitle') color: ConferenceStyle.description.color font { bold: true pointSize: ConferenceStyle.description.pointSize } height: parent.height width: parent.width - rightActions.width - leftActions.width - ConferenceStyle.description.width } // ----------------------------------------------------------------------- // Video actions. // ----------------------------------------------------------------------- ActionBar { id: rightActions anchors.right: parent.right iconSize: CallStyle.header.buttonIconSize ActionButton { isCustom: true backgroundRadius: 90 property bool recording: conference.conferenceModel.recording colorSet: recording ? CallStyle.buttons.recordOn : CallStyle.buttons.recordOff visible: SettingsModel.callRecorderEnabled onClicked: !recording ? conference.conferenceModel.startRecording() : conference.conferenceModel.stopRecording() } } } // ------------------------------------------------------------------------- // Contacts visual. // ------------------------------------------------------------------------- Item { id: container Layout.fillWidth: true Layout.fillHeight: true Layout.margins: CallStyle.container.margins GridView { id: grid anchors.fill: parent cellHeight: ConferenceStyle.grid.cell.height cellWidth: ConferenceStyle.grid.cell.width model: conference.conferenceModel delegate: Item { height: grid.cellHeight width: grid.cellWidth Column { readonly property string sipAddress: $modelData.peerAddress property var _sipAddressObserver : SipAddressesModel.getSipAddressObserver($modelData.peerAddress, $modelData.localAddress) anchors { fill: parent margins: ConferenceStyle.grid.spacing } spacing: ConferenceStyle.grid.cell.spacing Component.onDestruction: _sipAddressObserver=null// Need to set it to null because of not calling destructor if not. ContactDescription { id: contactDescription height: ConferenceStyle.grid.cell.contactDescription.height width: parent.width horizontalTextAlignment: Text.AlignHCenter subtitleText: SipAddressesModel.cleanSipAddress(parent.sipAddress) titleText: parent._sipAddressObserver ? UtilsCpp.getDisplayName(parent._sipAddressObserver.peerAddress) : '' } IncallAvatar { readonly property int size: Math.min( parent.width, parent.height - contactDescription.height - parent.spacing ) anchors.horizontalCenter: parent.horizontalCenter call: $modelData onCallChanged: if(!call) conference.conferenceModel.invalidate() height: size width: size BusyIndicator { anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } color: CallStyle.header.busyIndicator.color height: CallStyle.header.busyIndicator.height width: CallStyle.header.busyIndicator.width visible: $modelData && $modelData.status === CallModel.CallStatusOutgoing } } } VuMeter { anchors { bottom: parent.bottom left: parent.left leftMargin: ConferenceStyle.grid.spacing bottomMargin: ConferenceStyle.grid.spacing } enabled:!$modelData.speakerMuted Timer { interval: 50 repeat: true running: true onTriggered: parent.value = $modelData.speakerVu } } MouseArea{ anchors.fill:parent onClicked:$modelData.toggleSpeakerMute() } } } } // ------------------------------------------------------------------------- // Action Buttons. // ------------------------------------------------------------------------- Item { Layout.fillWidth: true Layout.preferredHeight: CallStyle.actionArea.height GridLayout { anchors { left: parent.left leftMargin: CallStyle.actionArea.leftButtonsGroupMargin verticalCenter: parent.verticalCenter } columns: incall.width < CallStyle.actionArea.lowWidth ? 2 : 4 rowSpacing: ActionBarStyle.spacing Row { spacing: CallStyle.actionArea.vu.spacing VuMeter { Timer { interval: 50 repeat: true running: micro.enabled onTriggered: parent.value = conference.conferenceModel.microVu } enabled: micro.enabled } ActionSwitch { id: micro isCustom: true backgroundRadius: 90 colorSet: enabled ? CallStyle.buttons.microOn : CallStyle.buttons.microOff enabled: !conference.conferenceModel.microMuted onClicked: conference.conferenceModel.microMuted = !conference.conferenceModel.microMuted } } } ActionBar { anchors { right: parent.right rightMargin: CallStyle.actionArea.rightButtonsGroupMargin verticalCenter: parent.verticalCenter } iconSize: CallStyle.actionArea.iconSize ActionButton { isCustom: true backgroundRadius: 90 colorSet: !conference.conferenceModel.isInConf ? CallStyle.buttons.play : CallStyle.buttons.pause onClicked: { var model = conference.conferenceModel if (model.isInConf) { model.leave() } else { model.join() } } } ActionButton { isCustom: true backgroundRadius: 90 colorSet: CallStyle.buttons.hangup onClicked: conference.conferenceModel.terminate() } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/Dialogs/000077500000000000000000000000001434616504300243135ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/Dialogs/CallSipAddress.qml000066400000000000000000000046711434616504300276730ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 // ============================================================================= DialogPlus { buttons: [ TextButtonA { text: qsTr('cancel') onClicked: exit(0) } ] buttonsAlignment: Qt.AlignCenter descriptionText: qsTr('callSipAddressDescription') height: CallSipAddressStyle.height + 30 width: CallSipAddressStyle.width // --------------------------------------------------------------------------- ColumnLayout { anchors.fill: parent spacing: 0 // ------------------------------------------------------------------------- // Address selector. // ------------------------------------------------------------------------- Item { Layout.fillHeight: true Layout.fillWidth: true ColumnLayout { anchors.fill: parent spacing: CallSipAddressStyle.spacing TextField { id: filter Layout.fillWidth: true icon: text == '' ? 'search_custom' : 'close_custom' overwriteColor: CallSipAddressStyle.searchField.color onTextChanged: sipAddressesModel.setFilter(text) } ScrollableListViewField { Layout.fillHeight: true Layout.fillWidth: true SipAddressesView { anchors.fill: parent function launchVideoCall(sipAddress){ CallsListModel.launchVideoCall(sipAddress) exit(1) } function launchAudioCall(sipAddress){ CallsListModel.launchAudioCall(sipAddress, "") exit(1) } actions: [{ colorSet: CallSipAddressStyle.videoCall, secure:0, visible:true, handler: function (entry) { launchVideoCall(entry.sipAddress) }, visible: SettingsModel.videoSupported && SettingsModel.showStartVideoCallButton, handlerSipAddress: function(sipAddress) { launchVideoCall(sipAddress) } }, { colorSet: CallSipAddressStyle.call, secure:0, visible:true, handler: function (entry) { launchAudioCall(entry.sipAddress) }, handlerSipAddress: function(sipAddress) { launchAudioCall(sipAddress) } }] genSipAddress: filter.text model: SearchSipAddressesModel { id: sipAddressesModel } onEntryClicked: actions[0].handlerSipAddress(entry.sipAddress) } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/Dialogs/CallTransfer.qml000066400000000000000000000054121434616504300274100ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 // ============================================================================= DialogPlus { id: callTransfer // --------------------------------------------------------------------------- property var call property bool attended: false // --------------------------------------------------------------------------- buttons: [ TextButtonA { text: qsTr('cancel') onClicked: exit(0) } ] buttonsAlignment: Qt.AlignCenter descriptionText: qsTr('callTransferDescription') height: CallTransferStyle.height + 30 width: CallTransferStyle.width onCallChanged: !call && exit(0) // --------------------------------------------------------------------------- ColumnLayout { anchors.fill: parent spacing: 0 // ------------------------------------------------------------------------- // Contact. // ------------------------------------------------------------------------- Contact { Layout.fillWidth: true entry: SipAddressesModel.getSipAddressObserver(call ? call.fullPeerAddress : '', call ? call.fullLocalAddress : '') Component.onDestruction: entry=null// Need to set it to null because of not calling destructor if not. } // ------------------------------------------------------------------------- // Address selector. // ------------------------------------------------------------------------- Item { Layout.fillHeight: true Layout.fillWidth: true ColumnLayout { anchors.fill: parent spacing: CallTransferStyle.spacing TextField { id: filter Layout.fillWidth: true icon: text == '' ? 'search_custom' : 'close_custom' overwriteColor: CallTransferStyle.searchField.color onTextChanged: sipAddressesModel.setFilter(text) } ScrollableListViewField { Layout.fillHeight: true Layout.fillWidth: true SipAddressesView { anchors.fill: parent function transfer(sipAddress){ if (attended) { var call = CallsListModel.launchAudioCall(sipAddress, callTransfer.call.peerAddress) } else { callTransfer.call.transferTo(sipAddress) } exit(1) } actions: [{ colorSet: CallTransferStyle.transfer, secure: 0, visible: true, handlerSipAddress: function(sipAddress){ transfer(sipAddress) }, handler: function (entry) { transfer(entry.sipAddress) } }] genSipAddress: filter.text model: SearchSipAddressesModel { id: sipAddressesModel } onEntryClicked: actions[0].handlerSipAddress(entry.sipAddress) } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/Dialogs/ConferenceManager.qml000066400000000000000000000105021434616504300303660ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQml 2.12 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 // ============================================================================= DialogPlus { id: conferenceManager readonly property int maxParticipants: 20 readonly property int minParticipants: 1 property ChatRoomModel chatRoomModel // Used to initialize participants property bool autoCall : false buttons: [ TextButtonA { text: qsTr('cancel') onClicked: exit(0) }, TextButtonB { enabled: toAddView.count >= conferenceManager.minParticipants text: qsTr('confirm') onClicked: { conferenceHelperModel.toAdd.update() exit(1) } } ] buttonsAlignment: Qt.AlignCenter descriptionText: qsTr('conferenceManagerDescription') height: ConferenceManagerStyle.height + 30 width: ConferenceManagerStyle.width Timer{ id:delayedExit onTriggered : exit(1) interval:1 } Component.onCompleted: if(chatRoomModel){ conferenceHelperModel.toAdd.addParticipants(chatRoomModel) if(autoCall) { conferenceHelperModel.toAdd.update() visible = false delayedExit.start() } } // --------------------------------------------------------------------------- RowLayout { anchors.fill: parent spacing: 0 // ------------------------------------------------------------------------- // Address selector. // ------------------------------------------------------------------------- Item { Layout.fillHeight: true Layout.fillWidth: true ColumnLayout { anchors.fill: parent spacing: ConferenceManagerStyle.columns.selector.spacing TextField { id: filter Layout.fillWidth: true icon: text == '' ? 'search_custom' : 'close_custom' overwriteColor: ConferenceManagerStyle.searchField.color onTextChanged: conferenceHelperModel.setFilter(text) } ScrollableListViewField { Layout.fillHeight: true Layout.fillWidth: true readOnly: toAddView.count >= conferenceManager.maxParticipants SipAddressesView { anchors.fill: parent function transfer(sipAddress){ conferenceHelperModel.toAdd.addToConference(sipAddress) } actions: [{ colorSet: ConferenceManagerStyle.transfer, secure:0, visible: true, handler: function (entry) { transfer(entry.sipAddress) }, handerSipAddress: function(sipAddress){ transfer(sipAddress) } }] genSipAddress: filter.text model: ConferenceHelperModel { id: conferenceHelperModel } onEntryClicked: actions[0].handerSipAddress(entry.sipAddress) } } } } // ------------------------------------------------------------------------- // Separator. // ------------------------------------------------------------------------- Rectangle { Layout.fillHeight: true Layout.leftMargin: ConferenceManagerStyle.columns.separator.leftMargin Layout.preferredWidth: ConferenceManagerStyle.columns.separator.width Layout.rightMargin: ConferenceManagerStyle.columns.separator.rightMargin color: ConferenceManagerStyle.columns.separator.color } // ------------------------------------------------------------------------- // See and remove selected addresses. // ------------------------------------------------------------------------- ScrollableListViewField { Layout.fillHeight: true Layout.fillWidth: true Layout.topMargin: filter.height + ConferenceManagerStyle.columns.selector.spacing SipAddressesView { id: toAddView anchors.fill: parent function cancel(sipAddress){ model.removeFromConference(sipAddress) } actions: [{ colorSet: ConferenceManagerStyle.cancel, visible:true, secure:0, handler: function (entry) { cancel(entry.sipAddress) }, handlerSipAddress: function(sipAddress){ cancel(sipAddress) } }] model: conferenceHelperModel.toAdd onEntryClicked: actions[0].handlerSipAddress(entry) } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/Incall.js000066400000000000000000000104741434616504300244770ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `Incall.qml` Logic. // ============================================================================= .import DesktopTools 1.0 as DesktopTools .import Linphone 1.0 as Linphone .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function handleCallStatisticsClosed () { // Prevent many clicks on call statistics button. Utils.setTimeout(callQuality, 500, function () { callQuality.enabled = true }) } function handleCameraFirstFrameReceived (width, height) { // Cell phone??? if (height > width) { return } var ratio = container.width / (width / (height / container.height)) var diff = container.height * ratio - container.height if (diff < 0) { return } window.setHeight(window.height + diff) } function handleStatusChanged (status, isFullscreen) { if (status === Linphone.CallModel.CallStatusEnded) { if (isFullscreen) { // Timeout => Avoid dead lock on mac. Utils.setTimeout(window, 0, isFullscreen.exit) } telKeypad.visible = false callStatistics.close() } } function handleVideoRequested (call) { if (window.virtualWindowVisible || !Linphone.SettingsModel.videoSupported) { call.rejectVideoRequest() return } /* // Close dialog after 10s. var timeout = Utils.setTimeout(incall, 10000, function () { call.statusChanged.disconnect(endedHandler) window.detachVirtualWindow() call.rejectVideoRequest() }) */ // Close dialog if call is ended. var endedHandler = function (status) { if (status === Linphone.CallModel.CallStatusEnded) { Utils.clearTimeout(timeout) call.statusChanged.disconnect(endedHandler) window.detachVirtualWindow() } } call.statusChanged.connect(endedHandler) // Ask video to user. window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('acceptVideoDescription'), }, function (status) { //Utils.clearTimeout(timeout) call.statusChanged.disconnect(endedHandler) if (status) { call.acceptVideoRequest() } else { call.rejectVideoRequest() } }) } function makeReadableSecuredString (isSecured, secureString) { if (!isSecured) { return qsTr('callNotSecured') } return qsTr('securedStringFormat').replace('%1', secureString) } function openCallStatistics () { callQuality.enabled = false callStatistics.open() } function openMediaParameters (window, incall) { window.attachVirtualWindow(Utils.buildLinphoneDialogUri('MultimediaParametersDialog'), { call: incall.call }) } // callerId = incall, qmlFile = 'IncallFullscreen.qml' // callerId need to have : _fullscreen and isFullScreen function showFullscreen (window, callerId, qmlFile, position) { callerId.isFullScreen = true if (callerId._fullscreen) { callerId._fullscreen.raise() return } DesktopTools.DesktopTools.screenSaverStatus = false Utils.setTimeout(window, 1, function() { var parameters = { caller: callerId, x:position.x, y:position.y, width:window.width, height:window.height, window:window } callerId._fullscreen = Utils.openWindow(Qt.resolvedUrl(qmlFile), parameters.window, { properties: parameters }, true) if(callerId._fullscreen) { callerId._fullscreen.cameraIsReady = Qt.binding(function(){ return !callerId.cameraIsReady}) callerId._fullscreen.previewIsReady = Qt.binding(function(){ return !callerId.previewIsReady}) } }) } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/Incall.qml000066400000000000000000000475521434616504300246630ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQml.Models 2.12 import QtGraphicalEffects 1.12 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import UtilsCpp 1.0 import App.Styles 1.0 // Temp import 'Incall.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Rectangle { id: mainItem property CallModel callModel property ConferenceModel conferenceModel: callModel && callModel.conferenceModel property bool cameraIsReady : false property bool previewIsReady : false property bool isFullScreen: false // Use this variable to test if we are in fullscreen. Do not test _fullscreen : we need to clean memory before having the window (see .js file) property bool layoutChanging: false property var _fullscreen: null on_FullscreenChanged: if( !_fullscreen) isFullScreen = false property bool listCallsOpened: true signal openListCallsRequest() property int participantCount: mainItem.conferenceModel ? mainItem.conferenceModel.participantDeviceCount : conferenceLayout.item ? conferenceLayout.item.participantCount : 2 // States property bool isAudioOnly: callModel && callModel.conferenceVideoLayout == LinphoneEnums.ConferenceLayoutAudioOnly property bool isReady : mainItem.callModel && (!mainItem.callModel.isConference || (mainItem.conferenceModel && mainItem.conferenceModel.isReady) ) && conferenceLayout.item && conferenceLayout.status == Loader.Ready function updateMessageBanner(){ //: ''You are alone in this conference' : Text in message banner when the user is the only participant. if( conferenceModel && isReady && participantCount <= 1) messageBanner.noticeBannerText = qsTr('aloneInConference') } Timer{ id: delayMessageBanner interval: 100 onTriggered: updateMessageBanner() } onParticipantCountChanged: Qt.callLater(function (){delayMessageBanner.restart()}) onIsReadyChanged: Qt.callLater(function (){delayMessageBanner.restart()}) // --------------------------------------------------------------------------- color: IncallStyle.backgroundColor Connections { target: callModel onCameraFirstFrameReceived: Logic.handleCameraFirstFrameReceived(width, height) onStatusChanged: {Logic.handleStatusChanged (status, mainItem._fullscreen) delayMessageBanner.restart() } onVideoRequested: Logic.handleVideoRequested(callModel) onEncryptionChanged: if(!callModel.isSecured && callModel.encryption === CallModel.CallEncryptionZrtp){ window.attachVirtualWindow(Utils.buildLinphoneDialogUri('ZrtpTokenAuthenticationDialog'), {call:callModel}) } } // --------------------------------------------------------------------------- Rectangle{ MouseArea{ anchors.fill: parent } anchors.fill: parent visible: callModel.pausedByUser color: IncallStyle.pauseArea.backgroundColor z: 1 ColumnLayout{ anchors.fill: parent spacing: 10 Item{ Layout.fillWidth: true Layout.fillHeight: true } ActionButton{ Layout.alignment: Qt.AlignCenter isCustom: true colorSet: IncallStyle.pauseArea.play backgroundRadius: width/2 onClicked: callModel.pausedByUser = !callModel.pausedByUser } Text{ Layout.alignment: Qt.AlignCenter //: 'You are currently out of the conference.' : Pause message in video conference. text: qsTr('incallPauseWarning') font.pointSize: IncallStyle.pauseArea.title.pointSize font.weight: IncallStyle.pauseArea.title.weight color: IncallStyle.pauseArea.title.color } Text{ Layout.topMargin: 10 Layout.alignment: Qt.AlignCenter //: 'Click on play button to join it back.' : Explain what to do when being in pause in conference. text: qsTr('incallPauseHint') font.pointSize: IncallStyle.pauseArea.description.pointSize font.weight: IncallStyle.pauseArea.description.weight color: IncallStyle.pauseArea.description.color } Item{ Layout.fillWidth: true Layout.fillHeight: true } } } // ------------------------------------------------------------------------- // Conference info. // ------------------------------------------------------------------------- RowLayout{ id: featuresRow // Aux features anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 10 anchors.leftMargin: 25 anchors.rightMargin: 25 spacing: 10 ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.callsList visible: !listCallsOpened && mainItem.isReady onClicked: openListCallsRequest() } ActionButton{ id: keypadButton isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.dialpad visible: mainItem.isReady toggled: telKeypad.visible onClicked: telKeypad.visible = !telKeypad.visible } ActionButton { id: callQuality isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.callQuality icon: IncallStyle.buttons.callQuality.icon_0 visible: mainItem.isReady toggled: callStatistics.isOpen onClicked: callStatistics.isOpen ? callStatistics.close() : callStatistics.open() Timer { interval: 500 repeat: true running: true triggeredOnStart: true onTriggered: { // Note: `quality` is in the [0, 5] interval and -1. var quality = callModel.quality if(quality > 4) callQuality.icon = IncallStyle.buttons.callQuality.icon_4 else if(quality > 3) callQuality.icon = IncallStyle.buttons.callQuality.icon_3 else if(quality > 2) callQuality.icon = IncallStyle.buttons.callQuality.icon_2 else if(quality > 1) callQuality.icon = IncallStyle.buttons.callQuality.icon_1 else callQuality.icon = IncallStyle.buttons.callQuality.icon_0 } } } // Title Item{ Layout.fillWidth: true Layout.preferredHeight: title.contentHeight + address.contentHeight property int centerOffset: mapFromItem(mainItem, mainItem.width/2,0).x - width/2 // Compute center from mainItem ColumnLayout{ anchors.top: parent.top anchors.bottom: parent.bottom width: parent.width x: parent.centerOffset Text{ id: title Layout.alignment: Qt.AlignHCenter Timer{ id: elapsedTimeRefresher running: true interval: 1000 repeat: true onTriggered: if(conferenceModel) parent.elaspedTime = Utils.formatElapsedTime(conferenceModel.getElapsedSeconds()) else parent.elaspedTime = Utils.formatElapsedTime(mainItem.callModel.duration) } property string elaspedTime horizontalAlignment: Qt.AlignHCenter Layout.fillWidth: true text: conferenceModel ? conferenceModel.subject ? conferenceModel.subject+ (elaspedTime ? ' - ' +elaspedTime : '') : elaspedTime : callModel ? elaspedTime : '' color: IncallStyle.title.color font.pointSize: IncallStyle.title.pointSize } Text{ id: address Layout.fillWidth: true horizontalAlignment: Qt.AlignHCenter visible: !conferenceModel && callModel && !callModel.isConference text: !conferenceModel && callModel ? SipAddressesModel.cleanSipAddress(callModel.peerAddress) : '' color: IncallStyle.title.color font.pointSize: IncallStyle.title.addressPointSize } } MessageBanner{ id: messageBanner anchors.fill: parent textColor: IncallStyle.header.messageBanner.textColor color: IncallStyle.header.messageBanner.color showIcon: false pointSize: IncallStyle.header.messageBanner.pointSize } } // Mode buttons ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.screenSharing visible: false //TODO } ActionButton { id: recordingSwitch isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.record property CallModel callModel: mainItem.callModel visible: SettingsModel.callRecorderEnabled && callModel && (callModel.recording || mainItem.isReady) toggled: callModel.recording onClicked: { return !toggled ? callModel.startRecording() : callModel.stopRecording() } //: 'Start recording' : Tootltip when straing record. tooltipText: !toggled ? qsTr('incallStartRecordTooltip') //: 'Stop Recording' : Tooltip when stopping record. : qsTr('incallStopRecordTooltip') } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.screenshot visible: SettingsModel.incallScreenshotEnabled && mainItem.isReady && mainItem.callModel && (!mainItem.callModel.isConference || mainItem.callModel.snapshotEnabled) onClicked: mainItem.callModel.takeSnapshot() //: 'Take Snapshot' : Tooltip for takking snapshot. tooltipText: qsTr('incallSnapshotTooltip') } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.fullscreen visible: mainItem.callModel.videoEnabled onClicked: Logic.showFullscreen(window, mainItem, 'IncallFullscreen.qml', title.mapToGlobal(0,0)) } } // ------------------------------------------------------------------------- // Contacts visual. // ------------------------------------------------------------------------- Item{ id: mainGrid anchors.top: featuresRow.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: actionsButtons.top anchors.topMargin: 15 anchors.bottomMargin: 20 Component{ id: gridComponent IncallGrid{ id: grid Layout.leftMargin: 70 Layout.rightMargin: rightMenu.visible ? 15 : 70 callModel: mainItem.callModel cameraEnabled: !mainItem.isFullScreen && !mainItem.layoutChanging } } Component{ id: activeSpeakerComponent IncallActiveSpeaker{ id: activeSpeaker callModel: mainItem.callModel isRightReducedLayout: rightMenu.visible isLeftReducedLayout: mainItem.listCallsOpened cameraEnabled: !mainItem.isFullScreen && !mainItem.layoutChanging } } RowLayout{ anchors.fill: parent Item{ Layout.fillHeight: true Layout.fillWidth: true Loader{ id: conferenceLayout anchors.fill: parent Timer{// Avoid Qt crashes when layout changes while videos are on id: layoutDelay interval: 100 property int step : 0 property var layoutMode onTriggered: { switch(step){ case 2 : step = 0; mainItem.layoutChanging = false; break; case 1: ++step; conferenceLayout.sourceComponent = conferenceLayout.getLayout(); layoutDelay.restart(); break; case 0: if( mainItem.callModel.conferenceVideoLayout != layoutMode) mainItem.callModel.conferenceVideoLayout = layoutMode else { ++step; layoutDelay.restart() } break; } } function begin(layoutMode){ step = 0 layoutDelay.layoutMode = layoutMode mainItem.layoutChanging = true layoutDelay.restart() } } function getLayout(){ return mainItem.conferenceModel ? mainItem.callModel.conferenceVideoLayout == LinphoneEnums.ConferenceLayoutActiveSpeaker ? activeSpeakerComponent : gridComponent : activeSpeakerComponent } Connections{ target: mainItem.callModel onConferenceVideoLayoutChanged: { layoutDelay.layoutMode = mainItem.callModel.conferenceVideoLayout layoutDelay.restart() } } sourceComponent: getLayout() active: mainItem.callModel && !mainItem.isFullScreen } Rectangle{ anchors.fill: parent color: mainItem.color visible: !mainItem.isReady ColumnLayout { anchors.fill: parent Loader{ Layout.preferredHeight: 40 Layout.preferredWidth: 40 Layout.alignment: Qt.AlignCenter active: parent.visible sourceComponent: Component{ BusyIndicator{ color: IncallStyle.buzyColor } } } Text{ Layout.alignment: Qt.AlignCenter text: false //mainItem.needMoreParticipants //: 'Waiting for another participant...' : Waiting message for more participant. ? qsTr('incallWaitParticipantMessage') //: 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. : qsTr('incallWaitMessage') color: IncallStyle.buzyColor } } } } IncallMenu{ id: rightMenu Layout.fillHeight: true Layout.preferredWidth: 400 Layout.rightMargin: 30 callModel: mainItem.callModel conferenceModel: mainItem.conferenceModel visible: false enabled: !mainItem.layoutChanging onClose: rightMenu.visible = !rightMenu.visible onLayoutChanging: { layoutDelay.begin(layoutMode) } } } } // ------------------------------------------------------------------------- // Action Buttons. // ------------------------------------------------------------------------- // Security ActionButton{ id: securityButton anchors.left: parent.left anchors.verticalCenter: actionsButtons.verticalCenter anchors.leftMargin: 25 height: IncallStyle.buttons.secure.buttonSize width: height isCustom: true iconIsCustom: ! (callModel.isSecured && SettingsModel.isPostQuantumAvailable && callModel.encryption === CallModel.CallEncryptionZrtp) backgroundRadius: width/2 colorSet: callModel.isSecured ? SettingsModel.isPostQuantumAvailable && callModel.encryption === CallModel.CallEncryptionZrtp && callModel.isPQZrtp == CallModel.CallPQStateOn ? IncallStyle.buttons.postQuantumSecure : IncallStyle.buttons.secure : IncallStyle.buttons.unsecure onClicked: if(callModel.encryption === CallModel.CallEncryptionZrtp){ window.attachVirtualWindow(Utils.buildLinphoneDialogUri('ZrtpTokenAuthenticationDialog'), {call:callModel}) } tooltipText: Logic.makeReadableSecuredString(callModel.isSecured, callModel.securedString) } RowLayout{ visible: callModel.remoteRecording anchors.verticalCenter: actionsButtons.verticalCenter anchors.left: securityButton.right anchors.leftMargin: 20 anchors.right: actionsButtons.left anchors.rightMargin: 10 Icon{ icon: IncallStyle.recordWarning.icon iconSize: IncallStyle.recordWarning.iconSize overwriteColor: IncallStyle.recordWarning.iconColor } Text{ Layout.fillWidth: true //: 'This call is being recorded.' : Warn the user that the remote is currently recording the call. text: qsTr('callWarningRecord') color: IncallStyle.recordWarning.color font.italic: true font.pointSize: IncallStyle.recordWarning.pointSize wrapMode: Text.WordWrap } } // Action buttons RowLayout{ id: actionsButtons anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom anchors.bottomMargin: 30 height: 60 spacing: 30 z: 2 RowLayout{ spacing: 10 visible: mainItem.isReady Row { spacing: 2 visible: SettingsModel.muteMicrophoneEnabled property bool microMuted: callModel.microMuted VuMeter { enabled: !parent.microMuted Timer { interval: 50 repeat: true running: parent.enabled onTriggered: parent.value = callModel.microVu } } ActionSwitch { id: micro isCustom: true backgroundRadius: 90 colorSet: parent.microMuted ? IncallStyle.buttons.microOff : IncallStyle.buttons.microOn onClicked: callModel.microMuted = !parent.microMuted } } Row { spacing: 2 property bool speakerMuted: callModel.speakerMuted VuMeter { enabled: !parent.speakerMuted Timer { interval: 50 repeat: true running: parent.enabled onTriggered: parent.value = callModel.speakerVu } } ActionSwitch { id: speaker isCustom: true backgroundRadius: 90 colorSet: parent.speakerMuted ? IncallStyle.buttons.speakerOff : IncallStyle.buttons.speakerOn onClicked: callModel.speakerMuted = !parent.speakerMuted } } ActionSwitch { id: camera isCustom: true backgroundRadius: 90 colorSet: callModel && callModel.cameraEnabled ? IncallStyle.buttons.cameraOn : IncallStyle.buttons.cameraOff updating: callModel.videoEnabled && callModel.updating && !mainItem.layoutChanging enabled: callModel && !callModel.pausedByUser property bool _activateCamera: false onClicked: if(callModel && !mainItem.layoutChanging){ if( callModel.isConference){// Only deactivate camera in conference. if(mainItem.isAudioOnly) { var layout = SettingsModel.videoConferenceLayout != LinphoneEnums.ConferenceLayoutAudioOnly ? SettingsModel.videoConferenceLayout : LinphoneEnums.ConferenceLayoutGrid layoutDelay.begin(layout) camera._activateCamera = true }else callModel.cameraEnabled = !callModel.cameraEnabled }else{// In one-one, we deactivate all videos. callModel.videoEnabled = !callModel.videoEnabled } } Connections{// Enable camera only when status is ok target: callModel onStatusChanged: if( camera._activateCamera && (status == LinphoneEnums.CallStatusConnected || status == LinphoneEnums.CallStatusIdle)){ camera._activateCamera = false callModel.cameraEnabled = true } } } } RowLayout{ spacing: 10 ActionButton{ isCustom: true backgroundRadius: width/2 visible: SettingsModel.callPauseEnabled && mainItem.isReady updating: callModel.updating colorSet: callModel.pausedByUser ? IncallStyle.buttons.play : IncallStyle.buttons.pause onClicked: callModel.pausedByUser = !callModel.pausedByUser } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.hangup onClicked: callModel.terminate() } } } // Panel buttons RowLayout{ anchors.right: parent.right anchors.bottom: parent.bottom anchors.bottomMargin: 30 anchors.rightMargin: 25 height: 60 visible: mainItem.isReady ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.chat visible: window.haveChat && (SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled) && callModel && !callModel.isConference toggled: window.chatIsOpened onClicked: { if (window.chatIsOpened) { window.closeChat() } else { window.openChat() } } } ActionButton{ visible: callModel && callModel.isConference isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.participants toggled: rightMenu.visible && rightMenu.isParticipantsMenu onClicked: { if(toggled) rightMenu.visible = false else rightMenu.showParticipantsMenu() } } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.options toggled: rightMenu.visible onClicked: rightMenu.visible = !rightMenu.visible } } // --------------------------------------------------------------------------- // TelKeypad. // --------------------------------------------------------------------------- CallStatistics { id: callStatistics call: mainItem.callModel width: mainItem.width height: mainItem.height } TelKeypad { id: telKeypad showHistory:true call: callModel visible: SettingsModel.showTelKeypadAutomatically y: 70 } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/IncallActiveSpeaker.qml000066400000000000000000000137631434616504300273270ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQml.Models 2.12 import QtGraphicalEffects 1.12 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import UtilsCpp 1.0 import App.Styles 1.0 // Temp import 'Incall.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Item { id: mainItem property CallModel callModel property bool isRightReducedLayout: false property bool isLeftReducedLayout: false property bool cameraEnabled: true property bool isConferenceReady: callModel.isConference && callModel.conferenceModel && callModel.conferenceModel.isReady property int participantCount: callModel.isConference ? allDevices.count + 1 : 2 // +me. allDevices==0 if !conference property ParticipantDeviceProxyModel participantDevices : ParticipantDeviceProxyModel { id: allDevices callModel: mainItem.callModel showMe: false onConferenceCreated: cameraView.resetCamera() } Sticker{ id: cameraView anchors.fill: parent anchors.leftMargin: isRightReducedLayout || isLeftReducedLayout? 30 : 140 anchors.rightMargin: isRightReducedLayout ? 10 : 140 callModel: mainItem.callModel currentDevice: isPreview ? allDevices.me : callModel.isConference ? allDevices.activeSpeaker : null deactivateCamera: !mainItem.cameraEnabled || (isPreview && callModel.pausedByUser) ? true : callModel.isConference ? (callModel && (callModel.pausedByUser || callModel.status === CallModel.CallStatusPaused) ) || (!callModel.cameraEnabled && mainItem.participantCount == 1) || (currentDevice && !currentDevice.videoEnabled)// && mainItem.participantCount == 2) || !mainItem.isConferenceReady : (callModel && (callModel.pausedByUser || callModel.status === CallModel.CallStatusPaused || !callModel.videoEnabled) ) || currentDevice && !currentDevice.videoEnabled isPreview: !preview.visible && mainItem.participantCount == 1 onIsPreviewChanged: {cameraView.resetCamera() } isCameraFromDevice: isPreview isPaused: isPreview && callModel.pausedByUser ? false : callModel.isConference ? //callModel && callModel.pausedByUser && mainItem.participantCount != 2 || (currentDevice && currentDevice.isPaused) : callModel && !callModel.pausedByUser && (callModel.status === CallModel.CallStatusPaused) quickTransition: true showCloseButton: false showActiveSpeakerOverlay: false // This is an active speaker. We don't need to show the indicator. showCustomButton: false avatarStickerBackgroundColor: isPreview ? IncallStyle.container.avatar.stickerPreviewBackgroundColor : IncallStyle.container.avatar.stickerBackgroundColor avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor } Item{// Need an item to not override Sticker internal states. States are needed for changing anchors. id: preview anchors.right: parent.right anchors.bottom: parent.bottom anchors.rightMargin: 30 anchors.bottomMargin: 15 height: visible ? miniViews.cellHeight : 0 width: 16 * height / 9 visible: mainItem.isConferenceReady && allDevices.count >= 1 || (!callModel.isConference && mainItem.callModel.cameraEnabled)// use videoEnabled if we want to show the preview sticker Loader{ anchors.fill: parent anchors.margins: 3 sourceComponent: Sticker{ id: previewSticker deactivateCamera: !mainItem.cameraEnabled || !mainItem.callModel || callModel.pausedByUser || !mainItem.callModel.cameraEnabled currentDevice: allDevices.me isPreview: true callModel: mainItem.callModel isCameraFromDevice: true showCloseButton: false showCustomButton: false showAvatarBorder: true avatarStickerBackgroundColor: IncallStyle.container.avatar.stickerPreviewBackgroundColor avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor } active: parent.visible } } Item{ anchors.right: parent.right anchors.top: parent.top anchors.bottom: preview.top anchors.rightMargin: 30 anchors.topMargin: 15 anchors.bottomMargin: 15 //--------------- width: 16 * miniViews.cellHeight / 9 visible: mainItem.isConferenceReady || !callModel.isConference ScrollableListView{ id: miniViews property int cellHeight: 150 anchors.fill: parent model : mainItem.callModel.isConference && mainItem.participantDevices.count > 1 ? mainItem.participantDevices : [] spacing: 15 verticalLayoutDirection: ListView.BottomToTop fitCacheToContent: false onCountChanged: updateView() onHeightChanged: updateView() function updateView(){ if(contentItem.height < miniViews.height){ contentItem.y = miniViews.height // Qt workaround because it do not set correctly value with positionning to beginning } } Component.onCompleted: updateView() Timer{ running: true interval: 500 repeat: true onTriggered: miniViews.updateView() } delegate:Item{ height: miniViews.cellHeight width: miniViews.width clip:false Sticker{ id: miniView anchors.fill: parent anchors.margins: 3 deactivateCamera: (!mainItem.isConferenceReady || !callModel.isConference) && (index <0 || !mainItem.cameraEnabled || (!modelData.videoEnabled) || (callModel && callModel.pausedByUser) ) currentDevice: modelData.isPreview ? null : modelData callModel: modelData.isPreview ? null : mainItem.callModel isCameraFromDevice: mainItem.callModel.isConference isPaused: currentDevice && currentDevice.isPaused showCloseButton: false showCustomButton: false showAvatarBorder: true avatarStickerBackgroundColor: IncallStyle.container.avatar.stickerBackgroundColor avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/IncallFullscreen.qml000066400000000000000000000467131434616504300267040ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQml.Models 2.12 import QtGraphicalEffects 1.12 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import DesktopTools 1.0 import LinphoneEnums 1.0 import UtilsCpp 1.0 import App.Styles 1.0 // Temp import 'Incall.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils Window { id: window // --------------------------------------------------------------------------- property alias callModel: conference.callModel property var caller property bool hideButtons: !hideButtonsTimer.realRunning property bool cameraIsReady : false property bool previewIsReady : false // --------------------------------------------------------------------------- function exit (cb) { DesktopTools.screenSaverStatus = true // `exit` is called by `Incall.qml`. // The `window` id can be null if the window was closed in this view. if (!window) { return } if(!window.close() && parent) parent.close() if (cb) { cb() } } // --------------------------------------------------------------------------- onCallModelChanged: if(!callModel) window.exit() Component.onCompleted: { window.callModel = caller.callModel } // --------------------------------------------------------------------------- Shortcut { sequence: StandardKey.Close onActivated: window.exit() } // --------------------------------------------------------------------------- // ============================================================================= Rectangle { id: conference property CallModel callModel property ConferenceModel conferenceModel: callModel && callModel.conferenceModel property var _fullscreen: null property bool listCallsOpened: false property bool layoutChanging: false property bool isAudioOnly: callModel && callModel.conferenceVideoLayout == LinphoneEnums.ConferenceLayoutAudioOnly signal openListCallsRequest() // --------------------------------------------------------------------------- anchors.fill: parent focus: true Keys.onEscapePressed: window.exit() color: hideButtons ? IncallStyle.fullBackgroundColor : IncallStyle.backgroundColor Connections { target: callModel onCameraFirstFrameReceived: Logic.handleCameraFirstFrameReceived(width, height) onStatusChanged: Logic.handleStatusChanged (status, conference._fullscreen) onVideoRequested: Logic.handleVideoRequested(callModel) onEncryptionChanged: if(!callModel.isSecured && callModel.encryption === CallModel.CallEncryptionZrtp){ window.attachVirtualWindow(Utils.buildLinphoneDialogUri('ZrtpTokenAuthenticationDialog'), {call:callModel}) } } // --------------------------------------------------------------------------- Rectangle{ anchors.fill: parent visible: callModel && callModel.pausedByUser color: IncallStyle.pauseArea.backgroundColor z: 1 ColumnLayout{ anchors.fill: parent spacing: 10 Item{ Layout.fillWidth: true Layout.fillHeight: true } ActionButton{ Layout.alignment: Qt.AlignCenter isCustom: true colorSet: IncallStyle.pauseArea.play backgroundRadius: width/2 onClicked: if(callModel) callModel.pausedByUser = !callModel.pausedByUser } Text{ Layout.alignment: Qt.AlignCenter //: 'You are currently out of the conference.' : Pause message in video conference. text: qsTr('incallPauseWarning') font.pointSize: IncallStyle.pauseArea.title.pointSize font.weight: IncallStyle.pauseArea.title.weight color: IncallStyle.pauseArea.title.color } Text{ Layout.topMargin: 10 Layout.alignment: Qt.AlignCenter //: 'Click on play button to join it back.' : Explain what to do when being in pause in conference. text: qsTr('incallPauseHint') font.pointSize: IncallStyle.pauseArea.description.pointSize font.weight: IncallStyle.pauseArea.description.weight color: IncallStyle.pauseArea.description.color } Item{ Layout.fillWidth: true Layout.fillHeight: true } } } // ------------------------------------------------------------------------- // Conference info. // ------------------------------------------------------------------------- RowLayout{ id: featuresRow // Aux features anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.topMargin: window.hideButtons ? 0 : 10 anchors.leftMargin: 25 anchors.rightMargin: 25 spacing: 10 visible: !window.hideButtons height: visible? undefined : 0 ActionButton{ id: keypadButton isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.dialpad onClicked: telKeypad.visible = !telKeypad.visible } ActionButton { id: callQuality isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.callQuality icon: IncallStyle.buttons.callQuality.icon_0 toggled: callStatistics.isOpen onClicked: callStatistics.isOpen ? callStatistics.close() : callStatistics.open() Timer { interval: 500 repeat: true running: true triggeredOnStart: true onTriggered: { if(callModel) { // Note: `quality` is in the [0, 5] interval and -1. var quality = callModel.quality if(quality > 4) callQuality.icon = IncallStyle.buttons.callQuality.icon_4 else if(quality > 3) callQuality.icon = IncallStyle.buttons.callQuality.icon_3 else if(quality > 2) callQuality.icon = IncallStyle.buttons.callQuality.icon_2 else if(quality > 1) callQuality.icon = IncallStyle.buttons.callQuality.icon_1 else callQuality.icon = IncallStyle.buttons.callQuality.icon_0 } } } } // Title Text{ Timer{ id: elapsedTimeRefresher running: true interval: 1000 repeat: true onTriggered: if(conference.conferenceModel) parent.elaspedTime = ' - ' +Utils.formatElapsedTime(conference.conferenceModel.getElapsedSeconds()) } property string elaspedTime horizontalAlignment: Qt.AlignHCenter Layout.fillWidth: true text: conference.conferenceModel ? conference.conferenceModel.subject+ elaspedTime : '' color: IncallStyle.title.color font.pointSize: IncallStyle.title.pointSize } // Mode buttons ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.screenSharing visible: false //TODO } ActionButton { id: recordingSwitch isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.record property CallModel callModel: conference.callModel visible: SettingsModel.callRecorderEnabled && callModel toggled: callModel && callModel.recording onClicked: { return !toggled ? callModel.startRecording() : callModel.stopRecording() } //: 'Start recording' : Tootltip when straing record. tooltipText: !toggled ? qsTr('incallStartRecordTooltip') //: 'Stop Recording' : Tooltip when stopping record. : qsTr('incallStopRecordTooltip') } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.screenshot visible: SettingsModel.incallScreenshotEnabled && conference.callModel && (!conference.callModel.isConference || window.callModel.snapshotEnabled) onClicked: conference.callModel && conference.callModel.takeSnapshot() //: 'Take Snapshot' : Tooltip for takking snapshot. tooltipText: qsTr('incallSnapshotTooltip') } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.stopFullscreen onClicked: window.exit() } } // ------------------------------------------------------------------------- // Contacts visual. // ------------------------------------------------------------------------- MouseArea{ id: mainGrid anchors.top: featuresRow.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: actionsButtons.top anchors.topMargin: window.hideButtons ? 0 : 15 anchors.bottomMargin: window.hideButtons ? 0 : 20 onClicked: { if(!conference.callModel) grid.add({color: '#'+ Math.floor(Math.random()*255).toString(16) +Math.floor(Math.random()*255).toString(16) +Math.floor(Math.random()*255).toString(16)}) } Component{ id: gridComponent IncallGrid{ id: grid Layout.leftMargin: window.hideButtons ? 15 : 70 Layout.rightMargin: rightMenu.visible ? 15 : 70 callModel: conference.callModel cameraEnabled: !conference.layoutChanging } } Component{ id: activeSpeakerComponent IncallActiveSpeaker{ id: activeSpeaker callModel: conference.callModel cameraEnabled: !conference.layoutChanging isRightReducedLayout: rightMenu.visible isLeftReducedLayout: conference.listCallsOpened } } RowLayout{ anchors.fill: parent Loader{ id: conferenceLayout Layout.fillHeight: true Layout.fillWidth: true Timer{// Avoid Qt crashes when layout changes while videos are on id: layoutDelay interval: 100 property int step : 0 property var layoutMode onTriggered: { switch(step){ case 2 : step = 0; conference.layoutChanging = false; break; case 1: ++step; conferenceLayout.sourceComponent = conferenceLayout.getLayout(); layoutDelay.restart(); break; case 0: if( conference.callModel.conferenceVideoLayout != layoutMode) conference.callModel.conferenceVideoLayout = layoutMode else { ++step; layoutDelay.restart() } break; } } function begin(layoutMode){ step = 0 layoutDelay.layoutMode = layoutMode conference.layoutChanging = true layoutDelay.restart() } } function getLayout(){ return conference.conferenceModel ? conference.callModel.conferenceVideoLayout == LinphoneEnums.ConferenceLayoutActiveSpeaker ? activeSpeakerComponent : gridComponent : activeSpeakerComponent } Connections{ target: conference.callModel onConferenceVideoLayoutChanged: { layoutDelay.layoutMode = conference.callModel.conferenceVideoLayout layoutDelay.restart() } } sourceComponent: getLayout() active: conference.callModel ColumnLayout { anchors.fill: parent visible: !conference.callModel || !conferenceLayout.item || conferenceLayout.item.participantCount == 0 BusyIndicator{ Layout.preferredHeight: 50 Layout.preferredWidth: 50 Layout.alignment: Qt.AlignCenter running: parent.visible color: IncallStyle.buzyColor } Text{ Layout.alignment: Qt.AlignCenter //: 'The meeting is not ready. Please Wait...' : Waiting message for starting a meeting. text: qsTr('incallWaitMessage') color: IncallStyle.buzyColor } } } IncallMenu{ id: rightMenu Layout.fillHeight: true Layout.preferredWidth: 400 Layout.rightMargin: 30 callModel: conference.callModel conferenceModel: conference.conferenceModel visible: false onClose: rightMenu.visible = !rightMenu.visible onLayoutChanging: { layoutDelay.begin(layoutMode) } } } } // ------------------------------------------------------------------------- // Action Buttons. // ------------------------------------------------------------------------- // Security ActionButton{ id: securityButton visible: !window.hideButtons && callModel && !callModel.isConference anchors.left: parent.left anchors.verticalCenter: actionsButtons.verticalCenter anchors.leftMargin: 25 height: IncallStyle.buttons.secure.buttonSize width: height isCustom: true iconIsCustom: ! (callModel.isSecured && SettingsModel.isPostQuantumAvailable && callModel.encryption === CallModel.CallEncryptionZrtp) backgroundRadius: width/2 colorSet: callModel.isSecured ? SettingsModel.isPostQuantumAvailable && callModel.encryption === CallModel.CallEncryptionZrtp && callModel.isPQZrtp == CallModel.CallPQStateOn ? IncallStyle.buttons.postQuantumSecure : IncallStyle.buttons.secure : IncallStyle.buttons.unsecure onClicked: if(callModel.encryption === CallModel.CallEncryptionZrtp){ window.attachVirtualWindow(Utils.buildLinphoneDialogUri('ZrtpTokenAuthenticationDialog'), {call:callModel}) } tooltipText: Logic.makeReadableSecuredString(callModel.isSecured, callModel.securedString) } RowLayout{ visible: callModel && callModel.remoteRecording anchors.verticalCenter: !window.hideButtons ? actionsButtons.verticalCenter : undefined anchors.bottom: window.hideButtons ? parent.bottom : undefined anchors.bottomMargin: window.hideButtons ? 20 : 0 anchors.left: securityButton.right anchors.leftMargin: 20 anchors.right: actionsButtons.left anchors.rightMargin: 10 Icon{ icon: IncallStyle.recordWarning.icon iconSize: IncallStyle.recordWarning.iconSize overwriteColor: IncallStyle.recordWarning.iconColor } Text{ Layout.fillWidth: true //: 'This call is being recorded.' : Warn the user that the remote is currently recording the call. text: qsTr('callWarningRecord') color: IncallStyle.recordWarning.color font.italic: true font.pointSize: IncallStyle.recordWarning.pointSize wrapMode: Text.WordWrap } } // Action buttons RowLayout{ id: actionsButtons anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom anchors.bottomMargin: visible ? 30 : 0 height: visible ? 60 : 0 spacing: 30 z: 2 visible: !window.hideButtons RowLayout{ spacing: 10 Row { spacing: 2 visible: SettingsModel.muteMicrophoneEnabled property bool microMuted: callModel && callModel.microMuted VuMeter { enabled: !parent.microMuted Timer { interval: 50 repeat: true running: parent.enabled onTriggered: if(callModel) parent.value = callModel.microVu } } ActionSwitch { id: micro isCustom: true backgroundRadius: 90 colorSet: parent.microMuted ? IncallStyle.buttons.microOff : IncallStyle.buttons.microOn onClicked: if(callModel) callModel.microMuted = !parent.microMuted } } Row { spacing: 2 property bool speakerMuted: callModel && callModel.speakerMuted VuMeter { enabled: !parent.speakerMuted Timer { interval: 50 repeat: true running: parent.enabled onTriggered: parent.value = callModel.speakerVu } } ActionSwitch { id: speaker isCustom: true backgroundRadius: 90 colorSet: parent.speakerMuted ? IncallStyle.buttons.speakerOff : IncallStyle.buttons.speakerOn onClicked: if(callModel) callModel.speakerMuted = !parent.speakerMuted } } ActionSwitch { id: camera isCustom: true backgroundRadius: 90 colorSet: callModel && callModel.cameraEnabled ? IncallStyle.buttons.cameraOn : IncallStyle.buttons.cameraOff updating: callModel && callModel.videoEnabled && callModel.updating enabled: callModel && !callModel.pausedByUser onClicked: if(callModel && !conference.layoutChanging){ if( callModel.isConference){// Only deactivate camera in conference. callModel.cameraEnabled = !callModel.cameraEnabled }else{// In one-one, we deactivate all videos. if(callModel.videoEnabled ) Qt.callLater(function(){window.exit()}) callModel.videoEnabled = !callModel.videoEnabled } } } } RowLayout{ spacing: 10 ActionButton{ isCustom: true backgroundRadius: width/2 visible: SettingsModel.callPauseEnabled updating: callModel && callModel.updating colorSet: callModel && callModel.pausedByUser ? IncallStyle.buttons.play : IncallStyle.buttons.pause onClicked: if(callModel) callModel.pausedByUser = !callModel.pausedByUser } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.hangup onClicked: if(callModel) callModel.terminate() } } } // Panel buttons RowLayout{ anchors.right: parent.right anchors.bottom: parent.bottom anchors.bottomMargin: visible ? 30 : 0 anchors.rightMargin: 25 height: visible ? 60 : 0 visible: !window.hideButtons /* Not available in fullscreen yet. ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.chat visible: false && (SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled) && callModel && !callModel.isConference toggled: window.chatIsOpened onClicked: { if (window.chatIsOpened) { window.closeChat() } else { window.openChat() } } }*/ ActionButton{ visible: callModel && callModel.isConference isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.participants toggled: rightMenu.visible && rightMenu.isParticipantsMenu onClicked: { if(toggled) rightMenu.visible = false else rightMenu.showParticipantsMenu() } } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: IncallStyle.buttons.options toggled: rightMenu.visible onClicked: rightMenu.visible = !rightMenu.visible } } // --------------------------------------------------------------------------- // TelKeypad. // --------------------------------------------------------------------------- CallStatistics { id: callStatistics call: conference.callModel width: conference.width - 20 height: conference.height * 2/3 relativeTo: conference relativeY: CallStyle.header.stats.relativeY relativeX: 10 } } TelKeypad { id: telKeypad showHistory:true call: callModel visible: SettingsModel.showTelKeypadAutomatically y: 50 } MouseArea{ Timer { id: hideButtonsTimer property bool realRunning : false property bool firstUse: true interval: firstUse ? 500 : 4000 running: false triggeredOnStart: !firstUse onTriggered: {if(!firstUse && realRunning != running) realRunning = running firstUse = false} function startTimer(){ if(!firstUse || !running) restart(); } function stopTimer(){ stop() realRunning = false hideButtonsTimer.firstUse = false } } anchors.fill: parent acceptedButtons: Qt.NoButton propagateComposedEvents: true cursorShape: undefined //cursorShape: Qt.ArrowCursor onEntered: hideButtonsTimer.startTimer() onExited: { var cursorPosition = UtilsCpp.getCursorPosition() mapToItem(window.contentItem, cursorPosition.x, cursorPosition.y) if (cursorPosition.x <= 0 || cursorPosition.y <= 0 || cursorPosition.x >= width || cursorPosition.y >= height) hideButtonsTimer.stopTimer() } onPositionChanged: hideButtonsTimer.startTimer() } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/IncallGrid.qml000066400000000000000000000045301434616504300254560ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQml.Models 2.12 import QtGraphicalEffects 1.12 import Common 1.0 import Common.Styles 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import UtilsCpp 1.0 import App.Styles 1.0 import ConstantsCpp 1.0 // Temp import 'Incall.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Mosaic { id: grid property alias callModel: participantDevices.callModel property bool cameraEnabled: true property int participantCount: gridModel.count // On grid view, we limit the quality if there are enough participants// The vga mode has been activated from the factory rc //onParticipantCountChanged: participantCount > ConstantsCpp.maxMosaicParticipants ? SettingsModel.setLimitedMosaicQuality() : SettingsModel.setHighMosaicQuality() delegateModel: DelegateModel{ id: gridModel property ParticipantDeviceProxyModel participantDevices : ParticipantDeviceProxyModel { id: participantDevices showMe: true } model: participantDevices delegate: Item{ id: avatarCell property ParticipantDeviceModel currentDevice: gridModel.participantDevices.getAt(index) onCurrentDeviceChanged: { if(index < 0) cameraView.enabled = false // this is a delegate destruction. We need to stop camera before Qt change its currentDevice (and then, let CameraView to delete wrong renderer) } height: grid.cellHeight - 10 width: grid.cellWidth - 10 Sticker{ id: cameraView anchors.fill: parent callModel: index >= 0 ? participantDevices.callModel : null // do this before to prioritize changing call on remove deactivateCamera: index <0 || !grid.cameraEnabled || grid.callModel.pausedByUser currentDevice: gridModel.participantDevices.getAt(index) isCameraFromDevice: true isPaused: !isPreview && avatarCell.currentDevice && avatarCell.currentDevice.isPaused showCloseButton: false showCustomButton: false avatarStickerBackgroundColor: isPreview? IncallStyle.container.avatar.stickerPreviewBackgroundColor : IncallStyle.container.avatar.stickerBackgroundColor avatarBackgroundColor: IncallStyle.container.avatar.backgroundColor //onCloseRequested: participantDevices.showMe = false } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/IncomingCall.qml000066400000000000000000000016551434616504300260120ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 // ============================================================================= AbstractStartingCall { ActionBar { anchors.centerIn: parent iconSize: CallStyle.actionArea.iconSize ActionButton { isCustom: true backgroundRadius: 90 colorSet: CallStyle.buttons.acceptVideoCall visible: SettingsModel.videoSupported onClicked: call.acceptWithVideo() } ActionButton { isCustom: true backgroundRadius: 90 colorSet: CallStyle.buttons.acceptCall onClicked: call.accept() } } ActionBar { anchors { right: parent.right rightMargin: CallStyle.actionArea.rightButtonsGroupMargin verticalCenter: parent.verticalCenter } iconSize: CallStyle.actionArea.iconSize ActionButton { isCustom: true backgroundRadius: 90 colorSet: CallStyle.buttons.hangup onClicked: call.terminate() } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Calls/WaitingRoom.qml000066400000000000000000000330231434616504300257040ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.12 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import UtilsCpp 1.0 import Common.Styles 1.0 import App.Styles 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= Rectangle { id: mainItem color: WaitingRoomStyle.backgroundColor property ConferenceInfoModel conferenceInfoModel property CallModel callModel // Store the call for processing calling. property bool previewLoaderEnabled: callModel ? callModel.videoEnabled : true property var _sipAddressObserver: callModel ? SipAddressesModel.getSipAddressObserver(callModel.fullPeerAddress, callModel.fullLocalAddress) : undefined property bool isEnded: callModel && callModel.status == CallModel.CallStatusEnded signal cancel() function reset(){ close() open() } function close(){ mainItem.previewLoaderEnabled = false// Need it to close camera. } function open(){ mainItem.previewLoaderEnabled = callModel ? callModel.videoEnabled : true } //onCallModelChanged: callModel ? contentsStack.replace(callingComponent) : contentsStack.replace(cameraComponent) //onCallModelChanged: contentsStack.flipped = !!callModel Component.onDestruction: {mainItem.previewLoaderEnabled = false;_sipAddressObserver=null}// Need to set it to null because of not calling destructor if not. Connections{ target:window onClosing: mainItem.close() } ColumnLayout { anchors.fill: parent ColumnLayout{ Layout.alignment: Qt.AlignCenter Layout.bottomMargin: (mainItem.conferenceInfoModel && mainItem.callModel ? 10 : 30) spacing: 10 BusyIndicator { id: busyIndicator Layout.alignment: Qt.AlignCenter Layout.preferredHeight: WaitingRoomStyle.header.busyIndicator.height Layout.preferredWidth: WaitingRoomStyle.header.busyIndicator.width Layout.topMargin: 30 color: WaitingRoomStyle.header.busyIndicator.color visible: mainItem.callModel && mainItem.callModel.status !== CallModel.CallStatusConnected && !mainItem.isEnded } Text{ Layout.alignment: Qt.AlignCenter Layout.topMargin: busyIndicator.visible ? 0 : 30 text: mainItem.callModel ? mainItem.isEnded //: "Call ended" : status of the call in waiting room when the call end. ? qsTr("endCallStatus") : mainItem.callModel.isOutgoing //: "Outgoing call" : status of the call in waiting room when user is calling. ? qsTr("outgoingCallStatus") //: "Incoming call" : status of the call in waiting room when user receive a call. : qsTr("incomingCallStatus") : '' color: WaitingRoomStyle.title.color font.pointSize: WaitingRoomStyle.title.pointSize horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter visible: mainItem.callModel } Text { id: elapsedTime Layout.alignment: Qt.AlignCenter color: WaitingRoomStyle.elapsedTime.color font.pointSize: WaitingRoomStyle.elapsedTime.pointSize horizontalAlignment: Text.AlignHCenter width: parent.width visible: mainItem.callModel && !mainItem.isEnded Timer { interval: 1000 repeat: true running: mainItem.callModel triggeredOnStart: true property var startDate onRunningChanged: if( running) { elapsedTime.text = Utils.formatElapsedTime(0); startDate = new Date() } onTriggered: {elapsedTime.text = Utils.formatElapsedTime((new Date() - startDate)/1000)} } } Text{ Layout.alignment: Qt.AlignCenter Layout.topMargin: mainItem.callModel ? 0 : 40 text: mainItem.conferenceInfoModel ? mainItem.conferenceInfoModel.subject : (mainItem._sipAddressObserver ? UtilsCpp.getDisplayName(mainItem._sipAddressObserver.peerAddress) : '') color: WaitingRoomStyle.title.color font.pointSize: WaitingRoomStyle.title.pointSize horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter visible: mainItem.conferenceInfoModel } } Text { id: errorArea Layout.fillWidth: true Layout.preferredHeight: implicitHeight Layout.bottomMargin: 10 horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter color: WaitingRoomStyle.callError.color font.pointSize: WaitingRoomStyle.callError.pointSize width: parent.width visible: mainItem.callModel && mainItem.callModel.callError text: mainItem.callModel && mainItem.callModel.callError ? mainItem.callModel.callError : '' } RowLayout{ Layout.fillWidth: true Layout.fillHeight: true spacing: 0 Item{ id: stickerView Layout.fillHeight: true Layout.fillWidth: true Layout.leftMargin: 10 Layout.rightMargin: 10 Sticker{ id: contentsStack property var previewDefinition: SettingsModel.getCurrentPreviewVideoDefinition() property real cameraRatio: previewDefinition.height > 0 ? previewDefinition.width/previewDefinition.height : 1.0 property int minSize: Math.min( stickerView.height, stickerView.width) property int cameraHeight: Math.min(Math.min(cameraRatio * minSize, stickerView.width) / cameraRatio, minSize) property int cameraWidth: cameraRatio * cameraHeight anchors.centerIn: parent height: cameraHeight width : cameraWidth callModel: mainItem.callModel conferenceInfoModel: mainItem.conferenceInfoModel ? mainItem.conferenceInfoModel : mainItem.callModel ? mainItem.callModel.conferenceModel : null deactivateCamera: !mainItem.previewLoaderEnabled || mainItem.isEnded /* image: mainItem._sipAddressObserver && mainItem._sipAddressObserver.contact && mainItem._sipAddressObserver.contact.vcard.avatar username: mainItem.conferenceInfoModel ? mainItem.conferenceInfoModel.subject : (mainItem._sipAddressObserver ? UtilsCpp.getDisplayName(mainItem._sipAddressObserver.peerAddress) : '') */ avatarRatio: 2/3 showCustomButton: !mainItem.callModel showUsername: false customButtonColorSet : WaitingRoomStyle.buttons.options customButtonToggled: multimediaLoader.active onVideoDefinitionChanged: previewDefinition = SettingsModel.getCurrentPreviewVideoDefinition() onCustomButtonClicked: multimediaLoader.active = !multimediaLoader.active } } Loader{ id: multimediaLoader Layout.fillHeight: true Layout.leftMargin: 0 Layout.rightMargin: 10 Layout.minimumHeight: item? item.fitHeight : 0 Layout.minimumWidth: item? item.fitWidth : 0 sourceComponent: multimediaComponent active: false Component{ id: multimediaComponent MultimediaParametersDialog{ radius: 8 flat: true showMargins: true fixedSize: false onExitStatus: multimediaLoader.active = false } } } } ColumnLayout{ Layout.fillWidth: true //Layout.preferredHeight: 120 + 30 Layout.topMargin: 20 Layout.bottomMargin: 70 Layout.alignment: Qt.AlignCenter visible: !mainItem.conferenceInfoModel spacing: 10 Text{ Layout.alignment: Qt.AlignCenter text: mainItem._sipAddressObserver ? UtilsCpp.getDisplayName(mainItem._sipAddressObserver.peerAddress) : '' color: WaitingRoomStyle.callee.color font.pointSize: WaitingRoomStyle.callee.displayNamePointSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter visible: !mainItem.conferenceInfoModel } Text{ Layout.fillWidth: true text: mainItem.callModel && SipAddressesModel.cleanSipAddress(mainItem.callModel.peerAddress) color: WaitingRoomStyle.callee.color font.pointSize: WaitingRoomStyle.callee.addressPointSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter visible: mainItem.callModel && !mainItem.conferenceInfoModel } } // ------------------------------------------------------------------------- // Action Buttons. // ------------------------------------------------------------------------- RowLayout{ Layout.preferredHeight: 40 Layout.alignment: Qt.AlignCenter Layout.topMargin: 20 Layout.bottomMargin: 15 visible: mainItem.conferenceInfoModel && !mainItem.isEnded spacing: 30 TextButtonA { //: 'Cancel' : Cancel button. text: qsTr('cancelButton') capitalization: Font.AllUppercase onClicked: { mainItem.close() if(mainItem.callModel) callModel.terminate() mainItem.cancel() } } TextButtonB { //: 'Start' : Button label for starting the conference. text: qsTr('startButton') capitalization: Font.AllUppercase visible: !mainItem.callModel onClicked: {CallsListModel.launchVideoCall(conferenceInfoModel.uri, '', 0, { video: modeChoice.selectedMode != 2 , camera: camera.cameraEnabled , micro: !micro.microMuted , audio: !speaker.speakerMuted , layout: (modeChoice.selectedMode % 2)}) } } } Item{ Layout.fillWidth: true Layout.preferredHeight: actionsButtons.height Layout.bottomMargin: 30 Layout.topMargin: 20 visible: !mainItem.isEnded // Action buttons RowLayout{ id: actionsButtons anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: parent.bottom height: 40 spacing: 10 ActionSwitch { id: micro visible: SettingsModel.muteMicrophoneEnabled property bool microMuted: false onMicroMutedChanged: if(mainItem.callModel) mainItem.callModel.microMuted = microMuted isCustom: true backgroundRadius: 90 colorSet: microMuted ? WaitingRoomStyle.buttons.microOff : WaitingRoomStyle.buttons.microOn onClicked: microMuted = !microMuted } VuMeter { enabled: !micro.microMuted Timer { interval: 50 repeat: true running: parent.enabled onTriggered: parent.value = SettingsModel.getMicVolume() } } ActionSwitch { id: speaker property bool speakerMuted: false onSpeakerMutedChanged: if(mainItem.callModel) mainItem.callModel.speakerMuted = speakerMuted isCustom: true backgroundRadius: 90 colorSet: speakerMuted ? WaitingRoomStyle.buttons.speakerOff : WaitingRoomStyle.buttons.speakerOn onClicked: speakerMuted = !speakerMuted } ActionSwitch { id: camera property bool cameraEnabled: true visible: !mainItem.callModel isCustom: true backgroundRadius: 90 colorSet: cameraEnabled ? WaitingRoomStyle.buttons.cameraOn : WaitingRoomStyle.buttons.cameraOff enabled: modeChoice.selectedMode != 2 onClicked: cameraEnabled = !cameraEnabled onCameraEnabledChanged: mainItem.previewLoaderEnabled = cameraEnabled } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: WaitingRoomStyle.buttons.call visible: false && !callModel && conferenceInfoModel onClicked: {mainItem.close() CallsListModel.launchVideoCall(conferenceInfoModel.uri, '', 0, { video: modeChoice.selectedMode != 2 , camera: camera.cameraEnabled , micro: !micro.microMuted , audio: !speaker.speakerMuted , layout: (modeChoice.selectedMode % 2)}) } } ActionButton{ isCustom: true backgroundRadius: width/2 colorSet: WaitingRoomStyle.buttons.hangup visible: callModel onClicked: { mainItem.close() if(mainItem.callModel) callModel.terminate() mainItem.cancel() } } } ActionButton{ id: modeChoice property int selectedMode: SettingsModel.videoConferenceLayout anchors.centerIn: parent anchors.horizontalCenterOffset: contentsStack.cameraWidth/2 - modeChoice.width/2 visible: !mainItem.callModel toggled: layoutMenu.visible isCustom: true backgroundRadius: width/2 colorSet: selectedMode == LinphoneEnums.ConferenceLayoutGrid ? WaitingRoomStyle.buttons.gridLayout : selectedMode == LinphoneEnums.ConferenceLayoutActiveSpeaker ? WaitingRoomStyle.buttons.activeSpeakerLayout : WaitingRoomStyle.buttons.audioOnly onClicked:layoutMenu.visible = !layoutMenu.visible onSelectedModeChanged: mainItem.previewLoaderEnabled = (selectedMode != 2) && camera.cameraEnabled Rectangle{ id: layoutMenu anchors.bottom: parent.top anchors.horizontalCenter: parent.horizontalCenter anchors.bottomMargin: 10 height: menuLayout.implicitHeight + 10 width: parent.width + 10 visible: false color: WaitingRoomStyle.menuColor radius: 5 ColumnLayout{ id: menuLayout anchors.centerIn: parent ActionButton{ isCustom: true backgroundRadius: width/2 toggled: modeChoice.selectedMode == LinphoneEnums.ConferenceLayoutGrid colorSet: WaitingRoomStyle.buttons.gridLayout onClicked: {modeChoice.selectedMode = LinphoneEnums.ConferenceLayoutGrid ; layoutMenu.visible = false} } ActionButton{ isCustom: true backgroundRadius: width/2 toggled: modeChoice.selectedMode == LinphoneEnums.ConferenceLayoutActiveSpeaker colorSet: WaitingRoomStyle.buttons.activeSpeakerLayout onClicked: {modeChoice.selectedMode = LinphoneEnums.ConferenceLayoutActiveSpeaker ; layoutMenu.visible = false} } ActionButton{ isCustom: true backgroundRadius: width/2 toggled: modeChoice.selectedMode == 2 colorSet: WaitingRoomStyle.buttons.audioOnly onClicked: {modeChoice.selectedMode = 2 ; layoutMenu.visible = false} } } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Dialog/000077500000000000000000000000001434616504300230725ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Dialog/NewConference.qml000066400000000000000000000544031434616504300263340ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 import Common.Styles 1.0 import Units 1.0 import UtilsCpp 1.0 import ColorsList 1.0 import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= DialogPlus { id: conferenceManager property bool isNew: !conferenceInfoModel || conferenceInfoModel.uri === '' property ConferenceInfoModel conferenceInfoModel: ConferenceInfoModel{} onConferenceInfoModelChanged: { dateField.setDate(conferenceManager.conferenceInfoModel.dateTime); timeField.setTime(conferenceManager.conferenceInfoModel.dateTime); selectedParticipants.setAddresses(conferenceInfoModel) } property bool forceSchedule : false property int creationState: 0// -1=error, 0=Idle, 1=processing, 2=processed Connections{ target: conferenceInfoModel onConferenceCreated: { if( conferenceInfoModel.inviteMode == 0 ) { conferenceManager.creationState = 2 conferenceManager.exit(1) } } onConferenceCreationFailed:{ conferenceManager.creationState = -1 } onInvitationsSent: { conferenceManager.creationState = 2 conferenceManager.exit(1) } } readonly property int minParticipants: 1 buttons: [ ColumnLayout{ Layout.fillWidth: true Layout.topMargin:15 Layout.alignment: Qt.AlignLeft Layout.leftMargin: 15 spacing:4 visible: false // TODO Text { Layout.fillWidth: true //: 'Would you like to encrypt your meeting ?' : Ask about setting the meeting as secured. text:qsTr('askEncryption') color: NewConferenceStyle.askEncryptionColor font.pointSize: NewConferenceStyle.titles.pointSize font.weight: Font.DemiBold } Item{ Layout.fillWidth: true Layout.preferredHeight: 50 Icon{ id:secureOff anchors.left:parent.left anchors.leftMargin : 0 anchors.verticalCenter: parent.verticalCenter width:20 height:20 icon: 'secure_off' iconSize:20 } Switch{ id:secureSwitch anchors.left:secureOff.right anchors.leftMargin : 5 anchors.verticalCenter: parent.verticalCenter width:50 enabled:true checked: !SettingsModel.standardChatEnabled && SettingsModel.secureChatEnabled onClicked: { var newCheck = checked if(SettingsModel.standardChatEnabled && checked || SettingsModel.secureChatEnabled && !checked) newCheck = !checked; checked = newCheck; } indicatorStyle: SwitchStyle.aux } Icon{ id:secureOn anchors.left:secureSwitch.right anchors.leftMargin : 15 anchors.verticalCenter: parent.verticalCenter width:20 height:20 icon: 'secure_on' iconSize:20 } } }, TextButtonA { //: 'Cancel' : Cancel button text: qsTr('cancelButton') capitalization: Font.AllUppercase onClicked: exit(0) }, TextButtonB { enabled: conferenceManager.creationState != 1 && selectedParticipants.count >= conferenceManager.minParticipants && subject.text != '' && AccountSettingsModel.conferenceUri != '' //: 'Launch' : Launch button text: conferenceManager.isNew ? qsTr('launchButton') //: 'Update' : Update button : qsTr('updateButton') capitalization: Font.AllUppercase function getInviteMode(){ return scheduledSwitch.checked ? (inviteAppAccountCheckBox.checked ? 1 : 0) + (inviteEmailCheckBox.checked ? 2 : 0) : 0 } onClicked: { if( rightStackView.currentItemType !== 0) { rightStackView.currentItemType = 0 rightStackView.pop() } conferenceManager.creationState = 1 if( scheduledSwitch.checked){ var startDateTime = Utils.buildDate(dateField.getDate(), timeField.getTime()) conferenceInfoModel.isScheduled = true startDateTime.setSeconds(0) conferenceInfoModel.timeZoneModel = timeZoneField.model.getAt(timeZoneField.currentIndex) conferenceInfoModel.dateTime = startDateTime conferenceInfoModel.duration = durationField.model[durationField.currentIndex].value }else conferenceInfoModel.isScheduled = false conferenceInfoModel.subject = subject.text conferenceInfoModel.description = description.text conferenceInfoModel.setParticipants(selectedParticipants.participantListModel) conferenceInfoModel.inviteMode = getInviteMode(); conferenceInfoModel.createConference(false && secureSwitch.checked) // TODO remove false when Encryption is ready to use } TooltipArea{ visible: AccountSettingsModel.conferenceUri == '' || subject.text == '' || selectedParticipants.count < conferenceManager.minParticipants maxWidth: participantView.width delay:0 text: { var txt = '\n'; if( subject.text == '') //: 'You need to fill a subject.' : Tooltip to warn a user on missing field. txt ='- ' + qsTr('missingSubject') + '\n' if( selectedParticipants.count < conferenceManager.minParticipants) //: 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the meeting creation. txt += '- ' + qsTr('missingParticipants', '', conferenceManager.minParticipants).arg(conferenceManager.minParticipants) + '\n' if( AccountSettingsModel.conferenceUri == '') //: 'You need to set the meeting URI in your account settings to create a meeting based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. txt += '- ' + qsTr('missingConferenceURI') + '\n' return txt; } } } , Icon{ id: creationStatus height: 10 width: 10 visible: icon != '' icon:conferenceManager.creationState==-1 ? 'led_red' : '' } ] buttonsAlignment: Qt.AlignRight buttonsLeftMargin: 15 //: 'Start a video conference' : Title of a popup about creation of a video conference title: conferenceManager.isNew ? qsTr('newConferenceTitle') //: 'Update the meeting' : Title of a popup about updating configuration of a video conference. : qsTr('updateConferenceTitle') height: window.height - 100 width: window.width - 100 expandHeight: true // --------------------------------------------------------------------------- RowLayout { height: parent.height width: parent.width spacing: 0 ColumnLayout { Layout.fillHeight: true //Layout.fillWidth: true Layout.preferredWidth: 3*conferenceManager.width/5 Layout.topMargin: 10 spacing: 10 ColumnLayout { Layout.fillWidth: true Layout.rightMargin: 15 spacing:5 Text{ id: subjectTitle textFormat: Text.RichText //: 'Subject' : Label of a text field about the subject of the conference text :qsTr('subjectLabel') +'*' color: NewConferenceStyle.titles.textColor font.pointSize: NewConferenceStyle.titles.pointSize font.weight: NewConferenceStyle.titles.weight } TextField { id:subject Layout.fillWidth: true //: 'Give a subject' : Placeholder in a form about setting a subject placeholderText : qsTr('subjectPlaceholder') text: conferenceInfoModel && conferenceInfoModel.subject || '' Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() TooltipArea{ //: 'Current subject of the Meeting. It cannot be empty' //~ Tooltip Explanation about the subject of the meeting text : qsTr('subjectTooltip') } } } Rectangle{ Layout.fillWidth: true //Layout.fillHeight: true Layout.fillHeight: conferenceInfoModel.isScheduled Layout.preferredHeight: scheduledSwitch.checked ? parent.parent.height-subjectTitle.contentHeight-subject.contentHeight-5 : 50 Layout.rightMargin: 15 color: '#F7F7F7' ColumnLayout{ anchors.fill: parent spacing: 0 RowLayout{ Layout.fillWidth: true Layout.preferredHeight: 50 Switch{ id:scheduledSwitch Layout.leftMargin : 5 Layout.alignment: Qt.AlignVCenter width:50 enabled: true checked: conferenceInfoModel.isScheduled onClicked: { if( !conferenceManager.forceSchedule) checked = !checked } indicatorStyle: SwitchStyle.aux } Text { Layout.fillWidth: true Layout.rightMargin: 15 //: 'Would you like to schedule your meeting?' : Ask about setting the meeting as scheduled. text: qsTr('newConferenceScheduleTitle') color: NewConferenceStyle.titles.textColor font.pointSize: NewConferenceStyle.titles.pointSize font.weight: NewConferenceStyle.titles.weight wrapMode: Text.WordWrap } } GridLayout{ id: scheduleForm visible: scheduledSwitch.checked Layout.fillWidth: true Layout.fillHeight: true Layout.margins: 10 columns: 4 property var locale: Qt.locale() property date currentDate: new Date() property int cellWidth: (parent.width-15-20)/columns Text{textFormat: Text.RichText; //: 'Date' : Date label. text: qsTr('newConferenceDate')+'*' Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize } Text{textFormat: Text.RichText //: 'Time' : Time label. text: qsTr('newConferenceTimeTitle')+'*' Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize } Text{textFormat: Text.RichText //: 'Duration' : Duration label. text: qsTr('newConferenceDurationTitle') Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize } Text{textFormat: Text.RichText //: 'Timezone' : Timezone label. text: qsTr('newConferenceTimezoneTitle') Layout.preferredWidth: parent.cellWidth; wrapMode: Text.WordWrap; color: NewConferenceStyle.titles.textColor; font.weight: NewConferenceStyle.titles.weight; font.pointSize: NewConferenceStyle.titles.pointSize } TextField{id: dateField; Layout.preferredWidth: parent.cellWidth color: NewConferenceStyle.fields.textColor; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize property date currentDate: new Date() function getDate(){ return currentDate } function setDate(date){ currentDate = date text = Utils.exactDate(date).toLocaleDateString(scheduleForm.locale, Qt.ISODate) } text: conferenceManager.conferenceInfoModel ? Utils.exactDate(conferenceManager.conferenceInfoModel.dateTime).toLocaleDateString(scheduleForm.locale, Qt.ISODate) : '' icon: 'drop_down_custom' MouseArea{ anchors.fill: parent onClicked: { window.attachVirtualWindow(Utils.buildCommonDialogUri('DateTimeDialog'), {hideOldDates:true, showDatePicker:true, selectedDate: new Date(dateField.getDate())} , function (status) { if(status){ dateField.setDate(status.selectedDate) } } ) } } } TextField{id: timeField; Layout.preferredWidth: parent.cellWidth color: NewConferenceStyle.fields.textColor; font.weight: NewConferenceStyle.fields.weight; font.pointSize: NewConferenceStyle.fields.pointSize function getTime(){ return Date.fromLocaleTimeString(scheduleForm.locale, timeField._text, 'hh:mm') } function setTime(date){ _text = date.toLocaleTimeString(scheduleForm.locale, 'hh:mm') text = UtilsCpp.toTimeString(date, 'hh:mm')// Display the unchanged time } // hidden time to be used from JS : JS Local time can be wrong on Windows because of daylights that are not takken account. property string _text: conferenceManager.conferenceInfoModel? conferenceManager.conferenceInfoModel.dateTime.toLocaleTimeString(scheduleForm.locale, 'hh:mm') : '' text: conferenceManager.conferenceInfoModel? UtilsCpp.toTimeString(conferenceManager.conferenceInfoModel.dateTimeUtc, 'hh:mm') : '' icon: 'drop_down_custom' onEditingFinished: if(rightStackView.currentItemType === 2) { rightStackView.currentItemType = 0 rightStackView.pop() } MouseArea{ anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right width: parent.width-50 onClicked: { window.attachVirtualWindow(Utils.buildCommonDialogUri('DateTimeDialog'), {showTimePicker:true, selectedTime: new Date(timeField.getTime())} , function (status) { if(status){ timeField.setTime(status.selectedTime) } } ) } } } ComboBox{ id: durationField Layout.preferredWidth: parent.cellWidth; currentIndex: conferenceManager.conferenceInfoModel && conferenceManager.conferenceInfoModel.duration >= 1800 ? conferenceManager.conferenceInfoModel.duration / 1800 - 1 : 1 model: [{text:Utils.formatDuration(30*60), value:30} ,{text:Utils.formatDuration(60*60), value:60} ,{text:Utils.formatDuration(120*60), value:120} ,{text:Utils.formatDuration(240*60), value:240} ] textRole: "text" selectionWidth: 200 //rootItem: conferenceManager } ComboBox{ id: timeZoneField Layout.preferredWidth: parent.cellWidth; currentIndex: conferenceManager.conferenceInfoModel ? model.getIndex(conferenceManager.conferenceInfoModel.timeZoneModel) : -1 model: TimeZoneProxyModel{} textRole: "displayText" selectionWidth: 500 rootItem: conferenceManager } function updateDateTime(){ var storedDate if( dateField.text != '' && timeField.text != ''){ storedDate = Utils.buildDate(dateField.getDate(), timeField.getTime() ) }else storedDate = new Date() var currentDate = new Date() if(currentDate >= storedDate){ var nextStoredDate = UtilsCpp.addMinutes(new Date(), 1) dateField.setDate(nextStoredDate) timeField.setTime(nextStoredDate) if(rightStackView.currentItemType === 2) rightStackView.currentItem.selectedTime = nextStoredDate } } Timer{ running: scheduleForm.visible && conferenceManager.isNew repeat: true interval: 1000 triggeredOnStart: true onTriggered: { if(conferenceManager.isNew) scheduleForm.updateDateTime() } } } ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true Layout.bottomMargin: 10 Layout.rightMargin: 15 Layout.leftMargin: 5 spacing:5 visible: scheduledSwitch.checked Text{ Layout.fillWidth: true Layout.preferredHeight: 20 textFormat: Text.RichText //: 'Add a description' : Label of a text field about the description of the conference text : qsTr('newConferenceDescriptionTitle') color: NewConferenceStyle.titles.textColor font.pointSize: NewConferenceStyle.titles.pointSize font.weight: NewConferenceStyle.titles.weight } TextAreaField { id: description Layout.fillWidth: true Layout.fillHeight: true //: 'Description' : Placeholder in a form about setting a description placeholderText : qsTr('newConferenceDescriptionPlaceholder') text: conferenceManager.conferenceInfoModel ? conferenceManager.conferenceInfoModel.description : '' Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() TooltipArea{ //: 'This description will describe the meeting' : Explanation about the description of the meeting text : qsTr('newConferenceDescriptionTooltip') } } CheckBoxText { id: inviteAppAccountCheckBox //: 'Send invite via %1' : Label for checkbox for sending invitations with the application. %1 is the application name. text: qsTr('newConferenceSendLinphoneInviteLabel').arg(applicationName) width: parent.width checked: true } CheckBoxText { id: inviteEmailCheckBox visible: false // TODO //: 'Send invite via Email' : Label for checkbox for sending invitations with mailer. text: qsTr('newConferenceSendEmailInviteLabel') width: parent.width } } }// ColumnLayout }// Rectangle Item{// Spacer //visible: !scheduledSwitch.checked Layout.fillHeight: true } } StackView{ id: rightStackView property int currentItemType: 0 Layout.fillHeight: true Layout.preferredWidth: 2*conferenceManager.width/5 Layout.minimumWidth: 200 Layout.topMargin: 10 Layout.bottomMargin: 10 clip: true // ------------------------------------------------------------------------- // See and remove selected addresses. // ------------------------------------------------------------------------- initialItem: ColumnLayout{ Rectangle{ Layout.fillHeight: true Layout.fillWidth: true border.width: 1 border.color: NewConferenceStyle.addressesBorderColor ColumnLayout { anchors.fill: parent anchors.topMargin: 15 anchors.leftMargin: 10 anchors.rightMargin: 10 spacing: 0 SmartSearchBar { id: smartSearchBar Layout.fillWidth: true Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing Layout.leftMargin: 5 Layout.rightMargin: 5 showHeader:false isMandatory: true maxMenuHeight: MainWindowStyle.searchBox.maxHeight //: 'Select participants' : Placeholder for a search on participant to add them in selection. placeholderText: qsTr('participantSelectionPlaceholder') //: 'Search in your contacts or add a custom one to the conference.' tooltipText: qsTr('participantSelectionTooltip') actions:[{ colorSet: NewConferenceStyle.addParticipant, secure: SettingsModel.secureChatEnabled, visible: true, secureIconVisibleHandler : function(entry) { return entry && entry.sipAddress ? UtilsCpp.hasCapability(entry.sipAddress, LinphoneEnums.FriendCapabilityLimeX3Dh) : false }, handler: function (entry) { if(entry){ selectedParticipants.addAddress(entry.sipAddress) smartSearchBar.addAddressToIgnore(entry.sipAddress); } }, }] onEntryClicked: { selectedParticipants.addAddress(entry.sipAddress) smartSearchBar.addAddressToIgnore(entry.sipAddress); } } Text{ Layout.preferredHeight: 20 Layout.rightMargin: 65 Layout.alignment: Qt.AlignRight | Qt.AlignBottom Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing //: 'Admin' : Admin(istrator) //~ one word for admin status text : qsTr('adminStatus') color: NewConferenceStyle.addressesAdminColor font.pointSize: Units.dp * 11 font.weight: Font.Light visible: participantView.count > 0 } ScrollableListViewField { Layout.fillHeight: true Layout.fillWidth: true Layout.bottomMargin: 5 textFieldStyle: TextFieldStyle.unbordered ParticipantsView { id: participantView anchors.fill: parent showSubtitle:false showSwitch : conferenceManager.isNew showSeparator: false isSelectable: false showInvitingIndicator: false function removeParticipant(entry){ smartSearchBar.removeAddressToIgnore(entry.sipAddress) selectedParticipants.removeModel(entry) } actions: [{ colorSet: NewConferenceStyle.removeParticipant, secure:0, visible:true, //: 'Remove this participant from the selection' : Explanation about removing participant from a selection //~ Tooltip This is a tooltip tooltipText: qsTr('removeParticipantSelection'), handler: function (entry) { removeParticipant(entry) } }] genSipAddress: '' model: ParticipantProxyModel { id:selectedParticipants chatRoomModel:null } onEntryClicked: participantView.showSubtitle = !participantView.showSubtitle } } } } Item{ Layout.fillWidth: true Layout.preferredHeight: 20 Text{ anchors.fill:parent textFormat: Text.RichText //: 'Required' : Word relative to a star to explain that it is a requirement (Field form) text : '* '+qsTr('requiredField') color: NewConferenceStyle.requiredColor font.pointSize: Units.dp * 8 } } } //---------------------------------------------------- // STACKVIEWS //---------------------------------------------------- } } foregroundItem: Item{ id: busyPanel anchors.fill: parent visible:conferenceManager.creationState == 1 MouseArea{// Grabber anchors.fill: parent cursorShape: Qt.ArrowCursor onClicked:{ window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { //: 'Do you want to close this form ?' : confirmation text for exiting the creatoin form descriptionText: qsTr('confirmFormExit'), }, function (status) { if (status) { exit(0) } }) } } Rectangle{ anchors.fill: parent opacity: 0.6 color: 'white' } ColumnLayout{ anchors.centerIn: parent spacing: 10 BusyIndicator{ Layout.preferredHeight: 20 Layout.preferredWidth: 20 Layout.alignment: Qt.AlignCenter color: NewConferenceStyle.busy.color } Text{ Layout.fillWidth: true color: NewConferenceStyle.busy.color //: 'Operations in progress, please wait' : Waiting message till the end of operations when creating a conference. text: qsTr('busyOperations') font.pointSize: NewConferenceStyle.busy.pointSize } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/000077500000000000000000000000001434616504300225575ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant.qml000066400000000000000000000014601434616504300252440ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Window 2.2 import Utils 1.0 import Common 1.0 import App.Styles 1.0 // ============================================================================= Item { id: assistant // --------------------------------------------------------------------------- Rectangle { anchors.fill: parent color: AssistantStyle.color } function pushView (view, properties) { stack.pushView(view, properties) } function getView (index) { return stack.getView(index) } function popView () { stack.popView() } // --------------------------------------------------------------------------- StackView { id: stack anchors.fill: parent viewsPath: 'qrc:/ui/views/App/Main/Assistant/' initialItem: viewsPath + 'AssistantHome.qml' onExit:window.setView('Home') } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/000077500000000000000000000000001434616504300245305ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/ActivateAppSipAccountWithEmail.qml000066400000000000000000000033021434616504300332370ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import Utils 1.0 import App.Styles 1.0 // ============================================================================= AssistantAbstractView { property var assistantModel backEnabled: false title: qsTr('activateAppSipAccount').replace('%1', Qt.application.name.toUpperCase()) mainAction: requestBlock.execute mainActionEnabled: !requestBlock.loading mainActionLabel: qsTr('confirmAction') Column { anchors.centerIn: parent spacing: ActivateAppSipAccountWithEmailStyle.spacing width: parent.width Text { color: ActivateAppSipAccountWithEmailStyle.activationSteps.color font.pointSize: ActivateAppSipAccountWithEmailStyle.activationSteps.pointSize horizontalAlignment: Text.AlignHCenter text: qsTr('activationSteps').replace('%1', assistantModel.email) width: parent.width wrapMode: Text.WordWrap } RequestBlock { id: requestBlock action: assistantModel.activate width: parent.width } } // --------------------------------------------------------------------------- // Assistant. // --------------------------------------------------------------------------- Connections { target: assistantModel onActivateStatusChanged: { requestBlock.stop(error) if (!error.length) { function quitToHome (window) { window.unlockView() window.setView('Home') } var codecInfo = VideoCodecsModel.getCodecInfo('H264') if (codecInfo.downloadUrl) { Utils.openCodecOnlineInstallerDialog(window, codecInfo, quitToHome) } else { quitToHome(window) } } } } } ActivateAppSipAccountWithPhoneNumber.qml000066400000000000000000000037511434616504300343630ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistantimport QtQuick 2.7 import Common 1.0 import Linphone 1.0 import Utils 1.0 import App.Styles 1.0 // ============================================================================= AssistantAbstractView { property var assistantModel backEnabled: false title: qsTr('activateAppSipAccount').replace('%1', Qt.application.name.toUpperCase()) mainAction: requestBlock.execute mainActionEnabled: activationCode.length === 4 && !requestBlock.loading mainActionLabel: qsTr('confirmAction') Column { anchors.centerIn: parent spacing: ActivateAppSipAccountWithPhoneNumberStyle.spacing width: parent.width Text { color: ActivateAppSipAccountWithPhoneNumberStyle.activationSteps.color font.pointSize: ActivateAppSipAccountWithPhoneNumberStyle.activationSteps.pointSize horizontalAlignment: Text.AlignHCenter text: qsTr('activationSteps').replace('%1', assistantModel.phoneNumber) width: parent.width wrapMode: Text.WordWrap } TextField { id: activationCode anchors.horizontalCenter: parent.horizontalCenter validator: IntValidator { bottom: 0 top: 9999 } onTextChanged: assistantModel.activationCode = text } RequestBlock { id: requestBlock action: assistantModel.activate width: parent.width } } // --------------------------------------------------------------------------- // Assistant. // --------------------------------------------------------------------------- Connections { target: assistantModel onActivateStatusChanged: { requestBlock.stop(error) if (!error.length) { function quitToHome (window) { window.unlockView() window.setView('Home') } var codecInfo = VideoCodecsModel.getCodecInfo('H264') if (codecInfo.downloadUrl) { Utils.openCodecOnlineInstallerDialog(window, codecInfo, quitToHome) } else { quitToHome(window) } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/AssistantAbstractView.qml000066400000000000000000000071321434616504300315360ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import App.Styles 1.0 // ============================================================================= Item { id: view // --------------------------------------------------------------------------- property alias mainActionEnabled: mainActionButton.enabled property alias mainActionLabel: mainActionButton.text property var mainAction property alias description: description.text property alias title: title.text property bool backEnabled: true property bool maximized: false // Used to stretch content to fit all the view (the title will be set to top) default property alias _content: content.data property alias contentItem: content property int decorationHeight: title.implicitHeight + title.anchors.topMargin +description.implicitHeight + description.anchors.topMargin +content.anchors.topMargin +buttons.implicitHeight+AssistantAbstractViewStyle.info.spacing // --------------------------------------------------------------------------- height: (maximized?stack.height:AssistantAbstractViewStyle.content.height) width: (maximized?stack.width:AssistantAbstractViewStyle.content.width) anchors.horizontalCenter: maximized || !parent? undefined : parent.horizontalCenter anchors.verticalCenter: maximized || !parent? undefined : parent.verticalCenter // --------------------------------------------------------------------------- // Info. // --------------------------------------------------------------------------- Text { id: title anchors.top:parent.top anchors.topMargin:(visible?AssistantAbstractViewStyle.info.spacing:0) anchors.horizontalCenter: parent.horizontalCenter color: AssistantAbstractViewStyle.info.title.color elide: Text.ElideRight font { pointSize: AssistantAbstractViewStyle.info.title.pointSize bold: true } horizontalAlignment: Text.AlignHCenter width: parent.width visible: text.length > 0 height:(visible?contentHeight:0) } Text { id: description anchors.top:title.bottom anchors.topMargin:(visible?AssistantAbstractViewStyle.info.spacing:0) anchors.horizontalCenter: parent.horizontalCenter color: AssistantAbstractViewStyle.info.description.color elide: Text.ElideRight font.pointSize: AssistantAbstractViewStyle.info.description.pointSize horizontalAlignment: Text.AlignHCenter width: parent.width visible: text.length > 0 height:(visible?contentHeight:0) } // ------------------------------------------------------------------------- // Content. // ------------------------------------------------------------------------- Item { id: content anchors.top:description.bottom anchors.topMargin:(description.visible || title.visible?AssistantAbstractViewStyle.info.spacing:0) anchors.bottom:buttons.top anchors.horizontalCenter: parent.horizontalCenter width: parent.width } // --------------------------------------------------------------------------- // Nav buttons. // --------------------------------------------------------------------------- Row { id: buttons anchors { bottom: parent.bottom bottomMargin:AssistantAbstractViewStyle.info.spacing horizontalCenter: parent.horizontalCenter } spacing: AssistantAbstractViewStyle.buttons.spacing TextButtonA { text: qsTr('back') visible: view.backEnabled onClicked: assistant.popView() anchors.verticalCenter: parent.verticalCenter } TextButtonB { id: mainActionButton visible: !!view.mainAction onClicked: view.mainAction() anchors.verticalCenter: parent.verticalCenter } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/AssistantHome.qml000066400000000000000000000110521434616504300300240ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import ConstantsCpp 1.0 import App.Styles 1.0 // ============================================================================= ColumnLayout { spacing: 0 // --------------------------------------------------------------------------- // Info. // --------------------------------------------------------------------------- Item { id: infoItem Layout.fillHeight: true Layout.fillWidth: true ColumnLayout { anchors.verticalCenter: parent.verticalCenter spacing: 0 height: AssistantHomeStyle.info.height width: parent.width Icon { //anchors.horizontalCenter: parent.horizontalCenter Layout.alignment: Qt.AlignHCenter icon: 'home_account_assistant' iconSize: AssistantHomeStyle.info.iconSize } Text { height: AssistantHomeStyle.info.title.height Layout.fillWidth: true color: AssistantHomeStyle.info.title.color elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font { bold: true pointSize: AssistantHomeStyle.info.title.pointSize } text: qsTr('homeTitle') } Text { height: AssistantHomeStyle.info.description.height Layout.fillWidth: true //width: parent.width color: AssistantHomeStyle.info.description.color elide: Text.ElideRight font.pointSize: AssistantHomeStyle.info.description.pointSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: qsTr('homeDescription') } } } // --------------------------------------------------------------------------- // Buttons. // --------------------------------------------------------------------------- CheckBoxText{ id: cguCheckBox Layout.bottomMargin: 10 Layout.maximumWidth: infoItem.width Layout.alignment: Qt.AlignHCenter visible: applicationVendor != '' && ConstantsCpp.CguUrl != '' && ConstantsCpp.PrivatePolicyUrl != '' checked: SettingsModel.cguAccepted onCheckedChanged: SettingsModel.cguAccepted = checked //: 'I accept %1's %2terms of use%3 and %4privacy policy%5' : where %1 is the vendor name and other %n are internal keywords that encapsulate links. text: qsTr('homeCgu').arg(applicationVendor).arg('< a href="'+ConstantsCpp.CguUrl+'">').arg('').arg('').arg('') } GridView { id: buttons Layout.alignment: Qt.AlignHCenter Layout.fillWidth: true Layout.maximumWidth: AssistantHomeStyle.buttons.maxWidth Layout.preferredHeight: AssistantHomeStyle.buttons.height Layout.leftMargin: AssistantStyle.leftMargin Layout.rightMargin: AssistantStyle.rightMargin Layout.bottomMargin: AssistantStyle.bottomMargin cellHeight: height / 2 cellWidth: width / 2 enabled: cguCheckBox.checked delegate: Item { height: buttons.cellHeight width: buttons.cellWidth TextButtonA { anchors { fill: parent margins: AssistantHomeStyle.buttons.spacing } enabled: cguCheckBox.checked && SettingsModel[$viewType.charAt(0).toLowerCase() + $viewType.slice(1) + "Enabled"] text: $text.replace('%1', Qt.application.name.toUpperCase()) onClicked:{ assistant.pushView($view, $props) } } } model: ListModel { Component.onCompleted: { insert(0, { $text: qsTr('createAppSipAccount'), $view: SettingsModel.useWebview() ? 'CreateAppSipAccountWithWebView' : 'CreateAppSipAccount', $viewType: 'CreateAppSipAccount', $props: SettingsModel.useWebview() ? {defaultUrl: SettingsModel.assistantRegistrationUrl, defaultLogoutUrl:SettingsModel.assistantLogoutUrl, configFilename: 'create-app-sip-account.rc'} : {} }) append({ $text: qsTr('useAppSipAccount'), $view: SettingsModel.useWebview() ? 'CreateAppSipAccountWithWebView' : 'UseAppSipAccount', $viewType: 'UseAppSipAccount', $props: SettingsModel.useWebview() ? {defaultUrl: SettingsModel.assistantLoginUrl, defaultLogoutUrl:SettingsModel.assistantLogoutUrl, configFilename: 'use-app-sip-account.rc'} : {} }) append({ $text: qsTr('useOtherSipAccount'), $view: 'UseOtherSipAccount', $viewType: 'UseOtherSipAccount', $props: {} }) append( { $text: qsTr('fetchRemoteConfiguration'), $view: 'FetchRemoteConfiguration', $viewType: 'FetchRemoteConfiguration', $props: {} }) } } interactive: false } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/CreateAppSipAccount.qml000066400000000000000000000021121434616504300310740ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import App.Styles 1.0 // ============================================================================= AssistantAbstractView { description: qsTr('createAppSipAccountDescription') title: qsTr('createAppSipAccountTitle').replace('%1', Qt.application.name.toUpperCase()) // --------------------------------------------------------------------------- // Menu. // --------------------------------------------------------------------------- Column { anchors.centerIn: parent spacing: CreateAppSipAccountStyle.buttons.spacing width: CreateAppSipAccountStyle.buttons.button.width TextButtonA { text: qsTr('withPhoneNumber') height: CreateAppSipAccountStyle.buttons.button.height width: parent.width onClicked: assistant.pushView('CreateAppSipAccountWithPhoneNumber') } TextButtonA { text: qsTr('withEmailAddress') height: CreateAppSipAccountStyle.buttons.button.height width: parent.width onClicked: assistant.pushView('CreateAppSipAccountWithEmail') } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/CreateAppSipAccountWithEmail.qml000066400000000000000000000056701434616504300327140ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 // ============================================================================= AssistantAbstractView { id: view property alias emailError: email.error property alias passwordError: password.error property alias usernameError: username.error title: qsTr('createAppSipAccountTitle').replace('%1', Qt.application.name.toUpperCase()) mainAction: requestBlock.execute mainActionEnabled: email.text.length && password.text.length && passwordConfirmation.text === password.text && username.text.length && !emailError.length && !passwordError.length && !requestBlock.loading && !usernameError.length mainActionLabel: qsTr('confirmAction') Column { anchors.fill: parent Form { orientation: Qt.Vertical width: parent.width FormLine { FormGroup { label: qsTr('usernameLabel') TextField { id: username onTextChanged: assistantModel.username = text } } FormGroup { label: qsTr('displayNameLabel') TextField { onTextChanged: assistantModel.displayName = text } } } FormLine { FormGroup { label: qsTr('emailLabel') TextField { id: email onTextChanged: assistantModel.email = text } } } FormLine { FormGroup { label: qsTr('passwordLabel') PasswordField { id: password onTextChanged: { assistantModel.password = text assistantModel.checkPassword() } } } } FormLine { FormGroup { label: qsTr('passwordConfirmationLabel') PasswordField { id: passwordConfirmation onTextChanged: assistantModel.checkPassword() } } } } RequestBlock { id: requestBlock action: assistantModel.create width: parent.width } } // --------------------------------------------------------------------------- // Assistant. // --------------------------------------------------------------------------- AssistantModel { id: assistantModel function checkPassword () { passwordConfirmation.error = password.text !== passwordConfirmation.text ? qsTr('passwordConfirmationError') : '' } configFilename: 'create-app-sip-account.rc' onEmailChanged: emailError = error onPasswordChanged: passwordError = error onUsernameChanged: usernameError = error onCreateStatusChanged: { requestBlock.stop(error) if (error.length) { return } window.lockView({ descriptionText: qsTr('quitWarning') }) assistant.pushView('ActivateAppSipAccountWithEmail', { assistantModel: assistantModel }) } } } CreateAppSipAccountWithPhoneNumber.qml000066400000000000000000000056171434616504300340310ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistantimport QtQuick 2.7 import Common 1.0 import Linphone 1.0 // ============================================================================= AssistantAbstractView { id: view property alias usernameError: username.error property alias phoneNumberError: phoneNumber.error function setCountryCode (index) { if(index>=0){ var model = country.model assistantModel.countryCode = model.data(model.index(index, 0),"countryCode") } } title: qsTr('createAppSipAccountTitle').replace('%1', Qt.application.name.toUpperCase()) mainAction: requestBlock.execute mainActionEnabled: phoneNumber.text.length && !phoneNumberError.length && !usernameError.length && !requestBlock.loading mainActionLabel: qsTr('confirmAction') Column { anchors.fill: parent Form { orientation: Qt.Vertical width: parent.width FormLine { FormGroup { label: qsTr('countryLabel') ComboBox { id: country currentIndex: model.defaultIndex model: TelephoneNumbersModel {} textRole: 'countryName' onActivated: { view.setCountryCode(index) var text = phoneNumber.text if (text.length > 0) { assistantModel.phoneNumber = text } } } } } FormLine { FormGroup { label: qsTr('phoneNumberLabel') TextField { id: phoneNumber inputMethodHints: Qt.ImhDialableCharactersOnly onTextChanged: assistantModel.phoneNumber = text } } } FormLine { FormGroup { label: qsTr('usernameLabel') TextField { id: username placeholderText: phoneNumber.text onTextChanged: assistantModel.username = text } } } FormLine { FormGroup { label: qsTr('displayNameLabel') TextField { onTextChanged: assistantModel.displayName = text } } } } RequestBlock { id: requestBlock action: assistantModel.create width: parent.width } } // --------------------------------------------------------------------------- // Assistant. // --------------------------------------------------------------------------- AssistantModel { id: assistantModel configFilename: 'create-app-sip-account.rc' Component.onCompleted: view.setCountryCode(country.model.defaultIndex) onPhoneNumberChanged: phoneNumberError = error onUsernameChanged: usernameError = error onCreateStatusChanged: { requestBlock.stop(error) if (error.length) { return } window.lockView({ descriptionText: qsTr('quitWarning') }) assistant.pushView('ActivateAppSipAccountWithPhoneNumber', { assistantModel: assistantModel }) } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/CreateAppSipAccountWithWebView.qml000066400000000000000000000111421434616504300332240ustar00rootroot00000000000000import QtQuick 2.7 import QtWebView 1.1 import QtQuick.Controls 1.3 // Busy indicator import Common 1.0 import Linphone 1.0 as Linphone import App.Styles 1.0 // ============================================================================= AssistantAbstractView { id: view maximized:true height: (parent?parent.height:0) width: (parent?parent.width:0) property string defaultUrl property string defaultLogoutUrl property string configFilename property bool printed : stack.currentItem == view onPrintedChanged: { webviewLoader.active = printed } //------------------------------- property int status : 0 // 0:nothing, -1:error, 1:ok property bool newPage : true // --------------------------------------------------------------------------- // Menu. // --------------------------------------------------------------------------- // Note : Use opacity and not visibility to allow smooth updating (when moving visibility to true, we could show the old page) Component{ id: webviewComponent WebView{ id:webview property bool isLogingOut : true state: 'hidden' Component.onCompleted: {if(webview.httpUserAgent != undefined) webview.httpUserAgent = Linphone.CoreManager.getUserAgent() // only available on Qt 5.15 (QtWebView 1.15) isLogingOut = true webview.url = view.defaultLogoutUrl } function getData(){// Check if account_infos exists in the page and retrieve data to make/update an account if(webview.loading){ view.status = 0 }else { var js = "(typeof account_infos !== 'undefined'?account_infos:'')"; webview.runJavaScript(js, function(result) { if( result == ''){ view.status = 0 }else{ webview.state = 'hidden' reloadTimer.stop(); console.log("[CreateAccount] SIP : " +result.sip); console.log("[CreateAccount] Username : " +result.username); console.log("[CreateAccount] Registrar : " +result.registrar_address); console.log("[CreateAccount] Domain : " +result.domain); if (Linphone.AccountSettingsModel.addOrUpdateAccount( { sipAddress: result.sip, serverAddress: result.registrar_address, configFilename: view.configFilename })) { console.log("[CreateAccount] Account created") view.status = 1 Linphone.AccountSettingsModel.setDefaultAccountFromSipAddress("sip:"+result.sip) } else { console.error("[CreateAccount] Cannot create account. Check logs.") view.status = -1 } } }); } } Timer {// Check data id:reloadTimer interval: 1000; running: true; repeat: true onTriggered: {webview.getData();} } onLoadingChanged: { if (loadRequest.errorString) console.error("[CreateAccount] error on loading page : " +loadRequest.errorString); if(loading){ view.newPage = true; }else if(view.newPage) { view.newPage = false; webview.runJavaScript("document.querySelector('nav').remove(); document.querySelector('footer').remove();"); } webview.state = (loading || isLogingOut ? 'hidden' : 'showed') if(!loading){ if(isLogingOut){ isLogingOut = false webview.url = view.defaultUrl }else{ reloadTimer.stop(); webview.getData(); if(view.status == 0) reloadTimer.start(); } }else reloadTimer.stop(); } states: [ State { name: 'hidden' PropertyChanges { target: webview; opacity: 0 } }, State { name: 'showed' PropertyChanges { target: webview; opacity: 1 } } ] transitions: [ Transition { from: '*'; to: 'showed' SequentialAnimation{ NumberAnimation{ properties: "opacity"; easing.type: Easing.OutBounce; duration: 500 } } }, Transition { SequentialAnimation{ NumberAnimation{ properties: "opacity"; duration: 1000 } } } ] } } Loader{ id: webviewLoader active: false anchors.fill:parent sourceComponent: webviewComponent } Rectangle{ id:statusPage anchors.fill:parent visible: webviewLoader.item && (webviewLoader.item.loading || webviewLoader.item.isLogingOut || webviewLoader.item.state == 'hidden') BusyIndicator{ id:busy anchors.centerIn : parent running:true width:CreateAppSipAccountStyle.busy.size height:CreateAppSipAccountStyle.busy.size } Icon{ visible: view.status != 0 icon: (view.status>0?"chat_read":"chat_error") iconSize:busy.width anchors.fill:busy MouseArea{ anchors.fill:parent onClicked: { assistant.popView() } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/FetchRemoteConfiguration.qml000066400000000000000000000174441434616504300322120ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Utils 1.0 import App.Styles 1.0 // ============================================================================= Item{ AssistantAbstractView { id: mainItem title: qsTr('fetchRemoteConfigurationTitle') maximized: true //: 'generate' : title button to generate a code. property string generateButtonText: qsTr('generateLabel') // --------------------------------------------------------------------------- AssistantModel { id: assistantModel property string qrcode } Connections { target: SettingsModel onRemoteProvisioningChanged: { requestBlock.stop('') window.detachVirtualWindow() window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('remoteProvisioningUpdateDescription'), }, function (status) { if (status) { App.restart() } else { window.setView('Home') } }) } onRemoteProvisioningNotChanged: requestBlock.stop(qsTr('remoteProvisioningError')) } Connections{ target: assistantModel onNewQRCodeReceived: {assistantModel.qrcode = 'image://qrcode/'+code; requestBlock.stop('')} onNewQRCodeNotReceived: requestBlock.stop(message) onProvisioningTokenReceived: {url.text = token SettingsModel.remoteProvisioning = url.text assistantModel.qrcode = '' requestBlock.stop('')} onQRCodeAttached: requestBlock.stop('Attached') onQRCodeNotAttached: requestBlock.stop(message) onQRCodeFound: { if(qRCodeRead.currentIndex == 0) url.text = token; else assistantModel.attachAccount(token) } } // --------------------------------------------------------------------------- ColumnLayout { anchors.fill: parent anchors.margins: 0 spacing: 0 Text{ Layout.alignment: Qt.AlignCenter Layout.preferredWidth: urlLayout.width font.pointSize: FetchRemoteConfigurationStyle.fieldTitles.pointSize font.weight: Font.Bold color: FetchRemoteConfigurationStyle.fieldTitles.color text: qsTr('urlLabel') } RowLayout{ id: urlLayout Layout.preferredHeight: fetchButton.fitHeight Layout.alignment: Qt.AlignCenter spacing: 10 TextField { Layout.preferredWidth: mainItem.width/2 id: url } TextButtonB { id: fetchButton Layout.preferredWidth: fitWidth Layout.preferredHeight: fitHeight addHeight: 15 onClicked: SettingsModel.remoteProvisioning = url.text text: qsTr('confirmAction') enabled: url.text.length > 0 } /*Dev Tool TextButtonB { id: testButton Layout.preferredWidth: fitWidth Layout.preferredHeight: fitHeight onClicked: assistantModel.createTestAccount() text: 'Create Test' }*/ } RequestBlock { id: requestBlock action: (function () { }) Layout.preferredWidth: parent.width } Text{ Layout.topMargin: 15 Layout.alignment: Qt.AlignCenter visible: SettingsModel.isQRCodeAvailable() font.pointSize: FetchRemoteConfigurationStyle.fieldTitles.pointSize font.weight: Font.Bold font.capitalization: Font.Capitalize color: FetchRemoteConfigurationStyle.fieldTitles.color //: 'or' : conjunction to choose between options. text: qsTr('or') } ColumnLayout{ id: simpleQRCodeOptionsView Layout.fillWidth: true Layout.margins: 15 visible: SettingsModel.isQRCodeAvailable() && !SettingsModel.developerSettingsEnabled Layout.alignment: Qt.AlignCenter spacing: 15 Rectangle{ Layout.fillHeight: true Layout.preferredWidth: height Layout.alignment: Qt.AlignCenter border.color: FetchRemoteConfigurationStyle.qRCode.borderColor radius: 20 border.width: 1 Text{ anchors.right: parent.right anchors.left: parent.left anchors.margins: 10 anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter visible: assistantModel.qrcode == '' wrapMode: Text.WordWrap font.pointSize: FetchRemoteConfigurationStyle.explanationQRCode.pointSize color: FetchRemoteConfigurationStyle.explanationQRCode.color //: 'Click on %1 to obtain your remote provisioning QR code' : Describe how to get a remote provisioning QR code by clicking on %1 button (1% is the text in button) text : qsTr('remoteProvisioningHow').arg(mainItem.generateButtonText) } Image{ anchors.fill: parent anchors.margins: 20 sourceSize.width: width sourceSize.height: height source: assistantModel.qrcode visible: source != '' } } Text{ Layout.fillWidth: true Layout.preferredHeight: contentHeight Layout.alignment: Qt.AlignCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter visible: assistantModel.qrcode != '' wrapMode: Text.WordWrap font.pointSize: FetchRemoteConfigurationStyle.explanationQRCode.pointSize color: FetchRemoteConfigurationStyle.explanationQRCode.color //: 'Scan the QR code with your phone' : Explain how to use the QRCode by flasing it. text: qsTr('scanQRCode') + '\n' //: 'In your app go in assistant - QR code provisioning' : Describe where to flash the QRCode in the mobile application. +qsTr('scanQRCodeWhere') } TextButtonB { Layout.alignment: Qt.AlignCenter text: mainItem.generateButtonText onClicked: assistantModel.requestQRCode() capitalization: Font.AllUppercase } } //------------------------------------------------------------------ // Developer Section //------------------------------------------------------------------ GridLayout{ id: allQRCodeOptionsView Layout.fillWidth: true Layout.margins: 15 visible: SettingsModel.isQRCodeAvailable() && SettingsModel.developerSettingsEnabled columns: 2 RowLayout{ Layout.fillWidth: true ComboBox { id: qRCodeGeneration model: ['URL', 'Attach token'] currentIndex:0 Component.onCompleted: {} } TextButtonB { text: mainItem.generateButtonText capitalization: Font.AllUppercase onClicked: if(qRCodeGeneration.currentIndex == 0 ) assistantModel.generateQRCode() else assistantModel.requestQRCode() } Item{ Layout.fillWidth: true } } RowLayout{ Layout.fillWidth: true Item{ Layout.fillWidth: true } ComboBox { id: qRCodeRead model: ['URL', 'Attach token'] currentIndex:0 Component.onCompleted: {} } TextButtonB { id: qQRCodeReadButton text: 'Read' onClicked:assistantModel.readQRCode() toggled: assistantModel.isReadingQRCode } } Image{ Layout.fillHeight: true Layout.preferredWidth: height Layout.alignment: Qt.AlignCenter sourceSize.width: width sourceSize.height: height source: assistantModel.qrcode visible: source != '' } CameraSticker{ Layout.fillHeight: true Layout.preferredWidth: height Layout.alignment: Qt.AlignCenter showUsername: false showCustomButton: false visible: allQRCodeOptionsView.visible && assistantModel.isReadingQRCode deactivateCamera: !visible isPreview: true } } Item{ Layout.fillWidth: true Layout.fillHeight: true } } } Component.onCompleted: { if( !CoreManager.isLastRemoteProvisioningGood() ) //: 'Last remote provisioning failed' : Test to warn the user that the last fetch of remote provisioning has failed. requestBlock.stop(qsTr('lastProvisioningFailed')) } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/UseAppSipAccount.qml000066400000000000000000000067641434616504300304460ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import Utils 1.0 import ConstantsCpp 1.0 import App.Styles 1.0 // ============================================================================= AssistantAbstractView { id: view readonly property bool usePhoneNumber: SettingsModel.assistantSupportsPhoneNumbers && !checkBox.checked mainAction: requestBlock.execute mainActionEnabled: { var item = loader.item return item && item.mainActionEnabled && !requestBlock.loading } mainActionLabel: qsTr('confirmAction') title: qsTr('useAppSipAccountTitle').replace('%1', Qt.application.name.toUpperCase()) // --------------------------------------------------------------------------- Column { anchors.fill: parent Loader { id: loader source: 'UseAppSipAccountWith' + (view.usePhoneNumber ? 'PhoneNumber' : 'Username') + '.qml' width: parent.width } CheckBoxText { id: checkBox text: qsTr('useUsernameToLogin') visible: SettingsModel.assistantSupportsPhoneNumbers width: UseAppSipAccountStyle.checkBox.width onClicked: { assistantModel.reset() requestBlock.stop('') if (!checked) { assistantModel.setCountryCode(telephoneNumbersModel.defaultIndex) } } } Text { anchors.right:parent.right visible: ConstantsCpp.PasswordRecoveryUrl elide: Text.ElideRight font.pointSize: AboutStyle.copyrightBlock.url.pointSize linkColor: AboutStyle.copyrightBlock.url.color //: 'Forgotten password?' : text for an url shortcut to change the password text: ''+qsTr("passwordRecovery")+'' horizontalAlignment: Text.AlignHCenter onLinkActivated: Qt.openUrlExternally(link) MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor } } RequestBlock { id: requestBlock action: assistantModel.login width: parent.width } } // --------------------------------------------------------------------------- // Assistant. // --------------------------------------------------------------------------- AssistantModel { id: assistantModel function setCountryCode (index) { var model = telephoneNumbersModel assistantModel.countryCode = index !== -1 ? model.data(model.index(index, 0),"countryCode") || '' : '' } configFilename: 'use-app-sip-account.rc' countryCode: setCountryCode(view.usePhoneNumber ? telephoneNumbersModel.defaultIndex : -1) onPasswordChanged: { if (!view.usePhoneNumber) { loader.item.passwordError = error } } onPhoneNumberChanged: { if (view.usePhoneNumber) { loader.item.phoneNumberError = error } } onLoginStatusChanged: { requestBlock.stop(error) if (!error.length) { var codecInfo = VideoCodecsModel.getCodecInfo('H264') if (codecInfo.downloadUrl) { Utils.openCodecOnlineInstallerDialog(window, codecInfo, function cb (window) { window.setView('Home') }) } else { window.setView('Home') } } } onRecoverStatusChanged: { if (!view.usePhoneNumber) { requestBlock.stop('') return } requestBlock.stop(error) if (!error.length) { window.lockView({ descriptionText: qsTr('quitWarning') }) assistant.pushView('ActivateAppSipAccountWithPhoneNumber', { assistantModel: assistantModel }) } } } TelephoneNumbersModel { id: telephoneNumbersModel } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/UseAppSipAccountWithPhoneNumber.qml000066400000000000000000000020541434616504300334310ustar00rootroot00000000000000import Common 1.0 import Linphone 1.0 // ============================================================================= Form { property alias phoneNumberError: phoneNumber.error property bool mainActionEnabled: phoneNumber.text.length && !phoneNumberError.length orientation: Qt.Vertical FormLine{ FormGroup { label: qsTr('displayNameLabel') TextField { onTextChanged: assistantModel.displayName = text } } } FormLine { FormGroup { label: qsTr('countryLabel') ComboBox { id: country currentIndex: model.defaultIndex model: telephoneNumbersModel textRole: 'countryName' onActivated: { assistantModel.setCountryCode(index) var text = phoneNumber.text if (text.length > 0) { assistantModel.phoneNumber = text } } } } } FormLine { FormGroup { label: qsTr('phoneNumberLabel') TextField { id: phoneNumber inputMethodHints: Qt.ImhDialableCharactersOnly onTextChanged: assistantModel.phoneNumber = text } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/UseAppSipAccountWithUsername.qml000066400000000000000000000015251434616504300327700ustar00rootroot00000000000000import Common 1.0 // ============================================================================= Form { property alias passwordError: password.error property alias usernameError: username.error property bool mainActionEnabled: password.text && username.length && !passwordError.length && !usernameError.length orientation: Qt.Vertical FormLine{ FormGroup { label: qsTr('displayNameLabel') TextField { onTextChanged: assistantModel.displayName = text } } } FormLine { FormGroup { label: qsTr('usernameLabel') TextField { id: username onTextChanged: assistantModel.username = text } } } FormLine { FormGroup { label: qsTr('passwordLabel') PasswordField { id: password onTextChanged: assistantModel.password = text } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Assistant/UseOtherSipAccount.qml000066400000000000000000000135061434616504300307770ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.5 import Common 1.0 import Linphone 1.0 import ConstantsCpp 1.0 import App.Styles 1.0 // ============================================================================= Item{ AssistantAbstractView { id: mainItem mainAction: requestBlock.execute property bool isValid: false mainActionEnabled: mainItem.showWarning || isValid //: 'I understand' : Popup confirmation for a warning mainActionLabel: showWarning ? qsTr('understandAction').toUpperCase() //: 'Use' : Popup confirmation for a form : qsTr('confirmAction').toUpperCase() title: qsTr('useOtherSipAccountTitle') width: mainStack.currentItem.implicitWidth height: mainStack.currentItem.implicitHeight + (requestBlock.implicitHeight > 0 ? requestBlock.implicitHeight : 0) + mainItem.decorationHeight anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter property bool showWarning : true // --------------------------------------------------------------------------- StackView { id: mainStack width: currentItem.implicitWidth>0 ? currentItem.implicitWidth : currentItem.width height: currentItem.implicitHeight>0 ? currentItem.implicitHeight : currentItem.height initialItem: warningComponent } Component{ id: warningComponent Column{ spacing: UseAppSipAccountStyle.warningBlock.spacing width: AssistantAbstractViewStyle.content.width Text { elide: Text.ElideRight font.pointSize: UseAppSipAccountStyle.warningBlock.pointSize width: parent.width horizontalAlignment: Text.AlignLeft color: UseAppSipAccountStyle.warningBlock.color wrapMode: Text.WordWrap //: 'Some features require a %1 account, such as group messaging or ephemeral messaging.' : Warning text about features. %1 is the application name text: qsTr('warningFeatures').arg(applicationName) } Text{ elide: Text.ElideRight font.pointSize: UseAppSipAccountStyle.warningBlock.pointSize width: parent.width horizontalAlignment: Text.AlignLeft color: UseAppSipAccountStyle.warningBlock.color wrapMode: Text.WordWrap //: 'These features are hidden when you register with a third party SIP account.' : Warning text for using third party account. text: qsTr('warningThirdParty') } Text { elide: Text.ElideRight font.pointSize: UseAppSipAccountStyle.warningBlock.pointSize width: parent.width horizontalAlignment: Text.AlignLeft color: UseAppSipAccountStyle.warningBlock.color wrapMode: Text.WordWrap //: 'To enable it in a commercial project, please contact us.' : Warning text for contacting about enabling features. text: qsTr('warningContact') } Text { elide: Text.ElideRight font.pointSize: UseAppSipAccountStyle.warningBlock.contactUrl.pointSize width: parent.width horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap color: UseAppSipAccountStyle.warningBlock.color linkColor: UseAppSipAccountStyle.warningBlock.contactUrl.color text: ''+ConstantsCpp.ContactUrl+'' visible: ConstantsCpp.ContactUrl != '' onLinkActivated: Qt.openUrlExternally(link) MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor } } } } Component { id: formComponent Column { width: AssistantAbstractViewStyle.content.width property bool isValid: username.text.length && sipDomain.text.length && password.text.length onIsValidChanged: mainItem.isValid = isValid property alias usernameText: username.text property alias displayNameText: displayName.text property alias sipDomainText: sipDomain.text property alias passwordText: password.text function getTransport(){ return transport.model[transport.currentIndex] } Form { orientation: Qt.Vertical width: parent.width FormLine { FormGroup { label: qsTr('usernameLabel') TextField { id: username } } FormGroup { label: qsTr('displayNameLabel') TextField { id: displayName } } } FormLine { FormGroup { label: qsTr('sipDomainLabel') TextField { id: sipDomain } } } FormLine { FormGroup { label: qsTr('passwordLabel') PasswordField { id: password } } } FormLine { FormGroup { label: qsTr('transportLabel') ComboBox { id: transport model: [ 'UDP', 'TCP', 'TLS'] } } } } } } RequestBlock { id: requestBlock width: parent.width anchors.top: mainStack.bottom anchors.topMargin: UseAppSipAccountStyle.warningBlock.spacing action: (function () { if(mainItem.showWarning) { mainItem.showWarning = false mainStack.push(formComponent); requestBlock.stop('') }else{ if (!assistantModel.addOtherSipAccount({ username: mainStack.currentItem.usernameText, displayName: mainStack.currentItem.displayNameText, sipDomain: mainStack.currentItem.sipDomainText, password: mainStack.currentItem.passwordText, transport: mainStack.currentItem.getTransport() })) { requestBlock.stop(qsTr('addOtherSipAccountError')) } else { requestBlock.stop('') window.setView('Home') } } }) } AssistantModel { id: assistantModel configFilename: 'use-other-sip-account.rc' } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Conferences.qml000066400000000000000000000146031434616504300255300ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Utils 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 // ============================================================================= Item{ ColumnLayout { id: mainItem property int filterType: -1 spacing: 0 onFilterTypeChanged: Qt.callLater( conferenceList.positionViewAtEnd) Component.onCompleted: filterType = ConferenceInfoProxyModel.Scheduled anchors.fill: parent // --------------------------------------------------------------------------- // Title // --------------------------------------------------------------------------- Rectangle { Layout.fillWidth: true Layout.preferredHeight: ConferencesStyle.bar.height color: ConferencesStyle.bar.backgroundColor Text{ anchors.fill: parent verticalAlignment: Qt.AlignVCenter anchors.leftMargin: 40 color: ConferencesStyle.bar.text.color font { bold: true pointSize: ConferencesStyle.bar.text.pointSize } //: 'Meetings' : Conference list title. text: qsTr('conferencesTitle') } } Rectangle { Layout.fillWidth: true Layout.preferredHeight: ConferencesStyle.bar.height color: 'white' RowLayout { anchors { fill: parent leftMargin: ConferencesStyle.bar.leftMargin rightMargin: ConferencesStyle.bar.rightMargin } spacing: ConferencesStyle.spacing ExclusiveButtons { texts: [ //: 'Finished' : Filter meetings on end status. qsTr('conferencesEndedFilter'), //: 'Scheduled' : Filter meetings on scheduled status. qsTr('conferencesScheduledFilter'), ] capitalization: Font.AllUppercase selectedButton: mainItem.filterType style: ConferencesStyle.filter.buttons onClicked: { if(button <= 1) mainItem.filterType = (button === 0 ? ConferenceInfoProxyModel.Ended : ConferenceInfoProxyModel.Scheduled); } } } } // --------------------------------------------------------------------------- // Conferences list. // --------------------------------------------------------------------------- Rectangle { Layout.fillWidth: true Layout.fillHeight: true color: ConferencesStyle.backgroundColor ScrollableListView { id: conferenceList anchors.fill: parent spacing: 10 highlightFollowsCurrentItem: false fitCacheToContent: false section { criteria: ViewSection.FullString delegate: sectionHeading property: '$sectionDate' } model: ConferenceInfoProxyModel{ id: conferencesProxyModel filterType: mainItem.filterType onFilterTypeChanged: setSortOrder(filterType == ConferenceInfoProxyModel.Ended ? Qt.AscendingOrder : Qt.DescendingOrder) } // ----------------------------------------------------------------------- // Heading. // ----------------------------------------------------------------------- Component { id: sectionHeading Item { implicitHeight: container.height + ConferencesStyle.sectionHeading.bottomMargin width: parent.width Borders { id: container borderColor: ConferencesStyle.sectionHeading.border.color bottomWidth: ConferencesStyle.sectionHeading.border.width implicitHeight: text.contentHeight + ConferencesStyle.sectionHeading.padding * 2 + ConferencesStyle.sectionHeading.border.width * 2 topWidth: ConferencesStyle.sectionHeading.border.width width: parent.width Text { id: text anchors.fill: parent color: ConferencesStyle.sectionHeading.text.color font { bold: true pointSize: ConferencesStyle.sectionHeading.text.pointSize } horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter // Cast section to integer because Qt converts the // sectionDate in string!!! text: new Date(section).toLocaleDateString( Qt.locale(App.locale) ) } } } } //---------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------- delegate: Item { height: entry.height + ConferencesStyle.conference.bottomMargin anchors { left: parent ? parent.left : undefined leftMargin: 10 right: parent ? parent.right : undefined rightMargin: 10 } Rectangle { id: entry anchors.centerIn: parent width: parent.width / 2 height: calendarMessage.height radius: 6 color: calendarMessage.isCancelled ? ConferencesStyle.conference.backgroundColor.cancelled : mainItem.filterType == ConferenceInfoProxyModel.Ended ? ConferencesStyle.conference.backgroundColor.ended : ConferencesStyle.conference.backgroundColor.scheduled border.color: calendarMessage.containsMouse || calendarMessage.isExpanded ? ConferencesStyle.conference.selectedBorder.color : 'transparent' border.width: ConferencesStyle.conference.selectedBorder.width ChatCalendarMessage{ id: calendarMessage anchors.centerIn: parent width: parent.width height: fitHeight conferenceInfoModel: $modelData gotoButtonMode: mainItem.filterType == ConferenceInfoProxyModel.Scheduled || mainItem.filterType == ConferenceInfoProxyModel.Ended? 1 : 0 onExpandToggle: isExpanded = !isExpanded //isExpanded: calendarGrid.expanded //: 'The meeting URL has been copied' : Message text in a banner to warn the user that the URL have been copied to the clipboard. onConferenceUriCopied: messageBanner.noticeBannerText = qsTr('conferencesCopiedURL') //: 'The meeting has been deleted' : Message text in a banner to warn the user that the meeting has been deleted. onConferenceRemoved: messageBanner.noticeBannerText = qsTr('conferencesDeleted') } } } } } } MessageBanner{ id: messageBanner anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right anchors.margins: 25 height: fitHeight } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/ContactEdit.js000066400000000000000000000126621434616504300253250ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `Conversation.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import UtilsCpp 1.0 as UtilsCpp .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function handleContactUpdated () { var contact = contactEdit._contact if (!contactEdit._edition) { var vcard = contact.vcard if (contactEdit._vcard !== vcard) { // Not in edition mode, the contact was updated in other place. contactEdit._vcard = vcard } else { // Edition ended. handleVcardChanged(contact.vcard) } } else { // Edition not ended, the contact was updated in other place. // Update fields with new data. contactEdit._vcard = contact.cloneVcardModel() } } function handleVcardChanged (vcard) { if (!vcard) { vcard = {} } addresses.setData(vcard.sipAddresses) companies.setData(vcard.companies) emails.setData(vcard.emails) urls.setData(vcard.urls) } // ----------------------------------------------------------------------------- function editContact () { var contact = contactEdit._contact contactEdit._vcard = contact.cloneVcardModel() contactEdit._edition = true window.lockView({ descriptionText: qsTr('abortEditDescriptionText') }) } function removeContact () { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('removeContactDescription'), }, function (status) { if (status) { window.unlockView() window.setView('Contacts') Linphone.ContactsListModel.removeContact(_contact) } }) } // ----------------------------------------------------------------------------- function save () { var contact = contactEdit._contact var vcard = contactEdit._vcard contactEdit._edition = false if (contact) { contact.vcard = vcard window.unlockView() } else { contactEdit._contact = Linphone.ContactsListModel.addContact(vcard) handleVcardChanged(vcard) // Called directly, because the vcard is not modified in the view. } } function cancel () { var contact = contactEdit._contact if (contact) { contactEdit._vcard = contact.vcard window.unlockView() contactEdit._edition = false } else { window.setView('Contacts') } } // ----------------------------------------------------------------------------- function setAvatar (url) { contactEdit._vcard.avatar = Utils.getSystemPathFromUri(url) } function setUsername (username) { var vcard = contactEdit._vcard vcard.username = username // Update current text with new/old username. usernameInput.text = _vcard.username } // ----------------------------------------------------------------------------- function handleValueChanged (fields, index, oldValue, newValue, add, update) { if (newValue === oldValue) { return } var vcard = contactEdit._vcard var soFarSoGood = (oldValue.length === 0) ? vcard[add](newValue) : vcard[update](oldValue, newValue) fields.setInvalid(index, !soFarSoGood) handleVcardChanged(vcard) } function handleSipAddressChanged () { var args = Array.prototype.slice.call(arguments) args.push('addSipAddress', 'updateSipAddress') handleValueChanged.apply(this, args) } function handleCompanyChanged () { var args = Array.prototype.slice.call(arguments) args.push('addCompany', 'updateCompany') handleValueChanged.apply(this, args) } function handleEmailChanged () { var args = Array.prototype.slice.call(arguments) args.push('addEmail', 'updateEmail') handleValueChanged.apply(this, args) } function handleUrlChanged () { var args = Array.prototype.slice.call(arguments) args.push('addUrl', 'updateUrl') handleValueChanged.apply(this, args) } // ----------------------------------------------------------------------------- function buildAddressFields () { if(!contactEdit._vcard) return [] var address = contactEdit._vcard.address return [{ placeholder: qsTr('street'), text: address.street }, { placeholder: qsTr('locality'), text: address.locality }, { placeholder: qsTr('postalCode'), text: address.postalCode }, { placeholder: qsTr('country'), text: address.country }] } function handleAddressChanged (index, value) { var vcard = contactEdit._vcard if (index === 0) { // Street. vcard.setStreet(value) } else if (index === 1) { // Locality. vcard.setLocality(value) } else if (index === 2) { // Postal code. vcard.setPostalCode(value) } else if (index === 3) { // Country. vcard.setCountry(value) } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/ContactEdit.qml000066400000000000000000000262541434616504300255040ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 import 'ContactEdit.js' as Logic // ============================================================================= ColumnLayout { id: contactEdit property string sipAddress readonly property alias vcard: contactEdit._vcard property bool _edition: false property var _contact property var _vcard // --------------------------------------------------------------------------- spacing: 0 Component.onDestruction: {_vcard=null}// Need to set it to null because of not calling destructor if not. Component.onCompleted:{ var sipAddress = contactEdit.sipAddress var contact = contactEdit._contact = ContactsListModel.getContactModelFromAddress(sipAddress) if (!contact) { // Add a new contact. var vcard = CoreManager.createDetachedVcardModel() if (sipAddress && sipAddress.length > 0) { vcard.addSipAddress(sipAddress) vcard.username = UtilsCpp.getDisplayName(SipAddressesModel.getSipAddressObserver(sipAddress, sipAddress).peerAddress) }else{ vcard.username = ' '// Username initialization to avoid Belr parsing issue when setting new name } contactEdit._vcard = vcard contactEdit._edition = true } else { // See or edit a contact. contactEdit._vcard = contact.vcard } } onVcardChanged: Logic.handleVcardChanged(vcard) // --------------------------------------------------------------------------- Loader { active: contactEdit._contact != null sourceComponent: Connections { target: contactEdit._contact onContactUpdated: Logic.handleContactUpdated() } } FileDialog { id: avatarChooser folder: shortcuts.home title: qsTr('avatarChooserTitle') onAccepted: Logic.setAvatar(fileUrls[0]) } // --------------------------------------------------------------------------- // Info bar. // --------------------------------------------------------------------------- Rectangle { id: infoBar Layout.fillWidth: true Layout.preferredHeight: ContactEditStyle.bar.height color: ContactEditStyle.bar.color RowLayout { anchors { fill: parent leftMargin: ContactEditStyle.bar.leftMargin rightMargin: ContactEditStyle.bar.rightMargin } spacing: ContactEditStyle.bar.spacing ActionButton { isCustom: true backgroundRadius: 90 colorSet: ContactEditStyle.bar.avatarTakePicture enabled: _edition onClicked: avatarChooser.open() Avatar { id: avatar anchors.fill: parent image: _vcard ? _vcard.avatar : '' username: _vcard ? _vcard.username : '' presenceLevel: _contact ? _contact.presenceLevel : -1 visible: (isLoaded() && !parent.hovered) || !_edition } } TransparentTextInput { id: usernameInput Layout.fillWidth: true Layout.preferredHeight: ContactEditStyle.bar.buttons.size color: ContactEditStyle.bar.username.color font { bold: true pointSize: ContactEditStyle.bar.username.pointSize } forceFocus: true readOnly: !_edition text: avatar.username onEditingFinished: Logic.setUsername(text) onReadOnlyChanged: { if (!readOnly) { forceActiveFocus() } } } Row { Layout.alignment: Qt.AlignRight Layout.fillHeight: true spacing: ContactEditStyle.bar.actions.spacing visible: _contact != null ActionBar { id: actionBar anchors.verticalCenter: parent.verticalCenter iconSize: ContactEditStyle.bar.actions.history.iconSize ActionButton { isCustom: true backgroundRadius: 90 colorSet: ContactEditStyle.videoCall visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.showStartVideoCallButton onClicked: sipAddressesMenu.open(sipAddressesMenu.startVideoCall) } ActionButton { isCustom: true backgroundRadius: 90 colorSet: ContactEditStyle.call visible: SettingsModel.outgoingCallsEnabled onClicked: sipAddressesMenu.open(sipAddressesMenu.startCall) } ActionButton { isCustom: true backgroundRadius: 90 colorSet: SettingsModel.getShowStartChatButton() ? ContactEditStyle.chat : ContactEditStyle.history visible: SettingsModel.standardChatEnabled onClicked: sipAddressesMenu.open(sipAddressesMenu.createChatRoom) tooltipText: qsTr('tooltipShowConversation') tooltipIsClickable: false } ActionButton { isCustom: true backgroundRadius: 90 colorSet: SettingsModel.getShowStartChatButton() ? ContactEditStyle.chat : ContactEditStyle.history visible: SettingsModel.secureChatEnabled enabled: AccountSettingsModel.conferenceUri != '' Icon{ icon:'secure_level_1' iconSize:parent.height/2 anchors.top:parent.top anchors.horizontalCenter: parent.right } onClicked: {sipAddressesMenu.open(sipAddressesMenu.createSecureChatRoom)} tooltipMaxWidth: actionBar.width tooltipVisible: AccountSettingsModel.conferenceUri == '' //: 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. tooltipText: '- ' + qsTr('missingConferenceURI') + '\n' } } ActionBar { anchors.verticalCenter: parent.verticalCenter ActionButton { isCustom: true backgroundRadius: 4 colorSet: ContactEditStyle.bar.actions.edit.colorSet visible: !_edition onClicked: Logic.editContact() } ActionButton { isCustom: true backgroundRadius: 4 colorSet: ContactEditStyle.bar.actions.del.colorSet onClicked: Logic.removeContact() } } } } } // --------------------------------------------------------------------------- SipAddressesMenu { id: sipAddressesMenu relativeTo: infoBar relativeX: infoBar.width - SipAddressesMenuStyle.entry.width relativeY: infoBar.height sipAddresses: _contact ? _contact.vcard.sipAddresses : [ contactEdit.sipAddress ] function viewConversation(chatRoomModel){ if( chatRoomModel && !chatRoomModel.updating){ window.setView('Conversation', { chatRoomModel:chatRoomModel }, function(){ TimelineListModel.select(chatRoomModel) }) } } function createChatRoom(sipAddress){ var entry = CallsListModel.createChatRoom( "", false, [sipAddress], false ) if(entry) viewConversation(entry.chatRoomModel) } function createSecureChatRoom(sipAddress){ var entry = CallsListModel.createChatRoom( "", true, [sipAddress], false ) if(entry) viewConversation(entry.chatRoomModel) } function startCall(sipAddress){ CallsListModel.launchAudioCall([sipAddress]) } function startVideoCall(sipAddress){ CallsListModel.launchVideoCall([sipAddress]) } } // --------------------------------------------------------------------------- // Info list. // --------------------------------------------------------------------------- Rectangle { Layout.fillHeight: true Layout.fillWidth: true color: ContactEditStyle.content.color Flickable { id: flick ScrollBar.vertical: ForceScrollBar {} anchors.fill: parent boundsBehavior: Flickable.StopAtBounds clip: true contentHeight: infoList.height contentWidth: width - ScrollBar.vertical.width flickableDirection: Flickable.VerticalFlick // ----------------------------------------------------------------------- ColumnLayout { id: infoList width: flick.contentWidth ListForm { id: addresses Layout.leftMargin: ContactEditStyle.values.leftMargin Layout.rightMargin: ContactEditStyle.values.rightMargin Layout.topMargin: ContactEditStyle.values.topMargin Layout.fillWidth: true minValues: _contact ? 1 : 0 placeholder: qsTr('sipAccountsPlaceholder') readOnly: !_edition title: qsTr('sipAccounts') onChanged: Logic.handleSipAddressChanged(addresses, index, oldValue, newValue) onRemoved: _vcard.removeSipAddress(value) } Rectangle { Layout.fillWidth: true Layout.preferredHeight: ContactEditStyle.values.separator.height color: ContactEditStyle.values.separator.color } ListForm { id: companies Layout.leftMargin: ContactEditStyle.values.leftMargin Layout.rightMargin: ContactEditStyle.values.rightMargin placeholder: qsTr('companiesPlaceholder') readOnly: !_edition title: qsTr('companies') onChanged: Logic.handleCompanyChanged(companies, index, oldValue, newValue) onRemoved: _vcard.removeCompany(value) } Rectangle { Layout.fillWidth: true Layout.preferredHeight: ContactEditStyle.values.separator.height color: ContactEditStyle.values.separator.color } ListForm { id: emails Layout.leftMargin: ContactEditStyle.values.leftMargin Layout.rightMargin: ContactEditStyle.values.rightMargin placeholder: qsTr('emailsPlaceholder') readOnly: !_edition title: qsTr('emails') onChanged: Logic.handleEmailChanged(emails, index, oldValue, newValue) onRemoved: _vcard.removeEmail(value) } Rectangle { Layout.fillWidth: true Layout.preferredHeight: ContactEditStyle.values.separator.height color: ContactEditStyle.values.separator.color } ListForm { id: urls Layout.leftMargin: ContactEditStyle.values.leftMargin Layout.rightMargin: ContactEditStyle.values.rightMargin placeholder: qsTr('webSitesPlaceholder') readOnly: !_edition title: qsTr('webSites') onChanged: Logic.handleUrlChanged(urls, index, oldValue, newValue) onRemoved: _vcard.removeUrl(value) } Rectangle { Layout.fillWidth: true Layout.preferredHeight: ContactEditStyle.values.separator.height color: ContactEditStyle.values.separator.color } StaticListForm { Layout.leftMargin: ContactEditStyle.values.leftMargin Layout.rightMargin: ContactEditStyle.values.rightMargin fields: Logic.buildAddressFields() readOnly: !_edition title: qsTr('address') onChanged: Logic.handleAddressChanged(index, value) } // --------------------------------------------------------------------- // Edition buttons. // --------------------------------------------------------------------- Row { Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: ContactEditStyle.values.bottomMargin Layout.topMargin: ContactEditStyle.buttons.topMargin spacing: ContactEditStyle.buttons.spacing visible: _edition TextButtonA { text: qsTr('cancel') onClicked: Logic.cancel() } TextButtonB { enabled: usernameInput.text.length > 0 && _vcard && _vcard.sipAddresses.length > 0 text: qsTr('save') onClicked: Logic.save() } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Contacts.qml000066400000000000000000000213361434616504300250550ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Utils 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 // ============================================================================= ColumnLayout { function _removeContact (contact) { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('removeContactDescription'), }, function (status) { if (status) { ContactsListModel.remove(contact) } }) } spacing: 0 // --------------------------------------------------------------------------- // Search Bar & actions. // --------------------------------------------------------------------------- Rectangle { Layout.fillWidth: true Layout.preferredHeight: ContactsStyle.bar.height color: ContactsStyle.bar.backgroundColor RowLayout { anchors { fill: parent leftMargin: ContactsStyle.bar.leftMargin rightMargin: ContactsStyle.bar.rightMargin } spacing: ContactsStyle.spacing TextField { Layout.fillWidth: true icon: ContactsStyle.filter.icon overwriteColor: ContactsStyle.filter.color placeholderText: qsTr('searchContactPlaceholder') onTextChanged: contacts.setFilter(text) } ExclusiveButtons { texts: [ qsTr('selectAllContacts'), qsTr('selectConnectedContacts') ] onClicked: contacts.useConnectedFilter = !!button } TextButtonB { addHeight: 15 text: qsTr('addContact') onClicked: window.setView('ContactEdit') } } } // --------------------------------------------------------------------------- // Contacts list. // --------------------------------------------------------------------------- Rectangle { Layout.fillWidth: true Layout.fillHeight: true color: ContactsStyle.backgroundColor ScrollableListView { anchors.fill: parent spacing: 0 model: ContactsListProxyModel { id: contacts } delegate: Borders { bottomColor: ContactsStyle.contact.border.color bottomWidth: ContactsStyle.contact.border.width height: ContactsStyle.contact.height width: parent ? parent.width : 0 // --------------------------------------------------------------------- Rectangle { id: contact anchors.fill: parent color: ContactsStyle.contact.backgroundColor.normal // ------------------------------------------------------------------- Component { id: container1 RowLayout { spacing: ContactsStyle.contact.spacing PresenceLevel { Layout.preferredHeight: ContactsStyle.contact.presenceLevelSize Layout.preferredWidth: ContactsStyle.contact.presenceLevelSize level: $modelData.presenceLevel } Text { Layout.fillWidth: true color: ContactsStyle.contact.presence.color elide: Text.ElideRight font.pointSize: ContactsStyle.contact.presence.pointSize text: Presence.getPresenceStatusAsString($modelData.presenceStatus) } } } Component { id: container2 Item { ActionBar { id:actionBar anchors { left: parent.left verticalCenter: parent.verticalCenter } iconSize: ContactsStyle.contact.actionButtonsSize ActionButton { isCustom: true backgroundRadius: 90 colorSet: ContactsStyle.videoCall visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.getShowStartVideoCallButton() onClicked: actions.itemAt(0).open() } ActionButton { isCustom: true backgroundRadius: 90 colorSet: ContactsStyle.call visible: SettingsModel.outgoingCallsEnabled onClicked: actions.itemAt(1).open() } ActionButton { isCustom: true backgroundRadius: 90 colorSet: SettingsModel.getShowStartChatButton() ? ContactsStyle.chat : ContactsStyle.history visible: SettingsModel.standardChatEnabled onClicked: actions.itemAt(2).open() } ActionButton { isCustom: true backgroundRadius: 90 colorSet: SettingsModel.getShowStartChatButton() ? ContactsStyle.chat : ContactsStyle.history visible: SettingsModel.secureChatEnabled enabled: AccountSettingsModel.conferenceUri != '' Icon{ icon:'secure_level_1' iconSize:parent.height/2 anchors.top:parent.top anchors.horizontalCenter: parent.right } onClicked: {actions.itemAt(3).open()} tooltipMaxWidth: actionBar.width tooltipVisible: AccountSettingsModel.conferenceUri == '' //: 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missing in its configuration. tooltipText: '- ' + qsTr('missingConferenceURI') + '\n' } } ActionButton { isCustom: true backgroundRadius: 90 colorSet: ContactsStyle.deleteAction anchors { right: parent.right verticalCenter: parent.verticalCenter } onClicked: _removeContact($modelData) } } } // ------------------------------------------------------------------- Repeater { id: actions property ChatRoomModel lastChatRoom property ContactModel contactModel: $modelData Connections{ target: lastChatRoom onStateChanged: if(state === 1) { console.debug("Load conversation from contacts") window.setView('Conversation', { chatRoomModel: lastChatRoom }) } } readonly property var handlers: [ CallsListModel.launchVideoCall, CallsListModel.launchAudioCall, function (sipAddress) {CallsListModel.launchChat( sipAddress,0 )}, function (sipAddress) {CallsListModel.launchChat( sipAddress,1 )} ] model: handlers SipAddressesMenu { relativeTo: loader relativeY: loader.height sipAddresses: actions.contactModel.vcard.sipAddresses onSipAddressClicked: actions.handlers[index](sipAddress) } } // ------------------------------------------------------------------- Rectangle { id: indicator anchors.left: parent.left color: 'transparent' height: parent.height width: ContactsStyle.contact.indicator.width } MouseArea { id: mouseArea anchors.fill: parent cursorShape: Qt.ArrowCursor MouseArea { anchors.fill: parent onClicked: window.setView('ContactEdit', { sipAddress: $modelData.vcard.sipAddresses[0] }) } RowLayout { anchors { fill: parent leftMargin: ContactsStyle.contact.leftMargin rightMargin: ContactsStyle.contact.rightMargin } spacing: ContactsStyle.contact.spacing Item { Layout.preferredHeight: parent.height Layout.preferredWidth: parent.height Avatar { anchors.centerIn: parent image: $modelData.vcard.avatar username: $modelData.vcard.username height: ContactsStyle.contact.avatarSize width: ContactsStyle.contact.avatarSize } } Text { Layout.fillHeight: true Layout.preferredWidth: ContactsStyle.contact.username.width color: ContactsStyle.contact.username.color elide: Text.ElideRight font { bold: true pointSize: ContactsStyle.contact.username.pointSize } text: $modelData.vcard.username verticalAlignment: Text.AlignVCenter } // Container. Loader { id: loader Layout.fillWidth: true Layout.fillHeight: true sourceComponent: container1 } } } // ------------------------------------------------------------------- states: State { when: mouseArea.containsMouse PropertyChanges { color: ContactsStyle.contact.backgroundColor.hovered target: contact } PropertyChanges { color: ContactsStyle.contact.indicator.color target: indicator } PropertyChanges { sourceComponent: container2 target: loader } } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Conversation.js000066400000000000000000000044561434616504300256000ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `Conversation.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import UtilsCpp 1.0 as UtilsCpp .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function removeAllEntries () { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('removeAllEntriesDescription'), }, function (status) { if (status) { chatRoomProxyModel.removeAllEntries() } }) } function getAvatar () { var contact = conversation._sipAddressObserver ? conversation._sipAddressObserver.contact : null return contact ? contact.vcard.avatar : '' } function getEditTooltipText() { return conversation._sipAddressObserver && conversation._sipAddressObserver.contact ? qsTr('tooltipContactEdit') : qsTr('tooltipContactAdd') } function updateChatFilter (button) { var ChatRoomModel = Linphone.ChatRoomModel if (button === 0) { chatRoomProxyModel.setEntryTypeFilter(ChatRoomModel.GenericEntry) } else if (button === 1) { chatRoomProxyModel.setEntryTypeFilter(ChatRoomModel.CallEntry) } else { chatRoomProxyModel.setEntryTypeFilter(ChatRoomModel.MessageEntry) } } function openConferenceManager (params) { var App = Linphone.App var callsWindow = App.getCallsWindow() App.smartShowWindow(callsWindow) callsWindow.openConferenceManager(params) } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Conversation.qml000066400000000000000000000551441434616504300257550ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Linphone 1.0 import Utils 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 import Common.Styles 1.0 import Units 1.0 import ColorsList 1.0 import 'Conversation.js' as Logic // ============================================================================= ColumnLayout { id: conversation // 1) chatRoomModel : chat + calls + conference // 2) no chatRoomModel : calls property string defaultPeerAddress property string defaultLocalAddress property string defaultFullPeerAddress property string defaultFullLocalAddress property ChatRoomModel chatRoomModel property string peerAddress : getPeerAddress() property string localAddress : chatRoomModel?chatRoomModel.getLocalAddress() : defaultLocalAddress property string fullPeerAddress : getFullPeerAddress() property string fullLocalAddress : chatRoomModel?chatRoomModel.getFullLocalAddress() : defaultFullLocalAddress property int securityLevel : chatRoomModel ? chatRoomModel.securityLevel : 1 property SipAddressObserver _sipAddressObserver: SipAddressesModel.getSipAddressObserver((fullPeerAddress?fullPeerAddress:peerAddress), (fullLocalAddress?fullLocalAddress:localAddress)) property bool haveMoreThanOneParticipants: chatRoomModel ? chatRoomModel.participants.count > 2 : false property bool haveLessThanMinParticipantsForCall: chatRoomModel ? chatRoomModel.participants.count <= 5 : false function getPeerAddress() { if(chatRoomModel) { if(chatRoomModel.groupEnabled || chatRoomModel.isSecure()) { return chatRoomModel.participants.addressesToString; }else { return chatRoomModel.sipAddress; } }else { return defaultPeerAddress } } function getFullPeerAddress() { if(chatRoomModel) { if(chatRoomModel.groupEnabled || chatRoomModel.isSecure()) { return chatRoomModel.participants.addressesToString; }else { return chatRoomModel.sipAddress; } }else { return defaultFullPeerAddress; } } // --------------------------------------------------------------------------- spacing: 0 clip:false Component.onDestruction: _sipAddressObserver=null// Need to set it to null because of not calling destructor if not. // --------------------------------------------------------------------------- // Contact bar. // --------------------------------------------------------------------------- Rectangle { id:mainBar Layout.fillWidth: true Layout.preferredHeight: ConversationStyle.bar.height color: ConversationStyle.bar.backgroundColor clip:false RowLayout { id:contactBar anchors { fill: parent leftMargin: ConversationStyle.bar.leftMargin rightMargin: ConversationStyle.bar.rightMargin } spacing: ConversationStyle.bar.spacing Avatar { id: avatar Layout.preferredHeight: ConversationStyle.bar.avatarSize Layout.preferredWidth: ConversationStyle.bar.avatarSize image: Logic.getAvatar() presenceLevel: chatRoomModel && chatRoomModel.presenceStatus //username: Logic.getUsername() username: chatRoomModel?chatRoomModel.username:( conversation._sipAddressObserver ? UtilsCpp.getDisplayName(conversation._sipAddressObserver.peerAddress) : '') isOneToOne: chatRoomModel==undefined || chatRoomModel.isOneToOne==undefined || chatRoomModel.isOneToOne } Item{ Layout.fillHeight: true Layout.fillWidth: true RowLayout{ anchors.fill: parent spacing:0 ColumnLayout{ property int maximumContentWidth: contactBar.width -(avatar.visible?avatar.width:0) -actionBar.width - (secureIcon.visible?secureIcon.width :0) -3*ConversationStyle.bar.spacing Layout.fillHeight: true Layout.minimumWidth: 20 Layout.maximumWidth: maximumContentWidth Layout.preferredWidth: contactDescription.contentWidth spacing: 5 Row{ Layout.topMargin: 15 Layout.preferredHeight: implicitHeight Layout.alignment: Qt.AlignBottom visible: chatRoomModel && chatRoomModel.isMeAdmin && !usernameEdit.visible && !chatRoomModel.isOneToOne Icon{ id:adminIcon icon : ConversationStyle.bar.status.adminStatusIcon overwriteColor: ConversationStyle.bar.status.adminStatusColor iconSize: ConversationStyle.bar.status.adminStatusIconSize } Text{ anchors.verticalCenter: parent.verticalCenter //: 'Admin' : Admin(istrator) //~ Context One word title for describing the current admin status text: qsTr('adminStatus') color: ConversationStyle.bar.status.adminTextColor font.pointSize: Units.dp * 8 } } ContactDescription { id:contactDescription Layout.minimumWidth: 20 Layout.maximumWidth: parent.maximumContentWidth Layout.preferredWidth: contentWidth Layout.preferredHeight: contentHeight Layout.alignment: Qt.AlignTop | Qt.AlignLeft visible: !usernameEdit.visible contactDescriptionStyle: ConversationStyle.bar.contactDescription titleText: avatar.username titleClickable: chatRoomModel && chatRoomModel.isMeAdmin && !chatRoomModel.isOneToOne subtitleText: if(chatRoomModel) { if(chatRoomModel.groupEnabled) { return chatRoomModel.participants.displayNamesToString; }else if(chatRoomModel.isSecure()) { return chatRoomModel.participants.addressesToString; }else return SipAddressesModel.cleanSipAddress(chatRoomModel.sipAddress) }else return '' /* participants: if(chatRoomModel) { if(chatRoomModel.groupEnabled) { return chatRoomModel.participants.displayNamesToString; }else if(chatRoomModel.isSecure()) { return chatRoomModel.participants.addressesToString; }else return '' }else return '' sipAddress: { if(chatRoomModel) { if(chatRoomModel.groupEnabled) { return ''; }else if(chatRoomModel.isSecure()) { return ''; }else { return chatRoomModel.sipAddress; } }else { return conversation.fullPeerAddress || conversation.peerAddress || ''; } } */ onTitleClicked: { if(!conversation.chatRoomModel.isReadOnly) { usernameEdit.visible = !usernameEdit.visible usernameEdit.forceActiveFocus() } } } Item{ Layout.fillHeight: true Layout.fillWidth: true visible: chatRoomModel && chatRoomModel.isMeAdmin && !chatRoomModel.isOneToOne } } Icon{ id: secureIcon Layout.alignment: Qt.AlignVCenter visible: securityLevel != 1 icon: securityLevel === 2?'secure_level_1': securityLevel===3? 'secure_level_2' : 'secure_level_unsafe' iconSize:30 MouseArea{ anchors.fill:parent visible: conversation.chatRoomModel && !conversation.chatRoomModel.isReadOnly onClicked : { window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/InfoEncryption.qml') ,{securityLevel:securityLevel} , function (status) { if(status){ window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ParticipantsDevices.qml') ,{chatRoomModel:chatRoomModel , window:window}) } }) } } } Item{//Spacer Layout.fillWidth: true } } ColumnLayout{ id: usernameEdit anchors.fill: parent visible: false TextField{ Layout.fillWidth: true text: avatar.username onEditingFinished: { chatRoomModel.subject = text usernameEdit.visible = false } font.bold: true onFocusChanged: if(!focus) usernameEdit.visible = false } } } Row { id:actionBar Layout.fillHeight: true spacing: ConversationStyle.bar.actions.spacing ActionBar { anchors.verticalCenter: parent.verticalCenter iconSize: ConversationStyle.bar.actions.call.iconSize ActionButton { isCustom: true backgroundRadius: 1000 colorSet: ConversationStyle.bar.actions.videoCall visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.showStartVideoCallButton && !conversation.haveMoreThanOneParticipants onClicked: CallsListModel.launchVideoCall(chatRoomModel.participants.addressesToString) } ActionButton { isCustom: true backgroundRadius: 1000 colorSet: ConversationStyle.bar.actions.call visible: SettingsModel.outgoingCallsEnabled && !conversation.haveMoreThanOneParticipants onClicked: CallsListModel.launchAudioCall(chatRoomModel.participants.addressesToString) } ActionButton { isCustom: true backgroundRadius: 1000 colorSet: ConversationStyle.bar.actions.chat visible: SettingsModel.standardChatEnabled && SettingsModel.getShowStartChatButton() && !conversation.haveMoreThanOneParticipants && conversation.securityLevel != 1 onClicked: CallsListModel.launchChat(chatRoomModel.participants.addressesToString, 0) } ActionButton { isCustom: true backgroundRadius: 1000 colorSet: ConversationStyle.bar.actions.chat visible: SettingsModel.secureChatEnabled && SettingsModel.getShowStartChatButton() && !conversation.haveMoreThanOneParticipants && conversation.securityLevel == 1 onClicked: CallsListModel.launchChat(chatRoomModel.participants.addressesToString, 1) Icon{ icon:'secure_level_1' iconSize: parent.height/2 anchors.top:parent.top anchors.horizontalCenter: parent.right } } ActionButton { id: groupCallButton property ConferenceInfoModel conferenceInfoModel: ConferenceInfoModel{} Connections{ target: groupCallButton.conferenceInfoModel onConferenceCreated: { groupCallButton.toggled = false } onConferenceCreationFailed:{ groupCallButton.toggled = false } } isCustom: true backgroundRadius: 1000 colorSet: ConversationStyle.bar.actions.groupChat visible: conversation.chatRoomModel && !conversation.chatRoomModel.isReadOnly && conversation.haveMoreThanOneParticipants && SettingsModel.outgoingCallsEnabled && (SettingsModel.videoConferenceEnabled || conversation.haveLessThanMinParticipantsForCall) //onClicked: CallsListModel. Logic.openConferenceManager({chatRoomModel:conversation.chatRoomModel, autoCall:true}) onClicked:{ if( SettingsModel.videoConferenceEnabled ){ groupCallButton.toggled = true conferenceInfoModel.resetConferenceInfo(); conferenceInfoModel.isScheduled = false conferenceInfoModel.subject = chatRoomModel.subject conferenceInfoModel.setParticipants(conversation.chatRoomModel.participants) conferenceInfoModel.inviteMode = 0; conferenceInfoModel.createConference(false)// TODO activate it when secure video conference is implemented }else{ Logic.openConferenceManager({chatRoomModel:conversation.chatRoomModel, autoCall:true}) } } //: "Call all chat room's participants" : tooltip on a button for calling all participant in the current chat room tooltipText: qsTr("groupChatCallButton") } } ActionBar { id:actionsBar anchors.verticalCenter: parent.verticalCenter ActionButton { id:dotButton isCustom: true backgroundRadius: 90 colorSet: ConversationStyle.bar.actions.openMenu visible: true //conversationMenu.showGroupInfo || conversationMenu.showDevices || conversationMenu.showEphemerals toggled: conversationMenu.opened longPressedTimeout: 3000 onPressed: {// Bug : Not working : Menu is still closed before pressing on button (even with closePolicy) if( conversationMenu.opened ) { conversationMenu.close() }else { conversationMenu.open() } } property string debugData: chatRoomModel ? 'Chat room ID:\n'+chatRoomModel.getFullPeerAddress() +'\nLocal account:\n'+chatRoomModel.getFullLocalAddress() : 'Chat room is null' onLongPressed:{ if( SettingsModel.logsEnabled){ conversationMenu.close() window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: debugData, showButtonOnly: 1, buttonTexts: ['', 'COPY'], height:320, }, function (status) { Clipboard.text = debugData }) } } } } Menu{ id:conversationMenu x:mainBar.width-width y:mainBar.height menuStyle : MenuStyle.aux2 property bool showGroupInfo: chatRoomModel && !chatRoomModel.isOneToOne property bool showDevices : conversation.securityLevel != 1 property bool showEphemerals: conversation.securityLevel != 1 // && chatRoomModel.isMeAdmin // Uncomment when session mode will be implemented property bool showScheduleMeeting: showGroupInfo && SettingsModel.conferenceEnabled MenuItem{ id:contactMenu property bool editMode: conversation._sipAddressObserver && conversation._sipAddressObserver.contact text: editMode ? //: 'View contact' : Item menu to view the contact in address book qsTr('conversationMenuViewContact') //: 'Add contact' : Item menu to add the contact to address book : qsTr('conversationMenuAddContact') iconMenu: editMode ? MenuItemStyle.contact.view : MenuItemStyle.contact.add iconSizeMenu: 40 menuItemStyle : MenuItemStyle.aux2 visible: conversation.chatRoomModel && SettingsModel.contactsEnabled && !conversation.chatRoomModel.groupEnabled onTriggered: { window.setView('ContactEdit', { sipAddress: conversation.getFullPeerAddress() }) } TooltipArea { text: Logic.getEditTooltipText() } } Rectangle{ height:visible ? 1 : 0 width:parent.width color: ConversationStyle.menu.separatorColor visible: groupInfoMenu.visible && contactMenu.visible } MenuItem{ id:groupInfoMenu //: 'Group information' : Item menu to get information about the chat room text: qsTr('conversationMenuGroupInformations') iconMenu: MenuItemStyle.info.icon //iconSizeMenu: 40 menuItemStyle : MenuItemStyle.aux2 visible: conversationMenu.showGroupInfo onTriggered: { window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/InfoChatRoom.qml') ,{chatRoomModel:chatRoomModel}) } } Rectangle{ height:visible ? 1 : 0 width:parent.width color: ConversationStyle.menu.separatorColor visible: devicesMenuItem.visible && (contactMenu.visible || groupInfoMenu.visible) } MenuItem{ id: devicesMenuItem //: "Conversation's devices" : Item menu to get all participant devices of the chat room text: qsTr('conversationMenuDevices') iconMenu: MenuItemStyle.devices.icon visible: conversationMenu.showDevices iconSizeMenu: 40 menuItemStyle : MenuItemStyle.aux2 onTriggered: { window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ParticipantsDevices.qml') ,{chatRoomModel:chatRoomModel, window:window}) } } Rectangle{ height:visible ? 1 : 0 width:parent.width color: ConversationStyle.menu.separatorColor visible: ephemeralMenuItem.visible && (contactMenu.visible || groupInfoMenu.visible || devicesMenuItem.visible) } MenuItem{ id: ephemeralMenuItem //: 'Ephemeral messages' : Item menu to enable ephemeral mode text: qsTr('conversationMenuEphemeral') iconMenu: MenuItemStyle.ephemeral.icon iconSizeMenu: 40 menuItemStyle : MenuItemStyle.aux2 visible: conversationMenu.showEphemerals onTriggered: { window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/EphemeralChatRoom.qml') ,{chatRoomModel:chatRoomModel}) } } Rectangle{ height:visible ? 1 : 0 width:parent.width color: ConversationStyle.menu.separatorColor visible: scheduleMeetingMenuItem.visible && (contactMenu.visible || groupInfoMenu.visible || devicesMenuItem.visible || ephemeralMenuItem.visible) } MenuItem{ id: scheduleMeetingMenuItem property ConferenceInfoModel conferenceInfoModel: ConferenceInfoModel{} //: 'Schedule a meeting' : Item menu to schedule a meeting with the chat participants. text: qsTr('conversationMenuScheduleMeeting') iconMenu: MenuItemStyle.scheduleMeeting.icon iconSizeMenu: 40 menuItemStyle : MenuItemStyle.aux2 visible: conversationMenu.showScheduleMeeting onClicked: { conferenceInfoModel.resetConferenceInfo() conferenceInfoModel.isScheduled = true conferenceInfoModel.subject = chatRoomModel.subject conferenceInfoModel.setParticipants(conversation.chatRoomModel.participants) window.detachVirtualWindow() window.attachVirtualWindow(Utils.buildAppDialogUri('NewConference') ,{conferenceInfoModel: scheduleMeetingMenuItem.conferenceInfoModel} , function (status) { if( status){ setView('Conferences') } }) } } Rectangle{ height:visible ? 1 : 0 width:parent.width color: ConversationStyle.menu.separatorColor visible: deleteMenuItem.visible && (contactMenu.visible || groupInfoMenu.visible || devicesMenuItem.visible || ephemeralMenuItem.visible || scheduleMeetingMenuItem.visible) } MenuItem{ id: deleteMenuItem //: 'Delete history' : Item menu to delete the chat's history text: qsTr('conversationMenuDelete') iconMenu: MenuItemStyle.deleteEntry.icon iconSizeMenu: 40 menuItemStyle : MenuItemStyle.aux2Error visible: true onTriggered: { Logic.removeAllEntries() } TooltipArea { text: qsTr('cleanHistory') } } } } } } // --------------------------------------------------------------------------- // Messages/Calls filters. // --------------------------------------------------------------------------- Borders { id:filtersBar Layout.fillWidth: true Layout.preferredHeight: active ? ConversationStyle.filters.height : 0 borderColor: ConversationStyle.filters.border.color bottomWidth: ConversationStyle.filters.border.bottomWidth color: ConversationStyle.filters.backgroundColor topWidth: ConversationStyle.filters.border.topWidth visible: chatRoomModel && (!chatRoomModel.haveEncryption && SettingsModel.standardChatEnabled || chatRoomModel.haveEncryption && SettingsModel.secureChatEnabled) ExclusiveButtons { id: filterButtons anchors { left: parent.left leftMargin: ConversationStyle.filters.leftMargin verticalCenter: parent.verticalCenter } texts: [ qsTr('displayCallsAndMessages'), qsTr('displayCalls'), qsTr('displayMessages') ] onClicked: Logic.updateChatFilter(button) } BusyIndicator{ id: chatLoading width: 20 height: 20 anchors.left: filterButtons.right anchors.leftMargin: 50 anchors.verticalCenter: parent.verticalCenter running: chatArea.tryingToLoadMoreEntries } // ------------------------------------------------------------------------- // Search. // ------------------------------------------------------------------------- MouseArea{ anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom anchors.rightMargin: 10 anchors.topMargin: 10 anchors.bottomMargin: 10 width: 30 Icon{ anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter icon: (searchView.visible? 'close_custom': 'search_custom') iconSize: 30 overwriteColor: ConversationStyle.bar.searchIconColor } onClicked: { searchView.visible = !searchView.visible chatRoomProxyModel.filterText = searchView.text } } Rectangle{ id:searchView property alias text: searchBar.text anchors.right: parent.right anchors.top: parent.top anchors.bottom: parent.bottom anchors.left : chatLoading.right anchors.rightMargin: 10 anchors.leftMargin: 50 anchors.topMargin: 10 anchors.bottomMargin: 10 visible: true TextField { id:searchBar anchors { fill: parent margins: 0 } width: parent.width-14 icon: text != '' ? 'close_custom' : 'search_custom' overwriteColor: ConversationStyle.filters.iconColor //: 'Search in messages' : this is a placeholder when searching something in the timeline list placeholderText: qsTr('searchMessagesPlaceholder') onTextChanged: searchDelay.restart() font.pointSize: ConversationStyle.filters.pointSize Timer{ id: searchDelay interval: 500 running: false onTriggered: if( searchView.visible){ chatRoomProxyModel.filterText = searchBar.text } } } } } // --------------------------------------------------------------------------- // Chat. // --------------------------------------------------------------------------- Chat { id:chatArea Layout.fillHeight: true Layout.fillWidth: true proxyModel: ChatRoomProxyModel { id: chatRoomProxyModel function updateFilter(){ if ( chatRoomModel && ((!chatRoomModel.haveEncryption && !SettingsModel.standardChatEnabled) || (chatRoomModel.haveEncryption && !SettingsModel.secureChatEnabled)) ) { setEntryTypeFilter(ChatRoomModel.CallEntry) } } chatRoomModel: conversation.chatRoomModel peerAddress: conversation.peerAddress fullPeerAddress: conversation.fullPeerAddress fullLocalAddress: conversation.fullLocalAddress localAddress: conversation.localAddress// Reload is done on localAddress. Use this order onChatRoomModelChanged: updateFilter() Component.onCompleted: { updateFilter() } } } Connections { target: AccountSettingsModel onSipAddressChanged: { if (conversation.localAddress !== AccountSettingsModel.sipAddress) { window.setView('Home') } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/000077500000000000000000000000001434616504300241415ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/About.qml000066400000000000000000000062221434616504300257300ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import ConstantsCpp 1.0 import App.Styles 1.0 // ============================================================================= DialogPlus { buttons: [ TextButtonB { text: qsTr('ok') onClicked: exit(0) } ] buttonsAlignment: Qt.AlignCenter objectName: '__about' flat: true showMargins: true height: AboutStyle.height + 30 width: AboutStyle.width Column { anchors.fill: parent spacing: AboutStyle.spacing RowLayout { id:versionsLayout spacing: AboutStyle.versionsBlock.spacing height: AboutStyle.versionsBlock.iconSize width: parent.width Icon { icon: 'linphone_logo' iconSize: parent.height } Column { id:versionsArea Layout.fillWidth: true Layout.preferredHeight: parent.height spacing: 0 TextEdit { id: appVersion color: AboutStyle.versionsBlock.appVersion.color selectByMouse: true font.pointSize: AboutStyle.versionsBlock.appVersion.pointSize text: 'Desktop ' + Qt.application.version + ' - Qt' + App.qtVersion +'\nCore ' + CoreManager.version height: parent.height width: parent.width verticalAlignment: Text.AlignVCenter onActiveFocusChanged: deselect(); } } } Column { spacing: AboutStyle.copyrightBlock.spacing width: parent.width Text { elide: Text.ElideRight font.pointSize: AboutStyle.copyrightBlock.url.pointSize linkColor: AboutStyle.copyrightBlock.url.color text: ''+applicationUrl+'' width: parent.width visible: applicationUrl != '' horizontalAlignment: Text.AlignHCenter onLinkActivated: Qt.openUrlExternally(link) MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor } } Text { color: AboutStyle.copyrightBlock.license.color elide: Text.ElideRight font.pointSize: AboutStyle.copyrightBlock.license.pointSize visible: applicationVendor != '' text: (applicationLicence? applicationLicence+'\n':'') +'\u00A9 '+ copyrightRangeDate + ' ' + applicationVendor width: parent.width horizontalAlignment: Text.AlignHCenter } Text { elide: Text.ElideRight font.pointSize: AboutStyle.copyrightBlock.url.pointSize color: AboutStyle.copyrightBlock.url.color linkColor: AboutStyle.copyrightBlock.url.color //: 'Help us translate %1' : %1 is the application name text: ''+qsTr('aboutTranslation').arg(applicationName)+'' textFormat: Text.RichText width: parent.width visible: ConstantsCpp.TranslationUrl != '' horizontalAlignment: Text.AlignHCenter onLinkActivated: Qt.openUrlExternally(link) MouseArea { anchors.fill: parent acceptedButtons: Qt.NoButton cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.IBeamCursor } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/AuthenticationRequest.js000066400000000000000000000023171434616504300310320ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `AuthenticationRequest.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone // ============================================================================= function confirmPassword () { Linphone.AccountSettingsModel.addAuthInfo(dialog.authInfo, password.text, userId.text) } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/AuthenticationRequest.qml000066400000000000000000000030241434616504300312030ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import App.Styles 1.0 import 'AuthenticationRequest.js' as Logic // ============================================================================= DialogPlus { id: dialog property alias realm: realm.text property alias sipAddress: identity.text property alias userId: userId.text property var authInfo buttons: [ TextButtonA { text: qsTr('cancel') onClicked: exit(0) }, TextButtonB { enabled: password.length > 0 text: qsTr('confirm') onClicked: { Logic.confirmPassword() exit(1) } } ] buttonsAlignment: Qt.AlignCenter descriptionText: qsTr('authenticationRequestDescription') height: AuthenticationRequestStyle.height + 60 width: AuthenticationRequestStyle.width // --------------------------------------------------------------------------- Form { anchors.fill: parent orientation: Qt.Vertical FormLine { FormGroup { label: qsTr('identityLabel') TextField { id: identity readOnly: true } } } FormLine { FormGroup { label: qsTr('realmLabel') TextField { id: realm readOnly: true } } } FormLine { FormGroup { label: qsTr('userIdLabel') TextField { id: userId } } } FormLine { FormGroup { label: qsTr('passwordLabel') PasswordField { id: password } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/EphemeralChatRoom.qml000066400000000000000000000100371434616504300302140ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 import Common.Styles 1.0 import Units 1.0 // ============================================================================= DialogPlus { id:dialog buttons: [ TextButtonA { //: 'cancel' : button text for cancelling operation text: qsTr('cancelButton') capitalization: Font.AllUppercase onClicked:{ exit(0) } }, TextButtonB { //: 'start' : button text to start ephemeral mode text: qsTr('startButton') visible: chatRoomModel.canBeEphemeral onClicked: { if(dialog.timer=== 0) chatRoomModel.ephemeralEnabled = false else { chatRoomModel.ephemeralLifetime = dialog.timer chatRoomModel.ephemeralEnabled = true } exit(1) } } ] flat : true //: "Ephemeral messages" : Popup title for ephemerals title: qsTr('ephemeralTitle') showCloseCross:false property ChatRoomModel chatRoomModel property int timer : 0 buttonsAlignment: Qt.AlignCenter height: EphemeralChatRoomStyle.height width: EphemeralChatRoomStyle.width // --------------------------------------------------------------------------- ColumnLayout { anchors.fill: parent anchors.topMargin: EphemeralChatRoomStyle.mainLayout.topMargin anchors.leftMargin: EphemeralChatRoomStyle.mainLayout.leftMargin anchors.rightMargin: EphemeralChatRoomStyle.mainLayout.rightMargin spacing: EphemeralChatRoomStyle.mainLayout.spacing Layout.alignment: Qt.AlignCenter Icon{ icon: EphemeralChatRoomStyle.timer.icon iconSize: EphemeralChatRoomStyle.timer.iconSize overwriteColor: EphemeralChatRoomStyle.timer.timerColor Layout.preferredHeight: EphemeralChatRoomStyle.timer.preferredHeight Layout.preferredWidth: EphemeralChatRoomStyle.timer.preferredWidth Layout.alignment: Qt.AlignCenter } Text{ Layout.fillWidth: true Layout.alignment: Qt.AlignCenter Layout.leftMargin: EphemeralChatRoomStyle.descriptionText.leftMargin Layout.rightMargin: EphemeralChatRoomStyle.descriptionText.rightMargin maximumLineCount: 4 wrapMode: Text.Wrap //: 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' : Context Explanation for ephemerals text: qsTr('ephemeralText') //: 'Ephemeral message is only supported in conference based chat room!' //~ Context Warning about not being in conference based chat room. +(!chatRoomModel.canBeEphemeral?'\n'+qsTr('ephemeralNotInConference!'):'') verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: EphemeralChatRoomStyle.descriptionText.pointSize color: EphemeralChatRoomStyle.descriptionText.color } ComboBox{ Layout.alignment: Qt.AlignCenter Layout.preferredWidth: EphemeralChatRoomStyle.descriptionText.preferredWidth Layout.topMargin: EphemeralChatRoomStyle.descriptionText.topMargin Layout.bottomMargin: EphemeralChatRoomStyle.descriptionText.bottomMargin id:timerPicker textRole: "text" currentIndex: if( chatRoomModel.ephemeralLifetime == 0 || !chatRoomModel.ephemeralEnabled) return 0; else if( chatRoomModel.ephemeralLifetime <= 60 ) return 1; else if( chatRoomModel.ephemeralLifetime <= 3600 ) return 2; else if( chatRoomModel.ephemeralLifetime <= 86400 ) return 3; else if( chatRoomModel.ephemeralLifetime <= 259200 ) return 4; else if( chatRoomModel.ephemeralLifetime <= 604800 ) return 5; else return 5; model:[ //: 'Disabled' {text:qsTr('disabled'), value:0}, //: '%1 minute' { text:qsTr('nMinute', '', 1).arg(1), value:60}, //: '%1 hour' { text:qsTr('nHour', '', 1).arg(1), value:3600}, //: '%1 day' { text:qsTr('nDay', '', 1).arg(1), value:86400}, { text:qsTr('nDay', '', 3).arg(3), value:259200}, //: '%1 week' { text:qsTr('nWeek', '', 1).arg(1), value:604800} ] onActivated: dialog.timer = model[index].value visible: chatRoomModel.canBeEphemeral } } }linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/InfoChatRoom.qml000066400000000000000000000125131434616504300272060ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 import Common.Styles 1.0 import Units 1.0 import UtilsCpp 1.0 // ============================================================================= DialogPlus { id:dialog buttons: [ TextButtonA { //: 'Exit group' : Button label text: qsTr('quitGroupButton') capitalization: Font.AllUppercase textButtonStyle: InfoChatRoomStyle.leaveButton showBorder: true onClicked:{ chatRoomModel.leaveChatRoom(); exit(0) } enabled: !chatRoomModel.isReadOnly visible: !chatRoomModel.isOneToOne },Item{ Layout.fillWidth: true }, TextButtonB { //: 'OK' : Button label text: qsTr('ok') capitalization: Font.AllUppercase onClicked: { if(!chatRoomModel.isReadOnly) chatRoomModel.updateParticipants(selectedParticipants.getParticipants()) // Remove/New exit(1) } } ] showCloseCross: true //: "Group information" : Popup title. //~ This popup display data about the current chat room title: qsTr("chatRoomDetailsTitle") property ChatRoomModel chatRoomModel buttonsAlignment: Qt.AlignBottom buttonsLeftMargin: InfoChatRoomStyle.mainLayout.leftMargin buttonsRightMargin: InfoChatRoomStyle.mainLayout.rightMargin height: InfoChatRoomStyle.height width: InfoChatRoomStyle.width readonly property bool adminMode : chatRoomModel.isMeAdmin && !chatRoomModel.isReadOnly // --------------------------------------------------------------------------- ColumnLayout { id:mainLayout anchors.fill: parent anchors.topMargin: InfoChatRoomStyle.mainLayout.topMargin anchors.leftMargin: InfoChatRoomStyle.mainLayout.leftMargin anchors.rightMargin: InfoChatRoomStyle.mainLayout.rightMargin spacing: InfoChatRoomStyle.mainLayout.spacing SmartSearchBar { id: smartSearchBar Layout.fillWidth: true Layout.topMargin: InfoChatRoomStyle.searchBar.topMargin showHeader:false visible: dialog.adminMode && chatRoomModel.canHandleParticipants maxMenuHeight: MainWindowStyle.searchBox.maxHeight //: 'Add Participants' : Placeholder in a search bar for adding participant to the chat room placeholderText: qsTr('addParticipantPlaceholder') //: 'Search participants in your contact list in order to invite them into the chat room.' //~ Tooltip Explanation for inviting the selected participants into chat room tooltipText: qsTr('addParticipantTooltip') actions:[{ colorSet: InfoChatRoomStyle.addParticipant, secure: chatRoomModel.haveEncryption, visible: true, secureIconVisibleHandler : function(entry) { return entry && entry.sipAddress && chatRoomModel && chatRoomModel.haveEncryption && UtilsCpp.hasCapability(entry.sipAddress, LinphoneEnums.FriendCapabilityLimeX3Dh); }, handler: function (entry) { selectedParticipants.addAddress(entry.sipAddress) }, }] onEntryClicked: { selectedParticipants.addAddress(entry.sipAddress) } } ScrollableListViewField { Layout.fillHeight: true Layout.fillWidth: true Layout.bottomMargin: 5 //readOnly: toAddView.count >= conferenceManager.maxParticipants textFieldStyle: TextFieldStyle.normal ColumnLayout{ anchors.fill:parent spacing:0 Text{ Layout.topMargin: InfoChatRoomStyle.results.title.topMargin Layout.leftMargin: InfoChatRoomStyle.results.title.leftMargin //: 'Participant list' text:qsTr('participantList') color: InfoChatRoomStyle.results.title.color font.pointSize:InfoChatRoomStyle.results.title.pointSize font.weight: InfoChatRoomStyle.results.title.weight } Text{ Layout.preferredHeight: implicitHeight Layout.rightMargin: InfoChatRoomStyle.results.header.rightMargin Layout.alignment: Qt.AlignRight | Qt.AlignBottom //: 'Admin' : Admin(istrator) //~ one word for admin status text : qsTr('adminStatus') color: InfoChatRoomStyle.results.header.color font.pointSize: InfoChatRoomStyle.results.header.pointSize font.weight: InfoChatRoomStyle.results.header.weight visible: dialog.adminMode && participantView.count > 0 } ParticipantsView { id: participantView Layout.fillHeight: true Layout.fillWidth: true showSubtitle:false showSwitch : dialog.adminMode showSeparator: false showAdminStatus:!dialog.adminMode isSelectable: false hoveredCursor:Qt.WhatsThisCursor actions: dialog.adminMode ? [{ colorSet: InfoChatRoomStyle.removeParticipant, secure:0, visible:true, tooltipText: 'Remove this participant from the selection', handler: function (entry) { selectedParticipants.removeModel(entry) // ++lastContacts.reloadCount } }] : [] genSipAddress: '' model: ParticipantProxyModel { id:selectedParticipants chatRoomModel:dialog.chatRoomModel onAddressAdded: smartSearchBar.addAddressToIgnore(sipAddress) onAddressRemoved: smartSearchBar.removeAddressToIgnore(sipAddress) showMe: dialog.adminMode } onEntryClicked: { contactItem.showSubtitle = !contactItem.showSubtitle } } } } } }linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/InfoEncryption.qml000066400000000000000000000063271434616504300276320ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 import Common.Styles 1.0 import Units 1.0 // ============================================================================= DialogPlus { id:dialog buttons: [ TextButtonA { //: 'CANCEL' : button text for cancelling operation text: qsTr('cancelButton') capitalization: Font.AllUppercase onClicked:{ exit(0) } }, TextButtonB { //: 'CALL' : Button that lead to a call text: (addressToCall != '' ? qsTr('callButton') //: 'OK' : Button that validate the popup to be redirected to the device list : qsTr('okButton') ) textButtonStyle: InfoEncryptionStyle.okButton onClicked: { if(addressToCall != ''){ CallsListModel.launchSecureAudioCall(addressToCall, LinphoneEnums.MediaEncryptionZrtp) } exit(1) } } ] //: 'End-to-end encrypted' Popup title about encryption information. title: qsTr('infoEncryptionTitle') showCloseCross:false property int securityLevel property string addressToCall buttonsAlignment: Qt.AlignCenter height: InfoEncryptionStyle.height width: InfoEncryptionStyle.width // --------------------------------------------------------------------------- ColumnLayout { anchors.fill: parent anchors.topMargin: InfoEncryptionStyle.mainLayout.topMargin anchors.leftMargin: InfoEncryptionStyle.mainLayout.leftMargin anchors.rightMargin: InfoEncryptionStyle.mainLayout.rightMargin spacing: InfoEncryptionStyle.mainLayout.spacing Layout.alignment: Qt.AlignCenter Icon{ icon: dialog.securityLevel === 2?'secure_level_1': dialog.securityLevel===3? 'secure_level_2' : 'secure_level_unsafe' iconSize: InfoEncryptionStyle.securityIcon.iconSize Layout.preferredHeight: InfoEncryptionStyle.securityIcon.preferredHeight Layout.preferredWidth: InfoEncryptionStyle.securityIcon.preferredWidth Layout.alignment: Qt.AlignCenter } Text{ Layout.fillWidth: true Layout.alignment: Qt.AlignCenter Layout.leftMargin: InfoEncryptionStyle.descriptionText.leftMargin Layout.rightMargin: InfoEncryptionStyle.descriptionText.rightMargin verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: InfoEncryptionStyle.descriptionText.pointSize color: InfoEncryptionStyle.descriptionText.color wrapMode: Text.Wrap //: "Instant messages are end-to-end encrypted in secured conversations. It is possible to upgrade the security level of a conversation by authentificating participants." //~ Context Explanation of Encryption text: qsTr('encryptionExplanation') } Text{ Layout.fillWidth: true Layout.alignment: Qt.AlignCenter Layout.leftMargin: InfoEncryptionStyle.descriptionText.leftMargin Layout.rightMargin: InfoEncryptionStyle.descriptionText.rightMargin verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.pointSize: InfoEncryptionStyle.descriptionText.pointSize color: InfoEncryptionStyle.descriptionText.color wrapMode: Text.Wrap //: "To do so, call the contact and follow the authentification process." //~ Context Explanation process text: qsTr('encryptionProcessExplanation') } } }linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/ManageAccount.js000066400000000000000000000024361434616504300272110ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `ManageAccount.qml` Logic. // ============================================================================= function getItemIcon (data) { var account = data.account if (!account) { return '' } var description = AccountSettingsModel.getAccountDescription(account) return description.registerEnabled && description.registrationState !== AccountSettingsModel.RegistrationStateRegistered ? 'generic_error' : '' } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/ManageAccounts.qml000066400000000000000000000063211434616504300275460ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import Utils 1.0 import App.Styles 1.0 import 'ManageAccount.js' as Logic // ============================================================================= DialogPlus { buttons: [ TextButtonB { text: qsTr('ok') onClicked: exit(0) } ] buttonsAlignment: Qt.AlignCenter objectName: '__manageAccounts' flat: true showMargins: true height: SettingsModel.rlsUriEnabled ? ManageAccountsStyle.height : ManageAccountsStyle.heightWithoutPresence width: ManageAccountsStyle.width // --------------------------------------------------------------------------- Form { anchors.fill: parent orientation: Qt.Vertical FormLine { visible: SettingsModel.rlsUriEnabled FormGroup { label: qsTr('selectPresenceLabel') ComboBox { currentIndex: Utils.findIndex(OwnPresenceModel.statuses, function (status) { return status.presenceStatus === OwnPresenceModel.presenceStatus }) model: OwnPresenceModel.statuses iconRole: 'presenceIcon' textRole: 'presenceLabel' onActivated: OwnPresenceModel.presenceStatus = model[index].presenceStatus } } } FormLine { FormGroup { label: qsTr('selectAccountLabel') ScrollableListViewField { width: parent.width height: ManageAccountsStyle.accountSelector.height radius: 0 ScrollableListView { id: view property string textRole: 'fullSipAddress' // Used by delegate. anchors.fill: parent model: AccountSettingsModel.accounts onModelChanged: currentIndex = Utils.findIndex(AccountSettingsModel.accounts, function (account) { return account.sipAddress === AccountSettingsModel.sipAddress }) delegate: CommonItemDelegate { id: item container: view flattenedModel: modelData itemIcon: ''//Start with no error and let some time before getting status with the below timer width: parent.width Timer{// This timer is used to synchronize registration state by proxy, without having to deal with change signals interval: 1000; running: item.visible; repeat: true onTriggered:itemIcon= Logic.getItemIcon(flattenedModel) } ActionButton { isCustom: true backgroundRadius: 90 colorSet: ManageAccountsStyle.options anchors.fill: parent visible:false //TODO handle click and jump to proxy config settings } onClicked: { container.currentIndex = index if(flattenedModel.account) AccountSettingsModel.setDefaultAccount(flattenedModel.account) else AccountSettingsModel.setDefaultAccount() } MessageCounter { anchors.fill: parent count: flattenedModel.unreadMessageCount+flattenedModel.missedCallCount } } } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/NewChatRoom.qml000066400000000000000000000316751434616504300270560ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 import Common.Styles 1.0 import Units 1.0 import UtilsCpp 1.0 import ColorsList 1.0 // ============================================================================= DialogPlus { id: conferenceManager property ChatRoomModel chatRoomModel readonly property int minParticipants: 1 buttons: [ TextButtonA { //: 'Cancel' : Cancel button text: qsTr('cancelButton') capitalization: Font.AllUppercase onClicked: exit(0) }, TextButtonB { enabled: selectedParticipants.count >= conferenceManager.minParticipants && subject.text != '' && AccountSettingsModel.conferenceUri != '' //: 'Launch' : Start button text: qsTr('startButton') capitalization: Font.AllUppercase onClicked: { if(CallsListModel.createChatRoom(subject.text, secureSwitch.checked, selectedParticipants.getParticipants(), true )) exit(1) } TooltipArea{ visible: AccountSettingsModel.conferenceUri == '' || subject.text == '' || selectedParticipants.count < conferenceManager.minParticipants maxWidth: participantView.width delay:0 text: { var txt = '\n'; if( subject.text == '') //: 'You need to fill a subject.' : Tooltip to warn a user on missing field. txt ='- ' + qsTr('missingSubject') + '\n' if( selectedParticipants.count < conferenceManager.minParticipants) //: 'You need at least %1 participant.' : Tooltip to warn a user that there are not enough participants for the chat creation. txt += '- ' + qsTr('missingParticipants', '', conferenceManager.minParticipants).arg(conferenceManager.minParticipants) + '\n' if( AccountSettingsModel.conferenceUri == '') //: 'You need to set the conference URI in your account settings to create a conference based chat room.' : Tooltip to warn the user that a setting is missong in its configuration. txt += '- ' + qsTr('missingConferenceURI') + '\n' return txt; } } } ] buttonsAlignment: Qt.AlignRight //: 'Start a chat room' : Title of a popup about creation of a chat room title:qsTr('newChatRoomTitle') height: 500 width: 800 // --------------------------------------------------------------------------- RowLayout { anchors.fill: parent spacing: 0 // ------------------------------------------------------------------------- // Address selector. // ------------------------------------------------------------------------- Item { Layout.fillHeight: true Layout.fillWidth: true ColumnLayout { anchors.fill: parent spacing: 20 Item{ Layout.fillHeight: true } ColumnLayout{ Layout.fillWidth: true Layout.topMargin:15 spacing:4 visible: SettingsModel.secureChatEnabled && SettingsModel.standardChatEnabled Text { Layout.fillWidth: true //: 'Would you like to encrypt your chat?' : Ask about setting the chat room as secured. text:qsTr('askEncryption') color: NewChatRoomStyle.askEncryptionColor font.pointSize: Units.dp * 11 font.weight: Font.DemiBold } Item{ Layout.fillWidth: true Layout.preferredHeight: 50 Icon{ id:secureOff anchors.left:parent.left anchors.leftMargin : 5 anchors.verticalCenter: parent.verticalCenter width:20 height:20 icon: 'secure_off' iconSize:20 } Switch{ id:secureSwitch anchors.left:secureOff.right anchors.leftMargin : 5 anchors.verticalCenter: parent.verticalCenter width:50 enabled:true checked: !SettingsModel.standardChatEnabled && SettingsModel.secureChatEnabled onClicked: { var newCheck = checked if(SettingsModel.standardChatEnabled && checked || SettingsModel.secureChatEnabled && !checked) newCheck = !checked; /* Uncomment if we need to remove participants that doesn't have the capability (was commented because we cannot get capabilities in all cases) if(newCheck){ // Remove all participants that have not the capabilities var participants = selectedParticipants.getParticipants() for(var index in participants){ if(!smartSearchBar.isUsable(participants[index].sipAddress)) participantView.removeParticipant(participants[index]) } } */ checked = newCheck; } indicatorStyle: SwitchStyle.aux } Icon{ id:secureOn anchors.left:secureSwitch.right anchors.leftMargin : 15 anchors.verticalCenter: parent.verticalCenter width:20 height:20 icon: 'secure_on' iconSize:20 } } } Item{ Layout.fillHeight:true } ColumnLayout { Layout.fillWidth: true spacing:10 RowLayout{ Icon{ id:defaultSecure Layout.alignment: Qt.AlignCenter Layout.preferredHeight: visible? 20 : 0 Layout.preferredWidth: visible? 20 : 0 icon: 'secure_on' iconSize:20 visible: SettingsModel.secureChatEnabled && !SettingsModel.standardChatEnabled } Text{ textFormat: Text.RichText //: 'Subject' : Label of a text field about the subject of the chat room text :qsTr('subjectLabel') +'*' color: NewChatRoomStyle.subjectTitleColor font.pointSize: Units.dp * 11 font.weight: Font.DemiBold } } TextField { id:subject Layout.fillWidth: true Layout.rightMargin: 15 //: 'Give a subject' : Placeholder in a form about setting a subject placeholderText : qsTr('subjectPlaceholder') text:(chatRoomModel?chatRoomModel.getSubject():'') Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() TooltipArea{ //: 'Current subject of the Chat Room. It cannot be empty' //~ Tooltip Explanation about the subject of the chat room text : qsTr('subjectTooltip') } } } Item{ Layout.fillHeight:true } ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true spacing:20 Text{ //: 'Last contacts' : Header for showing last contacts text : qsTr('LastContactsTitle') color: NewChatRoomStyle.recentContactTitleColor font.pointSize: Units.dp * 11 font.weight: Font.DemiBold } RowLayout{ Layout.fillWidth: true Layout.fillHeight : true spacing:0 Repeater{ id:lastContacts property int reloadCount : 0 model:TimelineListModel.getLastChatRooms(5) delegate : Item{ //Layout.fillHeight: true Layout.preferredHeight: 60 Layout.preferredWidth: 50 + avatar2.height/2 ColumnLayout{ anchors.fill:parent Avatar{ id:avatar2 Layout.preferredHeight: 50 Layout.preferredWidth: 50 Layout.alignment: Qt.AlignCenter username: modelData.username image:modelData.avatar Icon{ property int securityLevel : 2 anchors.top:parent.top anchors.horizontalCenter: parent.right visible: SettingsModel.secureChatEnabled && UtilsCpp.hasCapability(modelData.sipAddress, LinphoneEnums.FriendCapabilityLimeX3Dh) icon: 'secure_on' iconSize: parent.height/2 } } Text{ Layout.fillHeight: true Layout.preferredWidth: 60 Layout.alignment: Qt.AlignVCenter | Qt.AlignTop maximumLineCount: 5 wrapMode:Text.Wrap text: modelData.username verticalAlignment: Text.AlignTop horizontalAlignment: Text.AlignHCenter font.weight: Font.DemiBold lineHeight: 0.8 color: NewChatRoomStyle.recentContactUsernameColor font.pointSize: Units.dp * 9 clip:false } } Rectangle{ id:mask anchors.fill:parent //anchors.topMargin: -5 color:'white' opacity: 0.5 visible: smartSearchBar.isIgnored(modelData.sipAddress) Connections{// Workaround for refreshing data on events target:lastContacts onReloadCountChanged: { mask.visible=smartSearchBar.isIgnored(modelData.sipAddress) } } } MouseArea{ anchors.fill:parent visible:!mask.visible onClicked: { selectedParticipants.addAddress(modelData.sipAddress) smartSearchBar.addAddressToIgnore(modelData.sipAddress); ++lastContacts.reloadCount } } } } } } Item{ Layout.fillHeight: true Layout.fillWidth: true } } } // ------------------------------------------------------------------------- // See and remove selected addresses. // ------------------------------------------------------------------------- ColumnLayout{ Layout.fillHeight: true Layout.fillWidth: true Layout.topMargin: 10 Layout.bottomMargin: 10 Rectangle{ Layout.fillHeight: true Layout.fillWidth: true border.width: 1 border.color: NewChatRoomStyle.addressesBorderColor ColumnLayout { anchors.fill: parent anchors.topMargin: 15 anchors.leftMargin: 10 anchors.rightMargin: 10 spacing: 0 SmartSearchBar { id: smartSearchBar Layout.fillWidth: true Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing showHeader:false maxMenuHeight: MainWindowStyle.searchBox.maxHeight //: 'Select participants' : Placeholder for a search on participant to add them in selection. placeholderText: qsTr('participantSelectionPlaceholder') //: 'Search in your contacts or add a custom one to the chat room.' tooltipText: qsTr('participantSelectionTooltip') actions:[{ colorSet: NewChatRoomStyle.addParticipant, secure: SettingsModel.secureChatEnabled, visible: true, secureIconVisibleHandler : function(entry) { return entry && entry.sipsipAddress ? UtilsCpp.hasCapability(entry.sipAddress, LinphoneEnums.FriendCapabilityLimeX3Dh) : false }, handler: function (entry) { selectedParticipants.addAddress(entry.sipAddress) smartSearchBar.addAddressToIgnore(entry.sipAddress); ++lastContacts.reloadCount }, }] onEntryClicked: { selectedParticipants.addAddress(entry.sipAddress) smartSearchBar.addAddressToIgnore(entry.sipAddress); ++lastContacts.reloadCount } } Text{ Layout.preferredHeight: 20 Layout.rightMargin: 65 Layout.alignment: Qt.AlignRight | Qt.AlignBottom Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing //: 'Admin' : Admin(istrator) //~ one word for admin status text : qsTr('adminStatus') color: NewChatRoomStyle.addressesAdminColor font.pointSize: Units.dp * 11 font.weight: Font.Light visible: participantView.count > 0 } ScrollableListViewField { Layout.fillHeight: true Layout.fillWidth: true Layout.bottomMargin: 5 textFieldStyle: TextFieldStyle.unbordered ParticipantsView { id: participantView anchors.fill: parent showSubtitle:false showSwitch : true showSeparator: false isSelectable: false showInvitingIndicator: false function removeParticipant(entry){ smartSearchBar.removeAddressToIgnore(entry.sipAddress) selectedParticipants.removeModel(entry) ++lastContacts.reloadCount } actions: [{ colorSet: NewChatRoomStyle.removeParticipant, secure:0, visible:true, //: 'Remove this participant from the selection' : Explanation about removing participant from a selection //~ Tooltip This is a tooltip tooltipText: qsTr('removeParticipantSelection'), handler: function (entry) { removeParticipant(entry) } }] genSipAddress: '' model: ParticipantProxyModel { id:selectedParticipants chatRoomModel:null } // it's best to toggle all contacts instead of one (that will be reset after loadng another address) onEntryClicked: participantView.showSubtitle = !participantView.showSubtitle } } } } Item{ Layout.fillWidth: true Layout.preferredHeight: 20 Text{ anchors.fill:parent textFormat: Text.RichText //: 'Required' : Word relative to a star to explain that it is a requirement (Field form) text : '* '+qsTr('requiredField') color: NewChatRoomStyle.requiredColor font.pointSize: Units.dp * 8 } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Dialogs/ParticipantsDevices.qml000066400000000000000000000147151434616504300306300ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Utils 1.0 import UtilsCpp 1.0 import LinphoneEnums 1.0 import App.Styles 1.0 import Units 1.0 import ColorsList 1.0 import '../Conversation.js' as Logic // ============================================================================= DialogPlus { id:dialog buttons: [] //: 'Conversation's devices' : Title of window that show all devices title: qsTr('conversationDevicesTitle') showCloseCross:true property ChatRoomModel chatRoomModel property var window buttonsAlignment: Qt.AlignCenter height: 383 width: 450 // --------------------------------------------------------------------------- ScrollableListViewField { anchors.fill:parent radius: 0 ScrollableListView { id: view property var window : dialog.window anchors.fill: parent model: ParticipantProxyModel{ chatRoomModel : dialog.chatRoomModel } delegate: ColumnLayout{ id:mainHeader property var window : ListView.view.window property int securityLevel : $modelData.securityLevel property string addressToCall : $modelData.sipAddress width: parent ? parent.width : undefined spacing: 0 RowLayout { id: item Layout.fillWidth: true Layout.preferredHeight: 50 Layout.minimumHeight: 50// Fix layout to avoid infinite update loop between ListView and RowLayout Layout.maximumHeight: 50 Avatar{ id:avatar Layout.preferredHeight: 32 Layout.preferredWidth: 32 Layout.leftMargin: 14 username: $modelData?($modelData.contactModel ? $modelData.contactModel.vcard.username :$modelData.username?$modelData.username: UtilsCpp.getDisplayName($modelData.sipAddress) ):'' Icon{ property int securityLevel : $modelData.securityLevel anchors.top:parent.top anchors.horizontalCenter: parent.right visible: $modelData && securityLevel !== 1 icon: $modelData?(securityLevel === 2?'secure_level_1': securityLevel===3? 'secure_level_2' : 'secure_level_unsafe'):'secure_level_unsafe' iconSize: parent.height/2 Timer{// Workaround : no security events are send when device's security change. onTriggered: parent.securityLevel = $modelData.securityLevel repeat:true running:true interval:500 } } } Item{ Layout.fillHeight: true Layout.fillWidth: true Layout.leftMargin: 14 ContactDescription{ id:contactDescription anchors.fill:parent titleText: avatar.username } MouseArea{ anchors.fill:contactDescription onClicked: { if(participantDevices.count == 0 ) mainHeader.window.attachVirtualWindow(Qt.resolvedUrl('InfoEncryption.qml') ,{securityLevel : mainHeader.securityLevel , addressToCall : mainHeader.addressToCall} ) else participantDevices.visible = !participantDevices.visible } } } ActionButton { Layout.preferredHeight: iconSize Layout.preferredWidth: iconSize Layout.leftMargin: isCustom ? 9 : 14 Layout.rightMargin: isCustom ? 9 : 14 property int securityLevel : $modelData.securityLevel property bool participantsDevicesVisible: participantDevices.visible function setColorSet(){ if(isCustom) colorSet = participantsDevicesVisible ? ParticipantsDevicesStyle.expanded : ParticipantsDevicesStyle.collapsed iconSize = colorSet.iconSize } onSecurityLevelChanged: if( $modelData.deviceCount == 0){ icon = securityLevel === 2?'secure_level_1': (securityLevel===3? 'secure_level_2' : 'secure_level_unsafe') iconSize = 20 } onParticipantsDevicesVisibleChanged: setColorSet() visible:true useStates: $modelData.deviceCount > 0 isCustom: useStates onIsCustomChanged: setColorSet() onClicked: participantDevices.visible = !participantDevices.visible } } Rectangle { color: ParticipantsDevicesStyle.lineSeparatorColor Layout.preferredHeight: 1 Layout.fillWidth: true } ListView{ id:participantDevices property var window : dialog.window visible: view.count < 4 Layout.fillWidth: true Layout.preferredHeight: item.height * count interactive: false model: $modelData.getProxyDevices() Component.onDestruction: {model=null}// Need to set it to null because of not calling destructor if not. delegate: Rectangle{ id:mainRectangle property var window : ListView.view.window property int securityLevel : modelData.securityLevel property string addressToCall : modelData.address width:parent.width height:50 color: ParticipantsDevicesStyle.lineBackgroundColor RowLayout{ anchors.fill:parent Text{ Layout.fillWidth: true Layout.fillHeight: true Layout.leftMargin: avatar.width+14*2 font.weight: Font.Light font.pointSize: Units.dp * 11 verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap text: modelData.name MouseArea{ anchors.fill:parent onClicked: { mainRectangle.window.attachVirtualWindow(Qt.resolvedUrl('InfoEncryption.qml') ,{securityLevel : mainRectangle.securityLevel , addressToCall : mainRectangle.addressToCall} ) } } } Icon{ property int securityLevel : modelData.securityLevel Layout.rightMargin: 14 Layout.preferredHeight: 20 Layout.preferredWidth: 20 visible: securityLevel !== 1 icon: securityLevel === 2?'secure_level_1': securityLevel===3? 'secure_level_2' : 'secure_level_unsafe' iconSize:20 Timer{// Workaround : no security events are send when device's security change. onTriggered: parent.securityLevel = $modelData.securityLevel repeat:true running:true interval:500 } } } Rectangle { color: ParticipantsDevicesStyle.lineSeparatorColor anchors.left : parent.left anchors.right :parent.right anchors.bottom: parent.bottom height: 1 visible: (index !== (model.count - 1)) } } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/HistoryView.js000066400000000000000000000037551434616504300254230ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `Conversation.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import UtilsCpp 1.0 as UtilsCpp .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function removeAllEntries () { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('removeAllEntriesDescription'), }, function (status) { if (status) { historyProxyModel.removeAllEntries() } }) } function getAvatar () { var contact = historyView._sipAddressObserver.contact return contact ? contact.vcard.avatar : '' } function getEditTooltipText() { return historyView._sipAddressObserver && historyView._sipAddressObserver.contact ? qsTr('tooltipContactEdit') : qsTr('tooltipContactAdd') } function updateHistoryFilter (button) { var HistoryModel = Linphone.HistoryModel if (button === 0) { historyProxyModel.setEntryTypeFilter(HistoryModel.GenericEntry) } else if (button === 1) { historyProxyModel.setEntryTypeFilter(HistoryModel.CallEntry) } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/HistoryView.qml000066400000000000000000000130621434616504300255700ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import UtilsCpp 1.0 import App.Styles 1.0 import 'HistoryView.js' as Logic // ============================================================================= ColumnLayout { id: historyView property var entry property string peerAddress : entry ? entry.sipAddress : '' property string fullPeerAddress : entry ? entry.sipAddress : '' property var _sipAddressObserver: peerAddress?SipAddressesModel.getSipAddressObserver((fullPeerAddress?fullPeerAddress:peerAddress), ''):null onEntryChanged: historyProxyModel.resetMessageCount() // --------------------------------------------------------------------------- spacing: 0 Component.onDestruction: _sipAddressObserver=null// Need to set it to null because of not calling destructor if not. // --------------------------------------------------------------------------- // Contact bar. // --------------------------------------------------------------------------- Rectangle { Layout.fillWidth: true Layout.preferredHeight: peerAddress?HistoryViewStyle.bar.height:HistoryViewStyle.bar.height/2 color: HistoryViewStyle.bar.backgroundColor RowLayout { anchors { fill: parent leftMargin: HistoryViewStyle.bar.leftMargin rightMargin: HistoryViewStyle.bar.rightMargin } spacing: HistoryViewStyle.bar.spacing layoutDirection: peerAddress?Qt.LeftToRight :Qt.RightToLeft Avatar { id: avatar Layout.preferredHeight: HistoryViewStyle.bar.avatarSize Layout.preferredWidth: HistoryViewStyle.bar.avatarSize image: peerAddress && historyView._sipAddressObserver && historyView._sipAddressObserver.contact? historyView._sipAddressObserver.contact.avatar : null presenceLevel: historyView._sipAddressObserver?Presence.getPresenceLevel( historyView._sipAddressObserver.presenceStatus ):null username: historyView.entry && historyView.entry.wasConference ? historyView.entry.title : peerAddress && historyView._sipAddressObserver ? UtilsCpp.getDisplayName(historyView._sipAddressObserver.peerAddress) : null visible: peerAddress } ContactDescription { Layout.fillHeight: true Layout.fillWidth: true subtitleText: historyView.entry && historyView.entry.wasConference ? '' : SipAddressesModel.cleanSipAddress(historyView.peerAddress) subtitleColor: HistoryViewStyle.bar.description.subtitleColor titleText: avatar.username titleColor: HistoryViewStyle.bar.description.titleColor visible:peerAddress } Row { Layout.fillHeight: true spacing: HistoryViewStyle.bar.actions.spacing ActionBar { anchors.verticalCenter: parent.verticalCenter iconSize: HistoryViewStyle.bar.actions.call.iconSize visible: historyView.entry ? !historyView.entry.wasConference : false ActionButton { isCustom: true backgroundRadius: 90 colorSet: HistoryViewStyle.videoCall visible: peerAddress && SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.showStartVideoCallButton onClicked: CallsListModel.launchVideoCall(historyView.peerAddress) } ActionButton { isCustom: true backgroundRadius: 90 colorSet: HistoryViewStyle.call visible: peerAddress && SettingsModel.outgoingCallsEnabled onClicked: CallsListModel.launchAudioCall(historyView.peerAddress) } ActionButton { isCustom: true backgroundRadius: 90 colorSet: HistoryViewStyle.chat visible: peerAddress && SettingsModel.standardChatEnabled && SettingsModel.getShowStartChatButton() onClicked: CallsListModel.launchChat(historyView.peerAddress, 0) } ActionButton { isCustom: true backgroundRadius: 1000 colorSet: HistoryViewStyle.chat visible: peerAddress && SettingsModel.secureChatEnabled && SettingsModel.getShowStartChatButton() onClicked: CallsListModel.launchChat(historyView.peerAddress, 1) Icon{ icon:'secure_level_1' iconSize: parent.height/2 anchors.top:parent.top anchors.horizontalCenter: parent.right } } } ActionBar { anchors.verticalCenter: parent.verticalCenter ActionButton { isCustom: true backgroundRadius: 4 colorSet: historyView._sipAddressObserver && historyView._sipAddressObserver.contact ? ConversationStyle.bar.actions.edit.viewContact : ConversationStyle.bar.actions.edit.addContact iconSize: HistoryViewStyle.bar.actions.edit.iconSize visible: SettingsModel.contactsEnabled && historyView.entry ? !historyView.entry.wasConference : false onClicked: window.setView('ContactEdit', { sipAddress: historyView.peerAddress }) tooltipText: peerAddress?Logic.getEditTooltipText():'' } ActionButton { isCustom: true backgroundRadius: 90 colorSet: HistoryViewStyle.deleteAction onClicked: Logic.removeAllEntries() tooltipText: qsTr('cleanHistory') } } } } } // --------------------------------------------------------------------------- // History. // --------------------------------------------------------------------------- History { Layout.fillHeight: true Layout.fillWidth: true onEntryClicked:{ historyView.entry = entry } proxyModel: HistoryProxyModel { id: historyProxyModel Component.onCompleted: { setEntryTypeFilter() resetMessageCount() } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/Home.qml000066400000000000000000000046101434616504300241630ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 import Utils 1.0 import App.Styles 1.0 // ============================================================================= Rectangle { color: HomeStyle.color // TODO: Remove me when smart tooltip will be available. Component { Item { property var i18n: [ QT_TR_NOOP('showTooltips'), QT_TR_NOOP('howToDescription'), QT_TR_NOOP('howToTitle') ] } } ListView { anchors.horizontalCenter: parent.horizontalCenter boundsBehavior: Flickable.StopAtBounds clip: true orientation: ListView.Horizontal spacing: HomeStyle.spacing height: parent.height width: { var width = CardBlockStyle.width * count + (count - 1) * spacing return parent.width < width ? parent.width : width } model: ListModel { // TODO: Uncomment me when smart tooltip will be available. // ListElement { // $component: 'checkBox' // $componentText: qsTr('showTooltips') // $description: qsTr('howToDescription') // $icon: 'home_use_linphone' // $title: qsTr('howToTitle') // } ListElement { $component: 'button' $componentText: qsTr('inviteButton') $description: qsTr('inviteDescription') $view: 'InviteFriends' $icon: 'home_invite_friends' $title: qsTr('inviteTitle') } ListElement { $component: 'button' $componentText: qsTr('assistantButton') $description: qsTr('accountAssistantDescription') $icon: 'home_account_assistant' $title: qsTr('accountAssistantTitle') $view: 'Assistant' } } delegate: CardBlock { anchors.verticalCenter: parent.verticalCenter description: $description.replace('%1', Utils.capitalizeFirstLetter(Qt.application.name)) icon: $icon title: $title.replace('%1', Qt.application.name.toUpperCase()) Loader { Component { id: button TextButtonB { text: $componentText onClicked: window.setView($view) } } Component { id: checkBox CheckBoxText { text: $componentText } } anchors.horizontalCenter: parent.horizontalCenter sourceComponent: $component === 'button' ? button : checkBox } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/InviteFriends.qml000066400000000000000000000044171434616504300260510ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Utils 1.0 import App.Styles 1.0 // ============================================================================= Rectangle { color: InviteFriendsStyle.color ColumnLayout { anchors.fill: parent spacing: 0 Item { id: content Layout.fillHeight: true Layout.fillWidth: true Form { anchors.centerIn: parent orientation: Qt.Vertical title: qsTr('inviteFriendsTitle') width: InviteFriendsStyle.width FormLine { FormGroup { label: qsTr('enterEmailLabel') TextField { id: email inputMethodHints: Qt.ImhEmailCharactersOnly width: parent.width } } } FormLine { FormGroup { label: qsTr('messageLabel') TextAreaField { id: message height: InviteFriendsStyle.message.height text: qsTr('defaultMessage') .replace('%1', AccountSettingsModel.username) .replace('%2', Utils.capitalizeFirstLetter(Qt.application.name)) } } } } } // ------------------------------------------------------------------------- // Buttons. // ------------------------------------------------------------------------- Row { id: buttons Layout.alignment: Qt.AlignHCenter Layout.bottomMargin: InviteFriendsStyle.buttons.bottomMargin spacing: InviteFriendsStyle.buttons.spacing TextButtonA { text: qsTr('cancel') onClicked: window.setView('Home') } TextButtonB { enabled: email.length && message.length text: qsTr('confirm') onClicked: { Qt.openUrlExternally( 'mailto:' + encodeURIComponent(email.text) + '?subject=' + encodeURIComponent( qsTr('defaultSubject').replace('%1', Utils.capitalizeFirstLetter(Qt.application.name)) ) + '&body=' + encodeURIComponent( message.text + '\n\n' + qsTr('forcedMessage').replace(/%1/g, CoreManager.downloadUrl) ) ) window.setView('Home') } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/MainWindow.js000066400000000000000000000071631434616504300252000ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `MainWindow.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import QtQuick.Window 2.2 as Window .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function handleActiveFocusItemChanged (activeFocusItem) { var smartSearchBar = window._smartSearchBar if (activeFocusItem == null && smartSearchBar) { smartSearchBar.closeMenu() } } function handleClosing (close) { if (Linphone.SettingsModel.exitOnClose) { Qt.quit() return } } // ----------------------------------------------------------------------------- function lockView (info) { window._lockedInfo = info } function unlockView () { window._lockedInfo = undefined } function setView (view, props, callback) { function apply (view, props, showWindow, callback) { if(showWindow) Linphone.App.smartShowWindow(window) var item = mainLoader.item updateSelectedEntry(view, props) window._currentView = view item.contentLoader.setSource(view + '.qml', props || {}) if(callback) callback() } var lockedInfo = window._lockedInfo if (!lockedInfo) { apply(view, props, false, callback) return } window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: lockedInfo.descriptionText, }, function (status) { if (status) { unlockView() apply(view, props, true, callback) } else { updateSelectedEntry(window._currentView, props) } }) } // ----------------------------------------------------------------------------- function openConferenceManager (params) { var App = Linphone.App var callsWindow = App.getCallsWindow() App.smartShowWindow(callsWindow) callsWindow.openConferenceManager(params) } function manageAccounts () { window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ManageAccounts.qml')) } // ----------------------------------------------------------------------------- function updateSelectedEntry (view, props) { var item = mainLoader.item var menu = item.menu var timeline = item.timeline if (view === 'Home') { menu.resetSelectedEntry() } else if (view === 'Contacts') { item.contactsEntry.select() } else if (view === 'Conferences') { item.conferencesEntry.select() } } // ----------------------------------------------------------------------------- function handleAuthenticationRequested (authInfo, realm, sipAddress, userId) { window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/AuthenticationRequest.qml'), { authInfo: authInfo, realm: realm, sipAddress: 'sip:'+sipAddress, userId: userId, virtualWindowHash:Qt.md5('Dialogs/AuthenticationRequest.qml'+realm+sipAddress+userId) }) } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/MainWindow.qml000066400000000000000000000313641434616504300253550ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Qt.labs.platform 1.0 import Common 1.0 import Linphone 1.0 import Utils 1.0 import App.Styles 1.0 import ColorsList 1.0 import 'MainWindow.js' as Logic import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= ApplicationWindow { id: window property string _currentView property var _lockedInfo property SmartSearchBar mainSearchBar : (mainLoader.item ? mainLoader.item.mainSearchBar : null) // --------------------------------------------------------------------------- function lockView (info) { Logic.lockView(info) } function unlockView () { Logic.unlockView() } function setView (view, props, callback) { Logic.setView(view, props, callback) } // --------------------------------------------------------------------------- // Window properties. // --------------------------------------------------------------------------- minimumHeight: MainWindowStyle.minimumHeight minimumWidth: MainWindowStyle.minimumWidth title: Utils.capitalizeFirstLetter(Qt.application.name) // --------------------------------------------------------------------------- onActiveFocusItemChanged: Logic.handleActiveFocusItemChanged(activeFocusItem) onClosing: Logic.handleClosing(close) // --------------------------------------------------------------------------- Connections { target: CoreManager onCoreManagerInitialized: mainLoader.active = true } Shortcut { sequence: StandardKey.Close onActivated: window.hide() } // --------------------------------------------------------------------------- Loader { id: mainLoader active: false anchors.fill: parent sourceComponent: ColumnLayout { // Workaround to get these properties in `MainWindow.js`. readonly property alias contactsEntry: contactsEntry readonly property alias conferencesEntry: conferencesEntry readonly property alias contentLoader: contentLoader //readonly property alias conferencesEntry: conferencesEntry readonly property alias menu: menu readonly property alias timeline: timeline readonly property alias mainSearchBar: toolBar.mainSearchBar spacing: 0 // ----------------------------------------------------------------------- AuthenticationNotifier { onAuthenticationRequested: Logic.handleAuthenticationRequested(authInfo, realm, sipAddress, userId) } // ----------------------------------------------------------------------- // Toolbar properties. // ----------------------------------------------------------------------- ToolBar { id: toolBar property alias mainSearchBar : smartSearchBar Layout.fillWidth: true Layout.preferredHeight: MainWindowStyle.toolBar.height hoverEnabled : true background: MainWindowStyle.toolBar.background RowLayout { anchors { fill: parent leftMargin: MainWindowStyle.toolBar.leftMargin rightMargin: MainWindowStyle.toolBar.rightMargin } spacing: MainWindowStyle.toolBar.spacing ActionButton { icon: (leftPanel.visible?'panel_shown':'panel_hidden') //: 'Hide Timeline' : Tooltip for a button that hide the timeline tooltipText : (leftPanel.visible?qsTr('hideTimeline') //: 'Open Timeline' : Tooltip for a button that open the timeline :qsTr('openTimeline')) iconSize: MainWindowStyle.panelButtonSize //autoIcon: true onClicked: leftPanel.visible = !leftPanel.visible } ActionButton { isCustom: true backgroundRadius: 4 colorSet: MainWindowStyle.buttons.home //: 'Open Home' : Tooltip for a button that open the home view tooltipText : qsTr('openHome') //autoIcon: true onClicked: setView('Home') } AccountStatus { id: accountStatus betterIcon:true Layout.preferredHeight: parent.height Layout.preferredWidth: MainWindowStyle.accountStatus.width Layout.fillWidth: false TooltipArea { text: AccountSettingsModel.sipAddress hoveringCursor: Qt.PointingHandCursor } onClicked: { CoreManager.forceRefreshRegisters() Logic.manageAccounts() } } ColumnLayout { Layout.preferredWidth: MainWindowStyle.autoAnswerStatus.width visible: SettingsModel.autoAnswerStatus Icon { icon: SettingsModel.autoAnswerStatus ? 'auto_answer_custom' : '' iconSize: MainWindowStyle.autoAnswerStatus.iconSize overwriteColor: MainWindowStyle.autoAnswerStatus.text.color } Text { clip: true color: MainWindowStyle.autoAnswerStatus.text.color font { bold: true pointSize: MainWindowStyle.autoAnswerStatus.text.pointSize } text: qsTr('autoAnswerStatus') visible: SettingsModel.autoAnswerStatus width: parent.width } } SmartSearchBar { id: smartSearchBar Layout.fillWidth: true maxMenuHeight: MainWindowStyle.searchBox.maxHeight placeholderText: qsTr('mainSearchBarPlaceholder') tooltipText: qsTr('smartSearchBarTooltip') onAddContact: window.setView('ContactEdit', { sipAddress: sipAddress }) onEntryClicked: { if (SettingsModel.contactsEnabled) { window.setView('ContactEdit', { sipAddress: entry.sipAddress }) } else { CallsListModel.createChatRoom( '', false, [entry.sipAddress], true ) } } onLaunchCall: CallsListModel.launchAudioCall(sipAddress, '') onLaunchChat: CallsListModel.launchChat( sipAddress,0 ) onLaunchSecureChat: CallsListModel.launchChat( sipAddress,1 ) onLaunchVideoCall: CallsListModel.launchVideoCall(sipAddress, '') } ActionButton { isCustom: true backgroundRadius: 90 colorSet: MainWindowStyle.buttons.telKeyad onClicked: telKeypad.visible = !telKeypad.visible toggled: telKeypad.visible } ActionButton { Layout.leftMargin: 30 isCustom: true backgroundRadius: 4 colorSet: MainWindowStyle.buttons.newChatGroup //: 'Start a chat room' : Tooltip to illustrate a button tooltipText : qsTr('newChatRoom') visible: (SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled) enabled: SettingsModel.groupChatEnabled onClicked: { window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/NewChatRoom.qml') ,{}) } TooltipArea{ visible: !SettingsModel.groupChatEnabled maxWidth: smartSearchBar.width delay:0 //: 'Conference URI is not set. You have to change it in your account settings in order to create new group chats.' : Tooltip to warn the user to change a setting to activate an action. text: qsTr('newChatRoomUriMissing') } } ActionButton { isCustom: true backgroundRadius: 4 colorSet: MainWindowStyle.buttons.newConference visible: SettingsModel.conferenceEnabled enabled: SettingsModel.videoConferenceEnabled tooltipText:qsTr('newConferenceButton') onClicked: { window.detachVirtualWindow() window.attachVirtualWindow(Utils.buildAppDialogUri('NewConference') ,{}, function (status) { if( status){ setView('Conferences') } }) } TooltipArea{ visible: !SettingsModel.videoConferenceEnabled maxWidth: smartSearchBar.width delay:0 //: 'Video conference URI is not set. You have to change it in your account settings in order to create new meetings.' : Tooltip to warn the user to change a setting to activate an action. text: qsTr('newConferenceUriMissing') } } ActionButton { isCustom: true backgroundRadius: 4 colorSet: MainWindowStyle.buttons.burgerMenu visible: Qt.platform.os !== 'osx' toggled: menuBar.isOpenned onClicked: toggled ? menuBar.close() : menuBar.open()// a bit useless as Menu will depopup on losing focus but this code is kept for giving idea MainWindowMenuBar { id: menuBar } } } } // ----------------------------------------------------------------------- // Content. // ----------------------------------------------------------------------- RowLayout { Layout.fillHeight: true Layout.fillWidth: true spacing: 0 // Main menu. ColumnLayout { id:leftPanel Layout.maximumWidth: MainWindowStyle.menu.width Layout.preferredWidth: MainWindowStyle.menu.width spacing: 0 ApplicationMenu { id: menu defaultSelectedEntry: null entryHeight: MainWindowStyle.menu.height+10 entryWidth: MainWindowStyle.menu.width ApplicationMenuEntry { id: contactsEntry icon: MainWindowStyle.menu.contacts.icon iconSize: MainWindowStyle.menu.contacts.iconSize overwriteColor: isSelected ? MainWindowStyle.menu.contacts.selectedColor : MainWindowStyle.menu.contacts.color name: qsTr('contactsEntry') visible: SettingsModel.contactsEnabled onSelected: { ContactsListModel.update() timeline.model.unselectAll() setView('Contacts') } onClicked:{ ContactsListModel.update() setView('Contacts') } Icon{ anchors.right:parent.right anchors.verticalCenter: parent.verticalCenter anchors.rightMargin: 10 icon: MainWindowStyle.menu.direction.icon overwriteColor: contactsEntry.overwriteColor iconSize: MainWindowStyle.menu.direction.iconSize } } ApplicationMenuEntry { id: conferencesEntry icon: MainWindowStyle.menu.conferences.icon iconSize: MainWindowStyle.menu.conferences.iconSize overwriteColor: isSelected ? MainWindowStyle.menu.conferences.selectedColor : MainWindowStyle.menu.conferences.color //: 'Meetings' : Meeting title for main window. name: qsTr('mainWindowConferencesTitle').toUpperCase() visible: SettingsModel.videoConferenceEnabled && SettingsModel.conferenceEnabled onSelected: { timeline.model.unselectAll() setView('Conferences') } onClicked:{ setView('Conferences') } Icon{ anchors.right:parent.right anchors.verticalCenter: parent.verticalCenter anchors.rightMargin: 10 icon: MainWindowStyle.menu.direction.icon overwriteColor: conferencesEntry.overwriteColor iconSize: MainWindowStyle.menu.direction.iconSize } } } // History. Timeline { id: timeline Layout.fillHeight: true Layout.fillWidth: true model: TimelineProxyModel{ listSource: TimelineProxyModel.Main } onEntrySelected:{ if( entry ) { if( entry.selected){ console.debug("Load conversation from entry selected on timeline") window.setView('Conversation', { chatRoomModel:entry.chatRoomModel }) } }else{ window.setView('Home', {}) } menu.resetSelectedEntry() } onShowHistoryRequest: { timeline.model.unselectAll() window.setView('HistoryView') } } } // Main content. Item{ Layout.fillHeight: true Layout.fillWidth: true Loader { id: contentLoader objectName: '__contentLoader' anchors.fill: parent source: 'Home.qml' Component.onCompleted: if (AccountSettingsModel.accounts.length < 2) source= 'Assistant.qml' // default proxy = 1. Do not use this set diretly in source because of bindings that will override next setSource } TelKeypad { anchors.right: parent.right anchors.top: parent.top id: telKeypad onSendDtmf: smartSearchBar.text = smartSearchBar.previousText+dtmf onVisibleChanged: if(!visible) smartSearchBar.previousText = '' // this is a way to reset search text visible: SettingsModel.showTelKeypadAutomatically } } } } } Loader{ id: customMenuBar active:Qt.platform.os === 'osx' sourceComponent:MainWindowTopMenuBar{} } Component.onCompleted: if(Qt.platform.os === 'osx') menuBar = customMenuBar // --------------------------------------------------------------------------- // Url handlers. // --------------------------------------------------------------------------- Connections { target: UrlHandlers onSip: { mainSearchBar.text = sipAddress } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/MainWindowMenuBar.qml000066400000000000000000000031601434616504300266200ustar00rootroot00000000000000import QtQuick 2.7 //import QtQuick.Controls 2.3 //import Qt.labs.platform 1.0 import Linphone 1.0 import Common 1.0 import Utils 1.0 // ============================================================================= Item { id:menuParent property bool isOpenned: menu.visible function open () { menu.popup() } function close(){ menu.close() } // --------------------------------------------------------------------------- // Shortcuts. // --------------------------------------------------------------------------- Shortcut { id: settingsShortcut sequence: 'Ctrl+P' onActivated: App.smartShowWindow(App.getSettingsWindow()) } Shortcut { id: quitShortcut context: Qt.ApplicationShortcut sequence: StandardKey.Quit onActivated: Qt.quit() } Shortcut { id: aboutShortcut sequence: StandardKey.HelpContents onActivated: { window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/About.qml')) } } // --------------------------------------------------------------------------- // Menu. // --------------------------------------------------------------------------- Menu { id: menu title: qsTr('settings') MenuItem { text: qsTr('settings') onTriggered: settingsShortcut.onActivated() } MenuItem { //: 'Check for updates' : Item menu for checking updates text: qsTr('checkForUpdates') onTriggered: App.checkForUpdates(true) } MenuItem { text: qsTr('about') onTriggered: aboutShortcut.onActivated() } MenuItem { text: qsTr('quit') onTriggered: quitShortcut.onActivated() } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Main/MainWindowTopMenuBar.qml000066400000000000000000000022031434616504300273000ustar00rootroot00000000000000import QtQuick 2.7 import Qt.labs.platform 1.0 import Linphone 1.0 // ============================================================================= MenuBar { function open () { menu.open() } // --------------------------------------------------------------------------- // Menu. // --------------------------------------------------------------------------- // Menu { id: menu title: qsTr('settings') MenuItem { text: qsTr('settings') role: MenuItem.PreferencesRole onTriggered: App.smartShowWindow(App.getSettingsWindow()) shortcut: StandardKey.Preferences } MenuItem { //: 'Check for updates' : Item menu for checking updates text: qsTr('checkForUpdates') role: MenuItem.ApplicationSpecificRole onTriggered: App.checkForUpdates(true) } MenuItem { text: qsTr('about') role: MenuItem.AboutRole onTriggered: { window.detachVirtualWindow() window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/About.qml')) } shortcut: StandardKey.HelpContents } MenuItem { text: qsTr('quit') role: MenuItem.QuitRole onTriggered: Qt.quit() shortcut: StandardKey.Quit } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/000077500000000000000000000000001434616504300234735ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/Dialogs/000077500000000000000000000000001434616504300250555ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/Dialogs/SettingsLdapEdit.qml000066400000000000000000000245041434616504300310040ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 // ============================================================================= DialogPlus { id: dialog property LdapModel ldapData buttons: [ TextButtonA { text: qsTr('cancel') onClicked: { ldapData.unset() exit(0)} }, TextButtonB { enabled: ldapData.isValid text: qsTr('confirm') onClicked: {ldapData.save() exit(1) } } ] buttonsAlignment: Qt.AlignCenter height: SettingsSipAccountsEditStyle.height width: SettingsSipAccountsEditStyle.width // --------------------------------------------------------------------------- TabContainer { anchors.fill: parent Column { width: parent.width Form { title: '' width: parent.width FormLine { FormGroup { label: qsTr('displayNameLabel')//'Display Name' TextField { id:displayName placeholderText : (serverUrl.text?serverUrl.text:serverUrl.placeholderText) text:ldapData.displayName onTextChanged: Qt.callLater(function(){ldapData.displayName = text}) Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() TooltipArea{ text : qsTr('displayNameTooltip')//'The display name of the server to be shown in the list' //tooltipParent: dialog } } } } } Form { title: qsTr('connectionTitle')//'Connection' width: parent.width FormLine { FormGroup { label: qsTr('serverLabel')+' *'//'Server URL *' TextField { id:serverUrl placeholderText :"Server" text:ldapData.server onTextChanged: Qt.callLater(function(){ldapData.server = text}) Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() error : ldapData.serverFieldError TooltipArea{ text : qsTr('serverTooltip')//'LDAP Server. eg: ldap:/// for a localhost server or ldap://ldap.example.org/' //tooltipParent: dialog } } } } FormLine { FormGroup { label: qsTr('bindDNLabel')+' *'//'Bind DN *' TextField { id: bindDn placeholderText :"Bind DN" text:ldapData.bindDn error : ldapData.bindDnFieldError onTextChanged: Qt.callLater(function(){ldapData.bindDn= text}) TooltipArea{ text : qsTr('bindDNTooltip')//'The bind DN is the credential that is used to authenticate against an LDAP.\n eg: cn=ausername,ou=people,dc=bc,dc=com' //tooltipParent: dialog } } } } FormLine { FormGroup { id:passwordGroup label: qsTr('passwordLabel')//'Password' PasswordField { id:password text:ldapData.password error : ldapData.passwordFieldError onTextChanged: Qt.callLater(function(){ldapData.password = text}) placeholderText :"Password" } } } FormLine { id:useRow width:passwordGroup.width FormGroup { label: qsTr('useTLSLabel')//'Use TLS' Switch { id: useTls anchors.verticalCenter: parent.verticalCenter checked: ldapData.useTls onClicked: { ldapData.useTls = !checked } TooltipArea{ tooltipParent:useRow text : qsTr('useTLSTooltip')//'Encrypt transactions by LDAP over TLS(StartTLS). You must use \'ldap\' scheme. \'ldaps\' for LDAP over SSL is non-standardized and deprecated.\nStartTLS in an extension to the LDAP protocol which uses the TLS protocol to encrypt communication. \nIt works by establishing a normal - i.e. unsecured - connection with the LDAP server before a handshake negotiation between the server and the web services is carried out. Here, the server sends its certificate to prove its identity before the secure connection is established.' } } } FormGroup { label: qsTr('useSalLabel')//'Use Sal' Switch { id: useSal anchors.verticalCenter: parent.verticalCenter checked: ldapData.useSal onClicked: { ldapData.useSal = !checked } TooltipArea{ tooltipParent:useRow //: 'The dns resolution is done by %1 using Sal. It will pass an IP to LDAP. By doing that, the TLS negociation could not check the hostname. You may deactivate the verifications if wanted to force the connection.' text : qsTr('useSalTooltip').arg(applicationName) } } } } FormLine{ id:useSalRow FormGroup { label: qsTr('verifyTLSLabel')//'Verify Certificates on TLS' ComboBox { id:verifyServerCertificates currentIndex: ldapData.verifyServerCertificates+1 model: [qsTr('AutoMode'), qsTr('offMode'), qsTr('onMode')] width: parent.width onActivated: ldapData.verifyServerCertificates = index-1 TooltipArea{ text : qsTr('verifyTLSTooltip')//'Specify whether the tls server certificate must be verified when connecting to a LDAP server.' //tooltipParent: dialog } } } } } // ----------------------------------------------------------------------- // NAT and Firewall. // ----------------------------------------------------------------------- Form { title: qsTr('searchTitle')//'Search' width: parent.width FormLine { FormGroup { label: qsTr('baseObjectLabel')+' *'//'Base Object *' TextField { id:baseObject placeholderText :qsTr('baseObjectPlaceholder')//"Base Object" text:ldapData.baseObject error : ldapData.baseObjectFieldError onTextChanged: Qt.callLater(function(){ldapData.baseObject = text}) TooltipArea{ text : qsTr('baseObjectTooltip')//'BaseObject is a specification for LDAP Search Scopes that specifies that the Search Request should only be performed against the entry specified as the search base DN.\n\nNo entries below it will be considered.' //tooltipParent: dialog } } } } FormLine { FormGroup { id:filterGroup label: qsTr('filterLabel')//'Filter' TextField { id:filter text:ldapData.filter error : ldapData.filterFieldError onTextChanged: Qt.callLater(function(){ldapData.filter = text}) placeholderText :"(sn=%s)" TooltipArea{ text : qsTr('filterTooltip')//'The search is base on this filter to search friends. Default value : (sn=%s)' //tooltipParent: dialog } } } } FormLine { id:connectionRow width:filterGroup.width FormGroup { label: qsTr('maxResultsLabel')//'Max Results' NumericField { id:maxResults text:ldapData.maxResults error : ldapData.maxResultsFieldError onTextChanged: Qt.callLater(function(){ldapData.maxResults = text}) TooltipArea{ tooltipParent:connectionRow text : qsTr('maxResultsTooltip')//'The max results when requesting searches' } } } FormGroup { label: qsTr('timeoutLabel') NumericField { id:timeout text:ldapData.timeout error : ldapData.timeoutFieldError onTextChanged: Qt.callLater(function(){ldapData.timeout = text}) TooltipArea{ tooltipParent:connectionRow text : qsTr('timeoutTooltip')//'The connection and search timeout in seconds. Default is 5' } } } } } // ----------------------------------------------------------------------- // Parsing // ----------------------------------------------------------------------- Form { id:parsingForm title: qsTr('parsingTitle')//'Parsing' width: parent.width FormLine { FormGroup { label: qsTr('nameAttributesLabel')//'Name Attributes' TextField { id:nameAttributes placeholderText :'sn' text:ldapData.nameAttributes error : ldapData.nameAttributesFieldError onTextChanged: Qt.callLater(function(){ldapData.nameAttributes = text}) TooltipArea{ text : qsTr('nameAttributesTooltip')//'Check these attributes To build Name Friend, separated by a comma and the first is the highest priority. The default value is: sn' tooltipParent: nameAttributes } } } } FormLine { FormGroup { label: qsTr('sipAttributesLabel')//'Sip Attributes' TextField { id:sipAttributes placeholderText :'mobile,telephoneNumber,homePhone,sn' text:ldapData.sipAttributes error : ldapData.sipAttributesFieldError onTextChanged: Qt.callLater(function(){ldapData.sipAttributes = text}) TooltipArea{ text : qsTr('sipAttributesTooltip')//'Check these attributes to build the SIP username in address of Friend. Attributes are separated by a comma and the first is the highest priority. The default value is: mobile,telephoneNumber,homePhone,sn' tooltipParent: sipAttributes } } } } FormLine { FormGroup { label: qsTr('domainLabel')//'Domain' TextField { id:domain placeholderText : AccountSettingsModel.defaultAccountDomain text:ldapData.sipDomain error : ldapData.sipDomainFieldError onTextChanged: Qt.callLater(function(){ldapData.sipDomain = text}) TooltipArea{ //: 'Add the domain to the sip address(username@domain).' Tooltip to explain that this field is used to complete a result with this domain. text : qsTr('domainTooltip')// //tooltipParent: dialog } } } } } // ----------------------------------------------------------------------- // Misc // ----------------------------------------------------------------------- Form { title: qsTr('miscLabel')//'Misc' width: parent.width FormLine { id:miscLine FormGroup { label: qsTr('debugLabel')//'Debug' Switch { id: debugMode anchors.verticalCenter: parent.verticalCenter checked: ldapData.debug onClicked: { ldapData.debug = !checked } TooltipArea{ tooltipParent:miscLine //:'Get verbose logs in log file when doing transactions (useful to debug TLS connections)' text : qsTr('debugTooltip') } } } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.js000066400000000000000000000124051434616504300321770ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `SettingsSipAccounts.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= var gAccount function initForm (account) { var AccountSettingsModel = Linphone.AccountSettingsModel gAccount = account ? account.account : AccountSettingsModel.createAccount('create-app-sip-account.rc') var config = AccountSettingsModel.getAccountDescription(gAccount) sipAddress.text = config.sipAddress serverAddress.text = config.serverAddress registrationDuration.text = config.registrationDuration var currentTransport = config.transport.toUpperCase() transport.currentIndex = Number( Utils.findIndex(transport.model, function (value) { return value === currentTransport }) ) route.text = config.route conferenceUri.text = config.conferenceUri videoConferenceUri.text = config.videoConferenceUri limeServerUrl.text = config.limeServerUrl contactParams.text = config.contactParams avpfInterval.text = config.avpfInterval registerEnabled.checked = config.registerEnabled publishPresence.checked = config.publishPresence avpfEnabled.checked = config.avpfEnabled iceEnabled.checked = config.iceEnabled turnEnabled.checked = config.turnEnabled stunServer.text = config.stunServer turnPassword.text = config.turnPassword turnUser.text = config.turnUser if (account) { dialog._sipAddressOk = true dialog._serverAddressOk = true } dialog._routeOk = true } function formIsValid () { return dialog._sipAddressOk && dialog._serverAddressOk && dialog._routeOk && dialog._conferenceUriOk && dialog._videoConferenceUriOk && dialog._limeServerUrlOk } // ----------------------------------------------------------------------------- function validAccount (account) { var data = { sipAddress: sipAddress.text, serverAddress: serverAddress.text, registrationDuration: registrationDuration.text, transport: transport.currentText, route: route.text, conferenceUri: conferenceUri.text, videoConferenceUri: videoConferenceUri.text, limeServerUrl: limeServerUrl.text, contactParams: contactParams.text, avpfInterval: avpfInterval.text, registerEnabled: registerEnabled.checked, publishPresence: publishPresence.checked, avpfEnabled: avpfEnabled.checked, iceEnabled: iceEnabled.checked, turnEnabled: turnEnabled.checked, stunServer: stunServer.text, turnUser: turnUser.text, turnPassword: turnPassword.text } if (gAccount && Linphone.AccountSettingsModel.addOrUpdateAccount(gAccount, data) || !account && Linphone.AccountSettingsModel.addOrUpdateAccount(data)) { dialog.exit(1) } else { // TODO: Display errors on the form (if necessary). } } // ----------------------------------------------------------------------------- function handleRouteChanged (route) { dialog._routeOk = route.length === 0 || Linphone.SipAddressesModel.addressIsValid(route) } function handleConferenceUriChanged (uri) { dialog._conferenceUriOk = uri=='' || Linphone.SipAddressesModel.addressIsValid(uri) } function handleVideoConferenceUriChanged (uri) { dialog._videoConferenceUriOk = uri=='' || Linphone.SipAddressesModel.addressIsValid(uri) } function handleLimeServerUrlChanged (uri) { dialog._limeServerUrlOk = uri=='' || Linphone.SipAddressesModel.addressIsValid(uri) } function handleServerAddressChanged (address) { if (address.length === 0) { dialog._serverAddressOk = false return } var newTransport = Linphone.SipAddressesModel.getTransportFromSipAddress(address) if (newTransport.length > 0) { transport.currentIndex = Utils.findIndex(transport.model, function (value) { return value === newTransport }) dialog._serverAddressOk = true } else { dialog._serverAddressOk = false } } function handleSipAddressChanged (address) { dialog._sipAddressOk = address.length > 0 && Linphone.SipAddressesModel.sipAddressIsValid(address) } function handleTransportChanged (transport) { var newServerAddress = Linphone.SipAddressesModel.addTransportToSipAddress(serverAddress.text, transport) if (newServerAddress.length > 0) { serverAddress.text = newServerAddress dialog._serverAddressOk = true } else { dialog._serverAddressOk = false } } // ----------------------------------------------------------------------------- linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml000066400000000000000000000173621434616504300323630ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import App.Styles 1.0 import 'SettingsSipAccountsEdit.js' as Logic // ============================================================================= DialogPlus { id: dialog property var account // Optional. property bool _sipAddressOk: false property bool _serverAddressOk: false property bool _routeOk: true property bool _conferenceUriOk: true property bool _videoConferenceUriOk: true property bool _limeServerUrlOk: true flat: true showMargins: true buttons: [ TextButtonA { text: qsTr('cancel') onClicked: exit(0) }, TextButtonB { enabled: Logic.formIsValid() text: qsTr('confirm') onClicked: Logic.validAccount(dialog.account ? dialog.account.account : null) } ] buttonsAlignment: Qt.AlignCenter height: SettingsSipAccountsEditStyle.height width: SettingsSipAccountsEditStyle.width // --------------------------------------------------------------------------- Component.onCompleted: Logic.initForm(account) // --------------------------------------------------------------------------- TabContainer { anchors.fill: parent Column { width: parent.width Form { title: qsTr('mainSipAccountSettingsTitle') width: parent.width FormLine { FormGroup { label: qsTr('sipAddressLabel') + '*' TextField { id: sipAddress placeholderText: 'sip:name@sip.example.net' error: dialog._sipAddressOk ? '' : qsTr('invalidSipAddress') onTextChanged: Logic.handleSipAddressChanged(text) Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() } } } FormLine { FormGroup { label: qsTr('serverAddressLabel') + '*' TextField { id: serverAddress placeholderText: 'sip:sip.example.net' error: dialog._serverAddressOk ? '' : qsTr('invalidServerAddress') onActiveFocusChanged: if(!activeFocus && dialog._serverAddressOk) Logic.handleTransportChanged(transport.model[transport.currentIndex]) onTextChanged: Logic.handleServerAddressChanged(text) Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() } } } FormLine { FormGroup { label: qsTr('registrationDurationLabel') NumericField { id: registrationDuration Keys.onEnterPressed: route.forceActiveFocus() Keys.onReturnPressed: route.forceActiveFocus() } } } FormLine { FormGroup { label: qsTr('transportLabel') ComboBox { id: transport enabled: dialog._serverAddressOk model: [ 'UDP', 'TCP', 'TLS' ] onActivated: Logic.handleTransportChanged(model[index]) } } } FormLine { FormGroup { label: qsTr('routeLabel') TextField { id: route error: dialog._routeOk ? '' : qsTr('invalidRoute') onTextChanged: Logic.handleRouteChanged(text) Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() } } } FormLine { FormGroup { //: "Conference URI" : Label of a text edit for filling Conference URI label: qsTr('conferenceURI') TextField { id: conferenceUri //: "invalid conference URI" : Error text about conference URI error: dialog._conferenceUriOk ? '' : qsTr("invalidConferenceURI") onTextChanged: Logic.handleConferenceUriChanged(text) Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() } } } FormLine { FormGroup { //: "Video Conference URI" : Label of a text edit for filling Video conference URI. label: qsTr('videoConferenceURI') TextField { id: videoConferenceUri //: "invalid conference URI" : Error text about conference URI error: dialog._videoConferenceUriOk ? '' : qsTr("invalidConferenceURI") onTextChanged: Logic.handleVideoConferenceUriChanged(text) Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() } } } FormLine { FormGroup { //: 'E2E encryption keys server URL' : Label of a text edit for filling the Lime server URL. label: qsTr('limeServerUrl') TextField { id: limeServerUrl //: "invalid E2E encryption keys server URL" : Error text about E2E encryption keys server URL. error: dialog._limeServerUrlOk ? '' : qsTr("invalidLimeServerUrl") onTextChanged: Logic.handleLimeServerUrlChanged(text) Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() } } } FormLine { FormGroup { label: qsTr('contactParamsLabel') TextField { id: contactParams Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() } } } FormLine { FormGroup { label: qsTr('avpfIntervalLabel') NumericField { id: avpfInterval maxValue: 5 minValue: 1 Keys.onEnterPressed: focus=false Keys.onReturnPressed: focus=false } } } FormLine { FormGroup { label: qsTr('registerEnabledLabel') Switch { id: registerEnabled onClicked: checked = !checked } } } FormLine { FormGroup { label: qsTr('publishPresenceLabel') Switch { id: publishPresence onClicked: checked = !checked } } } FormLine { FormGroup { label: qsTr('avpfEnabledLabel') Switch { id: avpfEnabled onClicked: checked = !checked } } } } // ----------------------------------------------------------------------- // NAT and Firewall. // ----------------------------------------------------------------------- Form { title: qsTr('natAndFirewallTitle') width: parent.width FormLine { FormGroup { label: qsTr('enableIceLabel') Switch { id: iceEnabled onClicked: checked = !checked } } FormGroup { label: qsTr('stunServerLabel') TextField { id: stunServer placeholderText: 'stun.example.net' readOnly: !iceEnabled.checked Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() } } } FormLine { FormGroup { label: qsTr('enableTurnLabel') Switch { id: turnEnabled enabled: iceEnabled.checked onClicked: checked = !checked } } FormGroup { label: qsTr('turnUserLabel') TextField { id: turnUser readOnly: !turnEnabled.checked || !turnEnabled.enabled Keys.onEnterPressed: nextItemInFocusChain().forceActiveFocus() Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() } } } FormLine { FormGroup {} FormGroup { label: qsTr('turnPasswordLabel') TextField { id: turnPassword readOnly: !turnEnabled.checked || !turnEnabled.enabled || !turnUser.text.length } } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/Dialogs/SettingsVideoPreview.qml000066400000000000000000000013051434616504300317200ustar00rootroot00000000000000import QtQuick 2.7 import QtGraphicalEffects 1.12 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 // ============================================================================= DialogPlus { id: dialog buttons: [ TextButtonB { text: qsTr('confirm') onClicked: exit(1) } ] buttonsAlignment: Qt.AlignCenter flat: true showMargins: true height: SettingsVideoPreviewStyle.height width: SettingsVideoPreviewStyle.width // --------------------------------------------------------------------------- Item{ anchors.fill: parent CameraView{ id: previewLoader anchors.centerIn: parent height: parent.height width: height showCloseButton: false } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsAdvanced.js000066400000000000000000000040571434616504300272650ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `SettingsAdvanced.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function editLdap (ldap) { window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/SettingsLdapEdit.qml'), { ldapData: ldap }) } function cleanLogs () { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('cleanLogsDescription'), }, function (status) { if (status) { Linphone.CoreManager.cleanLogs() } }) } function handleLogsUploaded (url) { if (url.length && Utils.startsWith(url, 'http')) { if(Qt.openUrlExternally( 'mailto:' + encodeURIComponent(Linphone.SettingsModel.logsEmail) + '?subject=' + encodeURIComponent( ('Desktop %1 Log').arg(applicationName)) + '&body=' + encodeURIComponent(url) )) sendLogsBlock.stop(qsTr('logsMailerSuccess').replace('%1', url)) else sendLogsBlock.stop(qsTr('logsMailerFailed').replace('%1', url)) } else { sendLogsBlock.stop(qsTr('logsUploadFailed')) } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsAdvanced.qml000066400000000000000000000234031434616504300274360ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.5 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 import Linphone.Styles 1.0 import Common.Styles 1.0 import ContactsImporterPluginsManager 1.0 import 'SettingsAdvanced.js' as Logic // ============================================================================= TabContainer { id: mainItem color: "#00000000" signal showLogs() Column { id: column spacing: SettingsWindowStyle.forms.spacing width: parent.width // ------------------------------------------------------------------------- // Logs. // ------------------------------------------------------------------------- Form { title: qsTr('logsTitle') width: parent.width FormLine { FormGroup { label: qsTr('logsFolderLabel') FileChooserButton { selectedFile: SettingsModel.logsFolder selectFolder: true onAccepted: SettingsModel.logsFolder = selectedFile } } } FormLine { FormGroup { label: qsTr('logsUploadUrlLabel') TextField { readOnly: true text: SettingsModel.logsUploadUrl onEditingFinished: SettingsModel.logsUploadUrl = text } } } FormLine { FormGroup { label: qsTr('logsEnabledLabel') Switch { checked: SettingsModel.logsEnabled onClicked: SettingsModel.logsEnabled = !checked } } } } Row { anchors.right: parent.right spacing: SettingsAdvancedStyle.buttons.spacing TextButtonB { text: qsTr('viewlogs') onClicked: { mainItem.showLogs() } } TextButtonB { text: qsTr('cleanLogs') onClicked: { Logic.cleanLogs() sendLogsBlock.setText('') } } TextButtonB { enabled: !sendLogsBlock.loading && SettingsModel.logsEnabled text: qsTr('sendLogs') onClicked: sendLogsBlock.execute() } } RequestBlock { id: sendLogsBlock action: CoreManager.sendLogs width: parent.width Connections { target: CoreManager onLogsUploaded: Logic.handleLogsUploaded(url) } } onVisibleChanged: sendLogsBlock.setText('') // ------------------------------------------------------------------------- // LDAP // ------------------------------------------------------------------------- Form { title: 'LDAP' width: parent.width addButton:true onAddButtonClicked:ldapSection.add() visible: SettingsModel.isLdapAvailable() || SettingsModel.developerSettingsEnabled SettingsLdap{ id:ldapSection width: parent.width } } // ------------------------------------------------------------------------- // ADDRESS BOOK // ------------------------------------------------------------------------- Form { title: qsTr('contactsTitle') width: parent.width FormTable { id:contactsImporterTable width :parent.width legendLineWidth:0 disableLineTitle:importerRepeater.count!=1 titles: (importerRepeater.count==1? getTitles(): []) function getTitles(repeaterModel){ var fields = importerRepeater.itemAt(0).pluginDescription['fields']; var t = ['']; for(var i = 0 ; i < fields.length ; ++i){ t.push(fields[i]['placeholder']); } return t; } Repeater{ id:importerRepeater model:ContactsImporterListProxyModel{id:contactsImporterList} delegate : FormTable{ //readonly property double maxItemWidth: contactsImporterTable.maxItemWidth property var pluginDescription : importerLine.pluginDescription width :parent.width legendLineWidth:80 disableLineTitle:true FormTableLine { id:importerLine property var fields : $modelData.fields property int identity : $modelData.identity property var pluginDescription : ContactsImporterPluginsManager.getContactsImporterPluginDescription(fields["pluginID"]) // Plugin definition FormTableEntry { Row{ width :parent.width ActionButton { id:removeImporter isCustom: true backgroundRadius: 90 colorSet: SettingsAdvancedStyle.cancel onClicked:ContactsImporterListModel.removeContactsImporter($modelData) } Text{ height:parent.height width:parent.width-removeImporter.width text:importerLine.pluginDescription['pluginTitle'] horizontalAlignment:Text.AlignHCenter verticalAlignment: Text.AlignVCenter elide: Text.ElideRight wrapMode: Text.WordWrap font { bold: true pointSize: FormTableStyle.entry.text.pointSize } TooltipArea{ text:importerLine.pluginDescription['pluginDescription'] } } } } Repeater{ model:importerLine.pluginDescription['fields'] delegate: FormTableEntry { Loader{ sourceComponent: ($modelData['type']==0 ? textComponent:textFieldComponent) active:true width:parent.width Component{ id: textComponent Text { color: FormTableStyle.entry.text.color elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter text: importerLine.fields[$modelData['fieldId']]?importerLine.fields[$modelData['fieldId']]:'' height: FormTableStyle.entry.height width: parent.width font { bold: true pointSize: FormTableStyle.entry.text.pointSize } } } Component{ id: textFieldComponent TextField { readOnly: false width:parent.width placeholderText : $modelData['placeholder'] text: importerLine.fields[$modelData['fieldId']]?importerLine.fields[$modelData['fieldId']]:'' echoMode: ($modelData['hiddenText']?TextInput.Password:TextInput.Normal) onEditingFinished:{ importerLine.fields[$modelData['fieldId']] = text } Component.onCompleted: importerLine.fields[$modelData['fieldId']] = text } } } } }// Repeater : Fields FormTableEntry { Switch { checked: $modelData.fields["enabled"]>0 onClicked: { checked = !checked importerLine.fields["enabled"] = (checked?1:0) ContactsImporterListModel.addContactsImporter(importerLine.fields, importerLine.identity) if(checked){ ContactsImporterListModel.importContacts(importerLine.identity) }else contactsImporterStatus.text = '' } } }//FormTableEntry }//FormTableLine FormTableLine{ width:parent.width-parent.legendLineWidth FormTableEntry { id:contactsImporterStatusEntry visible:contactsImporterStatus.text!=='' width:parent.width TextEdit{ id:contactsImporterStatus property bool isError:false selectByMouse: true readOnly:true color: (isError?SettingsAdvancedStyle.error.color:SettingsAdvancedStyle.info.color) width:parent.width horizontalAlignment:Text.AlignRight font { italic: true pointSize: SettingsAdvancedStyle.info.pointSize } Connections{ target:$modelData onStatusMessage:{contactsImporterStatus.isError=false;contactsImporterStatus.text=message;} onErrorMessage:{contactsImporterStatus.isError=true;contactsImporterStatus.text=message;} } } } } }//Column }// Repeater : Importer } Row{ spacing:SettingsAdvancedStyle.buttons.spacing anchors.horizontalCenter: parent.horizontalCenter ActionButton { isCustom: true backgroundRadius: 90 colorSet: SettingsAdvancedStyle.options onClicked:{ ContactsImporterPluginsManager.openNewPlugin(); pluginChoice.model = ContactsImporterPluginsManager.getPlugins(); } } ComboBox{ id: pluginChoice model:ContactsImporterPluginsManager.getPlugins() textRole: "pluginTitle" //: 'No Plugins to load' : Text in combobox displayText: currentIndex === -1 ? qsTr('noPlugin') : currentText Text{// Hack, combobox show empty text when empty anchors.fill:parent visible:pluginChoice.currentIndex===-1 verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignHCenter text: qsTr('noPlugin') font { bold:false italic: true pointSize: FormTableStyle.entry.text.pointSize } } Connections{ target:SettingsModel onContactImporterChanged:pluginChoice.model=ContactsImporterPluginsManager.getPlugins() } } ActionButton { isCustom: true backgroundRadius: 90 colorSet: SettingsAdvancedStyle.add visible:pluginChoice.currentIndex>=0 onClicked:{ if( pluginChoice.currentIndex >= 0) ContactsImporterListModel.createContactsImporter({"pluginID":pluginChoice.model[pluginChoice.currentIndex]["pluginID"]}) } } } } // ------------------------------------------------------------------------- // Developer settings. // ------------------------------------------------------------------------- Form { title: qsTr('developerSettingsTitle') visible: SettingsModel.isDeveloperSettingsAvailable() width: parent.width FormLine { FormGroup { label: qsTr('developerSettingsEnabledLabel') Switch { checked: SettingsModel.developerSettingsEnabled onClicked: SettingsModel.developerSettingsEnabled = !checked } } } } } } /*##^## Designer { D{i:0;autoSize:true;height:480;width:640} } ##^##*/ linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsAudio.qml000066400000000000000000000175501434616504300270000ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.7 import QtQuick.Layouts 1.10 import Common 1.0 import Linphone 1.0 import Utils 1.0 import ColorsList 1.0 import App.Styles 1.0 // ============================================================================= TabContainer { Column { spacing: SettingsWindowStyle.forms.spacing width: parent.width // ------------------------------------------------------------------------- // Audio parameters. // ------------------------------------------------------------------------- Form { title: qsTr('audioTitle') width: parent.width //Warning if in call FormLine { visible: SettingsModel.isInCall FormGroup { RowLayout { spacing: SettingsAudioStyle.warningMessage.iconSize Icon { icon: 'warning' iconSize: SettingsAudioStyle.warningMessage.iconSize anchors { rightMargin: SettingsAudioStyle.warningMessage.iconSize leftMargin: SettingsAudioStyle.warningMessage.iconSize } } Text { text: qsTr('audioSettingsInCallWarning') } } } } FormLine { FormGroup { label: qsTr('playbackDeviceLabel') ComboBox { currentIndex: Utils.findIndex(model, function (device) { return device === SettingsModel.playbackDevice }) model: SettingsModel.playbackDevices onActivated: SettingsModel.playbackDevice = model[index] } } } FormLine { FormGroup { label: qsTr('playbackGainLabel') enabled: !SettingsModel.isInCall Slider { id: playbackSlider width: parent.width enabled: !SettingsModel.isInCall value: SettingsModel.playbackGain onPositionChanged: SettingsModel.playbackGain = position ToolTip { parent: playbackSlider.handle visible: playbackSlider.pressed text: (playbackSlider.value * 100).toFixed(0) + " %" } } } } FormLine { FormGroup { label: qsTr('captureDeviceLabel') ComboBox { currentIndex: Utils.findIndex(model, function (device) { return device === SettingsModel.captureDevice }) model: SettingsModel.captureDevices onActivated: SettingsModel.captureDevice = model[index] } } } FormLine { FormGroup { label: qsTr('captureGainLabel') Slider { id: captureSlider width: parent.width enabled: !SettingsModel.isInCall value: SettingsModel.captureGain onPositionChanged: SettingsModel.captureGain = position ToolTip { parent: captureSlider.handle visible: captureSlider.pressed text: (captureSlider.value * 100).toFixed(0) + " %" } } } } FormLine { FormGroup { id: audioTestRow label: qsTr('audioTestLabel') visible: !SettingsModel.isInCall Slider { id: audioTestSlider enabled: false width: parent.width anchors { leftMargin: SettingsAudioStyle.ringPlayer.leftMargin } background: Rectangle { x: audioTestSlider.leftPadding y: audioTestSlider.topPadding + audioTestSlider.availableHeight / 2 - height / 2 implicitWidth: 200 implicitHeight: 8 width: audioTestSlider.availableWidth height: implicitHeight radius: 2 color: SettingsAudioStyle.sliderBackgroundColor Rectangle { width: audioTestSlider.visualPosition * parent.width height: parent.height color: audioTestSlider.value > 0.8 ? SettingsAudioStyle.sliderHighColor : SettingsAudioStyle.sliderLowColor radius: 2 } } //Empty slider handle handle: Text { text: '' visible: false } Timer { interval: 50 repeat: true running: SettingsModel.captureGraphRunning onTriggered: parent.value = SettingsModel.getMicVolume() } } } } FormLine { FormGroup { label: qsTr('ringerDeviceLabel') ComboBox { enabled: !SettingsModel.isInCall currentIndex: Utils.findIndex(model, function (device) { return device === SettingsModel.ringerDevice }) model: SettingsModel.playbackDevices onActivated: SettingsModel.ringerDevice = model[index] } } } FormLine { FormGroup { label: qsTr('ringLabel') FileChooserButton { selectedFile: SettingsModel.ringPath onAccepted: { var item = ringPlayer.item if (item) { item.stop() } SettingsModel.ringPath = selectedFile } ActionButton{ anchors { left: parent.right leftMargin: SettingsAudioStyle.ringPlayer.leftMargin } property bool playing : ringPlayer.item && ringPlayer.item.playbackState === SoundPlayer.PlayingState isCustom: true backgroundRadius: 90 colorSet: playing ? SettingsAudioStyle.pause : SettingsAudioStyle.play onClicked: { var item = ringPlayer.item if (!item) { return } if (playing) { item.stop() } else { item.play() } } Loader { id: ringPlayer active: window.visible sourceComponent: SoundPlayer { source: SettingsModel.ringPath isRinger: true } } } } } } FormLine { FormGroup { label: qsTr('echoCancellationLabel') Row{ spacing: SettingsAudioStyle.warningMessage.iconSize Switch { checked: SettingsModel.echoCancellationEnabled onClicked: { SettingsModel.echoCancellationEnabled = !checked echoCalibrationStatus.text = '' } } Text{ id:echoCalibrationStatus text: '' Layout.fillWidth:true height:parent.height verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter } TextButtonB { id: echoCalibration enabled: SettingsModel.echoCancellationEnabled text: qsTr('echoCancellationCalibrationLabel') onClicked: { echoCalibrationStatus.text = qsTr("calibratingEchoCancellationInProgress");//"...calibrating echo cancellation..." SettingsModel.startEchoCancellerCalibration(); } Connections { target: SettingsModel onEchoCancellationStatus:{ switch(status){ case 0 : echoCalibrationStatus.text = qsTr("calibratingEchoCancellationInProgress"); break; case 1 : echoCalibrationStatus.text = qsTr("calibratingEchoCancellationDone").replace('%1', msDelay); break;//"Calibrated in -"+msDelay+"ms" case 2 : echoCalibrationStatus.text = qsTr("calibratingEchoCancellationFailed"); break;//"Calibration failed" case 3 : echoCalibrationStatus.text = qsTr("calibratingEchoCancellationNone");//"No echo detected" SettingsModel.echoCancellationEnabled = false;// Calibration turn off the echo cancellation break; default:{} } } } } } } } } // ------------------------------------------------------------------------- // Audio Codecs. // ------------------------------------------------------------------------- Form { title: qsTr('audioCodecsTitle') visible: SettingsModel.showAudioCodecs || SettingsModel.developerSettingsEnabled width: parent.width FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('showAudioCodecsLabel') Switch { checked: SettingsModel.showAudioCodecs onClicked: SettingsModel.showAudioCodecs = !checked } } } CodecsViewer { model: AudioCodecsModel width: parent.width } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsCallsChat.qml000066400000000000000000000257461434616504300276030ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Utils 1.0 import App.Styles 1.0 import Common.Styles 1.0 // ============================================================================= TabContainer { Column { spacing: SettingsWindowStyle.forms.spacing width: parent.width Form { title: qsTr('callsTitle') width: parent.width FormLine { visible: !!encryption.model.length FormGroup { label: qsTr('encryptionLabel') ComboBox { id: encryption textRole: 'key' model: (function () { var encryptions = SettingsModel.supportedMediaEncryptions if (encryptions.length) { encryptions.unshift({value:SettingsModel.MediaEncryptionNone, key:qsTr('noEncryption')}) } return encryptions })() property var currentEncryption: SettingsModel.mediaEncryption onCurrentEncryptionChanged: { currentIndex = Number( Utils.findIndex(encryption.model, function (value) { return currentEncryption === value.value }) ) } onActivated: SettingsModel.mediaEncryption = model[index].value } } FormGroup { label: qsTr('encryptionMandatoryLabel') Switch { id: encryptionMandatory checked: SettingsModel.mediaEncryptionMandatory onClicked: SettingsModel.mediaEncryptionMandatory = !checked } } } FormLine { FormGroup { label: qsTr('autoAnswerLabel') Switch { id: autoAnswer checked: SettingsModel.autoAnswerStatus onClicked: SettingsModel.autoAnswerStatus = !checked } } FormGroup { label: qsTr('autoAnswerDelayLabel') NumericField { readOnly: !autoAnswer.checked minValue: 0 maxValue: 30000 step: 1000 text: SettingsModel.autoAnswerDelay onEditingFinished: SettingsModel.autoAnswerDelay = text } } } FormLine { FormGroup { label: qsTr('autoAnswerWithVideoLabel') visible: SettingsModel.videoSupported Switch { checked: SettingsModel.autoAnswerVideoStatus enabled: autoAnswer.checked onClicked: SettingsModel.autoAnswerVideoStatus = !checked } } } FormLine { FormGroup { label: qsTr('showTelKeypadAutomaticallyLabel') Switch { checked: SettingsModel.showTelKeypadAutomatically onClicked: SettingsModel.showTelKeypadAutomatically = !checked } } FormGroup { label: qsTr('keepCallsWindowInBackgroundLabel') Switch { checked: SettingsModel.keepCallsWindowInBackground onClicked: SettingsModel.keepCallsWindowInBackground = !checked } } } FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('outgoingCallsEnabledLabel') Switch { checked: SettingsModel.outgoingCallsEnabled onClicked: SettingsModel.outgoingCallsEnabled = !checked } } } FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('callRecorderEnabledLabel') Switch { checked: SettingsModel.callRecorderEnabled onClicked: SettingsModel.callRecorderEnabled = !checked } } FormGroup { label: qsTr('callPauseEnabledLabel') Switch { checked: SettingsModel.callPauseEnabled onClicked: SettingsModel.callPauseEnabled = !checked } } } FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('muteMicrophoneEnabledLabel') Switch { checked: SettingsModel.muteMicrophoneEnabled onClicked: SettingsModel.muteMicrophoneEnabled = !checked } } } FormLine { FormGroup { //: 'Call when registered' : Label on switch to choose if calls are make when the current proxy is registered label: qsTr('waitRegistrationForCallLabel') Switch { id: waitRegistrationForCall checked: SettingsModel.waitRegistrationForCall onClicked: SettingsModel.waitRegistrationForCall = !checked } } FormGroup { visible: SettingsModel.callRecorderEnabled || SettingsModel.developerSettingsEnabled label: qsTr('automaticallyRecordCallsLabel') Switch { checked: SettingsModel.automaticallyRecordCalls onClicked: SettingsModel.automaticallyRecordCalls = !checked } } } FormLine { FormGroup { //: 'Enable screenshots' : Label on switch to choose if we can take screenshots while being in call. label: qsTr('callScreenshotEnabledLabel') Switch { checked: SettingsModel.incallScreenshotEnabled onClicked: SettingsModel.incallScreenshotEnabled = !checked } } } } Form { title: qsTr('chatTitle') visible: SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled || SettingsModel.developerSettingsEnabled width: parent.width FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('chatEnabledLabel') Switch { checked: SettingsModel.standardChatEnabled onClicked: SettingsModel.standardChatEnabled = !checked } } FormGroup { label: 'Activate secure chats' Switch { checked: SettingsModel.secureChatEnabled onClicked: SettingsModel.secureChatEnabled = !checked } } FormGroup { label: qsTr('conferenceEnabledLabel') Switch { checked: SettingsModel.conferenceEnabled onClicked: SettingsModel.conferenceEnabled = !checked } } } FormLine { FormGroup { //: 'Enable notifications': settings label for enabling notifications. label: qsTr('chatNotificationsEnabledLabel') Switch { id: enableChatNotifications checked: SettingsModel.chatNotificationsEnabled onClicked: SettingsModel.chatNotificationsEnabled = !checked } } } FormLine { FormGroup { label: qsTr('chatNotificationSoundEnabledLabel') Switch { id: enableChatNotificationSound checked: SettingsModel.chatNotificationSoundEnabled onClicked: SettingsModel.chatNotificationSoundEnabled = !checked } } FormGroup { label: qsTr('chatNotificationSoundLabel') FileChooserButton { readOnly: !enableChatNotificationSound.checked selectedFile: SettingsModel.chatNotificationSoundPath onAccepted: SettingsModel.chatNotificationSoundPath = selectedFile } } } FormLine { FormGroup { label: qsTr('fileServerLabel') TextField { text: SettingsModel.fileTransferUrl onEditingFinished: SettingsModel.fileTransferUrl = text } } } FormLine { FormGroup { //: 'Hide empty chat rooms' : Label for a switch to choose if Linphone hide empty chat rooms label: qsTr('hideEmptyChatRoomsLabel') Switch { id: hideEmptyChatRooms checked: SettingsModel.hideEmptyChatRooms onClicked: SettingsModel.hideEmptyChatRooms = !checked } } } FormLine { FormGroup { //: 'Auto download' : Label for a slider about auto download mode label: qsTr('AutoDownload') RowLayout{ Layout.fillHeight: true Layout.fillWidth: true spacing: 0 Slider { Layout.fillHeight: true Layout.fillWidth: true id: autoDownloadModes width: parent.width function toSlider(value){ if( value == -1) return autoDownloadModes.from; else if(value == 0) return autoDownloadModes.to; else return value/1000000+autoDownloadModes.from } function fromSlider(value){ if( value == autoDownloadModes.from) return -1 else if( value == autoDownloadModes.to) return 0 else return (value - autoDownloadModes.from) * 1000000 } onValueChanged: { SettingsModel.autoDownloadMaxSize = fromSlider(value) autoDownloadText.updateText(value) } from: 1 to: 500 stepSize: 1 value: toSlider(SettingsModel.autoDownloadMaxSize) } Text { id: autoDownloadText property int preferredWidth : 0 Layout.preferredWidth: preferredWidth color: FormHGroupStyle.legend.color font.pointSize: FormHGroupStyle.legend.pointSize function updateText(value){ //: 'Never' : auto download mode description for deactivated feature. text = value == autoDownloadModes.from ? qsTr('autoDownloadNever') //: 'Always' : auto download mode description for activated feature without any constraints. : value == autoDownloadModes.to ? qsTr('autoDownloadAlways') : ' < '+ ( autoDownloadModes.fromSlider(value) ).toFixed(0)/1000000 + ' Mb' } Component.onCompleted: updateMaxSize() function updateMaxSize(){ autoDownloadText.visible = false updateText(autoDownloadModes.from) var maxWidth = autoDownloadText.implicitWidth updateText(autoDownloadModes.to) maxWidth = Math.max( maxWidth, autoDownloadText.implicitWidth) updateText(autoDownloadModes.to-1) maxWidth = Math.max( maxWidth, autoDownloadText.implicitWidth) autoDownloadText.preferredWidth = maxWidth updateText(autoDownloadModes.value) autoDownloadText.visible = true } } } } } FormLine { visible: false // Use SettingsModel.limeIsSupported when we want to control lime FormGroup { label: qsTr('encryptWithLimeLabel') ExclusiveButtons { id: lime property var limeStates: ([ [ SettingsModel.LimeStateDisabled, qsTr('limeDisabled') ], [ SettingsModel.LimeStateMandatory, qsTr('limeRequired') ], [ SettingsModel.LimeStatePreferred, qsTr('limePreferred') ] ]) texts: limeStates.map(function (value) { return value[1] }) onClicked: SettingsModel.limeState = limeStates[button][0] Binding { property: 'selectedButton' target: lime value: { var toFound = SettingsModel.limeState return Number( Utils.findIndex(lime.limeStates, function (value) { return toFound === value[0] }) ) } } } } } } Form { title: qsTr('contactsTitle') visible: SettingsModel.developerSettingsEnabled width: parent.width FormLine { FormGroup { label: qsTr('contactsEnabledLabel') Switch { checked: SettingsModel.contactsEnabled onClicked: SettingsModel.contactsEnabled = !checked } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsLdap.qml000066400000000000000000000044041434616504300266110ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 import Linphone.Styles 1.0 import Common.Styles 1.0 import 'SettingsAdvanced.js' as Logic // ============================================================================= Column { id: mainColumn // --------------------------------------------------------------------------- function add(){ LdapListModel.add() } spacing: FormStyle.spacing // --------------------------------------------------------------------------- Repeater{ id: ldapList model:LdapProxyModel{id:ldapProxy} delegate:Item{ // LDAP line description : Summary (name + activation + remove) id: swipeView anchors.left: parent.left anchors.right: parent.right clip:true height:summaryRowItem.height Item{ id: summaryRow anchors.fill:parent Row{ id:summaryRowItem anchors.horizontalCenter: parent.horizontalCenter spacing:SettingsAdvancedStyle.lists.spacing Text { id: summaryTitle color: FormStyle.header.title.color text: (modelData.displayName?modelData.displayName:(modelData.server?modelData.server:qsTr('newServer')))//'New server')) font { bold: true pointSize: FormStyle.header.title.pointSize } anchors.verticalCenter: parent.verticalCenter MouseArea{ anchors.fill:parent onClicked:Logic.editLdap(modelData) } } Switch { id: ldapActivation anchors.verticalCenter: parent.verticalCenter checked: modelData.enabled onClicked: { modelData.enabled = !checked } } } ActionButton { id:removeldap anchors.verticalCenter: parent.verticalCenter anchors.right:parent.right anchors.rightMargin:SettingsAdvancedStyle.lists.margin isCustom: true backgroundRadius: 90 colorSet: SettingsAdvancedStyle.cancel scale:SettingsAdvancedStyle.lists.iconScale onClicked:{ LdapListModel.remove($modelData) } visible:hoveringRow.containsMouse } } MouseArea{ id:hoveringRow anchors.fill:parent preventStealing :true hoverEnabled :true propagateComposedEvents:true onClicked:{mouse.accepted = false} onPressed:{mouse.accepted = false} } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsNetwork.qml000066400000000000000000000233531434616504300273660ustar00rootroot00000000000000import QtQuick 2.7 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 // ============================================================================= TabContainer { Column { spacing: SettingsWindowStyle.forms.spacing width: parent.width // ------------------------------------------------------------------------- // General. // ------------------------------------------------------------------------- Form { title: qsTr('generalTitle') visible: SettingsModel.developerSettingsEnabled width: parent.width FormLine { FormGroup { label: qsTr('showNetworkSettingsLabel') Switch { checked: SettingsModel.showNetworkSettings onClicked: SettingsModel.showNetworkSettings = !checked } } } } // ------------------------------------------------------------------------- // Transport. // ------------------------------------------------------------------------- Form { title: qsTr('transportTitle') width: parent.width FormLine { FormGroup { label: qsTr('sendDtmfsLabel') ExclusiveButtons { selectedButton: Number(!SettingsModel.useSipInfoForDtmfs) texts: [ 'SIP INFO', 'RFC 2833' ] onClicked: SettingsModel.useSipInfoForDtmfs = !button } } FormGroup { label: qsTr('allowIpV6Label') Switch { checked: SettingsModel.ipv6Enabled onClicked: SettingsModel.ipv6Enabled = !checked } } } } // ------------------------------------------------------------------------- // Bandwidth control. // ------------------------------------------------------------------------- Form { title: qsTr('bandwidthControlTitle') width: parent.width FormLine { FormGroup { label: qsTr('downloadSpeedLimitLabel') NumericField { minValue: 0 maxValue: 100000 step: 100 text: SettingsModel.downloadBandwidth onEditingFinished: SettingsModel.downloadBandwidth = text } } FormGroup { label: qsTr('uploadSpeedLimitLabel') NumericField { minValue: 0 maxValue: 100000 step: 100 text: SettingsModel.uploadBandwidth onEditingFinished: SettingsModel.uploadBandwidth = text } } } FormLine { FormGroup { label: qsTr('enableAdaptiveRateControlLabel') Switch { checked: SettingsModel.adaptiveRateControlEnabled onClicked: SettingsModel.adaptiveRateControlEnabled = !checked } } } } // ------------------------------------------------------------------------- // Presence. // ------------------------------------------------------------------------- Form { title: qsTr('presenceTitle') width: parent.width FormLine { FormGroup { label: qsTr('rlsUriLabel') ExclusiveButtons { selectedButton: Number(!SettingsModel.rlsUriEnabled) texts: [ qsTr('rlsUriAuto'), qsTr('rlsUriDisabled') ] onClicked: SettingsModel.rlsUriEnabled = !button } } } } // ------------------------------------------------------------------------- // Network protocol and ports. // ------------------------------------------------------------------------- Form { title: qsTr('networkProtocolAndPortsTitle') width: parent.width FormTable { titles: [] FormTableLine { title: qsTr('sipUdpPortLabel') FormTableEntry { width:fixSipUdpPort.width Switch { id: fixSipUdpPort readonly property int defaultPort: 5060 checked: SettingsModel.udpPort > 0 onClicked: SettingsModel.udpPort = (checked ? -2 : defaultPort) } } FormTableEntry { NumericField { minValue: 1 maxValue: 65535 readOnly: !fixSipUdpPort.checked visible:fixSipUdpPort.checked text: SettingsModel.udpPort onEditingFinished: SettingsModel.udpPort = text } } } FormTableLine { title: qsTr('sipTcpPortLabel') FormTableEntry { width:fixSipTcpPort.width Switch { id: fixSipTcpPort readonly property int defaultPort: 5060 checked: SettingsModel.tcpPort > 0 onClicked: SettingsModel.tcpPort = (checked ? -2 : defaultPort) } } FormTableEntry { NumericField { minValue: 1 maxValue: 65535 readOnly: !fixSipTcpPort.checked visible:fixSipTcpPort.checked text: SettingsModel.tcpPort onEditingFinished: SettingsModel.tcpPort = text } } } FormTableLine { id: audioRtpUdpPort readonly property int defaultPort: 7078 title: qsTr('audioRtpUdpPortLabel') FormTableEntry { width:randomAudioRtpUdpPort.width Switch { id: randomAudioRtpUdpPort checked: SettingsModel.audioPortRange[0] !== -1 onClicked: SettingsModel.audioPortRange = checked ? [ -1, -1 ] : [ audioRtpUdpPort.defaultPort, -1 ] } } FormTableEntry { PortField { readOnly: !randomAudioRtpUdpPort.checked visible: randomAudioRtpUdpPort.checked supportsRange: true text: SettingsModel.audioPortRange.join(':') onEditingFinished: SettingsModel.audioPortRange = [ portA, portB ] } } } FormTableLine { id: videoRtpUdpPort readonly property int defaultPort: 9078 title: qsTr('videoRtpUdpPortLabel') visible: SettingsModel.videoSupported FormTableEntry { width:randomVideoRtpUdpPort.width Switch { id: randomVideoRtpUdpPort checked: SettingsModel.videoPortRange[0] !== -1 onClicked: SettingsModel.videoPortRange = checked ? [ -1, -1 ] : [ videoRtpUdpPort.defaultPort, -1 ] } } FormTableEntry { PortField { readOnly: !randomVideoRtpUdpPort.checked visible: randomVideoRtpUdpPort.checked supportsRange: true text: SettingsModel.videoPortRange.join(':') onEditingFinished: SettingsModel.videoPortRange = [ portA, portB ] } } } } } // ------------------------------------------------------------------------- // NAT and Firewall. // ------------------------------------------------------------------------- Form { title: qsTr('natAndFirewallTitle') width: parent.width FormLine { FormGroup { label: qsTr('enableIceLabel') Switch { id: enableIce checked: SettingsModel.iceEnabled onClicked: SettingsModel.iceEnabled = !checked } } FormGroup { label: qsTr('stunServerLabel') TextField { readOnly: !enableIce.checked text: SettingsModel.stunServer onEditingFinished: SettingsModel.stunServer = text } } } FormLine { FormGroup { label: qsTr('enableTurnLabel') Switch { id: enableTurn enabled: enableIce.checked checked: SettingsModel.turnEnabled onClicked: SettingsModel.turnEnabled = !checked } } FormGroup { label: qsTr('turnUserLabel') TextField { id: turnUser readOnly: !enableTurn.checked || !enableTurn.enabled text: SettingsModel.turnUser onEditingFinished: SettingsModel.turnUser = text } } } FormLine { FormGroup {} FormGroup { label: qsTr('turnPasswordLabel') TextField { readOnly: !enableTurn.checked || !enableTurn.enabled || !turnUser.text.length text: SettingsModel.turnPassword onEditingFinished: SettingsModel.turnPassword = text } } } } // ------------------------------------------------------------------------- // DSCP fields. // ------------------------------------------------------------------------- Form { title: qsTr('dscpFieldsTitle') width: parent.width FormLine { FormGroup { label: qsTr('sipFieldLabel') HexField { text: SettingsModel.dscpSip onEditingFinished: SettingsModel.dscpSip = value } } FormGroup {} } FormLine { FormGroup { label: qsTr('audioRtpStreamFieldLabel') HexField { text: SettingsModel.dscpAudio onEditingFinished: SettingsModel.dscpAudio = value } } FormGroup { label: qsTr('videoRtpStreamFieldLabel') visible: SettingsModel.videoSupported HexField { text: SettingsModel.dscpVideo onEditingFinished: SettingsModel.dscpVideo = value } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsSipAccounts.js000066400000000000000000000035241434616504300300110ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `SettingsSipAccounts.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function editAccount (account) { window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/SettingsSipAccountsEdit.qml'), { account: account }) } function deleteAccount (account) { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('deleteAccountDescription'), }, function (status) { if (status) { Linphone.AccountSettingsModel.removeAccount(account.account) } }) } function eraseAllPasswords () { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('eraseAllPasswordsDescription'), }, function (status) { if (status) { Linphone.AccountSettingsModel.eraseAllPasswords() } }) } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsSipAccounts.qml000066400000000000000000000124431434616504300301660ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import App.Styles 1.0 import 'SettingsSipAccounts.js' as Logic // ============================================================================= TabContainer { Column { spacing: SettingsWindowStyle.forms.spacing width: parent.width // ------------------------------------------------------------------------- // Default identity. // ------------------------------------------------------------------------- Form { title: qsTr('defaultIdentityTitle') width: parent.width FormLine { FormGroup { label: qsTr('defaultDisplayNameLabel') TextField { text: AccountSettingsModel.primaryDisplayName onEditingFinished: AccountSettingsModel.primaryDisplayName = text } } } FormLine { FormGroup { label: qsTr('defaultUsernameLabel') TextField { text: AccountSettingsModel.primaryUsername onEditingFinished: AccountSettingsModel.primaryUsername = text } } } FormLine { FormGroup { label: qsTr('defaultSipAddressLabel') TextField { readOnly: true text: AccountSettingsModel.primarySipAddress } } } FormLine { FormGroup { //: 'Device Name' : Label for setting the device name. label: qsTr('defaultDeviceNameLabel') TextField { text: SettingsModel.deviceName onEditingFinished: SettingsModel.deviceName = text } } } } // ------------------------------------------------------------------------- // Proxy accounts. // ------------------------------------------------------------------------- Form { title: qsTr('proxyAccountsTitle') width: parent.width FormTable { legendLineWidth: SettingsWindowStyle.sipAccounts.legendLineWidth titles: ['', qsTr('editHeader'), qsTr('deleteHeader') ] Repeater { model: SettingsModel.showLocalSipAccount ? AccountSettingsModel.accounts.slice(1) : AccountSettingsModel.accounts delegate: FormTableLine { title: modelData.sipAddress FormTableEntry { ActionButton { isCustom: true backgroundRadius: 4 colorSet: SettingsWindowStyle.buttons.editProxy onClicked: Logic.editAccount(modelData) } } FormTableEntry { ActionButton { isCustom: true backgroundRadius: 4 colorSet: SettingsWindowStyle.buttons.deleteProxy onClicked: Logic.deleteAccount(modelData) } } } } } FormEmptyLine {} } Row { anchors.right: parent.right spacing: SettingsWindowStyle.sipAccounts.buttonsSpacing TextButtonB { text: qsTr('eraseAllPasswords') onClicked: Logic.eraseAllPasswords() } TextButtonB { text: qsTr('addAccount') onClicked: Logic.editAccount(null) } } // ------------------------------------------------------------------------- // Assistant. // ------------------------------------------------------------------------- Form { title: qsTr('assistantTitle') width: parent.width visible: SettingsModel.useWebview() || SettingsModel.developerSettingsEnabled FormLine { FormGroup { //: 'Registration URL' : Label for registration URL. label: qsTr('webviewRegistrationUrlLabel') TextField { text: SettingsModel.assistantRegistrationUrl onEditingFinished: SettingsModel.assistantRegistrationUrl = text } } visible: SettingsModel.useWebview() } FormLine { FormGroup { //: 'Login URL' : Label for login URL. label: qsTr('webviewLoginUrlLabel') TextField { text: SettingsModel.assistantLoginUrl onEditingFinished: SettingsModel.assistantLoginUrl = text } } visible: SettingsModel.useWebview() } FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('createAppSipAccountEnabledLabel') Switch { checked: SettingsModel.createAppSipAccountEnabled onClicked: SettingsModel.createAppSipAccountEnabled = !checked } } FormGroup { label: qsTr('useAppSipAccountEnabledLabel') Switch { checked: SettingsModel.useAppSipAccountEnabled onClicked: SettingsModel.useAppSipAccountEnabled = !checked } } } FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('useOtherSipAccountEnabledLabel') Switch { checked: SettingsModel.useOtherSipAccountEnabled onClicked: SettingsModel.useOtherSipAccountEnabled = !checked } } FormGroup { label: qsTr('fetchRemoteConfigurationEnabledLabel') Switch { checked: SettingsModel.fetchRemoteConfigurationEnabled onClicked: SettingsModel.fetchRemoteConfigurationEnabled = !checked } } } FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('assistantSupportsPhoneNumbersLabel') Switch { checked: SettingsModel.assistantSupportsPhoneNumbers onClicked: SettingsModel.assistantSupportsPhoneNumbers = !checked } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsTunnel.qml000066400000000000000000000200061434616504300271720ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import Utils 1.0 import UtilsCpp 1.0 import App.Styles 1.0 // ============================================================================= TabContainer { Column { spacing: SettingsWindowStyle.forms.spacing width: parent.width Form{ width: view.width FormLine { FormGroup { //: 'Tunnel Status' : Field title to introduce the status of the tunnel (activated or not) label: qsTr('tunnelStatus') RowLayout{ Icon{ Layout.preferredWidth: 20 icon: 'led_orange' iconSize: 20 function update(){ if( view.tunnelModel.getActivated()) icon = 'led_green' else icon = 'led_red' } onVisibleChanged: if(visible){ icon = 'led_orange' tunnelStatusTimer.start() }else{ tunnelStatusTimer.stop() icon = 'led_orange' } Timer{ id: tunnelStatusTimer interval: 1000 onTriggered: parent.update() repeat: true } } Item{ Layout.fillWidth: true } } } } //---------------- DEVELOPER SETTINGS FOR TUNNEL FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { //: 'Domain' : Field title of a textfield to set domain. label: qsTr('tunnelDomain') TextField { text: view.tunnelModel.domain onEditingFinished: view.tunnelModel.domain = text } } } FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { //: 'Username' : Field title of a textfield to set username. label: qsTr('tunnelUsername') TextField { text: view.tunnelModel.username onEditingFinished: view.tunnelModel.username = text } } } FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { //: 'SIP' : Field title of a switch to set SIP mode. label: qsTr('tunnelSIP') Switch { checked: view.tunnelModel.sipEnabled onClicked: view.tunnelModel.sipEnabled = !checked } } TextButtonA{ //: 'Cancel' : Button to cancel the action. text: (proxyHttpOptions.showProxyOptions? qsTr('cancel'): //: 'Set HTTP proxy' : Button to set the new proxy. qsTr('setHTTPProxy')) onClicked:{ proxyHttpOptions.showProxyOptions = !proxyHttpOptions.showProxyOptions } Layout.preferredWidth: 100 } } FormLine{ visible: SettingsModel.developerSettingsEnabled RowLayout{ id: proxyHttpOptions property bool showProxyOptions : false TextField { id: proxyHttpHost visible: parent.showProxyOptions //: 'Host' : Placeholder to set hostname. placeholderText: qsTr('proxyHttpHost') } NumericField{ id: proxyHttpPort visible: parent.showProxyOptions //: 'Port' : Placehoilder to set port. placeholderText: qsTr('proxyHttpPort') Layout.preferredWidth: 100 maxValue: 65535 } TextField { id: proxyHttpUsername visible: parent.showProxyOptions //: 'Username' : Placeholder to set username. placeholderText: qsTr('proxyHttpUsername') } PasswordField { id: proxyHttpPassword visible: parent.showProxyOptions //: 'Password' : Placeholder to set password. placeholderText: qsTr('proxyHttpPassword') onVisibleChanged: if(!visible) text='' } TextButtonB{ visible: parent.showProxyOptions //: 'Apply' : Button to set proxy from changes. text: qsTr('proxyHttpApply') onClicked: { view.tunnelModel.setHttpProxy(proxyHttpHost.text, proxyHttpPort.text, proxyHttpUsername.text, proxyHttpPassword.text) parent.showProxyOptions = false } } } } //---------------- USER SETTINGS FOR TUNNEL FormLine { FormGroup { //: 'Mode' : Field title on form to set tunnel mode. label: qsTr('serverMode') ExclusiveButtons { id: mode property var modes: [ [LinphoneEnums.TunnelModeDisable], [LinphoneEnums.TunnelModeEnable], [LinphoneEnums.TunnelModeAuto] ] texts: modes.map(value => UtilsCpp.toString(value[0]) ) onClicked: view.tunnelModel.mode = modes[button][0] Binding { property: 'selectedButton' target: mode value: { var toFound = view.tunnelModel.mode return Number(Utils.findIndex(mode.modes, function (value) { return toFound == value[0] })) } } } } } FormLine { FormGroup { //: 'Dual mode' : Field title on form to set dual mode of the tunnel. label: qsTr('serverDualMode') Switch { checked: view.tunnelModel.dualModeEnabled onClicked: view.tunnelModel.dualModeEnabled = !checked } } } } //---------------------------------------------------------------------------------------------------- Repeater { id: view property TunnelModel tunnelModel : SettingsModel.getTunnel() property TunnelConfigProxyModel tunnelConfigProxyModel: tunnelModel.getTunnelProxyConfigs() Component.onDestruction: {tunnelConfigProxyModel=null}// Need to set it to null because of not calling destructor if not. width: parent.width model: tunnelConfigProxyModel delegate: Form { //: 'Server' : Title form to set a server title: qsTr('serverTitle')+' : '+$modelData.host+(view.tunnelModel.dualModeEnabled?' / '+$modelData.host2:'') width: view.width removeButton: SettingsModel.developerSettingsEnabled onRemoveButtonClicked: {view.tunnelModel.removeTunnelConfig($modelData)} FormLine { FormGroup { //: 'Hostname' : Field title on form to set hostname. label: qsTr('serverHostname') TextField { text: $modelData.host onEditingFinished: $modelData.host = text } } } FormLine { FormGroup { //: 'Port' : Field title on form to set port. label: qsTr('serverPort') NumericField { text: $modelData.port onEditingFinished: $modelData.port = text maxValue: 65535 } } } FormLine { visible: view.tunnelModel.dualModeEnabled FormGroup { //: 'Dual hostname URL' : Field title on form to set the second hostname for dual configuration. label: qsTr('serverDualHostname') TextField { text: $modelData.host2 onEditingFinished: $modelData.host2 = text } } } FormLine { visible: view.tunnelModel.dualModeEnabled FormGroup { //: 'Dual port' : Field title on form to set the second port for the dual configuration. label: qsTr('serverDualPort') NumericField { text: $modelData.port2 onEditingFinished: $modelData.port2 = text maxValue: 65535 } } } //------------------------- DEVELOPER SETTINGS FOR SERVER FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { //: 'Remote UDP mirror port' : Field title on form to set the remote UDP mirror port. label: qsTr('serverRemoteUDPMirrorPort') NumericField { text: $modelData.remoteUdpMirrorPort onEditingFinished: $modelData.remoteUdpMirrorPort = text maxValue: 65535 } } } FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { //: 'Delay' : Field title on form to set the delay of the tunnel. label: qsTr('serverDelay') NumericField { text: $modelData.delay onEditingFinished: $modelData.delay = text } } } } } RowLayout{ width: parent.width TextButtonB { visible: SettingsModel.developerSettingsEnabled //: 'Add server' : Button for adding a server text: qsTr('tunnelAddServer') onClicked: {view.tunnelModel.addTunnelConfig()} } Item{ Layout.fillWidth: true } TextButtonB { //: 'Apply' : Button to apply changes. text: qsTr('tunnelApply') onClicked: view.tunnelModel.apply() } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsUi.js000066400000000000000000000041521434616504300261310ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `SettingsUi.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function cleanAvatars () { window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('cleanAvatarsDescription'), }, function (status) { if (status) { Linphone.ContactsListModel.cleanAvatars() } }) } function getAvailableLocales () { var locales = [] Linphone.App.availableLocales.forEach(function (locale) { locales.push({ key: Utils.capitalizeFirstLetter(locale.nativeLanguageName), value: locale.name }) }) return [{ key: qsTr('systemLocale'), value: '' }].concat(locales.sort(function (a, b) { if( a.key < b.key ) return -1; if( a.key > b.key ) return 1; return 0; })) } function setLocale (locale) { var App = Linphone.App App.configLocale = locale window.detachVirtualWindow() window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), { descriptionText: qsTr('setLocaleDescription'), }, function (status) { if (status) { App.restart() } }) } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsUi.qml000066400000000000000000000171031434616504300263060ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Dialogs 1.3 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Utils 1.0 import UtilsCpp 1.0 import App.Styles 1.0 import Linphone.Styles 1.0 import Common.Styles 1.0 import 'SettingsUi.js' as Logic // ============================================================================= TabContainer { Column { spacing: SettingsWindowStyle.forms.spacing width: parent.width // ------------------------------------------------------------------------- // Languages. // ------------------------------------------------------------------------- Form { title: qsTr('languagesTitle') width: parent.width FormLine { FormGroup { label: qsTr('languagesLabel') ComboBox { textRole: 'key' Component.onCompleted: { var locales = Logic.getAvailableLocales() model = locales var locale = App.configLocale if (!locale.length) { currentIndex = 0 return } var value = Qt.locale(locale).name currentIndex = Number(Utils.findIndex(locales, function (locale) { return locale.value === value })) } onActivated: Logic.setLocale(model[index].value) } } } Form { //: 'Fonts' : title of fonts section in settings title: qsTr('fontsTitle') width: parent.width FormLine { FormGroup { //: 'Text Messages' : Label for changing text message fonts label: qsTr('fontsTextChange') RowLayout{ ActionButton { isCustom: true backgroundRadius: 90 colorSet: SettingsUiStyle.options onClicked: fontDialog.visible = true Layout.preferredWidth: 25 FontDialog { id: fontDialog //: 'Select a new font' : Popup title for choosing new fonts title: qsTr('fontsPopupTitle') visible:false font: SettingsModel.textMessageFont onAccepted: { SettingsModel.textMessageFont = fontDialog.font } onRejected: { } } } Text{ Layout.leftMargin: 18 Layout.fillWidth: true text: SettingsModel.textMessageFont.family +", "+SettingsModel.textMessageFont.pointSize font { bold: true pointSize: FormTableStyle.entry.text.pointSize } } } } } } } // ------------------------------------------------------------------------- // Paths. // ------------------------------------------------------------------------- Form { title: qsTr('pathsTitle') visible: SettingsModel.videoSupported || SettingsModel.callRecorderEnabled || SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled || SettingsModel.developerSettingsEnabled width: parent.width FormLine { visible: SettingsModel.videoSupported FormGroup { label: qsTr('savedScreenshotsLabel') FileChooserButton { selectedFile: SettingsModel.savedScreenshotsFolder selectFolder: true onAccepted: SettingsModel.savedScreenshotsFolder = selectedFile } } } FormLine { visible: SettingsModel.callRecorderEnabled || SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('savedCallsLabel') FileChooserButton { selectedFile: SettingsModel.savedCallsFolder selectFolder: true onAccepted: SettingsModel.savedCallsFolder = selectedFile } } } FormLine { visible: SettingsModel.standardChatEnabled || SettingsModel.secureChatEnabled || SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('downloadLabel') FileChooserButton { selectedFile: SettingsModel.downloadFolder selectFolder: true onAccepted: SettingsModel.downloadFolder = selectedFile } } } } // ------------------------------------------------------------------------- // Data. // ------------------------------------------------------------------------- Form { title: qsTr('dataTitle') visible: SettingsModel.contactsEnabled || SettingsModel.developerSettingsEnabled width: parent.width } TextButtonB { anchors.right: parent.right text: qsTr('cleanAvatars') visible: SettingsModel.contactsEnabled || SettingsModel.developerSettingsEnabled onClicked: Logic.cleanAvatars() } // ------------------------------------------------------------------------- // Other. // ------------------------------------------------------------------------- Form { title: qsTr('otherTitle') width: parent.width FormLine { FormGroup { label: qsTr('exitOnCloseLabel') Switch { checked: SettingsModel.exitOnClose onClicked: SettingsModel.exitOnClose = !checked } } FormGroup { label: qsTr('autoStartLabel') Switch { checked: App.autoStart onClicked: App.autoStart = !checked } } } FormLine { FormGroup { //: 'Enable Mipmap' label: qsTr('mipmapLabel') Switch { checked: SettingsModel.mipmapEnabled onClicked: SettingsModel.mipmapEnabled = !checked TooltipArea{ //: 'This property holds whether the image uses mipmap filtering when scaled or transformed.' : first line of a tooltip about Mipmap mode. text: qsTr('mipmapTooltip1')+'\n' //: 'Mipmap filtering gives better visual quality when scaling down compared to smooth, but it may come at a performance cost (both when initializing the image and during rendering).' : Second line of a tooltip about Mipmap mode. +qsTr('mipmapTooltip2') } } } FormGroup { //: 'Minimal Timeline filter' label: qsTr('minimalTimelineFilterLabel') Switch { checked: SettingsModel.useMinimalTimelineFilter onClicked: SettingsModel.useMinimalTimelineFilter = !checked TooltipArea{ //: 'Show a minimal version of what to display in timeline.' : text: qsTr('minimalTimelineFilterTooltip') } } } } FormLine { maxItemWidth: parent.width FormGroup { //: 'Check for updates' : Label switch for enabling check for updates label: qsTr('checkForUpdateLabel') maxWidth: 3*parent.width/2 RowLayout{ Switch { checked: SettingsModel.checkForUpdateEnabled onClicked: SettingsModel.checkForUpdateEnabled = !checked } TextField { Layout.fillWidth: true text: SettingsModel.versionCheckUrl onEditingFinished: SettingsModel.versionCheckUrl = text } ComboBox{ Layout.preferredWidth: fitWidth Layout.leftMargin: 10 //: 'Release' : Keyword for an option to check the release version model: [qsTr('versionCheckTypeRelease'), //: 'Nightly' : Keyword for an option to check the nightly version qsTr('versionCheckTypeNightly'), //: 'Custom' : Keyword for an option to check the custom version qsTr('versionCheckTypeCustom')] visible: SettingsModel.haveVersionNightlyUrl() currentIndex: SettingsModel.versionCheckType == SettingsModel.VersionCheckType_Release ? 0 : SettingsModel.versionCheckType == SettingsModel.VersionCheckType_Nightly ? 1 : 2 onActivated: SettingsModel.versionCheckType = ( index == 0 ? SettingsModel.VersionCheckType_Release : index == 1 ? SettingsModel.VersionCheckType_Nightly : SettingsModel.VersionCheckType_Custom) } } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsVideo.js000066400000000000000000000032421434616504300266210ustar00rootroot00000000000000/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . */ // ============================================================================= // `SettingsVideo.qml` Logic. // ============================================================================= .import Linphone 1.0 as Linphone .import 'qrc:/ui/scripts/Utils/utils.js' as Utils // ============================================================================= function showVideoPreview (account) { window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/SettingsVideoPreview.qml')) } function updateVideoPreview () { var count = Linphone.CallsListModel.rowCount() if (count === 0) { showCameraPreview.enabled = true } else if (count === 1) { showCameraPreview.enabled = false window.detachVirtualWindow() } } function hideVideoPreview () { window.detachVirtualWindow() } function handleCodecDownloadRequested (codecInfo) { Utils.openCodecOnlineInstallerDialog(window, codecInfo) } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsVideo.qml000066400000000000000000000201141434616504300267730ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import LinphoneEnums 1.0 import Utils 1.0 import App.Styles 1.0 import 'SettingsVideo.js' as Logic // ============================================================================= TabContainer { Column { spacing: SettingsWindowStyle.forms.spacing width: parent.width // ------------------------------------------------------------------------- // Video parameters. // ------------------------------------------------------------------------- Form { title: qsTr('videoCaptureTitle') width: parent.width //Warning if in call FormLine { visible: SettingsModel.isInCall FormGroup { RowLayout { spacing: SettingsWindowStyle.video.warningMessage.iconSize Icon { icon: 'warning' iconSize: SettingsWindowStyle.video.warningMessage.iconSize anchors { rightMargin: SettingsWindowStyle.video.warningMessage.iconSize leftMargin: SettingsWindowStyle.video.warningMessage.iconSize } } Text { text: qsTr('videoSettingsInCallWarning') } } } } FormLine { FormGroup { label: qsTr('videoInputDeviceLabel') ComboBox { currentIndex: Number(Utils.findIndex(model, function (device) { return device === SettingsModel.videoDevice })) // Number cast => Index is null if app does not support video. model: SettingsModel.videoDevices onActivated: SettingsModel.videoDevice = model[index] } } } FormLine { FormGroup { label: qsTr('videoPresetLabel') ComboBox { currentIndex: { var preset = SettingsModel.videoPreset var index = Number(Utils.findIndex(model, function (value) { return preset === value.value })) return index>=0 ? index : 0; } model: [{ key: qsTr('presetDefault'), value: 'default' }, { key: qsTr('presetHighFps'), value: 'high-fps' }, { key: qsTr('presetCustom'), value: 'custom' }] textRole: 'key' onActivated: SettingsModel.videoPreset = model[index].value } } } FormLine { FormGroup { label: qsTr('videoSizeLabel') ComboBox { currentIndex: Number(Utils.findIndex(model, function (definition) { return definition.value.name === SettingsModel.videoDefinition.name })) // Number cast => Index is null if app does not support video. model: SettingsModel.supportedVideoDefinitions.map(function (definition) { return { key: definition.name + ' (' + definition.width + 'x' + definition.height + ')', value: definition } }) textRole: 'key' onActivated: SettingsModel.videoDefinition = model[index].value } } FormGroup { label: qsTr('videoFramerateLabel') visible: SettingsModel.videoPreset === 'custom' NumericField { maxValue: 60 minValue: 1 text: SettingsModel.videoFramerate onEditingFinished: SettingsModel.videoFramerate = text } } } FormEmptyLine {} } TextButtonB { id: showCameraPreview anchors.right: parent.right enabled: CallsListModel.rowCount() === 0 text: qsTr('showCameraPreview') onClicked: Logic.showVideoPreview() Connections { target: CallsListModel onRowsInserted: Logic.updateVideoPreview() onRowsRemoved: Logic.updateVideoPreview() } Connections { target: window onClosing: Logic.hideVideoPreview() } } // ------------------------------------------------------------------------- // Video Codecs. // ------------------------------------------------------------------------- Form { title: qsTr('videoCodecsTitle') visible: SettingsModel.showVideoCodecs || SettingsModel.developerSettingsEnabled width: parent.width FormLine { visible: SettingsModel.developerSettingsEnabled FormGroup { label: qsTr('showVideoCodecsLabel') Switch { checked: SettingsModel.showVideoCodecs onClicked: SettingsModel.showVideoCodecs = !checked } } } CodecsViewer { model: VideoCodecsModel width: parent.width onDownloadRequested: Logic.handleCodecDownloadRequested(codecInfo) } } // ------------------------------------------------------------------------- // Diplay Video. // ------------------------------------------------------------------------- Form { id: videoDisplayForm //: 'Video display' : Title for display parameters title: qsTr('videoDisplayTitle') width: parent.width property var cameraModel: //: 'Hybrid' : Hybrid mode for camera. [{displayText:qsTr('videoHybrid')} //: 'Occupy all space' : Camera mode for a centered cropping view. , {displayText:qsTr('videoOccupyAllSpace')} //: 'Black bars' : Camera mode for a fit view with black bars to keep ratio. , {displayText:qsTr('videoBlackBars')} ] function getId(index){ if(index == 0 ) return SettingsModel.CameraMode_Hybrid else if(index == 1) return SettingsModel.CameraMode_OccupyAllSpace else return SettingsModel.CameraMode_BlackBars } Row{ property int orientation: Qt.Horizontal width: parent.width Text{ id: videoModeLabel height: parent.height //: 'Camera modes' : Label to choose a camera modes. text: qsTr('videoModeLabel') font: videoGridMode.labelFont horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignVCenter } FormLine { spacing: 5 width: parent.width-videoModeLabel.implicitWidth FormGroup { id: videoGridMode //: 'Mosaic' : Label to choose a camera mode. label: qsTr('videoGridModeLabel') + ':' fitLabel: true ComboBox { model: videoDisplayForm.cameraModel textRole: 'displayText' currentIndex: SettingsModel.gridCameraMode == SettingsModel.CameraMode_Hybrid ? 0 : SettingsModel.gridCameraMode == SettingsModel.CameraMode_OccupyAllSpace ? 1 : 2 onActivated: SettingsModel.gridCameraMode = videoDisplayForm.getId(index) } } FormGroup { //: 'Active speaker' : Label to choose a camera mode. label: qsTr('videoActiveSpeakerModeLabel') + ':' fitLabel: true ComboBox { model: videoDisplayForm.cameraModel textRole: 'displayText' currentIndex: SettingsModel.activeSpeakerCameraMode == SettingsModel.CameraMode_Hybrid ? 0 : SettingsModel.activeSpeakerCameraMode == SettingsModel.CameraMode_OccupyAllSpace ? 1 : 2 onActivated: SettingsModel.activeSpeakerCameraMode = videoDisplayForm.getId(index) } } FormGroup { //: 'Calls' : Label to choose a camera mode. label: qsTr('videoCallsModeLabel') + ':' fitLabel: true ComboBox { model: videoDisplayForm.cameraModel textRole: 'displayText' currentIndex: SettingsModel.callCameraMode == SettingsModel.CameraMode_Hybrid ? 0 : SettingsModel.callCameraMode == SettingsModel.CameraMode_OccupyAllSpace ? 1 : 2 onActivated: SettingsModel.callCameraMode = videoDisplayForm.getId(index) } } } } FormLine { FormGroup { //: 'Default video layout' : Label to choose the default layout in video conference. label: qsTr('videoLayout') fitLabel: true ComboBox { //: 'Mosaic' : Mosaic layout invideo conference. model:[{text:qsTr('videoMosaicLayout'), value:LinphoneEnums.ConferenceLayoutGrid} //: 'Active speaker' : Active speaker layout for video conference. , {text:qsTr('videoActiveSpeakerLayout'), value:LinphoneEnums.ConferenceLayoutActiveSpeaker} ] textRole: 'text' currentIndex: SettingsModel.videoConferenceLayout == LinphoneEnums.ConferenceLayoutGrid ? 0 : 1 onActivated: SettingsModel.videoConferenceLayout = model[index].value } } } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Settings/SettingsWindow.qml000066400000000000000000000135301434616504300272000ustar00rootroot00000000000000import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import Clipboard 1.0 import Common 1.0 import Common.Styles 1.0 import Konami 1.0 import Linphone 1.0 import App.Styles 1.0 // ============================================================================= ApplicationWindow { id: window minimumHeight: SettingsWindowStyle.height minimumWidth: SettingsWindowStyle.width title: qsTr('settingsTitle') onClosing: { logViewer.active = false SettingsModel.settingsWindowClosing() tabBar.setCurrentIndex(0) } // --------------------------------------------------------------------------- Shortcut { sequence: StandardKey.Close onActivated: window.hide() } // --------------------------------------------------------------------------- Rectangle { anchors.fill: parent color: SettingsWindowStyle.color } ColumnLayout { anchors.fill: parent spacing: 0 // ------------------------------------------------------------------------- // Navigation bar. // ------------------------------------------------------------------------- Item{ Layout.fillWidth: true Layout.preferredHeight: TabButtonStyle.text.height RowLayout { anchors.fill: parent spacing: 0 TabBar { id: tabBar onCurrentIndexChanged: SettingsModel.onSettingsTabChanged(currentIndex) TabButton { iconName: TabButtonStyle.icon.sipAccountsIcon text: qsTr('sipAccountsTab') width: implicitWidth } TabButton { iconName: TabButtonStyle.icon.audioIcon text: qsTr('audioTab') width: implicitWidth } TabButton { enabled: SettingsModel.videoSupported iconName: TabButtonStyle.icon.videoIcon text: qsTr('videoTab') width: implicitWidth } TabButton { iconName: TabButtonStyle.icon.callIcon text: qsTr('callsAndChatTab') width: implicitWidth } TabButton { enabled: SettingsModel.showNetworkSettings || SettingsModel.developerSettingsEnabled iconName: TabButtonStyle.icon.networkIcon text: qsTr('networkTab') width: implicitWidth } TabButton { visible: SettingsModel.tunnelAvailable() enabled: visible iconName: TabButtonStyle.icon.sipAccountsIcon //: 'Tunnel' : Tab title for tunnel section in settings. text: qsTr('tunnelTab') width: visible ? implicitWidth : 0 } TabButton { iconName: TabButtonStyle.icon.advancedIcon text: qsTr('uiTab') width: implicitWidth } TabButton { iconName: TabButtonStyle.icon.advancedIcon text: qsTr('uiAdvanced') width: implicitWidth } } Rectangle { Layout.fillWidth: true Layout.preferredHeight: TabButtonStyle.text.height color: TabButtonStyle.backgroundColor.normal MouseArea { anchors.fill: parent onClicked: konami.forceActiveFocus() cursorShape: Qt.ArrowCursor Konami { id: konami onTriggered: SettingsModel.developerSettingsEnabled = true } } } } Rectangle{ id: hideBar anchors.fill: parent color: TabButtonStyle.backgroundColor.normal visible: logViewer.active } } // ------------------------------------------------------------------------- // Content. // ------------------------------------------------------------------------- Item{ Layout.fillHeight: true Layout.fillWidth: true StackLayout { anchors.fill: parent currentIndex: tabBar.currentIndex SettingsSipAccounts {} SettingsAudio {} SettingsVideo {} SettingsCallsChat {} SettingsNetwork {} SettingsTunnel {} SettingsUi {} SettingsAdvanced {onShowLogs: logViewer.active=true } } Loader{ id: logViewer anchors.fill: parent active: false sourceComponent: Component{ Rectangle{ id: logBackground anchors.fill: parent property variant stringList: null function updateText() { stringList = SettingsModel.getLogText().split('\n') idContentListView.positionViewAtEnd() } Component.onCompleted: updateText() ColumnLayout{ anchors.fill: parent RowLayout{// Prepare for other actions ActionButton{ Layout.topMargin: 5 Layout.leftMargin: 5 backgroundRadius: width/2 isCustom: true colorSet: SettingsWindowStyle.buttons.back onClicked: logViewer.active = false } ActionButton{ Layout.topMargin: 5 Layout.leftMargin: 5 backgroundRadius: width/2 isCustom: true colorSet: SettingsWindowStyle.buttons.copy onClicked: {updating = true ; Clipboard.text = SettingsModel.getLogText();updating=false} } } ListView { id: idContentListView model: logBackground.stringList Layout.fillHeight: true Layout.fillWidth: true Layout.topMargin: 20 Layout.leftMargin: 10 Layout.rightMargin: 10 clip: true delegate: Text { width: idContentListView.width text: model.modelData font.pointSize: FormTableStyle.entry.text.pointSize textFormat: Text.PlainText wrapMode: Text.Wrap } ScrollBar.vertical: ScrollBar {} } } } } } } // ------------------------------------------------------------------------- // Buttons. // ------------------------------------------------------------------------- TextButtonB { Layout.alignment: Qt.AlignRight Layout.topMargin: SettingsWindowStyle.validButton.topMargin Layout.bottomMargin: SettingsWindowStyle.validButton.bottomMargin Layout.rightMargin: SettingsWindowStyle.validButton.rightMargin text: qsTr('validButton') onClicked: window.close() } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/000077500000000000000000000000001434616504300231565ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/000077500000000000000000000000001434616504300242145ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/CallFullscreenStyle.qml000066400000000000000000000616721434616504300306620ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'CallFullscreen' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'f').color property QtObject actionArea: QtObject { property int height: 100 property int iconSize: 40 property int leftButtonsGroupMargin: 50 property int lowWidth: 650 property int rightButtonsGroupMargin: 50 property QtObject userVideo: QtObject { property int height: 200 property int width: 130 property int heightReference: 1200 // height and width are fixed from these references property int widthReference: 780 } property QtObject vu: QtObject { property int spacing: 5 } property QtObject callError: QtObject { property color color: ColorsList.add(sectionName+'_action_error', 'i').color property int pointSize: Units.dp * 12 } } property QtObject container: QtObject { property int margins: 15 property QtObject avatar: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_container_avatar_bg', 'n').color property int maxSize: 300 } property QtObject pause: QtObject { property color color: ColorsList.add(sectionName+'_container_pause', 'g90').color property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_container_pause_text', 'q').color property int pointSizeFactor: 5 } } } property QtObject header: QtObject { property int buttonIconSize: 40 property int iconSize: 16 property int leftMargin: 20 property int rightMargin: 20 property int spacing: 10 property int topMargin: 26 property QtObject contactDescription: QtObject { property int height: 50 property int width: 150 } property QtObject elapsedTime: QtObject { property color color: ColorsList.add(sectionName+'_header_elapsed_time', 'j').color property int pointSize: Units.dp * 10 property QtObject fullscreen: QtObject { property int pointSize: Units.dp * 12 } } property QtObject stats: QtObject { property int relativeY: 90 } } property QtObject zrtpArea: QtObject { property int height: 50 property QtObject buttons: QtObject { property int spacing: 10 } property QtObject text: QtObject { property color colorA: ColorsList.add(sectionName+'_zrtp_text_a', 'j').color property color colorB: ColorsList.add(sectionName+'_zrtp_text_b', 'i').color property int pointSize: Units.dp * 10 property int wordsSpacing: 5 } } // Button colors property QtObject buttons: QtObject { property QtObject callQuality: QtObject { property int iconSize: 30 property string name : 'quality' property string icon_0 : 'call_quality_0_custom' property string icon_1 : 'call_quality_1_custom' property string icon_2 : 'call_quality_2_custom' property string icon_3 : 'call_quality_3_custom' property string icon_4 : 'call_quality_4_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon_2, 'me_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon_2, 'me_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon_2, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon_2, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon_2, 'me_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon_2, 'me_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon_2, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon_2, 'me_p_b_fg').color } property QtObject telKeyad: QtObject { property int iconSize: 30 property string name : 'telKeypad' property string icon : 'dialpad_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject secure: QtObject { property int iconSize: 16 property string icon : 'call_chat_secure_custom' property string name : 'secure' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, '', '', 'transparent').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, '', '', 'transparent').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, '', '', 'transparent').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, '', '', '#5a585b').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, '', '', '#5a585b').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, '', '', '#5a585b').color } property QtObject unsecure: QtObject { property int iconSize: 16 property string icon : 'call_chat_unsecure_custom' property string name : 'unsecure' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, '', '', 'transparent').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, '', '', 'transparent').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, '', '', 'transparent').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, '', '', '#ff0000').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, '', '', '#ff0000').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, '', '', '#ff0000').color } property QtObject screenshot: QtObject { property int iconSize: 40 property string icon : 'screenshot_custom' property string name : 'screenshot' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject record: QtObject { property int iconSize: 40 property string icon : 'record_custom' property string name : 'record' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 's_p_b_fg').color } property QtObject fullscreen: QtObject { property int iconSize: 40 property string icon : 'fullscreen_custom' property string name : 'fullscreen' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject stopFullscreen: QtObject { property int iconSize: 40 property string icon : 'stop_fullscreen_custom' property string name : 'stopFullscreen' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject microOn: QtObject { property int iconSize: 40 property string icon : 'micro_on_custom' property string name : 'microOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject microOff: QtObject { property int iconSize: 40 property string icon : 'micro_off_custom' property string name : 'microOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject speakerOn: QtObject { property int iconSize: 40 property string icon : 'speaker_on_custom' property string name : 'speakerOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject speakerOff: QtObject { property int iconSize: 40 property string icon : 'speaker_off_custom' property string name : 'speakerOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject cameraOn: QtObject { property int iconSize: 40 property string icon : 'camera_on_custom' property string name : 'cameraOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject cameraOff: QtObject { property int iconSize: 40 property string icon : 'camera_off_custom' property string name : 'cameraOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject options: QtObject { property int iconSize: 40 property string icon : 'menu_vdots_custom' property string name : 'options' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject pause: QtObject { property int iconSize: 40 property string icon : 'pause_custom' property string name : 'pause' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject play: QtObject { property int iconSize: 40 property string icon : 'play_custom' property string name : 'play' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject chat: QtObject { property int iconSize: 40 property string icon : 'chat_custom' property string name : 'chat' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject history: QtObject { property int iconSize: 40 property string icon : 'history_custom' property string name : 'history' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject hangup: QtObject { property int iconSize: 40 property string icon : 'hangup_custom' property string name : 'hangup' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'r_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'r_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'r_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'r_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'r_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'r_p_b_fg').color } property QtObject acceptVideoCall: QtObject { property int iconSize: 40 property string icon : 'video_call_accept_custom' property string name : 'videoCallAccept' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color } property QtObject acceptCall: QtObject { property int iconSize: 40 property string icon : 'call_accept_custom' property string name : 'callAccept' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/CallStyle.qml000066400000000000000000000626741434616504300266420ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'Call' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'f').color property QtObject actionArea: QtObject { property int height: 100 property int iconSize: 40 property int leftButtonsGroupMargin: 50 property int lowWidth: 650 property int rightButtonsGroupMargin: 50 property QtObject userVideo: QtObject { property int height: 200 property int width: 130 property int heightReference: 1200 // height and width are fixed from these references property int widthReference: 780 } property QtObject vu: QtObject { property int spacing: 5 } property QtObject callError: QtObject { property color color: ColorsList.add(sectionName+'_action_error', 'i').color property int pointSize: Units.dp * 12 } } property QtObject container: QtObject { property int margins: 15 property QtObject avatar: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_container_avatar_bg', 'n').color property int maxSize: 300 } property QtObject pause: QtObject { property color color: ColorsList.add(sectionName+'_container_pause', 'g90').color property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_container_pause_text', 'q').color property int pointSizeFactor: 5 } } } property QtObject header: QtObject { property int buttonIconSize: 40 property int iconSize: 16 property int leftMargin: 20 property int rightMargin: 20 property int spacing: 10 property int topMargin: 26 property QtObject busyIndicator: QtObject { property color color: ColorsList.add(sectionName+'_header_busy', 'q').color property int height: 30 property int width: 30 } property QtObject contactDescription: QtObject { property int height: 50 property int width: 150 } property QtObject elapsedTime: QtObject { property color color: ColorsList.add(sectionName+'_header_elapsed_time', 'j').color property int pointSize: Units.dp * 10 property QtObject fullscreen: QtObject { property int pointSize: Units.dp * 12 } } property QtObject stats: QtObject { property int relativeY: 90 } } property QtObject zrtpArea: QtObject { property int height: 50 property string pqIcon: 'secure_pq_zrtp' property int iconSize: 30 property QtObject buttons: QtObject { property int spacing: 10 } property QtObject text: QtObject { property color colorA: ColorsList.add(sectionName+'_zrtp_text_a', 'j').color property color colorB: ColorsList.add(sectionName+'_zrtp_text_b', 'i').color property int pointSize: Units.dp * 10 property int wordsSpacing: 5 } } // Button colors property QtObject buttons: QtObject { property QtObject callQuality: QtObject { property int iconSize: 30 property string name : 'quality' property string icon_0 : 'call_quality_0_custom' property string icon_1 : 'call_quality_1_custom' property string icon_2 : 'call_quality_2_custom' property string icon_3 : 'call_quality_3_custom' property string icon_4 : 'call_quality_4_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon_2, 'me_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon_2, 'me_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon_2, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon_2, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon_2, 'me_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon_2, 'me_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon_2, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon_2, 'me_p_b_fg').color } property QtObject telKeyad: QtObject { property int iconSize: 30 property string name : 'telKeypad' property string icon : 'dialpad_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 'me_u_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 'me_u_b_fg').color } property QtObject secure: QtObject { property int iconSize: 16 property string icon : 'call_chat_secure_custom' property string name : 'secure' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, '', '', 'transparent').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, '', '', 'transparent').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, '', '', 'transparent').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, '', '', '#5a585b').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, '', '', '#5a585b').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, '', '', '#5a585b').color } property QtObject unsecure: QtObject { property int iconSize: 16 property string icon : 'call_chat_unsecure_custom' property string name : 'unsecure' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, '', '', 'transparent').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, '', '', 'transparent').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, '', '', 'transparent').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, '', '', '#ff0000').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, '', '', '#ff0000').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, '', '', '#ff0000').color } property QtObject screenshot: QtObject { property int iconSize: 40 property string icon : 'screenshot_custom' property string name : 'screenshot' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject record: QtObject { property int iconSize: 40 property string icon : 'record_custom' property string name : 'record' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 'me_p_b_fg').color } property QtObject fullscreen: QtObject { property int iconSize: 40 property string icon : 'fullscreen_custom' property string name : 'fullscreen' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject stopFullscreen: QtObject { property int iconSize: 40 property string icon : 'stop_fullscreen_custom' property string name : 'stopFullscreen' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject microOn: QtObject { property int iconSize: 40 property string icon : 'micro_on_custom' property string name : 'microOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject microOff: QtObject { property int iconSize: 40 property string icon : 'micro_off_custom' property string name : 'microOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject speakerOn: QtObject { property int iconSize: 40 property string icon : 'speaker_on_custom' property string name : 'speakerOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject speakerOff: QtObject { property int iconSize: 40 property string icon : 'speaker_off_custom' property string name : 'speakerOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject cameraOn: QtObject { property int iconSize: 40 property string icon : 'camera_on_custom' property string name : 'cameraOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject cameraOff: QtObject { property int iconSize: 40 property string icon : 'camera_off_custom' property string name : 'cameraOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject options: QtObject { property int iconSize: 40 property string icon : 'menu_vdots_custom' property string name : 'options' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject pause: QtObject { property int iconSize: 40 property string icon : 'pause_custom' property string name : 'pause' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject play: QtObject { property int iconSize: 40 property string icon : 'play_custom' property string name : 'play' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject chat: QtObject { property int iconSize: 40 property string icon : 'chat_custom' property string name : 'chat' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject history: QtObject { property int iconSize: 40 property string icon : 'history_custom' property string name : 'history' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject hangup: QtObject { property int iconSize: 40 property string icon : 'hangup_custom' property string name : 'hangup' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'r_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'r_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'r_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'r_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'r_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'r_p_b_fg').color } property QtObject acceptVideoCall: QtObject { property int iconSize: 40 property string icon : 'video_call_accept_custom' property string name : 'videoCallAccept' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color } property QtObject acceptCall: QtObject { property int iconSize: 40 property string icon : 'call_accept_custom' property string name : 'callAccept' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/CallsWindowStyle.qml000066400000000000000000000074151434616504300302050ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CallsWindow' property int minimumHeight: 640 property int minimumWidth: 960 property QtObject call: QtObject { property int minimumWidth: 395 } property QtObject callsList: QtObject { property color color: ColorsList.add(sectionName+'_list_bg', 'q').color property int defaultWidth: 250 property int maximumWidth: 300 property int minimumWidth: 200 property QtObject header: QtObject { property color color1: ColorsList.add(sectionName+'_list_header_a', 'q').color property color color2: ColorsList.add(sectionName+'_list_header_b', 'f').color property int height: 60 property int iconSize: 40 property int leftMargin: 10 } property QtObject newCall: QtObject { property int iconSize: 40 property string name : 'newCall' property string icon : 'new_call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'ma_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'ma_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'ma_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'ma_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'ma_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'ma_p_b_fg').color } property QtObject mergeConference: QtObject { property int iconSize: 40 property string name : 'mergeConference' property string icon : 'conference_merge_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'ma_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'ma_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'ma_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'ma_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'ma_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'ma_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'ma_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'ma_d_b_fg').color } property QtObject closeButton: QtObject{ property int iconSize: 40 property string name : 'close' property string icon : 'close_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_b_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_f_p', icon, 'l_p_b_fg').color } } property QtObject chat: QtObject { property int minimumWidth: 300 } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/ConferenceStyle.qml000066400000000000000000000013111434616504300300130ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Conference' property QtObject description: QtObject { property color color: ColorsList.add(sectionName+'_description', 'j').color property int pointSize: Units.dp * 12 property int height: 60 property int width: 150 } property QtObject grid: QtObject { property int spacing: 5 property QtObject cell: QtObject { property int height: 145 property int spacing: 5 property int width: 154 property QtObject contactDescription: QtObject { property int height: 35 } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/Dialogs/000077500000000000000000000000001434616504300255765ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/Dialogs/CallSipAddressStyle.qml000066400000000000000000000041371434616504300321740ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'CallSipAddress' property int height: 420 property int spacing: 10 property int width: 450 property QtObject videoCall: QtObject { property int iconSize: 36 property string name : 'videoCall' property string icon : 'video_call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject call: QtObject { property int iconSize: 36 property string name : 'call' property string icon : 'call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject searchField: QtObject { property color color: ColorsList.add(sectionName+'_searchField', 'c').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/Dialogs/CallTransferStyle.qml000066400000000000000000000023701434616504300317140ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'CallTransfer' property int height: 420 property int spacing: 10 property int width: 450 property QtObject transfer: QtObject { property int iconSize: 36 property string icon : 'transfer_custom' property string name : 'transfer' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject searchField: QtObject { property color color: ColorsList.add(sectionName+'_searchField', 'c').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/Dialogs/ConferenceManagerStyle.qml000066400000000000000000000046231434616504300327010ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ConferenceManager' property int height: 420 property int width: 740 property QtObject columns: QtObject { property QtObject selector: QtObject { property int spacing: 10 } property QtObject separator: QtObject { property color color: ColorsList.add(sectionName+'_separator', 'c').color property int leftMargin: 25 property int rightMargin: 25 property int width: 1 } } property QtObject transfer: QtObject { property int iconSize: 36 property string icon : 'transfer_custom' property string name : 'transfer' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject cancel: QtObject { property int iconSize: 36 property string icon : 'cancel_custom' property string name : 'cancel' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject searchField: QtObject { property color color: ColorsList.add(sectionName+'_searchField', 'c').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/Dialogs/MultimediaParametersStyle.qml000066400000000000000000000022551434616504300334540ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'MultimediaParams' property int height: 312 property int width: 450 property QtObject column: QtObject { property int spacing: 24 property QtObject entry: QtObject { property int iconSize: 24 property int spacing: 10 property int spacing2: 5 property QtObject speaker: QtObject { property int iconSize: 30 property string icon : 'speaker_on_custom' property string name : 'speaker' property color color : ColorsList.addImageColor(sectionName+'_'+name, icon, 'g').color } property QtObject micro: QtObject { property int iconSize: 30 property string icon : 'micro_on_custom' property string name : 'micro' property color color : ColorsList.addImageColor(sectionName+'_'+name, icon, 'g').color } property QtObject camera: QtObject { property int iconSize: 30 property string icon : 'camera_on_custom' property string name : 'camera' property color color : ColorsList.addImageColor(sectionName+'_'+name, icon, 'g').color } } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/IncallStyle.qml000066400000000000000000001035431434616504300271600ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Incall' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'conference_bg').color property color fullBackgroundColor: ColorsList.add(sectionName+'_fullscreen_bg', 'fullscreen_conference_bg').color property color buzyColor: ColorsList.add(sectionName+'_indicator', 'q').color property QtObject title: QtObject { property color color: ColorsList.add(sectionName+'_title', 'q').color property int pointSize: Units.dp * 12 property int addressPointSize: Units.dp * 10 } property QtObject recordWarning: QtObject{ property string icon: 'record_custom' property color iconColor: ColorsList.add(sectionName+'_warning', 'i').color property int iconSize: 30 property color color: ColorsList.add(sectionName+'_warning_fg', 'q').color property int pointSize: Units.dp * 9 } property QtObject grid: QtObject { property int spacing: 5 property QtObject cell: QtObject { property int height: 145 property int spacing: 5 property int width: 154 property QtObject contactDescription: QtObject { property color color: ColorsList.add(sectionName+'_username', 'q').color property int pointSize: Units.dp * 12 property int weight: Font.Bold } } } property QtObject pauseArea: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_paused_bg', 'l50').color property QtObject title: QtObject{ property color color: ColorsList.add(sectionName+'_paused_title', 'q').color property int pointSize: Units.dp * 12 property int weight: Font.Bold } property QtObject description: QtObject{ property color color: ColorsList.add(sectionName+'_paused_description', 'q').color property int pointSize: Units.dp * 9 property int weight: Font.Medium } property QtObject play: QtObject { property int iconSize: 240 property string icon : 'play_custom' property string name : 'paused_play' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_p_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_n_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_p_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_n_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } } property QtObject actionArea: QtObject { property int height: 100 property int iconSize: 40 property int leftButtonsGroupMargin: 50 property int lowWidth: 650 property int rightButtonsGroupMargin: 50 property QtObject userVideo: QtObject { property int height: 200 property int width: 130 property int heightReference: 1200 // height and width are fixed from these references property int widthReference: 780 } property QtObject vu: QtObject { property int spacing: 5 } property QtObject callError: QtObject { property color color: ColorsList.add(sectionName+'_action_error', 'i').color property int pointSize: Units.dp * 12 } } property QtObject container: QtObject { property int margins: 15 property QtObject avatar: QtObject { property color stickerBackgroundColor: ColorsList.add(sectionName+'_container_avatar_sticker_bg', 'conference_avatar_sticker_bg').color property color stickerPreviewBackgroundColor: ColorsList.add(sectionName+'_container_avatar_preview_sticker_bg', 'conference_avatar_preview_sticker_bg').color property color backgroundColor: ColorsList.add(sectionName+'_container_avatar_initials_bg', 'conference_avatar_initials_bg').color property int maxSize: 300 } property QtObject pause: QtObject { property color color: ColorsList.add(sectionName+'_container_pause', 'g90').color property QtObject text: QtObject { property color color: ColorsList.add(sectionName+'_container_pause_text', 'q').color property int pointSizeFactor: 5 } } } property QtObject header: QtObject { property int buttonIconSize: 40 property int iconSize: 16 property int leftMargin: 20 property int rightMargin: 20 property int spacing: 10 property int topMargin: 26 property QtObject contactDescription: QtObject { property int height: 50 property int width: 150 } property QtObject elapsedTime: QtObject { property color color: ColorsList.add(sectionName+'_header_elapsed_time', 'j').color property int pointSize: Units.dp * 10 property QtObject fullscreen: QtObject { property int pointSize: Units.dp * 12 } } property QtObject stats: QtObject { property int relativeY: 90 } property QtObject messageBanner: QtObject{ property color color: ColorsList.add(sectionName+'_message_bg', 'incall_message_banner_bg').color property color textColor: ColorsList.add(sectionName+'_message_fg', 'incall_message_banner_fg').color property int pointSize: Units.dp * 10 } } property QtObject zrtpArea: QtObject { property int height: 50 property QtObject buttons: QtObject { property int spacing: 10 } property QtObject text: QtObject { property color colorA: ColorsList.add(sectionName+'_zrtp_text_a', 'j').color property color colorB: ColorsList.add(sectionName+'_zrtp_text_b', 'i').color property int pointSize: Units.dp * 10 property int wordsSpacing: 5 } } // Button colors property QtObject buttons: QtObject { property QtObject callsList: QtObject { property int iconSize: 40 property string name : 'callsList' property string icon : 'call_menu_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject dialpad: QtObject { property int iconSize: 40 property string name : 'dialpad' property string icon : 'dialpad_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 's_p_b_fg').color } property QtObject callQuality: QtObject { property int iconSize: 40 property string name : 'quality' property string icon_0 : 'call_quality_0_custom' property string icon_1 : 'call_quality_1_custom' property string icon_2 : 'call_quality_2_custom' property string icon_3 : 'call_quality_3_custom' property string icon_4 : 'call_quality_4_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon_2, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon_2, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon_2, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon_2, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon_2, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon_2, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon_2, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon_2, 's_p_b_fg').color } property QtObject screenSharing: QtObject { property int iconSize: 40 property string icon : 'screen_sharing_custom' property string name : 'screenSharing' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject record: QtObject { property int iconSize: 40 property string icon : 'record_custom' property string name : 'record' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 's_p_b_fg').color } property QtObject screenshot: QtObject { property int iconSize: 40 property string icon : 'screenshot_custom' property string name : 'screenshot' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject fullscreen: QtObject { property int iconSize: 40 property string icon : 'fullscreen_custom' property string name : 'fullscreen' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject stopFullscreen: QtObject { property int iconSize: 40 property string icon : 'stop_fullscreen_custom' property string name : 'stopFullscreen' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } //------------------------------------------------------------------------------ property QtObject secure: QtObject { property int buttonSize: 40 property int iconSize: 20 property string icon : 'secure_on' property string name : 'secure' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, '', '', '#66727B').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, '', '', '#66727B').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, '', '', '#66727B').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, '', '', '#66727B').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 's').color } property QtObject postQuantumSecure: QtObject { property int buttonSize: 40 property int iconSize: 20 property string icon : 'secure_pq_zrtp' property string name : 'secure_pq_zrtp' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, '', '', '#66727B').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, '', '', '#66727B').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, '', '', '#66727B').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, '', '', '#66727B').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 's').color } property QtObject unsecure: QtObject { property int buttonSize: 40 property int iconSize: 20 property string icon : 'call_chat_unsecure_custom' property string name : 'unsecure' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_inv_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_inv_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_inv_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 'me_c_b_inv_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'unsecure').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'unsecure').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'unsecure').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 'unsecure').color } property QtObject microOn: QtObject { property int iconSize: 40 property string icon : 'micro_on_custom' property string name : 'microOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject microOff: QtObject { property int iconSize: 40 property string icon : 'micro_off_custom' property string name : 'microOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject speakerOn: QtObject { property int iconSize: 40 property string icon : 'speaker_on_custom' property string name : 'speakerOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject speakerOff: QtObject { property int iconSize: 40 property string icon : 'speaker_off_custom' property string name : 'speakerOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject cameraOn: QtObject { property int iconSize: 40 property string icon : 'camera_on_custom' property string name : 'cameraOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject cameraOff: QtObject { property int iconSize: 40 property string icon : 'camera_off_custom' property string name : 'cameraOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject pause: QtObject { property int iconSize: 40 property string icon : 'pause_custom' property string name : 'pause' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject play: QtObject { property int iconSize: 40 property string icon : 'play_custom' property string name : 'play' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject hangup: QtObject { property int iconSize: 40 property string icon : 'hangup_custom' property string name : 'hangup' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'r_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'r_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'r_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'r_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'r_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'r_p_b_fg').color } //------------------------------------------------------------------------------ property QtObject chat: QtObject { property int iconSize: 40 property string icon : 'chat_custom' property string name : 'chat' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_inv_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_inv_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_inv_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 'me_c_b_inv_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_inv_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_inv_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_inv_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 'me_c_b_inv_fg').color } property QtObject participants: QtObject { property int iconSize: 40 property string icon : 'participants_custom' property string name : 'participants' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_inv_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_inv_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_inv_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 'me_c_b_inv_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_inv_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_inv_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_inv_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 'me_c_b_inv_fg').color } property QtObject options: QtObject { property int iconSize: 40 property string icon : 'menu_vdots_custom' property string name : 'options' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_inv_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_inv_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_inv_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 'me_c_b_inv_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_inv_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_inv_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_inv_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 'me_c_b_inv_fg').color } //------------------------------------------------------------------------------ property QtObject closePreview: QtObject { property int iconSize: 40 property string icon : 'close_custom' property string name : 'close_preview' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_inv_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_inv_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_inv_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_inv_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_inv_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_inv_fg').color } //------------------------------------------------------------------------------ property QtObject history: QtObject { property int iconSize: 40 property string icon : 'history_custom' property string name : 'history' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject acceptVideoCall: QtObject { property int iconSize: 40 property string icon : 'video_call_accept_custom' property string name : 'videoCallAccept' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color } property QtObject acceptCall: QtObject { property int iconSize: 40 property string icon : 'call_accept_custom' property string name : 'callAccept' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Calls/WaitingRoomStyle.qml000066400000000000000000000334201434616504300302110ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'WaitingRoom' property color backgroundColor: ColorsList.add(sectionName+'_description', '', '', '#798791').color property color menuColor: ColorsList.add(sectionName+'_menu', 'r').color property QtObject title: QtObject { property color color: ColorsList.add(sectionName+'_title', 'q').color property int pointSize: Units.dp * 10 } property QtObject elapsedTime: QtObject { property color color: ColorsList.add(sectionName+'_elapsed_time', 'q').color property int pointSize: Units.dp * 10 } property QtObject callee: QtObject { property color color: ColorsList.add(sectionName+'_callee', 'q').color property int displayNamePointSize: Units.dp * 10 property int addressPointSize: Units.dp * 8 } property QtObject callError: QtObject { property color color: ColorsList.add(sectionName+'_action_error', 'q').color property int pointSize: Units.dp * 12 } property QtObject header: QtObject { property QtObject busyIndicator: QtObject { property color color: ColorsList.add(sectionName+'_header_busy', 'q').color property int height: 20 property int width: 20 } } property QtObject avatar: QtObject { property int maxSize: 300 } // Button colors property QtObject buttons: QtObject { property QtObject microOn: QtObject { property int iconSize: 40 property string icon : 'micro_on_custom' property string name : 'microOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject microOff: QtObject { property int iconSize: 40 property string icon : 'micro_off_custom' property string name : 'microOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject speakerOn: QtObject { property int iconSize: 40 property string icon : 'speaker_on_custom' property string name : 'speakerOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject speakerOff: QtObject { property int iconSize: 40 property string icon : 'speaker_off_custom' property string name : 'speakerOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject cameraOn: QtObject { property int iconSize: 40 property string icon : 'camera_on_custom' property string name : 'cameraOn' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject cameraOff: QtObject { property int iconSize: 40 property string icon : 'camera_off_custom' property string name : 'cameraOff' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject gridLayout: QtObject { property int iconSize: 40 property string icon : 'conference_layout_grid_custom' property string name : 'gridLayout' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject activeSpeakerLayout: QtObject { property int iconSize: 40 property string icon : 'conference_layout_active_speaker_custom' property string name : 'activeSpeakerLayout' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject audioOnly: QtObject { property int iconSize: 40 property string icon : 'conference_audio_only_custom' property string name : 'audioOnly' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject options: QtObject { property int iconSize: 40 property string icon : 'menu_vdots_custom' property string name : 'options' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 's_p_b_fg').color } property QtObject hangup: QtObject { property int iconSize: 40 property string icon : 'hangup_custom' property string name : 'hangup' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'r_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'r_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'r_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'r_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'r_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'r_p_b_fg').color } property QtObject call: QtObject { property int iconSize: 40 property string icon : 'call_accept_custom' property string name : 'call' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'a_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'a_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'a_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'a_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'a_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'a_p_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Dialog/000077500000000000000000000000001434616504300243555ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Dialog/NewConferenceStyle.qml000066400000000000000000000063211434616504300306340ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'NewConference' property color askEncryptionColor: ColorsList.add(sectionName+'_ask_encryption', 'g').color property color subjectTitleColor: ColorsList.add(sectionName+'_subject_title', 'g').color property color recentContactTitleColor: ColorsList.add(sectionName+'_recent_contact_title', 'g').color property color recentContactUsernameColor: ColorsList.add(sectionName+'_recent_contact_username', 'g').color property color addressesBorderColor: ColorsList.add(sectionName+'_addresses_border', 'border_light').color property color addressesAdminColor: ColorsList.add(sectionName+'_addresses_admin', 'g').color property color requiredColor: ColorsList.add(sectionName+'_required_text', 'g').color property QtObject busy: QtObject{ property color color: ColorsList.add(sectionName+'_busy', 'i').color property real pointSize: Units.dp * 10 } property QtObject titles: QtObject{ property color textColor: ColorsList.add(sectionName+'_schedule_titles', 'g').color property int weight: Font.DemiBold property real pointSize: Units.dp * 10 } property QtObject fields: QtObject{ property color textColor: ColorsList.add(sectionName+'_schedule_fields', 'g').color property int weight: Font.Medium property real pointSize: Units.dp * 9 } property QtObject addParticipant: QtObject { property int iconSize: 30 property string name : 'addParticipant' property string icon : 'add_participant_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } property QtObject removeParticipant: QtObject { property int iconSize: 30 property string name : 'removeParticipant' property string icon : 'remove_participant_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } }linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/000077500000000000000000000000001434616504300240425ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Assistant/000077500000000000000000000000001434616504300260135ustar00rootroot00000000000000ActivateAppSipAccountWithEmailStyle.qml000066400000000000000000000006321434616504300354670ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Assistantpragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ActivateApp' property int spacing: 20 property QtObject activationSteps: QtObject { property color color: ColorsList.add(sectionName+'_email_steps', 'g').color property int pointSize: Units.dp * 10 } } ActivateAppSipAccountWithPhoneNumberStyle.qml000066400000000000000000000006321434616504300366620ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Assistantpragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ActivateApp' property int spacing: 20 property QtObject activationSteps: QtObject { property color color: ColorsList.add(sectionName+'_phone_steps', 'g').color property int pointSize: Units.dp * 10 } } AssistantAbstractViewStyle.qml000066400000000000000000000014421434616504300337610ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Assistantpragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Assistant' property QtObject buttons: QtObject { property int spacing: 10 } property QtObject content: QtObject { property int height: 375+60//+button bar property int width: 400 } property QtObject info: QtObject { property int spacing: 20 property QtObject description: QtObject { property color color: ColorsList.add(sectionName+'_info_description', 'g').color property int pointSize: Units.dp * 11 } property QtObject title: QtObject { property color color: ColorsList.add(sectionName+'_info_title', 'g').color property int pointSize: Units.dp * 11 } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Assistant/AssistantHomeStyle.qml000066400000000000000000000014721434616504300323350ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Assistant' property QtObject info: QtObject { property int height: 245 property int iconSize: 150 property QtObject description: QtObject { property color color: ColorsList.add(sectionName+'_home_description', 'g').color property int pointSize: Units.dp * 10 property int height: 40 } property QtObject title: QtObject { property color color: ColorsList.add(sectionName+'_home_title', 'g').color property int pointSize: Units.dp * 11 property int height: 40 } } property QtObject buttons: QtObject { property int height: 90 property int maxWidth: 690 property int spacing: 5 } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Assistant/CreateAppSipAccountStyle.qml000066400000000000000000000005731434616504300334110ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 // ============================================================================= QtObject { property QtObject buttons: QtObject { property int spacing: 10 property QtObject button: QtObject { property int height: 40 property int width: 258 } } property QtObject busy: QtObject { property int size: 40 } } FetchRemoteConfigurationStyle.qml000066400000000000000000000021401434616504300344220ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Assistantpragma Singleton import QtQml 2.2 import ColorsList 1.0 import Units 1.0 // ============================================================================= QtObject { property string sectionName: 'FetchRemoteConfiguration' property QtObject fieldTitles: QtObject{ property int pointSize: Units.dp * 10 property color color: ColorsList.add(sectionName+'_url_title', 'j').color } property QtObject qRCode : QtObject{ property color borderColor: ColorsList.add(sectionName+'_qrcode_border', 'border_light').color } property QtObject explanationQRCode : QtObject{ property int pointSize: Units.dp * 9 property color color: ColorsList.add(sectionName+'_qrcode_text', 'j').color } property QtObject checkBox: QtObject { property int width: 300 } property QtObject warningBlock: QtObject { property int spacing: 10 property int pointSize: Units.dp * 10 property color color: ColorsList.add(sectionName+'_description', 'g').color property QtObject contactUrl: QtObject { property color color: ColorsList.add(sectionName+'_url', 'i').color property int pointSize: Units.dp * 9 } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Assistant/UseAppSipAccountStyle.qml000066400000000000000000000012001434616504300327260ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 import Units 1.0 // ============================================================================= QtObject { property string sectionName: 'UseAppSipAccount' property QtObject checkBox: QtObject { property int width: 300 } property QtObject warningBlock: QtObject { property int spacing: 10 property int pointSize: Units.dp * 10 property color color: ColorsList.add(sectionName+'_description', 'g').color property QtObject contactUrl: QtObject { property color color: ColorsList.add(sectionName+'_url', 'i').color property int pointSize: Units.dp * 9 } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/AssistantStyle.qml000066400000000000000000000007071434616504300275530ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Assistant' property color color: ColorsList.add(sectionName, 'k').color property int bottomMargin: 35 property int leftMargin: 90 property int rightMargin: 90 property int topMargin: 50 property QtObject stackAnimation: QtObject { property int duration: 400 } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/ConferencesStyle.qml000066400000000000000000000201321434616504300300260ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Conferences' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'k').color property int spacing: 20 property QtObject filter: QtObject { property string icon: 'filter_custom' property color color: ColorsList.add(sectionName+'_filter_icon', 'c').color property QtObject buttons: QtObject{ property int buttonsSpacing: 8 property QtObject button: QtObject { property QtObject color: QtObject { property color hovered: ColorsList.add(sectionName+'_button_h', 'n').color property color normal: ColorsList.add(sectionName+'_button_n', 'x').color property color pressed: ColorsList.add(sectionName+'_button_p', 'g').color property color selected: ColorsList.add(sectionName+'_button_c', 'i').color } } } } property QtObject bar: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_bar_bg', 'e').color property int height: 60 property int leftMargin: 18 property int rightMargin: 18 property QtObject text: QtObject { property int pointSize: Units.dp * 11 property color color: ColorsList.add(sectionName+'_bar_text', 'j').color } } property QtObject conference: QtObject { property string name: 'conference' property int actionButtonsSize: 36 property int avatarSize: 30 property int deleteButtonSize: 22 property int height: 50 property int leftMargin: 40 property int bottomMargin: 10 property int presenceLevelSize: 12 property int rightMargin: 25 property int spacing: 15 property QtObject backgroundColor: QtObject { property color ended: ColorsList.add(sectionName+'_conference_ended_bg', 'conference_entry_bg').color property color scheduled: ColorsList.add(sectionName+'_conference_scheduled_bg', 'e').color property color hovered: ColorsList.add(sectionName+'_conference_bg_h', 'g10').color property color cancelled: ColorsList.add(sectionName+'_conference_cancelled_bg', 'cancelled_ics_bg').color } property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_conference_border', 'f').color property int width: 1 } property QtObject selectedBorder: QtObject{ property color color: ColorsList.add(sectionName+'_conference_selected_border', 'm').color property int width: 2 } property QtObject indicator: QtObject { property color color: ColorsList.add(sectionName+'_conference_indicator', 'i').color property int width: 5 } property QtObject schedule: QtObject { property int pointSize: Units.dp * 10 property string icon : 'schedule_custom' property int iconSize: 30 property color color: ColorsList.add(sectionName+'_schedule', 'j').color } property QtObject participants: QtObject { property int pointSize: Units.dp * 10 property string icon : 'contact_custom' property int iconSize: 30 property color color: ColorsList.add(sectionName+'_participants', 'j').color } property QtObject organizer: QtObject { property color color: ColorsList.add(sectionName+'_conference_organizer', 'j').color property int pointSize: Units.dp * 10 property int width: 220 } } property QtObject sectionHeading: QtObject { property string name: 'sectionHeading' property int padding: 5 property int bottomMargin: 20 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_sectionHeading_border', 'g10').color property int width: 1 } property QtObject text: QtObject { property int pointSize: Units.dp * 10 property color color: ColorsList.add(sectionName+'_sectionHeading_text', 'ab').color } } /* property QtObject schedule: QtObject { property int iconSize: 36 property string icon : 'schedule_custom' property string name : 'schedule' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color }*/ //---------------------------------------------------------------------------------- property QtObject videoCall: QtObject { property int iconSize: 36 property string name : 'videoCall' property string icon : 'video_call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject call: QtObject { property int iconSize: 36 property string name : 'call' property string icon : 'call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject chat: QtObject { property int iconSize: 36 property string name : 'chat' property string icon : 'chat_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject deleteAction: QtObject { property int iconSize: 36 property string name : 'delete' property string icon : 'contact_delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/ContactEditStyle.qml000066400000000000000000000205161434616504300300030ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ContactEdit' property QtObject buttons: QtObject { property int spacing: 20 property int topMargin: 20 } property QtObject bar: QtObject { property color color: ColorsList.add(sectionName+'_bar', 'e').color property int avatarSize: 60 property int height: 80 property int leftMargin: 40 property int rightMargin: 30 property int spacing: 20 property QtObject actions: QtObject { property int spacing: 40 property QtObject del: QtObject { property int iconSize: 36 property QtObject colorSet: QtObject { property int iconSize: 36 property string name : 'delete' property string icon : 'contact_delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } } property QtObject edit: QtObject { property int iconSize: 36 property QtObject colorSet: QtObject { property int iconSize: 36 property string name : 'edit' property string icon : 'contact_edit_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } } property QtObject history: QtObject { property int iconSize: 40 } } property QtObject avatarTakePicture: QtObject { property int iconSize: 60 property string icon : 'contact_card_photo_custom' property string name : 'avatarTakePicture' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_h_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_n_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_h_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject buttons: QtObject { property int size: 40 property int spacing: 20 } property QtObject username: QtObject { property color color: ColorsList.add(sectionName+'_username', 'j').color property int pointSize: Units.dp * 13 } } property QtObject content: QtObject { property color color: ColorsList.add(sectionName+'_content', 'k').color } property QtObject values: QtObject { property int bottomMargin: 20 property int leftMargin: 40 property int rightMargin: 20 property int topMargin: 20 property QtObject separator: QtObject { property color color: ColorsList.add(sectionName+'_separator', 'f').color property int height: 1 } } property QtObject call: QtObject { property int iconSize: 40 property string name : 'call' property string icon : 'call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject videoCall: QtObject { property int iconSize: 40 property string name : 'videoCall' property string icon : 'video_call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject chat: QtObject { property int iconSize: 40 property string name : 'chat' property string icon : 'chat_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject history: QtObject { property int iconSize: 40 property string icon : 'history_custom' property string name : 'history' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/ContactsStyle.qml000066400000000000000000000151201434616504300273530ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Contacts' property color backgroundColor: ColorsList.add(sectionName+'_bg', 'k').color property int spacing: 20 property QtObject filter: QtObject { property string icon: 'filter_custom' property color color: ColorsList.add(sectionName+'_filter_icon', 'c').color } property QtObject bar: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_bar_bg', 'e').color property int height: 60 property int leftMargin: 18 property int rightMargin: 18 } property QtObject contact: QtObject { property int actionButtonsSize: 36 property int avatarSize: 30 property int deleteButtonSize: 22 property int height: 50 property int leftMargin: 40 property int presenceLevelSize: 12 property int rightMargin: 25 property int spacing: 15 property QtObject backgroundColor: QtObject { property color normal: ColorsList.add(sectionName+'_contact_bg_n', 'k').color property color hovered: ColorsList.add(sectionName+'_contact_bg_h', 'g10').color } property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_contact_border', 'f').color property int width: 1 } property QtObject indicator: QtObject { property color color: ColorsList.add(sectionName+'_contact_indicator', 'i').color property int width: 5 } property QtObject presence: QtObject { property int pointSize: Units.dp * 10 property color color: ColorsList.add(sectionName+'_contact_presence', 'n').color } property QtObject username: QtObject { property color color: ColorsList.add(sectionName+'_contact_username', 'j').color property int pointSize: Units.dp * 10 property int width: 220 } } property QtObject videoCall: QtObject { property int iconSize: 36 property string name : 'videoCall' property string icon : 'video_call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject call: QtObject { property int iconSize: 36 property string name : 'call' property string icon : 'call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject chat: QtObject { property int iconSize: 36 property string name : 'chat' property string icon : 'chat_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject history: QtObject { property int iconSize: 36 property string icon : 'history_custom' property string name : 'history' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color } property QtObject deleteAction: QtObject { property int iconSize: 36 property string name : 'delete' property string icon : 'contact_delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/ConversationStyle.qml000066400000000000000000000241651434616504300302600ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'Conversation' property QtObject bar: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_bar_bg', 'e').color property color searchIconColor: ColorsList.add(sectionName+'_bar_search', 'c').color property int avatarSize: 60 property int groupChatSize: 80 property string groupChatIcon: 'chat_room_custom' property color groupChatColor: ColorsList.add(sectionName+'_bar_groupChat', 'g').color property int height: 80 property int leftMargin: 40 property int rightMargin: 10 property int spacing: 20 property QtObject status: QtObject { property string adminStatusIcon: 'admin_selected_custom' property int adminStatusIconSize: 24 property color adminStatusColor: ColorsList.add(sectionName+'_bar_status', 's').color property color adminTextColor : ColorsList.add(sectionName+'_description_admin_status', 'af').color } property QtObject actions: QtObject { property int spacing: 20 property QtObject call: QtObject { property int iconSize: 40 property string name : 'call' property string icon : 'call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject chat: QtObject { property int iconSize: 40 property string name : 'chat' property string icon : 'chat_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject groupChat: QtObject { property int iconSize: 40 property string name : 'groupChat' property string icon : 'group_chat_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject videoCall: QtObject { property int iconSize: 40 property string name : 'videoCall' property string icon : 'video_call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject del: QtObject { property int iconSize: 40 property QtObject deleteHistory: QtObject { property int iconSize: 40 property string name : 'deleteHistory' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } } property QtObject edit: QtObject { property int iconSize: 40 property QtObject addContact: QtObject { property int iconSize: 40 property string name : 'addContact' property string icon : 'contact_add_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } property QtObject viewContact: QtObject { property int iconSize: 40 property string name : 'viewContact' property string icon : 'contact_view_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } } property QtObject openMenu: QtObject { property int iconSize: 40 property string name : 'other' property string icon : 'menu_vdots_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 'l_u_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 'l_u_b_fg').color } } property QtObject contactDescription : QtObject { property QtObject subtitle: QtObject { property color color: ColorsList.add(sectionName+'_description_subtitle', 'n').color property int pointSize: Units.dp * 10 property int weight: Font.Light } property QtObject title: QtObject { property color color: ColorsList.add(sectionName+'_description_title', 'j').color property int pointSize: Units.dp * 11 property int weight: Font.Normal property QtObject status : QtObject{ property color color : ColorsList.add(sectionName+'_description_title_status', 'g').color property int pointSize : Units.dp * 9 } } } } property QtObject filters: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_filters_bg', 'q').color property color iconColor: ColorsList.add(sectionName+'_filters_icon', 'c').color property int height: 51 property int leftMargin: 40 property int pointSize: Units.dp * 9 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_filters_border', 'g10').color property int bottomWidth: 1 property int topWidth: 0 } } property QtObject menu: QtObject{ property color separatorColor: ColorsList.add(sectionName+'_separator', 'u').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Dialogs/000077500000000000000000000000001434616504300254245ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Dialogs/AboutStyle.qml000066400000000000000000000022211434616504300302270ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 import Units 1.0 // ============================================================================= QtObject { property string sectionName: 'About' property int height: 255 property int spacing: 20 property int width: 400 property QtObject copyrightBlock: QtObject { property int spacing: 10 property QtObject license: QtObject { property color color: ColorsList.add(sectionName+'_license', 'd').color property int pointSize: Units.dp * 10 } property QtObject url: QtObject { property color color: ColorsList.add(sectionName+'_url', 'i').color property int pointSize: Units.dp * 10 } } property QtObject versionsBlock: QtObject { property int iconSize: 48 property int spacing: 10 property QtObject appVersion: QtObject { property color color: ColorsList.add(sectionName+'_appVersion', 'd').color property int pointSize: Units.dp * 10 } property QtObject coreVersion: QtObject { property color color: ColorsList.add(sectionName+'_coreVersion', 'd').color property int pointSize: Units.dp * 10 } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Dialogs/AuthenticationRequestStyle.qml000066400000000000000000000002671434616504300335150ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 // ============================================================================= QtObject { property int height: 416 property int width: 450 } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Dialogs/EphemeralChatRoomStyle.qml000066400000000000000000000021611434616504300325170ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'EphemeralChatRoom' property int height: 320 property int width: 450 property QtObject mainLayout: QtObject { property int topMargin: 15 property int leftMargin: 10 property int rightMargin: 10 property int spacing: 0 } property QtObject timer: QtObject { property int iconSize: 60 property int preferredHeight: 60 property int preferredWidth: 60 property string icon: 'timer_custom' property color timerColor: ColorsList.addImageColor(sectionName+'_timer', icon, 'ad').color } property QtObject descriptionText: QtObject { property int preferredWidth: 200 property int leftMargin: 10 property int rightMargin: 10 property real pointSize: Units.dp * 11 property color color: ColorsList.add(sectionName+'_popup_description', 'd').color } property QtObject timerPicker: QtObject { property int preferredWidth: 150 property int topMargin: 10 property int bottomMargin: 10 } }linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Dialogs/InfoChatRoomStyle.qml000066400000000000000000000100201434616504300315010ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'InfoChatRoom' property int height: 500 property int width: 450 property QtObject mainLayout: QtObject { property int topMargin: 15 property int leftMargin: 25 property int rightMargin: 25 property int spacing: 7 } property QtObject searchBar : QtObject{ property int topMargin : 10 } property QtObject results : QtObject{ property int topMargin : 10 property color color : ColorsList.add(sectionName+'_results', 'g').color property QtObject title : QtObject{ property int topMargin: 10 property int leftMargin: 20 property color color: ColorsList.add(sectionName+'_results_title', 'j').color property int pointSize : Units.dp * 11 property int weight : Font.DemiBold } property QtObject header: QtObject{ property int rightMargin: 55 property color color: Colors.t.color property int weight : Font.Light property int pointSize : Units.dp * 10 } } property QtObject leaveButton : QtObject { property QtObject backgroundColor: QtObject { property color disabled: ColorsList.add(sectionName+'_leave_bg_d', 'o').color property color hovered: ColorsList.add(sectionName+'_leave_bg_h', 'j').color property color normal: ColorsList.add(sectionName+'_leave_bg_n', 'k').color property color pressed: ColorsList.add(sectionName+'_leave_bg_p', 'i').color } property QtObject textColor: QtObject { property color disabled: ColorsList.add(sectionName+'_leave_text_d', 'q').color property color hovered: ColorsList.add(sectionName+'_leave_text_h', 'q').color property color normal: ColorsList.add(sectionName+'_leave_text_n', 'i').color property color pressed: ColorsList.add(sectionName+'_leave_text_p', 'q').color } property QtObject borderColor : QtObject{ property color disabled: ColorsList.add(sectionName+'_leave_border_d', 'q').color property color hovered: ColorsList.add(sectionName+'_leave_border_h', 'q').color property color normal: ColorsList.add(sectionName+'_leave_border_n', 'i').color property color pressed: ColorsList.add(sectionName+'_leave_border_p', 'q').color } } property QtObject addParticipant: QtObject { property int iconSize: 30 property string name : 'addParticipant' property string icon : 'add_participant_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } property QtObject removeParticipant: QtObject { property int iconSize: 30 property string name : 'removeParticipant' property string icon : 'remove_participant_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } }linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Dialogs/InfoEncryptionStyle.qml000066400000000000000000000030761434616504300321340ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'InfoEncryption' property int height: 353 property int width: 450 property QtObject mainLayout: QtObject { property int topMargin: 15 property int leftMargin: 10 property int rightMargin: 10 property int spacing: 0 } property QtObject okButton : QtObject{ property QtObject backgroundColor: QtObject { property color disabled: ColorsList.add(sectionName+'_ok_bg_d', 'i30').color property color hovered: ColorsList.add(sectionName+'_ok_bg_h', 'b').color property color normal: ColorsList.add(sectionName+'_ok_bg_n', 's').color property color pressed: ColorsList.add(sectionName+'_ok_bg_p', 'm').color } property QtObject textColor: QtObject { property color disabled: ColorsList.add(sectionName+'_ok_text_d', 'q').color property color hovered: ColorsList.add(sectionName+'_ok_text_h', 'q').color property color normal: ColorsList.add(sectionName+'_ok_text_n', 'q').color property color pressed: ColorsList.add(sectionName+'_ok_text_p', 'q').color } } property QtObject securityIcon: QtObject{ property int iconSize: 40 property int preferredHeight: 50 property int preferredWidth: 50 } property QtObject descriptionText: QtObject{ property int leftMargin: 10 property int rightMargin: 10 property real pointSize: Units.dp * 11 property color color: ColorsList.add(sectionName+'_description', 'd').color } }linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Dialogs/ManageAccountsStyle.qml000066400000000000000000000023431434616504300320520ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ManageAccounts' property int height: 383 property int heightWithoutPresence: 314 property int width: 450 property QtObject accountSelector: QtObject { property int height: 176 } property QtObject options: QtObject { property int iconSize: 30 property string icon : 'options_custom' property string name : 'options' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Dialogs/NewChatRoomStyle.qml000066400000000000000000000052401434616504300313470ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'NewChatRoom' property color askEncryptionColor: ColorsList.add(sectionName+'_ask_encryption', 'g').color property color subjectTitleColor: ColorsList.add(sectionName+'_subject_title', 'g').color property color recentContactTitleColor: ColorsList.add(sectionName+'_recent_contact_title', 'g').color property color recentContactUsernameColor: ColorsList.add(sectionName+'_recent_contact_username', 'g').color property color addressesBorderColor: ColorsList.add(sectionName+'_addresses_border', 'border_light').color property color addressesAdminColor: ColorsList.add(sectionName+'_addresses_admin', 'g').color property color requiredColor: ColorsList.add(sectionName+'_required_text', 'g').color property QtObject addParticipant: QtObject { property int iconSize: 30 property string name : 'addParticipant' property string icon : 'add_participant_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } property QtObject removeParticipant: QtObject { property int iconSize: 30 property string name : 'removeParticipant' property string icon : 'remove_participant_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } }linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/Dialogs/ParticipantsDevicesStyle.qml000066400000000000000000000041711434616504300331270ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'ParticipantsDevices' property color lineBackgroundColor: ColorsList.add(sectionName+'_line_bg', 'ah').color property color lineSeparatorColor: ColorsList.add(sectionName+'_separator', 'ag').color property QtObject collapsed: QtObject { property int iconSize: 30 property string name : 'collapsed' property string icon : 'collapsed_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } property QtObject expanded: QtObject { property int iconSize: 30 property string name : 'expanded' property string icon : 'expanded_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } }linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/HistoryViewStyle.qml000066400000000000000000000113771434616504300301030ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'HistoryView' property QtObject bar: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_bar_bg', 'e').color property int avatarSize: 60 property int height: 80 property int leftMargin: 40 property int rightMargin: 30 property int spacing: 20 property QtObject actions: QtObject { property int spacing: 40 property QtObject call: QtObject { property int iconSize: 40 } property QtObject del: QtObject { property int iconSize: 40 } property QtObject edit: QtObject { property int iconSize: 40 } } property QtObject description: QtObject { property color subtitleColor: ColorsList.add(sectionName+'_bar_description_subtitle', 'g').color property color titleColor: ColorsList.add(sectionName+'_bar_description_title', 'j').color } } property QtObject filters: QtObject { property color backgroundColor: ColorsList.add(sectionName+'_filters_bg', 'q').color property int height: 51 property int leftMargin: 40 property QtObject border: QtObject { property color color: ColorsList.add(sectionName+'_filters_border', 'g10').color property int bottomWidth: 1 property int topWidth: 0 } } property QtObject videoCall: QtObject { property int iconSize: 40 property string name : 'videoCall' property string icon : 'video_call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject call: QtObject { property int iconSize: 40 property string name : 'call' property string icon : 'call_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject deleteAction: QtObject { property int iconSize: 40 property string name : 'delete' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } property QtObject chat: QtObject { property int iconSize: 40 property string name : 'chat' property string icon : 'chat_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/HomeStyle.qml000066400000000000000000000004331434616504300264660ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'Home' property color color: ColorsList.add(sectionName+'_bg', 'k').color property int spacing: 20 } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/InviteFriendsStyle.qml000066400000000000000000000007221434616504300303500ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'InviteFriends' property color color: ColorsList.add(sectionName+'_bg', 'k').color property int width: 400 property QtObject message: QtObject { property int height: 140 } property QtObject buttons: QtObject { property int bottomMargin: 35 property int spacing: 10 } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Main/MainWindowStyle.qml000066400000000000000000000171431434616504300276600ustar00rootroot00000000000000pragma Singleton import QtQuick 2.7 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'MainWindow' property int menuBurgerSize: 40 property int newConferenceSize: 40 property int minimumHeight: 610 property int minimumWidth: 950 property int width: 950 property int panelButtonSize : 20 property int homeButtonSize: 40 property QtObject accountStatus: QtObject { property int width: 200 } property QtObject autoAnswerStatus: QtObject { property int iconSize: 16 property int width: 28 property QtObject text: QtObject { property int pointSize: Units.dp * 8 property color color: ColorsList.add(sectionName+'_auto_answer_text', 'i').color } } property QtObject menu: QtObject { property int height: 50 property int width: 250 property QtObject direction: QtObject { property string icon: 'panel_arrow_custom' property int iconSize: 30 } property QtObject contacts: QtObject { property string icon: 'contact_custom' property int iconSize: 50 property color color: ColorsList.add(sectionName+'_me_contacts', 'me_n_b_inv_fg').color property color selectedColor: ColorsList.add(sectionName+'_me_contacts_c', 'me_p_b_inv_fg').color } property QtObject conferences: QtObject { property string icon: 'meetings_custom' property int iconSize: 50 property color color: ColorsList.add(sectionName+'_me_conferences', 'me_n_b_inv_fg').color property color selectedColor: ColorsList.add(sectionName+'_me_conferences_c', 'me_p_b_inv_fg').color } /* property string conferencesIcon: 'conference' property color conferencesColor: ColorsList.add(sectionName+'_me_confs', 'me_n_b_inv_fg').color property color conferencesSelectedColor: ColorsList.add(sectionName+'_me_confs_selected', 'me_p_b_inv_fg').color*/ } property QtObject searchBox: QtObject { property int maxHeight: 300 // See Hick's law for good choice. } property QtObject toolBar: QtObject { property int height: 70 property int leftMargin: 18 property int rightMargin: 18 property int spacing: 16 property var background: Rectangle { color: ColorsList.add(sectionName+'_toolbar_bg', 'f').color } } property QtObject buttons: QtObject { property QtObject home: QtObject { property int iconSize: 40 property string name : 'home' property string icon : 'home_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'ma_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'ma_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'ma_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'ma_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'ma_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'ma_p_b_fg').color } property QtObject telKeyad: QtObject { property int iconSize: 40 property string name : 'telKeypad' property string icon : 'dialpad_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 'l_u_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 'l_u_b_fg').color } property QtObject newChatGroup: QtObject { property int iconSize: 40 property string name : 'newChatGroup' property string icon : 'new_chat_group_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'ma_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'ma_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'ma_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'ma_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'ma_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'ma_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'ma_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'ma_d_b_fg').color } property QtObject newConference: QtObject { property int iconSize: 40 property string name : 'newConference' property string icon : 'conference_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'ma_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'ma_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'ma_p_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'ma_d_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'ma_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'ma_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'ma_p_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'ma_d_b_fg').color } property QtObject burgerMenu: QtObject { property int iconSize: 40 property string name : 'burgerMenu' property string icon : 'burger_menu_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 'l_u_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 'l_u_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Settings/000077500000000000000000000000001434616504300247565ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Settings/Dialogs/000077500000000000000000000000001434616504300263405ustar00rootroot00000000000000SettingsSipAccountsEditStyle.qml000066400000000000000000000002671434616504300346040ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Settings/Dialogspragma Singleton import QtQml 2.2 // ============================================================================= QtObject { property int height: 550 property int width: 934 } SettingsVideoPreviewStyle.qml000066400000000000000000000002671434616504300341530ustar00rootroot00000000000000linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Settings/Dialogspragma Singleton import QtQml 2.2 // ============================================================================= QtObject { property int height: 480 property int width: 640 } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Settings/SettingsAdvancedStyle.qml000066400000000000000000000076771434616504300317610ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'SettingsAdvanced' property QtObject buttons: QtObject { property int spacing: 10 } property QtObject error: QtObject { property color color: ColorsList.add(sectionName+'_error', 'error').color } property QtObject info: QtObject { property color color: ColorsList.add(sectionName+'_info', 'j').color property int pointSize: Units.dp * 11 } property QtObject lists: QtObject { property int spacing: 20 property real iconScale : 0.8 property int margin: 10 } property QtObject add: QtObject { property int iconSize: 35 property string name : 'add' property string icon : 'add_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'me_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'me_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject cancel: QtObject { property int iconSize: 35 property string icon : 'cancel_custom' property string name : 'cancel' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject options: QtObject { property int iconSize: 35 property string icon : 'options_custom' property string name : 'options' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Settings/SettingsAudioStyle.qml000066400000000000000000000054521434616504300313020ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'SettingsAudio' property color sliderBackgroundColor: ColorsList.add(sectionName+'_slider_bg', 'slider_bg').color property color sliderHighColor: ColorsList.add(sectionName+'_slider_high', 'slider_high').color property color sliderLowColor: ColorsList.add(sectionName+'_slider_low', 'slider_low').color property QtObject ringPlayer: QtObject { property int leftMargin: 10 } property QtObject warningMessage: QtObject { property int iconSize: 20 } property QtObject pause: QtObject { property int iconSize: 40 property string icon : 'pause_custom' property string name : 'pause' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } property QtObject play: QtObject { property int iconSize: 40 property string icon : 'play_custom' property string name : 'play' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_n_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 's_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Settings/SettingsUiStyle.qml000066400000000000000000000021061434616504300306070ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import Units 1.0 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName : 'SettingsUi' property QtObject options: QtObject { property int iconSize: 25 property string icon : 'options_custom' property string name : 'options' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/Settings/SettingsWindowStyle.qml000066400000000000000000000117721434616504300315120ustar00rootroot00000000000000pragma Singleton import QtQml 2.2 import ColorsList 1.0 // ============================================================================= QtObject { property string sectionName: 'SettingsWindow' property color color: ColorsList.add(sectionName+'_bg', 'k').color property int height: 640 property int width: 1024 property QtObject forms: QtObject { property int spacing: 10 } property QtObject validButton: QtObject { property int bottomMargin: 30 property int rightMargin: 30 property int topMargin: 30 } property QtObject sipAccounts: QtObject { property int buttonsSpacing: 8 property int iconSize: 22 property int legendLineWidth: 280 } property QtObject video: QtObject { property QtObject warningMessage: QtObject { property int iconSize: 20 } } property QtObject buttons: QtObject { property QtObject editProxy: QtObject { property int iconSize: 36 property string name : 'editProxy' property string icon : 'edit_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject deleteProxy: QtObject { property int iconSize: 36 property string name : 'deleteProxy' property string icon : 'delete_custom' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'me_n_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'me_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'me_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'me_n_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'me_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'me_p_b_fg').color } property QtObject back: QtObject { property int iconSize: 35 property string icon : 'back_custom' property string name : 'back' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 's_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 's_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg').color } property QtObject copy: QtObject { property int iconSize: 30 property string icon : 'menu_copy_text_custom' property string name : 'copy' property color backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 'l_n_b_bg').color property color backgroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_d', icon, 'l_d_b_bg').color property color backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 'l_h_b_bg').color property color backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 'l_p_b_bg').color property color backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_u', icon, 'l_p_b_bg').color property color foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 'l_n_b_fg').color property color foregroundDisabledColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_d', icon, 'l_d_b_fg').color property color foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 'l_h_b_fg').color property color foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 'l_p_b_fg').color property color foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_u', icon, 'l_p_b_fg').color } } } linphone-desktop-5.0.2/linphone-app/ui/views/App/Styles/qmldir000066400000000000000000000103101434616504300243640ustar00rootroot00000000000000# See: https://wiki.qt.io/Qml_Styling module App.Styles # Views styles ================================================================= # Calls Window ----------------------------------------------------------------- singleton CallFullscreenStyle 1.0 Calls/CallFullscreenStyle.qml singleton CallStyle 1.0 Calls/CallStyle.qml singleton CallsWindowStyle 1.0 Calls/CallsWindowStyle.qml singleton WaitingRoomStyle 1.0 Calls/WaitingRoomStyle.qml singleton ConferenceStyle 1.0 Calls/ConferenceStyle.qml singleton IncallStyle 1.0 Calls/IncallStyle.qml singleton CallSipAddressStyle 1.0 Calls/Dialogs/CallSipAddressStyle.qml singleton CallTransferStyle 1.0 Calls/Dialogs/CallTransferStyle.qml singleton ConferenceManagerStyle 1.0 Calls/Dialogs/ConferenceManagerStyle.qml # Dialog Window ------------------------------------------------------------------ singleton NewConferenceStyle 1.0 Dialog/NewConferenceStyle.qml # Main Window ------------------------------------------------------------------ singleton ActivateAppSipAccountWithEmailStyle 1.0 Main/Assistant/ActivateAppSipAccountWithEmailStyle.qml singleton ActivateAppSipAccountWithPhoneNumberStyle 1.0 Main/Assistant/ActivateAppSipAccountWithPhoneNumberStyle.qml singleton AssistantAbstractViewStyle 1.0 Main/Assistant/AssistantAbstractViewStyle.qml singleton AssistantHomeStyle 1.0 Main/Assistant/AssistantHomeStyle.qml singleton CreateAppSipAccountStyle 1.0 Main/Assistant/CreateAppSipAccountStyle.qml singleton FetchRemoteConfigurationStyle 1.0 Main/Assistant/FetchRemoteConfigurationStyle.qml singleton UseAppSipAccountStyle 1.0 Main/Assistant/UseAppSipAccountStyle.qml singleton AssistantStyle 1.0 Main/AssistantStyle.qml singleton ConferencesStyle 1.0 Main/ConferencesStyle.qml singleton ContactEditStyle 1.0 Main/ContactEditStyle.qml singleton ContactsStyle 1.0 Main/ContactsStyle.qml singleton ConversationStyle 1.0 Main/ConversationStyle.qml singleton HomeStyle 1.0 Main/HomeStyle.qml singleton HistoryViewStyle 1.0 Main/HistoryViewStyle.qml singleton InviteFriendsStyle 1.0 Main/InviteFriendsStyle.qml singleton MainWindowStyle 1.0 Main/MainWindowStyle.qml singleton AboutStyle 1.0 Main/Dialogs/AboutStyle.qml singleton AuthenticationRequestStyle 1.0 Main/Dialogs/AuthenticationRequestStyle.qml singleton EphemeralChatRoomStyle 1.0 Main/Dialogs/EphemeralChatRoomStyle.qml singleton InfoChatRoomStyle 1.0 Main/Dialogs/InfoChatRoomStyle.qml singleton InfoEncryptionStyle 1.0 Main/Dialogs/InfoEncryptionStyle.qml singleton ManageAccountsStyle 1.0 Main/Dialogs/ManageAccountsStyle.qml singleton NewChatRoomStyle 1.0 Main/Dialogs/NewChatRoomStyle.qml singleton ParticipantsDevicesStyle 1.0 Main/Dialogs/ParticipantsDevicesStyle.qml # Settings Window -------------------------------------------------------------- singleton SettingsAdvancedStyle 1.0 Settings/SettingsAdvancedStyle.qml singleton SettingsAudioStyle 1.0 Settings/SettingsAudioStyle.qml singleton SettingsUiStyle 1.0 Settings/SettingsUiStyle.qml singleton SettingsWindowStyle 1.0 Settings/SettingsWindowStyle.qml singleton SettingsSipAccountsEditStyle 1.0 Settings/Dialogs/SettingsSipAccountsEditStyle.qml singleton SettingsVideoPreviewStyle 1.0 Settings/Dialogs/SettingsVideoPreviewStyle.qml linphone-desktop-5.0.2/linphone-app/ui/views/App/qmldir000066400000000000000000000005331434616504300231070ustar00rootroot00000000000000# ============================================================================== # App's components to export. # ============================================================================== module App # Components ------------------------------------------------------------------- NewConference 1.0 Dialog/NewConference.qml linphone-desktop-5.0.2/linphone-app/webview_resources.qrc000066400000000000000000000002011434616504300236430ustar00rootroot00000000000000 ui/views/App/Main/Assistant/CreateAppSipAccountWithWebView.qml linphone-desktop-5.0.2/plugins/000077500000000000000000000000001434616504300164705ustar00rootroot00000000000000linphone-desktop-5.0.2/plugins/CMakeLists.txt000066400000000000000000000031001434616504300212220ustar00rootroot00000000000000################################################################################ # # Copyright (c) 2017-2020 Belledonne Communications SARL. # # This file is part of linphone-desktop # (see https://www.linphone.org). # # 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 . # ################################################################################ cmake_minimum_required(VERSION 3.1) project(app-plugins) ## Add custom plugins macro(get_all_subdirs result curdir) file(GLOB children RELATIVE ${curdir} ${curdir}/*) set(dirlist "") foreach(child ${children}) if(IS_DIRECTORY ${curdir}/${child} AND (ENABLE_BUILD_EXAMPLES OR NOT ${child} MATCHES "example")) list(APPEND dirlist ${child}) endif() endforeach() set(${result} ${dirlist}) endmacro() get_all_subdirs(SUBDIRS ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};${CMAKE_INSTALL_PREFIX}/include") foreach(subdir ${SUBDIRS}) message("Adding ${subdir} plugin") add_subdirectory(${subdir}) endforeach() linphone-desktop-5.0.2/plugins/example/000077500000000000000000000000001434616504300201235ustar00rootroot00000000000000linphone-desktop-5.0.2/plugins/example/CMakeLists.txt000066400000000000000000000140241434616504300226640ustar00rootroot00000000000000################################################################################ # # Copyright (c) 2017-2020 Belledonne Communications SARL. # # This file is part of linphone-desktop # (see https://www.linphone.org). # # 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 . # ################################################################################ cmake_minimum_required(VERSION 3.1) #------------------------------------------------- # Customizable data set(SOURCES src/Plugin.cpp src/NetworkAPI.cpp src/DataAPI.cpp ) set(HEADERS src/Plugin.hpp src/NetworkAPI.hpp src/DataAPI.hpp ) list(APPEND SOURCES src/PluginMetaData.json) set(TARGET_NAME linphonePluginExample ) #------------------------------------------------- find_package(bctoolbox CONFIG) set(FULL_VERSION ) bc_compute_full_version(FULL_VERSION) set(version_major ) set(version_minor ) set(version_patch ) set(identifiers ) set(metadata ) bc_parse_full_version("${FULL_VERSION}" version_major version_minor version_patch identifiers metadata) set(PLUGIN_VERSION "${version_major}.${version_minor}.${version_patch}") project(${TARGET_NAME} VERSION ${PLUGIN_VERSION}) include(GNUInstallDirs) include(CheckCXXCompilerFlag) message("${TARGET_NAME} version : ${PLUGIN_VERSION}") set(CMAKE_CXX_STANDARD 11) SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS true) if(UNIX AND NOT APPLE) set(CMAKE_INSTALL_RPATH "$ORIGIN;$ORIGIN/lib64;$ORIGIN/../lib64;$ORIGIN/lib;$ORIGIN/../lib") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake") list(APPEND CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/include) if(WIN32) set(EXECUTABLE_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_DIR} ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_DIR} ) set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_DIR} ) foreach( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} )# Apply to all configurations string( TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG ) set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${EXECUTABLE_OUTPUT_DIR} ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${EXECUTABLE_OUTPUT_DIR} ) set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${EXECUTABLE_OUTPUT_DIR} ) endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) endif() # Build configuration set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG -DQT_NO_DEBUG") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG -DQT_QML_DEBUG -DQT_DECLARATIVE_DEBUG" ) if( WIN32) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WINSOCKAPI_")#remove error from windows headers order endif() set(CMAKE_INCLUDE_CURRENT_DIR ON)#useful for config.h set(CMAKE_AUTOMOC ON) find_package(Qt5 COMPONENTS ${QT5_PACKAGES_OPTIONAL} QUIET) find_package(LinphoneCxx CONFIG) find_package(bctoolbox CONFIG) find_library(APP_PLUGIN_LIBRARY NAMES "app-plugin" PATHS "${APP_PLUGIN_LIB_PATH}" REQUIRED) # ------------------------------------------------------------------------------ # Build. # ------------------------------------------------------------------------------ add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS}) target_link_libraries(${TARGET_NAME} PRIVATE ${LINPHONECXX_LIBRARIES} ${APP_PLUGIN_LIBRARY}) target_compile_options(${TARGET_NAME} PRIVATE ${COMPILE_OPTIONS}) set_source_files_properties( ${TARGET_NAME} PROPERTIES EXTERNAL_OBJECT true GENERATED true ) set_property(TARGET ${TARGET_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON) #Need by Qt target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_PREFIX_PATH} ${LINPHONECXX_INCLUDE_DIRS}) set_target_properties(${TARGET_NAME} PROPERTIES OUTPUT_NAME "${TARGET_NAME}-${PLUGIN_VERSION}") # Qt stuffs set(QT5_PACKAGES Gui Core Widgets Network) find_package(Qt5 COMPONENTS ${QT5_PACKAGES} REQUIRED) foreach (package ${QT5_PACKAGES}) list(APPEND QT_INCLUDED_DIRECTORIES "${Qt5${package}_INCLUDE_DIRS}") list(APPEND QT_ALL_LIBRARIES ${Qt5${package}_LIBRARIES}) if(APPLE)# On Mac, shared libs can be generated with absolute path on Qt libs. This lead to get undeployable shared library. add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND install_name_tool -change "/usr/local/opt/qt/lib/Qt${package}.framework/Versions/5/Qt${package}" "@rpath/Qt${package}.framework/Versions/5/Qt${package}" $) endif() endforeach () target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QT_INCLUDED_DIRECTORIES}) target_link_libraries(${TARGET_NAME} PUBLIC ${QT_ALL_LIBRARIES}) if(APPLE) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" $) add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND install_name_tool -add_rpath "@executable_path/../lib/" $) endif() #------------------------------------------------------------------------------- # IDE #------------------------------------------------------------------------------- source_group( "Json" REGULAR_EXPRESSION ".+\.json$" ) #------------------------------------------------------------------------------- # Install. #------------------------------------------------------------------------------- set(LINPHONE_APP_CONTACT_PLUGINS_PATH "plugins/app") install(TARGETS ${TARGET_NAME} DESTINATION "${LINPHONE_APP_CONTACT_PLUGINS_PATH}") linphone-desktop-5.0.2/plugins/example/src/000077500000000000000000000000001434616504300207125ustar00rootroot00000000000000linphone-desktop-5.0.2/plugins/example/src/DataAPI.cpp000066400000000000000000000125031434616504300226220ustar00rootroot00000000000000#include "DataAPI.hpp" #include "NetworkAPI.hpp" #include "Plugin.hpp" #include #include #include DataAPI::DataAPI(Plugin *plugin, void * core, QPluginLoader * pluginLoader) :PluginDataAPI(plugin, core, pluginLoader){ auto proxyConfig = static_cast(mLinphoneCore)->getDefaultProxyConfig(); QVariantMap account; std::string domain; if(proxyConfig) domain = proxyConfig->getDomain(); else{ proxyConfig = static_cast(mLinphoneCore)->createProxyConfig(); if(proxyConfig) domain = proxyConfig->getDomain(); if(domain == "") domain = "sip.linphone.org"; } mInputFields[CONTACTS]["SIP_Domain"] = QString::fromLocal8Bit(domain.c_str(), int(domain.size())); } QString DataAPI::getUrl()const{ return mInputFields[CONTACTS]["URL"].toString(); } QString DataAPI::getDomain()const{ return mInputFields[CONTACTS]["SIP_Domain"].toString(); } QString DataAPI::getUsername()const{ return mInputFields[CONTACTS]["Username"].toString(); } QString DataAPI::getPassword()const{ return mInputFields[CONTACTS]["Password"].toString(); } QString DataAPI::getKey()const{ return mInputFields[CONTACTS]["Key"].toString(); } bool DataAPI::isEnabled()const{ return mInputFields[CONTACTS]["enabled"].toInt()>0; } void DataAPI::setPassword(const QString &password){ mInputFields[CONTACTS]["Password"] = password; } bool DataAPI::isValid(const bool &pRequestData, QString * pError){ QStringList errors; if( getDomain().isEmpty()) errors << "Domain is empty."; if( getUrl().isEmpty()) errors << "Url is empty."; if( getUsername().isEmpty()) errors << "Username is empty."; if( getPassword().isEmpty() && getKey().isEmpty()){ if(pRequestData) setPassword(QInputDialog::getText(nullptr, "Linphone example Address Book","Password",QLineEdit::EchoMode::Password)); if( getPassword().isEmpty()) errors << "Password is empty."; } if( errors.size() > 0){ if(pError) *pError = "Data is invalid : " + errors.join(" "); return false; }else return true; } QMap DataAPI::getInputFieldsToSave(const PluginCapability& capability){// Remove Password from config file QMap data = mInputFields; data[CONTACTS].remove("Password"); return data; } void DataAPI::run(const PluginCapability& actionType){ if( actionType == PluginCapability::CONTACTS){ NetworkAPI * network = new NetworkAPI(this); QObject::connect(this, &PluginDataAPI::dataReceived, network, &DataAPI::deleteLater); network->startRequest(); } } //----------------------------------------------------------------------------------------- void DataAPI::parse(const QByteArray& p_data){ QVector > parsedData; QString statusText; if(!p_data.isEmpty()) { QJsonDocument doc = QJsonDocument::fromJson(p_data); QJsonObject responses = doc.object(); QString status = responses["status"].toString(); QString comment = responses["comment"].toString(); if( responses.size() == 0){ statusText = "Contacts are not in Json format."; }else if( status != "OK"){ statusText = status; if( statusText.isEmpty()) statusText = "Cannot parse the request: The URL may not be valid."; if(!comment.isEmpty()) statusText += " "+comment; if( mInputFields[CONTACTS].contains("Key")){ QVariantMap newInputs = mInputFields[CONTACTS]; newInputs.remove("Key");// Reset key on error setInputFields(CONTACTS, newInputs); } }else{ if( responses.contains("key")){ QVariantMap newInputs = mInputFields[CONTACTS]; newInputs["Key"] = responses["key"].toString(); setInputFields(CONTACTS, newInputs); } if( responses.contains("contacts")){ QJsonArray contacts = responses["contacts"].toArray(); int contactCount = 0; for(int i = 0 ; i < contacts.size() ; ++i){ QMultiMap cardData; QJsonObject contact = contacts[i].toObject(); QString phoneNumber = contact["number"].toString(); QStringList name; bool haveData = false; QString company = contact["company"].toString(); if( contact.contains("firstname") && contact["firstname"].toString() != "") name << contact["firstname"].toString(); if( contact.contains("surname") && contact["surname"].toString() != "") name << contact["surname"].toString(); if(name.size() > 0){ QString username = name.join(" "); cardData.insert("displayName", username); } if(!phoneNumber.isEmpty()) { cardData.insert("phoneNumber", phoneNumber); cardData.insert("sipUsername", phoneNumber); haveData = true; } if(!company.isEmpty()) cardData.insert("organization", company); if( haveData){ cardData.insert("sipDomain", mInputFields[CONTACTS]["SIP_Domain"].toString()); parsedData.push_back(cardData); ++contactCount; } } QString messageStatus = QString::number(contactCount) +" contact"+(contactCount>1?"s":"")+" have been synchronized at "+QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); emit message(QtInfoMsg, messageStatus); qInfo() << messageStatus; } } }else statusText = "Cannot parse the request: The URL may not be valid."; if( !statusText.isEmpty()) emit message(QtWarningMsg, statusText); emit dataReceived(PluginDataAPI::CONTACTS, parsedData); } linphone-desktop-5.0.2/plugins/example/src/DataAPI.hpp000066400000000000000000000021501434616504300226240ustar00rootroot00000000000000#ifndef DATAAPI_HPP #define DATAAPI_HPP #include #include #include #include #include class Plugin; class QPluginLoader; // Example of address book importer class DataAPI : public PluginDataAPI { Q_OBJECT public: DataAPI(Plugin *plugin, void *core, QPluginLoader * pluginLoader); virtual ~DataAPI(){} QString getUrl()const; QString getDomain()const; QString getUsername()const; QString getPassword()const; QString getKey()const; bool isEnabled()const; void setPassword(const QString &password); virtual bool isValid(const bool &requestData, QString * pError= nullptr);// Test data and send signal. Used to get feedback virtual QMap getInputFieldsToSave(const PluginCapability& capability); virtual void run(const PluginCapability& actionType); public slots: virtual void parse(const QByteArray& p_data); signals: void inputFieldsChanged(const PluginCapability& capability, const QVariantMap &inputs); // The plugin made updates on input }; #endif // DATAAPI_HPP linphone-desktop-5.0.2/plugins/example/src/NetworkAPI.cpp000066400000000000000000000025571434616504300234120ustar00rootroot00000000000000#include "NetworkAPI.hpp" #include "DataAPI.hpp" #include #include NetworkAPI::NetworkAPI(DataAPI * data) : mData(data){ if(mData ) { connect(this, SIGNAL(requestFinished(const QByteArray&)), mData, SLOT(parse(const QByteArray&))); connect(this, &NetworkAPI::message, mData, &DataAPI::message); } } NetworkAPI::~NetworkAPI(){ } bool NetworkAPI::isEnabled()const{ return mData && mData->isEnabled(); } bool NetworkAPI::isValid(PluginDataAPI * pData, const bool &pShowError){ QString errorMessage; DataAPI * data = dynamic_cast(pData); bool ok = data; if(!ok) errorMessage = "These data are invalid"; else ok = pData->isValid(true, &errorMessage); if(!ok && pShowError){ qWarning() << errorMessage; emit message(QtMsgType::QtWarningMsg, errorMessage); } return ok; } //----------------------------------------------------------------------------------------- QString NetworkAPI::prepareRequest()const{ QString url = mData->getUrl()+"?user="+mData->getUsername()+"&"; if( mData->getKey() != "") url += "key="+mData->getKey(); else url += "password="+mData->getPassword(); return url; } void NetworkAPI::startRequest() { bool doRequest = false; if(isValid(mData)){ if(isEnabled()){ mCurrentStep=0; doRequest = true; } } if(doRequest) request(); else mData->parse(QByteArray()); } linphone-desktop-5.0.2/plugins/example/src/NetworkAPI.hpp000066400000000000000000000012251434616504300234060ustar00rootroot00000000000000#ifndef NETWORKAPI_HPP #define NETWORKAPI_HPP #include #include #include class DataAPI; class PluginDataAPI; // Interface between Network API and Data. class NetworkAPI : public PluginNetworkHelper { Q_OBJECT public: NetworkAPI(DataAPI * data); virtual ~NetworkAPI(); bool isEnabled()const; // Interface to test if data is enabled bool isValid(PluginDataAPI * pData, const bool &pShowError = true);// Test if data is valid virtual QString prepareRequest()const;// Prepare request for URL void startRequest(); // Data DataAPI * mData; int mCurrentStep; }; #endif // NETWORKAPI_HPP linphone-desktop-5.0.2/plugins/example/src/Plugin.cpp000066400000000000000000000046651434616504300226670ustar00rootroot00000000000000/****************************************************************************** * * Copyright (c) 2017-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . * *******************************************************************************/ #include "Plugin.hpp" #include #include #include "DataAPI.hpp" #include "NetworkAPI.hpp" QString Plugin::getGUIDescriptionToJson()const{ QJsonObject description; description["pluginTitle"] = "Plugin Example"; description["pluginDescription"] = "This is a test plugin to import an address book from an URL"; QJsonObject field; QJsonArray fields; field["placeholder"] = "SIP Domain"; field["fieldId"] = "SIP_Domain"; field["defaultData"] = ""; // Set by the Data instance from Core field["type"] = 1; field["capability"] = PluginDataAPI::CONTACTS; fields.append(field); field = QJsonObject(); field["placeholder"] = "URL"; field["fieldId"] = "URL"; field["defaultData"] = ""; field["type"] = 1; field["capability"] = PluginDataAPI::CONTACTS; fields.append(field); field = QJsonObject(); field["placeholder"] = "Username"; field["fieldId"] = "Username"; field["defaultData"] = "username@domain.com"; field["type"] = 1; field["capability"] = PluginDataAPI::CONTACTS; fields.append(field); field = QJsonObject(); field["placeholder"] = "Password"; field["fieldId"] = "Password"; field["defaultData"] = "This is a pass"; field["type"] = 1; field["hiddenText"] = true; field["capability"] = PluginDataAPI::CONTACTS; fields.append(field); description["fields"] = fields; QJsonDocument document(description); return document.toJson(); } PluginDataAPI * Plugin::createInstance(void * core, QPluginLoader *pluginLoader){ return new DataAPI(this, core, pluginLoader); } linphone-desktop-5.0.2/plugins/example/src/Plugin.hpp000066400000000000000000000027671434616504300226750ustar00rootroot00000000000000/****************************************************************************** * * Copyright (c) 2017-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop * (see https://www.linphone.org). * * 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 . * *******************************************************************************/ #ifndef PLUGIN_HPP #define PLUGIN_HPP #include #include #include #include #include //----------------------------- class QPluginLoader; class Plugin : public QObject, public LinphonePlugin { Q_OBJECT Q_PLUGIN_METADATA(IID LinphonePlugin_iid FILE "PluginMetaData.json") Q_INTERFACES(LinphonePlugin) public: Plugin(){} virtual QString getGUIDescriptionToJson() const; virtual PluginDataAPI * createInstance(void* core, QPluginLoader * pluginLoader); }; #endif linphone-desktop-5.0.2/plugins/example/src/PluginMetaData.json000066400000000000000000000004031434616504300244410ustar00rootroot00000000000000{ "ID" : "ExamplePlugin. This ID must be unique from all plugins.", "Version" : "1.0.0", "Capabilities" : "Contacts", "Description" : "This is an example for describing your plugin. Replace all fields above to be usable by the Application." } linphone-desktop-5.0.2/readme_screen.png000066400000000000000000003220421434616504300203140ustar00rootroot00000000000000PNG  IHDRD^sBIT|d IDATxwxStz/-۲d0ŦdndSvL&B!Bo+cp.]ƍ?%B!B Hmm-v159tpzHpj6l0%B!B ġCu}b&۷osvلB!ₑH$4 x%]7Kw s* ?3B!B,q.ƻ(,_/|`ܹb[uh=7tXB!BwD"1Ex@HN2ܺ ݱlg9B!J 4^/zB!B[H A}#Q 씈W!B`Yͮk@Jha4歅c/amiB!B[$@3 !##`08}Sqj(*oT 1j`wB!B m[3NJ}}='//Lm{uܵ^;=nn\_Eߣqϴ/L!8:Z>y衯ZB!L̖$jkkȑv(SO=@n*<'=[֌raٲelܸ˗_<⮝ˑ#G;IJ,zzz;.knKg VLwiZ!+x32.iB1nV^Ê+xgGvO4"<B2M˚Um9r;HΓO>A"a{6:# m۞PYYIyy9y<u]dddEeʖO;Swi a3'G07TmOnghOO8X"^!Sq?#4M#28N~`| 㜙<Bqf:upÇ뮻G]<ρѵegpWzM} _Ķi]0OII ?Ї$''{EQHII'x|> L~TJ$P*V7m§?!^!S1{]]=^{-w}7@ 6F[[| Ȇ Z !xgj?BNNƮp㏓~<--msUW۷i+#f n+ܹsys= @WW?sJKKHQ* c_ch"7Kαv xJׁ'iΉ瑗%/ykk=> PSS=K/ΝF[p!uuu<7xx챟24h$ݽ;k_:7p,Y~TWW9^xyկ:$}}}A=K^򒗼·if/˲b455c2ر&q۶׹x-.dt^hUUt]GUUg-}mTwfUsa nEo<E]]x |_?)?я(**+Wd]'MO?P@ !,§?&Ñ#z3N?zZ*>)/_INN<أ̝;pv+Wm)>nϕ^W~:ux wFo;4Wk ^{xܝj Ӿ`!>q~SŢbQRR҈ŢÃx>ݻ+￟KiPUut!>ׇ(ضM4!i)z}6Rn,/_>yu' ^h!'~.Ė-[N/?yNpjVZ޽{).CFucg/^yML1Y;Oqño [JSav?+^l _YSƒ,ʌJiq| *JQ0}F'Je˯Fm;}qO0e$ko+o!.hlf(B!x #{{{xiF;fxJK+4IKKb!],--mL |.pVP~"-ӛ>9hj|а7(c݆lK;>@kKq@srI1C6HΧ(ǔ!k~8s4]ex[vR>o̓#'A{ M1, C}ČdsFv|)hoy@#뉢S1z>+JOODff`'z38w:tX%ݘB1=@0B]xw6GNϲ8w>T?=[ |귾IEcצm/ӵFY\z=]~6lⱗ"6"eSTKO{}>իԍ\_/I ##$~-B!9;" ۧ'fꬶ/=t.dT1KSKplx$b4-CtDa c9t<U =ƁV]0w\uU?|3 ۸1O^?^~qkx1z߳J4U?kuo v sbiUB!b_ E+ey0 w`^zeVNucEl@1{5~ O>;8Lڤi+Chp4j$@ dQ9TLdy;FwG?'??u4~ w b4()>?N\hm0tpbL̯(`W!Bsap]ibH+t}؜rCNjU [v,Zk\榩8;O6QYq7]<~~M#ҋjA`F- dtB:i&}q{(7v=JdƶtMadr`W^^BҐ4{/g B!BK`ɩŠ{۝fu?/ ~UwߛnLx!6xdǙkpD p 9Q>bmG9:Ar2}xyx;K`-w,p1d@B!ABXalV/Ыne-qaMCB'j;W (]}ut9DbqشO &kgΠzw>@S)f>SB:r`!0c1b(]}7imnh$z麲/fѽ.V-8%OK/opGܶ42 ?Jn2Y:;zygXJK%\Uŗ/'|Hf'Mв;}l4tmQ˷RV/~C:ϧFe ȟ{#B!Ĺ8ʹnYh.Ș|“7nl< Ka+[imO6|x}}*RK%~KZzJjS俅c(]VzIQ5UQTGF\245w nᇯ4VFk>p_7)SQաmş[ГXSX([өhuE'o~-47@HPԕ7#^"|pjfזDO2neC^[~u{wQ(%> QUEB!kii v~?x<"$ >a$''"ĝ߁mn8UWBwӉvK܀wm# wLC_ΰ9tN}XQʃ>|  ؘ,:>XC儩ql kxܪh6Y9QBOR>m82OՆώBt/$/I3Z@h8L}xdNJ3'&mH8L\L9(Z _zeg?H:`B'eG;:$?1ٖ8vVFEEv,tİ54R81Esj9kx%ޙL:o)X~Êu H=Fرu)d! RUU@vv6C 4.4%zY|9:?;v 77`Η'ߡ>'`'_x)|i6耢27,`Kxh៶~}ʖަްw`oٟY`URPKT- E[Y[iK$0=i9t)cLL4TSPn՝SLjɜ |d$TM;3Dh9^@Ї2V)X]sw:}0<|DZlZ­u(<œQ߈]ܷ|:it Y3K#^̞DX/nvw?!0 R*++b]YIƽ}>f@bv?-Un^b {x;n3J9b$Y|}uvj6)DP.#xZhQZ$α:r`'Wv"AQuQ 86* eb*1b8? CqI$,tt!ma&l}±M"Q 6ub2'ho kc 2ܳek4-pgo'J.ض=&)n}H+pgWTu6\a}흄R)XɃ5&a&p;EϐcnLkAE&aZ?Hu, kz{q½'ym 3aƘtv>-,ipНq8--`w<6wbSc%}w.OyA4\s7bv(_K{މvPְ6b5w޸XXj*%eMְVCq =$sX` tE+w{m+r~$05Trh; <zU<%-Cv=oV[$s=cdp2 Vt`V(AJ+Ph6`XZe `s;: 8 )%Hki0ӷתt,{^XG-{A_˗hWO onNIaEȋaߡf ca,(_Na@:}7c'Qcbw:ԅ.k߰ʆ> VEO&m­m'l+@`j*J| ]%9fY4pWGKXE!J޽d5"rkCn *TU29Ar] IDATIϑJ*5e%`^oc96Lq JR5p´T+m+由OWNc8n67($]źdiB,m4FoI;/8T8u?Ⱦc{jp'J߁: ?p.Sʤvʧg;po1,iF_8ۥRP{7yO78ZЛK+21~GUC),JfL)qXK'FS/[NPSQn+0s˹$I3U|$P &Xr=+T]?@e}u2Pg\1 slnz[\5 v$k*jeS,`8|%G9P&szdzLJokA+Xmp]g#]̺\/ &yX`^+) Gϡty))^T'J]44(;F./_I*yh;vӓUE>NHT dcinT6uȑx嗖 bw!tk`:6{PBPٱ֢ #PG-`(NѰcL^K)q:PCyx?ŋXJfW olp`%vp, SM(00MH$r{sQ3Omt!w-6u>?7D`& >Ӷg`dzܳX" Bߔ$VT8}l{Zbݴx 1UINsRxp(p<bh=Iw"9En[2 IPP0RIQD&[ tBfE ' %JQQha(n| PPEK"5p1b laxJs2ݠO5GQuXbت?ɋ ($%(Ho`:ẘf2e%q0"$MܜX."||;{ߣ +n(I$ !:LR I(FRn1ڻt32/ӇN]L#Y"ajIZ31)~~9=u!,!%=(KIï$F"u?~LK9E] !$5՝Xl];;;ݥp14c7??}64Ox?a}碴'e;biL0v:WqSYBEBj_}Cu%ň15u'!sb8q5ЎXϤct1PQ&EkcC#*ԣuٷ:#E)ɘ)Cć˓Hm|cipbt5X &F`Mcba:H}6̠&fBSxQ0&bb TUEC(lH:@Z3yG1vˑ 9x|>3fBE<{RRR&<@rLNmа"q,& g@K"w~9%qz8PyﺕL<.:)_tg;5a:\V_2dp#;wtN;!8hƼy󨭭%q!^:DbAYAoZ K鯀!9]h0́t9q.BZ8Cb~Ї 79F[C-IDpvg-V'IҒ3IW9vcĘ٤:)8]h7rXQ1 oziv؀X/zFy$ņJר $mǺ9F@FF qvH,c9L[dB QXzH wrE31Gkȭswe;S/*`t5Qٌb]-tL:K!)=o8Q~mӽ^u8p{+3 ^[5&Hk4@vSr:S,2HLle}Dpl]ǝYFRPb{:CCn3vڡ߉us x&!y/%%Kb 躎eYԌ| ×_wgbx u×sJͅ/os&_׃?;pY6K- HpO5Sf<;.ښ~9x.Kr%fAAJzIڴZ#ü^>'~T[LJ{[f#sn.SY]IbdF  +r@ ͵(*8Fpn9kPXdwv6k(*wY,\V{ܨe.梲ZTzNֲ rrX,V6z\dÔOYG:ǵ$ZEaa{mgs jL~7@JX> okY] cuoA5,;i`8Vtۂ@22)hj6Z$WSTclT- &Yi=-:+Y]|  FyOɼVffy?ds:}^\=^o_OcGzZw3m!^I'͖xb͕^L =O4#7-!eidm%bϳq|hdmmmTTTLXǶH&{Iֿu̸n1~c{ 4l,GASw]WTuu_3V rl,A{9xIx Xt9|}=Nj5ղC.kZ) >v!g2K(fSZs 濜Nĉ{ǩXxX+rT4! i5~}ܗh̝k'E,fbk|Ɖ_j^4ѹe>/;v"FTx=cQ o◤/#NH,M2ix=c.#y ;\>>^9U~]E,nhCk6 Ԇ˩3/ [\8ҁ~z-8>|c#Kx|&4C1$ƬxLNouG[gj^$P4;[E3Ƽ=EEjbx}6MNdcI&\jx[)qd{{E &??yMX#N,I\Q ^MBA9 ]kL:V&=LR21BO&>'y)g*oIbcLUޅ ~ŋ02yͶᶯƽ0w;~(^ˡj4 lM3 MSv>=;vM̐g-nv2ZLTƴ ]m^Dj_5J&aU|}Cx|Ά;ndu2ĎґPT/~ӰE!j8 yW/ or 3oSl%+_P Ł%|/QD/^k5pmku5fvPTǛ˺k|=MYYOmc),x-/p}<7 jk۴#/%g[b+ͭȿ\Z`s|VTwCJN)\b{b>4@_VXJb>p:ڶq)s9K SiĠcck8E?e_ŷv?<=jn]AOgt%}eC7Wůh3ncY*YWr\ia󟷰DUlsWpQv'[~Him,qSYۭt0/i;n}VsȾлw/%HqylB!x[|>/bLCaZ VVvK5솣;ǧJ(p)k=]Ҩq7b#nqcrVG34[M`2qqin$tՍ|}(^zv /sh"?`JWzS4k,܀s<%lSlw٬CAU5,`+8vT"oBwwoW_cW{HQu 2o z}͋ouFzA!L]-w~"MMzv_2/;_~9WWl&^؟ˇV V^_](ǷsĻht}?6suI;AQTG,gg+Wƽ` ٤pgS=Զl 0iy~.o?%Ƒ# {,HV vu@Q45C_~y útP,J}'6zToJ%w߷L=Noue0@unЇރ/4.R/I[||f;[y2˛B!x"n p+{4^|}=g|A4Ro~3~lHW@:S $ЂŬYJ߱Vƍm1ŊP<,[;{7#\Ex>%D'NcЂ?"mή>bih]4vql 3L$H` ~#MWPT@jR0HeTE#t+mljQ sc((A,PZ9>qPȜ"N׻8?F]o t򐟟DOs;!71VVdTtU!ՇEKMm5eKM"Iw5CxKhܻ(%=?ōa\x(\x+ 3JQ'[ ?4@ʼ$k(h_>O)H5LĆH+5-*+ex_P* 1ǯVG5@fj_'CWH'/?@_K!Bq2ӿuܥ[Ĵ^#o w/ͯoH/]380ctCEN? 7iQ>EQ`AĄΘ k}U\!9 ǰQ"ALUkI}"C9~1zǢ5!F͘S.+I4:H+aca^3S 1RURXuxwŶU>k6^Eu1 OHZdR_w4V x5AB4j`O9e(CLyt?AOZm:IIc[MoG~5ƮLqF[!Bװкk3_Q ܥ\m`xTāv6Nd&sf9w&}gfw6kwvwv2$qH| ܇B}jUIH@B2_/^/Ut~W}t/QMf<;J|Kz8{J=䥩38uIKPImFƻؘ ?U ^ y=>OhMm~&HM#ΌMx /6IDp(C8)=U5sycY6Ԝ)ȄʳWRVF%Y4J\K+za/M}޺3^sqδE%?49t,5υB!_֧® (K)^<%la5Dl?;aFYSͿ~uliE<* rN[dO:X=Y %ƺr (<jbOϧ k0!Fap{^7v),iSIy2jwu!4Č+O3>o◠ś&QmL{xṓaM-,q~ jYmzvDS0-'l ;j6-Nc۶ ,f-)>>Oi шʼUwtn2=Mӄ1*qv lr|Bq(co `G_~]&躍u(NTBe2,޴w玣5Y"G{ jnz;4=9$`p2 (KIKMa !J^y:<ۡü+TVVv(B!h756Hv*t:`2Ro^B9|ӧNv(:}4o !B̢6w\r 4(Bu,̡h4UiSy≷+>᣽'1Mێ͢`bDz}t኏c΀HIb K")Ή6rg#Hg̬"ĜoHv>uczL !Q5tt{C!.NbTb\Yx8/@mC8c 1.,G,>.nJ;dwZ ä`/BS5*k?HUæ)cl`UnSq'Rx{NbpvLUMCU(taTEeSAa9dZO{nc9|0<B[L01x}*9q27ifYoVCa.TQ:R(XnY!Ox&'JhitWj % Y8UUh'FuĒRt0@Oo(ą ]MMHe :ǎbTXyf \ ~;aL={7o[2ۡ!Du՜!ٔ21]^]J_0/ey9X31뜯E,Ƣ]7RMxfgab;YYD:516NᲡ{ ״DNݡ. "rZFSmN\JV!9TEQKfV&Ό/s(FaҬ.> >/*Zb6$];D!怚V MѨJ.Vճ~e>YSܱL{z_U_Ov/>~f4H4iS|( =^X΢Y3*hK)0*V=!:Zë%1XXАNÁuӆ6]C*RaiDui~1d3+Ba>8VJQ~.+L4M\ř? W,^L_|h[x{Y|)ϜwJe٬V6VDi%Ȅ`(4*b!9=V30W0 s&a qM[\e!&1N2W|"?vCvCfǹW~?7ˢvc p4iڇwICCLxB( I]5LXrD51+}aiKǕFnztz鎨x(^j+j鰥SPG{3SZ$ȕNX22Hu]SwBu'恎j;tɛUo:^lOyƥPv79X (޾ I1CWT{nD KL5]z(f$RUDZJn֤7 sѵy;G;hIx0 {h6^BnUUPL4G9^ߐͰFu' Q-MѣdW.pC['yhcGrk<[+Yzt#yVF7K';Hp{g2w2 B^*+ڈf3> ߼>fQC2f&T:nj}}?Ȁ,Xվ} jyS5;Ήޙ7SP饻#Qn7ɠa\=O qިyNpAmڈB10*wq 2pF7`(̹cǿ0+1L7?8LMc qٶiFskX>V_[} ~7yq7$pn31[wGKZG&$줦f7y+7}?]d9PMU4w7]'%JJ +d1]5☾  uT7$K!e|=V\Φ %555tvv0s#uQ'w(B\fe;*9eqHe;LP S,rY=E8n.<3HM~qqGO}^:ZnHQ8JOsef\ 皣eX4]<3cP!nQu/+uB!BQ^UO4:z!O妠K}s۰TU4GQWQ?V UuDT6!ևf9=f$:U.*JMvp$B^V:7ޱ5$B)555twwOH+iw+H@Ȟ~kUypzJ3Zqe|:SE~‡\ :H^t3Z80NMMM'8Y=K\DgX}QPͯOwm%܍ܳc&^'ٟnrrS4CRI*`Ie֥3|RɈ-cC?z%${z]_}y}1Xw9nWgIvǧo-X1: ~14s kr>l4Hoo0.'.6URqghN71a=lzMqPH(4\1 ]E_06n!(\t =8 U碲tȏ8q`|W1C&N0RFuTUe.^@}s .AT²3^%G"ӊmm$w4\b&kͣn/^W=% PnhbqڭdKwߺYVjܔ¥+KqH!ΕWLSTB}FPSWs׊tʞBM,r:>z<)S9R,<ZŏMQ&Nl}\8F(k"+h˄'BV41ן9F(hv72fWq9N;nɉPSO@=t6l+ ͜ρgnhHsIgw/ic}RUԤx$zKs|`х`aVuMĸ]$KBlU7%wͨJ16N($bЛ(9pt@q ,.DOqroU!ĭ󸝨JR|fC&xx}}vb&ZY@e]㙬rq}[;PTLojNXH[W 8} ޜi{Mni!adL;pfw 7:RJu&ui{U!ĭn&B_oGHT'-%q믽HGn㞙q.۬ONgGݽ$`NfBl %{J+S&VliKG{:cIǭE8ym'_f%3Ro\Ϗ1:ascCWg,ӰZT'~z+'o^p0[;qFݶ;n~R.U:sa̦6>8ZCwuE!\Ԥi?>6NH5[wAZxs1KE˜jwak##L8!5֤UUABQP;3%9sBqs:?ލZ84>uIʹ>Mtv{)*XxͶfMxLzqPxݳ8{+@yUO=|cgBl }ʯq.4Pxݚ1+$B)d(I珟yg~g~\GqGOT\I r?W!NZLcc\m]faIozj߷ƾg| ߽N$NXDqc:FG>~Srөi`}R=1,ן'1n{D7 c}Z4ڴd hhiwwhLz?ubcOz'lpu+6պUN[,J+V !攔w`4gϙOQ1 ũ΀>.<*<6~z{.N ʟʹN]*.+<}ib30tKd9٩G I-͚zH'X0}\p E&ČNKsVޡ~'oN)FH4YձicM1kf͟X{䡻6M>p^}巈Dk]=)ºfq⿙ݩijxٵc'9m86c񍱌b?@r|vu !dvqw(_9 y7^UOFQ?™fGiNҹN aǪ8Y) /5 tSESMAmzr5#?A;ph`ʐ^p@bІ$&ӎUL` ac5F˦L"AӆiVa(z_Pp=iِ㢡 Ca҅ψGp_ kt,aӊDW/(?/ŇY;PN("pO[3 \1 ͌FQN'`bǗ?l:aQuBQՎ~=ܮ脂atר 8m5 }=(< BVk<6\јDPT09AfX}!&+Ɩvꛆ-3 ʹw {Te^)!6|:y;y jZ~qji-]Xyz⟙}1nqOX59ZZ9 E8z"k}3I+S{RDž懿X_|y )_/d{T4|Txcf>GW;("四}z(Pp6akj2mG^WgS<ӝ'+8Y.[|;DZ΃\𚨦5O?qcU-Wv;G+͒g%T4Fa yW^S*j"~&iĵŅUxM]eCɥ1E3W{8AQL<,R;8CS0x?ħ 6 S}M~~` ;PdIቕ1t\8GhXe]|^=^ϊ赇yu5c8O箰́_ʕxK1f=Tqo*^>>crx_[DliʼFm+;IP"\y%X}^=j!mv'gpK^h$6.ݑOD97y:Z큇x;_TB\(*hG5v?_77@v Q4t|仯}7lh<'h/RFNw&2Wcy. "WTg;I2^1d=k}G֎nVYi)Nx6}o Z^vHTۼӄlgD^|CccI!!.ۉŢꘘ饣K{W^:<I,a>xdž3^tW1$$$Mii$Ҹׁ揾W2*z(?'`w:p8شc%߿K'pl 0Aat`ZPp$$$L8LvDT`\ f⫮˧P+x<7HœGrn Rϻo$Xؖ3W^hn_Qvz%[3q=~uv/"y+_cImyꝜk4'2θB?{_v/m  ȩ-|[fR~Q)/H5jCYf HҢFqt⍖,-x}~.>#~.,lؐym,Up#m¶GvofK4Ey09pڐfF:ש(A+L寳cvz?x},4&6\h1~#_Ù|#˞M9s[ >gN[E+şpm>Ώ~~x$ ?zwro΢s{7wq q$Be^Wx&BCΣXI!1 IR9+7oc<_Ocv"(qs׺"Rx#׬{MF 2VR(/]^;PYG$%Gkg7. BBl qf'!6fԱ7*-9ǶnroIxsN)$ZR ?M+XbwᱻF4h#2w_O8~NrSpzl <40=%0 p~㳺=8.@*;IfW͏ -+z:7Vs7Mkp+#52pOhK%|c]1 `c>%.t DU_Dz%͎Z.tQ#8PXYûU}-fg5$h & 2KpuI+ >'phZ68X%$[Zh¥ҋ׽7a֧ĩKܙ2t0#즩DM!8Υ"-gs\I>X6cŦp} +qVNMlȍ#. g0 IK_Ja1+AyM (D\9Y@l),9CWo@Qpd.嶴+٘s=UmD( Bd )ڼ{8X!i}Ҋn6'Y1Q_IVTM.1MYpG26DBki7w^$}z*e II9o#-02l(=S#T gUb(Xϼ'lqجestoy9f(\4uTe\ 7+KiaW.Qf/'^Cxbqb5BA~/>KNcͼ?[}>xr Gϫv'@ !-QŅgHvTE~BӍG  N`Tq?\,./Ճ~XL%x?'֩?<֍ 8m[|Nb]#~ ޠm Z OxGոLجh躂wq^Xi!N£ɾ|Gأ&Qv&ҍZr\jq4 D_ g! vWԊ'J"wqĸ0h_H!Ph|At#q2pCҨ] *Ɲ@tă$=' Y]DqՂx:}>ot;~Q!!ey9\'{\X7!8)'8 Ӥ6+]86t NM;6M!~qUܹيCqh.7N o3ǫۙ+ՁS hFBFoQ!FeT֭S4}֯2nMc4 G&Ԋ;+3͕)xū<\( ūg|k#~[bnpd5`+& 8Abn& Jycy"`Ib㧛`{(;[suKj>^^-#uc SV3Yɩ:|ǔV3=aEz8y0\>VJ]l.Rh6;NELs%ڢ`bK i:u>0|#ͱ,]?&i''? DKlIDiɼur 8dz-Ŭ,z' c- ]}銝a\W@Uy.|ӽ,r* AIMaLLuVY4~«%Č/+n"g}xB0wOtu-/l|_c>P,h%|Iur*̂ڌݜG`70W:6։[v7dXԁKgE$;(Rr4gcSApsMO+?ᯂ&ѰB<D к42og4ЕXV0r޻WwuŪb q+x|'TbVI]|M,蘞<2|mΥ@dۮsAV.ɥ룺֛:-% nX)&O|?YG1Ӿߚ&<_g]Ɨk֮zh_ DD18!%X-ٻ8[U929k4FFalKz+K~J>>gr,[hrR8 `Dй  }Ιsƭ[ư>b%{Ȧ*!1eMpV'r_y*Pcn_`cd)!qEѴQkʾERפ,_}o=u!|N!K2͓LNLt_{b]['vH I7ӣd0eUeh{4~Duye8 C+rѯ~6`rXnpmڤ YRy|x&3\ !`Ҥ $2:eɧ3dw,Zt#99|!ĊfZZwU]ܱqŁç,]ŭX/ !V]wq~应M9yZ8^Htq)^N&fvmo3rCp MRtc@i ewg D 8(??эUy;T:@pRQ+\|\CW:эi0\W]BY>fLMiW TnyLdD}> n՗t7]2.rޣסЍ&++hz癎 ״/YkC\W ]R uuT6ýhU<^l[s/t-IJ1t;p,tq+7oo]Ӑ ]gDnŭM^!Ċvstww/w(R teSmOM9.dK]7J*֫h͟GV\t]~n 2!Z*,fPJ˯l)uk:TS104M*]٩D! 3 ^qw[Mٽ_:~憪7_fVY+WY7/_4psihʡ>2wLm*%;*ĉ?AEThgؿ*c| Ᏺ'-`^~+b;-F`sQ^>g14K dy M0u&vT[I]U9wp|;ys CgK*667P5*^=ׁidΖu(SŌ$BxGr2;[7l9$&w-HĹGM_w: /ӏqynth!/,Eo&rX@Ie5Wsb}$R#XSIb_Pe -9s_]#S5$Bp^Ku?jzQjdw‰?J<~σu IDATR(6u޺kHert0<"˓J{۴beAj⫴kzܓ;8+ }UH+%b1|A,G]p؁M?Zv%B~/;nʵbַwl_PXUG\{)ۂǰB!J# ▲fwsw-w( F8!B1$B[Nm]-|;yӛ߄a,ʌyc>B! [OӼLaηwbYr'Xby{ǟX}z}v+՘E4Bw.n`B!q˖F#aolZ!. 6#cVo>8^8nd=nt(fF V !B,eIxáw؊Rhs:M&( XhYvl幃GHf֬YCkk++qUM/;]ص   unPQB!kݒ'ضݽl;?`|a64ZxE*ڽ]wqarWOJr˴ ؼe3;;J/:&Zݐ: X'5QYfԚFb B!j޺*tSg/88p#ek_P;JhWWNttt0:zcUhjjbæ lܸ nAE _?.m=4DqP<*~3)B!c(죶uk,J+xH$ttt 8C!r\׋祢r*++h4z3)\6M=4N#P0w< F 4+B!b1,yzKv,rBܨh4J4etwZ{潋t!B1WKvi!tc -|6C2\>!?^Vl&XAkw0Bԫȍ0Z:yCT HR؅ȥ36 !B!,y ;:ki#T_3~b}ab\"N HgF1xn\.'x\/ $GHOJ))rw:)2/FxЋ){&#[C !B!n-Rp4"p%6斵>wt:#ݫ[|bUE(hB!B<$᭫dǖ hJSq^hGS;lr"t5-S(5k⧔ǹ\j4'&p#+u7´ Bpc>xA-țj3%ebT\ !B!nVĢl܊R ?uB8 "OB;J)on"] mR5ni(5dݥls)ϩ$)M8Ø>LJGwr{[(YJ=CgsL۸~$UpP(r&v!hCy$F6h(4pY?9')F*^hy2\UlJS(Ꮕi$Ʋ-]. ;Ox,[@!OQhH]RKJr B!ĢI$Y3kpö 멭ZȄs$VY`R(̌˙Mvt!+L$ql|4 K&I{"4\p<JH;,颏ύVcF[DW p,yfx* A.h,B!KiQP0pb gt8ϨRhB9dvJm-&/Y)pڌ+0CkI 3ӿ03 e&O3S$&ʧH883-+'{uAJW}vf>!B!X5u\ 3_O{Q8ql#,ٴZ\cc]gHvwqpwB!B`ZhM 4ioqB!B!ĸEMҔGʸs,fd4X*E:%Β,Ӳhkx=p0H,>^9>2 !B!-j{6֮<!S-΁CG9sIϛWϜٗ^a{U7o>ghhQLs'^(( d2@X,F4~äe߅Q^/h =B2Ix+R:xCdd*s/fhxŖu#dhhd2.t:M.#SYY9^˲$cY"EykrR(,wHB!-K^!ĊԁC;iNH&vUÜfd1aYNJk8C]]&%7B!OP!Ċ<¡cVݽmTx\ne BT !VW/yu9ǹnŭ($'I)Pum;$B,&Ix+ʉ3;9{y.u\M6T\^q$B!n$B%^o?B0Q[Y>_6bv|^ !7@^!Ċ22z#YKmuFފ͟D)u;Vnj׹'{v}7~jc˲n}BfʶDoڻyB#עU&^Ïҋ|7c M;~8=ry3r*Bn bK9Ai=5VgLWFi+)on&0FxHj#n t/* fƖFn]Jp8ɘbkVXCp${/>l^ESuTE.KU-u]mtM~ Wpk؅ Է&_EU:Pz:&v.f︿m}upSyc'^Pha$g8]$Nbej }rK!M^!mv;S'8x$Vs:rt\|l:ˌXh a$+=S0C$TZVSaX##Q槎Z;.µ46 8C2V6S_5s fCj]GCE'F$FGI\Dm[LOV5[D'Xp9 '`0u=SBt+ |g=KyVok1([nlŝ52Ad(Q dVs9ߟS MDntx9F(#у1fiKMrtt:M耓cvEhݰJ'Ov@T4@ዄ!粸&(az&,: s ѐ Wl5#ř]gzqp6ח^w}tita[6h:d4|mS%GY*@m&jRΙ?O]:i( Ub-5sy~I̵ّ1BF;vUM\=~ی\[Pټ A)(/r1[V\VWB!Rg}x9!b{衇;kūtn 016NqtÅ1it1kjds晉7p!zzzdJ0 caZ6W*K6uDB;evPk`)Z76vٰa5ۜ>}UQQ5ڕBSi5>HǶPahj救鯗ΥBiʱ.~ĹN1Ч0_xKfǶEqߤ\E9g!BLhO`0멪qikk#NϱXn6UXtl=hܽV8ϼ;8"Q:6nr8S<B{r1gȵ^]Z#2/:z<2Ds{xP(D-\~[:3 B1Ox {'D}}=CCC/8C_׊]fW7.wsuݴ3@ 0w^\.m;$B,Ҽ~z9rﺮO@xȑim!;fክg];c)^"q+ U~:הbI^fԆތ(?M;v;E_Y͎[ٵ1ó'0b o!sIV:P9zHhW7ɵi,?C|`rdW?~eFM= D0 *++) :rGQ<%QQQa!8Δݶ6FGGqݗ̏Hu-i~vb>:"* >cɛE[&~1p;m"t D.vv՛VSkip?Foql z~}9^ѕ/£E~lb6vNeCz+sL!}7oelmXz֮jq>b-TUUQYY9c+++LsP^^Nee%(6Bׄ$ ˕ m۞2[QQ1o`=G|ؕ-Fhե\=J2 Fƃ Ļ0w7Ph|E-~S[8) AbNխ}8|w,Ch;pnMF2uٰjo/ 5argN*BxݿXrR ؾ{EmՍB!n7CCCb^Y@ @,dSSSaxxx_;^/hX!-xwc=6w??^x5vy"siLgX9.zswQ_b{*cdSvf(5!Jצ OQVMnZ=ϱvXwp -^S-vgO02:6zo&)# -4tzbcccd2"B۞˯v1 C(" -l4%L&I&yLӼzo}avq\M,B`t:͋/=i IDATF9B*J՛w [1M*SI̜ Z 4PPnpxVzUK8D o8&w(UP.%:vDl]UM[;Ǟzj5Rb/s&vn۴ܡ,χ[0D""r"BԢ, JR 444~OCC?L_Fhz;¸u MwTod3M:PŚ2=Oqעu!-ofKsLw;CEΑLhrD&멤!ϥB3|<ViHW`5>]= !B!X|wB:&qllsLlp~/Mw(c tRS=t4"υF';O9}'[}3ДX^:?.GϩĞWccf8_ .[wm٠kX==@pyv:EG9֛aB!B~3><. \;U6lM4@M Esz;)g:YU K.(xR 7 .C@8iVih ۞Y/޻=CB!&/.3x6)Qə%Y_*2Z32Z<,)b;/ɮB!BNx_ SQ^Ww(?B!b>$]p4\9LN !B!ļH»"Y$_%Ѿq!B!ĭkQ%B!B!$B!B!nK !B!-^!^c:F;!B!MuS޷?򎥎Eq:wCB!b8p]xeWטƪr B!Ĝut q)E  ׃{ҷ\B!BܪyW^!nbT&K!_Zc6A? !B[ڍ|ޕ*B&r'oB!]IxM䋳'Ba #B!b]IxMX=LZHB!Xx7yW^!B!%IxB!Bܖ$B!-q;!+lK$Be.qnxJҗ;O~~SǏ}|- ,&lDg(w5k/y"7١BW!3*tW~v~E *ۦP̓77{_dV?.[k?(#] ML.n~f;_N]9RW_\x?Az'ri==00~ m=?EFU'WOX!% B!fUoڀ} ^hv;h*\\*5Tg7Θ2w|T1$w}A |y~+6 /~#j[N4_r/ORu w?xrb|Pl$/Y-'ŝ*Ld_|w8=hv?5a{?=tg>z4'Ɵ=?ƪe_? Jw*&bn}ۇX }S?hUÛSdzA<(;SlMeV7{P9qç>ۅcT3_Jv6`Bhݽ _8ãr+%UyWEtv=o'l& !MZ҄w p.SyQ<$,r4kq֪8ZJ^ hz/_uqA/wq#\{P# ~mq14kÄQxp!dg SԶ u8 !kL9T<]{y=ǟ8Ɂc^60eoV#''FV;2':}gnZK?>HgA#ڲ739!2o/mܻ#X֕9ŗy|lvZx)NluJ`(hs;cO«g9cj- G{i{va{gM0_Q~I#*}C.~0Ig- M7A\Iw˦zÌAuZX؀4 _`gBϼM= \?"!XKA>'4c>'D!N[Ԩ$.vj])^5kP's~O\먏> JE!Yj !x 5wEcM06ŞOZW_ejr=Oбy)~澗wm x>va+}Ņ~z{x$cD& ~ ? ]i z /û*\p. ÃE-N.~{| sG߇a}ta|W~0Crt?αIM?_ 3s޿EG6E8y !+frK/&`%.u]}ׯM=+C1PNv,4 30euUȻw/|ei{j3I`ٳŪf8PJN,x Mw? y_ )ю픊@͕mRt \n.(ޜ \zֿO}p7JН/="ԔC Gz/Ӟ>7q.FQ_Po^ !71JV1r=3UUGţUܱ6 f˅n`Cs?(ɞZ@񑇷}44kivhfKC1_O~/=Ec#_= b G;_mN-oJri⎇{S/[ !؂/XdwBS[9,L pG |$)&Q_N>pA{q8_JcQI!p>ƷzN~ߏ/䇾{'j }+Um0<|q8;XZTepje q/ 6}bq쳥$= p/88?>wpöBiJf@M._=ʅ=r>Нܿ?'uuIa3ɹCOo|~l޼*}oGk@8P{6j4 MSpϾD4_}7N&U-VTnNƶ}޹R}vRg D? u_<~6S򹔦]uԤʇ? g=S9L JN«GyJgW7 aڬ SؤI sRg/sb;Xַ"ohl? m#kwOJM zԹ,ZY b-(^7/J;ͩqs=M_[LN.`L\pUkJnjq_PQWK06T߉=5Ԁa(:X?5q̞MWf,n̶Z= cΤk( ԗQWW~ӮBI?y q";e(h~-ޙJ}](U&G؊ihkFЇpB-X(Z'C)0֯ۊCUW@Y5B1`,lEmI 3CQmB|) a36 !Q2-<6$)ܺdjs\PӝD`r"WCPR$RVK=ky{c7ecȤYcR+ӍcKY~Jf||mooX>Qx2 gyrJX[d0?Vj~j߉mYԼ~^0尣ֈ ׀3'C7/ AΔx.=aaZDt'0 rB/܆EMe!T0 ?om]U Y_~E zq~K }r#Zz2d9] {E386+աB!'p*Xp%q>ߘ{ÐJ7<5ڝLlb MA]yⓗ„ t3w孇ND IrNpxGp_!eNҺ QCDp쭀 ?RG/P?~6C#FxU055!q |t?zL<~3K n7@[B g#쩄΅Gomׁ忁Y F}afQ[NB!DX=ǔ>r6 ! HȢULL$3=?1#9#&Z\NꝫDžx!kˁM[ ѫ/IX[S(Mۨ;M6B,x袶kjFOniP~7 ͞琜d'zrc7g, \!߱lGE0!X#:;p-׾~*(YSaMpTM]HP!8 `1B!>ԝwޡv|M-y,6f.U\8;~CB;LJĪHE}KFw#IX-ů?ZsWg  M5d+^h!9Ʀ'>Ai!r:`H`@ t44?\`3v~#R 90H.RgTCˉr A6^"NH*B!Bw9ϻA_|z81)tF̜)v7X>g{UM/=0. 8gp}eRt1s>Q }rJ ;]y` xcuPSw5P9;|ՈcPS`( CݾQ;DYj|3 ?!B!%$-v: xS{ŗV?ٿ(<F)/7|`W[~V F\WB!B!Ng [)Ø4Öw v;``\*euu_ky!B!Wg6SrJeÞ4aOڡfFv2I[kOB|.,O]!BϗlKt i[=k[%x*QiYcQN B!1;hHHJsw+ 'ۺuPǸ7pNj*M2r3pwTqB!B!No xS:KDB)yXv>m$̩ vNoB!Bq:KH[`t`ɿߤW#2\|I6 &|z,\?1TB!BZBUm/ |˹0Qvlu[&CxWo ,Z;8y^6z; Pw@} S# UE+:>У\!(Y5 ?Lp* /vF?J [ _ %q,B!,JH[`T)#7mǓ/.[̟~~*LccbU'igAZ^O]ҬQrCwO{z`ԙ3 ] -IVK` !B^ IDAT񹐐7K52ë#\CT6 C ԄZ8cYKm=w*x44Ղ2xr$ZG.P1Zhj3wJԷ LT 6`(D&hlS2 V*$5%ΌI=4  0 pb` ;Zh@R&X!hk;@cUĕ If_%6„Qsr? :5ǯ=Z7tp@h>GcnnAEqA bi5mϧB ±#B!8e-0"75h_Tc>Bg-!~n]v>pɻ@ρqÏяB̚ }P u!]o g?47n}x)#Be RFxjWk΁QI1pahU&w8V 3:BRQLfxbzm ^^u,p7Þ6z̳\vpE s?P_9PQ?\*~ &_^~wӑf~h?DŞa7C\O> eC^aylB!B.!V7*fu$FNr7LZį/__={)'<.T_7o ^!~NmRs LA_CJȾ5k `si=砮B_GLɨo?栯 ow>Z dYu߹G7 Wm&:ej s_!zԷi}]N}]:zsSǏ!sr>gMB?'>?[8i]G!Bq$,f2ؓ"x{紹+%{ mک_G]zTZk@,%0lJ<(3!kV{ﵖw),E="E j]1x(~Q݊oO lwh-3{{͗#>xw` Z m6,@> i@tTA%`7@U`ToxWPUGxH%sa(A-0'c ,PL8la3>h,uPS`'Cê?G3,%G v?qځU̅TH/ llM:k!B!N4bUHmVr _K;rq}xޅ~x7Qpkߦ28 h;u3.? PSZg~a/L߅N9\g6%ئpZ8Rhj7H*${iCm΁0g~}藻Q?ϭ ?um5P mG@Z*߅oӮRpP`ˁI 44/ԭϟ}f __ :[8\ [*'#: 5mǎOk|5 LZ=B!B&/pU.x!:JDooc|xN7Vn4Q)@EJT/:PB?^^[#1y7>Rnx7Q0rvQ3ӡ5uZkz Zϗ5ʂoW{?Lj"6TG̅ ~fXQS).#kmU aPTOvzR= xkz5l;_E?y%ҽ@7ּ;E(} ]HY޲Ȇ A{>9ÀWC7G`ǀuY PpѯP#{xB!G]55̏ͩ4SWDVG?58@:@v.Q_[Gpoô*g(?FPMbek9eށtB?п~*@嗨[^DM K^斠O3 @OvCvo>`P_E缈U]v?{K;[kVfF}/J7Ƿ 'ˠ57n== PQr _XxMf8D2hɨkAUJ𞁚y X˷~G2;뷢nYذ5U@>LmYy*8jݣ3Pom+`cOB!]}]? ͻm` '%Rz{c ssvRQ,h[+`r,;^E^NE=wAu]1+s n 9ˆz.6HIkpZ| jq\^M/X";n=tY뺲ܶ}AWBccv%KW.m͂@:etCRаBrYk]@: =$ {!Ҧ#S x6D}kw⋀`;p6CӒ,~>oC~Jʎjynv=rRB!®% C=|FxoTƷ-҅;l;F'ҤL a JJ JT$x`:g*0Ntjfj?k:·:5 `W?8v@4dp : uiC1;t^~B/QE`eAUw0mCsYž _:cCYm>:>iAUΪ~B!B;%Y, tDQ1R>jN|԰MccPNdTB!BOS~fzSGDݩB}ʲ8y.x| :מFxy&ZzB!'BcѸuc!R U' է(]~ǩG!BqNʶDB!B!'MFxS¶mb`4ga`Y%҄Bў|:BO۶D"v8H+B$Y!>b'@kM$ɖnB!8mI+sӢҏB!hG^!FB!hC^!B!K !B!\W!B!B!B!>dB!{ b1֘vMB! jB|-v"T6O#{*y#hYy͡[BuX&\W: ݳJ2uUTGݤR4q!PVVƞ={ƶmͥgϞA,K !UW\q\bۘh *HVpfӇѓJ/\ɺ:7AzV\Gس}%+XQH ײlŐs'3)צp++DB^ge x pm۶QXXH(:Qss3TVV2d? !xI+8.VjwF"#7 Rʂ7Vnoz` [F|*7| [P]0  P7.+G714 enas{0`TW7䍷'4Db}zXױgN1[=Kxes/MfgObohO!Gz>ۍQ~h߻B`v6i cyck@&vE %^!BZ28"8>K`q⁃79&1, KS^[̀"u HJжCq]aHiK7x:%xV돼U ZkuX_wϞxWcw6!$fj7Rݱc`W)ucРAn*++K`B!8N!I)Eq(3r-G*hP[u N#I^QY Vyg!=.cmqqI>wzVb{Gͼ.ȫ)&Nfl{mb2NKB:; vuPCQ|n*^fAR mqjjj0M"555 >p8Luu5eܜGB!8u$B0ݼ#0t":T,d0p gD0,OꠠVe=4TS]<..vi_>{+ lM!ޗrw`;ʕK))t }SgW9h /st\+#ϬF0 F5oxq;AXXt2TuGN9۶53aY.׺ͥ @.OHQ=x'|Xswv&VR:2͏uè^skld5#Y^<.aJ]]{%0x`^/1hIJJ")) !SI^! xGtţ2 AIZn& kDؽz ʳqPzt0۴XS{).\m#~McCѦa }0ˍXaлwoJKKD"l۶UUUXEFFɒ@/B|VI+8> ,9#9h*#eXc5#ˏ PILP%k7cYw32D,8ul\DNlfvkuv}b?-?A?W7` Dh^YFT ݂cT4MYJ!Rwy 7GUPRVyr=m]p+4N,\Lx4m (ᘁm!8ء8ЏЇNFlO2ɾ6{:Q1mqc i1;ψM\.kꉺ?#XnnMQ"c#U%>Jğ&B!ݠ/pC=|ؕ[}QHH?*>gzwBײG(;~TlMUظ;c !B!D+Vb JJJ:6o @nn.Əc츱Mš)GpFzLB!Bτ2o<ϛOMMM^[RR«ʢ6}gL'%PMvE+1wޖ ^+s_0_D-qa[WgӿO.A b PJ_AOҏeeQPDYj>Ni޹a`6=8Xv j)Y%l-.ٛIAnNS9EE1/L1P(NQMz~Isc4h_ڟEHk3.\SJir հ ?^M&琮VjR/lBGjYzB!Bwݺu{g+V8rjjjxٵsgNg ja[Η+0H;Ɯi+fTΞ̞‡"$\?gf|'713ȳ''jWZLn?+;zZkz0۹aT2:lth@;yώl%~*|Z5r_`95₝D ]J{1{L&@y &͘Cq3R!79µvܗ>c_1LbU+yEOKGa /]HѺn-syLC5| EOo Y0kD6>={]iW8V M=_KEsEcT̳? | *c)?ZB!vnݚٺu+y*r\͏j_ndTOrtރ IDAT=Og|{oH.%e<5ws zuW]@}E5!Һta&1ؕȕ,ITۚ0" Rqcvp$ c .fj-;h?mT 呵XfΡ۹a 4IGj˅7ud ㊯MŢ7iJf:ӿ r4VE E\JtFWiGnN+=)RnY}|\Tdu'4Q@0tػJ˲pGFyR dB!8%,]be:555\2a{4|=rKRuVSvWOQdL&4T6?~>\^L f4tb%)9|bs;XϸiQ7|U)r?SzF+t83Bu"hwwneYVmׇ )H&6ј#t)@10-,C\vcyp1'XI ه8F<IB;1beCڴA;1FVÇ9.;@mHH v&ld N&O ߥZeaNˉ9MvPy=9][@IY͎#I!BDݠ/}C=_ԆR5g 9C_oײӅLxʴZ;Ltpu! y|/j -otoǗn?m)âK]azh6@&z9t'@z.>mWGm5L܆ !B!:|⢮46QSUΞM[9t B!B񹗐R!{ ں[!B!RBRj 8?Cytqꮽ@!B!i!1{8tq ~˻ts! !B!@B޴47ڋvvzB,fѺF~\0C9bRSSVB!BBB$pv$L~bp xB!Bt$!oA&?9W͠WC!kjq-B!s/!)] :Sn)Ss}/,]Ĩܺ!K2V𘋙k IAډ#ۿۜy$zy ܓ Ď>\f'dHJ&-5pPcӇ14w$//ǚ| g&f8}ضM,ql+:a`&n)e.B|n%&W@nn.%%%pu5|hXJbĜ>{FxcԗeZ{&h󵴜{w7^_b9OaN#{,fyQf0dA&aBԣG-C9/q, a6HDӔ۶ !7++q+ڎIgqǑuXp ,{că]``(2؅mvI:xLkvbDۍuYUKl Ll(0&nuSh}ݣr{Z@P`GGqc*@DQc9WDb(ˍuy3B!gNr.ǎˢ'JMMe츱'V]$a8Yf0smƦFh{U짧ֲ0xrGqԡdXsyc1\`SWY8F 'Mc\/JX".erI7XId llE3]+w68F`Kng({}؎6獦aU0MMIY3' bkv{K)m76=+ְ?qQә>,CYǨ^F˙aѰ]^_Ȱi\~e;ۿ % 3pE+X|ᢳzf3p4F5m׽[Rk2vO/G25->\abjEC{& L^M0g%b{K8g63\0.bVʍ1Izk;"uٳ9Wfy5=")[[S4#]\GI;Y4w5zL;Ş 5{\Bi&c8Cʡ(M&+Է{vhڻ\C;YIzʻ]і25B!H>>>c:ƍKd7ngL?r$?bVEm,_)I/L>CҸ L>k 8d"`N){bK 7 @v3l< 74Psƶcbκ(蛁[rgү_}nRpŚGqGCD&y-'T`&ŝՇ>ړMi9 hTI= OP$uHAZ%% p);CAHw)Pn2 #(agi#GCr~^xI_?& WZ.0M!"5T4Nwڿ`Feru;DbVwqr]?;slJՋ%˲l !P:lC  !@Byd[-Y]dJWm-[%5zeisν3{O;ƞI'#yP1S4*mj%vIW!B!x=uH$F9vX:6uT߾H5pmAvn#T:K9X~<0&$b$]8uk ~E,>VUx|B){PC6-p f?:ߏ=ζhL2~R䏪߃72.W) gG9ww(ssj?^~dcʭbkXit%>I,>|zX"1b 9SN}Ls&{õ&ѣyMԶ̬ @lV3dg ;6̦W3$c$lL$bI"y !B!^GWeE9sp;Mŋ9Bθ (p!%>ŘyU=؀ǻ(* CA(vsWYtr:]5U4|r0* oxT5Xij)H.3Ӻ9qKLL&)ō@w5•eL|W <{M&4cOobo-K*([EyC}ͤ,ÃX8ܚAJ?̉ uZEge31Mǎp$hȡ'ΠaN3}b39C*"J L4 cA,F>s{RIUUvb玝477_9,^E]jIyu ]Xs7k[j؉qǃ9I[hRMs _DaN]<-~'|'23m-_#:W6~5?xIH)B*M===TWWv4M# iǏ'33P(qB!8#]x)wn*otSyC^GA18!6/:pq Cs(y[xd?g0 ܸ3bue*rxwӹdBB҆/.Zu?<Joy,EB qɇὝ~e%Kൄ]Ů}CKi mmm ]b&SN6$B[x/ f׻oɶzM }iKmm@ƬDT危\W4GNɝsD+/.?UVv8sHx?1K`j`>$#Ëv$z P8sIscL%0j=ى>: ?NawIz^1;I(_v`'gGo@vV?FڝVE Z/U}5=x|#D/})@-ʆY@bzuO>,"??0hiiu]JJJ|bY8N!9 B+ƚxF2f-g'ٔ P\C?X|9n N垊]u{4jxgɻKųۚXtg!vs<У"r'?ś+}8矾rm)ǓXTv,5س(}8XZ>19>N$5ڲwM@~uJsz;}?fsC]Qs~Fo=,]`"=J6W;5[/<Eq^~o3>;t1}s]9 N܅''z WL&Z)~r-gVR>&M+gx0,T(xf9lC1ܲA L^ʡ/EbL[Jv]3PZSׯcz+K:u#}^7uR˜i&^y4k3;@)@N6A-Nc0g=k+hJ_;nΠfJa i`gBႦPJh ɓ'O&x]-1q!sB]464 `Zf>+4/< r PJHCC]]]:ujw7 RPP W! GzxMi\4+I lU嵺6īN6YP`-Lgjm<ߖڕ%I<(axv9|`roctG _f{<-4&;'B<E+ _?30pxGRiq=$|D£{B}g"; Ǻc=:D2F5֗I;I%nf5*ᚡY^}Hଶ&N15 :%r:{ ~g}I#պ=c,\|ח $Cmm-aH <L2L B!n8xIωcJtG^׳>I>ʗ3)#L՛]M^}n0K\ż$k8ԓɂUYcf1?-g>Gj}xp8?CWV% z&a(&cR p 2tG07^!Ä^=6 Ӕ'n10ԭ'9麟 3E[w{ M0EoO(R.LXQaR'C>nc9(~Rxt=֝Xk`Y̬~NNͻ^=۩EC|ξE]=3 kK>v7Sse&w&S|/ҋ'XxO)f/Wsb1gN7=ˡ`fN.O g?uӸOy;lmȄ1g&p <“Z`~*|x諟qMDZ &X}{0zA{ZO/met4J_|wV =pb=5 s qHFMZ12<.~bGká7OwO=!ct#dAxٵXС}?D=>,GF00p%m'%׋+օBwCK>4B7fx1/om$s8+ hgwḭy0U:WYj唐&8a+GDnX| fdqVed`&TLǘ<"cxϺG2FB"|I7f$/=B!8? B!o=1PvŢ}E]X!+" _[c 1dY!9#F/6L!8?eF|_!ByB!B!$ !B!$ !B!$ !u@$nJ]x^!B\ Bq4X B!DzOXBq0 CB!"Md^!躎Ų,׻IRJi@!!W!K/B!Dɘ)!B!^!B!^!B!^!B!,ZuK4nyT^ !B!5 RWWG]m-==tuu Hp$BYy9eees-:sq]R#C/O4ׯqB!BsMoMM eߞyLkk+ ??y 3w|**yKx3䩋"B!®jf˦Mlټh4zY綶SO}ˬZUk@7I^$;7.`>4z>A̦)P<| 5t 3G#`Ay'y7'Gtc/>|f*߅=P7LguN2)Y.oӹ߼n\ 2]5zB!bܹ*9Cs ۍxN~Gl3g|y֯Cq;$)d(ؾWq3Q*7O&cT*{)2]Qv7XV<\_Y9<794Ye·f/}=4wġ$ɾA]g`>%A M?r'yB!QoMM [6oNWqefjjjY}Ҙ`)xk53&^fU(m VsϡgR61X4HPn@*bR%Hy̿>-/N!B!xH^CW"w g{5ԅ,)gL| ɱ ~̹ee{/mg]8{J$ӧM'O%/1/B! -=s C1 Vh@aKGGeLBJє1j[JG7,71A39~[Е~9 y-sm3=&hhk|dYh@nY&zfeq!Jg;?'V |Ȇ^-/3iJB!b|R=ݻv}E~?L뙳GU:?t R}CbE ?(L~canlcĎKf_G>9MrA) C C*Gj53{]4,$=3xJ?'a3Kx {ў^[/ ?WMB!BKf &>W3U MbŒlIw4 bR ڳ.xngx_GOˉvxiu5 d^B kBdqNh1v1QVI0d,B![ZF^AsI WLmlUp/UUw}w7K-މ+?wW-B!BqiŪNvVT>FfJGGKZӓx/YY3A'&.$;m !B!HĴ[Ytaw9z_~Y]]]+;kB!Bqҳ@e}n^zUŚEW.IB!BqIK̼\e LX[t48UT$B!BxCq\Ue[7ײne)[`^^mXB!B1%𖕗lI7qkI#/hvn!B!]zoY٥<:!ux^d˪YthK*}u׾S{UkqHGhm?Huժ6]Egٸ pz8tlv}ݍ4&B!^erimm=;wک;xi,_!~G.^o~~Uh*au ٜ>~9Td郯8Wm!/xa쯥W Yfp^9|6ZtreXnF)%u/{5!B!--7''y SO_@&z)5vp,.pނ\V[nVǃL\x+hupДJG$1́\MC9))EWV2y0:F-A p$IKax=eNm8@N~ikN9^ڤn>xp5#)Kn4u4bY9Y$S6lx)9zμgb:5|#"<&Q:( Z$S{u;fA3}\d ^T!Bq}IK;o>۷|{vHp'\;JnДdX^NnHuJL8LI\ś7KitWȦc:֭aO^Huq\ϝD4pͬm f~4}lVMcN 9Q׍\\#K3-=d̆SG4Cg8Օĵ]H%" ӤuMr-a<8ŶM$kۨdn^='X`û9؃OfŔmyIWt{G;4cxrgrY%ZKlSɈFE'뺰\לeK< 8+ڏ"8+YTts{HMtIbB.\MC= Ab 8=u>),Oq0=;{ !B!/mUWKWjj***dQJXy,25;M H =8|Vlk$`5w@bKWrX%7Tr%,czϱwWo\/=l}ʕ쮣5QDHq&g~>&oY{ 8v?+)kmG,]Ǭl"ԂUܾqZvB4o ͙IW^b*"+B!u)?Zy̛?Uk\q9Wϡ@GΓ|eZʒmm͝΂yDdN*&Pde1“(1PYUXœSDo;1 E2(*/Gvnb\PC}Rjd :Ye$It4S4›]"RINEIFEoW vOfed}xձ)\݇pͽ)|  h )\e~NVq^ b˻NR U^IAkJ1< i (\F$MWm´B! 5aV]Ceee:=Cee%׮!Ǣb助[yaw ,<&Rx|&$;yGNf`P"U 5EgQ\$Hd5g$LugzHHm du:i`X*eЖxzTu$rtm`~1%M?7C7jF=O繱;5Լ>$IX$ |QHxQ>L"S3:N2AJ36^?"q+T !B!Ɯ1c&wW%VVVr[̌3T›]5wrǺdwCmTpqI/K]HTJFNQ4)>VmX#K$HdRrjn6ĩڱƤIƓ,29QUOk1)<}-4H&pofOs`aNc4I88Ixt/ HZ͊tދi$Hb$3úB!rU~Z 7SSC ?Tg =V%i'Q¤K{hX`318 bEn.kp+-ZXN(7B) /TkdN•,(imx1 I[wPF,kHI~In;Ehb2.sȡd IDATyB1q-La^5Y4E{) ɚL:߹,K'{2|`әvb YxS))N[MbJ~ ХG~i3mCsH9> oZNۧgQ^x|7.'&VP%!ϙ25^䆺c$qɠdb M|*"L^4;xct* ܾfjkP22 목e謧x'JW폛B!b s?tTRSS}{ٷg/tN~~>gWvd2<̑=`]AG ްe| )L|}X) σűm\_>1hp}v~Ǵu) ͮ(Mֱٛ] ]UV*A1M9]$Q:X$) 9@}4MìzwG* Lb9s^EՒj-SIܷ9ǣκv s}蟱ŅnF;c{DL5F@QfܛF-gqNYgrx#Z" }nXey|עt/>?R7C R4{5El8) tӫѫ1sgjRhs;@5FqJuoƸ^Jiޣa1?'0|ZQW!Bq!teNN999,\ZV{Ѱc[r-Ȼkt ދyI"XB!B"_޼YO{f3i-^gw"nn^ 2r-qB!KyRVRFt/'נ!B!D6B!B1.IB!B1.IB!B1.IB!B1.IB!B1.IB!B1.IB!B1.IB!B1.IB!B1.IB!B1.IB!B1. g^BFў׻ B!B\W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸ$W!B!ĸd\ڨ>^Muu5ǫvG233̤rr%TN$//Z6O!B!8rMoUUvb玝477yLss39r/Y̢ŋ2eʵhohplw4u[#B!h4ʆg7qF.f{1l­oemD"im_5o yxpIt5r~OIf:9G 21ӠcI&Ϡ$pS474ޏˤ״c] L=F!BUK^`3عs#~}=sIS gϹ{0j|L|L1oJXAIpV1 ngI+yN$Fa/ۻK0v%BizIYea蘦^!Bq*0 ƫ?*XxAFG֓Ġ➏Y /;>ZVpMyKs}:dDQ/`odf^>Ӌ*0t' !B1N5qEQ6<᪄!ǎc3())Iۜ}ߞAÁ_׌oxlJ CB!i݇wóW>gRܹ nHKY|/dg=[K-`fpt:/ QC~̓G+n _ f !B!ު*6nؘ.jㆍTUU]Y!v+O~y4.%~@!S LcN ,lvs&@ :v5u6X0R5Y!B!ĥIې];w]CW];w]?|<x˃=4~V5}G2nXy\r7cBD[y<ɑiB!BqM;.c(fzϸܱo0)ڣp5=^>?|d%!>Ջ.Ώ})J)4uVsx-t:pm{| 8p!B!镖}GuvrzH{X%RX8e[qL&2N2ıyˋ'qĎEŶ$Rl")(6 ADn;?P`yAﳖg}Z}=SKKK-O]O-%䌙4L,{CiiiL-2bG})WðmEk?_uz$-5=&f?׾!fMц0=TR&X1"""""")1vBLwgmZjB׌0,lfk$e0~SwJ +HzF /[x6;`탗+}O=͑}{um/5fU}v ooDt""""""=>J+|?,O;zKw0K+""""""IH୘^1GxW91'3ybGOm吐c**&:M ,?cǾ~Ј3)mߪ8GhNÂqO9{ Igf仵4Țbp:A]IiOaBBscE#<Ksnd+%&N[(r?7_?yNnAA$mmBf nwOġ o@8FzJ"}tu `Rn8j\W:s'x6#+$C+G_DDDDD~~%$|r^z[*91jo`q=^~xC4aqE qgyD 8{q"8/Ưi*N$:N٫zcr0"""""rx-_]Lމ`ewVs;HK t>ro9n9ٲ 6"(Xm{tx߱YG޳w5D^́ KYNə9uϗLV|߷tg! 2#4Wmn< qH.GFW57<;6jp=#6GixE xޘHK?3S^{eQ6JY@o?OQy=oq>}O>ZF6l[Xst%14 ;Y%wpLLfx&Ndգf̘6>ѳe-+ZW$wmY$8A  QqUr>L, c~c%cnvk pqm\᳆`$ۘD, `$QTb˛Ɋ3mV1!|N'8+6 㪡>!+J8@` |E '&K&8m+Lmc0 }&V6=Ý9v$B \U`;I0  TF#@0iw$ct*񊈈- ￐;c ӣm9@,l&+{y{>vNiהb="<8‘Kec3HH8#)8;^O5R௰<SeN#a;ɓ Ɖn?9"ۑłAi,~` ޷=o&6D<펇Y~q:Ѝ13/c :~27o { l j_0cɻas6Mw80i]"ah,"1?xĢ1<:-""""r$tIN˗'8BΰiS( sWhqL)S H1 z43z2MS)SH뫧y"5)5 1|).)fm{sFFnzMy,#j8N`*K6NOgq/k~w_BKo)N<ޡ!觼phZߔ, La%LH/gj{ fVp~gL~/)f I]ԟ:EmZ&H[ĺoc9ixIZ}<03mkoȜmlgmTJ 0YDDDDn1>dffOJ#555:HՁ*ZZZn~PPPY||#%>BA2av Bp0}$^L ۼh<2Hĵ Ǟ;ͪ'2Hص n1;޿#MX~!pc(̭':8 \>ucbޥüZ'H5Hر i 'Mv89]2'⎁ev O<+` sr=s/DafP5 d!oabM p`'KL!ye'`${'YW;:\.?a[x⦻]ODc75 Lmia<:ɛJ6mDZjkk=SKWW̯S1 *W;}3,?kwb2Lk4$edi봈Hg L{ $]4+3qpcD_0Ho];j=ukXkgK Иݝ k8L° l4k _{=?|^Z> ҵ[krg'@vvII9K4IqW\rssYzl##^C Pl콝3us'y] VXM/CFl|7no ˶lƸտzWY9CZ7."""""FdR';܅vL?I)X 㭂ɧՕ""""""rOR{ܓxEDDDDD䞤+""""""$^')=IWDDDDDDI """"""rOR{ܓ{!""""""P{Hb1nJOO+"""@hM6uVzEDC """" 4pϟ۹xB{DWDDDdgժUl۶K.)􊈼xEDDD&Acc#ٶm ""w$b444}uե+"r)Lh4J,O|lܸQ7e """"qbXj>O^<{("rS$#hǏϳqFmۦ+"r(L;2{׳}vzED&$ #СC|K_b͚5رCWDd)^-oͷ88^x; k_]{EcȿWV^ͮ]Pl:멯. ==tJ(--ٽ!F_.%XQ|0AN}^cޯbu{p]]+8tVGQ" IDAT7ݕ{Y9̑CikkL[[mmm //EKpbF7__<%%%lٲo|k_b6~"QII ===޹ݻv=kxg[]ד6)tکog - IS]wLbJK[C=6Ŕd1(M4vyN)<7xnu-s(20ǻ~N م(Mc =~x\lҧRbt57Pw)Fr~ ;S^^ζmHKK?1w?Mcc#+W+""5iڱ#Q=ݼK;wu3{pH0نe|8'Ymr?r-?6u'7E}If#~pf`e?|u5PS938ԢVaLyoշE'yk)Ai=D?ĺ7yeAoy/pó|YȊr1c[n__27?>xd,K?5LIٴKwv:r0'OHXDjwޤ)agMg3;Ōyl\Z,qOǔ8;lzd=K}sGSȃ/BNo̠Všֱ4P͞fae%sVnj^wتkz<xaμo{c=)h;vZDDN?T{9 7Yz5G%߼"m  Z^y%.|+[ޱ%{o1sǢci48ʏhm#xY#_-/3{.# >1'HlGݗޠ9~Ҍ5.L~9~snf__geLby\|mf^\RTź<˳DĨnڼT? ~kI 2gݴ:}W38ÏcfΜҥK ݻO~|_e߾}200@RRƸSȝJha׎vGֲkN HW'}.:f (δ^?;銸`x XWO&S2)) MIA;9$ <\+1-ȧr*x8Ɏ>D<|+WwiC{~?~m-8r07/x=v![>s|lyOr c_FosmbOm~3u.ii С~ Lcyy7s LONgY<Îm{9\%d&00ͫz k2>V*yAbSu3}=}R:i)[k(Mp1xkٛVR fSq}z*X;ݖ޼yБzۏՍG6~uţD]uNx(Ѹa]4 i |6q1G.pdQ"+x`E˃BŻo-i9qudSBsw~t{/[‚ؿq뻈{.$`VMǪHF(3!6C2 ;b ''` m`fSrR<ƒf4Js%TL~i(\CnGx0 vJ - LygGh8EDDDD.Kiii۰ޞjkkY mXOZڝkee!;ΚCar 0/8&q+Hh>Mv 5>ą0V(P&wc //5 ӏLF ׳ctz F)1?`a _S\ %Cs#7Jw0+p$g- x] 7OJ&d[Z}wyNj(E| 1 ģ13y0|G_ m(p+gs%cc 8 5IDDDDD&,̞=W^|)ᡷ-{b;gϪ`rD!NaA5%Xp#x>04n"qIZN,3"ݖ< o=t{8KXдkM6N4:&p9ĢJ~7w%.F4? HIaL D.[/iiƞ4Of֖.qǻ)X~u{b.v/!;fi'&>DqJmDQ.4_~Lwvo5|7`X>,Gfn:NKz#ĹΑ{p=|x7:f4RȡsqBӖbKM-YPyyKXVAΞ B.x1Vυ{'HnN2qnC7;y|n9/FQ$ 3h€;ϋg7%D psӴ´Wé*{\gxG̞="ؽkmޜuX>1˘rϰbspyYc2 +PP3y|SNQn}l3Pm{8Nf0)t)ON~Vgc|7Ѹ~l*gg`I+R6m. (Kn{8Ywee_lݯppeMZ%$] a$duu;Ξ=#9r0mmmtM^^,fwxNh4c|Cban {`6Ab=s׉03 ,ucD"q Cϸs}cqOo1Үu#8.#>3x|n,Lı|ޕ=7F$`Lr<0Lq?e /l=ɑw ,pDֲ"xh_oyiDh>)0;X8 F4B$8x=X5l,ӣ`bHgmqsDx܋ 0IC$ `CJ"d"޾ /.,6EDDDDZ:nX /ԤI/|ዓ7;Vyy9[:멯{h׀ RS(-+Ҳ2'o#0vMk0 @ҵfH9^s/y9<_Ӯab]Ua7}F8˸ai^۸mݬ?9<'&u,?,g`Wa^9>B~E@2%`X՟hC\?`h.@f q.+wDvv6,]n6+w >׸"""""s_TŬ˱)~ 'N>)eX_⿅ DDDDD-0}!], $_$0IwyoL${DWDDDDDDI """"""rOR{ܓxEDDDDD䞤+""""""$^')=IWDDDDDDI """"""rO!=-ꇈܦ޾ """""?4+""""""$^')=IWDDDDDDI """"""rOR{ܓxEDDDDD䞤+""""""$^')=IWDDDDDDI{݁_*w8 aF:4{ ]8L+ X93Y ]v5a ϣb.f\_ v7K9OE^o鿱fR1R?7d<ޮHݕ[SSTe2---p) Xb9˖/nt.`n;v]5ռ; -O.'A ݫ/ IDAT-3xÃ|,oTf'_z'z<6~1 `LOvmY8G^} M];k˕xX|:=Tq SJhI_S9ZBsx(5[w?PFHŬ?22k m;m82Tcţ~ʜFiPj32(M78 M{+ÿoN~mq uGq 躷ib=h7HaIAi3cx;}۶ǝq߳'xVCwU2k8Y˷wZ2M 4c=o[_ȁy\(8տ>M M7}s#i-}muY}֒d?|SVob+g޺?ZY=E4$DDDDDD.}=!4SuV2X>>kXfX|-&U|wL0 mZ&ab,1[G NzZ}/|`·+z9SW[]ғLLü&մ-,^mQi>ϾWb9 ;`Ӟiۘ5­"JL}8que LOk;Ŏm;x}q&4+ewt]}勺BH$Ur [֛)B!Wى1z=s+1wQI4gNH \\r@tuRt}2Yvmk\ }"t"X6kx]6SJ:.hN1|NJE&@ 0ɊHXꍍ:krʗB!1D2I,'L`X"ohY!B!wIxӯ|l_bO}v Yv_ZZ -B!'I"d8G$y/HJ ɋ/ѽyfs}K+B!wD2I8:J4:eؖCt4N8::oһ o⊛:?cR5;s;lw*וB!B;G,'&Ibs| V\g|6NN uFtm_s8uߌ-{c7nw{(/ͰqD s~;ڹO8qǻ8tu},jfoB!Bܔ.'9?_U+WGg5=`(v޿凪u[ݢ^v:+XQ近5nw6JS mq8c/H0p mZVXnڂ-/ÏN{HkEFM-gýwQ˖ygizZ])i4ΫM`zIcN-n潷?uEWmn*v EKm``kToɺ>>xi7FP~'XxO+e+4fl=~ȿqvuqE,Dp=N ci> n*ӝl̇oΩ,Uű< ξΏyRxv24v\_kwlciڕJv](x0Tybd.UQn,V"Qe'|YB!B|L-X[YYɎ;x-TWc*++ox)NG+xӵdj.Vt(.S}w^$>+VՙKYTn+0Dv> 97щ::@䞃m (mwO ҭ69Q>$F98JЙ@<``٣ܻiZ&%ed}Vj}GujKF7ЧwT{N4䁥lxx+]qw B/c'ws>3%4 MkHMp8jװeg!GQ (*An',}ӬHp1 0;X;GI0;\) мm( 'ZoОҲߦqpLJy-Wo=E!U"8LX5{<Q}bV\NQi؊}>ab3N Ç rܽ9ŠsǏ f]wʍRf]gJn,Z/-).+x0z%H'}^JҸdr!B!n|Ŋ<<魪zz^懗qwcmHUMwqM/uy|x~F0mbmГ4-5mxfz$Y39i'.'a7x?{qO4bq\o6)G9 _g; K{8wG ?e[&1Hw8G0k`4@Xeǣ0fϰ,ezN_Pê9V+w]\,U \FNJڸ*S\줅sEaYdAѧ;ϦXIlEҗkÔ+k'I:*1sq:wyӏ倦;L(:+9V SW!Bܙn0DBkops&! ݫS-iͽgmFȭFۨo$,g9ѢQz1W+R S R0+{9UMΪ<5:P2iyGή ;~{kXn3cָ̹'=ҕ,g;کtQmShS}  KgF]{]͚ @}}6(X^$={@!.]Bײ"a.=`*V'nOЗU%^멿0HH)\BMY:&. G9)&g|AK'ѓU>b9=FIKw =g)kdqy|?Iϙctɰ!&Zh2V,+$(]N{PfdPl ~7- $Aqm%@U, i=]Gۈ8خNz*ytS2@qP,+{Dh?EKT=lX*jrLpct;MCOl,=e~B!7ͻ\b%%%oiiiعfT@oFB7] S{1Ì-#+|瀅koY&*#PC`bWTYuo 6u.'C<%lgݻ_继tǢ9@/Ы(يJYu!gQQRkXZeSy}|琇Ma,1L•8:{ٶTo喱0ԉlkCQq:ggomfRB XC-o"-Tcl uPmXJ-(.ןa8YJMBoY:G[9ʹӗPX㷍3:Hg'fj~PQb*@#Z*XMQ*Ghp*Ec p%UXg15Hu؄;,Z]ٞt%\?z'#gI{m6y+6RD:9{ kXkZno?u0ul{,Y>8߀UM(@PU*34flS)!b/ZOR;j6ᐌq}* tE]_qcTZ!W/!P};zU+9t?8Hgg5 Y~M<_v.I'>ާX4e$qR~`+Qx{'d|'h٬SK'AUl~|b$0yi\+e$Fc$][}WdOLeѶ')/_'Ǐ:T/ON)SjdxO-T<^m<]N[{ЦǭYmƑø^ŊLj+ oJ;?UA"'i>/xe<]^2.i+f8I~z:}z@xm<񤂦'p)1}%< j]<^a4n{u/'<)h@ݏt `x+Q}dwLb 誄dEOf>YF0>@{꜂O\%0YyG7E3ݜCv"mi;[?Ssy[)A^{)MQI,ċHFS* &5UǕ_VЌո-Nxa3Sjp]4Zhj> &%UT)"d .EɤaLYHL51u^'I@NI{S2KPwb?QW,wUov1y]g?MUPgUUW!QǙJ4qiGQ דEŲTtAAIUşK~ŋ)ԁN4 @2vh)Gh<㱨Z9+B!wgW~kܢFqPRwM~O?83*6c(ڔ}b]ǁ6jSu lTt@RAl2fX$6nG;1OcN:خvg7~^^%ܝZiI:#&NЌ}s'p IDATH&m\Uǜ_L`c+:O#4#h-ƴ6s+BQ4ngwE?^!U4uƪ(Ҋ9k%(ݜcq*ef}st)BJAd@1=ZSLSIEeV~|8GQg/hƜa)qyUiu͘01#KzE!N% X6Zz9d/a* .ZZC_B!Ǜ$B>5H5:J3YKX`YIlGA7ͫX !B| H+ӷOvS() ,]WX!cHV-B!BqGW!B!I^!B!w$IxB!Bܑ$B!BqG=B!B,aodqI!kWuZ;4!B!3 ܫ{(+ލDc1:G0mp^!B! b:ʋ y sΏRw/)~?'5y+B!bAt PYZ82R$2٦$B!B!D<4YF2ix$B!B:oa! B!B$B!B!L !B!#I+B!$ B!B;$B!B!H !B!#I+B!1Ox]gw<];!B! ILKK -͌ 388.B!RC!h"ykJ4ڣ_)@-~DcC+ ʇB!'(Dc1>C#X PYZH8:޾qے655qQ9JwweyrrrXUk)//59XUzK2>.D,gu;!B!OL;{ٸu Tg߶xni;<<̻w= ]׹ݼʫط۶uvRSS8BmwŽsIVQ1iO'@%.tWS-.$3Y&9KwB dbsPA)2=s=D]Yd[y)-hFL66øLJsI3>&ZQ.vd(?in}]#wDPi{n{#;t/d.V =  >y]7ӗxKHQ7E#=E,r\;Knm/9EELB!Cr=LYQ?v/PYRH[WZxd-Kxn=zS ҋ/ƶ{S]]@N/o6g?lⴼM)&>ͳ%[s/~ar]I;YAS?y];j}=>V2H[Pʼn:46|W̱3sx|{Io||vypd/ ^D -_yZF!m/~9Y_1Jz3בIS뭄'OTT&;Bxy)$|oަ% ܛyWNXWM@?y&P;666.t466 /,Hˁ[g84k`pIa k]+ Yai^rrz1/DDcAiÑ/,-_d`Q7nW`csW"@W jaol #rQ2Rž.'9Ͽa. K)CNı\ O[½bƏq`îByr"LJǯwRw"+Of#tU^DJ\"Uuޛ z\Ȩ}OU ]ԽʹM oXBO|B!B\:;Mx[Nhlld;),,#_>2s> ojiv,F㥰!hzoZLSAq.g֒1}:qF-=ћMe[N{0a yBdĒ뿼B!B;Ԃ%ǏnǏ]d,C_揾ȺtZ{)i0k盗t՝-[Wrb>x"?XNe(@W燫<2Kpoq~p c+KIvȒgOk w?ȯ2gOmUW(dYMg咡PrzNM۱o]45EY˿g*_#MY7hjdٸ=h:RzT g~Y֖b u8H~/W!B1fAViؑʬh1esQnyhy[_}>b>mKI%d*q۩ ;Oڛ.x/?Y_Q&(hi*LJV54EEQnr1&|z\3Ja5 jaԗOˆsx^

W~~Vd xD TR&2_|7O)ۄX[-ʖ匒֗eMI~5ѫ>]!B1hpA'+sw*":2S?[vkS$9!P<~> _W¼[B! B{'W􏍄}k0S[ZZnn^Etx=3S<ÛB|3dU@!EEØz5UYx;iV:yG5||W*0?xW,1GsXZ~"4{cCEy _ WB!ma慨vl -pd1y+^ v-{!B!Ȃ$÷qFFo K e—SɖmT<4q=lKs+XX !B!<$Ὕ3488x'KR]p H[̖lCB!Bf臹X{"B!B!>~$MKK[jnH($ !B!mA`0u!! !B!Â$B!B|t-L»hBTjb$gbK\7'f>k;ŹS 'x^y+B!X8 J2rrr)XfsFxm"}X%TzoM.'F%U)8 5;,8K9I[T#$či,o;^NuYo=B9CQo>74B}iPU1]uATcoաn 8(Xo-Ψ^Vma]i *pI:F]śk9}7 b bԤspv:gW pdI:㠺6{԰r%G!PM+ )I:CsbBhp\e*ruGURJ(OۿKB($ǡ㱦RnxN6UoaUp N7tu4y,^5E{8O~uV)jdVQדǪm(βgO/" 2G3~/VmfMI%B!Xh 𖗗u6^zūiukomT4e.0Q%B|lJK5xhs J{q|۪fPXl99b8т8)Гt͡cMdnYLAMj.sUd8rI3ξ{g(XI;L[Ow{<}Ed+;tHEys7 #V7]|U^#`cX ⫹ hs쒗۷S< TzZl 55mlgdxƅzREENje.cfOc#PWE1; _(qtthۛG1 ކpopFKdZqKULqs͋t[g5B!B|4^\𤷢Gl`fTPK< *&]8ab\i gUbs$*At}{1>#uxa3RMJ7m"0#Hf34ݦãab۪_[蹬aOV3B\hW=D`MMd閍 G05DˍM2O1M 3w%[ksrĎFICFF:  CFwg;M/ ]ܪ-G9x<{u_m;F,y-B!ĝ疬S]]#?ӛW.p >䐟ŵjuutaN6~M:¬wT} 63j 28Bp0 ;'5X@~`s.N"H 00 c|5eXmgiHߵ ,bdhMdpB.guC|b065IlR\N̂LKgiCjΰB.jt^ǻx=VauvmduQzP~>e P | ؄Hb;N}Gtw4$ȥ:N7hgN{T0B!VX 5QTTĻ{\s BlݶfTpt t6sٔA]@ eG-j6V0DSlީ-h/Ȋ~":.z ]NNY ɬ(!g(z貖Β +8›/BUqu2m9h sݗ9j(_{I)* `jC5tl̥l(NJ K9ڨ.kR>֦i+^͊%N*7L/C=9/s uP|7O!-+((_I}sYT]Dok$9]]oU9gf.[Kl `"B4^}|o5EA!^(}@λmgiي-`>y\ɒӜr;K+4? ^guaQNErӠq6S7F1ѓoGc!B!^1$IXKP57w9r+9tw@όqvzONi{Ә(Io>ya0ګa.̞Ї7 iƚwQܛMI-QWqvo2=IDAT- =|0Fy>,QFo .S 1{^W$a(ӸNF ʱ,i%wKdID%XWPf$6agI7qeq0u6f 0N1ٍc^1֔4U'N Ǧ.1^qwzGq|=HK6r { M"B{P{[-r[@ҦzTRV+ O7^6W6!B!xI*XjGU:uի_ o$\,/-̯aj!4 M[2))B'X*[71qfpN;,."Jm-9%׋1rp e<iwʟNi/֩1R ܜx+O C"zX )fϒ-8C9§`\KPz'[+BidJX{rB"ǧ dB!K&DaHϺ~[:zsorujj_?6FZ<e?(e!B!zrm <|嵂TeM13j\ p,Ɩ$A飧G$qLlC4>_~B!xm)x݀9U-[ gސqj_ $!B!ěؠVGO["B!⿷߻Щqۇw~>!B!of@]|'B!Bȵkߨ ] bIENDB`linphone-desktop-5.0.2/tools/000077500000000000000000000000001434616504300161475ustar00rootroot00000000000000linphone-desktop-5.0.2/tools/app_notarization.sh000077500000000000000000000045621434616504300220760ustar00rootroot00000000000000#!/bin/bash #Notarization for Mac. Launch it from the build folder #rm notarize_result.plist FILES=OUTPUT/Packages/*.dmg for f in $FILES do linphone_file=$f done echo "Uploading $linphone_file file with xcrun altool" xcrun altool --notarize-app --primary-bundle-id $MACOSX_SIGNING_IDENTIFIER -u "$MACOSX_SIGNING_MAIL" -p "$MACOSX_SIGNING_PASS" --asc-provider "$MACOSX_SIGNING_PROVIDER" --file $linphone_file --output-format xml > "notarize_result.plist" echo "dmg processed. Checking UUID" request_uuid="$("/usr/libexec/PlistBuddy" -c "Print notarization-upload:RequestUUID" notarize_result.plist)" echo "Notarization UUID: ${request_uuid}" #Get status from upload declare -i tryCount=0 declare -i maxCount=4 for (( ; ; )) do echo "Getting notarization status" xcrun altool --notarization-info "${request_uuid}" -u "$MACOSX_SIGNING_MAIL" -p "$MACOSX_SIGNING_PASS" --asc-provider "$MACOSX_SIGNING_PROVIDER" --output-format xml > "notarize_result2.plist" xcrun_result=$? if [ "${xcrun_result}" != "0" ] then if [ "$tryCount" -lt "$maxCount" ] then tryCount=$((tryCount + 1)) sleep 60 continue else echo "Notarization failed: ${xcrun_result}" cat "notarize_result2.plist" exit 1 fi fi notarize_status="$("/usr/libexec/PlistBuddy" -c "Print notarization-info:Status" notarize_result2.plist)" if [[ "${notarize_status}" == *"in progress"* ]]; then echo "Waiting for notarization to complete: ${notarize_status}" sleep 20 else echo "Notarization status: ${notarize_status}" break fi done log_url="$("/usr/libexec/PlistBuddy" -c "Print notarization-info:LogFileURL" notarize_result2.plist)" echo "Notarization log URL: ${log_url}" if [ "${notarize_status}" != "success" ] then echo "Notarization failed." if [ ! -z "${log_url}" ] then curl "${log_url}" fi exit 1 fi echo "Stapling notarization result..." for (( ; ; )) do xcrun stapler staple -q $linphone_file stapler_result=$? if [ "${stapler_result}" = "65" ] then echo "Waiting for stapling to find record" sleep 10 else echo "Stapler status: ${stapler_result}" break fi done spctl --assess --type open --context context:primary-signature -v $linphone_file #validation_result=$? echo "Validating image : $?" #if [ "${validation_result}" != 0 ] #then # echo "Failed to validate image: ${validation_result}" # curl "${log_url}" # exit 1 #fi exit 0 linphone-desktop-5.0.2/tools/build_qt_rpm000077500000000000000000000043131434616504300205570ustar00rootroot00000000000000#!/usr/bin/env bash # See: http://wiki.qt.io/Building_Qt_5_from_Git # See: http://doc.qt.io/qt-5/configure-options.html REPO_URL='https://gitlab.linphone.org/BC/public/external/qt/qt5.git' QT_VERSION='5.12.12' RPM_NAME=linphone-qt-${QT_VERSION} REPO_FOLDER=linphone-qt RPM_FOLDER=rpm-${RPM_NAME} # ============================================================================== RED='\e[1;31m' NC='\e[0m' SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "${SCRIPT_DIR}/.." # ============================================================================== if [ ! -d "${REPO_FOLDER}/.git" ]; then #Simplest way to use paralle fetch if available #Otherwise we would have to parse the git version number git --version git clone "${REPO_URL}" "${REPO_FOLDER}" --recursive -j 12 -b ${QT_VERSION} || git clone "${REPO_URL}" "${REPO_FOLDER}" -b ${QT_VERSION} --recursive || git clone "${REPO_URL}" "${REPO_FOLDER}" -b ${QT_VERSION} fi cd "${REPO_FOLDER}" while test $# -gt 0 do case "$1" in --clean) echo "Clean..." git submodule foreach 'git clean -dfx' ;; --*) echo "Invalid option: $1" ;; esac shift done git submodule update --init --recursive #git checkout "${QT_VERSION}" #git submodule foreach --recursive 'if [ -n "$(git branch -a | grep 5.12.12)" ]; then git checkout 5.12.12; fi' if [[ $? != 0 ]] ; then printf "${RED}Unable to checkout ${QT_VERSION}.${NC}\n" exit 1 fi ./init-repository --module-subset=default,\ -qtandroidextras,\ -qtcharts,\ -qtdoc,\ -qtlocation,\ -qtmacextras,\ -qtnetworkauth,\ -qtpurchasing,\ -qtremoteobjects,\ -qtrepotools,\ -qtscript,\ -qtscxml,\ -qtsensors,\ -qtspeech,\ -qtwebchannel,\ -qtwebengine,\ -qtwebglplugin,\ -qtwebsockets,\ -qtwebview,\ -qtwinextras,\ -qtx11extras,\ -qtxmlpatterns \ -f cd .. mkdir -p ${RPM_FOLDER}/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} #use pigz to parallelize compression tar --transform "s/^${REPO_FOLDER}/${RPM_NAME}/" -c ${REPO_FOLDER}/ | pigz -p 24 > ${RPM_FOLDER}/rpmbuild/SOURCES/${RPM_NAME}.tar.gz #tar --transform "s/^${REPO_FOLDER}/" -czvf ${RPM_FOLDER}/rpmbuild/SOURCES/${RPM_NAME}.tar.gz ${REPO_FOLDER}/ rpmbuild -bb --define "_topdir $PWD/${RPM_FOLDER}/rpmbuild" linphone-app/build/rpm/qt5.spec linphone-desktop-5.0.2/tools/check_qml_syntax000077500000000000000000000022351434616504300214330ustar00rootroot00000000000000#!/usr/bin/env bash # ============================================================================== # Tool to check the syntax of `.qml`/`.js` files. # ============================================================================== RESOURCES_FILE='resources.qrc' LINTER=qmllint RED='\e[1;31m' GREEN='\e[1;32m' BLUE='\e[1;34m' NC='\e[0m' SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "${SCRIPT_DIR}/.." # ============================================================================== if ! [ -x "$( command -v "$LINTER" )" ]; then printf "${RED}Unable to find ${LINTER}.${NC}\n" exit 0 fi "${LINTER}" -v 2> /dev/null 1>&2 if [[ $? != 0 ]] ; then printf "${RED}Unable to check qml syntax.${NC}\n" exit 0 fi printf "${BLUE}Checking qml files...${NC}\n" so_far_so_good=0 while read file; do $LINTER "$file" if [[ $? != 0 ]] ; then so_far_so_good=1 fi done < <(git diff --name-only --cached --diff-filter=d | grep -E '\.(qml|js|js\.spec)$') if [[ $so_far_so_good == 0 ]]; then printf "${GREEN}Done. No qml error found.\n" else printf "${RED}One or more errors were found. Please to fix them.\n" fi printf "${NC}" exit $so_far_so_good linphone-desktop-5.0.2/tools/private/000077500000000000000000000000001434616504300176215ustar00rootroot00000000000000linphone-desktop-5.0.2/tools/private/pre-commit000077500000000000000000000002671434616504300216300ustar00rootroot00000000000000#!/usr/bin/env bash # Check QML files, quit on failure. './tools/check_qml_syntax' if [[ $? != 0 ]] ; then exit 1 fi printf '\n' # Run unit tests. './tools/test_qml' exit $? linphone-desktop-5.0.2/tools/test_qml000077500000000000000000000035631434616504300177340ustar00rootroot00000000000000#!/usr/bin/env bash # ============================================================================== # Tool to run unit tests on all `*.spec.qml` files. # ============================================================================== TEST_RUNNER='qmltestrunner' RESOURCES_FILE='resources.qrc' TEST_FILE_EXTENSION='spec.qml' DEV_MODULES_PATH='./ui/dev-modules' MODULES_PATH='./ui/modules' SCRIPTS_PATH='./ui/scripts' RED='\e[1;31m' GREEN='\e[1;32m' BLUE='\e[1;34m' NC='\e[0m' SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "${SCRIPT_DIR}/.." # ============================================================================== if ! [ -x "$( command -v "$TEST_RUNNER" )" ]; then printf "${RED}Unable to find `$TEST_RUNNER`. No tests can be executed.${NC}\n" exit 0 fi git diff --name-only --cached --diff-filter=d | grep -Eq '\.(qml|js|js\.spec)$' if [[ $? != 0 ]] ; then exit 0 fi # Check all `*.spec.qml` files. so_far_so_good=0 while read line do source_file=$( printf "$line" | sed -n 's/^\s*<\s*file\s*>\s*\(.*\.\(qml\|js\)\)\s*<\s*\/\s*file\s*>\s*$/\1/p' ) if [[ ! -z $source_file ]]; then spec_file="${source_file%.*}.${TEST_FILE_EXTENSION}" if [ -f $spec_file ]; then printf "${BLUE}Running unit qml tests of '${source_file}'...${NC}\n" $TEST_RUNNER -import $DEV_MODULES_PATH -import $MODULES_PATH -import $SCRIPTS_PATH -input "$spec_file" if [[ $? == 0 ]]; then printf "${GREEN}All unit tests have succeeded for '${spec_file}'.\n" else printf "${RED}Unit tests have failed for '${spec_file}'.\n" so_far_so_good=1 fi printf "${NC}\n" fi fi done < $RESOURCES_FILE if [[ $so_far_so_good == 0 ]]; then printf "${GREEN}Done. All tests have succeeded.\n" else printf "${RED}Fail. One or many tests have failed.\n" so_far_so_good=1 fi printf "${NC}\n" exit $so_far_so_good linphone-desktop-5.0.2/tools/update_resources000077500000000000000000000020651434616504300214540ustar00rootroot00000000000000#!/usr/bin/env bash # ============================================================================== # Tool to build automatically `resources.qrc`. # # It should be used sparingly, it adds all `.qml`, `.svg`, `.png`, # `.jpg` and `.js` (contained in `ui` and `imgs` folders) # in the resources file. # # If you don't want to add a particular file, do not use this script! # ============================================================================== RESOURCES_FILE='resources.qrc' SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" cd "${SCRIPT_DIR}/.." echo ' ' > $RESOURCES_FILE for filename in $(find ui/modules/ ui/scripts/ ui/views/ assets/fonts/ assets/images/ -type f | sort) do basename="${filename##*/}" extension="${filename##*.}" if [[ $extension == @(svg|png|jpg|js|ttf) || $basename == qmldir || ($extension == qml && $basename != *\.spec\.qml) ]]; then echo " $filename" >> $RESOURCES_FILE fi done echo ' ' >> $RESOURCES_FILE