From 1f91576a0bf33c9d632595cf433d547d1f1d1e06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= Date: Mon, 3 Aug 2020 13:27:42 -0400 Subject: [PATCH] client-qml: add initial commit Change-Id: I32bfdd2a618aa7ac6181da2697e241667b010aab --- .clang-format | 101 + .gitignore | 21 + .gitmodules | 4 + .gitreview | 6 + .tx/config | 8 + JamiInstaller/.gitignore | 3 + JamiInstaller/Config.wxi | 19 + JamiInstaller/HarvestFilter.xslt | 44 + JamiInstaller/JamiInstaller.wax | 9 + JamiInstaller/JamiInstaller.wixproj | 70 + JamiInstaller/Localization.wxl | 7 + JamiInstaller/Product.wxs | 135 + JamiInstaller/StandardComponents.wxs | 86 + JamiInstaller/main-banner.bmp | Bin 0 -> 461814 bytes JamiInstaller/top-banner.bmp | Bin 0 -> 85894 bytes License.rtf | 869 ++++++ README.md | 194 ++ changelog.md | 203 ++ copy-runtime-files.ps1 | 99 + ico.rc | 1 + images/FontAwesome.otf | Bin 0 -> 134808 bytes images/ajax-loader.gif | Bin 0 -> 673 bytes images/default_avatar_overlay.svg | 106 + images/icons/av_icons/delete-24px.svg | 1 + .../av_icons/fiber_manual_record-24px.svg | 1 + images/icons/av_icons/mic-24px.svg | 1 + images/icons/av_icons/pause-24px.svg | 1 + .../av_icons/play_circle_outline-24px.svg | 1 + images/icons/av_icons/re-record-24px.svg | 1 + images/icons/av_icons/send-24px.svg | 1 + images/icons/av_icons/stop-24px-red.svg | 1 + images/icons/av_icons/stop-24px.svg | 1 + images/icons/baseline-camera_alt-24px.svg | 1 + images/icons/baseline-close-24px.svg | 1 + .../icons/baseline-desktop_windows-24px.svg | 1 + images/icons/baseline-done-24px.svg | 1 + images/icons/baseline-error_outline-24px.svg | 1 + images/icons/baseline-people-24px.svg | 1 + images/icons/baseline-refresh-24px.svg | 1 + images/icons/extension_24dp.svg | 4 + images/icons/ic_add_black_18dp_2x.png | Bin 0 -> 237 bytes images/icons/ic_arrow_back_24px.svg | 1 + images/icons/ic_arrow_back_white_24dp.png | Bin 0 -> 151 bytes .../ic_arrow_drop_down_black_18dp_2x.png | Bin 0 -> 205 bytes .../icons/ic_arrow_drop_down_black_9dp_2x.png | Bin 0 -> 193 bytes .../icons/ic_arrow_drop_up_black_18dp_2x.png | Bin 0 -> 198 bytes .../icons/ic_arrow_drop_up_black_9dp_2x.png | Bin 0 -> 186 bytes .../icons/ic_arrow_forward_white_48dp_2x.png | Bin 0 -> 235 bytes .../icons/ic_arrow_tab_next_black_9dp_2x.png | Bin 0 -> 209 bytes .../ic_arrow_tab_previous_black_9dp_2x.png | Bin 0 -> 202 bytes images/icons/ic_baseline-search-24px.svg | 1 + images/icons/ic_block_24px.svg | 1 + images/icons/ic_call_transfer_white_24px.png | Bin 0 -> 1324 bytes images/icons/ic_chat_black_24dp_2x.png | Bin 0 -> 193 bytes images/icons/ic_chat_white_24dp.png | Bin 0 -> 205 bytes images/icons/ic_check_white_18dp_2x.png | Bin 0 -> 181 bytes images/icons/ic_clear_24px.svg | 1 + images/icons/ic_close_black_24dp.png | Bin 0 -> 1107 bytes images/icons/ic_close_white_24dp.png | Bin 0 -> 257 bytes images/icons/ic_content_copy.svg | 60 + images/icons/ic_delete_black_18dp_2x.png | Bin 0 -> 265 bytes images/icons/ic_done_white_24dp.png | Bin 0 -> 199 bytes images/icons/ic_exit_full_screen_black.png | Bin 0 -> 6079 bytes images/icons/ic_folder_black_18dp_2x.png | Bin 0 -> 241 bytes images/icons/ic_full_screen_black.png | Bin 0 -> 861 bytes images/icons/ic_group_add_white_24dp.png | Bin 0 -> 384 bytes images/icons/ic_hide_password.png | Bin 0 -> 4717 bytes images/icons/ic_high_quality_white_24dp.png | Bin 0 -> 218 bytes images/icons/ic_mic_off_white_24dp.png | Bin 0 -> 484 bytes images/icons/ic_mic_white_24dp.png | Bin 0 -> 394 bytes images/icons/ic_pause_white_100px.png | Bin 0 -> 437 bytes images/icons/ic_pause_white_24dp.png | Bin 0 -> 90 bytes images/icons/ic_person_add_black_24dp_2x.png | Bin 0 -> 323 bytes images/icons/ic_person_add_white_24dp.png | Bin 0 -> 329 bytes images/icons/ic_phone_24px.svg | 1 + .../icons/ic_photo_camera_white_24dp_2x.png | Bin 0 -> 446 bytes images/icons/ic_play_white_24dp.png | Bin 0 -> 196 bytes images/icons/ic_send_24px.svg | 1 + images/icons/ic_send_white_24dp.png | Bin 0 -> 344 bytes images/icons/ic_settings_white_48dp_2x.png | Bin 0 -> 1074 bytes images/icons/ic_share_black_48dp_2x.png | Bin 0 -> 888 bytes images/icons/ic_show_password.png | Bin 0 -> 7530 bytes images/icons/ic_video_call_24px.svg | 1 + images/icons/ic_videocam_off_white_24dp.png | Bin 0 -> 296 bytes images/icons/ic_videocam_white.png | Bin 0 -> 234 bytes images/icons/ic_voicemail_black_24dp_2x_.png | Bin 0 -> 327 bytes images/icons/ic_voicemail_white_24dp_2x.png | Bin 0 -> 487 bytes images/icons/icon-keypad-24-2x.png | Bin 0 -> 4183 bytes images/icons/icon-keypad-24.png | Bin 0 -> 879 bytes images/icons/info-24px.svg | 1 + images/icons/outline-info-24px.svg | 1 + images/icons/round-add-24px.svg | 1 + images/icons/round-add_a_photo-24px.svg | 1 + images/icons/round-arrow_drop_down-24px.svg | 1 + images/icons/round-arrow_drop_up-24px.svg | 1 + images/icons/round-arrow_right-24px.svg | 1 + images/icons/round-check_circle-24px.svg | 1 + images/icons/round-close-24px.svg | 1 + images/icons/round-edit-24px.svg | 1 + images/icons/round-error-24px.svg | 1 + images/icons/round-folder-24px.svg | 1 + images/icons/round-remove_circle-24px.svg | 1 + images/icons/round-save_alt-24px.svg | 1 + images/icons/round-settings-24px.svg | 1 + images/icons/round-undo-24px.svg | 1 + .../settings_backup_restore-black-18dp.svg | 1 + images/jami.ico | Bin 0 -> 165662 bytes images/jami.png | Bin 0 -> 26060 bytes images/jami_eclipse_spinner.gif | Bin 0 -> 63452 bytes images/jami_rolling_spinner.gif | Bin 0 -> 87724 bytes images/logo-jami-standard-coul.png | Bin 0 -> 48645 bytes images/qrcode.png | Bin 0 -> 4068 bytes images/spike.png | Bin 0 -> 355 bytes images/waiting.gif | Bin 0 -> 2506 bytes jami-qt.pro | 186 ++ jami-qt.sln | 636 ++++ make-client.py | 249 ++ projectcredits.html | 40 + qml.qrc | 96 + qrencode-win32.patch | 243 ++ qt.conf | 4 + ressources.qrc | 111 + ringtones/Makefile.am | 5 + ringtones/default.opus | Bin 0 -> 22089 bytes ringtones/default.opus.LICENSE | 4 + ringtones/default.wav | Bin 0 -> 56472 bytes ringtones/default.wav.LICENSE | 4 + ringtones/konga.ul | 11 + src/MainApplicationWindow.qml | 117 + src/accountadapter.cpp | 438 +++ src/accountadapter.h | 128 + src/accountlistmodel.cpp | 139 + src/accountlistmodel.h | 56 + src/accountstomigratelistmodel.cpp | 151 + src/accountstomigratelistmodel.h | 65 + src/audiocodeclistmodel.cpp | 108 + src/audiocodeclistmodel.h | 53 + src/audioinputdevicemodel.cpp | 128 + src/audioinputdevicemodel.h | 62 + src/audiomanagerlistmodel.cpp | 128 + src/audiomanagerlistmodel.h | 62 + src/audiooutputdevicemodel.cpp | 142 + src/audiooutputdevicemodel.h | 67 + src/avadapter.cpp | 132 + src/avadapter.h | 67 + src/bannedlistmodel.cpp | 120 + src/bannedlistmodel.h | 54 + src/calladapter.cpp | 444 +++ src/calladapter.h | 112 + src/clientwrapper.cpp | 103 + src/clientwrapper.h | 96 + .../AccountMigrationDialog.qml | 969 +++++++ src/commoncomponents/CustomBorder.qml | 54 + src/commoncomponents/DeleteAccountDialog.qml | 254 ++ src/commoncomponents/GeneralMenuItem.qml | 140 + src/commoncomponents/GeneralMenuSeparator.qml | 38 + src/commoncomponents/HoverableButton.qml | 89 + .../HoverableButtonTextItem.qml | 103 + .../HoverableRadiusButton.qml | 93 + src/commoncomponents/InfoLineEdit.qml | 58 + src/commoncomponents/JamiFileDialog.qml | 51 + src/commoncomponents/ListViewJami.qml | 51 + src/commoncomponents/LookupStatusLabel.qml | 101 + src/commoncomponents/MessageBox.qml | 42 + src/commoncomponents/PasswordDialog.qml | 372 +++ src/commoncomponents/PhotoboothView.qml | 280 ++ src/commoncomponents/TintedButton.qml | 125 + src/connectivitymonitor.cpp | 167 ++ src/connectivitymonitor.h | 46 + src/constant/JamiTheme.qml | 105 + src/contactadapter.cpp | 180 ++ src/contactadapter.h | 99 + src/conversationsadapter.cpp | 304 ++ src/conversationsadapter.h | 75 + src/deviceitemlistmodel.cpp | 117 + src/deviceitemlistmodel.h | 59 + src/distantrenderer.cpp | 65 + src/distantrenderer.h | 45 + src/globalsystemtray.cpp | 45 + src/globalsystemtray.h | 55 + src/jamiavatartheme.h | 43 + src/lrcinstance.h | 475 +++ src/main.cpp | 72 + src/mainapplication.cpp | 450 +++ src/mainapplication.h | 59 + src/mainview/MainView.qml | 506 ++++ src/mainview/components/AboutPopUp.qml | 308 ++ src/mainview/components/AccountComboBox.qml | 303 ++ .../components/AccountComboBoxPopup.qml | 180 ++ src/mainview/components/AudioCallPage.qml | 248 ++ src/mainview/components/CallOverlay.qml | 381 +++ .../components/CallOverlayButtonGroup.qml | 311 ++ src/mainview/components/CallStackView.qml | 227 ++ .../components/ChangeLogScrollView.qml | 59 + src/mainview/components/ContactPicker.qml | 149 + .../components/ContactPickerItemDelegate.qml | 135 + src/mainview/components/ContactSearchBar.qml | 107 + .../ConversationSmartListContextMenu.qml | 151 + .../ConversationSmartListUserImage.qml | 88 + .../components/ConversationSmartListView.qml | 104 + .../ConversationSmartListViewItemDelegate.qml | 280 ++ src/mainview/components/IncomingCallPage.qml | 342 +++ src/mainview/components/MessageWebView.qml | 293 ++ .../components/MessageWebViewHeader.qml | 237 ++ src/mainview/components/OutgoingCallPage.qml | 229 ++ .../components/ProjectCreditsScrollView.qml | 61 + src/mainview/components/RecordBox.qml | 318 ++ src/mainview/components/ScreenRubberBand.qml | 113 + src/mainview/components/SelectScreen.qml | 320 +++ src/mainview/components/SidePanel.qml | 311 ++ src/mainview/components/SidePanelTabBar.qml | 199 ++ src/mainview/components/UserProfile.qml | 170 ++ .../VideoCallFullScreenWindowContainer.qml | 40 + src/mainview/components/VideoCallPage.qml | 375 +++ .../components/VideoCallPageContextMenu.qml | 209 ++ .../VideoCallPageContextMenuDeviceItem.qml | 94 + src/mainview/components/WelcomePage.qml | 234 ++ .../components/WelcomePageQrDialog.qml | 61 + src/mainview/js/contactpickercreation.js | 80 + src/mainview/js/incomingcallpagecreation.js | 120 + src/mainview/js/screenrubberbandcreation.js | 80 + src/mainview/js/selectscreenwindowcreation.js | 72 + ...eocallfullscreenwindowcontainercreation.js | 97 + .../js/videodevicecontextmenuitemcreation.js | 92 + src/messagesadapter.cpp | 657 +++++ src/messagesadapter.h | 111 + src/pixbufmanipulator.cpp | 131 + src/pixbufmanipulator.h | 49 + src/pluginitemlistmodel.cpp | 123 + src/pluginitemlistmodel.h | 56 + src/preferenceitemlistmodel.cpp | 153 + src/preferenceitemlistmodel.h | 70 + src/previewrenderer.cpp | 146 + src/previewrenderer.h | 74 + src/qmladapterbase.cpp | 35 + src/qmladapterbase.h | 50 + src/qrimageprovider.h | 93 + src/rendermanager.cpp | 374 +++ src/rendermanager.h | 282 ++ src/runguard.cpp | 130 + src/runguard.h | 55 + src/settingsadaptor.cpp | 1047 +++++++ src/settingsadaptor.h | 241 ++ src/settingskey.h | 34 + src/settingsview/SettingsView.qml | 290 ++ .../components/AdvancedSIPSettingsView.qml | 2554 +++++++++++++++++ .../components/AdvancedSettingsView.qml | 1370 +++++++++ .../components/AudioCodecDelegate.qml | 102 + src/settingsview/components/AvSettingPage.qml | 832 ++++++ .../components/BannedItemDelegate.qml | 206 ++ .../CurrentAccountSettingsScrollPage.qml | 1376 +++++++++ .../CurrentSIPAccountSettingScrollPage.qml | 633 ++++ .../components/DeviceItemDelegate.qml | 220 ++ .../components/GeneralSettingsPage.qml | 678 +++++ src/settingsview/components/IconButton.qml | 131 + src/settingsview/components/LeftPanelView.qml | 191 ++ src/settingsview/components/LevelMeter.qml | 47 + .../components/LinkDeviceDialog.qml | 678 +++++ .../components/NameRegistrationDialog.qml | 555 ++++ .../components/PluginItemDelegate.qml | 176 ++ .../components/PluginListPreferencesView.qml | 334 +++ .../components/PluginListSettingsView.qml | 200 ++ .../components/PluginSettingsPage.qml | 148 + .../components/PreferenceItemDelegate.qml | 102 + .../components/RevokeDevicePasswordDialog.qml | 193 ++ .../components/SettingParaCombobox.qml | 110 + src/settingsview/components/ToggleSwitch.qml | 74 + .../components/VideoCodecDelegate.qml | 100 + src/smartlistmodel.cpp | 383 +++ src/smartlistmodel.h | 99 + src/tintedbuttonimageprovider.h | 61 + src/utils.cpp | 1082 +++++++ src/utils.h | 562 ++++ src/version.h | 57 + src/videocodeclistmodel.cpp | 130 + src/videocodeclistmodel.h | 53 + src/videoformatfpsmodel.cpp | 216 ++ src/videoformatfpsmodel.h | 77 + src/videoformatresolutionmodel.cpp | 164 ++ src/videoformatresolutionmodel.h | 62 + src/videoinputdevicemodel.cpp | 166 ++ src/videoinputdevicemodel.h | 73 + src/webchathelpers.cpp | 150 + src/webchathelpers.h | 39 + src/wizardview/WizardView.qml | 736 +++++ src/wizardview/components/BackupKeyPage.qml | 200 ++ .../components/CollapsiblePasswordWidget.qml | 156 + .../ConnectToAccountManagerPage.qml | 102 + .../components/CreateAccountPage.qml | 325 +++ .../components/CreateSIPAccountPage.qml | 202 ++ .../components/HoverableGradientButton.qml | 142 + .../components/ImportFromBackupPage.qml | 176 ++ .../components/ImportFromDevicePage.qml | 142 + src/wizardview/components/SpinnerPage.qml | 110 + .../components/WelcomePageLayout.qml | 238 ++ stylesheet.css | 963 +++++++ stylesheet.linux.css | 930 ++++++ translations/ring_client_windows.ts | 1591 ++++++++++ translations/ring_client_windows_ar.ts | 1589 ++++++++++ translations/ring_client_windows_bg.ts | 1589 ++++++++++ translations/ring_client_windows_ca.ts | 1605 +++++++++++ translations/ring_client_windows_cs_CZ.ts | 1590 ++++++++++ translations/ring_client_windows_da.ts | 1589 ++++++++++ translations/ring_client_windows_da_DK.ts | 1591 ++++++++++ translations/ring_client_windows_de.ts | 1606 +++++++++++ translations/ring_client_windows_de_DE.ts | 1589 ++++++++++ translations/ring_client_windows_el.ts | 1589 ++++++++++ translations/ring_client_windows_en_GB.ts | 1591 ++++++++++ translations/ring_client_windows_eo.ts | 1589 ++++++++++ translations/ring_client_windows_es.ts | 1605 +++++++++++ translations/ring_client_windows_es_AR.ts | 1605 +++++++++++ translations/ring_client_windows_es_MX.ts | 1589 ++++++++++ translations/ring_client_windows_et_EE.ts | 1591 ++++++++++ translations/ring_client_windows_eu.ts | 1589 ++++++++++ translations/ring_client_windows_fa_IR.ts | 1602 +++++++++++ translations/ring_client_windows_fi.ts | 1605 +++++++++++ translations/ring_client_windows_fr.ts | 1600 +++++++++++ translations/ring_client_windows_fr_BE.ts | 1589 ++++++++++ translations/ring_client_windows_fr_CA.ts | 1591 ++++++++++ translations/ring_client_windows_fr_CH.ts | 1589 ++++++++++ translations/ring_client_windows_fr_FR.ts | 1589 ++++++++++ translations/ring_client_windows_gl.ts | 1589 ++++++++++ translations/ring_client_windows_he.ts | 1589 ++++++++++ translations/ring_client_windows_hi_IN.ts | 1589 ++++++++++ translations/ring_client_windows_hr.ts | 1589 ++++++++++ translations/ring_client_windows_hu.ts | 1604 +++++++++++ translations/ring_client_windows_id.ts | 1589 ++++++++++ translations/ring_client_windows_it.ts | 1589 ++++++++++ translations/ring_client_windows_it_IT.ts | 1606 +++++++++++ translations/ring_client_windows_ja.ts | 1592 ++++++++++ translations/ring_client_windows_ko_KR.ts | 1589 ++++++++++ translations/ring_client_windows_lt.ts | 1606 +++++++++++ translations/ring_client_windows_ms.ts | 1589 ++++++++++ translations/ring_client_windows_nb.ts | 1589 ++++++++++ translations/ring_client_windows_nb_NO.ts | 2073 +++++++++++++ translations/ring_client_windows_nl.ts | 1605 +++++++++++ translations/ring_client_windows_nl_BE.ts | 1605 +++++++++++ translations/ring_client_windows_nl_NL.ts | 1589 ++++++++++ translations/ring_client_windows_oc.ts | 1589 ++++++++++ translations/ring_client_windows_pa.ts | 1589 ++++++++++ translations/ring_client_windows_pl.ts | 1604 +++++++++++ translations/ring_client_windows_pl_PL.ts | 1590 ++++++++++ translations/ring_client_windows_pt.ts | 1606 +++++++++++ translations/ring_client_windows_pt_BR.ts | 1592 ++++++++++ translations/ring_client_windows_pt_PT.ts | 1606 +++++++++++ translations/ring_client_windows_ro.ts | 1589 ++++++++++ translations/ring_client_windows_ro_RO.ts | 1598 +++++++++++ translations/ring_client_windows_ru.ts | 1602 +++++++++++ translations/ring_client_windows_ru_RU.ts | 1606 +++++++++++ translations/ring_client_windows_sk_SK.ts | 1606 +++++++++++ translations/ring_client_windows_sl.ts | 1589 ++++++++++ translations/ring_client_windows_sq_AL.ts | 1605 +++++++++++ translations/ring_client_windows_sr@latin.ts | 1589 ++++++++++ translations/ring_client_windows_sv.ts | 1589 ++++++++++ translations/ring_client_windows_ta.ts | 1589 ++++++++++ translations/ring_client_windows_tr.ts | 1605 +++++++++++ translations/ring_client_windows_tr_TR.ts | 1589 ++++++++++ translations/ring_client_windows_uk.ts | 1599 +++++++++++ translations/ring_client_windows_zh.ts | 1589 ++++++++++ translations/ring_client_windows_zh_CN.ts | 1606 +++++++++++ translations/ring_client_windows_zh_TW.ts | 1606 +++++++++++ update-translations.py | 62 + 362 files changed, 145249 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .gitreview create mode 100644 .tx/config create mode 100644 JamiInstaller/.gitignore create mode 100644 JamiInstaller/Config.wxi create mode 100644 JamiInstaller/HarvestFilter.xslt create mode 100644 JamiInstaller/JamiInstaller.wax create mode 100644 JamiInstaller/JamiInstaller.wixproj create mode 100644 JamiInstaller/Localization.wxl create mode 100644 JamiInstaller/Product.wxs create mode 100644 JamiInstaller/StandardComponents.wxs create mode 100644 JamiInstaller/main-banner.bmp create mode 100644 JamiInstaller/top-banner.bmp create mode 100644 License.rtf create mode 100644 README.md create mode 100644 changelog.md create mode 100644 copy-runtime-files.ps1 create mode 100644 ico.rc create mode 100644 images/FontAwesome.otf create mode 100644 images/ajax-loader.gif create mode 100644 images/default_avatar_overlay.svg create mode 100644 images/icons/av_icons/delete-24px.svg create mode 100644 images/icons/av_icons/fiber_manual_record-24px.svg create mode 100644 images/icons/av_icons/mic-24px.svg create mode 100644 images/icons/av_icons/pause-24px.svg create mode 100644 images/icons/av_icons/play_circle_outline-24px.svg create mode 100644 images/icons/av_icons/re-record-24px.svg create mode 100644 images/icons/av_icons/send-24px.svg create mode 100644 images/icons/av_icons/stop-24px-red.svg create mode 100644 images/icons/av_icons/stop-24px.svg create mode 100644 images/icons/baseline-camera_alt-24px.svg create mode 100644 images/icons/baseline-close-24px.svg create mode 100644 images/icons/baseline-desktop_windows-24px.svg create mode 100644 images/icons/baseline-done-24px.svg create mode 100644 images/icons/baseline-error_outline-24px.svg create mode 100644 images/icons/baseline-people-24px.svg create mode 100644 images/icons/baseline-refresh-24px.svg create mode 100644 images/icons/extension_24dp.svg create mode 100644 images/icons/ic_add_black_18dp_2x.png create mode 100644 images/icons/ic_arrow_back_24px.svg create mode 100644 images/icons/ic_arrow_back_white_24dp.png create mode 100644 images/icons/ic_arrow_drop_down_black_18dp_2x.png create mode 100644 images/icons/ic_arrow_drop_down_black_9dp_2x.png create mode 100644 images/icons/ic_arrow_drop_up_black_18dp_2x.png create mode 100644 images/icons/ic_arrow_drop_up_black_9dp_2x.png create mode 100644 images/icons/ic_arrow_forward_white_48dp_2x.png create mode 100644 images/icons/ic_arrow_tab_next_black_9dp_2x.png create mode 100644 images/icons/ic_arrow_tab_previous_black_9dp_2x.png create mode 100644 images/icons/ic_baseline-search-24px.svg create mode 100644 images/icons/ic_block_24px.svg create mode 100644 images/icons/ic_call_transfer_white_24px.png create mode 100644 images/icons/ic_chat_black_24dp_2x.png create mode 100644 images/icons/ic_chat_white_24dp.png create mode 100644 images/icons/ic_check_white_18dp_2x.png create mode 100644 images/icons/ic_clear_24px.svg create mode 100644 images/icons/ic_close_black_24dp.png create mode 100644 images/icons/ic_close_white_24dp.png create mode 100644 images/icons/ic_content_copy.svg create mode 100644 images/icons/ic_delete_black_18dp_2x.png create mode 100644 images/icons/ic_done_white_24dp.png create mode 100644 images/icons/ic_exit_full_screen_black.png create mode 100644 images/icons/ic_folder_black_18dp_2x.png create mode 100644 images/icons/ic_full_screen_black.png create mode 100644 images/icons/ic_group_add_white_24dp.png create mode 100644 images/icons/ic_hide_password.png create mode 100644 images/icons/ic_high_quality_white_24dp.png create mode 100644 images/icons/ic_mic_off_white_24dp.png create mode 100644 images/icons/ic_mic_white_24dp.png create mode 100644 images/icons/ic_pause_white_100px.png create mode 100644 images/icons/ic_pause_white_24dp.png create mode 100644 images/icons/ic_person_add_black_24dp_2x.png create mode 100644 images/icons/ic_person_add_white_24dp.png create mode 100644 images/icons/ic_phone_24px.svg create mode 100644 images/icons/ic_photo_camera_white_24dp_2x.png create mode 100644 images/icons/ic_play_white_24dp.png create mode 100644 images/icons/ic_send_24px.svg create mode 100644 images/icons/ic_send_white_24dp.png create mode 100644 images/icons/ic_settings_white_48dp_2x.png create mode 100644 images/icons/ic_share_black_48dp_2x.png create mode 100644 images/icons/ic_show_password.png create mode 100644 images/icons/ic_video_call_24px.svg create mode 100644 images/icons/ic_videocam_off_white_24dp.png create mode 100644 images/icons/ic_videocam_white.png create mode 100644 images/icons/ic_voicemail_black_24dp_2x_.png create mode 100644 images/icons/ic_voicemail_white_24dp_2x.png create mode 100644 images/icons/icon-keypad-24-2x.png create mode 100644 images/icons/icon-keypad-24.png create mode 100644 images/icons/info-24px.svg create mode 100644 images/icons/outline-info-24px.svg create mode 100644 images/icons/round-add-24px.svg create mode 100644 images/icons/round-add_a_photo-24px.svg create mode 100644 images/icons/round-arrow_drop_down-24px.svg create mode 100644 images/icons/round-arrow_drop_up-24px.svg create mode 100644 images/icons/round-arrow_right-24px.svg create mode 100644 images/icons/round-check_circle-24px.svg create mode 100644 images/icons/round-close-24px.svg create mode 100644 images/icons/round-edit-24px.svg create mode 100644 images/icons/round-error-24px.svg create mode 100644 images/icons/round-folder-24px.svg create mode 100644 images/icons/round-remove_circle-24px.svg create mode 100644 images/icons/round-save_alt-24px.svg create mode 100644 images/icons/round-settings-24px.svg create mode 100644 images/icons/round-undo-24px.svg create mode 100644 images/icons/settings_backup_restore-black-18dp.svg create mode 100644 images/jami.ico create mode 100644 images/jami.png create mode 100644 images/jami_eclipse_spinner.gif create mode 100644 images/jami_rolling_spinner.gif create mode 100644 images/logo-jami-standard-coul.png create mode 100644 images/qrcode.png create mode 100644 images/spike.png create mode 100644 images/waiting.gif create mode 100644 jami-qt.pro create mode 100644 jami-qt.sln create mode 100644 make-client.py create mode 100644 projectcredits.html create mode 100644 qml.qrc create mode 100644 qrencode-win32.patch create mode 100644 qt.conf create mode 100644 ressources.qrc create mode 100644 ringtones/Makefile.am create mode 100644 ringtones/default.opus create mode 100644 ringtones/default.opus.LICENSE create mode 100644 ringtones/default.wav create mode 100644 ringtones/default.wav.LICENSE create mode 100644 ringtones/konga.ul create mode 100644 src/MainApplicationWindow.qml create mode 100644 src/accountadapter.cpp create mode 100644 src/accountadapter.h create mode 100644 src/accountlistmodel.cpp create mode 100644 src/accountlistmodel.h create mode 100644 src/accountstomigratelistmodel.cpp create mode 100644 src/accountstomigratelistmodel.h create mode 100644 src/audiocodeclistmodel.cpp create mode 100644 src/audiocodeclistmodel.h create mode 100644 src/audioinputdevicemodel.cpp create mode 100644 src/audioinputdevicemodel.h create mode 100644 src/audiomanagerlistmodel.cpp create mode 100644 src/audiomanagerlistmodel.h create mode 100644 src/audiooutputdevicemodel.cpp create mode 100644 src/audiooutputdevicemodel.h create mode 100644 src/avadapter.cpp create mode 100644 src/avadapter.h create mode 100644 src/bannedlistmodel.cpp create mode 100644 src/bannedlistmodel.h create mode 100644 src/calladapter.cpp create mode 100644 src/calladapter.h create mode 100644 src/clientwrapper.cpp create mode 100644 src/clientwrapper.h create mode 100644 src/commoncomponents/AccountMigrationDialog.qml create mode 100644 src/commoncomponents/CustomBorder.qml create mode 100644 src/commoncomponents/DeleteAccountDialog.qml create mode 100644 src/commoncomponents/GeneralMenuItem.qml create mode 100644 src/commoncomponents/GeneralMenuSeparator.qml create mode 100644 src/commoncomponents/HoverableButton.qml create mode 100644 src/commoncomponents/HoverableButtonTextItem.qml create mode 100644 src/commoncomponents/HoverableRadiusButton.qml create mode 100644 src/commoncomponents/InfoLineEdit.qml create mode 100644 src/commoncomponents/JamiFileDialog.qml create mode 100644 src/commoncomponents/ListViewJami.qml create mode 100644 src/commoncomponents/LookupStatusLabel.qml create mode 100644 src/commoncomponents/MessageBox.qml create mode 100644 src/commoncomponents/PasswordDialog.qml create mode 100644 src/commoncomponents/PhotoboothView.qml create mode 100644 src/commoncomponents/TintedButton.qml create mode 100644 src/connectivitymonitor.cpp create mode 100644 src/connectivitymonitor.h create mode 100644 src/constant/JamiTheme.qml create mode 100644 src/contactadapter.cpp create mode 100644 src/contactadapter.h create mode 100644 src/conversationsadapter.cpp create mode 100644 src/conversationsadapter.h create mode 100644 src/deviceitemlistmodel.cpp create mode 100644 src/deviceitemlistmodel.h create mode 100644 src/distantrenderer.cpp create mode 100644 src/distantrenderer.h create mode 100644 src/globalsystemtray.cpp create mode 100644 src/globalsystemtray.h create mode 100644 src/jamiavatartheme.h create mode 100644 src/lrcinstance.h create mode 100644 src/main.cpp create mode 100644 src/mainapplication.cpp create mode 100644 src/mainapplication.h create mode 100644 src/mainview/MainView.qml create mode 100644 src/mainview/components/AboutPopUp.qml create mode 100644 src/mainview/components/AccountComboBox.qml create mode 100644 src/mainview/components/AccountComboBoxPopup.qml create mode 100644 src/mainview/components/AudioCallPage.qml create mode 100644 src/mainview/components/CallOverlay.qml create mode 100644 src/mainview/components/CallOverlayButtonGroup.qml create mode 100644 src/mainview/components/CallStackView.qml create mode 100644 src/mainview/components/ChangeLogScrollView.qml create mode 100644 src/mainview/components/ContactPicker.qml create mode 100644 src/mainview/components/ContactPickerItemDelegate.qml create mode 100644 src/mainview/components/ContactSearchBar.qml create mode 100644 src/mainview/components/ConversationSmartListContextMenu.qml create mode 100644 src/mainview/components/ConversationSmartListUserImage.qml create mode 100644 src/mainview/components/ConversationSmartListView.qml create mode 100644 src/mainview/components/ConversationSmartListViewItemDelegate.qml create mode 100644 src/mainview/components/IncomingCallPage.qml create mode 100644 src/mainview/components/MessageWebView.qml create mode 100644 src/mainview/components/MessageWebViewHeader.qml create mode 100644 src/mainview/components/OutgoingCallPage.qml create mode 100644 src/mainview/components/ProjectCreditsScrollView.qml create mode 100644 src/mainview/components/RecordBox.qml create mode 100644 src/mainview/components/ScreenRubberBand.qml create mode 100644 src/mainview/components/SelectScreen.qml create mode 100644 src/mainview/components/SidePanel.qml create mode 100644 src/mainview/components/SidePanelTabBar.qml create mode 100644 src/mainview/components/UserProfile.qml create mode 100644 src/mainview/components/VideoCallFullScreenWindowContainer.qml create mode 100644 src/mainview/components/VideoCallPage.qml create mode 100644 src/mainview/components/VideoCallPageContextMenu.qml create mode 100644 src/mainview/components/VideoCallPageContextMenuDeviceItem.qml create mode 100644 src/mainview/components/WelcomePage.qml create mode 100644 src/mainview/components/WelcomePageQrDialog.qml create mode 100644 src/mainview/js/contactpickercreation.js create mode 100644 src/mainview/js/incomingcallpagecreation.js create mode 100644 src/mainview/js/screenrubberbandcreation.js create mode 100644 src/mainview/js/selectscreenwindowcreation.js create mode 100644 src/mainview/js/videocallfullscreenwindowcontainercreation.js create mode 100644 src/mainview/js/videodevicecontextmenuitemcreation.js create mode 100644 src/messagesadapter.cpp create mode 100644 src/messagesadapter.h create mode 100644 src/pixbufmanipulator.cpp create mode 100644 src/pixbufmanipulator.h create mode 100644 src/pluginitemlistmodel.cpp create mode 100644 src/pluginitemlistmodel.h create mode 100644 src/preferenceitemlistmodel.cpp create mode 100644 src/preferenceitemlistmodel.h create mode 100644 src/previewrenderer.cpp create mode 100644 src/previewrenderer.h create mode 100644 src/qmladapterbase.cpp create mode 100644 src/qmladapterbase.h create mode 100644 src/qrimageprovider.h create mode 100644 src/rendermanager.cpp create mode 100644 src/rendermanager.h create mode 100644 src/runguard.cpp create mode 100644 src/runguard.h create mode 100644 src/settingsadaptor.cpp create mode 100644 src/settingsadaptor.h create mode 100644 src/settingskey.h create mode 100644 src/settingsview/SettingsView.qml create mode 100644 src/settingsview/components/AdvancedSIPSettingsView.qml create mode 100644 src/settingsview/components/AdvancedSettingsView.qml create mode 100644 src/settingsview/components/AudioCodecDelegate.qml create mode 100644 src/settingsview/components/AvSettingPage.qml create mode 100644 src/settingsview/components/BannedItemDelegate.qml create mode 100644 src/settingsview/components/CurrentAccountSettingsScrollPage.qml create mode 100644 src/settingsview/components/CurrentSIPAccountSettingScrollPage.qml create mode 100644 src/settingsview/components/DeviceItemDelegate.qml create mode 100644 src/settingsview/components/GeneralSettingsPage.qml create mode 100644 src/settingsview/components/IconButton.qml create mode 100644 src/settingsview/components/LeftPanelView.qml create mode 100644 src/settingsview/components/LevelMeter.qml create mode 100644 src/settingsview/components/LinkDeviceDialog.qml create mode 100644 src/settingsview/components/NameRegistrationDialog.qml create mode 100644 src/settingsview/components/PluginItemDelegate.qml create mode 100644 src/settingsview/components/PluginListPreferencesView.qml create mode 100644 src/settingsview/components/PluginListSettingsView.qml create mode 100644 src/settingsview/components/PluginSettingsPage.qml create mode 100644 src/settingsview/components/PreferenceItemDelegate.qml create mode 100644 src/settingsview/components/RevokeDevicePasswordDialog.qml create mode 100644 src/settingsview/components/SettingParaCombobox.qml create mode 100644 src/settingsview/components/ToggleSwitch.qml create mode 100644 src/settingsview/components/VideoCodecDelegate.qml create mode 100644 src/smartlistmodel.cpp create mode 100644 src/smartlistmodel.h create mode 100644 src/tintedbuttonimageprovider.h create mode 100644 src/utils.cpp create mode 100644 src/utils.h create mode 100644 src/version.h create mode 100644 src/videocodeclistmodel.cpp create mode 100644 src/videocodeclistmodel.h create mode 100644 src/videoformatfpsmodel.cpp create mode 100644 src/videoformatfpsmodel.h create mode 100644 src/videoformatresolutionmodel.cpp create mode 100644 src/videoformatresolutionmodel.h create mode 100644 src/videoinputdevicemodel.cpp create mode 100644 src/videoinputdevicemodel.h create mode 100644 src/webchathelpers.cpp create mode 100644 src/webchathelpers.h create mode 100644 src/wizardview/WizardView.qml create mode 100644 src/wizardview/components/BackupKeyPage.qml create mode 100644 src/wizardview/components/CollapsiblePasswordWidget.qml create mode 100644 src/wizardview/components/ConnectToAccountManagerPage.qml create mode 100644 src/wizardview/components/CreateAccountPage.qml create mode 100644 src/wizardview/components/CreateSIPAccountPage.qml create mode 100644 src/wizardview/components/HoverableGradientButton.qml create mode 100644 src/wizardview/components/ImportFromBackupPage.qml create mode 100644 src/wizardview/components/ImportFromDevicePage.qml create mode 100644 src/wizardview/components/SpinnerPage.qml create mode 100644 src/wizardview/components/WelcomePageLayout.qml create mode 100644 stylesheet.css create mode 100644 stylesheet.linux.css create mode 100644 translations/ring_client_windows.ts create mode 100644 translations/ring_client_windows_ar.ts create mode 100644 translations/ring_client_windows_bg.ts create mode 100644 translations/ring_client_windows_ca.ts create mode 100644 translations/ring_client_windows_cs_CZ.ts create mode 100644 translations/ring_client_windows_da.ts create mode 100644 translations/ring_client_windows_da_DK.ts create mode 100644 translations/ring_client_windows_de.ts create mode 100644 translations/ring_client_windows_de_DE.ts create mode 100644 translations/ring_client_windows_el.ts create mode 100644 translations/ring_client_windows_en_GB.ts create mode 100644 translations/ring_client_windows_eo.ts create mode 100644 translations/ring_client_windows_es.ts create mode 100644 translations/ring_client_windows_es_AR.ts create mode 100644 translations/ring_client_windows_es_MX.ts create mode 100644 translations/ring_client_windows_et_EE.ts create mode 100644 translations/ring_client_windows_eu.ts create mode 100644 translations/ring_client_windows_fa_IR.ts create mode 100644 translations/ring_client_windows_fi.ts create mode 100644 translations/ring_client_windows_fr.ts create mode 100644 translations/ring_client_windows_fr_BE.ts create mode 100644 translations/ring_client_windows_fr_CA.ts create mode 100644 translations/ring_client_windows_fr_CH.ts create mode 100644 translations/ring_client_windows_fr_FR.ts create mode 100644 translations/ring_client_windows_gl.ts create mode 100644 translations/ring_client_windows_he.ts create mode 100644 translations/ring_client_windows_hi_IN.ts create mode 100644 translations/ring_client_windows_hr.ts create mode 100644 translations/ring_client_windows_hu.ts create mode 100644 translations/ring_client_windows_id.ts create mode 100644 translations/ring_client_windows_it.ts create mode 100644 translations/ring_client_windows_it_IT.ts create mode 100644 translations/ring_client_windows_ja.ts create mode 100644 translations/ring_client_windows_ko_KR.ts create mode 100644 translations/ring_client_windows_lt.ts create mode 100644 translations/ring_client_windows_ms.ts create mode 100644 translations/ring_client_windows_nb.ts create mode 100644 translations/ring_client_windows_nb_NO.ts create mode 100644 translations/ring_client_windows_nl.ts create mode 100644 translations/ring_client_windows_nl_BE.ts create mode 100644 translations/ring_client_windows_nl_NL.ts create mode 100644 translations/ring_client_windows_oc.ts create mode 100644 translations/ring_client_windows_pa.ts create mode 100644 translations/ring_client_windows_pl.ts create mode 100644 translations/ring_client_windows_pl_PL.ts create mode 100644 translations/ring_client_windows_pt.ts create mode 100644 translations/ring_client_windows_pt_BR.ts create mode 100644 translations/ring_client_windows_pt_PT.ts create mode 100644 translations/ring_client_windows_ro.ts create mode 100644 translations/ring_client_windows_ro_RO.ts create mode 100644 translations/ring_client_windows_ru.ts create mode 100644 translations/ring_client_windows_ru_RU.ts create mode 100644 translations/ring_client_windows_sk_SK.ts create mode 100644 translations/ring_client_windows_sl.ts create mode 100644 translations/ring_client_windows_sq_AL.ts create mode 100644 translations/ring_client_windows_sr@latin.ts create mode 100644 translations/ring_client_windows_sv.ts create mode 100644 translations/ring_client_windows_ta.ts create mode 100644 translations/ring_client_windows_tr.ts create mode 100644 translations/ring_client_windows_tr_TR.ts create mode 100644 translations/ring_client_windows_uk.ts create mode 100644 translations/ring_client_windows_zh.ts create mode 100644 translations/ring_client_windows_zh_CN.ts create mode 100644 translations/ring_client_windows_zh_TW.ts create mode 100755 update-translations.py diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..8a799fd7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,101 @@ +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: DontAlign +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +AlwaysBreakAfterDefinitionReturnType: All +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - forever # avoids { wrapped to next line + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeCategories: + - Regex: '^.ts +source_file = translations/ring_client_windows.ts +source_lang = en +type = TS diff --git a/JamiInstaller/.gitignore b/JamiInstaller/.gitignore new file mode 100644 index 00000000..26814d3f --- /dev/null +++ b/JamiInstaller/.gitignore @@ -0,0 +1,3 @@ +/obj +/bin +Components.wxs diff --git a/JamiInstaller/Config.wxi b/JamiInstaller/Config.wxi new file mode 100644 index 00000000..f24ee3ff --- /dev/null +++ b/JamiInstaller/Config.wxi @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/JamiInstaller/HarvestFilter.xslt b/JamiInstaller/HarvestFilter.xslt new file mode 100644 index 00000000..b9179f47 --- /dev/null +++ b/JamiInstaller/HarvestFilter.xslt @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JamiInstaller/JamiInstaller.wax b/JamiInstaller/JamiInstaller.wax new file mode 100644 index 00000000..a3b03657 --- /dev/null +++ b/JamiInstaller/JamiInstaller.wax @@ -0,0 +1,9 @@ + + + + + + false + true + false + \ No newline at end of file diff --git a/JamiInstaller/JamiInstaller.wixproj b/JamiInstaller/JamiInstaller.wixproj new file mode 100644 index 00000000..9c64ca6e --- /dev/null +++ b/JamiInstaller/JamiInstaller.wixproj @@ -0,0 +1,70 @@ + + + + Debug + x64 + 3.10 + dbbfbc55-1c20-4d21-ae3b-6e8b14c4fe48 + 2.0 + jami.release.$(Platform) + jami.beta.$(Platform) + Package + JamiInstaller + x64 + + + bin\$(Configuration)\ + obj\$(Configuration)\ + HarvestPath=..\x64\Release + True + + + + + + + bin\$(Configuration)\ + obj\$(Configuration)\ + HarvestPath=..\x64\Beta + True + + + + + + + + + + + + + + + + + + + + $(WixExtDir)\WixUtilExtension.dll + WixUtilExtension + + + $(WixExtDir)\WixUIExtension.dll + WixUIExtension + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JamiInstaller/Localization.wxl b/JamiInstaller/Localization.wxl new file mode 100644 index 00000000..a649d175 --- /dev/null +++ b/JamiInstaller/Localization.wxl @@ -0,0 +1,7 @@ + + + By installing this software you agree to the terms in the license agreement + + + diff --git a/JamiInstaller/Product.wxs b/JamiInstaller/Product.wxs new file mode 100644 index 00000000..fe6a02bd --- /dev/null +++ b/JamiInstaller/Product.wxs @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + APPLICATIONFOLDER="" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + 1 + 1 + 1 + + + WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed + + + NOT AbortInstall = 1 + + + + + + + WIXNONUILAUNCH + + + + + + + + + + + + + + + + + + + diff --git a/JamiInstaller/StandardComponents.wxs b/JamiInstaller/StandardComponents.wxs new file mode 100644 index 00000000..85847159 --- /dev/null +++ b/JamiInstaller/StandardComponents.wxs @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JamiInstaller/main-banner.bmp b/JamiInstaller/main-banner.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a5e88425fe3e4a88e89796ae34d912a72e7768bb GIT binary patch literal 461814 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 literal 0 HcmV?d00001 diff --git a/JamiInstaller/top-banner.bmp b/JamiInstaller/top-banner.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c7efb6fc01f79fe1b6feb0c18661e298a5fa7283 GIT binary patch literal 85894 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 literal 0 HcmV?d00001 diff --git a/License.rtf b/License.rtf new file mode 100644 index 00000000..fecb01cc --- /dev/null +++ b/License.rtf @@ -0,0 +1,869 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff0\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi0\deflang4105\deflangfe2052\themelang4105\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f34\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria Math;} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f420\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\f421\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\f423\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f424\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f425\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\f426\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f427\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f428\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f760\fbidi \froman\fcharset238\fprq2 Cambria Math CE;} +{\f761\fbidi \froman\fcharset204\fprq2 Cambria Math Cyr;}{\f763\fbidi \froman\fcharset161\fprq2 Cambria Math Greek;}{\f764\fbidi \froman\fcharset162\fprq2 Cambria Math Tur;}{\f767\fbidi \froman\fcharset186\fprq2 Cambria Math Baltic;} +{\f768\fbidi \froman\fcharset163\fprq2 Cambria Math (Vietnamese);}{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;} +{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;}{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;} +{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} +{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; +\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green0\blue0;\red0\green0\blue0;}{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap +\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang4105\langfe2052\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp4105\langfenp2052 \snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa160\sl259\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang4105\langfe2052\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp4105\langfenp2052 \snext11 \ssemihidden \sunhideused +Normal Table;}{\s15\qc \li0\ri0\sb240\sa60\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af31503\afs32\alang1025 \ltrch\fcs0 +\b\fs32\lang4105\langfe2052\kerning28\loch\f31502\hich\af31502\dbch\af31501\cgrid\langnp4105\langfenp2052 \sbasedon0 \snext0 \slink16 \sqformat \spriority10 \styrsid15664314 Title;}{\*\cs16 \additive \rtlch\fcs1 \ab\af31503\afs32 \ltrch\fcs0 +\b\fs32\kerning28\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink15 \slocked \spriority10 \styrsid15664314 Title Char;}}{\*\rsidtbl \rsid350521\rsid1073743\rsid4589873\rsid9323379\rsid10056905\rsid14959562\rsid15664314\rsid16088643\rsid16389575} +{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\operator Zhang mingrui}{\creatim\yr2019\mo4\dy23\hr10\min37}{\revtim\yr2019\mo4\dy23\hr11\min15}{\version14}{\edmins11} +{\nofpages28}{\nofwords5147}{\nofchars29338}{\nofcharsws34417}{\vern101}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml0\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors0\horzdoc\dghspace120\dgvspace120\dghorigin1701 +\dgvorigin1984\dghshow0\dgvshow3\jcompress\viewkind1\viewscale100\rsidroot1073743 \fet0{\*\wgrffmtfilter 2450}\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2 +\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang +{\pntxtb (}{\pntxta )}}\pard\plain \ltrpar\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid4589873 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 +\fs22\lang4105\langfe2052\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp4105\langfenp2052 {\rtlch\fcs1 \af31501\afs18 \ltrch\fcs0 \f31501\fs18\lang1033\langfe2052\langnp1033\insrsid16389575\charrsid16088643 \hich\af31501\dbch\af31505\loch\f31501 +GNU GENERAL PUBLIC LICENSE}{\rtlch\fcs1 \af31501\afs18 \ltrch\fcs0 \f31501\fs18\lang1033\langfe2052\langnp1033\insrsid15664314\charrsid16088643 +\par }{\rtlch\fcs1 \af31501\afs18 \ltrch\fcs0 \f31501\fs18\lang1033\langfe2052\langnp1033\insrsid16389575\charrsid16088643 \hich\af31501\dbch\af31505\loch\f31501 Version 3, 29 June 2007}{\rtlch\fcs1 \af31501\afs18 \ltrch\fcs0 +\f31501\fs18\lang1033\langfe2052\langnp1033\insrsid4589873\charrsid16088643 +\par }{\rtlch\fcs1 \af31501\afs18 \ltrch\fcs0 \f31501\fs18\lang1033\langfe2052\langnp1033\insrsid16389575\charrsid16088643 \hich\af31501\dbch\af31505\loch\f31501 Copyright (C) 2007 Free Software Foundation, Inc. +\par \hich\af31501\dbch\af31505\loch\f31501 Everyone is permitted to copy and distribute verbatim copies +\par \hich\af31501\dbch\af31505\loch\f31501 of this license document, but changing it is not allowed. +\par }{\rtlch\fcs1 \af31501\afs18 \ltrch\fcs0 \f31501\fs18\lang1033\langfe2052\langnp1033\insrsid4589873\charrsid16088643 +\par }{\rtlch\fcs1 \af31501\afs18 \ltrch\fcs0 \f31501\fs18\lang1033\langfe2052\langnp1033\insrsid16389575\charrsid16088643 \hich\af31501\dbch\af31505\loch\f31501 Preamble +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The GNU General Public License is a free, copyleft license\hich\af31501\dbch\af31505\loch\f31501 for +\par \hich\af31501\dbch\af31505\loch\f31501 software and other kinds of works. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The licenses for most software and other practical works are designed +\par \hich\af31501\dbch\af31505\loch\f31501 to take away your freedom to share and change the works. By contrast, +\par \hich\af31501\dbch\af31505\loch\f31501 the GNU General Public License is intended to guarantee your freedom to +\par \hich\af31501\dbch\af31505\loch\f31501 sh\hich\af31501\dbch\af31505\loch\f31501 are and change all versions of a program--to make sure it remains free +\par \hich\af31501\dbch\af31505\loch\f31501 software for all its users. We, the Free Software Foundation, use the +\par \hich\af31501\dbch\af31505\loch\f31501 GNU General Public License for most of our software; it applies also to +\par \hich\af31501\dbch\af31505\loch\f31501 any other work released this way by its au\hich\af31501\dbch\af31505\loch\f31501 thors. You can apply it to +\par \hich\af31501\dbch\af31505\loch\f31501 your programs, too. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 When we speak of free software, we are referring to freedom, not +\par \hich\af31501\dbch\af31505\loch\f31501 price. Our General Public Licenses are designed to make sure that you +\par \hich\af31501\dbch\af31505\loch\f31501 have the freedom to distribute copies of free software (and charge for +\par \hich\af31501\dbch\af31505\loch\f31501 them if you wish), that you receive source code or can get it if you +\par \hich\af31501\dbch\af31505\loch\f31501 want it, that you can change the software or use pieces of it in new +\par \hich\af31501\dbch\af31505\loch\f31501 free programs, and that you know you can do these things. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 To protect your rights, we need to prevent others from denying you +\par \hich\af31501\dbch\af31505\loch\f31501 these rights or asking you to surrender the rights. Therefore, you have +\par \hich\af31501\dbch\af31505\loch\f31501 certain responsibilities if you distribute copies of the software, or if +\par \hich\af31501\dbch\af31505\loch\f31501 you modify it: responsibilities to respect \hich\af31501\dbch\af31505\loch\f31501 the freedom of others. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 For example, if you distribute copies of such a program, whether +\par \hich\af31501\dbch\af31505\loch\f31501 gratis or for a fee, you must pass on to the recipients the same +\par \hich\af31501\dbch\af31505\loch\f31501 freedoms that you received. You must make sure that they, too, receive +\par \hich\af31501\dbch\af31505\loch\f31501 or can get the source code. A\hich\af31501\dbch\af31505\loch\f31501 nd you must show them these terms so they +\par \hich\af31501\dbch\af31505\loch\f31501 know their rights. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Developers that use the GNU GPL protect your rights with two steps: +\par \hich\af31501\dbch\af31505\loch\f31501 (1) assert copyright on the software, and (2) offer you this License +\par \hich\af31501\dbch\af31505\loch\f31501 giving you legal permission to copy, distribute and/or mo\hich\af31501\dbch\af31505\loch\f31501 dify it. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 For the developers' and authors' protection, the GPL clearly explains +\par \hich\af31501\dbch\af31505\loch\f31501 that there is no warranty for this free software. For both users' and +\par \hich\af31501\dbch\af31505\loch\f31501 authors' sake, the GPL requires that modified versions be marked as +\par \hich\af31501\dbch\af31505\loch\f31501 changed, so that their problems will \hich\af31501\dbch\af31505\loch\f31501 not be attributed erroneously to +\par \hich\af31501\dbch\af31505\loch\f31501 authors of previous versions. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Some devices are designed to deny users access to install or run +\par \hich\af31501\dbch\af31505\loch\f31501 modified versions of the software inside them, although the manufacturer +\par \hich\af31501\dbch\af31505\loch\f31501 can do so. This is fundamentally incompatible with the aim of +\par \hich\af31501\dbch\af31505\loch\f31501 protecting users' freedom to change the software. The systematic +\par \hich\af31501\dbch\af31505\loch\f31501 pattern of such abuse occurs in the area of products for individuals to +\par \hich\af31501\dbch\af31505\loch\f31501 use, which is precisely where it is most unacceptable. \hich\af31501\dbch\af31505\loch\f31501 Therefore, we +\par \hich\af31501\dbch\af31505\loch\f31501 have designed this version of the GPL to prohibit the practice for those +\par \hich\af31501\dbch\af31505\loch\f31501 products. If such problems arise substantially in other domains, we +\par \hich\af31501\dbch\af31505\loch\f31501 stand ready to extend this provision to those domains in future versions +\par \hich\af31501\dbch\af31505\loch\f31501 of the GPL, as needed to p\hich\af31501\dbch\af31505\loch\f31501 rotect the freedom of users. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Finally, every program is threatened constantly by software patents. +\par \hich\af31501\dbch\af31505\loch\f31501 States should not allow patents to restrict development and use of +\par \hich\af31501\dbch\af31505\loch\f31501 software on general-purpose computers, but in those that do, we wish to +\par \hich\af31501\dbch\af31505\loch\f31501 avoid the special \hich\af31501\dbch\af31505\loch\f31501 danger that patents applied to a free program could +\par \hich\af31501\dbch\af31505\loch\f31501 make it effectively proprietary. To prevent this, the GPL assures that +\par \hich\af31501\dbch\af31505\loch\f31501 patents cannot be used to render the program non-free. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The precise terms and conditions for copying, distribution and +\par \hich\af31501\dbch\af31505\loch\f31501 modification \hich\af31501\dbch\af31505\loch\f31501 follow. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 TERMS AND CONDITIONS +\par +\par \hich\af31501\dbch\af31505\loch\f31501 0. Definitions. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 "This License" refers to version 3 of the GNU General Public License. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 "Copyright" also means copyright-like laws that apply to other kinds of +\par \hich\af31501\dbch\af31505\loch\f31501 works, such as semiconductor masks. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 "The Program" refers to any copyrightable work licensed under this +\par \hich\af31501\dbch\af31505\loch\f31501 License. Each licensee is addressed as "you". "Licensees" and +\par \hich\af31501\dbch\af31505\loch\f31501 "recipients" may\hich\af31501\dbch\af31505\loch\f31501 be individuals or organizations. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 To "modify" a work means to copy from or adapt all or part of the work +\par \hich\af31501\dbch\af31505\loch\f31501 in a fashion requiring copyright permission, other than the making of an +\par \hich\af31501\dbch\af31505\loch\f31501 exact copy. The resulting work is called a "modified version" of the +\par \hich\af31501\dbch\af31505\loch\f31501 earlier\hich\af31501\dbch\af31505\loch\f31501 work or a work "based on" the earlier work. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 A "covered work" means either the unmodified Program or a work based +\par \hich\af31501\dbch\af31505\loch\f31501 on the Program. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 To "propagate" a work means to do anything with it that, without +\par \hich\af31501\dbch\af31505\loch\f31501 permission, would make you directly or secondarily liable f\hich\af31501\dbch\af31505\loch\f31501 or +\par \hich\af31501\dbch\af31505\loch\f31501 infringement under applicable copyright law, except executing it on a +\par \hich\af31501\dbch\af31505\loch\f31501 computer or modifying a private copy. Propagation includes copying, +\par \hich\af31501\dbch\af31505\loch\f31501 distribution (with or without modification), making available to the +\par \hich\af31501\dbch\af31505\loch\f31501 public, and in some countries other activitie\hich\af31501\dbch\af31505\loch\f31501 s as well. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 To "convey" a work means any kind of propagation that enables other +\par \hich\af31501\dbch\af31505\loch\f31501 parties to make or receive copies. Mere interaction with a user through +\par \hich\af31501\dbch\af31505\loch\f31501 a computer network, with no transfer of a copy, is not conveying. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 An interactive user interface displays "Appropriate Legal Notices" +\par \hich\af31501\dbch\af31505\loch\f31501 to the extent that it includes a convenient and prominently visible +\par \hich\af31501\dbch\af31505\loch\f31501 feature that (1) displays an appropriate copyright notice, and (2) +\par \hich\af31501\dbch\af31505\loch\f31501 tells the user that there is no warranty for the work \hich\af31501\dbch\af31505\loch\f31501 (except to the +\par \hich\af31501\dbch\af31505\loch\f31501 extent that warranties are provided), that licensees may convey the +\par \hich\af31501\dbch\af31505\loch\f31501 work under this License, and how to view a copy of this License. If +\par \hich\af31501\dbch\af31505\loch\f31501 the interface presents a list of user commands or options, such as a +\par \hich\af31501\dbch\af31505\loch\f31501 menu, a prominent item in the list \hich\af31501\dbch\af31505\loch\f31501 meets this criterion. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 1. Source Code. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The "source code" for a work means the preferred form of the work +\par \hich\af31501\dbch\af31505\loch\f31501 for making modifications to it. "Object code" means any non-source +\par \hich\af31501\dbch\af31505\loch\f31501 form of a work. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 A "Standard Interface" means an interface that either is an offic\hich\af31501\dbch\af31505\loch\f31501 ial +\par \hich\af31501\dbch\af31505\loch\f31501 standard defined by a recognized standards body, or, in the case of +\par \hich\af31501\dbch\af31505\loch\f31501 interfaces specified for a particular programming language, one that +\par \hich\af31501\dbch\af31505\loch\f31501 is widely used among developers working in that language. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The "System Libraries" of an executable work include any\hich\af31501\dbch\af31505\loch\f31501 thing, other +\par \hich\af31501\dbch\af31505\loch\f31501 than the work as a whole, that (a) is included in the normal form of +\par \hich\af31501\dbch\af31505\loch\f31501 packaging a Major Component, but which is not part of that Major +\par \hich\af31501\dbch\af31505\loch\f31501 Component, and (b) serves only to enable use of the work with that +\par \hich\af31501\dbch\af31505\loch\f31501 Major Component, or to implement a Standard Interface for which an +\par \hich\af31501\dbch\af31505\loch\f31501 implementation is available to the public in source code form. A +\par \hich\af31501\dbch\af31505\loch\f31501 "Major Component", in this context, means a major essential component +\par \hich\af31501\dbch\af31505\loch\f31501 (kernel, window system, and so on) of the specific op\hich\af31501\dbch\af31505\loch\f31501 erating system +\par \hich\af31501\dbch\af31505\loch\f31501 (if any) on which the executable work runs, or a compiler used to +\par \hich\af31501\dbch\af31505\loch\f31501 produce the work, or an object code interpreter used to run it. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The "Corresponding Source" for a work in object code form means all +\par \hich\af31501\dbch\af31505\loch\f31501 the source code needed to generate, instal\hich\af31501\dbch\af31505\loch\f31501 l, and (for an executable +\par \hich\af31501\dbch\af31505\loch\f31501 work) run the object code and to modify the work, including scripts to +\par \hich\af31501\dbch\af31505\loch\f31501 control those activities. However, it does not include the work's +\par \hich\af31501\dbch\af31505\loch\f31501 System Libraries, or general-purpose tools or generally available free +\par \hich\af31501\dbch\af31505\loch\f31501 programs which are us\hich\af31501\dbch\af31505\loch\f31501 ed unmodified in performing those activities but +\par \hich\af31501\dbch\af31505\loch\f31501 which are not part of the work. For example, Corresponding Source +\par \hich\af31501\dbch\af31505\loch\f31501 includes interface definition files associated with source files for +\par \hich\af31501\dbch\af31505\loch\f31501 the work, and the source code for shared libraries and dynamically +\par \hich\af31501\dbch\af31505\loch\f31501 link\hich\af31501\dbch\af31505\loch\f31501 ed subprograms that the work is specifically designed to require, +\par \hich\af31501\dbch\af31505\loch\f31501 such as by intimate data communication or control flow between those +\par \hich\af31501\dbch\af31505\loch\f31501 subprograms and other parts of the work. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The Corresponding Source need not include anything that users +\par \hich\af31501\dbch\af31505\loch\f31501 can regenerate au\hich\af31501\dbch\af31505\loch\f31501 tomatically from other parts of the Corresponding +\par \hich\af31501\dbch\af31505\loch\f31501 Source. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The Corresponding Source for a work in source code form is that +\par \hich\af31501\dbch\af31505\loch\f31501 same work. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 2. Basic Permissions. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 All rights granted under this License are granted for the term of +\par \hich\af31501\dbch\af31505\loch\f31501 copyright on the Program, and are irrevocable provided the stated +\par \hich\af31501\dbch\af31505\loch\f31501 conditions are met. This License explicitly affirms your unlimited +\par \hich\af31501\dbch\af31505\loch\f31501 permission to run the unmodified Program. The output fr\hich\af31501\dbch\af31505\loch\f31501 om running a +\par \hich\af31501\dbch\af31505\loch\f31501 covered work is covered by this License only if the output, given its +\par \hich\af31501\dbch\af31505\loch\f31501 content, constitutes a covered work. This License acknowledges your +\par \hich\af31501\dbch\af31505\loch\f31501 rights of fair use or other equivalent, as provided by copyright law. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You may make, run and propagate c\hich\af31501\dbch\af31505\loch\f31501 overed works that you do not +\par \hich\af31501\dbch\af31505\loch\f31501 convey, without conditions so long as your license otherwise remains +\par \hich\af31501\dbch\af31505\loch\f31501 in force. You may convey covered works to others for the sole purpose +\par \hich\af31501\dbch\af31505\loch\f31501 of having them make modifications exclusively for you, or provide you +\par \hich\af31501\dbch\af31505\loch\f31501 with facilities f\hich\af31501\dbch\af31505\loch\f31501 or running those works, provided that you comply with +\par \hich\af31501\dbch\af31505\loch\f31501 the terms of this License in conveying all material for which you do +\par \hich\af31501\dbch\af31505\loch\f31501 not control copyright. Those thus making or running the covered works +\par \hich\af31501\dbch\af31505\loch\f31501 for you must do so exclusively on your behalf, under your dire\hich\af31501\dbch\af31505\loch\f31501 ction +\par \hich\af31501\dbch\af31505\loch\f31501 and control, on terms that prohibit them from making any copies of +\par \hich\af31501\dbch\af31505\loch\f31501 your copyrighted material outside their relationship with you. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Conveying under any other circumstances is permitted solely under +\par \hich\af31501\dbch\af31505\loch\f31501 the conditions stated below. Sublicensing is not all\hich\af31501\dbch\af31505\loch\f31501 owed; section 10 +\par \hich\af31501\dbch\af31505\loch\f31501 makes it unnecessary. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 3. Protecting Users' Legal Rights From Anti-Circumvention Law. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 No covered work shall be deemed part of an effective technological +\par \hich\af31501\dbch\af31505\loch\f31501 measure under any applicable law fulfilling obligations under article +\par \hich\af31501\dbch\af31505\loch\f31501 11 of the WIPO copyright treaty adopted on 20 December 1996, or +\par \hich\af31501\dbch\af31505\loch\f31501 similar laws prohibiting or restricting circumvention of such +\par \hich\af31501\dbch\af31505\loch\f31501 measures. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 When you convey a covered work, you waive any leg\hich\af31501\dbch\af31505\loch\f31501 al power to forbid +\par \hich\af31501\dbch\af31505\loch\f31501 circumvention of technological measures to the extent such circumvention +\par \hich\af31501\dbch\af31505\loch\f31501 is effected by exercising rights under this License with respect to +\par \hich\af31501\dbch\af31505\loch\f31501 the covered work, and you disclaim any intention to limit operation or +\par \hich\af31501\dbch\af31505\loch\f31501 modification of the work \hich\af31501\dbch\af31505\loch\f31501 as a means of enforcing, against the work's +\par \hich\af31501\dbch\af31505\loch\f31501 users, your or third parties' legal rights to forbid circumvention of +\par \hich\af31501\dbch\af31505\loch\f31501 technological measures. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 4. Conveying Verbatim Copies. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You may convey verbatim copies of the Program's source code as you +\par \hich\af31501\dbch\af31505\loch\f31501 receive it, in any \hich\af31501\dbch\af31505\loch\f31501 medium, provided that you conspicuously and +\par \hich\af31501\dbch\af31505\loch\f31501 appropriately publish on each copy an appropriate copyright notice; +\par \hich\af31501\dbch\af31505\loch\f31501 keep intact all notices stating that this License and any +\par \hich\af31501\dbch\af31505\loch\f31501 non-permissive terms added in accord with section 7 apply to the code; +\par \hich\af31501\dbch\af31505\loch\f31501 keep intact all notices of the absence of any warranty; and give all +\par \hich\af31501\dbch\af31505\loch\f31501 recipients a copy of this License along with the Program. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You may charge any price or no price for each copy that you convey, +\par \hich\af31501\dbch\af31505\loch\f31501 and you may offer support or warranty protection for a fee. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 5. Conveying Modified Source Versions. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You may convey a work based on the Program, or the modifications to +\par \hich\af31501\dbch\af31505\loch\f31501 produce it from the Program, in the form of source code under the +\par \hich\af31501\dbch\af31505\loch\f31501 terms of section 4, provided that you also meet all of these conditions: +\par +\par \hich\af31501\dbch\af31505\loch\f31501 a) The \hich\af31501\dbch\af31505\loch\f31501 work must carry prominent notices stating that you modified +\par \hich\af31501\dbch\af31505\loch\f31501 it, and giving a relevant date. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 b) The work must carry prominent notices stating that it is +\par \hich\af31501\dbch\af31505\loch\f31501 released under this License and any conditions added under section +\par \hich\af31501\dbch\af31505\loch\f31501 7. This requirement modifies the re\hich\af31501\dbch\af31505\loch\f31501 quirement in section 4 to +\par \hich\af31501\dbch\af31505\loch\f31501 "keep intact all notices". +\par +\par \hich\af31501\dbch\af31505\loch\f31501 c) You must license the entire work, as a whole, under this +\par \hich\af31501\dbch\af31505\loch\f31501 License to anyone who comes into possession of a copy. This +\par \hich\af31501\dbch\af31505\loch\f31501 License will therefore apply, along with any applicable section 7 +\par \hich\af31501\dbch\af31505\loch\f31501 additional term\hich\af31501\dbch\af31505\loch\f31501 s, to the whole of the work, and all its parts, +\par \hich\af31501\dbch\af31505\loch\f31501 regardless of how they are packaged. This License gives no +\par \hich\af31501\dbch\af31505\loch\f31501 permission to license the work in any other way, but it does not +\par \hich\af31501\dbch\af31505\loch\f31501 invalidate such permission if you have separately received it. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 d) If the work has interactive user interfaces, each must display +\par \hich\af31501\dbch\af31505\loch\f31501 Appropriate Legal Notices; however, if the Program has interactive +\par \hich\af31501\dbch\af31505\loch\f31501 interfaces that do not display Appropriate Legal Notices, y\hich\af31501\dbch\af31505\loch\f31501 our +\par \hich\af31501\dbch\af31505\loch\f31501 work need not make them do so. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 A compilation of a covered work with other separate and independent +\par \hich\af31501\dbch\af31505\loch\f31501 works, which are not by their nature extensions of the covered work, +\par \hich\af31501\dbch\af31505\loch\f31501 and which are not combined with it such as to form a larger program, +\par \hich\af31501\dbch\af31505\loch\f31501 in or on a vol\hich\af31501\dbch\af31505\loch\f31501 ume of a storage or distribution medium, is called an +\par \hich\af31501\dbch\af31505\loch\f31501 "aggregate" if the compilation and its resulting copyright are not +\par \hich\af31501\dbch\af31505\loch\f31501 used to limit the access or legal rights of the compilation's users +\par \hich\af31501\dbch\af31505\loch\f31501 beyond what the individual works permit. Inclusion of a covered wo\hich\af31501\dbch\af31505\loch\f31501 rk +\par \hich\af31501\dbch\af31505\loch\f31501 in an aggregate does not cause this License to apply to the other +\par \hich\af31501\dbch\af31505\loch\f31501 parts of the aggregate. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 6. Conveying Non-Source Forms. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You may convey a covered work in object code form under the terms +\par \hich\af31501\dbch\af31505\loch\f31501 of sections 4 and 5, provided that you also convey the +\par \hich\af31501\dbch\af31505\loch\f31501 machine-r\hich\af31501\dbch\af31505\loch\f31501 eadable Corresponding Source under the terms of this License, +\par \hich\af31501\dbch\af31505\loch\f31501 in one of these ways: +\par +\par \hich\af31501\dbch\af31505\loch\f31501 a) Convey the object code in, or embodied in, a physical product +\par \hich\af31501\dbch\af31505\loch\f31501 (including a physical distribution medium), accompanied by the +\par \hich\af31501\dbch\af31505\loch\f31501 Corresponding Source fixed on a durable physical medium +\par \hich\af31501\dbch\af31505\loch\f31501 customarily used for software interchange. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 b) Convey the object code in, or embodied in, a physical product +\par \hich\af31501\dbch\af31505\loch\f31501 (including a physical distri\hich\af31501\dbch\af31505\loch\f31501 bution medium), accompanied by a +\par \hich\af31501\dbch\af31505\loch\f31501 written offer, valid for at least three years and valid for as +\par \hich\af31501\dbch\af31505\loch\f31501 long as you offer spare parts or customer support for that product +\par \hich\af31501\dbch\af31505\loch\f31501 model, to give anyone who possesses the object code either (1) a +\par \hich\af31501\dbch\af31505\loch\f31501 copy of the Corresponding So\hich\af31501\dbch\af31505\loch\f31501 urce for all the software in the +\par \hich\af31501\dbch\af31505\loch\f31501 product that is covered by this License, on a durable physical +\par \hich\af31501\dbch\af31505\loch\f31501 medium customarily used for software interchange, for a price no +\par \hich\af31501\dbch\af31505\loch\f31501 more than your reasonable cost of physically performing this +\par \hich\af31501\dbch\af31505\loch\f31501 conveying of source, or (2) access\hich\af31501\dbch\af31505\loch\f31501 to copy the +\par \hich\af31501\dbch\af31505\loch\f31501 Corresponding Source from a network server at no charge. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 c) Convey individual copies of the object code with a copy of the +\par \hich\af31501\dbch\af31505\loch\f31501 written offer to provide the Corresponding Source. This +\par \hich\af31501\dbch\af31505\loch\f31501 alternative is allowed only occasionally and noncommercially, \hich\af31501\dbch\af31505\loch\f31501 and +\par \hich\af31501\dbch\af31505\loch\f31501 only if you received the object code with such an offer, in accord +\par \hich\af31501\dbch\af31505\loch\f31501 with subsection 6b. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 d) Convey the object code by offering access from a designated +\par \hich\af31501\dbch\af31505\loch\f31501 place (gratis or for a charge), and offer equivalent access to the +\par \hich\af31501\dbch\af31505\loch\f31501 Corresponding Source in the same way through the same place at no +\par \hich\af31501\dbch\af31505\loch\f31501 further charge. You need not require recipients to copy the +\par \hich\af31501\dbch\af31505\loch\f31501 Corresponding Source along with the object code. If the place to +\par \hich\af31501\dbch\af31505\loch\f31501 copy the object code is a network server, the Corresponding Sou\hich\af31501\dbch\af31505\loch\f31501 rce +\par \hich\af31501\dbch\af31505\loch\f31501 may be on a different server (operated by you or a third party) +\par \hich\af31501\dbch\af31505\loch\f31501 that supports equivalent copying facilities, provided you maintain +\par \hich\af31501\dbch\af31505\loch\f31501 clear directions next to the object code saying where to find the +\par \hich\af31501\dbch\af31505\loch\f31501 Corresponding Source. Regardless of what server hosts \hich\af31501\dbch\af31505\loch\f31501 the +\par \hich\af31501\dbch\af31505\loch\f31501 Corresponding Source, you remain obligated to ensure that it is +\par \hich\af31501\dbch\af31505\loch\f31501 available for as long as needed to satisfy these requirements. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 e) Convey the object code using peer-to-peer transmission, provided +\par \hich\af31501\dbch\af31505\loch\f31501 you inform other peers where the object code and Corresp\hich\af31501\dbch\af31505\loch\f31501 onding +\par \hich\af31501\dbch\af31505\loch\f31501 Source of the work are being offered to the general public at no +\par \hich\af31501\dbch\af31505\loch\f31501 charge under subsection 6d. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 A separable portion of the object code, whose source code is excluded +\par \hich\af31501\dbch\af31505\loch\f31501 from the Corresponding Source as a System Library, need not be +\par \hich\af31501\dbch\af31505\loch\f31501 included in conveying \hich\af31501\dbch\af31505\loch\f31501 the object code work. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 A "User Product" is either (1) a "consumer product", which means any +\par \hich\af31501\dbch\af31505\loch\f31501 tangible personal property which is normally used for personal, family, +\par \hich\af31501\dbch\af31505\loch\f31501 or household purposes, or (2) anything designed or sold for incorporation +\par \hich\af31501\dbch\af31505\loch\f31501 into a dwelling. \hich\af31501\dbch\af31505\loch\f31501 In determining whether a product is a consumer product, +\par \hich\af31501\dbch\af31505\loch\f31501 doubtful cases shall be resolved in favor of coverage. For a particular +\par \hich\af31501\dbch\af31505\loch\f31501 product received by a particular user, "normally used" refers to a +\par \hich\af31501\dbch\af31505\loch\f31501 typical or common use of that class of product, regardless of the status +\par \hich\af31501\dbch\af31505\loch\f31501 of the particular user or of the way in which the particular user +\par \hich\af31501\dbch\af31505\loch\f31501 actually uses, or expects or is expected to use, t\hich\af31501\dbch\af31505\loch\f31501 he product. A product +\par \hich\af31501\dbch\af31505\loch\f31501 is a consumer product regardless of whether the product has substantial +\par \hich\af31501\dbch\af31505\loch\f31501 commercial, industrial or non-consumer uses, unless such uses represent +\par \hich\af31501\dbch\af31505\loch\f31501 the only significant mode of use of the product. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 "Installation Information" for a User P\hich\af31501\dbch\af31505\loch\f31501 roduct means any methods, +\par \hich\af31501\dbch\af31505\loch\f31501 procedures, authorization keys, or other information required to install +\par \hich\af31501\dbch\af31505\loch\f31501 and execute modified versions of a covered work in that User Product from +\par \hich\af31501\dbch\af31505\loch\f31501 a modified version of its Corresponding Source. The information must +\par \hich\af31501\dbch\af31505\loch\f31501 suffice to en\hich\af31501\dbch\af31505\loch\f31501 sure that the continued functioning of the modified object +\par \hich\af31501\dbch\af31505\loch\f31501 code is in no case prevented or interfered with solely because +\par \hich\af31501\dbch\af31505\loch\f31501 modification has been made. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 If you convey an object code work under this section in, or with, or +\par \hich\af31501\dbch\af31505\loch\f31501 specifically for use in, a User Prod\hich\af31501\dbch\af31505\loch\f31501 uct, and the conveying occurs as +\par \hich\af31501\dbch\af31505\loch\f31501 part of a transaction in which the right of possession and use of the +\par \hich\af31501\dbch\af31505\loch\f31501 User Product is transferred to the recipient in perpetuity or for a +\par \hich\af31501\dbch\af31505\loch\f31501 fixed term (regardless of how the transaction is characterized), the +\par \hich\af31501\dbch\af31505\loch\f31501 Corresponding So\hich\af31501\dbch\af31505\loch\f31501 urce conveyed under this section must be accompanied +\par \hich\af31501\dbch\af31505\loch\f31501 by the Installation Information. But this requirement does not apply +\par \hich\af31501\dbch\af31505\loch\f31501 if neither you nor any third party retains the ability to install +\par \hich\af31501\dbch\af31505\loch\f31501 modified object code on the User Product (for example, the work has +\par \hich\af31501\dbch\af31505\loch\f31501 been installed in ROM). +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The requirement to provide Installation Information does not include a +\par \hich\af31501\dbch\af31505\loch\f31501 requirement to continue to provide support service, warranty, or updates +\par \hich\af31501\dbch\af31505\loch\f31501 for a work that has been modified or installed by the recipient, or for +\par \hich\af31501\dbch\af31505\loch\f31501 the User Product in which it has been modified or installed. Access to a +\par \hich\af31501\dbch\af31505\loch\f31501 network may be denied when the modification itself materially and +\par \hich\af31501\dbch\af31505\loch\f31501 adversely affects the operation of the netwo\hich\af31501\dbch\af31505\loch\f31501 rk or violates the rules and +\par \hich\af31501\dbch\af31505\loch\f31501 protocols for communication across the network. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Corresponding Source conveyed, and Installation Information provided, +\par \hich\af31501\dbch\af31505\loch\f31501 in accord with this section must be in a format that is publicly +\par \hich\af31501\dbch\af31505\loch\f31501 documented (and with an implementation avai\hich\af31501\dbch\af31505\loch\f31501 lable to the public in +\par \hich\af31501\dbch\af31505\loch\f31501 source code form), and must require no special password or key for +\par \hich\af31501\dbch\af31505\loch\f31501 unpacking, reading or copying. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 7. Additional Terms. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 "Additional permissions" are terms that supplement the terms of this +\par \hich\af31501\dbch\af31505\loch\f31501 License by making exceptions from one or mo\hich\af31501\dbch\af31505\loch\f31501 re of its conditions. +\par \hich\af31501\dbch\af31505\loch\f31501 Additional permissions that are applicable to the entire Program shall +\par \hich\af31501\dbch\af31505\loch\f31501 be treated as though they were included in this License, to the extent +\par \hich\af31501\dbch\af31505\loch\f31501 that they are valid under applicable law. If additional permissions +\par \hich\af31501\dbch\af31505\loch\f31501 apply only to part of t\hich\af31501\dbch\af31505\loch\f31501 he Program, that part may be used separately +\par \hich\af31501\dbch\af31505\loch\f31501 under those permissions, but the entire Program remains governed by +\par \hich\af31501\dbch\af31505\loch\f31501 this License without regard to the additional permissions. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 When you convey a copy of a covered work, you may at your option +\par \hich\af31501\dbch\af31505\loch\f31501 remove any additional permissions from that copy, or from any part of +\par \hich\af31501\dbch\af31505\loch\f31501 it. (Additional permissions may be written to require their own +\par \hich\af31501\dbch\af31505\loch\f31501 removal in certain cases when you modify the work.) You\hich\af31501\dbch\af31505\loch\f31501 may place +\par \hich\af31501\dbch\af31505\loch\f31501 additional permissions on material, added by you to a covered work, +\par \hich\af31501\dbch\af31505\loch\f31501 for which you have or can give appropriate copyright permission. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Notwithstanding any other provision of this License, for material you +\par \hich\af31501\dbch\af31505\loch\f31501 add to a covered work, you may (if author\hich\af31501\dbch\af31505\loch\f31501 ized by the copyright holders of +\par \hich\af31501\dbch\af31505\loch\f31501 that material) supplement the terms of this License with terms: +\par +\par \hich\af31501\dbch\af31505\loch\f31501 a) Disclaiming warranty or limiting liability differently from the +\par \hich\af31501\dbch\af31505\loch\f31501 terms of sections 15 and 16 of this License; or +\par +\par \hich\af31501\dbch\af31505\loch\f31501 b) Requiring preservation of specified rea\hich\af31501\dbch\af31505\loch\f31501 sonable legal notices or +\par \hich\af31501\dbch\af31505\loch\f31501 author attributions in that material or in the Appropriate Legal +\par \hich\af31501\dbch\af31505\loch\f31501 Notices displayed by works containing it; or +\par +\par \hich\af31501\dbch\af31505\loch\f31501 c) Prohibiting misrepresentation of the origin of that material, or +\par \hich\af31501\dbch\af31505\loch\f31501 requiring that modified versions of such material be\hich\af31501\dbch\af31505\loch\f31501 marked in +\par \hich\af31501\dbch\af31505\loch\f31501 reasonable ways as different from the original version; or +\par +\par \hich\af31501\dbch\af31505\loch\f31501 d) Limiting the use for publicity purposes of names of licensors or +\par \hich\af31501\dbch\af31505\loch\f31501 authors of the material; or +\par +\par \hich\af31501\dbch\af31505\loch\f31501 e) Declining to grant rights under trademark law for use of some +\par \hich\af31501\dbch\af31505\loch\f31501 trade names, trademarks, or service marks; or +\par +\par \hich\af31501\dbch\af31505\loch\f31501 f) Requiring indemnification of licensors and authors of that +\par \hich\af31501\dbch\af31505\loch\f31501 material by anyone who conveys the material (or modified versions of +\par \hich\af31501\dbch\af31505\loch\f31501 it) with cont\hich\af31501\dbch\af31505\loch\f31501 ractual assumptions of liability to the recipient, for +\par \hich\af31501\dbch\af31505\loch\f31501 any liability that these contractual assumptions directly impose on +\par \hich\af31501\dbch\af31505\loch\f31501 those licensors and authors. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 All other non-permissive additional terms are considered "further +\par \hich\af31501\dbch\af31505\loch\f31501 restrictions" within the meaning of s\hich\af31501\dbch\af31505\loch\f31501 ection 10. If the Program as you +\par \hich\af31501\dbch\af31505\loch\f31501 received it, or any part of it, contains a notice stating that it is +\par \hich\af31501\dbch\af31505\loch\f31501 governed by this License along with a term that is a further +\par \hich\af31501\dbch\af31505\loch\f31501 restriction, you may remove that term. If a license document contains +\par \hich\af31501\dbch\af31505\loch\f31501 a further restriction\hich\af31501\dbch\af31505\loch\f31501 but permits relicensing or conveying under this +\par \hich\af31501\dbch\af31505\loch\f31501 License, you may add to a covered work material governed by the terms +\par \hich\af31501\dbch\af31505\loch\f31501 of that license document, provided that the further restriction does +\par \hich\af31501\dbch\af31505\loch\f31501 not survive such relicensing or conveying. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 If you add terms to a co\hich\af31501\dbch\af31505\loch\f31501 vered work in accord with this section, you +\par \hich\af31501\dbch\af31505\loch\f31501 must place, in the relevant source files, a statement of the +\par \hich\af31501\dbch\af31505\loch\f31501 additional terms that apply to those files, or a notice indicating +\par \hich\af31501\dbch\af31505\loch\f31501 where to find the applicable terms. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Additional terms, permissive or non-permissive, may be stated in the +\par \hich\af31501\dbch\af31505\loch\f31501 form of a separately written license, or stated as exceptions; +\par \hich\af31501\dbch\af31505\loch\f31501 the above requirements apply either way. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 8. Termination. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You may not propagate or modify a covered work except as expressl\hich\af31501\dbch\af31505\loch\f31501 y +\par \hich\af31501\dbch\af31505\loch\f31501 provided under this License. Any attempt otherwise to propagate or +\par \hich\af31501\dbch\af31505\loch\f31501 modify it is void, and will automatically terminate your rights under +\par \hich\af31501\dbch\af31505\loch\f31501 this License (including any patent licenses granted under the third +\par \hich\af31501\dbch\af31505\loch\f31501 paragraph of section 11). +\par +\par \hich\af31501\dbch\af31505\loch\f31501 However, if you cease\hich\af31501\dbch\af31505\loch\f31501 all violation of this License, then your +\par \hich\af31501\dbch\af31505\loch\f31501 license from a particular copyright holder is reinstated (a) +\par \hich\af31501\dbch\af31505\loch\f31501 provisionally, unless and until the copyright holder explicitly and +\par \hich\af31501\dbch\af31505\loch\f31501 finally terminates your license, and (b) permanently, if the copyright +\par \hich\af31501\dbch\af31505\loch\f31501 holder fails t\hich\af31501\dbch\af31505\loch\f31501 o notify you of the violation by some reasonable means +\par \hich\af31501\dbch\af31505\loch\f31501 prior to 60 days after the cessation. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Moreover, your license from a particular copyright holder is +\par \hich\af31501\dbch\af31505\loch\f31501 reinstated permanently if the copyright holder notifies you of the +\par \hich\af31501\dbch\af31505\loch\f31501 violation by some reasonable means\hich\af31501\dbch\af31505\loch\f31501 , this is the first time you have +\par \hich\af31501\dbch\af31505\loch\f31501 received notice of violation of this License (for any work) from that +\par \hich\af31501\dbch\af31505\loch\f31501 copyright holder, and you cure the violation prior to 30 days after +\par \hich\af31501\dbch\af31505\loch\f31501 your receipt of the notice. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Termination of your rights under this section does not terminate the +\par \hich\af31501\dbch\af31505\loch\f31501 licenses of parties who have received copies or rights from you under +\par \hich\af31501\dbch\af31505\loch\f31501 this License. If your rights have been terminated and not permanently +\par \hich\af31501\dbch\af31505\loch\f31501 reinstated, you do not qualify to receive new \hich\af31501\dbch\af31505\loch\f31501 licenses for the same +\par \hich\af31501\dbch\af31505\loch\f31501 material under section 10. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 9. Acceptance Not Required for Having Copies. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You are not required to accept this License in order to receive or +\par \hich\af31501\dbch\af31505\loch\f31501 run a copy of the Program. Ancillary propagation of a covered work +\par \hich\af31501\dbch\af31505\loch\f31501 occurring solely as a co\hich\af31501\dbch\af31505\loch\f31501 nsequence of using peer-to-peer transmission +\par \hich\af31501\dbch\af31505\loch\f31501 to receive a copy likewise does not require acceptance. However, +\par \hich\af31501\dbch\af31505\loch\f31501 nothing other than this License grants you permission to propagate or +\par \hich\af31501\dbch\af31505\loch\f31501 modify any covered work. These actions infringe copyright if you do +\par \hich\af31501\dbch\af31505\loch\f31501 not ac\hich\af31501\dbch\af31505\loch\f31501 cept this License. Therefore, by modifying or propagating a +\par \hich\af31501\dbch\af31505\loch\f31501 covered work, you indicate your acceptance of this License to do so. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 10. Automatic Licensing of Downstream Recipients. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Each time you convey a covered work, the recipient automatically +\par \hich\af31501\dbch\af31505\loch\f31501 receives \hich\af31501\dbch\af31505\loch\f31501 a license from the original licensors, to run, modify and +\par \hich\af31501\dbch\af31505\loch\f31501 propagate that work, subject to this License. You are not responsible +\par \hich\af31501\dbch\af31505\loch\f31501 for enforcing compliance by third parties with this License. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 An "entity transaction" is a transaction transferring control of an +\par \hich\af31501\dbch\af31505\loch\f31501 organization, or substantially all assets of one, or subdividing an +\par \hich\af31501\dbch\af31505\loch\f31501 organization, or merging organizations. If propagation of a covered +\par \hich\af31501\dbch\af31505\loch\f31501 work results from an entity transaction, each party\hich\af31501\dbch\af31505\loch\f31501 to that +\par \hich\af31501\dbch\af31505\loch\f31501 transaction who receives a copy of the work also receives whatever +\par \hich\af31501\dbch\af31505\loch\f31501 licenses to the work the party's predecessor in interest had or could +\par \hich\af31501\dbch\af31505\loch\f31501 give under the previous paragraph, plus a right to possession of the +\par \hich\af31501\dbch\af31505\loch\f31501 Corresponding Source of the work from the\hich\af31501\dbch\af31505\loch\f31501 predecessor in interest, if +\par \hich\af31501\dbch\af31505\loch\f31501 the predecessor has it or can get it with reasonable efforts. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You may not impose any further restrictions on the exercise of the +\par \hich\af31501\dbch\af31505\loch\f31501 rights granted or affirmed under this License. For example, you may +\par \hich\af31501\dbch\af31505\loch\f31501 not impose a license fee, ro\hich\af31501\dbch\af31505\loch\f31501 yalty, or other charge for exercise of +\par \hich\af31501\dbch\af31505\loch\f31501 rights granted under this License, and you may not initiate litigation +\par \hich\af31501\dbch\af31505\loch\f31501 (including a cross-claim or counterclaim in a lawsuit) alleging that +\par \hich\af31501\dbch\af31505\loch\f31501 any patent claim is infringed by making, using, selling, offering for +\par \hich\af31501\dbch\af31505\loch\f31501 sale, o\hich\af31501\dbch\af31505\loch\f31501 r importing the Program or any portion of it. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 11. Patents. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 A "contributor" is a copyright holder who authorizes use under this +\par \hich\af31501\dbch\af31505\loch\f31501 License of the Program or a work on which the Program is based. The +\par \hich\af31501\dbch\af31505\loch\f31501 work thus licensed is called the contributor's "contributo\hich\af31501\dbch\af31505\loch\f31501 r version". +\par +\par \hich\af31501\dbch\af31505\loch\f31501 A contributor's "essential patent claims" are all patent claims +\par \hich\af31501\dbch\af31505\loch\f31501 owned or controlled by the contributor, whether already acquired or +\par \hich\af31501\dbch\af31505\loch\f31501 hereafter acquired, that would be infringed by some manner, permitted +\par \hich\af31501\dbch\af31505\loch\f31501 by this License, of making, using, or selling its contributor version, +\par \hich\af31501\dbch\af31505\loch\f31501 but do not include claims that would be infringed only as a +\par \hich\af31501\dbch\af31505\loch\f31501 consequence of further modification of the contributor \hich\af31501\dbch\af31505\loch\f31501 version. For +\par \hich\af31501\dbch\af31505\loch\f31501 purposes of this definition, "control" includes the right to grant +\par \hich\af31501\dbch\af31505\loch\f31501 patent sublicenses in a manner consistent with the requirements of +\par \hich\af31501\dbch\af31505\loch\f31501 this License. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Each contributor grants you a non-exclusive, worldwide, royalty-free +\par \hich\af31501\dbch\af31505\loch\f31501 patent license under the\hich\af31501\dbch\af31505\loch\f31501 contributor's essential patent claims, to +\par \hich\af31501\dbch\af31505\loch\f31501 make, use, sell, offer for sale, import and otherwise run, modify and +\par \hich\af31501\dbch\af31505\loch\f31501 propagate the contents of its contributor version. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 In the following three paragraphs, a "patent license" is any express +\par \hich\af31501\dbch\af31505\loch\f31501 agreement or commitmen\hich\af31501\dbch\af31505\loch\f31501 t, however denominated, not to enforce a patent +\par \hich\af31501\dbch\af31505\loch\f31501 (such as an express permission to practice a patent or covenant not to +\par \hich\af31501\dbch\af31505\loch\f31501 sue for patent infringement). To "grant" such a patent license to a +\par \hich\af31501\dbch\af31505\loch\f31501 party means to make such an agreement or commitment not to enforce a +\par \hich\af31501\dbch\af31505\loch\f31501 patent against the party. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 If you convey a covered work, knowingly relying on a patent license, +\par \hich\af31501\dbch\af31505\loch\f31501 and the Corresponding Source of the work is not available for anyone +\par \hich\af31501\dbch\af31505\loch\f31501 to copy, free of charge and under the terms of this License, through a +\par \hich\af31501\dbch\af31505\loch\f31501 publicly available network server or other readily accessible means, +\par \hich\af31501\dbch\af31505\loch\f31501 then you must either (1) cause the Corresponding Source to be so +\par \hich\af31501\dbch\af31505\loch\f31501 available, or (2) arrange to deprive yourself of the benefit of the +\par \hich\af31501\dbch\af31505\loch\f31501 patent license for this particular work, or (3) arrang\hich\af31501\dbch\af31505\loch\f31501 e, in a manner +\par \hich\af31501\dbch\af31505\loch\f31501 consistent with the requirements of this License, to extend the patent +\par \hich\af31501\dbch\af31505\loch\f31501 license to downstream recipients. "Knowingly relying" means you have +\par \hich\af31501\dbch\af31505\loch\f31501 actual knowledge that, but for the patent license, your conveying the +\par \hich\af31501\dbch\af31505\loch\f31501 covered work in a country, or \hich\af31501\dbch\af31505\loch\f31501 your recipient's use of the covered work +\par \hich\af31501\dbch\af31505\loch\f31501 in a country, would infringe one or more identifiable patents in that +\par \hich\af31501\dbch\af31505\loch\f31501 country that you have reason to believe are valid. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 If, pursuant to or in connection with a single transaction or +\par \hich\af31501\dbch\af31505\loch\f31501 arrangement, you convey, or pro\hich\af31501\dbch\af31505\loch\f31501 pagate by procuring conveyance of, a +\par \hich\af31501\dbch\af31505\loch\f31501 covered work, and grant a patent license to some of the parties +\par \hich\af31501\dbch\af31505\loch\f31501 receiving the covered work authorizing them to use, propagate, modify +\par \hich\af31501\dbch\af31505\loch\f31501 or convey a specific copy of the covered work, then the patent license +\par \hich\af31501\dbch\af31505\loch\f31501 you grant is a\hich\af31501\dbch\af31505\loch\f31501 utomatically extended to all recipients of the covered +\par \hich\af31501\dbch\af31505\loch\f31501 work and works based on it. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 A patent license is "discriminatory" if it does not include within +\par \hich\af31501\dbch\af31505\loch\f31501 the scope of its coverage, prohibits the exercise of, or is +\par \hich\af31501\dbch\af31505\loch\f31501 conditioned on the non-exercise of one or mor\hich\af31501\dbch\af31505\loch\f31501 e of the rights that are +\par \hich\af31501\dbch\af31505\loch\f31501 specifically granted under this License. You may not convey a covered +\par \hich\af31501\dbch\af31505\loch\f31501 work if you are a party to an arrangement with a third party that is +\par \hich\af31501\dbch\af31505\loch\f31501 in the business of distributing software, under which you make payment +\par \hich\af31501\dbch\af31505\loch\f31501 to the third party based on the extent of your activity of conveying +\par \hich\af31501\dbch\af31505\loch\f31501 the work, and under which the third party grants, to any of the +\par \hich\af31501\dbch\af31505\loch\f31501 parties who would receive the covered work from you, a discriminatory +\par \hich\af31501\dbch\af31505\loch\f31501 patent license (a) in connection with copies of the c\hich\af31501\dbch\af31505\loch\f31501 overed work +\par \hich\af31501\dbch\af31505\loch\f31501 conveyed by you (or copies made from those copies), or (b) primarily +\par \hich\af31501\dbch\af31505\loch\f31501 for and in connection with specific products or compilations that +\par \hich\af31501\dbch\af31505\loch\f31501 contain the covered work, unless you entered into that arrangement, +\par \hich\af31501\dbch\af31505\loch\f31501 or that patent license was granted, prior\hich\af31501\dbch\af31505\loch\f31501 to 28 March 2007. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Nothing in this License shall be construed as excluding or limiting +\par \hich\af31501\dbch\af31505\loch\f31501 any implied license or other defenses to infringement that may +\par \hich\af31501\dbch\af31505\loch\f31501 otherwise be available to you under applicable patent law. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 12. No Surrender of Others' Freedom. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 If cond\hich\af31501\dbch\af31505\loch\f31501 itions are imposed on you (whether by court order, agreement or +\par \hich\af31501\dbch\af31505\loch\f31501 otherwise) that contradict the conditions of this License, they do not +\par \hich\af31501\dbch\af31505\loch\f31501 excuse you from the conditions of this License. If you cannot convey a +\par \hich\af31501\dbch\af31505\loch\f31501 covered work so as to satisfy simultaneously your\hich\af31501\dbch\af31505\loch\f31501 obligations under this +\par \hich\af31501\dbch\af31505\loch\f31501 License and any other pertinent obligations, then as a consequence you may +\par \hich\af31501\dbch\af31505\loch\f31501 not convey it at all. For example, if you agree to terms that obligate you +\par \hich\af31501\dbch\af31505\loch\f31501 to collect a royalty for further conveying from those to whom you convey +\par \hich\af31501\dbch\af31505\loch\f31501 the Prog\hich\af31501\dbch\af31505\loch\f31501 ram, the only way you could satisfy both those terms and this +\par \hich\af31501\dbch\af31505\loch\f31501 License would be to refrain entirely from conveying the Program. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 13. Use with the GNU Affero General Public License. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Notwithst\hich\af31501\dbch\af31505\loch\f31501 anding any other provision of this License, you have +\par \hich\af31501\dbch\af31505\loch\f31501 permission to link or combine any covered work with a work licensed +\par \hich\af31501\dbch\af31505\loch\f31501 under version 3 of the GNU Affero General Public License into a single +\par \hich\af31501\dbch\af31505\loch\f31501 combined work, and to convey the resulting work. The terms of t\hich\af31501\dbch\af31505\loch\f31501 his +\par \hich\af31501\dbch\af31505\loch\f31501 License will continue to apply to the part which is the covered work, +\par \hich\af31501\dbch\af31505\loch\f31501 but the special requirements of the GNU Affero General Public License, +\par \hich\af31501\dbch\af31505\loch\f31501 section 13, concerning interaction through a network will apply to the +\par \hich\af31501\dbch\af31505\loch\f31501 combination as such. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 14. Revised Versio\hich\af31501\dbch\af31505\loch\f31501 ns of this License. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The Free Software Foundation may publish revised and/or new versions of +\par \hich\af31501\dbch\af31505\loch\f31501 the GNU General Public License from time to time. Such new versions will +\par \hich\af31501\dbch\af31505\loch\f31501 be similar in spirit to the present version, but may differ in detail to +\par \hich\af31501\dbch\af31505\loch\f31501 address new prob\hich\af31501\dbch\af31505\loch\f31501 lems or concerns. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Each version is given a distinguishing version number. If the +\par \hich\af31501\dbch\af31505\loch\f31501 Program specifies that a certain numbered version of the GNU General +\par \hich\af31501\dbch\af31505\loch\f31501 Public License "or any later version" applies to it, you have the +\par \hich\af31501\dbch\af31505\loch\f31501 option of following the terms and condi\hich\af31501\dbch\af31505\loch\f31501 tions either of that numbered +\par \hich\af31501\dbch\af31505\loch\f31501 version or of any later version published by the Free Software +\par \hich\af31501\dbch\af31505\loch\f31501 Foundation. If the Program does not specify a version number of the +\par \hich\af31501\dbch\af31505\loch\f31501 GNU General Public License, you may choose any version ever published +\par \hich\af31501\dbch\af31505\loch\f31501 by the Free Software Foundation. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 If the Program specifies that a proxy can decide which future +\par \hich\af31501\dbch\af31505\loch\f31501 versions of the GNU General Public License can be used, that proxy's +\par \hich\af31501\dbch\af31505\loch\f31501 public statement of acceptance of a version permanently authorizes you +\par \hich\af31501\dbch\af31505\loch\f31501 to choose that versi\hich\af31501\dbch\af31505\loch\f31501 on for the Program. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Later license versions may give you additional or different +\par \hich\af31501\dbch\af31505\loch\f31501 permissions. However, no additional obligations are imposed on any +\par \hich\af31501\dbch\af31505\loch\f31501 author or copyright holder as a result of your choosing to follow a +\par \hich\af31501\dbch\af31505\loch\f31501 later version. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 15. Disclaimer of Warra\hich\af31501\dbch\af31505\loch\f31501 nty. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +\par \hich\af31501\dbch\af31505\loch\f31501 APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +\par \hich\af31501\dbch\af31505\loch\f31501 HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +\par \hich\af31501\dbch\af31505\loch\f31501 OF ANY KIND, EITHER EXPRESSED OR IMPLIED\hich\af31501\dbch\af31505\loch\f31501 , INCLUDING, BUT NOT LIMITED TO, +\par \hich\af31501\dbch\af31505\loch\f31501 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +\par \hich\af31501\dbch\af31505\loch\f31501 PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +\par \hich\af31501\dbch\af31505\loch\f31501 IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +\par \hich\af31501\dbch\af31505\loch\f31501 ALL \hich\af31501\dbch\af31505\loch\f31501 NECESSARY SERVICING, REPAIR OR CORRECTION. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 16. Limitation of Liability. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +\par \hich\af31501\dbch\af31505\loch\f31501 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +\par \hich\af31501\dbch\af31505\loch\f31501 THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +\par \hich\af31501\dbch\af31505\loch\f31501 GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +\par \hich\af31501\dbch\af31505\loch\f31501 USE OR INABILITY TO USE THE PROGR\hich\af31501\dbch\af31505\loch\f31501 AM (INCLUDING BUT NOT LIMITED TO LOSS OF +\par \hich\af31501\dbch\af31505\loch\f31501 DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +\par \hich\af31501\dbch\af31505\loch\f31501 PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +\par \hich\af31501\dbch\af31505\loch\f31501 EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBIL\hich\af31501\dbch\af31505\loch\f31501 ITY OF +\par \hich\af31501\dbch\af31505\loch\f31501 SUCH DAMAGES. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 17. Interpretation of Sections 15 and 16. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 If the disclaimer of warranty and limitation of liability provided +\par \hich\af31501\dbch\af31505\loch\f31501 above cannot be given local legal effect according to their terms, +\par \hich\af31501\dbch\af31505\loch\f31501 reviewing courts shall apply local law that most closely \hich\af31501\dbch\af31505\loch\f31501 approximates +\par \hich\af31501\dbch\af31505\loch\f31501 an absolute waiver of all civil liability in connection with the +\par \hich\af31501\dbch\af31505\loch\f31501 Program, unless a warranty or assumption of liability accompanies a +\par \hich\af31501\dbch\af31505\loch\f31501 copy of the Program in return for a fee. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 END OF TERMS AND CONDITIONS +\par +\par \hich\af31501\dbch\af31505\loch\f31501 How to Apply These Terms to Your New Pr\hich\af31501\dbch\af31505\loch\f31501 ograms +\par +\par \hich\af31501\dbch\af31505\loch\f31501 If you develop a new program, and you want it to be of the greatest +\par \hich\af31501\dbch\af31505\loch\f31501 possible use to the public, the best way to achieve this is to make it +\par \hich\af31501\dbch\af31505\loch\f31501 free software which everyone can redistribute and change under these terms. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 To do so, attach the following notices to the program. It is safest +\par \hich\af31501\dbch\af31505\loch\f31501 to attach them to the start of each source file to most effectively +\par \hich\af31501\dbch\af31505\loch\f31501 state the exclusion of warranty; and each \hich\af31501\dbch\af31505\loch\f31501 file should have at least +\par \hich\af31501\dbch\af31505\loch\f31501 the "copyright" line and a pointer to where the full notice is found. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 +\par \hich\af31501\dbch\af31505\loch\f31501 Copyright (C) +\par +\par \hich\af31501\dbch\af31505\loch\f31501 This program is free software: you can redistri\hich\af31501\dbch\af31505\loch\f31501 bute it and/or modify +\par \hich\af31501\dbch\af31505\loch\f31501 it under the terms of the GNU General Public License as published by +\par \hich\af31501\dbch\af31505\loch\f31501 the Free Software Foundation, either version 3 of the License, or +\par \hich\af31501\dbch\af31505\loch\f31501 (at your option) any later version. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 This program is distributed in the hope that it will be useful, +\par \hich\af31501\dbch\af31505\loch\f31501 but WITHOUT ANY WARRANTY; without even the implied warranty of +\par \hich\af31501\dbch\af31505\loch\f31501 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +\par \hich\af31501\dbch\af31505\loch\f31501 GNU General Public License for more details. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You should have received a copy of the GNU General Public License +\par \hich\af31501\dbch\af31505\loch\f31501 along with this p\hich\af31501\dbch\af31505\loch\f31501 rogram. If not, see . +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Also add information on how to contact you by electronic and paper mail. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 If the program does terminal interaction, make it output a short +\par \hich\af31501\dbch\af31505\loch\f31501 notice like this when it starts in an interactive mode: +\par +\par \hich\af31501\dbch\af31505\loch\f31501 Copyright (C) +\par \hich\af31501\dbch\af31505\loch\f31501 This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. +\par \hich\af31501\dbch\af31505\loch\f31501 This is free software, and you are welcome to redistribute it +\par \hich\af31501\dbch\af31505\loch\f31501 under certai\hich\af31501\dbch\af31505\loch\f31501 n conditions; type `show c' for details. +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The hypothetical commands `show w' and `show c' should show the appropriate +\par \hich\af31501\dbch\af31505\loch\f31501 parts of the General Public License. Of course, your program's commands +\par \hich\af31501\dbch\af31505\loch\f31501 might be different; for a GUI interface, you would use an "about \hich\af31501\dbch\af31505\loch\f31501 box". +\par +\par \hich\af31501\dbch\af31505\loch\f31501 You should also get your employer (if you work as a programmer) or school, +\par \hich\af31501\dbch\af31505\loch\f31501 if any, to sign a "copyright disclaimer" for the program, if necessary. +\par \hich\af31501\dbch\af31505\loch\f31501 For more information on this, and how to apply and follow the GNU GPL, see +\par \hich\af31501\dbch\af31505\loch\f31501 . +\par +\par \hich\af31501\dbch\af31505\loch\f31501 The GNU General Public License does not permit incorporating your program +\par \hich\af31501\dbch\af31505\loch\f31501 into proprietary programs. If your program is a subroutine library, you +\par \hich\af31501\dbch\af31505\loch\f31501 may consider it more useful to permit linking proprietary applications with +\par \hich\af31501\dbch\af31505\loch\f31501 the library. If this is wh\hich\af31501\dbch\af31505\loch\f31501 at you want to do, use the GNU Lesser General +\par \hich\af31501\dbch\af31505\loch\f31501 Public License instead of this License. But first, please read +\par \hich\af31501\dbch\af31505\loch\f31501 . +\par +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100b6f4679893070000c9200000160000007468656d652f7468656d652f +7468656d65312e786d6cec59cd8b1bc915bf07f23f347d97f5d5ad8fc1f2a24fcfda33b6b164873dd648a5eef2547789aad28cc56208de532e81c026e49085bd +ed21842cecc22eb9e48f31d8249b3f22afaa5bdd5552c99e191c3061463074977eefd5afde7bf5de53d5ddcf5e26d4bbc05c1096f6fcfa9d9aefe174ce16248d +7afeb3d9a4d2f13d2151ba4094a5b8e76fb0f03fbbf7eb5fdd454732c609f6403e1547a8e7c752ae8eaa5531876124eeb0154ee1bb25e30992f0caa3ea82a34b +d09bd06aa3566b55134452df4b51026a1f2f97648ebd9952e9dfdb2a1f53784da5500373caa74a35b6243476715e5708b11143cabd0b447b3eccb3609733fc52 +fa1e4542c2173dbfa6fffceabdbb5574940b517940d6909be8bf5c2e17589c37f49c3c3a2b260d823068f50bfd1a40e53e6edc1eb7c6ad429f06a0f91c569a71 +b175b61bc320c71aa0ecd1a17bd41e35eb16ded0dfdce3dc0fd5c7c26b50a63fd8c34f2643b0a285d7a00c1feee1c3417730b2f56b50866fede1dbb5fe28685b +fa3528a6243ddf43d7c25673b85d6d0159327aec8477c360d26ee4ca4b144443115d6a8a254be5a1584bd00bc6270050408a24493db959e1259a43140f112567 +9c7827248a21f056286502866b8ddaa4d684ffea13e827ed5174849121ad780113b137a4f87862cec94af6fc07a0d537206f7ffef9cdeb1fdfbcfee9cd575fbd +79fdf77c6eadca923b466964cafdf2dd1ffef3cd6fbd7ffff0ed2f5fff319b7a172f4cfcbbbffdeedd3ffef93ef5b0e2d2146ffff4fdbb1fbf7ffbe7dfffebaf +5f3bb4f7393a33e1339260e13dc297de5396c0021dfcf119bf9ec42c46c494e8a791402952b338f48f656ca11f6d10450edc00db767cce21d5b880f7d72f2cc2 +d398af2571687c182716f094313a60dc6985876a2ec3ccb3751ab927e76b13f714a10bd7dc43945a5e1eaf579063894be530c616cd2714a5124538c5d253dfb1 +738c1dabfb8210cbaea764ce99604be97d41bc01224e93ccc899154da5d03149c02f1b1741f0b7659bd3e7de8051d7aa47f8c246c2de40d4417e86a965c6fb68 +2d51e252394309350d7e8264ec2239ddf0b9891b0b099e8e3065de78818570c93ce6b05ec3e90f21cdb8dd7e4a37898de4929cbb749e20c64ce4889d0f6394ac +5cd829496313fbb938871045de13265df05366ef10f50e7e40e941773f27d872f787b3c133c8b026a53240d4376beef0e57dccacf89d6ee8126157aae9f3c44a +b17d4e9cd131584756689f604cd1255a60ec3dfbdcc160c05696cd4bd20f62c82ac7d815580f901dabea3dc5027a25d5dcece7c91322ac909de2881de073bad9 +493c1b9426881fd2fc08bc6eda7c0ca52e7105c0633a3f37818f08f480102f4ea33c16a0c308ee835a9fc4c82a60ea5db8e375c32dff5d658fc1be7c61d1b8c2 +be04197c6d1948eca6cc7b6d3343d49aa00c9819822ec3956e41c4727f29a28aab165b3be596f6a62ddd00dd91d5f42424fd6007b4d3fb84ffbbde073a8cb77f +f9c6b10f3e4ebfe3566c25ab6b763a8792c9f14e7f7308b7dbd50c195f904fbfa919a175fa04431dd9cf58b73dcd6d4fe3ffdff73487f6f36d2773a8dfb8ed64 +7ce8306e3b99fc70e5e3743265f3027d8d3af0c80e7af4b14f72f0d46749289dca0dc527421ffc08f83db398c0a092d3279eb838055cc5f0a8ca1c4c60e1228e +b48cc799fc0d91f134462b381daafb4a492472d591f0564cc0a1911e76ea5678ba4e4ed9223becacd7d5c16656590592e5782d2cc6e1a04a66e856bb3cc02bd4 +6bb6913e68dd1250b2d721614c6693683a48b4b783ca48fa58178ce620a157f65158741d2c3a4afdd6557b2c805ae115f8c1edc1cff49e1f06200242701e07cd +f942f92973f5d6bbda991fd3d3878c69450034d8db08283ddd555c0f2e4fad2e0bb52b78da2261849b4d425b46377822869fc17974aad1abd0b8aeafbba54b2d +7aca147a3e08ad9246bbf33e1637f535c8ede6069a9a9982a6de65cf6f35430899395af5fc251c1ac363b282d811ea3717a211dcbccc25cf36fc4d32cb8a0b39 +4222ce0cae934e960d122231f728497abe5a7ee1069aea1ca2b9d51b90103e59725d482b9f1a3970baed64bc5ce2b934dd6e8c284b67af90e1b35ce1fc568bdf +1cac24d91adc3d8d1797de195df3a708422c6cd795011744c0dd413db3e682c0655891c8caf8db294c79da356fa3740c65e388ae62945714339967709dca0b3a +faadb081f196af190c6a98242f8467912ab0a651ad6a5a548d8cc3c1aafb6121653923699635d3ca2aaa6abab39835c3b60cecd8f26645de60b53531e434b3c2 +67a97b37e576b7b96ea74f28aa0418bcb09fa3ea5ea12018d4cac92c6a8af17e1a56393b1fb56bc776811fa07695226164fdd656ed8edd8a1ae19c0e066f54f9 +416e376a6168b9ed2bb5a5f5adb979b1cdce5e40f2184197bba6526857c2c92e47d0104d754f92a50dd8222f65be35e0c95b73d2f3bfac85fd60d80887955a27 +1c57826650ab74c27eb3d20fc3667d1cd66ba341e31514161927f530bbb19fc00506dde4f7f67a7cefee3ed9ded1dc99b3a4caf4dd7c5513d777f7f5c6e1bb7b +8f40d2f9b2d598749bdd41abd26df627956034e854bac3d6a0326a0ddba3c9681876ba9357be77a1c141bf390c5ae34ea5551f0e2b41aba6e877ba9576d068f4 +8376bf330efaaff23606569ea58fdc16605ecdebde7f010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d65 +2f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d36 +3f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e +3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d985 +0528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000000000 +0000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000 +000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019020000 +7468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d0014000600080000002100b6f4679893070000c92000001600000000000000 +000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000 +000000000000000000009d0a00007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000980b00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax376\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; +\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid; +\lsdsemihidden1 \lsdlocked0 Placeholder Text;\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid; +\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List;\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1; +\lsdsemihidden1 \lsdlocked0 Revision;\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2; +\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3; +\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdpriority62 \lsdlocked0 Light Grid Accent 5; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; +\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; +\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; +\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; +\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; +\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; +\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; +\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; +\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; +\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; +\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hashtag;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Unresolved Mention;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Link;}}{\*\datastore 01050000 +02000000180000004d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e500000000000000000000000000bf +4070e7f9d401feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..ef735235 --- /dev/null +++ b/README.md @@ -0,0 +1,194 @@ +# Jami-qt + +`jami-qt` is the cross platform client for Jami. For now, it's mainly used for the Windows platform and is not tested on other platforms. + +![jami-logo](images/logo-jami-standard-coul.png) + + +For more information about the jami project, see the following: + +- Main website: https://jami.net/ +- Bug tracker: https://git.jami.net/ +- Repositories: https://gerrit-ring.savoirfairelinux.com + +## Building On Native Windows +--- + +Only 64-bit MSVC build can be compiled. + +> Note: command ```./make-ring.py --init``` is not required on the Windows build
+ +**Setup Before Building:** +- Download [Qt (Open Source)](https://www.qt.io/download-open-source?hsCtaTracking=9f6a2170-a938-42df-a8e2-a9f0b1d6cdce%7C6cb0de4f-9bb5-4778-ab02-bfb62735f3e5)
+ + | | Prebuild | Module | + |---|---|---| + | Components: | msvc2017_64 | Qt WebEngine | + +- Download [Visual Studio](https://visualstudio.microsoft.com/) (version >= 2015)
+- Install Qt Vs Tools under extensions, and configure msvc2017_64 path under Qt Options
+ + | | Qt Version | SDK | Toolset | + |---|---|---|---| + | Minimum requirement: | 5.9.4 | 10.0.16299.0 | V141 | + +- Install [Python3](https://www.python.org/downloads/) for Windows + +**Start Building** +- Using Command Prompt +```sh + git clone https://review.jami.net/ring-project + cd ring-project/ + git submodule update --init daemon lrc client-windows + git submodule update --recursive --remote daemon lrc client-windows +``` +- Using **Elevated Command Prompt** +```sh + python make-ring.py --dependencies +``` + +> Note: +> 1. This command will install **chocolatey** which may require you to restart the Command Prompt to be able to use it. +> 2. This command will install **msys2 (64 bit)** by using chocolatey command which may cause issues below:
+> a. Choco may require you to restart the Command Prompt after finishing installing msys2.
+> b. Only if you have already installed msys2 (64 bit) under the default installation folder, we will use the existing one. +> 3. This command will install **strawberry perl** by using chocolatey command which may fail if you have already installed it. +> 4. This command will install **cmake** by using chocolatey command which will not add cmake into PATH (environment variable).
+> +> The issue 1, 2(a), 3 can be solved by restarting the Command Prompt under Administrator right and re-run the command.
+> The issue 3 can be solved by uninstalling your current strawberry perl and re-run the command.
+> The issue 4 can be solved by adding the location of the cmake.exe into PATH.
+ +- Using a new **Non-Elevated Command Prompt** +```sh + python make-ring.py --install +``` +- Then you should be able to use the Visual Studio Solution file in client-windows folder **(Configuration = Release, Platform = x64)** + +> Note:
+> To control the toolset and the sdk version that are used by msbuild, you can use ```--toolset``` and ```--sdk``` options
+> To control which Qt version should be used (qmake, windeployqt), uou can use ```--qtver``` option
+> By default: ```toolset=v141```, ```sdk=10.0.16299.0```, ```qtver=5.9.4```
+> For example: +```sh + python make-ring.py --install --toolset v142 --sdk 10.0.18362.0 --qtver 5.12.0 +``` + +### Build Module individually +--- + +- Jami-qt also support building each module (daemon, lrc, jami-qt) seperately + +**Daemon** + +- Make sure that dependencies is built by make-ring.py +- On MSVC folder (ring-project\daemon\MSVC): +```sh + cmake -DCMAKE_CONFIGURATION_TYPES="ReleaseLib_win32" -DCMAKE_VS_PLATFORM_NAME="x64" -G "Visual Studio 16 2019" -A x64 -T '$(DefaultPlatformToolset)' .. + python winmake.py -b daemon +``` +- This will generate a ```.lib``` file in the path of ring-project\daemon\MSVC\x64\ReleaseLib_win32\bin + +> Note: each dependencies contrib for daemon can also be updated individually
+> For example: +```bash + python winmake.py -b opendht +``` + +**Lrc** + +- Make sure that daemon is built first + +```bash + cd lrc + python make-lrc.py -gb +``` + +**Jami-qt** + +- Make sure that daemon, lrc are built first + +```bash + cd client-windows + pandoc -f markdown -t html5 -o changelog.html changelog.md + python make-client.py -d + python make-client.py -b + powershell -ExecutionPolicy Unrestricted -File copy-runtime-files.ps1 +``` + +**Note** +- For all python scripts, both ```--toolset``` and ```--sdk``` options are available. +- For more available options, run scripts with ```-h``` option. +- ```--qtver``` option is available on ```make-lrc.py``` and ```make-client.py```. + +## Packaging On Native Windows +--- + +- To be able to generate a msi package, first download and install [Wixtoolset](https://wixtoolset.org/releases/). +- In Visual Studio, download WiX Toolset Visual Studio Extension. +- Build client-windows project first, then the JamiInstaller project, msi package should be stored in ring-project\client-windows\JamiInstaller\bin\Release + +## Linux +--- + +> For now, this process is experimental. + +- LibRing and LibRingClient +must be installed first. If you have not already done so, go to the +[\#How to Build LibRing (or +Daemon)](#How_to_Build_LibRing_(or_Daemon) "wikilink") and [\#How to +Build LibRingClient (or +LRC)](#How_to_Build_LibRingClient_(or_LRC) "wikilink") sections. +- Building the whole ring-project is recommended, however, lrc might need to be rebuilt with cmake option ```-DCMAKE_INSTALL_PREFIX=/usr``` + +#### Other Requirements + +- Qt 5.9.4 (qt open source) +- libqt5svg*, qtwebengine5-dev, qtmultimedia5-dev, qtdeclarative5-dev, pandoc + +#### Getting the Source Code + +```bash + git clone https://review.jami.net/ring-client-windows +``` + +#### Build Instructions + +**Windows Client dependencies** + +- For Debian based: +```bash + sudo apt install qtmultimedia5-dev libqt5svg5* qtwebengine5-dev qtdeclarative5-dev qtquickcontrols2-5-dev qml-module-qtquick* pandoc +``` +- For Fedora: +```bash + sudo dnf install qt5-qtsvg-devel qt5-qtwebengine-devel qt5-qtmultimedia-devel qt5-qtdeclarative-devel qt5-qtquickcontrols2-devel pandoc +``` + +**Build Windows Client** + +```bash + cd ring-client-windows + pandoc -f markdown -t html5 -o changelog.html changelog.md + mkdir build + cd build + qmake -qt=qt5 ../jami-qt.pro + make -j9 +``` +- Then, you are finally ready to launch jami-qt in your build directory. + +#### Debugging + +Compile the client with `BUILD=Debug` and compile LibRingClient with +`-DCMAKE_BUILD_TYPE=Debug` + +#### Known issues + +1. The build system is not straight forward +2. Video doesn't work +3. Can't maximize/minimize window +4. Crash if the daemon is not started and installed. + +## Mac OS +--- +TBD diff --git a/changelog.md b/changelog.md new file mode 100644 index 00000000..63ae7d9b --- /dev/null +++ b/changelog.md @@ -0,0 +1,203 @@ +# 2020-03-16 + +## Bug Fixes + ++ Fixed the bug that the system notification cannot be disabled ++ Fixed video device enumeration for higher framerates ++ More reliable high dpi adaption ++ Prevent crash when video device events happen with no account + +## New Features + ++ Add type indicator ++ Movable splitter in main view ++ Connectivity improvement for calls and messages + +# 2019-12-13 + +## Bug Fixes + ++ Fixed the bug that caused lag on call overlay ++ Fixed a typo on about dialog + +## New Features + ++ Add various useful shortcuts + +# 2019-11-20 + +## Bug Fixes + ++ Fixed bug that prevented pasting multiline text into chat + +## New Features + ++ Calls now automatically un-hold when the conversation is selected ++ SIP dialpad now has A,B,C,D dtmf options + +# 2019-11-19 + +## Bug Fixes + ++ Fixed notifications popping up for outgoing calls ++ Removing conference participant selects a remaining participant conversation + +# 2019-11-15 + +## Notes + +Change version name to 'Free as in Freedom' +Linux: ffmpeg now embedded in official package for auto bitrate + hardware acceleration + +## Features + ++ callview: rework conference ui ([#1052](https://git.jami.net/savoirfairelinux/ring-client-gnome/issues/1052)) ++ JAMS: support login to an account manager ++ wizard: re-work account creation ++ chatview: add video recorder ++ chatview: add audio recorder ++ conversation: automatically accepts < 20Mb files ++ video: auto adapt bitrate ++ Beta version ++ Change logs ++ Save draft text messages + +## Bugfixes + ++ Fix multiple crash with SIP accounts ++ Fix DTMF for SIP ++ chatview: show generated avatar into the chatview ([#947](https://git.jami.net/savoirfairelinux/ring-client-gnome/issues/947)) ++ Fix account migration ++ Fix alignment in settings view ++ call: handle PEER_BUSY state ++ lrc: fix clearAllHistory ++ avmodel: sort framerates ++ sip: fix temporary item removal ++ avmodel: getCurrentRenderedDevice support conferences ++ upnp: now async ++ some deadlocks are fixed ++ some segfault are fixed ++ sip_transport: avoid PJ_ENOTSUITABLE when ipv4<->ipv6 ++ sipcall: fix rotation ++ ffmpeg: handle ebusy when opening devices ++ file transfer: fix cancel ++ conference: fix color inversion during conferences ++ lan: improve pjsip behavior in a LAN ++ [trustrequests: handle removed contacts](https://git.jami.net/savoirfairelinux/ring-daemon/issues/129) ++ dbus: only authorize one daemon per DBUS_SESSION_BUS_ADDRESS ++ Fixed crash when user has no account ++ Fixed display name changes not saving ++ Fixed not being able to change back to camera from screen share ++ Fixed utf-8 handling on display names + +## Internal changes + ++ chatview code is now in LRC and shared with the desktop clients ++ database: migrate to per account database ++ avmodel: optionally switchInput using a callId ++ video sender: send only 1 keyframe at start ++ contrib: various bump (opendht, upnp, ffmpeg, etc) ++ p2p: use one IceTransport by sub transfer ++ decoder: set fpsprobesize, use default probesize ++ ice: enable aggressive nomination to avoid latencies ++ accel: remove libdrm code ++ sipvoiplink: remove wait for completed transactions in dtor ++ Remove some thread creations ++ replace restbed by restinio ++ namedirectory: don't create temporary items during lookup ++ file transfer: use different ice for each transfer ++ manager: allow switchInput on conference + + +# 2019-08-24 + +## Features + ++ [Erase data securely before removing account](https://git.jami.net/savoirfairelinux/ring-daemon/issues/60) ++ [Negotiate calls in TCP and UDP and prefer TCP if necessary](https://git.jami.net/savoirfairelinux/ring-daemon/issues/103) ++ Improve negotiation for p2p file transfer ++ Auto change the video quality ++ Add hardware acceleration support for NVidia ++ SIP fix SMS issues + +## Bugfixes + ++ Improve connectivityChange detection and account switching. ++ Translate strings from daemon. ++ Sort resolutions by width ++ [Sort conversations when clearing history](https://git.jami.net/savoirfairelinux/ring-lrc/issues/411) ++ Fix subscriptions for new contacts ++ Hangup if contact is deleted ++ [Various deadlocks](https://git.jami.net/savoirfairelinux/ring-daemon/issues/120) + +## Internal changes + ++ LRC remove unused code ++ Change from enableAccount() to setAccountEnabled() ++ Update msgpack, gnutls, opendht ++ Rewrite code for UPnP support + +# 2019-06-20 + +## Bugfixes + ++ Improve name registration errors detection ++ Improve SIP text/plain detection ++ Fix temporary item when copy/paste a full ring id ++ SIP: Fix online status ++ [Fix audio recorder](https://git.jami.net/savoirfairelinux/ring-daemon/issues/95) ++ Fix some deadlocks ++ [Fix calls via TURN](https://git.jami.net/savoirfairelinux/ring-daemon/issues/105) ++ [Fix multi devices support for calls](https://git.jami.net/savoirfairelinux/ring-daemon/issues/120) + + +## Internal changes + ++ Cleanup daemon side ++ Update restbed ++ Update opendht to 1.9.5 ++ [Improve UPnP implementation](https://git.jami.net/savoirfairelinux/ring-daemon/issues/96) ++ Increase default video bitrate + +# 2019-05-16 + +## Features + ++ [Add peer to peer file transfer support](https://git.jami.net/savoirfairelinux/ring-project/issues/486) ++ Advanced settings: add DHT peer discovery support ++ Media Settings: add hardware acceleration support ++ [UPnP add TCP mapping support](https://git.jami.net/savoirfairelinux/ring-daemon/issues/86) + +## Bugfixes + ++ Name registration: better handling for wrong archive password ++ tls_session: close transport after cleanup ++ sip: check message utf8 validity before emitting signal + +## Internal changes + ++ Bump OpenDHT to 1.9.4 ++ Bump GNUTls to 3.6.7 ++ Bump Pjsip to (6b9212dcb4b3f781c1e922ae544b063880bc46ac + patches) ++ Internal renaming from Ring to Jami ++ Use new methods from LRC ++ Fix some data races ++ dring/dbus: unregister signals on exit + +# 2019-04-12 + +## Features + ++ (Not linked to the UI for now) Hardware encoding support + +## Bugfixes + ++ Sets up video streams upon receiving the first video frame. ++ Pulseaudio: start streams when ready + +## Internal changes + ++ Continue name migration, change data locations, binary names and methods names. ++ Nettle 3.4.1 is now required ++ Support video rotation when recording ++ Some code clean \ No newline at end of file diff --git a/copy-runtime-files.ps1 b/copy-runtime-files.ps1 new file mode 100644 index 00000000..4db58531 --- /dev/null +++ b/copy-runtime-files.ps1 @@ -0,0 +1,99 @@ +[cmdletbinding()] +param ( + [string]$mode, + [string]$qtver, + [string]$daemonDir, + [string]$lrcDir +); + +write-host "Copying runtime files..." -ForegroundColor Green + +# default values +$qtver = If ($qtver) {$qtver} Else {"5.9.4"} +$mode = If ($mode) {$mode} Else {"Release"} + +$qtverSplit1, $qtverSplit2 ,$qtverSplit3 = $qtver.Split('.') +$qtMsvcDir = If((([int]$qtverSplit1) -ge 6) -OR ( (([int]$qtverSplit1) -eq 5) -AND (([int]$qtverSplit2) -ge 15))){"msvc2019_64"} Else{"msvc2017_64"} + +$QtDir = "C:\Qt\$qtver\$qtMsvcDir" + +$ClientDir = split-path -parent $MyInvocation.MyCommand.Definition + +$OutDir = $ClientDir + "\x64\" + $mode +If(!(test-path $OutDir)) { New-Item -ItemType directory -Path $OutDir -Force } + +if (!$daemonDir) { $daemonDir = $ClientDir + '\..\daemon' } +if (!$lrcDir) { $lrcDir = $ClientDir + '\..\lrc' } + +write-host "********************************************************************************" -ForegroundColor Magenta +write-host "using daemonDir: " $daemonDir -ForegroundColor Magenta +write-host "using lrcDir: " $lrcDir -ForegroundColor Magenta +write-host "using QtDir: " $QtDir -ForegroundColor Magenta +write-host "********************************************************************************" -ForegroundColor Magenta + +# dependency bin files and misc +$FilesToCopy = @( + "$daemonDir\contrib\build\ffmpeg\Build\win32\x64\bin\avcodec-58.dll", + "$daemonDir\contrib\build\ffmpeg\Build\win32\x64\bin\avutil-56.dll", + "$daemonDir\contrib\build\ffmpeg\Build\win32\x64\bin\avformat-58.dll", + "$daemonDir\contrib\build\ffmpeg\Build\win32\x64\bin\avdevice-58.dll", + "$daemonDir\contrib\build\ffmpeg\Build\win32\x64\bin\swresample-3.dll", + "$daemonDir\contrib\build\ffmpeg\Build\win32\x64\bin\swscale-5.dll", + "$daemonDir\contrib\build\ffmpeg\Build\win32\x64\bin\avfilter-7.dll", + "$daemonDir\contrib\build\openssl\out32dll\libeay32.dll", + "$daemonDir\contrib\build\openssl\out32dll\ssleay32.dll", + "$ClientDir\qt.conf", + "$ClientDir\images\jami.ico", + "$ClientDir\License.rtf" + ) +foreach ($i in $FilesToCopy) { + write-host "copying: " $i " => " $OutDir -ForegroundColor Cyan + Copy-Item -Path $i -Recurse -Destination $OutDir -Force -Container +} + +############ +# qt +############ +$windeployqt = "$QtDir\bin\windeployqt.exe --qmldir $ClientDir\src --release $OutDir\Jami.exe" +iex $windeployqt + +# ringtones +$CopyDir = $OutDir + "\ringtones" +If(!(test-path $CopyDir)) { New-Item -ItemType directory -Path $CopyDir -Force } +$RingtonePath = "$ClientDir\..\daemon\ringtones" +write-host "copying ringtones..." +Get-ChildItem -Path $RingtonePath -Include *.ul, *.ogg, *.wav, *.opus -Recurse | ForEach-Object { + write-host "copying ringtone: " $_.FullName " => " $CopyDir -ForegroundColor Cyan + Copy-Item -Path $_.FullName -Destination $CopyDir -Force –Recurse +} + +# qt translations +$lrelease = "$QtDir\bin\lrelease.exe" + +# lrc translations +$lrcTSPath = "$lrcDir\translations" +Get-ChildItem -Path $lrcTSPath -Include *.ts -Recurse | ForEach-Object { + & $lrelease $_.FullName +} +$CopyDir = $OutDir + "\share\libringclient\translations" +If(!(test-path $CopyDir)) { New-Item -ItemType directory -Path $CopyDir -Force } +write-host "copying lrc translations..." +Get-ChildItem -Path $lrcTSPath -Include *.qm -Recurse | ForEach-Object { + write-host "copying translation file: " $_.FullName " => " $CopyDir -ForegroundColor Cyan + Copy-Item -Path $_.FullName -Destination $CopyDir -Force –Recurse +} + +# client translations +$clientTSPath = "$ClientDir\translations" +Get-ChildItem -Path $clientTSPath -Include *.ts -Recurse | ForEach-Object { + & $lrelease $_.FullName +} +$CopyDir = $OutDir + "\share\ring\translations" +If(!(test-path $CopyDir)) { New-Item -ItemType directory -Path $CopyDir -Force } +write-host "copying client translations..." +Get-ChildItem -Path $clientTSPath -Include *.qm -Recurse | ForEach-Object { + write-host "copying translation file: " $_.FullName " => " $CopyDir -ForegroundColor Cyan + Copy-Item -Path $_.FullName -Destination $CopyDir -Force –Recurse +} + +write-host "copy completed" -NoNewline -ForegroundColor Green \ No newline at end of file diff --git a/ico.rc b/ico.rc new file mode 100644 index 00000000..ad48aa51 --- /dev/null +++ b/ico.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "images/jami.ico" diff --git a/images/FontAwesome.otf b/images/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..401ec0f36e4f73b8efa40bd6f604fe80d286db70 GIT binary patch literal 134808 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 literal 0 HcmV?d00001 diff --git a/images/ajax-loader.gif b/images/ajax-loader.gif new file mode 100644 index 0000000000000000000000000000000000000000..f2a1bc0c6f545e20e631a96e8e92f9822e75d046 GIT binary patch literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nnmm28Kh24mmkF0U1e2Nli^nlO|14{3qpHl$uzQnxasi zS(2fUn3Y(Olb@KPmzkHA&!G5|g@FsGT=74*pKD04vtxj(k)8oFBTz^Oh=E26FfcG1 zbL_hF&)}42ws10s6^G;;cE1^EoUR)U5A70}d2pLv!jVIT7j&Z~EblI3x0K*v_sV|m z0W=b9G$XP(CLnYCdK49;TX=SFc-G}o=oA=|U?{1O;Nu!CwW3C5Yw7*Bi4yD$3fCnb zwK+>}QdQ9sf*QnxY>*kpE+b{_Q;sJloS71)&(@kO!}mqf@1v(v;*8Y=G9S3kY~Cw# zY=t&c z;3~JK4HxB^lY(MD+sYeQ=t%XSSW;x^1M?dTvN=W^yNcAcy`HCte31C;)5xP%b~qs> zDP&4(%TBqBNGHwnryK;BdMI$fEg xd0mc!C@j^ZpLxYv4HmnPfI0THYuv<%+6iSmMn&w3dPGDfL1|=LY008wP(boU~ literal 0 HcmV?d00001 diff --git a/images/default_avatar_overlay.svg b/images/default_avatar_overlay.svg new file mode 100644 index 00000000..43cf56b5 --- /dev/null +++ b/images/default_avatar_overlay.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/images/icons/av_icons/delete-24px.svg b/images/icons/av_icons/delete-24px.svg new file mode 100644 index 00000000..47e4e302 --- /dev/null +++ b/images/icons/av_icons/delete-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/av_icons/fiber_manual_record-24px.svg b/images/icons/av_icons/fiber_manual_record-24px.svg new file mode 100644 index 00000000..7ba3488b --- /dev/null +++ b/images/icons/av_icons/fiber_manual_record-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/av_icons/mic-24px.svg b/images/icons/av_icons/mic-24px.svg new file mode 100644 index 00000000..033bdee7 --- /dev/null +++ b/images/icons/av_icons/mic-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/av_icons/pause-24px.svg b/images/icons/av_icons/pause-24px.svg new file mode 100644 index 00000000..a82cdb69 --- /dev/null +++ b/images/icons/av_icons/pause-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/av_icons/play_circle_outline-24px.svg b/images/icons/av_icons/play_circle_outline-24px.svg new file mode 100644 index 00000000..bfba42d9 --- /dev/null +++ b/images/icons/av_icons/play_circle_outline-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/av_icons/re-record-24px.svg b/images/icons/av_icons/re-record-24px.svg new file mode 100644 index 00000000..db13333a --- /dev/null +++ b/images/icons/av_icons/re-record-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/av_icons/send-24px.svg b/images/icons/av_icons/send-24px.svg new file mode 100644 index 00000000..a90777f6 --- /dev/null +++ b/images/icons/av_icons/send-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/av_icons/stop-24px-red.svg b/images/icons/av_icons/stop-24px-red.svg new file mode 100644 index 00000000..1ec9ca99 --- /dev/null +++ b/images/icons/av_icons/stop-24px-red.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/av_icons/stop-24px.svg b/images/icons/av_icons/stop-24px.svg new file mode 100644 index 00000000..7d18eed3 --- /dev/null +++ b/images/icons/av_icons/stop-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/baseline-camera_alt-24px.svg b/images/icons/baseline-camera_alt-24px.svg new file mode 100644 index 00000000..83479645 --- /dev/null +++ b/images/icons/baseline-camera_alt-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/baseline-close-24px.svg b/images/icons/baseline-close-24px.svg new file mode 100644 index 00000000..dea86781 --- /dev/null +++ b/images/icons/baseline-close-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/baseline-desktop_windows-24px.svg b/images/icons/baseline-desktop_windows-24px.svg new file mode 100644 index 00000000..f56c4759 --- /dev/null +++ b/images/icons/baseline-desktop_windows-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/baseline-done-24px.svg b/images/icons/baseline-done-24px.svg new file mode 100644 index 00000000..8790bcc1 --- /dev/null +++ b/images/icons/baseline-done-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/baseline-error_outline-24px.svg b/images/icons/baseline-error_outline-24px.svg new file mode 100644 index 00000000..39062fa7 --- /dev/null +++ b/images/icons/baseline-error_outline-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/baseline-people-24px.svg b/images/icons/baseline-people-24px.svg new file mode 100644 index 00000000..6bcb8591 --- /dev/null +++ b/images/icons/baseline-people-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/baseline-refresh-24px.svg b/images/icons/baseline-refresh-24px.svg new file mode 100644 index 00000000..f1ad30d2 --- /dev/null +++ b/images/icons/baseline-refresh-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/extension_24dp.svg b/images/icons/extension_24dp.svg new file mode 100644 index 00000000..057bf7f3 --- /dev/null +++ b/images/icons/extension_24dp.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/images/icons/ic_add_black_18dp_2x.png b/images/icons/ic_add_black_18dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..6f33be6bac35e3e9dae81da4931ee44b64ad180e GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQP7*hjf^q2lgl{ zC;vq{KryukTc2-r7j^Vs6!KY$dslP3vA}Cnq1;PZ8~*42k6)c=;@BtU;yIJobCHVr e{y>9G3=HW;N{PB%3tE6KVeoYIb6Mw<&;$T^*H6Fz literal 0 HcmV?d00001 diff --git a/images/icons/ic_arrow_back_24px.svg b/images/icons/ic_arrow_back_24px.svg new file mode 100644 index 00000000..3a735d8a --- /dev/null +++ b/images/icons/ic_arrow_back_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/ic_arrow_back_white_24dp.png b/images/icons/ic_arrow_back_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ce5b878b0fa37a263da66cfe403868fef2d4357c GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DI8PVHkch)?FCOG literal 0 HcmV?d00001 diff --git a/images/icons/ic_arrow_drop_down_black_18dp_2x.png b/images/icons/ic_arrow_drop_down_black_18dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ec71509f355e483ad3f2d2d49cc8af4cb52767e2 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQ0#<(P&RY) zWqrv!=hF?-n))AC9{yrh;ysirqL*AC)pA$k&anrE9G5j}jy*8txT^8zn1BICqKINL whZKupS7YM@Ll;J0R^Qf(HAY-+2RAS?-1?z0Ywp1;3!qI5p00i_>zopr0A6f7ivR!s literal 0 HcmV?d00001 diff --git a/images/icons/ic_arrow_drop_down_black_9dp_2x.png b/images/icons/ic_arrow_drop_down_black_9dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9ccea133ddb3b5a0293ccdbd374c8085040bc1c8 GIT binary patch literal 193 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mn3BBRT^Rni_n+Ah4nJ za0`PlBg3pY5H=O_6N+Yf@XS;4&>Pag@Qa?978nDCnrdpYna&R80`3qVYyA& z%lG{AtzzmAoH)=BE$PC^%&gqf?JMcS86&vNs6|M{b;YR(Qx|ABX*h>AE#p|!)5sXg jDYjtcf|Uj$kN6mR@-$|t#=3k3n$FgTe~DWM4f+|@Yw literal 0 HcmV?d00001 diff --git a/images/icons/ic_arrow_drop_up_black_18dp_2x.png b/images/icons/ic_arrow_drop_up_black_18dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..65b14e07e4d317a898df215c7a6e89ed06957d5d GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQG5XyV#` qQ6$za@XJigqvk!IL%lR>a~NLyQQz@{XBr343I4nJ za0`PlBg3pY5H=O_6N+Yf@W4fq;@#~g}gmo978nDCnrcSIRrZfGsgC?#&&*T zWMgA%f6YI^Rr2E0f>Xu@HT6G4TqTv439+%2r1spHvOqie$U2r#No76N6Q?8!wGWou c{=mX;aJ~ADRkFp8fW|U-y85}Sb4q9e0E)FWxc~qF literal 0 HcmV?d00001 diff --git a/images/icons/ic_arrow_forward_white_48dp_2x.png b/images/icons/ic_arrow_forward_white_48dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5e93f8862b879d31e51adaf46b3a0ebd2063c5a1 GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD0wg^q?%xcgHhH=@hE&{od-WkNqa#C0AnyXt zUlw25*12(Xp7<;H#OGZt@0&d-=DQ<2CaHK{@;^0UVdX!)>rtFHp9d;G%&b#ZY~ORu z<%C)CSAk7u1Q}7m!^6tUvNG6fx+hE#o>atipl5x+Pvv(%&TLxsuluZd{jtyf&t~WE z`}u6Y`Tv}KKR3_WZ+5@t^V#_f8&=M%SLV6;*_|Wzb38}y=lL9KKfiBz_7muWN&j|n Y%N<#k$X1qa1#~@wr>mdKI;Vst0GCi?UH||9 literal 0 HcmV?d00001 diff --git a/images/icons/ic_arrow_tab_next_black_9dp_2x.png b/images/icons/ic_arrow_tab_next_black_9dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..62e6238e5597d2256a9407ce145ca5ddff4cf9a0 GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|mn3BBRT^Rni_n+Ah4nJ za0`PlBg3pY5H=O_6N+Yf>uI_QnA~BLP?%3jv*T7lM@s~Gqe)K)197q$RAk8 z<0`n^rYzNGMa-L1GcK@h&`dsLA`>hrY}UR=JNeMN%#c){BQ5fLuJ`5bN_YHYSg#bK z8X>w+``{vuV9qwB9Ip!vr4nJ za0`PlBg3pY5H=O_6N+Yf>vq{>sB%Yg`z!O978nDCnqS#XJ{#Wp0PoDqMd`o z#AQui82u{ubQw%d&^WY=#aFOQRimpw(=n(enzK*gz{(fDnwRt4mlrbO+-@ZDxVXl@3Kgia@s(mML`!`R`;0TEpP!>gTe~DWM4fkytw0 literal 0 HcmV?d00001 diff --git a/images/icons/ic_baseline-search-24px.svg b/images/icons/ic_baseline-search-24px.svg new file mode 100644 index 00000000..19c9df70 --- /dev/null +++ b/images/icons/ic_baseline-search-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/ic_block_24px.svg b/images/icons/ic_block_24px.svg new file mode 100644 index 00000000..8032acfd --- /dev/null +++ b/images/icons/ic_block_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/ic_call_transfer_white_24px.png b/images/icons/ic_call_transfer_white_24px.png new file mode 100644 index 0000000000000000000000000000000000000000..a43dbb4be9d54a6be42b89d03488487b86ed2613 GIT binary patch literal 1324 zcmV+{1=IS8P)002t}1^@s6I8J)%00004b3#c}2nYxW zdw6wHTi~5K zd&VJ|Dl5nJ=DZ9wE(4y=P-i+DflwbU5|%n%k7NjRW`-J7G6uX+G$cnU>)Z@As%8v$ zeUPwJS$|{*vN%JHQLvE#9X86&x->(LQPH=uU7Q=Oa~0`7^3i7eIBdFpQ& zW*;8N5-2_QOtTKZsyC+8O75)g`Qbpxy^ z5OuH#9o0kq&{15>3j##9d-`!)y|T7&(`dv8r>-yPuJO9_H41f(y1zi!opony6zYZo zS^L#9>dx30y4|&_K-i0QXKWPeGWEm&Str$7>(1CH)U5^GbwIta?u?B=JzxE~K-j0& z^vY2qQ6DOhrTSdm85@QAT7j%n>ZZCgHU>Ly2o_D@oUo>s+8T*^x%zv7u%qhgx^p%L z^?nE6PV|I5nCV>e?L2xtrQWBWm80%RQ6Ddm)fo{M)Ss&Jq$bL$ zW!+swd@_MePAh_E^Efk;Q(rv z1rQOF-7Z@J?5aD@AQ~a7M?~xcZUHs``)iJK8jX?#5D}*$VjFNRu$IpATO(+ktR7uy z{qfhWn|ZO1a#YZmHKb+fu%ogDI}^s$t4Ryv($dn>2>u2U>m&eF1#S-j0000wjP~7f zM%N=^>jsTu{-rtzEdq0ncg?i^|K5SaK6KmT6TH7ay_<9M@#2DmP49knS|l`<9}0DD zY;^O`?wA3iBP*~4L(UtOThSE{#-;qG@Op!*m+UHx3vIVCg! E0A!#`|k0wldT1B8K8ji-xah{y4_S9bF@81T3TmS5N? z#C(9Q$LV3`iyt|zzNMK?OD+gTJ2maGvJ86jqI1fWJrY@L@@dRh+8HxyC3MfUOIWFC zSzj(@_&m4jkH)bAn}wUwn%lHbyQcPAEcKb)b;T<=reaCi=lz``&(3vDxn&&?D \ No newline at end of file diff --git a/images/icons/ic_close_black_24dp.png b/images/icons/ic_close_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..3f5c070fe1ba58f89943f0644e2246b2ac411979 GIT binary patch literal 1107 zcmV-Z1g!gsP)T+N!3SamL?wP9V-!E4iOw{?CBw42cl5SIl5R3# zTdu!(?9A-UT&c8LtF>CI_1IQmp#K^74tS_h5r%nj10TXOXc<%yq`nL9$xO$s7;wV{ zaE**4lNwQ75Pz-;rg@m@m{kjIMKewz;j82EI10n?N4Z>nL7$s~8uws$3lqXN2=j=r zzxVt7L}1qO%)&|p%1SJJ5Cqq>|4V=W5W`orZxgK%DW+KrmGJ5$S;!_~9Wy4duhTSb z12Y0LUN_a)Y>nDt(<1M7XoFOrKsWalw z#OxjI`$SzZO*EGZ4Kc))P1pb?OjZg;Y2n#gQ3dKa>LZI1?`c0IS|{p=(}maL1rpZhgI6}^$k9HhhQw!gcWi; zvy11(2H!7aeZ6KWYp$e}*mNlIiRjYuzjNB}!F8nYZQ(lAqSm)v|4NAt{B_G_WsC3^ zw0{cgPvC0nj?2qSX)Tqwxr`8?9Izv{sxGiW_%F17Mzjy>k3`TW%Mv~-sC%&D)Z{Wk z_$%5!Cpv%v6S$5kXhYw^XQkF50@kI#H`*UU0rvlGoSv7IStKi^NR?+fTM?(S?NyDntE8RFjIo7#$*5*;`7K+JIx&3SfQrAiqXfhBN%r? zRka^@X*O(t*PcR zJu#h#v}s#YRI%260%H|#J@H%to|wtO7OXS^ZNYbYS{A8k^G;dCs(#{DXGed3$8o&j zdnz%nJUg_A!7t2hR8S6uzuSgep-2Wd*$hf_RgcY|(&Il{`!(ZcUN_tR~ZuVLBb;oKDL* zX%WnYX8DpTnw5f_^UD^x3NAs3)wN}fr Z{sC&&J8cwe%}W3P002ovPDHLkV1hRb1}Ojl literal 0 HcmV?d00001 diff --git a/images/icons/ic_close_white_24dp.png b/images/icons/ic_close_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b7c7ffd0e795ba76ed3a062566c9016448795f7a GIT binary patch literal 257 zcmV+c0sj7pP) zOA5m<3jYBNi%A&<@ZO?$P9};P4Y5CjG5$M&YXI45J{s}~# zf|&?x1_gn4B7+hS@X!l}&!voFhmZP^sujifL@~PKMMM~{6xH}^g$q7WOzwCQ5vHTU z6`v~H@rlA8e;CUh_(b84zg=+ih`wG<)HiJjzSlQx5#CnjMR;A)R^jtaTa9;7rSy)7O%~`cm?ZjXImW?6TYRT<;U^@VKiSj`soFk00000NkvXX Hu0mjfhD&W| literal 0 HcmV?d00001 diff --git a/images/icons/ic_content_copy.svg b/images/icons/ic_content_copy.svg new file mode 100644 index 00000000..f23e8f28 --- /dev/null +++ b/images/icons/ic_content_copy.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/icons/ic_delete_black_18dp_2x.png b/images/icons/ic_delete_black_18dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2acd00139c108dc9456611150cfb233a7313249a GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQYG?^|F3tE{PLDbv3cKI^-~MJ zSJv-;Wc=*?^Y4`|VK=*s-W>F4y3zad`@iS2e*3%MwtB+AFzvkB`;SGhjDfCX@O1Ta JS?83{1ONuWVgmpG literal 0 HcmV?d00001 diff --git a/images/icons/ic_done_white_24dp.png b/images/icons/ic_done_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..3b2b65d26291575f2741d223cdf80facb436dc20 GIT binary patch literal 199 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 IHY(x&0Jyp_qyPW_ literal 0 HcmV?d00001 diff --git a/images/icons/ic_folder_black_18dp_2x.png b/images/icons/ic_folder_black_18dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fceeb6778dbbdcfaaf03c0b4f7d10350731fb59c GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8JTQkNJIc8w+!RmvNoxMYoW7ORpQv}jQgbmM4TV(vuM(0k55v04IK(c!6BL*)bU!4zM jBK_H>bc>t(DF%k@sWL$~=62i%x`x5i)z4*}Q$iB}pJ!1n literal 0 HcmV?d00001 diff --git a/images/icons/ic_full_screen_black.png b/images/icons/ic_full_screen_black.png new file mode 100644 index 0000000000000000000000000000000000000000..906ca1e46c4385c89faca3735ce121169568b7ab GIT binary patch literal 861 zcmeAS@N?(olHy`uVBq!ia0vp^CqS5k4M?tyST_$yu@pObhHwBu4M$1`kk47*5n0T@ zz%2~Ij105pNH8!k{r7Zn45^s&_O5TAbf`$f#oH5PQzm$Ro1hTEl*HteBigBH?ZG0L zqZl+rx8+^R6h@BPqR{fEH{TW?`&!KYe)H*1lWm!SW@7*wCv)k(zwb6^%H3<9S-H8Y zL;G`4+PpP#NzLKQWwxkJ=(lE%eE9Z^O}y=qsg>r&=fn6n9`wxqzk2cV25&V14yHy0 z>_mLh_ouJ*3;O?8?SC-u`n$7dY+?)U=lAdVadCHL{_{=S9EvNyy))WATl}53f&dGX zBP#Lm&c3I1+bbg;&%erCQFSw15wPd67}zoz|?^-;NcxJ?RTZ-)4%HP zx&9zc?jZMvWX>l?&-~e2#qsay(=&fIR(1T|_2bY(53t*#?VA7Wdi<;0`dp*#w>76H z=W;%oet22KU%uGsavK#mSehIL0bz5q;7z{ux_WbC`}Y?7ep}OH&z<>Ga>_B9>1W|_ zf$cE2R~~5s$Hs=I#wRR)XMbg|@w?|p!1;G_EWSPNdwu`hj7_xd5w>qh z2WvTThvA`VclX?vF*|zr_lBwl?Js|SOmtwt0v;IEFxkFQ+xV27MFAx3>FVdQ&MBb@ E05U!*i~s-t literal 0 HcmV?d00001 diff --git a/images/icons/ic_group_add_white_24dp.png b/images/icons/ic_group_add_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..a769931c4236a87d4b19f944f56af54faa0eb94e GIT binary patch literal 384 zcmV-`0e}99P);f2(pYK4M(orTNERTjAfR?S5tVdeX%k4g#9)Nu z6tDOUkIB3uAo|bk?){I$5EA@1JRT24YP1+8iK$W`keu=5c5VXlXNfj0&n(EY%qZR2 z1x4DVc_NhcTIq~}8XCK@ZYiB25L7`6jeA*FmCh&_qS2Fe*6n_&AV%X()-|Ow3aV)A z$hxg`M!__%q=}i5^+xGLkc}%_k~sT0b@ z3?BdtI3XM-DA6P@O-gcgfWR%KfUI0#;aslPo$fMQ(8WSmt_n`q%N7{X?!3S#m_WS1 e3%tM!a^weR3L$5R(Ms?D0000RlPraleFIG12X`e*krvkWSRP>jfPWZiY+xJu zW3edgS)m;%p=7!D8Yqu1TNWa93UdBsu5q9uPF5*d1P19DPiBvk5^nkVJ`q;-=0!G$ zQ;G8~$q$^9d(V!{i#UDd`XS3x{<91Tec5nvH9~$ z!4tEX!Kh3=kYVC(MiuPIGrA#+s`4+G1s=!pWjewnNpb%I6)(kSh~|=%Y5X@xb`V3e zK*=Z5LjQtHWAf#HwtT|}R*Ln@l0kl(tXL@XJ0?=q=S_VJjme)e^srwF=I{!nzN}Uy z#TnrKT9$lZ*XxO4I7?K>pr`RO2Y5j{<_Hr^9njz5+{SBXF8Z2ZB!WtSr)@v z3o>&RgWn3o)qR-G{bm?z9+;#eLz>Q|on(L0QyUdKYPeJm^{&2{&DaoV9zvf#dt$9d zEr)nC0nVY7L!Q1Fb54SgFBR@3=$4}wI{p~xEHi9z?FL(VtZ7MGv5emzBS{I8 zSc@E`_TyZU-9hcFvZ<;>tg&;VQU z*_!?~^5NExWPEFb!t>zq`sf7ciXo#z2I>6((Jgq1-v^)u5St4PPmM<4tMS~g`w&I1 zmpeGVzpKfijSS`-f+j<-~bV zoLR>rE*fC_K@%LTvq`jV=mjekg;G@$&4B`AR!B;CwA)D=3oolPiJQ%e`BPJ_rj^K= zUu8Y`duhy}Mj-1Q7zU4(_pC$XvAqv#iI+fjFzr94m<_w1Ac9)JN)rr ztlDUHb*&krHu9~rZeB}Ip}z`5Vn4-HY)jSG?9$D#?Y65Gxfk9^}1Pn0FY)k&T&?< zt4aO<4R2U}rA)Ht_v!pzM$xM>k`(LITQb#$pj3KhTDlK3P_6h$R*T5}JSH*4VJK>@ zJScDPOD$a=vQ&7Vg9+gRyQDphNPyLHy_L%s^0cjBDRsy37y5;bM9H;OTd#o%*dmH1 zZdO`sUc3!dBH0BNic%{AC{TGpj5{gtqi3K4sDe>T3gro z2PRo#TtR?saX0@v?KtJTvX9`szF~KW30QjoSU1OtmrZ-DVAeB(&9#bxFK3wKS|#<~ z^}VAXPkb!Oj-md@Dvr7+)|SYUV7aXqNeg%LQ__1+G@D%CjO=M5N06Ig+t)iLM?oW? zLFf`pS1A04krzA4bDR9!tu$QRi9@s+JB+4E6EQ#C(x;xI*ey9EtKnZ2)?H=`O^MD5 zlVyibWsA6hlOUe#kzqb35B*rMZj`5(z(w|m@)DtdA%DP*JBo`CIk}cZR+tGyeeLNN zs4i@;{vCP@8d^7|@q76I95OZ7;+`K@Z21c9NO7h(hJ`MMo!*4hY=Q)31*#0r`)?|3 z4rg@o(B_qDp?~z(ug}eEDmTSFEp)}=T~eqnv=>GBbF%!+*KXhx{Ke;jn(Xfo8{Oaj z_2-!3iIm^Q#lLJjJc*tz9(dj5h;efy`}gGH--_r{Zz?A-oIIP`sfaac%Iuu`afj?m z0!Q&nj{Au|D`6*({S-n?teBegj=wV~tJ7*Xe@03Nv?BLKLO?5R6!Vz(l7?)rBiS!g z*hg|bQ@(qecJPH{_pSOppB;tf(ge}XU7kYrKI2@?$-aF)K2nX)kFb!XA7!+aNTCQs zXv%Jw&G~|qT?Cw=B9?HQa6GhatNxY6S^2X{Ukhe+0QNxh=Zx?ySRodn_Uc#ICxva` zULd0|HEIjipz@$|GZ3!mw8YcCwee?FuzSfmJ{^27i=L??ullh(vi=3zKInqpll!jg z>M17)s@uUWJ9QGDKy3mA&3(rc?bcqmsG{kM}4&Vj-a-}d-spY_AS3p$UUzq4@W9+Jh<{ygOu z6u%s#zrw@Vt?n2w4is*V9Qm4asrgWc>(?%(P_^9zV!0KXU+_1E8!QaKVWj$0E$f*^ z;fEMumf5H|21txW&V(D^mtcgH78}mS`E&+rhjmlP(#4FU?Ug_8!6keS?3ud?t`spNwD& zzf}k6HJhs$?FCrD7z=H$l^u%GvJD(rawSD~I*6boPaGS|olHcuUJjTEX*Z2o_RQK6 z365A0BzpR%*{n5Zh|X?Ad$^RQjdz5}-1k&?a~ro;77^9d@yKZfijG)_qqVLL`fN<+ zGRl-2cU&gzLl0&RdL7Ga!GZI8nmJ0;#n^jmty`5Yd|`Fm+ZqI^x0iFK%lI&Hqic1d zZI)cOc$?X`?M6~;%TJHR@=lb@Ti^yggb~vLooX>#0R6!^Njc0KE}Wy`{4Sxe?FJXw zYWc#wRk2W}Y!f!CPb?Agv|HEU1ra8S3-s;jOL4?x_GwLBIqL0ii z(0r@X9&is@Y`bDF0t|$tQGbTM{?W5&kkLfL0TMC~LcRok5Zs2WC`wsO4&VNr=rPD< z$!-Z0vyZLSibqH{s6V-HTDkC|!^c*hW&*`|K;I9Zrkaj1d5d~u^slb;b+|lP9MtE~ zx!uUkjcI8Up^08~2}9=TmvFTTuINRF4=Q~(ZRT_YZdf42ESP1qaMr7<>%$-|JF)U1dd*#U24Uck52kr1J`KoR;2Lwj zAgXi&iL{HQR`j?eI-BXGd;HQQ|2-=Pv3E1)_g4&ul+L=3t~Cod+TFZzg$S3Z1x4AH zYqK1v;~AYLX)5;4*H)$HZB`>0V-pdFro$%b66-D#biGqLwd88ZAkE(W)!Kp~TY)Wd zSTERAI#+uxTQvAz%^?j}qkYGFw7)S>_#~2Ha)@N=B=!m=50`uRn19v%EnIa~2`s6ABI_3BwvgZ+t2s z1zyjSRJc$6UQOu&q;DilUqVTL1`U4XQVjIGGV;BNZ6B^)Wh7P{ z`H+3g_z$Xm>78Rzk?cg(nD8}b!~PfOSUf=yXXWOf<)e@r_w}O?qJ@K;5#o>NtSO@- zGI;@m9f3AZ%zkUc(;=tNmmH%x#@@D-2llUg?jD!i;h~8x>`WwxKAV2}Qdb~Kq(T30 zIX6vBx55wv!^{p}TAZ=?%nvL0)qN!QgXnOl>^hlJ5RN54e+PyD-KiqS*FnJjvi zxgPJBcWCDMG3d+{uL6cNEyn=owzqKJHb59RJy5Q%C+zrf*AI2J>xFul>|N5g1!rbT z475P?{q#hX+$67#CgT{3J=-=Dr)AT4Z+jbd-wWoBPc}hY$%SJ63IN_ewW5yNDTAo- zdeTnq+0m|cSX@?CcqaRZH**jY(#a!q0paM1$2R^v@p%Lr4H#1;W8?em&6ij*_jsFI zL>=ct&NQrDGk2)J7oP6!=vv#(POSkh)$aAEQ=L=i>IqpU6LHRw?=-Zw%Ui?f-dkFO zefmJX>5~}a+Yabi$zAT{6!GMuA3@K2&`7-&?(wLn9$nUvK%I9UMNe|+CSJcc0d%11 zPB0M+_cs4sLlW0{{?mbVZRH%zEc6*Ilg$0Hf)<~xO@o(pc|JO)Mq&T;F6|(rf61N2 z(=h1q=wkpSHzn=d!*hAlHI`TIrL>+9iNQ1+MxH;62E!H8Heb+SA%1=Rw3=#HkGE8@ zmstN+*+(sRO6F^g2;e2&PWCBp@|-VxcVRJ3cG1q$F=mgzzhp-XUk;$H=hW3t*Wg*f zguSkbNmg$MrF-@@2}w zd%gnKNrVnAvP;0M^?Utlcr?CU_13mZgCFXhQ8+vi8Tdsq;>QBb9jM?97mTiHi*FX!=hD%riQM(a@ZEcDH0_SM{Ptkh8&$L^cer;effax~e)`B~ zT-VHxKK{Q7<49=ykL);7AOE98{xKT=r8)jT`)?ugpX|ELAGSS2HO@2O;qs9M0w7H+ KjcW|uV*d|5)(MdS literal 0 HcmV?d00001 diff --git a/images/icons/ic_high_quality_white_24dp.png b/images/icons/ic_high_quality_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..6d2d87d21b5f22f29f5dc56f5317e05bf59b4bea GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0Dg`O^sArXh)PV?q!G2n41<|xfG zsPN%833z>wE%1B(wtrDheuls`W$v+ZKh5ySq8RJ&!Gz>TOoDY|88t)7L2k zti9ykopimUMKj? S=f4Fymci52&t;ucLK6T?_*xnO literal 0 HcmV?d00001 diff --git a/images/icons/ic_mic_off_white_24dp.png b/images/icons/ic_mic_off_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..bb7915f33df69c09fbc22cb39e913205a6f06004 GIT binary patch literal 484 zcmV5}42RexTey86(fe-f{4kyh~{+(!Bv2JQ==%JP-)<&$CY}%iXF4O^C zeZ85VwQV|>k=Fb`*B6Xuh!wdk&kyt=L#)eXFh9^U&Jt6(OkzNU7|13DM2L<-JrE|U zfjChQ#EE(!PSgW&q8^A7aiEfj1C>M^h!A@@A`Arjn;cOO`~-K?yi0~SmDcZrUvXd? zJWcaEazyGguoVYZ!OJv1ph&y|Z{olcFmd|te9T6HSnvZpjssT#kJ6L#M&t<{L$DYI z(&hPG`tP7+*}%la?hJ9pd#^R(fNS=^n&UHTi?%awaU}?(J_oGogoxBA(6$En>Uup< z(iaQ?ymd=At#sY?i3rgs^`sZ}XUE8f9VvMt1f<^dO+JkCA1Vl>o^a0x2Qzd>ThCUR zXheI_4FF5h`6v(xh&O*#UM>;!K#-^eGDLp6>wyB%KNKLH?$6F00+5Klv++Hs?-B5S a0e=92ZRRk`JirnF0000ZF|rCoNU|et+{_tYt4DdZprqN?VfzQw(otWkLR5ZLR0O~&LX$KEsL}Z z#bux?qFpgC6pt2;i+4hcP%LIhW@acBCna+_6pMF~c^isF3N;`2Uh2TNQU|`)eBeu| z1D|U?@Tt^+kJTIS7OafEPvUvx{i+-nKR3a~49oSA$ooy;X2_NUU~h&EXG+}n2Sc_j z1D|yHd*9Lt@s4U4*{{EYbs<}Z`3{ywo(ytHv`hatecJ(JL-yJiA z7~v~88FFC+;E>iK4gK5$j0?H31+Yt(kbx`7I;J! z19e;nVMZs7*%d%R_7YEDSM~=?+(Jy+-zq!jGcYhZdAc};RNQ)drIC}tfP>lb^51g5 z;~F=1^D6Bz2I{~K{%GH3+xqr$v)ab`{>SbO4h{+m0sgTe~ HDWM4fmnlwG literal 0 HcmV?d00001 diff --git a/images/icons/ic_pause_white_24dp.png b/images/icons/ic_pause_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..f49aed757118a941b567629ec217cde1aaf257e8 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZA`BpB)|k7xlYrjj7PUzopr0AjRhGgrtC09epr)}*o}WcTAW!WrpOjEo88M+Yf^&A5yc5k z$hU2jYG4^2*r95&=c7)Ex&ZntsOymfDWHiA zg$@)`eY^zFie0;oB?c^qK%8UTE4gLto&GPuldssShyXo3?#1bOU!tx^)93lHT2bFewlK`St_| bKmdLLcf8r0$|reu00000NkvXXu0mjflV6Dp literal 0 HcmV?d00001 diff --git a/images/icons/ic_phone_24px.svg b/images/icons/ic_phone_24px.svg new file mode 100644 index 00000000..9385a7bf --- /dev/null +++ b/images/icons/ic_phone_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/ic_photo_camera_white_24dp_2x.png b/images/icons/ic_photo_camera_white_24dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..be9fb226a53ce5ee4008cfafa0754f42284d51b3 GIT binary patch literal 446 zcmV;v0YUzWP)S3bFt5nS?tU*5U+ zuRv$a3fXL!qPq!nmZ-`S-6wEv!Im}!G@?dn#;*=rm4ZHT$3Hy5 zK#ky=d(UOmFsQV^o<2gJx=}eIwnA3W7xau83lN!8vECcV2)4w<^v@2~67MD11sAjh ztGUt}LA${P1=0J3E1eNGc`3L+Gf5t=WKEt5E{OV^X;qWQ4p$&rl*20-lc(iB^4yia zijQ0lF6anWaiv8;dol|xA!y6No&?ck78<-*{Fa&B9E#TSq3mYYuttQ*QZ*_=*n0M$ zV!JjJ9`z`_U5gAzIwsc`sys4xbSaS{W>lIoJ$6TM=qna%_I>f1qB5iXUVh&=cfY<* oSSx7G$evyZ)YnOH5*(G_7e~OF#v^w+0000007*qoM6N<$g3E)#tN;K2 literal 0 HcmV?d00001 diff --git a/images/icons/ic_play_white_24dp.png b/images/icons/ic_play_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..d7768701870ef857e807e4cbe5029736f4cdb709 GIT binary patch literal 196 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DZci7-kP61P(>97aBnTW^F(r#x z;xOMfgBD+%MD}C}hqWP{&!QElo@T1-|5JYbPvatu8z4g<{_Ngr|K?BOTYZ`A oR4(uP_R)V1pSG9nUi5=e-ru*qd#_!i7tmP@p00i_>zopr0I_{c5&!@I literal 0 HcmV?d00001 diff --git a/images/icons/ic_send_24px.svg b/images/icons/ic_send_24px.svg new file mode 100644 index 00000000..4b8cef68 --- /dev/null +++ b/images/icons/ic_send_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/ic_send_white_24dp.png b/images/icons/ic_send_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ef59e77678dbd3f5d866bca9058b6e90cb8d6098 GIT binary patch literal 344 zcmV-e0jK_nP)&W`22C;JryNwK41(MIA#GvFb#~s zwsao25E&6n03w(GL@)`A!D$_85` z%9!`-s3_@X0OI`y_5>OaJWhc7Ux1pxX-=Z&t1o;=Bq}NC$3*rSV}~5rah@Eqct#)^ z#sARKe>b^SMWD8;A|0im5z9_QQGnQ=9+)gN{ZC&+5rBvy01-u99ZM?OT$kUFh@ye? qUBLr0?$ZKzW0Q{J0Y5k327Cd{Pq+sr^Mm~W0000+t)}?IPi4`?n-BcG{I5ur- z(S>O`4876<#~6}k=`>Up4hWM~CUltm-)|Ri1Ni>m=Xw7B>2vXZK2lazR#sM4OxVB` zGWm@sMM^ z-;l({UGfbz*r=0l*o=*rbn~#1)BM5?7AY+r;d^YHV6D>PUbfT6IjR&Lx6#YE6o*+) zuBKoO-MHxHK}|s}>p4WgxIShmD&AzWe%_@-6DZ+pqS#BZCQ!mV^fOt5qM?`(rW@k` zYsq0Fe-hOnJV_2~IlvgxjZ&g0_?+}E6PL@R*DQCeAjPd$B{wAK#0SSo$OW75#b()K zHUs$LSLVnT+wsWmw*M;;$z=L|Dth}oRO&PU|Q78@DG)EG-;f#n348sTZV zVF~9kvs0GXg_$1ilRM_pLS`uz%MxXz$ZS8iD;g5KO+dIKazGp50^T5@IM7A7EpotC z!W~mwxJ0;WIiQwsy^0H?ge#H*l7zdexR6PK98gHOKye|H0y&_NaDn2&2;t_-0ZGDL zRb044xEeWN72z%_E_4yDUJlqsxMPX~IW&?Y+)+88op1pSBoqyIaEQ!Ol*$riq{ys= zxpK!+dNI=^OYFwX1(wJSoA?`3V^qijl?0d?Wut77&!^Zq%PbjU4n5fUgnXH!m=5f< zk}m_~a~ON=BxQz5hH%r$Y-PY44&!E!3Yp*}?#{7XA*iGWcbzi9S)vHoMVW4ZGIkRX z#g8(<%S4r;jjhy@q>w_AtYRDOq=;&>OpwD#d~}?I%ut7qs$`Bm_@-GlSja#4WRw!w z;XQoPAY06$A7At_L-u$UUpyrjB^DU#RVFRX2UE@R5j!x|C?D|xrk<0JScR#T@)5nxBvhE literal 0 HcmV?d00001 diff --git a/images/icons/ic_share_black_48dp_2x.png b/images/icons/ic_share_black_48dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..5a8544ce5d6c8bf471c8fbd54cf4f88254ea9ccb GIT binary patch literal 888 zcmV-;1Bd*HP)9LMqRypZ5w`JO>))98$f6)oE2rdCPO(4t&OEy|HVqeUTEVJbno>6L`orcEYB zO@<!#gj`DvJ3WY+U zOXmgB9rTlB8$sA6%WE_c81+o!A$fUBQ49J#&EJTj15zm0gJ7>4#oCY}dJ?5-IY5eN zpBhw}M2c)2g=+DTB0D3FI(>*kub|EX;?NxGY$6V=qfQQS=oocQ5r>XYXB~0qXVl3e z4tVzmNT;dsiN1Pi)h5PviagRT!#CK8B$bZVC8K2!qANxqb ze#NtY#_=pZYi128*dhH~rkNeY&<>B`tGoCTF*HdH08qt9fnCb+E*1Ewj3MJ(e#-+~ zR7EGBvc?{}{Kz!VQ|a$qwzFkC7SC}5s@=!e##izkZb7x%_y94q$UP_!vCL%;d&aNH zC-7Aruae<6&hXeGLoabIdX!b`T>2=%Bwm5@oF)bU+{qV~xs2nhR?GF%E-l>5TjY#$ z*@VwtMyz>kYo5!e5PQ1kGR{6?PWxQK$A~#ab7_#^3~}m^0WPCPFJegBTpG+H4$TMs z?(%xj?=Ew}e*kqhf_{YNg8mYBBTjun9ja7wia2$|>y)C%hlq0X|_d z%}}r7A-edCpE%%<>&)^3Q7%gIgYj>?Z%_*QmGGkRxNVXm0{T@jVtnnFXe1CCSu$?c zC>7{OgcO^`%dnRyK|kxfF7pWa614L+0CA`db%qd! z22p1macB&6(m{WK;h-O(4#c4p>Li?IGzvlf-`xdNN+Lzpgi=#T5q*SO<*Xp~_>M9Z zt7h5$>vKEGU1h>@c*+QbhsfaN4d(()1WJrH1{phVkQ7lup-?CkD#AaVcIHkIiEiWo O0000i!|vyC>^Cq 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 literal 0 HcmV?d00001 diff --git a/images/icons/ic_video_call_24px.svg b/images/icons/ic_video_call_24px.svg new file mode 100644 index 00000000..e062c7f3 --- /dev/null +++ b/images/icons/ic_video_call_24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/ic_videocam_off_white_24dp.png b/images/icons/ic_videocam_off_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..5d540589b4a4f9e22a8c4f1f292e12ec75ad443b GIT binary patch literal 296 zcmV+@0oVSCP)KoGGG(20u# z;3d`r9)bgK5t u`h zQBv~(_A|Uaeg>M$g-&EJ3;(E>F0WfsuqEq@i4qW8@}AzdO6rx>m9XRe^SrNqJ^A{6 z_JW14-f=$P%=XJsxKGL9nM=c&PKKGnj7I8ADSj*`=EzL26jUe%O0+bb;bi0#P;hAY zVbJ!!)MTJ4jtodymX4oS`!d zOAf&SiqtxhQ+-*Xl&VW5EWO3w{y2$=qAj-gWfVk9jKrN7h>A$w^dwv}aL>pdg)A(I ziFgF)i8NrIvKH!@*z+~{oVlk9e4tzCnfyswNZyEyhy$m}!8{L4YkLmt;Wg{+?#(9- zVoD8~g1myf7Cjkg*4sUfPsF0Q0j-974f#g&>_D^LZUcOxgu*>xKs$nVg1q5cTo|ue zZ?|T6pPHvK;?XX|nYjwhaE})6R{34-7dGM>di;md57urC-ORt^)=$AztVM}+-eQY? Zh$ldbpupmuRh<9;002ovPDHLkV1k69i@X2; literal 0 HcmV?d00001 diff --git a/images/icons/ic_voicemail_white_24dp_2x.png b/images/icons/ic_voicemail_white_24dp_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..59126d7066f7b20a94c169f329275153accfbe7d GIT binary patch literal 487 zcmV zJ4*vW6on^MHu0InUI})J{wu{0Z51sf8qgmi1S~}2>u*E?b|Ntkt<6KS2%km9OE%eA zM@?Zdr@DLQoICG3tCWL-gTp@q3+!-910SDfuE>%8){|kI8|wJ@)VU^4CR~{0lpdpK zbA)G1p5%n?n5RRL#He(c2TZtUDKveQr%E;`eIW`B7DLiUds@;3$)S-n*ky${aaP%* zDSRckP*$F*UMNn4I%6u3W}1?;9R^zLJja7m&=qNHY$0MIe`9{=u}E~KG2FwVeYIwWkp_xO{PPov%G4>rf@sfo-IX& zj{YcxUYc{cXmdkly=UcVlTu1il&f80W@IGi-b1wywEG;IDX5bYeJ2Z<)VEmCH!Le-j-_WLDcxFkHqfCwduM+pEQD%#j`+ah7 daB!G3J^?=jcHqPb?!8mXg4YO5SmJ5jCi ziqUoS7-gVG>I*ScNqtGQo>z;qkJ=I03B76affP~27$YGKgnT5ump`KzLrgTpSb9dE zjfm25=(}*8z`#H&;gsTtaw1B0Xy$xf3kwUwb?esIpFe-T*R^Zc zM&7=CJLA@^TiI8xT$xf_Ts(NuqD7wG-rjbmrlw8sd}D}Zbab@cvSrIUA3l6I`uE>| zpLFBKjd|CvU!Q;J(xo&6AT~QY+ao+Y+#b);sK#byW>&twzRhxTbKOs$KAniQr{27I zb3XJkOG`_~{rvOKeWp#D*51(2&{8;TEHc1AN~?F%CM_+^gvK8~emwNfojWrgJb1AA zufP5}^61f{qKb-&Jy)+@U4H1$p`r8U%?k(%3v;Yjubyq|)~)Sk&z|l6+i$-m!r{#O z_wVoI^A8_BJXTg#mVf#3<$3$|?Ms?Gd9qiF7A@?l6A=;NuypBCzh8g-H3`AV#XftX za}0YOEiW(6`{R#4GI07aIK7U3etx#%ekXq9T;!N$iw*Qj~_oi@Z!ab>yWZn zuU_59zqhf$DMWVt(W6IGCr+H`XJcdYNoZ(jr)}G|^@Aa6o<4nA^8ESp8!unJET`~2 zbV{JJcEg4ZalygCuGE<_Wr{C?G#)zZo;-PSl52}S%BTb9591`}=I7`48!=);M{&~1 zNf-(zs#G+O#DF=^v|;37z_q_sFKqAtkvenn;>A_S)2NOeJGzV-H7e-f!Gq(mY0;ZE zZ=OS5ODeBkzkdB3Q9Xt_O8x4quY#$A6O8=x&p%gUtrG0>K)uhiXU}fJx$Q+oMQJl; z%CublW$bkbFgu!YD-r#gD zqA+PpqQAd?r4IHffT87FQ^SD@oZ1OEkc*S<1>0IcY{V%v6wvz)<{J?zQK*yQz-}yZ zN4+pkl~)g)3I9m=F>Y z65gaq6QAM3hsWSV2%G!4CfDX3+zUDn5&g4(*EQ%1V-pe*0%2Qg;ecGsZyl(kIKU=F zoF`Nw0_#|?j0V#N4pkbDV$4oYPyfQl$EUl2fq{peot@W&2@_)B)NH`t4m@`Wby7jW z>oU+}*Up_gvr)it*4Ea3fYB~Uqu{Kpti%Hc4$P$!TpRm503rc-%g&!WcW&F7HEU9n zlanKGVmjE?M%00oJ_iU(AF;!-NfGA>l|&nyMn0mu7l!09@-!X>gyLBr=(s^#@&7>P zH&877?Af#bh9iX(L~$cJ^6aTorw;AXr3;JE6XK38+y(U$OdSA64%bF-_CaU&$&)A7 z{`ljM={TiWTU%QXG_wxU30O%z;oTIXcX(N2aYr>1n9&}>hz=6G?HxSS_6wfD*A3r`3ry5J+p+h*NPG{)4LC2Fi!-fs(gT63m)~s1y zKrab8@m!~U`}ThI>(_U|^KBt5RVk$DfRQK=OLj}V9EjP3N}>&m4>|9P%{`UK(|Ac- z&CSibxVX3k;uL$(=yvVebw@z_@Ld;(hgy%0AO*cUbm$P+wQJWN)N9qMl|R?1UAuN? zJj3WasvIyANTlfizpx-Z0C(xgCPkcw#3RpRSg_1_mGPY*&M>Akj1B-QeTI`845Udy z7yaDb-94CjuS8$yIP+gn3s0_zeLjUw64En~(bws80pMiJeJ6+m#9rmVJ14*zXh2p; zVn%L5dcY3LCIw{5Ayg7=Sg<^LrhwvvtE;Q`m@#9bR;^lZ8roVCmC}wx|gVfa2KI_-7A5~abSgCX5$dLue|H+FNFHRmbXiyl^NY}7o!?y43 z0O>7RC5aii4e0?pEYPSF?@I`kM4Qaa%mg~2RP#w?qi<`59<+KB77T zwgf}RYsioxkvOU8)Zv=)9_$Bbisw(9II;BGZ@*3I+qbWefKfYvLgs1*>WHbwn8OJ9 zJSj0*p~P(}g-r^JUm{czZBVcQW5t-LcoKlvIU`4o?C32oX(YhrT?aT7aTp;NA{|z@cy?Dd zC}KRJlBmIgW&Sc{{?_wiLqk0+Rr&!Tlf9)nE`)}5^3YvVvTfV89`9QN#^|yZpbhpw7%$GSXkMv^#C+yB z`8+8xS*65nxVSr;6h4n7R1$4iu*`Wh!z|$a7#a^lLTEf`1-TSC6;TOLIUYfo3kJLt zDYInLrcE=TGpt{~e!U$W9K4NG$g1tcbikCCPKdNaT3VVfC_^L%aL%xZ z&;U+SzP`S}C~A9OOECZzz8iy>9VDGxzi3TKR%bJHIa*t#p9beY`Q_rPHxv$Hw#O1C!YfG`HQHVL>x;#4sN$lR46Da+}?| zch6vlWs|}nREUnf@u#1Dn#F=;&MOYs*w}c6hK2@l&R~rJ9P$7fd(aySptHK5pkNy2 zf+$ey&KPLgy*C%)NQpw#i5oMHl)R|A%aM`;I1D?$F_7b6U|?VnBB>)K#^BwD#EjfV z(1QvJP~|d^X%`&JWWh3BI0!NFG#-UJ7XjLjwXbpyP>3?<5bg3X5067Vg{#P%RkaSl z_;$RjM8tD>W5!D-^-#|lmM1~f;V_~#)QbWA1>fVdn}kZD4KO{7h0C;IT&v6>+Hm>;zlmp*X*~9MtZ_4U4?W}%Xni(nqNjjSyXyKtr31ilFQm>qL`{>- zDHmJhG2}BG16h26%$4f9*kU6j&Z8EFDmsG9Rp{eFaQYOoVI|-=TCmX;H9266#^c2+ zZWwb^-_;_oJ`la#9KeY#K$`?Oki%i5nybZ9G`GbtCdtdoD}-o+v?^fEGi@{+C`U&w z#!QfllaA7$)0#EFdB6oIG8RlQPmsAPFJ3zF-UeglTHYRT0FR4{>&-C`5j)Ok%9pze zw;?^)vSrJ3fJAi5mMsHN^l>0_Ygn+%dG$Udb6~s$7&T|0kV7?6NOP!fz=;fz>VpVp z@QRza#4wuI-n^)~%RGmJTX9n9!1pLNE;l!~K)`M?$lNAQQXEDU6rv%}DJ8cdJ&-3d zAk#F~0SlIwXJU_X>fE_=rwFI|uiV_+gsyJIUq<-j0`KQhNZWZ!%;AD} zm5kcjwQDDEzQttKl~*5)8Z~kUQiOAck>CGSYOcb zXhJ2?hHEqDnKn!<8jl8=6crT}1ekV~U{tvqHI^@TKa^(zG!6^?G6K}-&--~^ai2MJ zW;rifVdOYo_wmL|TsraI#tCTT&HI3lT8XX<&gW3q?7|2Nq5ZjAwG?|dRow>}3Hq=3+puy&XCl0{5Z%v4IjPB5I1$Hsb$H#rM{FbY!s-hBuNfbLHQQ>(;8_7kUHna0B5;L$Q!q4Pq`fwbT#` z@m(X;^XAg$^7p1v)QzObD}P_1>A;5?P8dml5@4eGQ-X#XVyfZn$I37CLbP<`@8myC hFp~bp;_V-Y{0GJ~wa?>-<+1<(002ovPDHLkV1gCP_uv2k literal 0 HcmV?d00001 diff --git a/images/icons/icon-keypad-24.png b/images/icons/icon-keypad-24.png new file mode 100644 index 0000000000000000000000000000000000000000..2abc9be80cb8393a2357c49383663951b5a4b88f GIT binary patch literal 879 zcmV-#1CacQP)RWcJnBlKMY8pjz0QInvV-0Ssf)oRsOuh+jF z9v*&JSy^$A-3;vS?>{*@I{E_a?d?q~`||R#lYCcewOYJfE-%f@%$S+X#KpF%@1^8# zPsBsNKat(Gv9U2lKP$?9e0=;T91c&>yX)k$C1Z-kqL1u@f}}}+qZm=SbZ#^nC9>NO z4i4r|bPt_kp5Bd+Pg&aOgx|?-5hTs(p_d7==$unzAB#q#kH9CnH#avQ(7SOu!H$e6 z6beC{nkDR41kg%@=8y$dA^H0H`aQDWAUNn0uSn+{AxqW2B%2dhTU)zNG24itXKQhB z(M|Rt!ZpIce*}Ciu-j2NCr6@*3&6?#ql96CmGiX-pqCjJ3e6!4D(B!yx7+PXr_*z( zRB9G~!=^wNKAp*AUIYSxyIfd%0<@y&mIh=2fyd)<)BXHXI@8}jTrL-+xVgK#`$pXl zbyp=Gk3VAtY^Mm&D!7J(B>J9En73V~DuVRR>KXVKSMVL***DLMRk^OqcxJ@Au!q zY;+{x7bEQ0B9IB&V3PeD9SN|wuv;)Rm{1InB0#s`LIKF4k_Usqha}lg#FR><&kG9+ zx6UM>3tH7JyKNAl&-JSUvVa)LWlTPw|HyN2ia^84ZL{p0&{y9@uz_5^?5LuOelIB z;8w(Cjze?Eg35Eb+ \ No newline at end of file diff --git a/images/icons/outline-info-24px.svg b/images/icons/outline-info-24px.svg new file mode 100644 index 00000000..ab9cff72 --- /dev/null +++ b/images/icons/outline-info-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-add-24px.svg b/images/icons/round-add-24px.svg new file mode 100644 index 00000000..a2fcb8eb --- /dev/null +++ b/images/icons/round-add-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-add_a_photo-24px.svg b/images/icons/round-add_a_photo-24px.svg new file mode 100644 index 00000000..522c3351 --- /dev/null +++ b/images/icons/round-add_a_photo-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-arrow_drop_down-24px.svg b/images/icons/round-arrow_drop_down-24px.svg new file mode 100644 index 00000000..49addb23 --- /dev/null +++ b/images/icons/round-arrow_drop_down-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-arrow_drop_up-24px.svg b/images/icons/round-arrow_drop_up-24px.svg new file mode 100644 index 00000000..f5477aec --- /dev/null +++ b/images/icons/round-arrow_drop_up-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-arrow_right-24px.svg b/images/icons/round-arrow_right-24px.svg new file mode 100644 index 00000000..81124fad --- /dev/null +++ b/images/icons/round-arrow_right-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-check_circle-24px.svg b/images/icons/round-check_circle-24px.svg new file mode 100644 index 00000000..7466a897 --- /dev/null +++ b/images/icons/round-check_circle-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-close-24px.svg b/images/icons/round-close-24px.svg new file mode 100644 index 00000000..e7261bc0 --- /dev/null +++ b/images/icons/round-close-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-edit-24px.svg b/images/icons/round-edit-24px.svg new file mode 100644 index 00000000..8d6bc172 --- /dev/null +++ b/images/icons/round-edit-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-error-24px.svg b/images/icons/round-error-24px.svg new file mode 100644 index 00000000..3daa4785 --- /dev/null +++ b/images/icons/round-error-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-folder-24px.svg b/images/icons/round-folder-24px.svg new file mode 100644 index 00000000..4f02e223 --- /dev/null +++ b/images/icons/round-folder-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-remove_circle-24px.svg b/images/icons/round-remove_circle-24px.svg new file mode 100644 index 00000000..9413863e --- /dev/null +++ b/images/icons/round-remove_circle-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-save_alt-24px.svg b/images/icons/round-save_alt-24px.svg new file mode 100644 index 00000000..07b2cb52 --- /dev/null +++ b/images/icons/round-save_alt-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-settings-24px.svg b/images/icons/round-settings-24px.svg new file mode 100644 index 00000000..fd34fc46 --- /dev/null +++ b/images/icons/round-settings-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/round-undo-24px.svg b/images/icons/round-undo-24px.svg new file mode 100644 index 00000000..c0e92414 --- /dev/null +++ b/images/icons/round-undo-24px.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/icons/settings_backup_restore-black-18dp.svg b/images/icons/settings_backup_restore-black-18dp.svg new file mode 100644 index 00000000..f61d2745 --- /dev/null +++ b/images/icons/settings_backup_restore-black-18dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/jami.ico b/images/jami.ico new file mode 100644 index 0000000000000000000000000000000000000000..468e452e44e4a712930decc791450e1ac0b0236e GIT binary patch literal 165662 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 XVHWpGW|Vg`00000NkvXXu0mjfD&eco literal 0 HcmV?d00001 diff --git a/images/jami_eclipse_spinner.gif b/images/jami_eclipse_spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..7c66d3ff5ca07baf9a84785735451c585f6123ab GIT binary patch literal 63452 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

X6b468>NLIj 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 literal 0 HcmV?d00001 diff --git a/images/jami_rolling_spinner.gif b/images/jami_rolling_spinner.gif new file mode 100644 index 0000000000000000000000000000000000000000..63ff54ade1e44ba428974b7499eff56c38f1db58 GIT binary patch literal 87724 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

A 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�$&zf z;yxF7)x6Ru6V6qW9c}4eaheQ8R_Y~vFt2nala5qyCDtJ;EyzC0l;qSX^9qFE4RCYR znna-(VU0EKM$qjsgj;K^{RI)Z%^QBgJ}<7-r^?KjnN_6Z>_Jti#rL%pUCQ&l(}&_; z1WQCAT?(O5sSCNn24&8VDaO&0l|?$`iW!_iG7IDVp1TMx0zps4uR$WJJsQ;thu0jYM)9e8a;XY?+#Cz;Em9TUQWbWs z<#Aes7xLdm2X9FSloc7fi9Bji@s_SCcs81sV$|59aKPO2S|6l$&x=t7g;H7fkM>GX zn{WkmgOJb=tnLp&Esxlt$F6P!Vm1Mu;TVB)qY5^tCBn~m(x7g5)`mTAHQ?UOLRY(W zT8-qj;Qq~yV>f7nCVP0{8D12r?z|zR3M9O~DWCs#tgvlIt{c4SXyv~80*Sa~9{4)| zh%JQ(^tHqh*m^in5VW~pzlhhxe2C4O7IOS|RbBif-YQQA*+z|b8t#4fdY#QK)q+6y zOc*GyPLG%hNKx!EZ)xQc{-!;`e#sQWALz@`=v3u$(*~pY%jWe99jz`%$6*6YOX8tD zb^Ld>NMhD&2vw+ZfC?d2re=K%vX-6!WVN=EZ9ZmHB-i4?s?{TvlgCNW2|S?z7P4Sp zB}9l=^4}41?(t+x%UXO9W{Nd`6u1Xd3=*uiVFB9XvB0;c6hEy13v`fk^2ZNw2IXJfMI}L<>EcuIv!w-n}%>^W+U$ws~yu-eL-2zqZ9;%`XIL6M5dGr7cGb zH}5nxLDsYN{nk9n@AA`LUeiHTf0(zj3I$V_hkx z@ly0Hz~xFRt!$R>a&oN+6x)2Tdr9M0aQU*3Ek*Cv>aA?G>`SG zPrqpT_c?qFyYk0?J_inGB{LE@zdB(;wPUqdkmx=vwJM^Tg;r15`zG1l%u?vp<5sT} zxtc}_>;&xPMexg@0ymF6{gP8>f?Fn1Drm8`>eM$bY^)C?4>odIJRVVPfAdTM>s@*% zfZhB|_c`IrFm+0-O8$fUq+bdZ`6nyZg8ixV&e0+_a5^IY5_R#quGt74IeJ@gpQ5<> z1#xW?|6~3hU=BV}Lr|oOo#vPzUW`ZnS9&tbQ-2?6*UeV~j;)&UN40=W{AfC#jUD20 z) zU+)~W{Vk&G%iNOP^&1cF{uX`n%j4DTPd9fKG=KRAv@G4SWw^H6t^8d-hg6*zLY66E zf0(u3#p{l2-DZF?Rx|0!b;p|h!INjeP6+tE{I`t)ZDmabF(qELTWtvfpPziTl4_kwjtx&D_Q{tqSSzwKAkwe0Et z8!dbI&b5}=>tWAEDy*+xdVDkX{p-iGt&k&iN%rwwXDr7Ykm_vyERLV7SVYd|;fUbM zwZ*P%wfn7+llQYmD)(fjmx-_Q9kg_Q$8tBW*Z+E*`zL$LNYIK3-2IZUTp#iGm!0eYQbP zLpTt`&$22tV6p>)mmm&Qr702zXiB>MMb+uj4j;TT)`y+QoSLCxC}-4jf4sa-%S#1+ z*ow0AS#y_{<~B#+!?;|m{IU<7w^wV$;|Osdbab%eGFRJmGFLGO9z5(+?HVwI$o#pRFjRog3$)P=}|Pbh7{J1|6f7ru#C#fq(Nc@-g{uFhf+ zO_&M(5dJD>!H_k32&|pHX9Bx!*T|{>Z_`JJcsP&yLE$Z02tKb+XDi;LyHqD+LS8Qj zONVU9=UsF6+?Yys$4ll39BukCb-{g0&_NC6)0pgL;NxzF{%Xe+;O3JVz=^i^8K$|l zG~_5=m(9}m74p%}t!tM_CqAR3W&zQKu-Zr9n~smXMEKlaE`UYdJ-w%)?)=lagN~~B z>0!xJq|5n2=Tj>3O=8z+l!0@KeH87;+K~jiK!S^iW1KxVD#3>fggbn2^8+%=NQH<~ zP;y^JP9tUlaLJP{SXkTTTN{jlu|BHr(95bk z$#o z^PybV_%h{SuspUZ|2C&cu`MQD9$c0Gh(%;iCKpMztty}6V*$}OJxvyDQP$K~z?+qh zgQY*oap@~KnB#ACTqvFYijPTir?RyHluKckL7gB~P)%RLk&|i;k@sCraKk+1wxwOv zULI8ijY2A|*0^APDgY#fTh>fY?QlVD=!*$}v(7?~cGqioSf`Lw6|GObWEt)koDZ#q zXd7~ADN=9TS1FqS9%z9m5^vn+;O~I$v2XWSu)?U!!r2m3`uSP&{j3Y4m7vA%l}{QF z&Z9darJDf&?JXF$RJEd#rkl#im^BmjG<<5ua3k<)P`cC$66-#;I-V=NP@91lN@H;E znEhOqTaTdZ{I_dQ3AeDy$LoN^ouImdB(7a*R>TKOaNgy8%bLD0u9N3Fbj-p4c2Gid z83zlns3E;LUV;VU)g!8l?HArS>EJ!ymu*o;7{xnq=0oh*zKWY;Y6?dLm~w(#+bTeYVQf+8(v)Q_Q_J8O+aW6 z076>YN|u_;0V-~Iamb3j4dHl{U-0_lb;ZxJ_7<$J^Ae&5jHpWob>EddGrGQCe@qdp z?mhJir*EJ$pNW+jfAA*my3Ve!LabnT|L;bwz#WbGqn2(oJtoW25yE}b8`Fo8yw5U~tGQ1eJzYSr@iRL740=5SAayYG zdxxJHE!bv~R?y8?CP|9f;b*1`m_nAZx0%M+erICrzhg;r81wIRCMm;z-ANa04{y>H zEi+)Sy#P=BzP;Jp#~j|=wqqHo*zH!;pFAnLBTFys(;NHrXuzej7`=lP+L%^_fQO3S$SJxn{Ke@7fX* zPP!d4UfI1;HTKY-#-8K|1!S% zw+YGrl^;Y`tN#SRo)L`wOUB?|oLAfj3(L3D#_DVy;irVB3;9{ufrT|~Gz39oyEmXyKyfqt{KYU5)r`Vnh zx)P-2bLC=oQ$CPI^~c{;#sWh3q@GHcbQ<9i&A0$^%L0JiNV@J|d=#vibvpHIg0`c4 zYzs(=|4jS~kYGi+SGrBjOz|wVDITa0fa$YH&1pYLP0il-a=Oa8_UnMpF~rh4VR6vQ z`d({qkC_r4a_m)wn23%^D-S%PkR9O}uJWWvy=vSKzneTURr34eHi){;T!|~sfe6J5 z38u}^%1Q*KOZYt7=PHL2F{!PUE@=p~g;fHGZn~aDisQ4D-o{*j9Lgn>Wx178tctc= zX(m=KIggfz>WG%`SDYlDy1=ri;y(8>oR8ui{+L}@2ZUtDFi`~uc34)16T)BRAF>QT zCJvucsuwt%fPYaiPcQFRB**$5D+^2Qvn(@9ysAAiO(8GG|D8%*K>EKMp*^BMtsQyU8;_D?l$?kAio5PC z$PAT97GH5cd}J7eiXt1T;ly}KULw79Hi@_-ZOj7F!Gli4spmEXZlU;fx?*sU-OIlt zd$|o$aSUG9B0AkzS3I(>WQ9b8V-BhxI~D zia<6QRP2;lsfp+twbWLyx@*&l3zyC3bKu`?USASncgEj+IF0*D5J-I@z#3~nG8`YcRP*IhQeJsS4td2efRm8}9BW}=dlrQ>CgQ1;J5?u;VZISaX`|1iowzk@$Dl;+wieyTqgT2|A42gYH>;&!7LqTp*%QsR6v4l2txrnfXlT<$uXHrw?!JX#h+EtUU}|56 zw3q(D4_$p6?|G@94KJu(&ie|jE^`ZZ7GeTF@R;O>8Edl0IDc5x+5PhUdlcgOiwuZW zU_K&m3=uRHOabgpNI$A4kp$frs&p(Vz~Y5|zSBd>g@a3U4QmQG_tKZcqc$B`A(13R zHZ9g+?T7A?m?&$WY}6};*bLVEGS>#PBXy!UqogR+S(^AnQ zihi}PO&_8O`jct=#;Y@ZnU&xJZS-KQt5CB1z8;?7C3bmeIG4Q`J2X?yLgrCes-_z& z=ty~pr_^U57S&H(dhIm(*Bp;u8~cN>d>QKKz{kI3406`PM^xhGAFs(=;22cTP+AOp z^3iwzm|?-KIKAZgsh$Oo#sZ|?%pL}lB2)@1MNhY!A(He`gdRz@MjTFhTAxJh<~|p~ z7x5bEP`R5H^wu=4ZX!b&|FF?AsnFS{2iTqX(9w?i+IcNE|I1USj1gWcUZ@5 z12d4jr!9nAv1D(b#^}Miwf2Cuu}=B>EPSe$?#fa}pGkMJ){SqP`zY)K91vCpyUKYb zxQC^1h=N3-+}H}w^AO>q^7nXQL(2EUUe2r~@k(ZQm>Kn?Dvz05(h4(^7;0W7M^^JW zBsEmxsHEMQEt3-O`lN2Nq{uZmn<%gL2vNN_9eO3;b#i?YH%x#^a^4`L9M%B`!DK0x z9*}hsO_&yz_UpILj~?E;cJ2Dzx1p!L%>L}W>c#A2Wd8{U>Yw`6{!iOg^Jm+Y$)?(F zb^WkiGwB&wrrJJvRjSBoo3;dlU_1EOU-v9s% z!TLW+Sf--=S0TIo9Y+53|K}H7zy1>p)W7T3kKIQtFFr^Mjst+SmM+dDx6|gt_PUJB zTpzUxC-fACaJz>6k{5uze!RHb{mbf-btk9S#p23Xxlyq(@5Yha&0aQ}!$%v^6X_Y* z>d6=Np-#avZ(m$-aJENYyyPtrP z^;37pvyJ28BQdb1g|D&YO9t%kTDJCbMVBQwY=y<#UixBVrarKD5O3^PE%v(Nrr7io zMa9?eDy}#F8j+v zNowP`y3Ww1nOtd7P||SqbVcO$KjkOcn!$V>ZuQ| z7=DRmi-eI@Z2)H`A2v@xTFjlO@Ny3kc(^Po2ZZs^ZS0SUR0gw!4rfs=X6aC?=afC? z=gavOfD>@>-fyfa)w?BU{6vheH+r4Y38Mgy_tY=5{# zi9n%ND=JQ`q~E(Z46B^xo!F6*OJj9u%i=oKp_Pf=7fm{=n7FQ!8NBaWLl$g)3Ol6N zv>NW&y3&lVkZuW9fSxy8c-AIW75FTw=y0HduuMAeDCjp&mys?li3Mz*0{8f%9<7Nn ztcr1Vu!M+SwBR*l{6+(6D=2oOlM{8|Vk}!o@9IU^ZAn(%XMHU|@0vM@{qNR89eRg8 zAkhyu5Fr^C{b!IC50?f)_cSd%Mh0JOXbkvBLjzFs(M#W;vWN7BR%_M}AwsYH+BmNk zzwum%YC9EMpn9Wn&PTP7nmZwUuTm;rHJiSoq-u3Q>VrZxH8*QOz;%T@a$MIq?VbSY zBY7-@qLhg`t#>9%Ka5hHp>chhoV7RXxj|;aL%wmRb@ptr9;8h5-SZ-YU<5T--=#Ho zM$b%MOvPIt(>mS`8T{q31vfJ14tg00?U<(EwY6~UD8Pb(VaH28V{SRrw0`xa;(BYn z8)e8~uNt;eKdmjTI0A|vq#&?r6|C$87Tibdg(eR08V3?l)cKnGGnw8p%gJYvNe#Wh zc!2R*FKXgwjn2LSSG~0-C>`aV!BHDap>?uoJeF2RPokE%r0;l@)(+-)5b08oAm2d*X~kLD!%r$?HpdFXK9j>0+$(dPVheV=VR#WU zfOqjEHlTBg;QbCPSGF7EqIe_c#k>*q-(J=5oO{k+TWuxWO-iGPHDF{KlATsqS`eFp ziV9on$?>bH%jvSMoU_+u7I>(&Xjr!wKxqA%rizyTLC=$Ox1XDExyrH2OvmNe&J%cD z?1|l_*2UwjbrEyYw%lG`>@q7<4o&-zSpD}?mAudV-emnw$h60u>#TNMyZd{1yqs&5 z53&3+->P4v$>S<}wpcbhEpM4piW%IghpW&uEEf-=nz`LpS6+4yX?x6bCkA*rHbW8r zYT@O8SCs7xNziKav6R2A>ZK&jGp>gKp;mSJyE#IHOsont)b|W}axXZqt$N8F8olXR zWwDgGQX{MUKpnTCozME5RUM?FX>o2X*>mjPppTh0F8d&O#b3ghMSFCRU^>Yb^FD=E z-KD9}N<3C=o{pG&f0v|9O#LM}Oe?(pF5XnA%7G;Eb#6||$D3A<=a9dhLo(Hi2BKGK z=Z+@i?Dr>t2ChzkNIAg$9wiU>OZsI;?NeaQI>xfCtdM4}RO2DG8Sxo_^bHoE+{!$M zZW1ex{rnx4N;+|M!xT(mNl~M))<4X?-u=q<_qf@=XJi|9CPt_Jfjj|S;eL-4&{G9} z1`AR&x93Lxwl%u57sW)c?qIMOZteEm=#F8#Gdjw&ZMOplMF&IZnwLIc&q&KM7NlbS zoUUg?W$ChadwrB{*fObDJDFJ~z+hYXw$sB0bnZWWuAaUiMel-buhcWF+MF6j_<*i_ z=~nGdY<6cpictsK0btSVU^l*_|C#xN&TR>NlMYw?K`)?-U#3<2eUP4>oMkMIZtu`f z(jy3r1*v-v{xU_+TmoS3&@&h4>9#GMinWcz`Y$%6UM&Cq^9cQq6vAI70e)ZmNPqv( z$lKC*odH+fk;XePEXIeAoAi+Y=1#y4KmkyTOvTrdOG*P6`?aTxkO^~Y+LZyh_6A2 zK!M2d_Z*!%*Dss891UKQ_UOCpZR_s9>uEz(?z>+iAE`j_7g{&Y$?u^IqHj55nKaD*t|#TAL=hH*WS(9 zP(F>qpKrbsn}`_*2>NDlVa4F;R^;_W&iK1tzYHGu1D{ymqVT%qK!EIh$LWnzfAxv(;rC$IMPfwU}J69vu15lx34DPw77$&W2rt1#vB$f_tK4|JaDzF>oM8ZxQ1 zcLZVCqUdy4;a-c3X9YdgsKZo)bI{!p05peQ^egjh3BsD$S?X1)g&1Lj%?G&(HpB^d z6<>XhJYs+m=l5{aQ$(e#`qz0{JZ!5&UFci&^|a#Ed5LPQz_F_ zW3>!BmkGj+OPg4+(0g9wO4)G8Y>{;(QMv30QEQ;)M54P{IVHO9LV-FwuMrjGX4nfq zAMImv-eR#5(Gy+x}{n^kyRnmziv5#5(@$xerZPxP^=S3esV{n0?ov zUoAICk2r?DBHlLstacJ#OUgPRmjBkWHqz@^$5UKwYL*M-6gD5$E_mjKa4!C+QXU1YSolv|t*1lW#)qmF6y2Rtr$F;Xnsz!;mT87jZ_VR{m_COmTLnL7j8PhWovgYirCKb}W?se|DKq*6K;TGm5gFhQxR&MKKN(%s(PlJBgm#J}{*;~`RY zDETAn;2R5z5{=#RU)Qnncu}c$L~?NJ`R;E@yClQUlif^ZrN~19Qp4rsk)tw`>HhkX z!1aRtq{U?fo#-F3j-BikUVH{}csR zh5@D#J}|rmx-gaoE3FsPthHY;TLg%4UD)G4HwLlD>&(8&k*x($K$~zX01{sEYQ1Jr zK{^=LV!GfC98=y+n8r}&VZk)WOG?==a?@2o=E zTJVS^`Q{x`SGCk2Pq}kz?**qGLUo@b4@5-B#Q(CLYDK!z)~1t;RHR~*7R_-G!ru4@ zE)b;1BRFm1koO$Mx0f8mvM)(UsH5(xF{!tQ-g_mn)iKZ|usOX1dF%Aa}v7UWr92r==UrzVRUm z4q+whClR<&ou)w_BAn0rM05^?NKdx}Jp-O->VeicLS==rW~gW<+G9;XmCx z2{t>N5|Ms}@TuEpfuNc88QMQU?y~)Fc)Oq(|KG@N#FnJJc0i_f&V;#f7cWEe?C~MA^-pY literal 0 HcmV?d00001 diff --git a/images/logo-jami-standard-coul.png b/images/logo-jami-standard-coul.png new file mode 100644 index 0000000000000000000000000000000000000000..0352b55ba88fc05d8b9c599262a3d8caad8aa9d3 GIT binary patch literal 48645 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 z zePms}b%JIQpF|ObcHXz-bX6+6=a`a}x?VCT{VkoLvd%U5eEu4u<4LneJr}l}ed?|A zk3rT*YyHi-w`hKoS8L-UV8R_XM&V|VW}`++ZsH|#cMl+6@Zj|poGJlM$lrL zjzux{-@x%*TfuPrWc2^TGh!?i50*{j?*sXwj(hmL$Bt!ubH@^Gr}@}R>Yu(@y0yZN z=FV}92xDG(8Hj-WUII}hzD|=2;@TeY_nxPtrUQxgxPcYv&Hgei*Bp#d5IJB3tBB#bG&87TEdlnrwp*H&~+(}0)%g5l<=3Z&j6W%o)Y)OIU!QeSjoVKYzHooz&?!YN)TzzFQxECF>~ z)mBiY)b*#GWq2_%)?dmtPtUxbH0kiL02XeXi3FIyLid=0JgbjVrPX`vrHTVSA=j&) zqN}-LcDJ8H&&CtW@-Jy3nQ~02%-@XV)3}ZS-QXMf(n0SSqc5tXWtG)viJtsheKp|P zVqDb(GsmFQ{T1J^BY36T4{;vqwE2EtECB(6gG0lOF$4g|8-%ML+<2bKoLlAv&6*-`H6+5siU4v~|f z-J%Bn4|qU_zkU(qcMyF$fdR)3r+|e+{Cv=JM!eHGkO^(pPE9%cRc(>)yre1apbKuFu zRh@GwJodKZ$(w;wbN+>!K;K5?>KEebuhWzfBAR7q&tt1{)miA8fd}A0%H9%U!IZY) zExZT#!TceY*W%TmgKJ+v-P;=mHT$9K9-v-x@x1>ZtFLD?r(Kp!g1!yIHmv+1O$SQB zkavW7>Bwuhvg^nwGj?50)sq=`)(kyozVjoYs$&dfz(rOZPVZQhl;|<&iXW$TiDz8J z0}UmSVlxM03jM^Xw;G#1(~RejJ^dK~~~UDDNiU|dH{N%q}B z70$nqg<7MlcTFDfGkm6(GWNu7wCrUlawF~p%}m?hw&Bp&<-i>$HuOYv>wW2|V8%yY zo)heB7r*MYZ^vT5Q#T%EeLJE*z0SY$mH6hIkfyW06%@ZD2#fRUYun?2d& z$vuq?1@Nqct%|_6z5TTHty@{H)q8?dSD0by zdp*Mtqd;pNFkT`;>F^<(m6cTR^4bPSdLGU3tRDr>`QE3!;;&EesU(s+)wwi>MIFR9 zk0-Ky(qD03+zHaHxwCloQ|k^qo#SmZ*Ili&v(JyeY(#P3n;7{%44++R;AtJ>&!l|# zw=k{PpA?M z><2pq@T@}V+4A?-M=mTARY8{iWo-r({L4HnS9jN~kZxTl10^}nS%Y|IxbEtSJ&H(x~n#x|zV_K*?M+JK3{^mV6juU2JCDN$Vuq{1R zo}O_$DQ{HP&X$1(Ma!+!9nYhZ-E(OucxbCQ(greBb>hkraaUG`i)+nrlEcBSBmeJM zy86&_ZHB_58UoPPt*lhWs2y5Nj7tK3GRA8d_*qS+S7~7$cXm(X^jG^WdV3m-p@fG7NIMj%K~TqmVl8S%m_ zZ$z0q!oO*na{Vf#auw*6M|?y@UV}70ocWpYW9Oz`I`RZ-*@@J8ObR)YN=}O^fG6XS z==H(OU%x9Rm`Db!bn5CP_EA=L*3d4lA$wyRHf$;;b+)*pXj^pp@X=~i`eq&VtJ&;wsh%LS>U55 z%Pi7Lk2W4*ZIuz1C%du^YVlqY8(#Ubqjr%U?W(zpW~wr=2H}&^;frY(J38KM#5C~H z=B`;P*Phw)M3tykkdi}Iq1~B1?=~*M_$gm_KonYofEKiR^|71>A*YRj$D75h#++ac}t ziu!l;a(k^R-baQbp6Oo_>l|^r3d`1CfM0Kun$vR#-EzY zb6!gQc1;v$j0bX`*cKm%9AYM5H=cJWU1?XwuI`MsSN@;CDaYYpHEEjr@ND;q6HZu{ zQBcp?%#rb)4UxmgOLLfK_n~%A7x;f@U(To0Xd8MYAtI0Fs#(ie&)?woQc@(=3#z+z zPIkI_Z$&2kxr}^FXapc}w^q6$bAOeHRJ!`$ZPHY8(5!-l+H0LBO1gTlH1(8=tGRI_ zbyK2QU5xZW?KFIB7Yr0sw7XSP2K=lw(+g{veB?W*H%3=1#d-{y=8l`kwk1*=&84HK z?P};gsCwPNBLc3o6+ig(<8-v{#|!F-K}!a!U0%@3T9qE^{R^<<0hF?ntk}Gao4Wia z_+t-vjzkyvJi4j(g`R}U(H4N~YqI1_K=k3Vi~nwR2_8 zN>|SeWM0ideH^8$_xwqbi%XpnwauGpt}MqgsWSRBN@oB=_>l;l(lXH6)7;1Q?m?Oj z?9^Ot^O6iZvIEqy6HDol@NRna(cOgz+h04|ndn-LD+LIGUk573mxBeD`;uJQmwmqYkhR9&9~{l%C|6=24h%SND8j!eHN8-No~M zKIb3c19zkH4Mg`N_^56|Fr6e+u-4OMcbKqq`P^{}jeUijc#^{x`#iSL1_ zch=RK4E*Nh`L?LiD#*Wr?4WMHh$qWoF~=!~($eLa;B_FsK>mHPTIcdSsyBeS0rkH_ z!+#X;sko%Vqx^0aN`%q42CH^u%3Ty+oMqbahF@ z5qEP*_r-hIh@PI8`ZicdCs$b9b8tlonaeh0ff9fr%itl^jgpd;`?X&tHQ1gJ3hEK0xjnu>gF0edj~Lj zyp5QhlgtypMA42#?>hFMUmbT6zWU7aBsjSk2F8_;O98@@W<*HjR5 zzrI`j)76il-*(6guLwBCP;wo-l1rsjV%+nX7^^TbKF;LC1d|ivOifKPIXOvXY#g`b zq2l290Y(HH1Ym9Nzl2&86%S0k*YzA-b&a2??|wVnM@}hERP4EjCAGulWT?|)f{v?3 zZ5|ix2l2`^+@6KmuF7E@`!MDQK^{O2VN+9W0}sIVS8j=^OSTUB#T?eVK<%c7v*!b% z%&i;_NYyuB>?<&ha9u{B6jcy*Z4`8;t%QQ>fjnh}Ma29N@LPC|mG-1ka-giyv0_0j zc;zJm`ztw}h7b2VJr!_r8QX^_%W@Y8RPru#dThk=oT$J98Sbs@boE#Oq9L*62yWfGdJAf{9?( zF@j?ZM!+$Gh`|xTsD*mXN_9o4Rx#BTL!&PE0TTES!Hn7fZ8T90Z^ElHoA3|r#qoV= z+s?*U1#8PvH2cNH-|iUyzSdmM;?zz4?xW4;FZ7|6B}kDCX=-y}WaYw`%N*PS6C zr{6aDp66?b4=M@~jtD(%^=7T<>VxX`jGV3>Ycv{fM=_w$XeRjd+a{q~m62rYi_E@T zU0}6->FOea12`f$0!B>u+6b-)Qe6(r!a-zt*~s#enCc2rt3tg7%?9{Q#74?YFXgY7 zBozI2voKG6YL>t&(=<_*jkU?xuy_nq768R4N27^L;)F8{{KQ_&e*0UfQwk^6BWBv9YlBmSI<2{c;sT3X~R<6iFp!+tB=U$I@TVjW8f9;@NY%)6Zi0(us zy9#Q(?&5hD<*+}#Hq<^a62@Yopuwa0g!RZjg1vR9`Q12ih3gT7Z6mJx9)d>L^fUV= zB!?ho*-21os~~OAP|a7gH%Gr9_ld0XbU}}ZWXIsFC0)H&r|Yg$ew8{$io%9%G&)qe zde>(rr3!T__;&T(TFZ4}6+hV!)~9y>mk?^4dKLHJlQ>HYn56||Wf7`1q*)KunNdsH zx8csj0L3Jr&j|v=I4Fv5Oage~f06*j)ZMN~m;1Am>D8;oPYQk_s~p%%u=P|L)3e|Q zP_LuS2GVFCO+VMWvhKXGctH5cKs|}ddikWQ$F1bXPsJ-n9#Yl!P`W+oI|~M!BCfv$ z-_4-}uk7q5_-OpfW9Ne%J=)&bodh!MSVWuP1i8eW3E#xfl|a>VUg`Wlf8=#ry+?yj zNA;YfGj(_N^yuy_E4$R*u(=lK_zf^hi9eDTJWX$|AK*r&Fm_{km6iRRnFRKK`MtnH**{Hy zpmb?204usPyP>jz2AAh>^;D|OyYali-)!1Y?Fr*Yyj%{2K<^ql9z_EXr?u7P9De#e z-KS`@1M9&CJm-wjf1vHk+}d&7-QaCHVb1200eHHhCv{;ODqVd*W6|OC9a&X+m!|q{ zWybULRdxE!O^R5>u{L9RkJSfC6(rQy`1NyZrNL2DaRQ4`#kGpNc!=_${WymX;vPPT zv;PT9t=jXFqsz<4#E>mzB6!g>*u>Qwy44*`x%0Z-6QNXFz%R|sJ2u#)V#KPV#`JV+ zNw2mYSqwU4ji0I8Zxy#%MT~)^1?1r>m2z@Uw__9dGjlXIY@)egmga_y*zy?0Z{jqX z7~k*NWONV%$w>n{se$lK4%Ke8N`2znc!yHZ*gnsnG4h7iN_XPb6Pv#MwcWt8<6rlK z6CG@tLp%>at+W#_CTfTqN)f7H$fM`hqm;~{p}y%jokpCZ+j3ZUKy$-AcnhnSNQF~J zT(?idd7w*qw9(_3TsnGIp;l>Zcpwc3^8TyxJ(YyQjx_Z2N>@+1lzODAXQ@7Yl%W3m zykEbnR3LM|c%FU=Py2Yb7~f*7#i>^D4(+EjzaMY^Uf6nODsP_{RIMUiJ@uLK1H@X~ zYK_XiCnz7>OZmWFO7r`plIE+p|5#qcsf=~vS9*X;FEvN$R$e*{Rnzaq{y$0Zajiu( z*j)D6eDCKxnGSR_waruc6}W&rc+)aurc*08vE)px;udK@gvF@_?S*l*Z;^F){DNDstwKU8K8^A+xd*cMf`X4p-az z@fNDX@9R?d`|JBlc$-ceaorDu8FoD`7&eEo6}gA<;qHmRk$k<__UKX1$)fUPDm_{0 z>QU%&*YP9+gOT1W>=Px*-c59NvS!S0+nvH z#&m&90!T7GYlBtvS*2d%(A>t0E7j%wO)YIw(GWsZQ6tzigvHdZ)xb|yjh~q>eKC|e(6EvcH{8PZ!kjr{}$ zpHlo9pgF`$uoKBvUTB(UFDGP<(B%BNKMZOVm^_@9!Z12$m#04dio5lK0OEN|DFW!K&S)`zD8d(dY?+!nSz{- zR9Uaj;CZ9RoY+>i;8he~)x3aJb`HOP66Z(`3L$sXe>di!^ytoPHdAu-N!6cI5Xli& zdQytdQ0eOZK&LP5tnr?ym9CyePpS8eD$dF>FN- z*)A?XY(QzHrW22US0?v7LaDl(KjdWD;$ecR*)Wb~HEyaQU0f}w>AaVO&GzRAUcG*% zluBPUjmEP>H&+NeVhpN^M5<3OxJk~*{cHS~z-Q(wpO1hi8O$!N?Q8~^@wLZmI;xBx ze3J2lPol=5ws9-<@tsV)x9T?w-qjb6z973ZF0t%*|Nbc0R>APJ9Khq= zE=8a@AYsaswqcvIxd2I)`m8|3yR5s`#hNC@T|;M{w}v6F)*%uFF%R=A;`_kJB_r=w ztM_8d$oRGKGZrLh-G^K<^5}}}#7?H(5~y6A-)XL(`o^Pj3ZG1_tpxOB*5)DIeuD0F z^&TqE`gB+Cs88MPCpv0_6IjOg?`6Zo_fcM4?40Eys{#*evg+}47bMi^;ybbb05cEW zO=)FWjtJ-^P8y4@ED+3XVeM|JSde%Z)DTm7NPQ=%_DJAUotk;()Pa4EN+4&b3FCKK zZZ1iwK0V=Qh#EiBcYYanWifK)bwDTK_QYaQjN^_zzXM|j_b_&F57sSHKV=)i)LiQB z;Uof|%8Q?Z>h#FVao9yOuH@NH4HV_+IqbN5(z|2(YX|U*^)Jn5kj&2Per*aWITR(t zr)loPv#UFj#~L{V`2ynZ94=SA3xGy1Xo-8b`X5IsFODs@1XwWcHf(d_=*OnNQ>!0H z9rst|dwLf^%tuG*>6NbDMxK7@>RAmk`=zU|a^Fk38D~bix?8WaVc!!>KK2l%)#2N$ zz3^7NGq-K29I145<6GTu;BlFL^g&EBiU~SO1*Y7ELo{bMquyAio2sW`lX@=!I!G*N zT)M%i$NzO!7R}-*TT0VUJmIT~D`GH4+itGVUb(0+|Sf~-$WwdAp>~Y zac7MtOnAADx3K|Zdmcge?xwkE3p%j@@zb}{C%CD-EQdu(IKF|*#Nf`!DKG$ zK1}V#NARv5RyB#88zUEe^l9k*?bA&Nj~l6nZ{!25`|;hq8F$3_c2_T;=O{qWkxo}n z#0L$(^Ajy6q6@7Fe5Q6k%=q5NJ110L;L%n2nMlc1D)al<@bG;iL2zVMojz9Hzn7J> z&hNOeSR>TLp09NorInSf%?;Dv^bQ|>nuvyOu1NK1KUYH-@Z52E4GTXT@Aw=B3Ed!Z z5SMgcHbFJ&`Qw&yA9Df92cLxbeKcoJ3DvBYG8Gf4PE6>@!F{0Y!j@6J0r6hkNvB4j zv~U@t7TJWy@hXRsjbO7?kwg_})k|`?7~RjrQX^4<&uP+0N0iG(cfYU;TLT%QbVa=2 zV;(`wu(PYZM*kTKXh6MI!SkLv;<~@x&fIEsgJ|tn0muQ!k-NwW8p^r*#8B~Nh2;x9 zt4UYSruQR}t{#If5yUFv^Lvxb*<07!s`8=zXsL`HpM+*BlU5x)fhM&bA37K- zte4Y;Q)`HOb@Cjp`x_{mU(Z2F+TAU_^;l*ynIWI{6Y243$$?=!R36zyyM_Xu`1ujK z*YfB|b=T1QM<+m<5qy;{935u$9>mbSZ(~q5vKo_7RB}D;Vw%lWeZ9!s%1P-NHeJ1E zzx2%Fayh1}$NsBZTVZ_ngBafrvD>V}=vW(qUdvS`LV58Z<4-(%%qTlqHn#5xR%SL5 zIOYDXsdON67w9CxD6{6#fIer|4_Mr`?d+NFerFlgiLj22qAG~durqt4`ly8I?R{(f zsNz;vn7!xD>};(z=w!t9sG`;)rSiItzt{k0=`eO|98<4?)r(eJf9cp(cWmcEBbrAC z>s^#=XJ6HyYpC4zYCPvnz?RXsS0C$|a(Z2}91{nU(F3%txr?k|cL6FdIvZs=haIwW zEw`8CkLu^3=YEGp#PV>GY^xbs?z@N+=$G`s_LrVp%<3!X5SGJ z0YyQfHw4rc5XTSo8%G@-Tt*#dTt>$k6&1%_XVh;-84*Dn1!ZYKaR&wwXu4UOrWd-q zdZ}J&&B%COjJ_$ld~7C{ z>MnZUUU`)dHW71Orl+pEI(6LDWss=HHF}>0@7xI7)m0(8V=LbF&1258m|_u8#e@N2 zOIurRTPKwH#-n+ra8xjph6jGsr{ zEP@tA4nWWoCCWT*^bI9Z`iZD}o`-k;*P0?;E>{o^g^gNz!{+jcm|J`V8n!n90k3Gt zUR&psrjU+Om9uQnXP1CGW)d=e^i69Y#}2dKNRwDJD^EtOQFm?I`W+75t`nNT60WR1 zhseTaCGQvwC>tL&$K=UCQPi$_6&!u8SFb9!fy(>9Hv;D7v#K^nSp_m)EGpa$b~)5 zs9xT$WPPe*{Ny%opmp;LwPvlsfKKXDtwll`={EKR0JRoE{gPx3B%6IaN=Jz@y-}ZR zAb1?S$#}sRQ2v_c+o)egz9Wmbt3ZPkHA{cIr5xc~0JSGIsROJ;8;u##g!CQs;h(d* zGIRV8n4T~^r3nm%GLU<`BDKfRsg2RIOe)VJ8X3u|o6+kz8l+{^bqvU}cvbC`)8+;y zlyTIv$3ahB@p6gmNf;j~=kiPgqX;F+H5IJQC|Dl=-}BH}vrd@y?6WV3wTG#(QFeA?5MwZ^ z*qHS(Dp*O(T;&))6bnqPXV$6!Iz!O*6n$h5u{|iiKVFXf#mzTfegF<1wi%h#pC{`s zMuIQX3{zJ9GLu}-M-ll{DPSea(wjC{_S0nYjNwv;L~r!?i+wd8w-oIauQuqJ>?8#&zDxNWU1TOpbfNjqt>GD{n|#+&9+G~<|O z?=U^J=B+X`sc~0l@42hD{p~_}|KsjG)=wlXqE)$5rdn-@|&d?p|B8`?Q@Q5o3`pnUybXju~9bm68MSDT(Ry)G!{I^F@D-t zt-vYz^<(l>T~A$cb8>u8j3QV!A#TrZ$`k51m#@_I2aIj20w@?x9{r1`M>v&(D{? zb0UDD$!BUG$BDLNKlPi2-z7rh)V?H}G39*`LeE37lY3^&e%b_4MspJcIO%;G;)L;5 z+PCC(b1X6w;3ji(YvD$eaaQC#k>DWlusaBh&rdBdORZMgM1i~hY(%bG{6&)5g)|At1^F$ z(Zhic0>_O0u5?8b-)Q&GF6C!!1?^{7u zE~4{0_o6!XQJpfaS#00}A=cFm;ug=-!27a_{>}dIkf*JCjtwWAS~d%g#LP8>=RK6Z zQD~chsGeGAo$zS3zxQ-ei>iqtN~?paiCG^fGI<$;U}=B$@w!0>YXrvsl~IA>C88%-Vu4EWp_oqgeaQ7Yn-BKCg>@#GCO?_lQ<&Z*O$efk9xhKETQ;#i9{7BvY2 z743^E-ljD;`GFD*EHVsmQ-@V7nM;cUsN2!Zrv8f&eZRH*e#On7JTONLiPO6H*;c&5 zQH+63(Oe9=a&N(0gEbb zS8Q+7HTMbl@sb7`7yOJ3=&74hqWqmy>v^h0qJypOYiJ~?nuE#*xK?ph$tq+3FHR%S zvwa)AJ>6{T+QzPJTj}1mh5nwhn_}Nh-{X}3^Nl^i`o!VeQ56(!FHF&UynGU0{Atfm5j4$F1B9AZ6lKqQ+A>0?j8{yJ^^(;xg`JSy`@L6204 zm}|+NN_X}20Yi6Jj{%TXTpN-N71t_3e=pseJL%i8oxbf|?Cjh)s+(D(%ODSZCm+tq z&&?Q9@Z)9^k}MJjYK!&?@`V!yrhI8d%gzgo*!KY!vk$%9nSlW&Zd}jK!{&@yo70SA zi*0Qf8y?X&Wy;F7?b}bPT-;ck?OQPUfd*!{TA(BKz|CNwM+8$CAk4K5rx^G%+b5!GPxAWvA9wTlk_)dC3phKrHa2IKz8VJ5sg1n=K{0Y+ce8`d z@u4CQsPdDd^6XYn z48e1U-+wGy4+!&?vHipHUK{aq9$R4bhPS-sph`zk()2WPSCNt9bqis!3gnD&?kdyW&6-1^pq#zeeUuab&vynTLgDoWxk%CTh{SPwVct@vKKBfrr=C$M$rv+mv-@L$@ih{HK1oZ@H<3rGrs+KvBlY8tm4@M%dh|2@d+0_c@L17 zx8MfvCU75zCZtRlepwG3zVCt_^El2BqY_FlkpR7;1+V+)CJZFY@kSbqTUQ%${nNOn!h#FRWwdk#ie^n-Q=!yI+_#{)cUAcec$vs%84>RhXXbn8HAXhD_?} zc82I?Uj=e%z;R&!tCM;$<^fUe4OQ-aYVL+dPFOS3iWNf7K}A{lU++A)M+-|7*~@n) z;{~(FuqGe)5SVWexFGx&%18F_-LApf%Q5DmeHC>wkXbpT6c8FSJfe`^TPkA7kH)f6v2s3)TTg?1PVF4W8dKRD4t# z)Ev`{NW~i|WV(huN~)$8EI$SP(0=?%1bEipBQ=RIUYZGdQtU_dWYW-G-CC^g)3_87Ps^f_g-1hbB0+vx#^KKej3NDfDzGaR5(b({!)M89hfwxz+~*Axb^L^M{Vn~NzL})I#}2q zx7J-kdHEQ=lh^r z!P+Q_yW?Rq8E~@^7|G_)j5q9|SkwUnnQUQZvEZJINR(?z!sr^TMZ+Sd&xf7}2#K|* zYLqs@$wryNV^Ahp4>BH-%|?Suqqb|*kBPd3)-Gy&*T%?TG-%#5QR>+W22QY+&nZu0D+IDJR5O4nDRe4@Fv=m5g| z!F-2Yl&e4o?u_csT^wJCy#@4rAYU6(hDYS>7<+RxIScrF8;}1BoUm^_7#(^f=E{X@ zGXU%IQa1YGUwBl#U)YA>A5gwCMlG}))xSdI4#q~8)yK#iVVvpNt4vQ7^HFN!w6I4l zg{@t5Kl3zUA!hXUv$$l1t}Z5Ybkt>fDiTzRFlpJdV4OJZfkidS-YaUM=wti=P#Xag zjS~H;lZi|;nJhRN$a+Y|121A}((guQS7+3I3Pqc{5@Su$&e8SQC@`P3(S8Di#74(h zix&IPz7P$g&DyX$p*v0lfHGMmla1IRH-k>-VEc>Dp>^F_vfVw{mR7uNo0+h2J$eKTCtOabQONy)W#z|`za8-|2k8Uk>xg!v@^zrS$+x^7(KloHU9`Z9Z`_kU zl*0A>^1)pdC!K`ajcw+1K{!F569yC^de{-vo*9sC!*z8_Ee zF!m~}xtnH0pe~nnh`w!)d8?Z}2YL<$wx=e`dWE~XD#h+y^gg$WaG-y`1UpFyiv^0^ zyU0zQwp*r0Tql|=RY@3_GWfBG5!6P>E+qy(gvBV(U4J(kTGWQv_*1PyCWE*UJCi;9 zFcgWYUcDmH8)=jqrv$J}CEzhOVxD3kM4Vhx;{=y#O$h)Ih7k6n`To*-sh-EqS1l&n z(@l2g4y@~9P&C)dq-R%Bm^6iATU(QX&Ct$OZz1D)IkugCHtvJJp=HOmu>v~P-&|_L z81x*(f%hc0C|>Wx86sV02GVl_s;_D;!NdPh@ZU|Yy_e8}e-5g(={wZ_36fhJC#;ko zVDxvWTnD@sMjalm0DK4Ed<-}2F2T7Z!DQ4ARwC|M5Z;V()+p-g2L20pe-mcH6~nf_ zW`n5|+*wVi`|%M0p36F)TptDi=A9{38%luaf3pK! z+sRFyg2`sVxDP?6BW6?0q9~GJi_=Evk@M$^e zwD>n*z3^~Tbk2_Ih&2ov1D~YMG~kg+=0}Z1jKL(JC&`)~r*kOoW}qYh03ZNKL_t(d z8ji!Ri?76FJiHAXQ8B1-v5tb*f@3X{S3JVjm%JPu(W*j*I3^A6Tuw_HooBq9Sr7ag zuXop20iDwK0vBZ4vXhQ}tY_z{lX4u4MZ87vPU2~!uObL!1kj4 zvcVr){tm=j7*CYudC(<@ITpfq5Cs7pZpQ295cvrv{5pZzhzaWG5tBuT16J2kSavqq zg>OLYdq7@34906Y$_*6w9xbL7ZGr&SZ+m*kDIk&;FUYna_Vc5!yZxyVrJtx6jJH8c z9ECvTe{hR;6S^NjM<7@i9tOTj{_Gp@9^H5Aa`z^JO$# zOD;qQmhpzSyoIGVZyr0~BcKC=CmJ1(%shf-ZU4gmBa_Ks90$j7Fe3O}+wg~w{$YPC zUPlKL=gdjDsi&e%i_m`8vse{1D(N+DgCc32o>c@MQ?ug#9Z_|4kGU@ z$t@^98-JOQUGQ;)Ptsg-7QhpTo&*B<4peU*-gRa!0TU2YKT)SM;Sq$+ix4>$>>Na< zq0$1{0^||xMP(hrO04-a#y)|>3k~bk>7NSzGL~F@H2_O*+@7iv$e8IA^yQ!@fE91Bb&3Q*Y*w`~{j1S$`pdIv@x!#Zf;m+=_10CJ|ZaI75%%u$G*hw3Q^ zvr#6YOhT9#H_$d!wXS8iw?*Ur*52JE$0Te=dGcFU8(oyonR*03X2`6HkIwtV=!S#|T)1 zijB-%X%m>(cyJQLrdS_|-uh@*l?%$5bWIdl)i$V`g!PF`Yh~8QBF2IZ!In*4Y7D9l zJ6|@RVAdRRTee2up`~yuHZ}QcOB-$L*OH$y9e>L1i^+E5oKOl79|0z;+Mp9;LZO&@Vc?7QSV0u&EGY$Ul)H- zdHrpRK-_hRU58D~#3{Ba5zpG_k&=nY05z3;RU$yL zT)K1{1i|RWJ^$>wCf>aTE)GO9S1C?Un9t*{Ul*|l2YB-2T`z3lkckr!c`kQx38h{ro&AJI0h&QCbK1lr%q57FeeMk4hN`MJ6`G z&KI4FKko!G+qzH@ECR;FfJce5hGE@Iv>%2{e&R88!*4v}tregfB1739MbT*!O z9<%@Vx0s+fbfA;|z5&oFO*+vb=c6$wbQ})!A}xz6$PLir@$4-iW9O|T@<21er1ssJ zx}OE9o+P!sD;qCrLXAxJJ}kL%rS5!8tpG6icu!sxRYqI%D$f$CVXJqIH2aWx1CRVi-R5WT?Wz#r(XLhqKXOge0K z1;I6>+!@@JVZaVj*)i z;7I^h_i<;?d+e!Xw(pGYSp) z&^E>|-~$vUjtz%2uxyX9Bnhw?^q8wpYAS+0G!FHAA{1vG&ZaY77J02z1D%FC zth8P#(pyztN9Aua#=O$RLU1Vt2PWY07L@PVZ+C#}5V>*80T1+0nE2$r#&C@FfIcoZ z9WVQH|2>fUu-p6Pk2B$epFJo$%8rd2nR(&~Df5;ofs(}RUcta<>X_=S#w;Zjaeg8u zrp(r;a9%0mu$1GW)U7vRo5a@F{3Nl4)Ua#b(d<0>RHUaj(m%%xkBxbqWZxK5k~yUH zJ;u$Txi(ritfe?{G6RRqY(8r<)N|E^>4w7WBiS5U4*km?kaT>aL0BG5nXstXul=pH zoGogvKTyrQ(y}n6-|^b=+Jt=(m_4 z7&_3|9r#hjz^OJC_V!-)PX`Xz$t;NaPsvHa9EIoK2AsI(E^{3%9?RA2!Iac- zyc~G_w-Ept%RW68_u~TeUiVK-`0(cs%8s&QV<&A>rf}+$ZPB!^<%xCl;HL~qBnfr~ z94GUfr&)cB#9(Fj;75>j9xAD=qvx|7!Y%$~z$<1pjFUn9jr>NJCaoBBW}`iR<8(NkCa z?rWmIJ#Zovio38fva6AoW9==#o)sRbJV0jOU1W^|pYbvdeD)py+7s7aipbpi#g8!I zBVRZuJIdBIYng3BBmp4l8Ftj*CjvX6=i$UUktglbbByce(m##ngTd=^SkX(Sw^IVu&Fl4T1cu z!|6QxLJqnA9!#OYP)%5gFkF?CNIV8UX!}Vof8XK2x&s36I18glJ5uUbZ^!DlfXp7g z6eVN+S9|UlpE$*O=o;v?!(XSJhozZ=E{T2s`H-$M!?#VT)k}3sMh2)CihW z!e%uFYs$iAD+YC3Y_1J&+ZNhhSW`M*y{!h^)Z6NSo86vkcOXiY{ES(2p8py`uWfKa za~#MqN)3_(bu90z4NCzZ3H-wkgh5Dh3!|lt0EK0O$fr=gyB7ctz$%Kmb$ntN2UO!_ zY(e{Y86xNb=)LyC^j`bnl4sAsvVm<3Kq!AdT=3&hp3at&UP@@CG@*t;IGZpDC}^vK z5GHlo%6gOY#&ufbuw%{%?3y?uPB|ep=AQ~mqy&6S3ht^8&ck1s#BMBcCY`5ROkA=Y%lwwO8 zn_jVq=@0&f-1_HIj9K)5P3>3fc&f}>piJ9;*_}mHWZ?Gw33%C?fW>?F!sG#hecNe5 zvag8)eA@EMAGj=eQ}esS!K&|B`s2;z0Zk2r9!uWbK0X_d0q_|I*TVok0DYhRdjQ%# z_T>Y(zEVS_RlQ5zspfWQfnS~#hz3iyLfTM~t zDTLW9o#(!qNzcq+(qDcb=SM389j(&zR05p}fV5v`92IY2g%F;MnU3e(1)QhoHMD$I7ve%l5g)vvbPqh}cPk5gVd5KttPt4T(%#$rd)K*OuzdcT8r> zyp!==FLl;>@Ih(@+450RVtcg8%wI?K;OT1&x>={=MTV4;ODM8ZOkh5>{DgkgQ)lW2b839Ng@k!+iN9Dz7Bj7L4-(@5V_ z4SZ~su(={?b4ue=irU=ThC498 za~1x~BdUN-107E)Q$0b@yWcIE(;6O;IoRTFfYIjA`Yy`Pn{ai%Bbyz4c- z4$R2s20bP4^Xd1}_v!Z@P>rB|1GZ%t;HQ7;A#9m@5(URA4WiXxM1ueg0;~i?+0r+rEvCHP4O` z+zi6n43Z`yI-lL`IU^vNqx01lv*WylsN+-s9}UA2;IUx@b~Fs5eXHB|n|Z`r5T}($ znmarJwSNGP9>r-x;Qhcm5MDm|tF-rFN2mOfmPO?WGdusX5};H24}|xKWabb5e8~{# zIy3ORKcajT=#1`N7lU4k$|r#55#CC{`Dct_>GeD7b@Qtb`#zikOMiTDJE<+ben-3w zl_VTx-;fIsc}=`YgJWKa$c2bpG!A?Y;^L0YywJpTB*63-dS)Gw0yhl? zvx*O`b4Z;*uVGxD`t{T%O)iVCcFjG8zL|$J<)PoZ6C;+KySl@M_`?!M}47IeyzpP>UmO~NX&;&?Lv4I)z=E5AGeEwKfA)v)9Z7VyAH#nV1K*x z=FKd*;?R*j*W5xhKXRSY(wjO{?|j8zRP&B4dE<;KFt~7VERxpc`O7N6cd5>-l)O74 z?Nj3U+UU^=OK;p-q3@9&FDRW~GM9l$W5c-nPk=F5;?n-W4q|I}#NBoG?5Z*Grf%KOz3)>1;p#O!}tIir+~^&=l0s zhKx@=ea}!CA5FQlr}q=4r-JoyGuXBXOniP7ZCg5b&)hHslhh#ko%Ht=`khoggPzk` zf^3#8=U&LROWve~LuR32J%%U5+OSUxyB^)II&t(|j|RDKlz?X=R$qzQ+oF!)yn6Ih zDqR%Md3;p2ms?O4nj8Bv(3fHKI|!eN5BUfmx8<=Cw@nqa2AB~$QRr)P`bFP4Z?|7dnTd2n0#~}6~J(u z^*Q)JPeTB-p9Cj-Z2r6w;Gi@K*3;a?adzA++vlE4*Ad4OxS9CH8VrcUYw&17B@o$S zgYZupR05vD#A)=Lc&6<;>{zUu#iYQFiOpzf$Wm|Il~Dknv}vot%vBOJI~AYmWC>f^ znE2dNcPV=3ko7jZ1vj=1(oO?;shIQAtNdAQU^EEkOaLir83`$y-ZQxRp;d>9B2zKwDp z!v6y2;TB$o<3nzUZC9dtxh#HXZ^Y>fb>J?fWl?;LMofVdZwO1SJhVQ8qdbQ24&YG2 z;!=F$ExoaGZ}t!nJvX`uxeYiEulsoRPRINMwEN-x;LRTPGpya2q#e1+*A1gwbA=8E^a}*;I?!7YT}sGW zME`}*+<-dIk;@%cx)&#mbz}Y{RDOu(Y{8g^F!E=-?k&K3L0S5f&i#7w|LG5Ya8QCD zcPvld_o=@tvnikY#=hMWVE|%KHIdm#Ofkab+*j-de{BA!XARk(tW^7*mSdW8(8$z+}8zJHo!p`?NPRLEG+CIH#do`LIk< zjR9o+tm+JsZleDG^OL7Y{=(Pdb?*vtYo6`TKJgdm+S)E^|J|~0XIK~S;`n)Mq(=$t zuYn^*F+V;`CRjkht#m&UeN|(d#E?&ZPrX(A(C>9YsxLdoLU;+nQq-)E^uv4=;RdYv z2hhu+S8^e$cMT2*%DsMG{P#)?Zl|tDSjsk{r8jM1$(4sR0%!jN`Z7e`i5GqqG4HDP zKGgHvYHCRYfT8J)%{Ql_{6AE_jp%ZOS*ZC(L_bY-ei28mU3$~zbaLaPu=XCHh{{LH zzu_NI+8WlI`_sRm@^R1&z^A}Gjp!_t%Mg79$mbffP}+u9xD|LcqCY0zbu}#%iPuFK z`!cM#1?Z``CgdMME3QeLh2@yz+3t^md>FU|;Tu@pijlK{FOZ5%E-eeoew6X;j5(uv{b0|5jaY$h*vPgRr#Kmcw3^3BT0XfJ1a*J2SI2T=ny zGU1fKk7O8_J&*jf8H7$2kthLZIeBLcigp8hIxoJO8Na&~GjTF{_|Z3NHaAzZxqnl~ z6;VN~MM}Dw=o4cq5`jvxFX<#v z@U*<9JPG^RRD?vYNkYtj_Ii!9fLy&+#l9<)ZHjy{jO{9rkw=x zPS8Jte6Nm}hS7t`!P-0tTM+i54#1Hhmmzvvsd^{8ozOgk$j2GTehshCU%ov1F@!c$ zUx(<=OEJlk-_$D^Go7OSNVGQ7*I=ASON$+m?~xBCl4~&K`1uKV-YRfFB^4qu~AGW z5%4X|h-)VpPF# zVs=NdxfTkC9YZi_8liDPOq?FVRET>M;8T}@sRll;xQy8wS4HL_08ZVy9l3q_6) zaWNXbH_2zj^rX2SO8d5+fO)vUr(y^$|AHi7p+`{T65wJNjjAN_!bNzi=+;4h& z(ow+u-;&Q=LQA1X`|m;~Toj4;BeC$SnhbZ?u z@TL`Uoy!Trhf%(PO`{Hdw{cn zZ{jbzh87!TKTe{u`o_bvuP9wpF6FIQbxkE_6M$^b@hFo~{VJXp;1(f>v+ouPH{p5z zU1EE*VgdQO2sGMO;2q_GA`dDXfK$kO?Mts;*+U}WPQX@j4PFqPZxDRD^yW_Ryc8QQ zCAkEiL2`@H{GLzHyDtEsu`<26dwmploXEv63<&#+&}&P%lO&TxTo>_jnAWxk5M{2BFec$Wr~y6(8)Yot4e;48>u6?fS{+4L z7Vj}Na-<2vYf=5Mbu%V0DV10sBPG+8q|97`8KOtmD55qClwnu2r^=nCCHH#4=3h8s zE(A7uw-x-*+m#D)3-Ts^7w9>4!ObYmQ_@7BR$|HF56ZLrNdZi3vixuO_VR0L*Elu1I3BijoF3&ws?{Rz|8)Z^zmSL})bmoDeZ5Y(WI=y*CoYqX}5v0USaFr34T& zrz&RwFR7}m6X;|@zJ&XS6X&c-y(VSgQ)zl?1V%Q^oX3of&qv_SDr%g6 zR3qOt#p0bd!!f43Zj6DCQ#NgpDA1$*kpZ8Ix38fqd-7e$+y=K!%I&(MC*NM!cKDoF zh@9jci`-Q6Cg0sZU@vO%^p_IR)x!oU(rloV`bZbo8jJr`z&9Ouc6v=8iUx=v z7J~l`YM!s`3vpoEqJZQ};3VD8v}ak_Ox&pCytqz5{X0`bRyt3lhT|ZP6a8JaCEweEPuSZ{u%(mY`n43EeG>n%zcBE?AMhW082|A{ zC_McH!P?aX8`fcW?!fNcF*M+FSRXBZh=c(qEF#uoV*76}001BWNklc$&#M#kJl?!sFP$rM^();iWzpnsQ!&;K1lBRm{c7&nC*opgZaoIzlcL2xXdDoVJi_mzX5H@y3E3sx#`3l01V%F^3@~e4k zD%d6<^1Jx+-4xC;hy_#fSOPJx1WpCEFwpgv5wzx9)IFtQr4~h?z9_DDMNJ=pxtPo; z(ZV$TUHUia{vv?bd!q~1vv3QCmc|=Lz?BH{)nhD-tS6)A0gnOi!CUwr6izj$fN^3| zk+(Bu9fx05OMKxO)n2zTrJ77Q$rC@yVW}FfLk{AHh13tCiyWqNr>!N-h+t*9j zwUy%fwG>x969J*e9%kUT_cHL?d&xiehthY2r&bcISsiUJti$fy&bB!{EWGkmB(#{I z68M+|_-OncqXRwyY??if&KYx}9e%QIW50 z{nrcLI%U?D;?|kd8%C_wq7Pw!QDs6Q-`l^d`(o67XHal6L_kvkmcqDQ!Vsql)MSGA z@6u=dzaS*n*R!%M=sCiK`q@pNUC6rY7Vpz9sN@#MZ)rT}X0y{U`cvu~SB6ra!wnYE zqTTA>UInferUS5D(uB)jhL_8aYIKSwS{L{L5Q4cDSY5HHtj3zv70hF1em03QVIMCJ9UIT7SIjGh~N|4c3Yws800^I0nkV)ex+A4Tmi(~8Q882$~B zuQa^6qHMwU&qui*^d02x{~?8~8GyKzUFQfo|P2CFc2Rf>&=Vt(3u$dSz0xE35G z%w@N!k+VHpe3OrUPsD98Dv`)JuBD_S!ldS^!A)HZ!<9WYbp-xZt0f9!cX@%17+`8mo}c+)4~=dZx?q8Gp~e1)8M7Ru$7 z*ThQTR^YYeF)Y=#hFefB1^pb#Cu6hKO2k}(>c?Z?hvt8YWZwwV4c0t}+BbuI7vwg) z`}05^l}SJW;d7CvN%9ykK=r<=M|(j^q&x^7QYy?_<7JG;)x9N<%D(^8RS|p}Mc~zv zusKN(S^XV04qDfUeW=c<)u4E}9B+8b+hF2kZdtV&KPZ-dGk5JFi0#H^GUcq}Nu*+l zBNbDfz_XmsYPjsm^sWsN6cR&zAlKU3lE0~#@m`bE8wCiIim1t@Xv*a1kx&w7x zhNxRb-(*y7FC+m~wd&Wq0We`kFhv{bmW00+tKR~$mFL<$sHp@oDR7enaI673(WkK% zH=qCYfZKPeryYH3ue)sjcroPy@Nz`D-F*CR$W@5`XoRQqv2q@cKGY~pLqw@7C?_GT z809S#@!dmJf4O7CZIDp>q8EbB1A8x+)g|+U5g3SdPPx>o+sfq_I~lQeVV!l+@-7$@ zAV~JrAn_`{kZq-R4IMjQt%CNa(jdo1eyhESiu*tiWkTFCXh z39N&4?^pl!$0fTsYQw@Z!Mo@pFs}wYR3D(={EVz#iqRJn>Q8XwVT^@P%9e!06rVR8 zv==fIoUT}R^e2RKFqy-&{}&qoB=cI3LR6oZokpSX2H>QaBv^&$Ed=IyE#8^ZNGa83 z+~^s|Apc)fMm~NAsy8!U1~1JJQSUv_^Y{N&H>$4IB$liKKh*#y1%R~ryHpfjO=m7o zj;R7XufO`ObWA&x@2^@#u^7}w_YFhlZEQu=C3Ia>%D^WALQMuf+tw3GtaUPi6WRrV zo4GM`+{q=-6`$Km<$m3|8q`w{_$aJY)z>@lOmTuYI5RWYeq67FA)-oXc(D`ePX*>w zf}4?nn!2n{e5?}$J(-~RreenVr3oz0eR;|Lvl_`Qj0WqZJsq8iWd+KV5uWnz5Wd+M z@TBuC=|3Wa1d$tBxd+wx6(*dKf*!QYs-%RW)f0Y2Y5)NSI>77REkGy(p9N*HZdyMq zn#3(i;x<6mL}pi|QtT{Rll!Zj|0#YS>#XW4@>Zn@+2m8oxuGg1%LrkB@j(2@Qz|X; ztpGhbx8j}GNTZ_uJ$0_9PVT9CER6{G%PPgUjcAR8c@I^AD)&s-vbIFZ^o_*B3CFzc6O29^{!B0Am zw^R1=A_f+v&Sh<6@=DzMD$HJjgwbkCt*Ojp9=`;OFMlHwrcLLT^)FDcL_i}RPts=9 z)wBAMFyzP$lhABw$SGOZN|B4TZ00J(^%;h#3xuuH4hLZJ_Kk=dLN~K)PJ728Yj*AY zUg&18HXt^>&YDO*qlnsUkpF2@?_GWL_ZM5UyESJGX68zJVi{H3qJ229`Vtqt0hm7& z(^Lu64CR=3esGKVpJgX=Rez5uJpaYl?O*h(w=jzF5jf>Z-tv_Z10LZggyx%C7$n|@ znpCn!xwHwlam%Q!V^tMMF^qbMs+71`dT(&)dTSA-w^{Jtptf8Wl-1XSq$8h z^6(PyoHA%k%wh^6mqo(&@zRvCcNJ0+KWs^hnN7@Wm~z(fwBXs4y}cTFdewd(37r$M zv&8#2E*QfFmt4x^*@yA77dqKiD3-FQYXX|8>Q={L?W`U2ZEM8|3MCIK6APT9Xw8+v zW~mSxH#8@u7B1z#cTAau5V_Gm-`f+YZAuU^PfKCQq>lq*Jf%4f-&w+{w* zB$ry%XY>$cG{Fudt0u1V^U<(8_4{sF%cF%MY9R7{d9iOtHr#2?9nl2qyD0{xie)g0 z=I^S%Do^tB?Z(So_0Qn@N&JVwkM);DN#K)X(~MJ-+4~5{Xw@YN__>BAL)F{B@2CI` zC3Yvjt^B<-)S=lg&Cop(vsW0Kx-74pzks7sG)JrfN#riyu~Z-T1)%o&FuEP_?sP)@QY{Jtn> z*46T)bv`9Cm{u}_D5$kk4F2=kF7KM-=V|!lwO8zaFQc3Z*=g~>S+ezK@wy)ajvv7( z11vAa$X^;U3=!yAP+nD|e+%-h(VMoIpI*6-mpu)hV{@6GnM^MD2$mg)etxLo14>Mi z?7!v}$)*zWI(8P;Tnro;#Szk;NDnmeHoTs~VDHDM7cuep)$~QcL0I-%O;7g2pBtJ7 zPgOQ;ZKU1)iD-A`* zU>z50Duu?8)~7kZ2Vlz1tr4hkU9!d;?c4Bn>t>F%Vp5tSQ&m^pdY%FOxK-yT0H@NrR{Tmm#` zjoGu_cc@+EXt>y7p0$|_&wTx={d15md(NcZ3B3m7Ya=}6S3o|ny8&Ht^UEty)%%eo zg~*7}+uN}A`{RTBz5sk03uzB@&=cK95{%HsdZ*ZoYb=OEGr&${LNzt%A}WFts*HPR z?E`>k?}DDe08b70u~nX4wM@{ESfAAQr@!dMoOJpb+_8NJ+Y7~VYJ&zW5^+awc2;p79_Md8j=lUYS9h< zS*(4BsOFOADmAQ5@|_I?`|;3qJ}*{z>ObDL{|?UCg=Gdjq3V5Y!;@9Oj1io&6F)p& z^`2dW57_f#DqR43449`GT_(&temG-y^^TW)9V{Fepl9sa*_2y)TExuYBIfb3_dt)k zmwHOMa}2g$`p(3num)o1wBXqcB6g0yKkDdXIpOpdamS8bY!AYc_*v@Nmrcx%T2u10 zOyz-yruCLqq`!}6AAOY57rg?gm;ij!wfVuk?~vv(B>u zXUY_gI{O?}J#atyP5010vxS+(m&V#X9h~_oeWpdnT4Is}B`6qhEP-RYOc=h;2Ek`s z;XP{gZK8U3HS-g(P4YXj?v-IiI-ma5Tle#UGbLa(Ze)H%Ar>0_Xq6dkFtgXZIdeJlrDt>ZuHFdnR53eJudcdguGIHoh!hG)e;*x_Cv*H+<@|K( zZ(EDaI9TIUn4SKAduJXkM^)$X@4Z#s)vsUjvM;=l z!=}ifpeG!3U>G&W<2Z~C&L|EdL!6Ou5E&;bCJ?{{1!NJ)f{Eq@ADD#%lDxIIy1)6O zs=BMHtGcVYtJimbIi1&C-CcF-R#o3meSg3E`w>`p#fJzYPWX#6i>m|t>8F3rCuN32`eiHj}^)5W{m&)Tgq*9>EkWd*7I_gD5hG52N?_XOS8(QjAO*f2I*&4ey*f zo^C4mEGL5C0!%jp!3FU9?$`O!eJ8F0#CZ(jT?8?i0lNjnSD|P-8DrRS|Fvy!S8c@} z*ruqbQweC=pO7zx7|Najr?Xt;5d60d0GU-B+Oxd?W>>j4mw>QYO8L@8ulm#20-;jn zX;$cQ4d@laPl)7(Ju_$FU8kAvHLV@1qmOtpapspB1~JOf|~}t@-6bhn^$s7q8}3uer*};AS2avZl5&%Fk5Z z`QXRf;|J1Rjk)mjIuO29;eomkz)#Cb>MUluFgO54=YrUiRqoC~q>s!ZdNC`1rlm=f z){M|ofA?0Qz@GDYP?wh-m~83}GjsirJ+F`D5@QT=PMnG5%TF*%&9Rgq5&|&>?9J_fGw~UrH@h7GFdvH@EqGmjt-U1otQ-9E?#@qC7+Ui? zq#xfB=w^o7rsE3!k>X@@p-+zopiPm$BTbsLrUahGL5~}Fs)8SsO>Q`A#tbZ5wj56l zjo_GWCJG~4)-6Z;kO%l!pW!Cq2zbW#0D$O7f{Q`)?%09F$(2Zs9h-k|=DKNy^7Gi6 zfWnQOVjkebAkf(r!|WAfec`5@%{zZe700O5z{a3Wor<|Pv(gRbnAKdaKDNo!V>r|wEm{MIkn%1^M;@ZjGM~@j4Y`Dpd~OoG=x9B_Iu1d`DFJkF#urB zTIa-^1?Nm^I=Cri+T_WYxB6V{edS(V40 z!^1im306RHWvUCqV^EJ~f_>Hmo^*OVH0`6+pJ(h|%Kk|HN=uU_t*NcPN)K7}T=sR^Xd)>fH9F*fWSF~rh)4^jvP3E zWY<_sn>DM(eOt%FXxmt9{CLb?yAFNN{1O8P4tT&!39u;zG;It#p2y2N$zso*0^o#- z)+y+Jk;|3m9c@ivrdT}jw*ZZ;a&Pt_Gj_B5Ih7XFZu`QbD+4;V_ksXUJE}bFVnDN>QI>y{ z0UWCsNUcp;Cwg*c)5EO9iQ{m0OV=lWEgO7f@HLvJI#s!6rqK4;GxYj=+ zksc_IMhsBQjVJp54uon+{5aD5KQ?^+y?Gd^1%P;gPO10ZZSebkxwZxYta96~q`lIl zN#JP%=rQ0yfaR;end$GVfgkg=s;W47;Uc`ANaC>YP;rW~i-rUr7|8)y(Qi`HkMpBkc(*`O*Pb=7a13*7*lF z9-KR8cvL41G)XmwA>8%V*{5ybnmAf+pHTO^M z3nwa2sWtFOlO|ze^tG`ufy`tB2*wPzk;C1^owK$|s6JCBOhng=nb^)_$T{wq$%A5o zDn9OItoWwfJ-yvGzQ=e%u{fX!4D|P7-_D)rS-Q08l^frcmMqC*ygBBC6EOFpi}A+O zPhsTnTV>TA7E=9bS+ytbl^E09mzbE3ii>>;@>h7)Iw1)Hr#ne z-4!0YI#aEyNUA;3+8uc8P+?a+Mg#1)_qV>#qw57V#OC56a>EY-xxBikO+$A24D4pS zc(C^!Iw}Mo7yML`wV4tLKnNT;co1VJOu*#s?iPg`^4u=Do@rfOn6qv@4!-ak9NoDi zJm|FJ3@+w#UJ(FJlPYE&hKZ%Wn=gX62!y*M*porT_}$1v-$kW>#|us*upm+ZthR3( z6|YonfO{+Nd!?Y!2mord@>GB5A#%Eb@^HgIX7NgM4!Ccyv%)#Ld*C8spN(3I3p8&Jgc%NlK=_qd+jx-u^7frnbJV8DYQL2+)$ae8GEH@adfXb z1Ig|g*#G1cKrUBxj8ALF^pt2j@$r?Hf?&(tF+D7?&UyXBbCZx!{|~^}s&|9otH{#f z4R@SbH}#~J14t{WJzfK=A1Ia!E)NqRUaEgettaR@g08ca$`^M3ORXaed8 zaN+ftT7XcaK*wue>N|VOgdS$8BM$g6q>;SK3r;o2a*1_zcA~Rq9{OS$^dg`M13vJW z*cDasLfEo4GccP11P)L~5}+aU?%IV#D^?&mc1+{pri^Oi2R4qk$oqZ987B11LFWfQ zj5nX$hLJbls0?&k9DEpC4QQG_2XX4C;`pBkPG_;$`d>n4fMOOBwN{u;j|atn42DG! z0MAjR(qGx|`SSt|yv+_gtq4A0z$L1E^IJzPj(ZfqQ?IrMT}P{&U*!PB4~m*CcF3=t z^$ld!bwJla<^#HJ3sQ9kygGuN&G#ovpN`!8Mc5zHp!*Eg-lMlqU^5UfxY^}qv%9(6 zij*DVxbYx&aI>Oq{^Ab-5TsXm?7xnjG669GaNVvT zErF)}3XHucBC+c45sx2>YA|R7@Qg;6SUusS9^hN8>#m-_(}@16B@}r*TE*(0oRA8Y z&#eKIS*|0_i_{%r3|dDL@%fAJc1MR%ptDG&aL>#Ns`!{HPC3QLy)0<*LVMO`xPzM& zh|V#<&>;T!>Z@3^Vntypdw@|VNHB3#%1t1c^l)`~e!SqoV~k%WenZUNH6w2nm4JgNdtA{Y)+Ecsz&9 z8Q<9Od8H-*(da>;)q|R?wg>G;w?bE`k`hFb3EHof#^zMp_8=~+Alrzl4{NE`w7ZpZ z(be6(qaUjzvEBo-l2%q))@HqnhI&t@gkJG{C*(aBbOKT<+!C^lm82PQ~#_Q;`Fl3F?YsI{XIs z5U#GkIea`^T~-bsk7aqc7B^g8a6Hk$000a`Nkl?w91 zm2ycDm2gX$pDpK>LcuNN<&q-U-Bxh5!M=WYZXd4q?GF&4YeqNbUUfC5u3ZP-(c$6J z(hIM3<24g{YhI1Kj7u(?*=@>zO{!Gk%YfLv;UC@;ctC6RAN+Q9bJaTjChBq^QLnGw z$5zfFY<}RWyV9dSazB*IuE`IYa4S4Sh|Qc0(LEC*v3QX?fLUNcUO9A36^DY{+*Xsr z$NjZ{S(}+T=n3ZxFED`2;MTVf9RgzvvllI@nQG&?-q6ZT3GK;8g&~hZqj{GX=NP|W z0lMbS#j!v90q^|mX^6o=M?4X8L04OuFri!11_A5=a8eVtat~7Jjlh&q@#8t#a`&{d z8d0mZw`#o3XmxD`opq+xy=8h1AjJ{a!(ws7X3qglnu>HRW&(jQV|{j`TcpL!X$e}}+wQymCa8o|sCJfw=4O+^=jeEuzQE5P84C5Cr zMAy7|7&>qe$DjQ*())T{utRk%-x0M`;@xQ-0M7tS2MAl55PDJ&_z+@=)P_5kx6NQ_ zze69bE(i%b#p{zUsrCh)hCmPHa$sD5b##KynhiE#B1EziS<~Iah>TfV+!`56fRBCP zw1ZEez~+d-#|1wnvo`xBFN|x*e&z-M!0FpwZdkmHlX<+>>&h$?5^ku}YcsIDv-<>e zUVb@psT2nP{AUdB+J*Fv9RLvqezT=DuS^o2=QETMfSG~8oW2jiZfP=B`vipg@`k%A zoBUOeoISOO->}-n-1A^zzv0uv^J6a_+2OjXAmA%)@Y zAR;61lbZvD+tpx{+c}2MMTE3GT#+KAT=_A{&(JKZQucbOozIdN8 zz`9XPm|}|g+*r;Xs!DNs9gldPigWmQ3p~+fZRVzm#gM!43g15Z&6?A&a_b9$U@cU< zjaz*Q>5?j`+=Ovwm08!|vE%5w{<5~XP>Jp`N<(-GJ&NJ)Gy=Xh18fU`b&YE+dmQQ1 z6-e~!G`ux*O`%%ETgbsDT>nHp8>$}1snRq8QO#7VcJ^7L&(TLVZE9;5R}>{Oc%yeB z_1PPcx&1mQj!e8{s61p-e5{PkE_H`_DjSGM@u_{*X8p$Zm+hk$)~@uJhVDHDyeK2H zrL?$|m%RF7S61xGjqSCty>_TTu5|_dE_oW;tGZHZFt@p9AE5=89lu1B`vpvsK3-?db1D#^C&2Upef zU8x#e^*DbmFt8r&1UpgfkJ>CdmA?HtXkWVD7yy|W=_?4}R47y672Ir2doMzksFN24 z$YB^ZYqNg+C4u|rg>~=7X+M2Fn9>qfxrsP1xl#qm0;Sa85c+Poygk7tMnJYiP6fsd zz|XP$P}n69;z@vY)Q@-Y05X}6A~BFdsw>hv_AqzCL5gVB5`4mJqmgZma(lJT;ZtiB zsggv3VUL@A`=wN=W>iL6Q30w2xbbG%!SwCdA$!|3P;xns_#QK`Ij;gam7SU=FD$pL zT=GJ|k-Sj9?&8q>^Wyr`OUK)|V`h9}WJE;qHeL#n@KBK4Dv;F%p$pq{9jW0F4EGPT zFYo}Mfvx2~l+%s#T&HKj_=Nxl>T&r2xCKPlLep}H#X$6MLtG^F-cBRiTF-rS57^Co zjH-?AsTWkX(mC2H3Q(=v$(wPf)3;p%HJ1a|bssmEhfB*!3h0KPu;7Cd;1f~SX8oFr zD(;(?{&H23E66vP*vpON0Y-s9DF|fwL6@!_#&u$yP`#}g*&6jQ{D&yc0D?{#?QVv&Fh45kr zqDmAa6T-w_uaBf(PTBm7r(3EffywUm`MJid=jMO*?dHEjh4Q0Bgj^l z$yj1N%KA%n^R|QmNbBREDH{RY2tbLb8T$d+i1fJopdQtsC;-<%!%sAAzqL{hm0GD} zrPf=i5)q|>QExS*lI>MHWk0ME)#^d`)w;%-NnL7s=m7xrtN#g31fj+N&hpc|^YG&b zK5qDFF!+e8*GAee%YXQby2abrzel#Bp#G3k=qG{;cnBb&V|Zv7hi?8bMx!yt0Bvp1 ztJIt!2movmh;9SJ2LU82+d6Fra4*v19z^2!NPgX_0vh}Yq&2rS(&gB$eHb=YNcMN@ zn%WADw37D4zw#ZBq5=v72{V%+2bw*3Vbri=*{heHjb)Gi%o9+2Rhn|IJrtQDxUm6U z(dQiikBHiNO%fLU<1q{Ya2o~g+}5WWW{z6S;%bvTB^v~KA7G$`l-6C==L z{m_&HNN0}Z>z3uO1zTpU^cGON6>*g&tww+(Ri4&1)~Y{tv48n4z_}6c<8XI5-CE?A z71;qV9zav{(#OpO_`15pRcq?nCrcjQ=AQ^Kv@6ORXv#s36N(DYv*{GnxP~`B`hI!U z6*2jRfF{C!Q7C{Syw62@4PJtAbD*%|Q#ZXu9soHI%RUTYvahAm(-6zvVw7!&f9)aY zYAj#3tG-#A{c`v)h^sEBd!Ow3!1*}ozqjUrrqXzuir^*>Th7nx=IpJ5H(x2QHebc( zG2^V!)|=;9K;yc9EVN4)asUq_ApN*hcce*^z$2BORlk4La8;* zn4bdN6vzH}K#mJ|tXw=tZvBY7!m5QIZlAeT3FR2U{xrN*cB(XK(&TSzyGh*2Hy!~4 zfz+7yRQ4KykGS&urtFV75BxB41;-xnod0XSr{{8b`_q3TudGM`=yO2qStC0S5p4BX zT(LXMC}Jx(z;mFHCuHYIlO|1CJ426U>Nmb`OmFu{U|2SJVG+~#=kRG(*s9IyBC5JeDzz!)U(NRuW_nluSLjWFe#j~c~DRkbEro14j<6~t!T`><#5 zu@hu$Fl2+8f}&%GC$lt#E^zGj>*UoWO`0@m(j@dW+SG6U0K}Cd)u+;|&5aIJuGXgg z>qDL(;|Dd)*q_4Zhy(zJ{?A-5uOw;Gq)C$|p{H3)x$lRD>Z2$XgHOHl?$pvIf8{%Q zfboMGC;XUzWGO`?FmT6>@(PkBO`0@m5_*~ze(vAw0GuGn3&quI+O|K&|Kqnkp(g*= z%;f6`h9<)BT{p?AN18Ng(xge~X$koG_KysegLB^;KDDZu)vPvVpAB+&$*xbex7rkf6lYpCXkDaR~acNC@$VI&^JF@4bF|8 z_H4FXuZNs1lqhqEF2(CXzLm2LgRg-Nih_cP4IvT}IN05%@5JLo&cP*oQnFBvDe zkF%>xpqK9%nu0<-Q1#69az#4<0=*F4DAhm>;lB{7Xa0E@BnPoG=%S?(Y~r6P(VO{On`!n57HeZr=p?)l9dO^ z%S)djq)|cMXs1AFZ%^w?}~CkdibI}e7phYyiU$OerOG0;d7yX$KQ3LJ>33Z z$s6@gTW1Y|&QCycGP0n5xzAG7&!eiQNDtRD<#T>bIrYDg{}cO1M;&x7{$IuX-RWP^ zvsN`3)j|JWn7e=1x~%=|45wY$ zmS$DFTH}GX5_qd{*=jhou&TWHG+p4zY1?w$at-(g@E~9qJS=1v8dS0TsSpBw@!R{C z4|ltHvGAe5gTTwBjqLIKuGc3H6+JDijZcBOV@;Rew|kA@J1@L=75%zuH@O3EDD|`- zcfH= zvDO>5S#d`6Uo@bQ$1UBl$+p{U!ye=$X+q#})zSpRMKobqco2Vj^9UDewpnhM{l{g);vvf?CK8=m-9e7fWK7l38*mF)v zn2*a33r1QHJ%hBBUOe;>nRi;=C>+aqkfq7#`2hMd96}~zix0c#=8Fnrz!H4|RgctKGb$5BRJNK{J5iar;#(b+PTntzc4X)p#@dniE;#H{M@R-^ zmhsUXn*uoMa;8OOYjqDDnD9r;i9L1ZM8Z-;S+FlAyltS0&&?$pq$O?m{Q+v!IpmV^ zF6zOfxkr8blT4YWM!`vzbF~^aWqOu_LXAY+?h=mLfc&Y<6_B~Y=)#k1hE%@a{E3JAufcwqLyl z9tv6a-3Q<8k$C!8=y2-SNJ}*tmAl^yS1z!c3PetuMDb0uV^(Y;O4avS2;)7r9hw

#&rT-`9^r10fD&M$YIXw5$A zy6!_J0B&qZwn7x3znjwV&T*C(Na5X1P2^MWc3r zThlMk%C z!A&K8*t-$8OXW=B0nPyr!iSUN!tEB=_F9Eq!0_Lb_#F55rjtYA)uY6k6uk{^wghp{ zHN;ZPkLH-U)rL-wkI&f($n(N+uOj3JdM>MSQ7tuu(yu`U<5DO|t_tJFVI@za#LBDW zxB-;(&tyw_@A^^?m2xoeMaO-cqru)vS7}}Fm1Yf10*2XMG3in;wN5dgqTF9udav)Q zp;?S6Vas$8IULm7N65^Z0Tbr+{pxDNHPa-;&DlIcDDF*3WVXVZjOs7)*QR}NPN*l~ zb9~o`JS_^5Ack?-;c#}})!J-jxPwT@<*sMAXC!gOfHH$;y_dxVxpZW}B*wSpKjW9$7QmgN^79 zcig}>Vl&y>qoY!VsZ*eMq^P{9_}%A9X$5Jn4*8G5ijwaWlYC)mghakF}7yCc2g2Ri0?W$SwLLW5x%8$ee~1>i{~7X{l|MO$D#r zlwYrMnJz&l73!(fzLi~scM)6NyYD((sO;?0lI|rnTOFSOzvpj@?lhn;?HZi+*(Jfu z$D^Yz2R|Z%xBZ9Pg;{P?DVN?iGnkLFqS7Q6N*`iVno~yYNTkQwAe*nZ(P$qRa`U}XfLBooYrva9a zL0n#@v$aFjB`tIWYDXSuI8P~$*bYZ}k={*_?DXeB!77=LVFB+X`6N9zJ|>AzL>`K& zA3N;i-Z;h&U*oaa*=#;a72|GJY$UPD(o^^GqnnnegmsgyVrAkxfX;Bv*C%|`x}_Q$ zGI7hr6TC!mu#!RY2Yb$0mm<11Pu+!d0)D(RqESiPesdrYCGoJ#u+gPW@Un))T;HGW z>Lyg8^jHDdm%7-12Mw#FC&*!4&DFiCWbx0X#pp?l4z2;Ep6lSC8_L;P6V_TDox3J? z@cY67U3JgZJVb(d=lJzZ$XJ|qscLWok{*eeR4Z6;F}kU?m9pcv4v`A~43bAf_tI;4 z+Cx9;ap#ie<9}{G`sPybL5|W(=>}BlXf17-*+=Qcq0VZcZq9Uq5@nNI##?J;EtobW zMV9Ekk-414+0reVS+QnD%%b=L6bef+19C(;ny7_Q<3?F<)K(v+@rJdt9aIjIgvM7` z@L0U|uSdpAh$98=_al{nLorEZ+m;V^z+v8BYp-dCxyVcLG{tXl$vKJ)9u5dYL|H5Y z6wPm)AP%#ImF!=4{9(lU1+ivAYIoo~tyHrLu1p=$YPx;S4BS4{*k`! zHHL1N?SYhA+I2k~qieO_X2UZ(N&D}BJ;Ch<$8tUSV$VZ7jB-tVVJA8{tra=$t^pzJi-XN>sHKbvEb-jh9<-NXU|Yc~)CZ^$NtN!ydg~VDM&-^SGk%r}w2NR|GvR zr0Xa>a*3#D4Pd1pZ>iQ|-f#RHfp?%SH|cP(@}!n|28J~^GTFLhNfHMNX}M`vMhmnd z%}`|&U2L8t4}qifu&ytLq#;v2wOkW{8=NXunN=J6TI>f~)1@{?$BX`!X^guu;N&>b zkn%~33;MD1tWB^1fv)9E3}0R>R4HGCgF%Ud8T~Ec zl|+bZdnVJ+2^|eed`Wm*J!+_(0Iq0_Sv6IdCLc;=sAjLJ%TRqZ zH#;JHG^b%;CSxbu-1cs;z;Sn;aumQD)N+5eNOjPGFO_MPcLXF=uFES<>LFa`YrkLc zOwu}k8o%IemM%Ie%-v?Zn;%Ljy!4g!l1%673)ONvn7IAulOQvepR7#*-kf4vQT6+` zbC_-q?(uvvch)1o{MCQH7GXKTpv>L&+Ai9{-8^JLg%HxJQ*8BCvpDu Nz6CbXf352n^&jPas;U40 literal 0 HcmV?d00001 diff --git a/images/spike.png b/images/spike.png new file mode 100644 index 0000000000000000000000000000000000000000..1f0392703b91ddcc49f5fd47707ee5f82eb51021 GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^4nQox!3HFkJ+IURQfx`y?k)^63>6GJ4s#z~1r*^d z@Q5sCV9-+rVaAH3_GLgp_7YEDSN6wDG7KE@mAC$z0fi(>Tq8=H^K)}k^GX<;i&7Iy zQd1PlGfOfQ+&z5*!W;R-fr?Ihx;TbJ9DaHs){7}oqU~Y+w1ljkhSwEEqNZ?W-AK9> znqm65vXMP*ZiPXO0>?QEd%F+w_J0>WB-gzJ=J^i-W zdx;h+p1a&iSG4V%Dl{Y$yF=eTQf@Ka=hbmOOLo5Vi^L!Ur#ACDt!i%?K12vOZGOb< zc49+LOijy&miMc>?S&W0eza_{o9FLxVnc>6P_$|O>h6AlW$pr(!j8LhtQU0Zs(k&p tUm-+len>}ww3CEf-4BmRDr+D0*YjU$mRqrLsVC4|44$rjF6*2UngED9fl>ef literal 0 HcmV?d00001 diff --git a/images/waiting.gif b/images/waiting.gif new file mode 100644 index 0000000000000000000000000000000000000000..ba2fbbcf203d31269617a75a0bf1fb308226791d GIT binary patch literal 2506 zcmZ?wbhEHblwe?E_`tyM_3PKWcke3xWMSoEU|>-Erx27{oLQBcqEMb$lA(}Vm7bZV zmzKn5}}iTChVu6RA|KzIH& zSM7a1uktG6C*(c~1R4#r0;m9?wh_p=c=008b|8aEriWkiH0K$ yU{WPbE6~s6Y6XTixmtnoOs-Z$`Xbq{i29mjt%!C4$yyQZ6Oy$e+C})ZS_1&{Irq5$ literal 0 HcmV?d00001 diff --git a/jami-qt.pro b/jami-qt.pro new file mode 100644 index 00000000..13a17562 --- /dev/null +++ b/jami-qt.pro @@ -0,0 +1,186 @@ +win32-msvc { + TARGET = Jami + TEMPLATE = vcapp + + QT += core winextras qml quickcontrols2 quick xml multimedia network webengine svg sql + + CONFIG += suppress_vcproj_warnings c++17 qtquickcompiler + + QTQUICK_COMPILER_SKIPPED_RESOURCES += ./ressources.qrc + + # compiler options + QMAKE_CXXFLAGS += /wd"4068" /wd"4099" /wd"4189" /wd"4267" /wd"4577" /wd"4467" /wd"4715" /wd"4828" + QMAKE_CXXFLAGS += /MP /GS /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /Zc:inline /fp:precise /errorReport:prompt + QMAKE_CXXFLAGS += /Gd /Oi /MD /std:c++17 /FC /EHsc /nologo /sdl + + # linker options + QMAKE_LFLAGS+= /ignore:4006,4049,4078,4098 /FORCE:MULTIPLE /INCREMENTAL:NO /Debug /LTCG /NODEFAULTLIB:LIBCMT + + # preprocessor defines + DEFINES += UNICODE QT_NO_DEBUG NDEBUG + + # dependencies + LRC= ../lrc + DRING= ../daemon + QRENCODE= qrencode-win32/qrencode-win32 + + # client deps + INCLUDEPATH += $${QRENCODE} + LIBS += $${QRENCODE}/vc8/qrcodelib/x64/Release-Lib/qrcodelib.lib + + # lrc + INCLUDEPATH += $${LRC}/src/ + LIBS += $${LRC}/msvc/release/ringclient_static.lib + LIBS += $${LRC}/msvc/src/qtwrapper/Release/qtwrapper.lib + + # daemon + INCLUDEPATH += ../daemon/contrib/msvc/include/ + LIBS += $${DRING}/build-local/x64/ReleaseLib_win32/bin/dring.lib + LIBS += $${DRING}/contrib/msvc/lib/x64/libgnutls.lib + + # windows system libs + LIBS += Shell32.lib Ole32.lib Advapi32.lib Shlwapi.lib User32.lib Gdi32.lib Crypt32.lib Strmiids.lib + + # output paths + OBJECTS_DIR = obj/.obj + MOC_DIR = obj/.moc + RCC_DIR = obj/.rcc + UI_DIR = obj/.ui + + # ReleaseCompile config + contains(CONFIG, ReleaseCompile) { + CONFIG(ReleaseCompile) { + message(ReleaseCompile config enabled) + Release: DEFINES += COMPILE_ONLY + } + } + + # beta config + contains(CONFIG, Beta) { + CONFIG(Beta) { + message(Beta config enabled) + Release: DESTDIR = x64/Beta + Release: DEFINES += BETA + } + } else { + Release: DESTDIR = x64/Release + } + Debug: DESTDIR = x64/Debug + + # qt dir + QMAKE_INCDIR_QT=$(QTDIR)\include + QMAKE_LIBDIR=$(QTDIR)\lib + QMAKE_MOC=$(QTDIR)\bin\moc.exe + QMAKE_QMAKE=$(QTDIR)\bin\qmake.exe + + # exe icons + Release: RC_FILE = ico.rc +} + +unix { + TARGET = jami-qt + TEMPLATE = app + + QT += quick quickwidgets widgets xml multimedia multimediawidgets network webenginewidgets svg quickcontrols2 webengine webenginecore sql dbus + + #check Qt version + QT_VERSION = $$[QT_VERSION] + QT_VERSION = $$split(QT_VERSION, ".") + QT_VER_MAJ = $$member(QT_VERSION, 0) + QT_VER_MIN = $$member(QT_VERSION, 1) + + lessThan(QT_VER_MIN, 12) { + QMAKE_CXXFLAGS += -std=c++17 + } + greaterThan(QT_VER_MIN, 12) | equals(QT_VER_MIN, 12) { + CONFIG += c++17 + } + + isEmpty(LRC) { LRC=../../install/lrc/ } + + INCLUDEPATH += $${LRC}/include/libringclient + INCLUDEPATH += $${LRC}/include + INCLUDEPATH += ../src + + LIBS += -L$${LRC}/lib -lringclient + LIBS += -lqrencode +} + +# Input +HEADERS += ./src/smartlistmodel.h \ + ./src/utils.h \ + ./src/bannedlistmodel.h \ + ./src/version.h \ + ./src/accountlistmodel.h \ + ./src/runguard.h \ + ./src/lrcinstance.h \ + ./src/globalsystemtray.h \ + ./src/settingskey.h \ + ./src/webchathelpers.h \ + ./src/pixbufmanipulator.h \ + ./src/rendermanager.h \ + ./src/connectivitymonitor.h \ + ./src/jamiavatartheme.h \ + ./src/mainapplication.h \ + ./src/qrimageprovider.h \ + ./src/messagesadapter.h \ + ./src/accountadapter.h \ + ./src/tintedbuttonimageprovider.h \ + ./src/calladapter.h \ + ./src/conversationsadapter.h \ + ./src/distantrenderer.h \ + ./src/previewrenderer.h \ + ./src/qmladapterbase.h \ + ./src/avadapter.h \ + ./src/contactadapter.h \ + ./src/settingsadaptor.h \ + ./src/deviceitemlistmodel.h \ + ./src/pluginitemlistmodel.h \ + ./src/preferenceitemlistmodel.h \ + ./src/audiocodeclistmodel.h \ + ./src/videocodeclistmodel.h \ + ./src/accountstomigratelistmodel.h \ + ./src/clientwrapper.h \ + ./src/audioinputdevicemodel.h \ + ./src/videoinputdevicemodel.h \ + ./src/audiooutputdevicemodel.h \ + ./src/videoformatfpsmodel.h \ + ./src/videoformatresolutionmodel.h \ + ./src/audiomanagerlistmodel.h +SOURCES += ./src/bannedlistmodel.cpp \ + ./src/accountlistmodel.cpp \ + ./src/runguard.cpp \ + ./src/webchathelpers.cpp \ + ./src/main.cpp \ + ./src/globalsystemtray.cpp \ + ./src/smartlistmodel.cpp \ + ./src/utils.cpp \ + ./src/pixbufmanipulator.cpp \ + ./src/rendermanager.cpp \ + ./src/connectivitymonitor.cpp \ + ./src/mainapplication.cpp \ + ./src/messagesadapter.cpp \ + ./src/accountadapter.cpp \ + ./src/calladapter.cpp \ + ./src/conversationsadapter.cpp \ + ./src/distantrenderer.cpp \ + ./src/previewrenderer.cpp \ + ./src/qmladapterbase.cpp \ + ./src/avadapter.cpp \ + ./src/contactadapter.cpp \ + ./src/settingsadaptor.cpp \ + ./src/deviceitemlistmodel.cpp \ + ./src/pluginitemlistmodel.cpp \ + ./src/preferenceitemlistmodel.cpp \ + ./src/audiocodeclistmodel.cpp \ + ./src/videocodeclistmodel.cpp \ + ./src/accountstomigratelistmodel.cpp \ + ./src/clientwrapper.cpp \ + ./src/audioinputdevicemodel.cpp \ + ./src/videoinputdevicemodel.cpp \ + ./src/audiooutputdevicemodel.cpp \ + ./src/videoformatfpsmodel.cpp \ + ./src/videoformatresolutionmodel.cpp \ + ./src/audiomanagerlistmodel.cpp +RESOURCES += ./ressources.qrc \ + ./qml.qrc diff --git a/jami-qt.sln b/jami-qt.sln new file mode 100644 index 00000000..1ae2b969 --- /dev/null +++ b/jami-qt.sln @@ -0,0 +1,636 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ring-daemon", "..\daemon\build-local\ring-daemon.vcxproj", "{1CA4822C-F599-3879-A78F-332D823E4871}" + ProjectSection(ProjectDependencies) = postProject + {2BB84911-C1B4-4747-B93D-36AA82CC5031} = {2BB84911-C1B4-4747-B93D-36AA82CC5031} + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} = {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} + {B82CDD25-6903-430E-BD38-D8129A2015C1} = {B82CDD25-6903-430E-BD38-D8129A2015C1} + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} + {711397CE-E5D5-467D-9457-8716C047E50C} = {711397CE-E5D5-467D-9457-8716C047E50C} + {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {B8719FD5-E8A6-4A36-943C-891D07F5DD21} + {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {DA0E03ED-53A7-4050-8A85-90541C5509F8} + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ringclient_static", "..\lrc\msvc\ringclient_static.vcxproj", "{774F1C61-6659-36E6-9519-17A753890576}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qtwrapper", "..\lrc\msvc\src\qtwrapper\qtwrapper.vcxproj", "{2CFF20AA-9341-3564-A044-C38E70D5372A}" + ProjectSection(ProjectDependencies) = postProject + {1CA4822C-F599-3879-A78F-332D823E4871} = {1CA4822C-F599-3879-A78F-332D823E4871} + EndProjectSection +EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "JamiInstaller", "JamiInstaller\JamiInstaller.wixproj", "{DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "daemon", "daemon", "{B6752729-7398-46FA-9CF0-DC854C6AB8CA}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjnath", "..\daemon\contrib\build\pjproject\pjnath\build\pjnath.vcxproj", "{A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pjproject", "pjproject", "{14A77BA7-0E23-4D8F-A74F-5D5D5583035D}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia", "..\daemon\contrib\build\pjproject\pjmedia\build\pjmedia.vcxproj", "{7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjmedia_codec", "..\daemon\contrib\build\pjproject\pjmedia\build\pjmedia_codec.vcxproj", "{855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib_util", "..\daemon\contrib\build\pjproject\pjlib-util\build\pjlib_util.vcxproj", "{FE07F272-AE7F-4549-9E9F-EF9B80CB1693}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjlib", "..\daemon\contrib\build\pjproject\pjlib\build\pjlib.vcxproj", "{DA0E03ED-53A7-4050-8A85-90541C5509F8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_core", "..\daemon\contrib\build\pjproject\pjsip\build\pjsip_core.vcxproj", "{2BB84911-C1B4-4747-B93D-36AA82CC5031}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsua2_lib", "..\daemon\contrib\build\pjproject\pjsip\build\pjsua2_lib.vcxproj", "{B82CDD25-6903-430E-BD38-D8129A2015C1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsua_lib", "..\daemon\contrib\build\pjproject\pjsip\build\pjsua_lib.vcxproj", "{9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_simple", "..\daemon\contrib\build\pjproject\pjsip\build\pjsip_simple.vcxproj", "{4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pjsip_ua", "..\daemon\contrib\build\pjproject\pjsip\build\pjsip_ua.vcxproj", "{B8719FD5-E8A6-4A36-943C-891D07F5DD21}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lrc", "lrc", "{31F92F98-3580-445D-A3DA-D081B427A562}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "client", "client", "{1ECB5BD6-B3A0-4135-BA3F-48FC367B61C9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "contrib", "contrib", "{B4FD804C-ABD9-4F54-858B-911F0119580C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "opendht", "..\daemon\contrib\build\opendht\MSVC\opendht.vcxproj", "{711397CE-E5D5-467D-9457-8716C047E50C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jami-qt", "jami-qt.vcxproj", "{A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}" + ProjectSection(ProjectDependencies) = postProject + {774F1C61-6659-36E6-9519-17A753890576} = {774F1C61-6659-36E6-9519-17A753890576} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Beta|Any CPU = Beta|Any CPU + Beta|x64 = Beta|x64 + Beta|x86 = Beta|x86 + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + ReleaseCompile|Any CPU = ReleaseCompile|Any CPU + ReleaseCompile|x64 = ReleaseCompile|x64 + ReleaseCompile|x86 = ReleaseCompile|x86 + ReleaseLib_win32|Any CPU = ReleaseLib_win32|Any CPU + ReleaseLib_win32|x64 = ReleaseLib_win32|x64 + ReleaseLib_win32|x86 = ReleaseLib_win32|x86 + ReleaseLib|Any CPU = ReleaseLib|Any CPU + ReleaseLib|x64 = ReleaseLib|x64 + ReleaseLib|x86 = ReleaseLib|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1CA4822C-F599-3879-A78F-332D823E4871}.Beta|Any CPU.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Beta|Any CPU.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Beta|x64.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Beta|x64.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Beta|x86.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Beta|x86.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Debug|Any CPU.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Debug|Any CPU.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Debug|x64.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Debug|x64.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Debug|x86.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Debug|x86.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Release|Any CPU.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Release|Any CPU.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Release|x64.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Release|x64.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Release|x86.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.Release|x86.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseCompile|Any CPU.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseCompile|Any CPU.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseCompile|x64.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseCompile|x64.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseCompile|x86.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseCompile|x86.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib_win32|Any CPU.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib_win32|x64.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib_win32|x64.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib_win32|x86.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib|Any CPU.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib|Any CPU.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib|x64.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib|x64.Build.0 = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib|x86.ActiveCfg = ReleaseLib_win32|x64 + {1CA4822C-F599-3879-A78F-332D823E4871}.ReleaseLib|x86.Build.0 = ReleaseLib_win32|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Beta|Any CPU.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Beta|x64.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Beta|x64.Build.0 = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Beta|x86.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Debug|Any CPU.ActiveCfg = Debug|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Debug|x64.ActiveCfg = Debug|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Debug|x64.Build.0 = Debug|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Debug|x86.ActiveCfg = Debug|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Release|Any CPU.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Release|x64.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Release|x64.Build.0 = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.Release|x86.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseCompile|Any CPU.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseCompile|Any CPU.Build.0 = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseCompile|x64.Build.0 = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseCompile|x86.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseCompile|x86.Build.0 = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib_win32|Any CPU.ActiveCfg = RelWithDebInfo|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib_win32|Any CPU.Build.0 = RelWithDebInfo|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib_win32|x86.ActiveCfg = RelWithDebInfo|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib_win32|x86.Build.0 = RelWithDebInfo|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib|Any CPU.ActiveCfg = RelWithDebInfo|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib|Any CPU.Build.0 = RelWithDebInfo|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib|x64.ActiveCfg = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib|x64.Build.0 = Release|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib|x86.ActiveCfg = MinSizeRel|x64 + {774F1C61-6659-36E6-9519-17A753890576}.ReleaseLib|x86.Build.0 = MinSizeRel|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Beta|Any CPU.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Beta|x64.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Beta|x64.Build.0 = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Beta|x86.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Debug|Any CPU.ActiveCfg = Debug|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Debug|x64.ActiveCfg = Debug|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Debug|x64.Build.0 = Debug|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Debug|x86.ActiveCfg = Debug|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Release|Any CPU.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Release|x64.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Release|x64.Build.0 = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.Release|x86.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseCompile|Any CPU.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseCompile|Any CPU.Build.0 = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseCompile|x64.Build.0 = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseCompile|x86.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseCompile|x86.Build.0 = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib_win32|Any CPU.ActiveCfg = RelWithDebInfo|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib_win32|Any CPU.Build.0 = RelWithDebInfo|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib_win32|x86.ActiveCfg = RelWithDebInfo|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib_win32|x86.Build.0 = RelWithDebInfo|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib|Any CPU.ActiveCfg = RelWithDebInfo|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib|Any CPU.Build.0 = RelWithDebInfo|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib|x64.ActiveCfg = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib|x64.Build.0 = Release|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib|x86.ActiveCfg = MinSizeRel|x64 + {2CFF20AA-9341-3564-A044-C38E70D5372A}.ReleaseLib|x86.Build.0 = MinSizeRel|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Beta|Any CPU.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Beta|x64.ActiveCfg = Beta|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Beta|x64.Build.0 = Beta|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Beta|x86.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Debug|Any CPU.ActiveCfg = Beta|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Debug|Any CPU.Build.0 = Beta|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Debug|x64.ActiveCfg = Beta|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Debug|x64.Build.0 = Beta|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Debug|x86.ActiveCfg = Beta|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Debug|x86.Build.0 = Beta|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Release|Any CPU.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Release|x64.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Release|x64.Build.0 = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.Release|x86.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseCompile|Any CPU.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseCompile|Any CPU.Build.0 = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseCompile|x64.Build.0 = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseCompile|x86.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseCompile|x86.Build.0 = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib_win32|Any CPU.Build.0 = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib_win32|x86.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib_win32|x86.Build.0 = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib|Any CPU.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib|Any CPU.Build.0 = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib|x64.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib|x64.Build.0 = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib|x86.ActiveCfg = Release|x64 + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48}.ReleaseLib|x86.Build.0 = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Beta|Any CPU.ActiveCfg = Release|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Beta|x64.ActiveCfg = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Beta|x64.Build.0 = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Beta|x86.ActiveCfg = Release|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Beta|x86.Build.0 = Release|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Debug|x64.ActiveCfg = Debug|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Debug|x64.Build.0 = Debug|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Debug|x86.ActiveCfg = Debug|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Debug|x86.Build.0 = Debug|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Release|Any CPU.ActiveCfg = Release|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Release|x64.ActiveCfg = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Release|x64.Build.0 = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Release|x86.ActiveCfg = Release|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.Release|x86.Build.0 = Release|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseCompile|x64.Build.0 = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib|x64.ActiveCfg = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib|x64.Build.0 = Release|x64 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Beta|Any CPU.ActiveCfg = Release|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Beta|x64.ActiveCfg = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Beta|x64.Build.0 = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Beta|x86.ActiveCfg = Release|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Beta|x86.Build.0 = Release|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Debug|x64.ActiveCfg = Debug|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Debug|x64.Build.0 = Debug|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Debug|x86.ActiveCfg = Debug|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Debug|x86.Build.0 = Debug|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Release|Any CPU.ActiveCfg = Release|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Release|x64.ActiveCfg = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Release|x64.Build.0 = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Release|x86.ActiveCfg = Release|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.Release|x86.Build.0 = Release|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseCompile|x64.Build.0 = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib|x64.ActiveCfg = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib|x64.Build.0 = Release|x64 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Beta|Any CPU.ActiveCfg = Release|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Beta|x64.ActiveCfg = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Beta|x64.Build.0 = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Beta|x86.ActiveCfg = Release|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Beta|x86.Build.0 = Release|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Debug|x64.ActiveCfg = Debug|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Debug|x64.Build.0 = Debug|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Debug|x86.ActiveCfg = Debug|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Debug|x86.Build.0 = Debug|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Release|Any CPU.ActiveCfg = Release|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Release|x64.ActiveCfg = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Release|x64.Build.0 = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Release|x86.ActiveCfg = Release|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.Release|x86.Build.0 = Release|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseCompile|x64.Build.0 = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib|x64.ActiveCfg = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib|x64.Build.0 = Release|x64 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Beta|Any CPU.ActiveCfg = Release|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Beta|x64.ActiveCfg = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Beta|x64.Build.0 = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Beta|x86.ActiveCfg = Release|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Beta|x86.Build.0 = Release|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Debug|x64.ActiveCfg = Debug|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Debug|x64.Build.0 = Debug|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Debug|x86.ActiveCfg = Debug|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Debug|x86.Build.0 = Debug|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Release|Any CPU.ActiveCfg = Release|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Release|x64.ActiveCfg = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Release|x64.Build.0 = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Release|x86.ActiveCfg = Release|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.Release|x86.Build.0 = Release|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseCompile|x64.Build.0 = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib|x64.ActiveCfg = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib|x64.Build.0 = Release|x64 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Beta|Any CPU.ActiveCfg = Release|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Beta|x64.ActiveCfg = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Beta|x64.Build.0 = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Beta|x86.ActiveCfg = Release|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Beta|x86.Build.0 = Release|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Debug|x64.ActiveCfg = Debug|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Debug|x64.Build.0 = Debug|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Debug|x86.ActiveCfg = Debug|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Debug|x86.Build.0 = Debug|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Release|Any CPU.ActiveCfg = Release|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Release|x64.ActiveCfg = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Release|x64.Build.0 = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Release|x86.ActiveCfg = Release|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.Release|x86.Build.0 = Release|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseCompile|x64.Build.0 = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib|x64.ActiveCfg = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib|x64.Build.0 = Release|x64 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {DA0E03ED-53A7-4050-8A85-90541C5509F8}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Beta|Any CPU.ActiveCfg = Release|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Beta|x64.ActiveCfg = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Beta|x64.Build.0 = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Beta|x86.ActiveCfg = Release|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Beta|x86.Build.0 = Release|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Debug|x64.ActiveCfg = Debug|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Debug|x64.Build.0 = Debug|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Debug|x86.ActiveCfg = Debug|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Debug|x86.Build.0 = Debug|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Release|Any CPU.ActiveCfg = Release|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Release|x64.ActiveCfg = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Release|x64.Build.0 = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Release|x86.ActiveCfg = Release|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.Release|x86.Build.0 = Release|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseCompile|x64.Build.0 = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib|x64.ActiveCfg = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib|x64.Build.0 = Release|x64 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {2BB84911-C1B4-4747-B93D-36AA82CC5031}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Beta|Any CPU.ActiveCfg = Release|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Beta|x64.ActiveCfg = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Beta|x64.Build.0 = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Beta|x86.ActiveCfg = Release|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Beta|x86.Build.0 = Release|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|x64.ActiveCfg = Debug|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|x64.Build.0 = Debug|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|x86.ActiveCfg = Debug|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Debug|x86.Build.0 = Debug|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|Any CPU.ActiveCfg = Release|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|x64.ActiveCfg = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|x64.Build.0 = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|x86.ActiveCfg = Release|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.Release|x86.Build.0 = Release|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseCompile|x64.Build.0 = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib|x64.ActiveCfg = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib|x64.Build.0 = Release|x64 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {B82CDD25-6903-430E-BD38-D8129A2015C1}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Beta|Any CPU.ActiveCfg = Release|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Beta|x64.ActiveCfg = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Beta|x64.Build.0 = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Beta|x86.ActiveCfg = Release|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Beta|x86.Build.0 = Release|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Debug|x64.ActiveCfg = Debug|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Debug|x64.Build.0 = Debug|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Debug|x86.ActiveCfg = Debug|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Debug|x86.Build.0 = Debug|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Release|Any CPU.ActiveCfg = Release|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Release|x64.ActiveCfg = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Release|x64.Build.0 = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Release|x86.ActiveCfg = Release|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.Release|x86.Build.0 = Release|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseCompile|x64.Build.0 = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib|x64.ActiveCfg = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib|x64.Build.0 = Release|x64 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Beta|Any CPU.ActiveCfg = Release|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Beta|x64.ActiveCfg = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Beta|x64.Build.0 = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Beta|x86.ActiveCfg = Release|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Beta|x86.Build.0 = Release|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Debug|x64.ActiveCfg = Debug|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Debug|x64.Build.0 = Debug|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Debug|x86.ActiveCfg = Debug|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Debug|x86.Build.0 = Debug|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Release|Any CPU.ActiveCfg = Release|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Release|x64.ActiveCfg = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Release|x64.Build.0 = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Release|x86.ActiveCfg = Release|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.Release|x86.Build.0 = Release|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseCompile|x64.Build.0 = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib|x64.ActiveCfg = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib|x64.Build.0 = Release|x64 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Beta|Any CPU.ActiveCfg = Release|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Beta|x64.ActiveCfg = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Beta|x64.Build.0 = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Beta|x86.ActiveCfg = Release|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Beta|x86.Build.0 = Release|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Debug|x64.ActiveCfg = Debug|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Debug|x64.Build.0 = Debug|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Debug|x86.ActiveCfg = Debug|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Debug|x86.Build.0 = Debug|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Release|Any CPU.ActiveCfg = Release|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Release|x64.ActiveCfg = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Release|x64.Build.0 = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Release|x86.ActiveCfg = Release|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.Release|x86.Build.0 = Release|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseCompile|Any CPU.ActiveCfg = Release|ARM + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseCompile|Any CPU.Build.0 = Release|ARM + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseCompile|x64.Build.0 = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseCompile|x86.ActiveCfg = Release-Static|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseCompile|x86.Build.0 = Release-Static|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|ARM + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib_win32|Any CPU.Build.0 = Release|ARM + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib_win32|x86.ActiveCfg = Release-Static|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib_win32|x86.Build.0 = Release-Static|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib|Any CPU.ActiveCfg = Release|ARM + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib|Any CPU.Build.0 = Release|ARM + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib|x64.ActiveCfg = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib|x64.Build.0 = Release|x64 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib|x86.ActiveCfg = Release-Static|Win32 + {B8719FD5-E8A6-4A36-943C-891D07F5DD21}.ReleaseLib|x86.Build.0 = Release-Static|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.Beta|Any CPU.ActiveCfg = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.Beta|x64.ActiveCfg = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.Beta|x64.Build.0 = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.Beta|x86.ActiveCfg = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.Beta|x86.Build.0 = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x64.ActiveCfg = Debug|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x64.Build.0 = Debug|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x86.ActiveCfg = Debug|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.Debug|x86.Build.0 = Debug|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.Release|Any CPU.ActiveCfg = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.Release|x64.ActiveCfg = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.Release|x64.Build.0 = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.Release|x86.ActiveCfg = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.Release|x86.Build.0 = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseCompile|Any CPU.ActiveCfg = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseCompile|Any CPU.Build.0 = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseCompile|x64.Build.0 = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseCompile|x86.ActiveCfg = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseCompile|x86.Build.0 = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib_win32|Any CPU.Build.0 = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib_win32|x86.ActiveCfg = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib_win32|x86.Build.0 = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib|Any CPU.ActiveCfg = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib|Any CPU.Build.0 = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib|x64.ActiveCfg = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib|x64.Build.0 = Release|x64 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib|x86.ActiveCfg = Release|Win32 + {711397CE-E5D5-467D-9457-8716C047E50C}.ReleaseLib|x86.Build.0 = Release|Win32 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Beta|Any CPU.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Beta|Any CPU.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Beta|x64.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Beta|x64.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Beta|x86.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Beta|x86.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Debug|Any CPU.ActiveCfg = Debug|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Debug|x64.ActiveCfg = Debug|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Debug|x64.Build.0 = Debug|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Debug|x86.ActiveCfg = Debug|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Release|Any CPU.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Release|x64.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Release|x64.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.Release|x86.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseCompile|Any CPU.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseCompile|Any CPU.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseCompile|x64.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseCompile|x64.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseCompile|x86.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseCompile|x86.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib_win32|Any CPU.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib_win32|Any CPU.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib_win32|x64.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib_win32|x64.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib_win32|x86.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib_win32|x86.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib|Any CPU.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib|Any CPU.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib|x64.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib|x64.Build.0 = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib|x86.ActiveCfg = Release|x64 + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027}.ReleaseLib|x86.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1CA4822C-F599-3879-A78F-332D823E4871} = {B6752729-7398-46FA-9CF0-DC854C6AB8CA} + {774F1C61-6659-36E6-9519-17A753890576} = {31F92F98-3580-445D-A3DA-D081B427A562} + {2CFF20AA-9341-3564-A044-C38E70D5372A} = {31F92F98-3580-445D-A3DA-D081B427A562} + {DBBFBC55-1C20-4D21-AE3B-6E8B14C4FE48} = {1ECB5BD6-B3A0-4135-BA3F-48FC367B61C9} + {A5D9AA24-08ED-48B9-BD65-F0A25E96BFC4} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} = {B4FD804C-ABD9-4F54-858B-911F0119580C} + {7FDE3880-A4AB-49E3-B439-EBEF0A0C7A65} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {855DC8C0-D3E9-4A2E-AE47-116605A7BC9B} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {FE07F272-AE7F-4549-9E9F-EF9B80CB1693} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {DA0E03ED-53A7-4050-8A85-90541C5509F8} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {2BB84911-C1B4-4747-B93D-36AA82CC5031} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {B82CDD25-6903-430E-BD38-D8129A2015C1} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {9CA0FDFB-2172-41FC-B7F1-5CE915EDCB37} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {4B5945CD-0CB3-49AA-A7FF-7612D93F82C0} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {B8719FD5-E8A6-4A36-943C-891D07F5DD21} = {14A77BA7-0E23-4D8F-A74F-5D5D5583035D} + {B4FD804C-ABD9-4F54-858B-911F0119580C} = {B6752729-7398-46FA-9CF0-DC854C6AB8CA} + {711397CE-E5D5-467D-9457-8716C047E50C} = {B4FD804C-ABD9-4F54-858B-911F0119580C} + {A1BDC12C-5DF6-3E77-9793-AB9AB5C82027} = {1ECB5BD6-B3A0-4135-BA3F-48FC367B61C9} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5C2901E8-26FE-444A-A80E-02AF8147CD34} + EndGlobalSection +EndGlobal diff --git a/make-client.py b/make-client.py new file mode 100644 index 00000000..a496e2ee --- /dev/null +++ b/make-client.py @@ -0,0 +1,249 @@ +import tempfile +import re +import sys +import os +import subprocess +import platform +import argparse +import multiprocessing +import fileinput +import re + +# vs help +win_sdk_default = '10.0.16299.0' +win_toolset_default = 'v141' + +vs_where_path = os.path.join( + os.environ['ProgramFiles(x86)'], 'Microsoft Visual Studio', 'Installer', 'vswhere.exe' +) + +host_is_64bit = (False, True)[platform.machine().endswith('64')] + +def execute_cmd(cmd, with_shell=False, env_vars={}): + if(bool(env_vars)): + p = subprocess.Popen(cmd, shell=with_shell, + stdout=sys.stdout, + env=env_vars) + else: + p = subprocess.Popen(cmd, shell=with_shell) + _, _ = p.communicate() + return p.returncode + +def getLatestVSVersion(): + args = [ + '-latest', + '-products *', + '-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64', + '-property installationVersion' + ] + cmd = [vs_where_path] + args + output = subprocess.check_output(' '.join(cmd)).decode('utf-8') + if output: + return output.splitlines()[0].split('.')[0] + else: + return + + +def findVSLatestDir(): + args = [ + '-latest', + '-products *', + '-requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64', + '-property installationPath' + ] + cmd = [vs_where_path] + args + output = subprocess.check_output(' '.join(cmd)).decode('utf-8', errors='ignore') + if output: + return output.splitlines()[0] + else: + return + + +def findMSBuild(): + filename = 'MSBuild.exe' + for root, _, files in os.walk(findVSLatestDir() + r'\\MSBuild'): + if filename in files: + return os.path.join(root, filename) + + +def getVSEnv(arch='x64', platform='', version=''): + env_cmd = 'set path=%path:"=% && ' + \ + getVSEnvCmd(arch, platform, version) + ' && set' + p = subprocess.Popen(env_cmd, + shell=True, + stdout=subprocess.PIPE) + stdout, _ = p.communicate() + out = stdout.decode('utf-8', errors='ignore').split("\r\n")[5:-1] + return dict(s.split('=', 1) for s in out) + +def getVSEnvCmd(arch='x64', platform='', version=''): + vcEnvInit = [findVSLatestDir() + r'\VC\Auxiliary\Build\"vcvarsall.bat'] + if platform != '': + args = [arch, platform, version] + else: + args = [arch, version] + if args: + vcEnvInit.extend(args) + vcEnvInit = 'call \"' + ' '.join(vcEnvInit) + return vcEnvInit + +def build_project(msbuild, msbuild_args, proj, env_vars): + args = [] + args.extend(msbuild_args) + args.append(proj) + cmd = [msbuild] + cmd.extend(args) + if (execute_cmd(cmd, True, env_vars)): + print("Build failed when building ", proj) + sys.exit(1) + +def replace_vs_prop(filename, prop, val): + p = re.compile(r'(?s)<' + prop + r'\s?.*?>(.*?)<\/' + prop + r'>') + val = r'<' + prop + r'>' + val + r'' + with fileinput.FileInput(filename, inplace=True) as file: + for line in file: + print(re.sub(p, val, line), end='') + +def deps(arch, toolset, qtver): + print('Deps Qt Client Release|' + arch) + + # Fetch QRencode + print('Generate QRencode') + apply_cmd = "git apply --reject --ignore-whitespace --whitespace=fix" + qrencode_path = 'qrencode-win32' + if (os.path.isdir(qrencode_path)): + os.system('rmdir qrencode-win32 /s /q') + if (execute_cmd("git clone https://github.com/BlueDragon747/qrencode-win32.git", True)): + print("Git clone failed when cloning from https://github.com/BlueDragon747/qrencode-win32.git") + sys.exit(1) + if(execute_cmd("cd qrencode-win32 && git checkout d6495a2aa74d058d54ae0f1b9e9e545698de66ce && " + apply_cmd + ' ..\\qrencode-win32.patch', True)): + print("Qrencode-win32 set up error") + sys.exit(1) + + print('Building qrcodelib') + build(arch, '', '', 'Release-Lib', '\\qrencode-win32\\qrencode-win32\\vc8\\qrcodelib\\qrcodelib.vcxproj', qtver, False) + +def build(arch, toolset, sdk_version, config_str, project_path_under_current_path, qtver, force_option=True): + configuration_type = 'StaticLibrary' + + qtFolderDir = "msvc2017_64" + qtverSplit = qtver.split('.') + + if((int(qtverSplit[0]) >= 6) or((int(qtverSplit[0]) == 5) and (int(qtverSplit[1]) >= 15))): + qtFolderDir = "msvc2019_64" + + if (config_str == 'Release'): + print('Generating project using qmake ' + config_str + '|' + arch) + if(execute_cmd("C:\\Qt\\" + qtver + "\\" + qtFolderDir + "\\bin\\qmake.exe " + "-tp vc jami-qt.pro -o jami-qt.vcxproj")): + print("Qmake vcxproj file generate error") + sys.exit(1) + configuration_type = 'Application' + elif (config_str == 'Beta'): + print('Generating project using qmake ' + config_str + '|' + arch) + if(execute_cmd("C:\\Qt\\" + qtver + "\\" + qtFolderDir + "\\bin\\qmake.exe " + "-tp vc jami-qt.pro -o jami-qt.vcxproj CONFIG+=Beta")): + print("Beat: Qmake vcxproj file generate error") + sys.exit(1) + config_str = 'Release' + configuration_type = 'Application' + elif (config_str == 'ReleaseCompile'): + print('Generating project using qmake ' + config_str + '|' + arch) + if(execute_cmd("C:\\Qt\\" + qtver + "\\" + qtFolderDir + "\\bin\\qmake.exe " + "-tp vc jami-qt.pro -o jami-qt.vcxproj CONFIG+=ReleaseCompile")): + print("ReleaseCompile: Qmake vcxproj file generate error") + sys.exit(1) + config_str = 'Release' + + # Note: If project is configured to Beta or ReleaseCompile, the configuration name is still release, + # but will be outputted into x64/Beta folder (for Beta Only) + + print('Building projects in ' + config_str + '|' + arch) + vs_env_vars = {} + vs_env_vars.update(getVSEnv()) + this_dir = os.path.dirname(os.path.realpath(__file__)) + qt_client_proj_path = this_dir + project_path_under_current_path + + msbuild = findMSBuild() + if not os.path.isfile(msbuild): + raise IOError('msbuild.exe not found. path=' + msbuild) + msbuild_args = [ + '/nologo', + '/verbosity:minimal', + '/maxcpucount:' + str(multiprocessing.cpu_count()), + '/p:Platform=' + arch, + '/p:Configuration=' + config_str, + '/p:ConfigurationType=' + configuration_type, + '/p:useenv=true'] + if (toolset != ''): + msbuild_args.append('/p:PlatformToolset=' + toolset) + if (force_option): + # force toolset + replace_vs_prop(qt_client_proj_path, + 'PlatformToolset', + toolset) + # force unicode + replace_vs_prop(qt_client_proj_path, + 'CharacterSet', + 'Unicode') + # force sdk_version + replace_vs_prop(qt_client_proj_path, + 'WindowsTargetPlatformVersion', + sdk_version) + + build_project(msbuild, msbuild_args, qt_client_proj_path, vs_env_vars) + +def parse_args(): + ap = argparse.ArgumentParser(description="Windows Jami-lrc build tool") + ap.add_argument( + '-b', '--build', action='store_true', + help='Build Qt Client') + ap.add_argument( + '-a', '--arch', default='x64', + help='Sets the build architecture') + ap.add_argument( + '-d', '--deps', action='store_true', + help='Build Deps for Qt Client') + ap.add_argument( + '-bt', '--beta', action='store_true', + help='Build Qt Client in Beta Config') + ap.add_argument( + '-c', '--releasecompile', action='store_true', + help='Build Qt Client in ReleaseCompile Config') + ap.add_argument( + '-s', '--sdk', default=win_sdk_default, type=str, + help='Use specified windows sdk version') + ap.add_argument( + '-t', '--toolset', default=win_toolset_default, type=str, + help='Use specified platform toolset version') + ap.add_argument( + '-q', '--qtver', default='5.9.4', + help='Sets the version of Qmake') + + parsed_args = ap.parse_args() + + return parsed_args + + +def main(): + if not host_is_64bit: + print('These scripts will only run on a 64-bit Windows system for now!') + sys.exit(1) + + if int(getLatestVSVersion()) < 15: + print('These scripts require at least Visual Studio v15 2017!') + sys.exit(1) + + parsed_args = parse_args() + + if parsed_args.deps: + deps(parsed_args.arch, parsed_args.toolset, parsed_args.qtver) + + if parsed_args.build: + build(parsed_args.arch, parsed_args.toolset, parsed_args.sdk, 'Release', '\\jami-qt.vcxproj', parsed_args.qtver) + + if parsed_args.beta: + build(parsed_args.arch, parsed_args.toolset, parsed_args.sdk, 'Beta', '\\jami-qt.vcxproj', parsed_args.qtver) + + if parsed_args.releasecompile: + build(parsed_args.arch, parsed_args.toolset, parsed_args.sdk, 'ReleaseCompile', '\\jami-qt.vcxproj', parsed_args.qtver) + +if __name__ == '__main__': + main() diff --git a/projectcredits.html b/projectcredits.html new file mode 100644 index 00000000..e5f7b6bb --- /dev/null +++ b/projectcredits.html @@ -0,0 +1,40 @@ +

Alexandre Lision

+

Alexandr Sergheev

+

Adrien Béraud

+

Alexandre Viau

+

Aline Bonnet

+

Andreas Traczyk

+

Anthony Léonard

+

Cyrille Béraud

+

Dorina Mosku

+

Eden Abitbol

+

Édric Milaret

+

Éloi Bail

+

Emmanuel Lepage-Vallée

+

Frédéric Guimont

+

Guillaume Roguez

+

Hadrien De Sousa

+

Julien Grossholtz

+

Kateryna Kostiuk

+

Loïc Siret

+

Mingrui Zhang

+

Mohamed Amine Younes Bouacida

+

Nicolas Jäger

+

Nicolas Reynaud

+

Olivier Gregoire

+

Olivier Soldano

+

Patrick Keroulas

+

Philippe Gorley

+

Pierre Lespagnol

+

Rayan Osseiran

+

Romain Bertozzi

+

Sébastien Blin

+

Silbino Gonçalves Matado

+

Simon Désaulniers

+

Stepan Salenikovich

+

Simon Zeni

+

Thibault Wittemberg

+

Vsevolod Ivanov

+

Yang Wang

+

Marianne Forget

+


\ No newline at end of file diff --git a/qml.qrc b/qml.qrc new file mode 100644 index 00000000..d939b564 --- /dev/null +++ b/qml.qrc @@ -0,0 +1,96 @@ + + + src/settingsview/SettingsView.qml + src/settingsview/components/IconButton.qml + src/settingsview/components/LeftPanelView.qml + src/settingsview/components/AvSettingPage.qml + src/settingsview/components/GeneralSettingsPage.qml + src/settingsview/components/PluginSettingsPage.qml + src/settingsview/components/PluginListSettingsView.qml + src/settingsview/components/PluginListPreferencesView.qml + src/settingsview/components/CurrentAccountSettingsScrollPage.qml + src/settingsview/components/CurrentSIPAccountSettingScrollPage.qml + src/settingsview/components/ToggleSwitch.qml + src/settingsview/components/AdvancedSettingsView.qml + src/settingsview/components/AdvancedSIPSettingsView.qml + src/settingsview/components/LevelMeter.qml + src/settingsview/components/SettingParaCombobox.qml + src/settingsview/components/DeviceItemDelegate.qml + src/settingsview/components/PluginItemDelegate.qml + src/settingsview/components/PreferenceItemDelegate.qml + src/settingsview/components/BannedItemDelegate.qml + src/settingsview/components/VideoCodecDelegate.qml + src/settingsview/components/AudioCodecDelegate.qml + src/settingsview/components/NameRegistrationDialog.qml + src/settingsview/components/LinkDeviceDialog.qml + src/settingsview/components/RevokeDevicePasswordDialog.qml + src/commoncomponents/HoverableButtonTextItem.qml + src/commoncomponents/HoverableRadiusButton.qml + src/commoncomponents/PasswordDialog.qml + src/commoncomponents/InfoLineEdit.qml + src/commoncomponents/PhotoboothView.qml + src/commoncomponents/LookupStatusLabel.qml + src/commoncomponents/ListViewJami.qml + src/commoncomponents/DeleteAccountDialog.qml + src/commoncomponents/MessageBox.qml + src/wizardview/WizardView.qml + src/wizardview/components/WelcomePageLayout.qml + src/wizardview/components/CreateAccountPage.qml + src/wizardview/components/CreateSIPAccountPage.qml + src/wizardview/components/ImportFromBackupPage.qml + src/wizardview/components/BackupKeyPage.qml + src/wizardview/components/ImportFromDevicePage.qml + src/wizardview/components/ConnectToAccountManagerPage.qml + src/wizardview/components/SpinnerPage.qml + src/wizardview/components/CollapsiblePasswordWidget.qml + src/MainApplicationWindow.qml + src/mainview/MainView.qml + src/commoncomponents/CustomBorder.qml + src/constant/JamiTheme.qml + src/mainview/components/AboutPopUp.qml + src/mainview/components/SidePanel.qml + src/mainview/components/WelcomePage.qml + src/mainview/components/MessageWebView.qml + src/mainview/components/MessageWebViewHeader.qml + src/commoncomponents/HoverableButton.qml + src/mainview/components/AccountComboBox.qml + src/mainview/components/ConversationSmartListView.qml + src/commoncomponents/JamiFileDialog.qml + src/mainview/components/CallStackView.qml + src/mainview/components/IncomingCallPage.qml + src/mainview/components/OutgoingCallPage.qml + src/mainview/components/AudioCallPage.qml + src/mainview/components/CallOverlay.qml + src/commoncomponents/TintedButton.qml + src/mainview/components/CallOverlayButtonGroup.qml + src/mainview/js/incomingcallpagecreation.js + src/mainview/components/ContactSearchBar.qml + src/mainview/components/VideoCallPage.qml + src/mainview/components/ChangeLogScrollView.qml + src/mainview/components/ProjectCreditsScrollView.qml + src/mainview/components/AccountComboBoxPopup.qml + src/mainview/components/ConversationSmartListViewItemDelegate.qml + src/mainview/components/ConversationSmartListUserImage.qml + src/mainview/components/SidePanelTabBar.qml + src/mainview/components/WelcomePageQrDialog.qml + src/commoncomponents/GeneralMenuItem.qml + src/mainview/components/ConversationSmartListContextMenu.qml + src/commoncomponents/GeneralMenuSeparator.qml + src/mainview/components/UserProfile.qml + src/mainview/components/VideoCallPageContextMenu.qml + src/mainview/js/videodevicecontextmenuitemcreation.js + src/mainview/components/VideoCallPageContextMenuDeviceItem.qml + src/mainview/components/SelectScreen.qml + src/mainview/js/selectscreenwindowcreation.js + src/mainview/components/ScreenRubberBand.qml + src/mainview/js/screenrubberbandcreation.js + src/mainview/js/videocallfullscreenwindowcontainercreation.js + src/mainview/components/VideoCallFullScreenWindowContainer.qml + src/mainview/components/ContactPicker.qml + src/mainview/js/contactpickercreation.js + src/mainview/components/ContactPickerItemDelegate.qml + src/wizardview/components/HoverableGradientButton.qml + src/commoncomponents/AccountMigrationDialog.qml + src/mainview/components/RecordBox.qml + + diff --git a/qrencode-win32.patch b/qrencode-win32.patch new file mode 100644 index 00000000..efeb2967 --- /dev/null +++ b/qrencode-win32.patch @@ -0,0 +1,243 @@ +From 261d830b9b4126d76519db0e6b6b51b5a730eb40 Mon Sep 17 00:00:00 2001 +From: Andreas Traczyk +Date: Tue, 4 Dec 2018 17:42:43 -0500 +Subject: [PATCH] b + +--- + .../vc8/qrcodelib/qrcodelib.vcxproj | 129 +++++++++++++++++- + 1 file changed, 125 insertions(+), 4 deletions(-) + +diff --git a/qrencode-win32/vc8/qrcodelib/qrcodelib.vcxproj b/qrencode-win32/vc8/qrcodelib/qrcodelib.vcxproj +index aabc6b6..8d8293b 100644 +--- a/qrencode-win32/vc8/qrcodelib/qrcodelib.vcxproj ++++ b/qrencode-win32/vc8/qrcodelib/qrcodelib.vcxproj +@@ -5,45 +5,84 @@ + Debug-Dll + Win32 + ++ ++ Debug-Dll ++ x64 ++ + + Debug-Lib + Win32 + ++ ++ Debug-Lib ++ x64 ++ + + Release-Dll + Win32 + ++ ++ Release-Dll ++ x64 ++ + + Release-Lib + Win32 + ++ ++ Release-Lib ++ x64 ++ + + + {9A90BF5C-84B0-41F6-B83C-C20EADC1F46C} + qrcodelib + Win32Proj ++ 10.0.16299.0 + + + + StaticLibrary +- v110_xp ++ $(DefaultPlatformToolset) ++ MultiByte ++ true ++ ++ ++ StaticLibrary ++ $(DefaultPlatformToolset) + MultiByte + true + + + StaticLibrary +- v110_xp ++ $(DefaultPlatformToolset) ++ MultiByte ++ ++ ++ StaticLibrary ++ $(DefaultPlatformToolset) + MultiByte + + + DynamicLibrary +- v110_xp ++ $(DefaultPlatformToolset) ++ MultiByte ++ true ++ ++ ++ DynamicLibrary ++ $(DefaultPlatformToolset) + MultiByte + true + + + DynamicLibrary +- v110_xp ++ $(DefaultPlatformToolset) ++ MultiByte ++ ++ ++ DynamicLibrary ++ $(DefaultPlatformToolset) + MultiByte + + +@@ -52,15 +91,27 @@ + + + ++ ++ ++ + + + ++ ++ ++ + + + ++ ++ ++ + + + ++ ++ ++ + + + <_ProjectFileVersion>11.0.50727.1 +@@ -70,11 +121,17 @@ + ../.build/$(ProjectName)/$(Configuration)\ + true + ++ ++ true ++ + + ../.build/$(Configuration)\ + ../.build/$(ProjectName)/$(Configuration)\ + false + ++ ++ false ++ + + ../.build/$(Configuration)\ + ../.build/$(ProjectName)/$(Configuration)\ +@@ -104,6 +161,26 @@ + MachineX86 + + ++ ++ ++ Disabled ++ .\;..\qrcode;..\qrcode\png;%(AdditionalIncludeDirectories) ++ WIN32;_DEBUG;_WINDOWS;_USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;%(PreprocessorDefinitions) ++ EnableFastChecks ++ MultiThreadedDebugDLL ++ ++ ++ Level3 ++ ProgramDatabase ++ ++ ++ libpng15d.lib;%(AdditionalDependencies) ++ ..\qrcode\png;%(AdditionalLibraryDirectories) ++ qrcodelib.def ++ true ++ Windows ++ ++ + + + .\;..\qrcode;..\qrcode\png;%(AdditionalIncludeDirectories) +@@ -124,6 +201,26 @@ + MachineX86 + + ++ ++ ++ .\;..\qrcode;..\qrcode\png;%(AdditionalIncludeDirectories) ++ WIN32;NDEBUG;_WINDOWS;_USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;%(PreprocessorDefinitions) ++ MultiThreadedDLL ++ ++ ++ Level3 ++ ProgramDatabase ++ ++ ++ libpng15.lib;%(AdditionalDependencies) ++ ..\qrcode\png;%(AdditionalLibraryDirectories) ++ qrcodelib.def ++ true ++ Windows ++ true ++ true ++ ++ + + + Disabled +@@ -137,6 +234,19 @@ + EditAndContinue + + ++ ++ ++ Disabled ++ .\;..\qrcode;..\qrcode\png;%(AdditionalIncludeDirectories) ++ WIN32;_DEBUG;_WINDOWS;_USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;%(PreprocessorDefinitions) ++ EnableFastChecks ++ MultiThreadedDebug ++ ++ ++ Level3 ++ ProgramDatabase ++ ++ + + + .\;..\qrcode;..\qrcode\png;%(AdditionalIncludeDirectories) +@@ -147,6 +257,17 @@ + ProgramDatabase + + ++ ++ ++ .\;..\qrcode;..\qrcode\png;%(AdditionalIncludeDirectories) ++ WIN32;NDEBUG;_WINDOWS;_USRDLL;QRCODELIB_EXPORTS;HAVE_CONFIG_H;%(PreprocessorDefinitions) ++ MultiThreaded ++ ++ ++ Level3 ++ ProgramDatabase ++ ++ + + + +-- +2.19.0.windows.1 + diff --git a/qt.conf b/qt.conf new file mode 100644 index 00000000..7bbb77fb --- /dev/null +++ b/qt.conf @@ -0,0 +1,4 @@ +[Paths] +Prefix=./ +Resources=./resources +Translations=./translations \ No newline at end of file diff --git a/ressources.qrc b/ressources.qrc new file mode 100644 index 00000000..1093bde9 --- /dev/null +++ b/ressources.qrc @@ -0,0 +1,111 @@ + + + images/icons/outline-info-24px.svg + images/icons/baseline-camera_alt-24px.svg + images/icons/baseline-refresh-24px.svg + images/jami_rolling_spinner.gif + images/icons/baseline-close-24px.svg + images/icons/baseline-done-24px.svg + images/icons/baseline-error_outline-24px.svg + stylesheet.css + stylesheet.linux.css + changelog.html + projectcredits.html + images/ajax-loader.gif + images/default_avatar_overlay.svg + images/FontAwesome.otf + images/logo-jami-standard-coul.png + images/qrcode.png + images/jami.ico + images/jami.png + images/spike.png + images/waiting.gif + images/icons/ic_add_black_18dp_2x.png + images/icons/info-24px.svg + images/icons/ic_arrow_back_24px.svg + images/icons/ic_arrow_back_white_24dp.png + images/icons/ic_arrow_drop_down_black_9dp_2x.png + images/icons/ic_arrow_drop_down_black_18dp_2x.png + images/icons/ic_arrow_drop_up_black_9dp_2x.png + images/icons/ic_arrow_drop_up_black_18dp_2x.png + images/icons/ic_arrow_forward_white_48dp_2x.png + images/icons/ic_arrow_tab_next_black_9dp_2x.png + images/icons/ic_arrow_tab_previous_black_9dp_2x.png + images/icons/ic_block_24px.svg + images/icons/ic_call_transfer_white_24px.png + images/icons/ic_chat_black_24dp_2x.png + images/icons/ic_chat_white_24dp.png + images/icons/ic_check_white_18dp_2x.png + images/icons/ic_clear_24px.svg + images/icons/ic_close_white_24dp.png + images/icons/ic_content_copy.svg + images/icons/ic_delete_black_18dp_2x.png + images/icons/ic_done_white_24dp.png + images/icons/ic_exit_full_screen_black.png + images/icons/ic_folder_black_18dp_2x.png + images/icons/ic_full_screen_black.png + images/icons/ic_group_add_white_24dp.png + images/icons/ic_high_quality_white_24dp.png + images/icons/ic_mic_off_white_24dp.png + images/icons/ic_pause_white_24dp.png + images/icons/ic_pause_white_100px.png + images/icons/ic_person_add_black_24dp_2x.png + images/icons/ic_person_add_white_24dp.png + images/icons/ic_phone_24px.svg + images/icons/ic_photo_camera_white_24dp_2x.png + images/icons/ic_baseline-search-24px.svg + images/icons/ic_send_24px.svg + images/icons/ic_send_white_24dp.png + images/icons/ic_settings_white_48dp_2x.png + images/icons/ic_share_black_48dp_2x.png + images/icons/ic_video_call_24px.svg + images/icons/ic_videocam_off_white_24dp.png + images/icons/ic_videocam_white.png + images/icons/ic_voicemail_white_24dp_2x.png + images/icons/round-add-24px.svg + images/icons/round-arrow_drop_down-24px.svg + images/icons/round-arrow_drop_up-24px.svg + images/icons/round-arrow_right-24px.svg + images/icons/round-close-24px.svg + images/icons/round-edit-24px.svg + images/icons/round-folder-24px.svg + images/icons/round-remove_circle-24px.svg + images/icons/round-settings-24px.svg + images/icons/round-undo-24px.svg + ../lrc/src/web-chatview/chatview.css + ../lrc/src/web-chatview/chatview.html + ../lrc/src/web-chatview/chatview.js + ../lrc/src/web-chatview/jed.js + ../lrc/src/web-chatview/linkify.js + ../lrc/src/web-chatview/linkify-html.js + ../lrc/src/web-chatview/linkify-string.js + ../lrc/src/web-chatview/qwebchannel.js + ../lrc/src/web-chatview/chatview-windows.css + images/icons/round-check_circle-24px.svg + images/icons/round-error-24px.svg + images/icons/round-save_alt-24px.svg + images/jami_eclipse_spinner.gif + images/icons/ic_hide_password.png + images/icons/ic_show_password.png + images/icons/baseline-desktop_windows-24px.svg + images/icons/baseline-people-24px.svg + images/icons/round-add_a_photo-24px.svg + images/icons/ic_mic_white_24dp.png + images/icons/icon-keypad-24.png + images/icons/ic_play_white_24dp.png + images/icons/icon-keypad-24-2x.png + images/icons/ic_voicemail_black_24dp_2x_.png + images/icons/av_icons/delete-24px.svg + images/icons/av_icons/fiber_manual_record-24px.svg + images/icons/av_icons/play_circle_outline-24px.svg + images/icons/av_icons/re-record-24px.svg + images/icons/av_icons/stop-24px-red.svg + images/icons/av_icons/pause-24px.svg + images/icons/av_icons/send-24px.svg + images/icons/av_icons/stop-24px.svg + images/icons/av_icons/mic-24px.svg + images/icons/ic_close_black_24dp.png + images/icons/extension_24dp.svg + images/icons/settings_backup_restore-black-18dp.svg + + diff --git a/ringtones/Makefile.am b/ringtones/Makefile.am new file mode 100644 index 00000000..64fab830 --- /dev/null +++ b/ringtones/Makefile.am @@ -0,0 +1,5 @@ +ringringtonesdir = $(datadir)/ring/ringtones +dist_ringringtones_DATA = \ + konga.ul\ + default.wav \ + default.opus diff --git a/ringtones/default.opus b/ringtones/default.opus new file mode 100644 index 0000000000000000000000000000000000000000..c3c0a8076c1bdb474d95514005a0865063373b88 GIT binary patch literal 22089 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 Fh|#3==Z*jX literal 0 HcmV?d00001 diff --git a/ringtones/default.opus.LICENSE b/ringtones/default.opus.LICENSE new file mode 100644 index 00000000..98b99bfc --- /dev/null +++ b/ringtones/default.opus.LICENSE @@ -0,0 +1,4 @@ +Original file name: 171324__swidmark__ringtone.wav +Download location: https://freesound.org/people/swidmark/sounds/171324 +Uploader: https://freesound.org/people/swidmark +License: CC0 1.0 Universal (CC0 1.0) (https://creativecommons.org/publicdomain/zero/1.0/) diff --git a/ringtones/default.wav b/ringtones/default.wav new file mode 100644 index 0000000000000000000000000000000000000000..f68346e5a2df863e1e6741532042db4c173014e0 GIT binary patch literal 56472 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 hT1#AA<2//4;>>=6/*'""$*,.+&"!#(++--+++*-.3>QSndf\D91//.)'*.26BA84.0/5;<41;>BEAOp˼qѾȻeXL>B]OD;36//153/00,)).N]SNNTVTID@GKOFbFD??=<>FO`nJCCENLGbmWJ>EIWWX[`L=>:9<=Mr¿̻LK\_SGE@GF_ürW`Z<77AMo`REOkcbf@?FJOMKRPN^`e}YXgpNF:7>FGGDTuɾe\vh__\RSE?:9=::=U{^gi\m]ZjVM]dNLSloYJGIUR]unkʼMJA<;=EMY\JG?@GFMaĽNKFIPV^zne]tl^dkbUKD>?IR[ZY`ov}tviXU^mÿd\iprX]\XPK??>>===??CJSghou^sy[W_gVWY[\n˾YOqjXUZ^QHDHIKNdnivb\a~YZWX~_YW_dfWSczrk\dqv`]_geo{bVQOLKO_j^WSNDCJKMJMSsTMGHlq]Xl\WZTKGGJJKLNMPRLNVVOJJORU\ſpm]SXXZWNMOX\_e`ZXajcZSOQ_kWQKMQOPR_da^Z_m_XOMMRZYZk{inxj]ds}yrjd]cdaTTKE>ACGKS[UOO[mzc\Z_z`YMLU[ksVMFHGHIMPYi|ojUOLNQOJKLKLR]c]VUSVTW[etxhiqy؜407=>LS5JU( ')1;$& 03˟L!#,.6-$NM.*8ì0!:&$'GD))7K9ϫL(&*+8ӾH>6-3HK:)#%0r0$#$1q?&$.=M﫠?-,+->ƫȪH/.:Ͷv.-:lI-.78IƹPE7469==61/358Ga~?/$$+5XϻYC<=T޼ORTuϿ]@?J_ƼC:55;99IohdK@FFLYlO9568;;FZ̾QGDEI߿þf¾jUM\ʽYOQTR½wMINZ91..28QU<-.07QQB9<>EhUJ?9=bµ<89=øeOJD\fh`UjļNHJKX¼KC87:MjKBFBLRTL>?D]vDB>>BOŽJ;78;I忲K<CN\hga]_O_[KIM^gIMKK\Ƚ`^lmlɽ_HHNlgOMDE\ndvfnxWH?=7103EUcMCDJWZf_bapk|fgK?=DL_=4:=FOYOSQqټ˼ƿX5--5ALǽk_D1+('(,6?KILG?;;;99:<=><;?ADPay^XID>=Lc½ĺey~žo]PQ[}tC<78:=S'# + 8N $RE50m& /-FTU(&#ҦP3<)$*\Z08:C()/+ />*,%$A鷲Si۬za/*3<=,!(318ZU]UK@/..23))$ !&# %# "%=ͽnY]imϴ·l9=H?=Yо¹jLA<==IYnIoUA>@>?[LOUUrgdOK?KMlKS>=D[F;?;?c\ZǺٿpƿIOCSwN91-@_osGX@:7;=:;:PFKERg]J>ʹ~LMb\\X^W|XbW`TNYiWU`kLJ?768O;MDB5,*$!"'0>@KiμŷY>:64;:7779:==><;@@AAFo\zxfYQLKISOU_{{ddZUhvdZNK?NqoldMLY\ZzRNLHLN_ZOHFMOfONINRVLFIMSyh`nl_[F?ALYusenxgRik_ZlvklûǾ[\[hcJKTQMRRyucZUURKKPq_cu_Wk\LJTnxUaƾ{|mqozOMR[Rcn[KFBFMOMLSY`^]][SJIM]op~~ȿ_Zds{[clONMWkWTWfw_^YZTUT[Z|q]\gZIIP^W^]ZQNNULPY]pp]VZiz{zhypyma__WUNP[mrzn^[neSKQW_b\VNMIGIRTPPJHKPNJJO_SPS[XUPQU[Z`iknm`VYYellf]_`eZZ]ZjRNMKLNTVagf^c_nx_Za_pvjdkkztnx|b_m|e_jeb^VKIIKLMLMU][kmjjdzbftrb[]\XYTOORRRONMMPT\geiq}m\S]ir{^WX]tolj`ZVWWVPQ[_\[agai|}{ytecXVQPSSV[_YW[_][]jpwzomhssveYZTMOTWYY^k{zjfca\VXVSVVVWWYXRZ_YXTWZ[Z\_hklsnkc]_binsxjnpnh``eovjb]XX\bihgoe_c`iqkhdcoux~}z{oeklm`]Z[[^bb_b^VTXZX[eb_^uuvyklyk^YTX\kyjc^WXXWZ]]`kyxxrjysmmxœ2/LV:;$ 60) + +#ɶ! + #3їf&8F<+&6ʷ0*:/]$"'*:JϬTFɬ?E-'<8RQ4.&-79Ǭ@+#"/E챞B.=B8Ȝ/97%)<ȪU/*(,=VSH>^U:37/-,05-,-/,.8O>@˲ON:BOENúX?6/,+3XϽ[PF޾GBce.+=W𿭧D;436=X{SLFC852((/:ȱX<--;FmyŴi9/..9Oī{+*+2FϼoLB@58Aį5)!$3MȸU:.-..03@_hF:-.6N̽JE?89=\ļn_>>N÷kLC?GRd=>HQsŹνk<47AlɹMFEV^86=DSC4<CUƸ]JCNڷJ84=nY9..9Zo<9?EJ_pMA;5:QϼI?<9HtjB:/.9WƼVG=;ALngPcȼzC>;EjSLILKI`KHNvbE=9;FA55FbM5,*0>˹OI[klR>;>JvϺ^A38=BԼJ:L㾾VB426;R۽ND>EoſO=98=L]żZXS\oVQRIEFMt¾ZB>K^L768C_tGD==??Mt\E>?@@D\PF>?FLYl^XY@87?LžTLFHR\eSMS`wuqJ?:?IƾO>65?Nlx`D=>Bj¼D@CNTJGMe[XPc^KJIKO_VNMSRP\peVMN[{~_^_SMSiļtIFM[}zmlbUURbmYOLOcm^SOMZZmzeYfZGBFMOZnf\POKP[YSJMfOJHN_lsaQMUpkVPSVgovZSQRVWhjXTMKOMWo|oYTOQX`lh^[_kxhn^OMVi{SWh[VU_|VNJLTkona\\]i{_\]ieZZdredYSQ\hk}vigc[NN`h[ZckTOKViclkcNN[cleszePV_hoi\`kjoicP^\WYU]Xodޮ +/"2  >6 kNO'*' 0$'2E:8i˽eO7CgƼl¾󹹬ŽQOgf7.=<>[KE0(*+?V??GC@N|Ŀzúμ׿ڿe_XbW?@F\YmpQhwnA;?@C551//:CLINBK=C@;BEYh;Hfx>98GZUĽƽueoII?JwhYNNgcQ`T[hICIMzo[GF=HIIYiSHR^TH227O`[\]OuvYJEONYT[ozoOYQ]^]ɺĺfWOLA@78473765<;>@BB?[Kƿ¸ɻWZKBJ>EKMGCB69ENQKCBRan¼¾]YP]e[G?@JLGjI?:AOy`hwmy]n]`mݿ`ŻZ[NB>ORaNF>78;DED?@HDIKNyt»ľþp[INSNLAGDCHNZh[XaTURgwYV[k|blYO^g{s\SS[^^XRONPac^YNVMT\dyq[fǿyhubZ^_l_f^cpZMGLKNPWXJGNZYveykrr]_od_hofr_a[_k`d\[NMVoidnzljywl_TQU[jpq_W\TYWYYW[OPX_vsppvqjkp`bcjf`YMONKMT]TLIHSV]YUWez~^Z^jYY_ZY\[_YT[WYXYd\gbfgWgyndmshh^^]tqmt`ZZOTT[]PPKVXZclrkjͥICLI,&$$&$ܭZF~ô4$ "$'-&$)6=ũ]-))2浤L('*54)06ֽL/ (/N괶.#%%5èN*+/AC,)):Oj<3GE. $'7KbGLHC㻵JB;00<ȬYKO>58=Qjuí:,()),:u̹EE\ɾX1-1;>/1IίH6<>=qؿTF>89BOTM@<>FMb¼F/%&5OwH;=Rپa?0./9c]PC>BJ`ZFG\½f@>K绯d<867:DȺbiUA;@UQIBGlW?619PǾKJNX`bMRTznKFJVžvJLD?ESs]XWWE=>?GaǸM@>:<>GdpXMAFLXʿ]`OF>CMch\NIV|LHLUWTU]nVV\WWrL=8:Hl`THFKSb\QLLh^IDDKTd]OJFELW~ZLIIFLVmKGGJYlx[MLMVk|eSMO\mof^[[\d_NFIL[uVEAAGQVemjURNLOT_\QPTmzmljomcVOIHN[gROKIMVaVVYVj^OHISgo]V[lkccfkmZTNOYjr[TQVZovvf]PMQWki[POQUZgxlmmgfadt[TOT^oc`_[ZjkZTUV_hmXRPS^c|ohc``_i}ia[Z_lwhj__^_djwga[_v}nooh^\]]]_xwcUOMOUan|utsnfjg^[[ekdbejt|qlfb^_dmnhnd[Z^ywz{jd^^mi^_^TWcr{s~viihftnfpm^daƵfm  !Tn.*'./*"+,%')/B<&P- *8;PC50)""IļB2*%)Fg6)(7iU>-'+-( &-/IN>+% $&)%&&)+.:ر[<-()*)-47*!#&/63*%!$!!! #'4Enǿʹ_sK=,% #""!!(-9HU_NKXWVKMZǰʿcI::40///+(&""(/250***+-../3364303/.39743.1:=Wt~~HSeGB@ERQY^V^NQLWSkw_O\REFF:1102029:<=FUUID@=<>>CYXw\MTȻȽ¹ĻǾ|S:658>CDHEGMG==KPONMTxhA95467<>ArŻǼTL`ŽWKLQX~nKFJKMGCKMHJACUnb\TY^VE<;98AAPfǽcIIi~TNMMWǿh[M>=>NxlpVQFKYVZm]JGECA?DBACPYk\OQg}ZKC>CMPUYedwƽu_rϽd^uaUNPKEC@>>><99?\w_f_PSSXYbrb_cYRWowdPNSj|r~XOKECAFEOSQLE>=CBDDBCLNbjYNKKTZbrus``|f\MEDJMWd`^oyoq]VZciƿ^XQ^kn^RRPPNLFFJIA??@??HL\avm[_ne]go|sWOR^jngZVOSYXTRVTMMTZ\YOOMU\lwt_TV^uf_ipoytvztdUTONWbvlWXOJJNNONPYccfWJGGKpxuwcYY[pwc[`VOSSU[]\acee[^_\VXXW]_ikjjdX]efi]SPQR\e`\ZZ_^YUPS[ur^SROQVVXX]][[\nzkf[Z\]b`r^XZ^bkolhn{|eYWNEDEFJQ_Z[\p}vh``_}whqzg^USZhxn_RURQUW[jvlnyrn]VTUSROKLMNU]hpwg\\^ZZkvto~kuuzϯU4),O@* ȱZ6+$5%! 7m.'2f!D"@8-O$$Qâ^:1,(9K)98  `R( *CB]!*E˪0!(M/"*=V&%(+E8,$")+'" %)3LW! 2¼xN+%*;K.+4OȲȾR-,<ʩo(4[@;-+,&(*))5^:'# 'WǹHOʺýűYE8:Zǽ]>:8?>1%! %*7A|5&%,38>?NYD=98JժVJ8/:?SŶ:=1,&(9JɿK?80,2GWZ8/5C^g:.244>Žn?JFCMZɼ?3-?嶫x[YaUODLS^UK>GZVA=L]?%+@ӻM.+),=H[\νD::EKH88?Kȯ922=λbVt˽@=@GD=>aͿeN<.&$&-:?Uo̾zSFFUzL:8>kȿvB=?9=cܼ[MYLIO^ҹ?/0:I̽WC97>xbOE>4,)*8BTn»E,&#).4CXZIHFGKMλKBBMH825K7,())*7;BB:-$"6T;;=<760+%$)*8ZCFմɽ̼I/,*+.3CM[>/+**/57>>92.035:>A0,('$'+18DIR|G3'"!#'-:>KI=AADOoþķzG?<958DU`D4.,-279::;??;941/1;943568>VtL><;>Nzʽǿ^NLSTo{YMMRSH?;6788?HHDFB<7/-04;8:=FKMXZjxoǻgUUW[cziWB=98=DECBC<;@EACEEDDFFMMYmt}xufZXZ\TOZyk¿XR_yszZ]acZPOHDA?DHQ]ŴLOsXx.))'')3,(*23///QʰϷTU}@62211666=NºU@?>BƵd=<;80.7E_V>81/339:BLf^Vlo_f˻ǾjXVb]M>=96;>EJUk˿L?@XԾm_^RD>BDA@=<<)"$*&$&%-::gϺI4%$&(#$%& (.?CE::VCA0,2451,-#% "&%)1*-=FT=/3߲deIJBA;LBFWHVzyi=:/3+)7:<:?1313:49FUR=9:49Gŷ̼kwɭW;.4I?GKMND:3EA9]5G=7ceZUXHgijW^WPCoI@4(3/.342/SQL\;807BVB׾Ke_EHB?8*!#,2/$-&(*,,+6ǹ˯sPX7CH:;>7-+&,'*,2CIDE>1.,11C`\OBDO]yjƼWLXNK^OOIM?60,--4=KLNEIJLSNU]QzɾÿþoeO_NW]SRE;533266=@TW`}cHGFZ~Tb_qb]MA@CEFBBEJKFKVQ|YEPUȺghXVKHHDE?;24=BK_>:>EdXSfɴUJKRÿVQSXSEE[ll[KCEFOT_MNYY}_{}ok^LGJHIBHTROan[SVi\Xc\mUOOKLRQLVZ[]c|exe[KGIG]mi]okrcMNdnabdvfgu|nc`^g^ankbo}jxaXUZSX|uqi]neUohc^XKILU^afiPLDCN[_RYj``ght^aZXNTLGE>?EHV\f~VROO]lhu|wm`nq_g_\KKKDEHJEBDEQ_jmc^ytϿceo_MMKIGMMLIKKIMOSNLQZ_mf޿ʹ"+-*.+Jʦ/%+,*$#*4( ('$ (9i-($*=ENΧEBK9,  &>|nlѿe?Kݻn?9473553*#!&*+**,-.45504:>=>GuɽebJNTWUMA846=FKLF=3-)'$$'),*,+)''*4?FI?HSiſȽk_OJ>8>M]dO@@C??FRVW^B:9:?IS__K;69=M\_OUMRYI=9AAJKK?=968>>?@HM[dcgYH:97:@CWtj_^l^»;fH@@FMLKIDB??>AEXg{fp_QGCCKPSWSW^UI=99??H_k]fmjZMIGGDDKNOYYb[Y[VVZOIHHKLGC@@FMPkNJGBCDJL]nfdb]W^WLLRZ¾lnnw~c~|gWOOR__YVXQLLE?BEJNYWTPLIJMN^akemo{Ƽhjwvk}|jRNNONKKJKOQv|oeVQOQ_t_RNSSVTNJKMOQRUVSSORQUZfaWSOLMR_}fNJHHIKLLJFFFA@BHPXaUUcfj`ooihi~o_Ykp~icaWUX^ZZ^^ZYSNLMLHDFIKRU]^svk`[[qf]\Y[fye_ZZbiimifd^_]]a^YX[_mtg_]_q~sm{lbimgd^]\]kmc[WSKILNOYbowqv{qc\WQNOW[k}~^TMIGGIKOSX^bffrvmz_^ZY\YVY^\bginj_YWZYYa^jioin\]XPngd[jƒ7..rJ{:ŗA:L:=F*# +%(2[ +[=, " 쐑 ̔R( #N(1F$ 6ī,'-:L-&*&,Ǩ:A, (8J>J/+- ,XߪƻR,!.5+%!/E<%&-I[?2/>W{$ WU9"&>1"ޯP5+:mr:8Oĸ;+,?m$)8S74HL/#%=庿6))DS@FYM?N]>../3EUrB60>_J8?DBG\̶KIJNJ953>/'(3EiŹdD-,1]=#%8b,##-a\H:$%(-C֫b;,&&<{MTI>UľA5=QQ5/5<ϳt7268AI{~B>IzIMmYmŲX5'*5V?376@LnĺN4,;?KAD_θ2+&)>Wnv[H?@@̷J4/4A˵s;4>Hɺ`3+(1=IYʿSHReJ3-/EɯP84//:IںEA:773/6LʵdE=+)8ؾU>77?Lb˿];/$#->nR=0.68@Iba_VH@9q.'%(5K϶MSJ>=?40/3E߷N,&).=b>48O|žE;I`ntÿO1)(-6cųT>505Bs{Z^eiQ<:Gwm;804;EʽG<7558D|ɿP>85/2<˸=527?I_AAS]ɻF?9LƻQ?79?XdWJQxm_U[YoG?==JSĹE<9?E_i`eTKQWjpZNJKW^liVMO`TQLFRmrH>>FP\Y[SMZ_KKMOUdm˿]GBCQ[hTKDBFZtZFCDIfoOLHHNVux[MEFPnl\TXn]XW_]b_mM@??CJbdituj\ON^SMDBLg_^UY\bwzlj_Z]ahnw]WisNR_rXHDIRlq^SCCKVfnwtf\ZUYiyePTUVayqq\SZQMRYomd^]SNsnT[_N^nܟ 짶>[! + m&ܬ?:)K! 'BE/7F+*׺ղ^43CXױS*'GF+8BAʼ^!'' 2иж~=<3&( #,(%),2>պJMUpJ>;><50+-)%+&1C}ƻ|ȲpNL=30401.(& #5>8?A98>ݫmQOaRn60('""%().+(#'3CFK=6:48H亭ȿgL79=9?62'%""-76.3*/-0A;NaȿlBET;;:<3-.-'!&#$.(+)'+(++++=OMQI?>JX]ź]Z[IKbK3LIDMG;8ɺooF\ĿɮĽ}=?@5AQC//()&##)+,6;?F;4=K϶ſSVlC85=G<:1./2/53//08:20,/;oĹϼȸdhOA:I?/41(,,-+,,61;9>FD:4:fisźXʽX?::HFQNG799083=:JLPG:69@LLOJ>AOWGF[Lr~fQG}ԿcRھοXMAf¿OqSOWYcdbSMH<<@FOZLGCMSk_cHMĿZimhUq`\xO@=;:KFNGH>845<<8:DVmgĻ»Žo]zhQECWM[QVPKMJ@D?BW~f_ORaTunVUPObX]irbhv~ZKORLLGIMOWMMWboSǾüR@=<>E^L?:8>CDaI=8>JoMKOf|ih\PNZfڿZamƼg\TJcmrVUMHLVVVMNgstjal{dI>@CIELXQEEGBKIKJGGKYn|ͽoeSZSdg_TXYhVPY^V[ql]^YQWPKITYQX\rff^Y\cye^ee_oyhvg}^Snrk`hhfUKLNJNOV[Z\UHHC@JZXOY\utirj`lYXNHHJNJObd|b[X^~od__TTNOPROFHJGHOWNKKFMY[\Y]v]^n]XSNQUZ\SQPMMLMNNSlvziQYTgh^Y^{ggnf_`dB9>[QS<(!#%i&%%&/) $2:ϭ6"*4ǭ2'/}-#-2ί9&&+:[@%%'>E().9?$!%,A˭90OM3#%-.Dϴ]ٳI95)*4I:8<վ\9,/7>hQȲP,+,,3P˺zeָ<./:V.&(4vY:.066L]J=9;Dh[GMV{߹C0$&6MbT_vV9/0>V¹M=/,.5aƼVBKñw|øC@WθQ:.,,-9VWJOB529IMF@G񴬩F;7<ĿOMNXgp~_LHJMNSULDEW]HIFCJfɾvLB?BKYĺK<:68>DLXg\maOFNVkWGI<:B^½[B9:JySIA?FVƾ^LEHl̾]TT_cgǾ?866C]H>;=DloWSUnLHIRs^WlSC>>HY^¼OQ_kNJKexVMJA@KTþ~XO@???BIc|XBAN]lYMGNYatZK?:68KdTLGOQ]hzXJEKVWMIGIMN\VOV^inpaRNKITiLEOp[>::=Nn»xrh[ONI?::ANzjN;9=BOLGKVkbwt}NJUeMDBDJUaVNRON[cjllYC<; + * Author: Anthony L�onard + * Author: Andreas Traczyk + * Author: Isa Nanic + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "accountadapter.h" + +#undef REGISTERED +#include "../daemon/src/dring/account_const.h" + +AccountAdapter::AccountAdapter(QObject *parent) + : QmlAdapterBase(parent) +{} + +AccountAdapter::~AccountAdapter() {} + +AccountAdapter & +AccountAdapter::instance() +{ + static auto instance = new AccountAdapter; + return *instance; +} + +void +AccountAdapter::initQmlObject() +{ + connectAccount(LRCInstance::getCurrAccId()); +} + +void +AccountAdapter::accountChanged(int index) +{ + auto accountList = LRCInstance::accountModel().getAccountList(); + if (accountList.size() > index) + setSelectedAccount(accountList.at(index), index); +} + +void +AccountAdapter::connectFailure() +{ + Utils::oneShotConnect(&LRCInstance::accountModel(), + &lrc::api::NewAccountModel::accountRemoved, + [this](const QString &accountId) { + Q_UNUSED(accountId); + + emit reportFailure(); + }); + Utils::oneShotConnect(&LRCInstance::accountModel(), + &lrc::api::NewAccountModel::invalidAccountDetected, + [this](const QString &accountId) { + Q_UNUSED(accountId); + + emit reportFailure(); + }); +} + +void +AccountAdapter::createJamiAccount(QString registeredName, + const QVariantMap &settings, + QString photoBoothImgBase64, + bool isCreating) +{ + Utils::oneShotConnect( + &LRCInstance::accountModel(), + &lrc::api::NewAccountModel::accountAdded, + [this, registeredName, settings, isCreating, photoBoothImgBase64](const QString &accountId) { + QSettings qSettings("jami.net", "Jami"); + if (not qSettings.contains(SettingsKey::neverShowMeAgain)) { + qSettings.setValue(SettingsKey::neverShowMeAgain, false); + } + auto showBackup = isCreating && !settings.value(SettingsKey::neverShowMeAgain).toBool(); + + if (!registeredName.isEmpty()) { + Utils::oneShotConnect(&LRCInstance::accountModel(), + &lrc::api::NewAccountModel::nameRegistrationEnded, + [this, showBackup](const QString &accountId) { + emit accountAdded(showBackup, + LRCInstance::accountModel() + .getAccountList() + .indexOf(accountId)); + }); + LRCInstance::accountModel().registerName(accountId, + settings["password"].toString(), + registeredName); + } else { + emit accountAdded(showBackup, + LRCInstance::accountModel().getAccountList().indexOf(accountId)); + } + // set up avatar pixmap from photobooth + QImage avatarImg; + const bool ret = avatarImg.loadFromData( + QByteArray::fromBase64(photoBoothImgBase64.toLatin1())); + if (!ret) { + qDebug() << "JAMI account creation BASE64 image loading failed"; + } else { + LRCInstance::setAvatarForAccount(QPixmap::fromImage(avatarImg), accountId); + } + }); + + connectFailure(); + + QtConcurrent::run([this, settings] { + QMap additionalAccountConfig; + additionalAccountConfig.insert(DRing::Account::ConfProperties::Ringtone::PATH, + Utils::GetRingtonePath()); + + LRCInstance::accountModel().createNewAccount(lrc::api::profile::Type::RING, + settings["alias"].toString(), + settings["archivePath"].toString(), + settings["password"].toString(), + settings["archivePin"].toString(), + "", + additionalAccountConfig); + }); +} + +void +AccountAdapter::createSIPAccount(const QVariantMap &settings, QString photoBoothImgBase64) +{ + Utils::oneShotConnect(&LRCInstance::accountModel(), + &lrc::api::NewAccountModel::accountAdded, + [this, settings, photoBoothImgBase64](const QString &accountId) { + auto confProps = LRCInstance::accountModel().getAccountConfig( + accountId); + // set SIP details + confProps.hostname = settings["hostname"].toString(); + confProps.username = settings["username"].toString(); + confProps.password = settings["password"].toString(); + confProps.proxyServer = settings["proxy"].toString(); + LRCInstance::accountModel().setAccountConfig(accountId, confProps); + + // set up photobooth avatar to SIP avatar + QImage avatarImg; + const bool ret = avatarImg.loadFromData( + QByteArray::fromBase64(photoBoothImgBase64.toLatin1())); + if (!ret) { + qDebug() << "SIP account creation BASE64 image loading failed"; + } else { + LRCInstance::setAvatarForAccount(QPixmap::fromImage(avatarImg), + accountId); + } + + emit accountAdded(false, + LRCInstance::accountModel().getAccountList().indexOf( + accountId)); + }); + + connectFailure(); + + QtConcurrent::run([this, settings] { + QMap additionalAccountConfig; + additionalAccountConfig.insert(DRing::Account::ConfProperties::Ringtone::PATH, + Utils::GetRingtonePath()); + + LRCInstance::accountModel().createNewAccount(lrc::api::profile::Type::SIP, + settings["alias"].toString(), + settings["archivePath"].toString(), + "", + "", + settings["username"].toString(), + additionalAccountConfig); + QThread::sleep(2); + emit LRCInstance::instance().accountListChanged(); + }); +} + +void +AccountAdapter::createJAMSAccount(const QVariantMap &settings) +{ + Utils::oneShotConnect(&LRCInstance::accountModel(), + &lrc::api::NewAccountModel::accountAdded, + [this](const QString &accountId) { + Q_UNUSED(accountId) + if (!LRCInstance::accountModel().getAccountList().size()) + return; + emit accountAdded(false, + LRCInstance::accountModel().getAccountList().indexOf( + accountId)); + emit LRCInstance::instance().accountListChanged(); + }); + + connectFailure(); + + QtConcurrent::run([this, settings] { + QMap additionalAccountConfig; + additionalAccountConfig.insert(DRing::Account::ConfProperties::Ringtone::PATH, + Utils::GetRingtonePath()); + + LRCInstance::accountModel().connectToAccountManager(settings["username"].toString(), + settings["password"].toString(), + settings["manager"].toString(), + additionalAccountConfig); + }); +} + +void +AccountAdapter::deleteCurrentAccount() +{ + LRCInstance::accountModel().removeAccount(LRCInstance::getCurrAccId()); +} + +bool +AccountAdapter::savePassword(const QString accountId, + const QString oldPassword, + const QString newPassword) +{ + return LRCInstance::accountModel().changeAccountPassword(accountId, oldPassword, newPassword); +} + +void +AccountAdapter::startAudioMeter(bool async) +{ + LRCInstance::startAudioMeter(async); +} + +void +AccountAdapter::stopAudioMeter(bool async) +{ + LRCInstance::stopAudioMeter(async); +} + +void +AccountAdapter::startPreviewing(bool force) +{ + LRCInstance::renderer()->startPreviewing(force); +} + +void +AccountAdapter::stopPreviewing() +{ + if (!LRCInstance::hasVideoCall() && LRCInstance::renderer()->isPreviewing()) { + LRCInstance::renderer()->stopPreviewing(); + } +} + +bool +AccountAdapter::hasVideoCall() +{ + return LRCInstance::hasVideoCall(); +} + +bool +AccountAdapter::isPreviewing() +{ + return LRCInstance::renderer()->isPreviewing(); +} + +RenderManager * +AccountAdapter::getRenderManager() +{ + return LRCInstance::renderer(); +} + +void +AccountAdapter::setCurrAccDisplayName(QString text) +{ + LRCInstance::setCurrAccDisplayName(text); +} + +void +AccountAdapter::setSelectedAccountId(QString accountId) +{ + LRCInstance::setSelectedAccountId(accountId); +} + +void +AccountAdapter::setSelectedConvId(QString accountId) +{ + LRCInstance::setSelectedConvId(accountId); +} + +bool +AccountAdapter::hasPassword() +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + return confProps.archiveHasPassword; +} + +void +AccountAdapter::setArchiveHasPassword(bool isHavePassword) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.archiveHasPassword = isHavePassword; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} +bool +AccountAdapter::exportToFile(const QString &accountId, + const QString &path, + const QString &password) const +{ + return LRCInstance::accountModel().exportToFile(accountId, path, password); +} + +void +AccountAdapter::setArchivePasswordAsync(const QString &accountID, const QString &password) +{ + QtConcurrent::run([this, accountID, password] { + auto config = LRCInstance::accountModel().getAccountConfig(accountID); + config.archivePassword = password; + LRCInstance::accountModel().setAccountConfig(accountID, config); + }); +} + +lrc::api::NewAccountModel * +AccountAdapter::accoundModel() +{ + return &(LRCInstance::accountModel()); +} + +lrc::api::AVModel * +AccountAdapter::avModel() +{ + return &(LRCInstance::avModel()); +} + +lrc::api::DataTransferModel * +AccountAdapter::dataTransferModel() +{ + return &(LRCInstance::dataTransferModel()); +} + +void +AccountAdapter::settingsNeverShowAgain(bool checked) +{ + QSettings settings("jami.net", "Jami"); + settings.setValue(SettingsKey::neverShowMeAgain, checked); +} + +void +AccountAdapter::passwordSetStatusMessageBox(bool success, QString title, QString infoToDisplay) +{ + if (success) { + QMessageBox::information(0, title, infoToDisplay); + } else { + QMessageBox::critical(0, title, infoToDisplay); + } +} + +void +AccountAdapter::setSelectedAccount(const QString &accountId, int index) +{ + LRCInstance::setSelectedAccountId(accountId); + + backToWelcomePage(index); + + QMetaObject::invokeMethod(qmlObj_, "updateSmartList", Q_ARG(QVariant, accountId)); + connectAccount(accountId); + emit accountSignalsReconnect(accountId); +} + +void +AccountAdapter::backToWelcomePage(int index) +{ + deselectConversation(); + QMetaObject::invokeMethod(qmlObj_, "backToWelcomePage", Q_ARG(QVariant, index)); +} + +void +AccountAdapter::deselectConversation() +{ + if (LRCInstance::getCurrentConvUid().isEmpty()) { + return; + } + + auto currentConversationModel = LRCInstance::getCurrentConversationModel(); + + if (currentConversationModel == nullptr) { + return; + } + + currentConversationModel->selectConversation(""); + LRCInstance::setSelectedConvId(); +} + +void +AccountAdapter::connectAccount(const QString &accountId) +{ + try { + auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + + QObject::disconnect(accountStatusChangedConnection_); + QObject::disconnect(contactAddedConnection_); + + accountStatusChangedConnection_ + = QObject::connect(accInfo.accountModel, + &lrc::api::NewAccountModel::accountStatusChanged, + [this] { emit accountStatusChanged(); }); + + contactAddedConnection_ + = QObject::connect(accInfo.contactModel.get(), + &lrc::api::ContactModel::contactAdded, + [this, accountId](const QString &contactUri) { + auto &accInfo = LRCInstance::accountModel().getAccountInfo( + accountId); + auto conversation = LRCInstance::getCurrentConversation(); + if (conversation.uid.isEmpty()) { + return; + } + if (contactUri + == accInfo.contactModel + ->getContact(conversation.participants.at(0)) + .profileInfo.uri) { + /* + * Update conversation. + */ + emit updateConversationForAddedContact(); + } + }); + QObject::disconnect(addedToConferenceConnection_); + addedToConferenceConnection_ + = QObject::connect(accInfo.callModel.get(), + &NewCallModel::callAddedToConference, + [this](const QString &callId, const QString &confId) { + Q_UNUSED(callId); + LRCInstance::renderer()->addDistantRenderer(confId); + }); + } catch (...) { + qWarning() << "Couldn't get account: " << accountId; + } +} diff --git a/src/accountadapter.h b/src/accountadapter.h new file mode 100644 index 00000000..7ebc19a6 --- /dev/null +++ b/src/accountadapter.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "qmladapterbase.h" + +#include +#include +#include + +#include "lrcinstance.h" +#include "utils.h" + +class AccountAdapter : public QmlAdapterBase +{ + Q_OBJECT + +public: + explicit AccountAdapter(QObject *parent = 0); + ~AccountAdapter(); + //Singleton + static AccountAdapter &instance(); + + /* + * Change to account corresponding to combox box index. + */ + Q_INVOKABLE void accountChanged(int index); + /* + * Create normal Jami account, SIP account and JAMS accounts. + */ + Q_INVOKABLE void createJamiAccount(QString registeredName, + const QVariantMap &settings, + QString photoBoothImgBase64, + bool isCreating); + Q_INVOKABLE void createSIPAccount(const QVariantMap &settings, QString photoBoothImgBase64); + Q_INVOKABLE void createJAMSAccount(const QVariantMap &settings); + /* + * Delete current account + */ + Q_INVOKABLE void deleteCurrentAccount(); + /* + * Setting related + */ + Q_INVOKABLE void settingsNeverShowAgain(bool checked); + Q_INVOKABLE void passwordSetStatusMessageBox(bool success, QString title, QString infoToDisplay); + /* + * conf property + */ + Q_INVOKABLE bool hasPassword(); + Q_INVOKABLE void setArchiveHasPassword(bool isHavePassword); + Q_INVOKABLE bool exportToFile(const QString &accountId, + const QString &path, + const QString &password = {}) const; + Q_INVOKABLE void setArchivePasswordAsync(const QString &accountID, const QString &password); + /* + * lrc instances functions wrappers + */ + Q_INVOKABLE bool savePassword(QString accountId, QString oldPassword, QString newPassword); + Q_INVOKABLE void startAudioMeter(bool async); + Q_INVOKABLE void stopAudioMeter(bool async); + Q_INVOKABLE void startPreviewing(bool force); + Q_INVOKABLE void stopPreviewing(); + Q_INVOKABLE bool hasVideoCall(); + Q_INVOKABLE bool isPreviewing(); + Q_INVOKABLE void setCurrAccDisplayName(QString text); + Q_INVOKABLE void setSelectedAccountId(QString accountId = {}); + Q_INVOKABLE void setSelectedConvId(QString accountId = {}); + + /* + * lrc model instances getter + */ + Q_INVOKABLE lrc::api::NewAccountModel *accoundModel(); + Q_INVOKABLE lrc::api::AVModel *avModel(); + Q_INVOKABLE lrc::api::DataTransferModel *dataTransferModel(); + + Q_INVOKABLE RenderManager *getRenderManager(); +signals: + + /* + * Trigger other components to reconnect account related signals. + */ + void accountSignalsReconnect(const QString &accountId); + void accountStatusChanged(); + void updateConversationForAddedContact(); + /* + * send report failure to QML to make it show the right UI state . + */ + void reportFailure(); + void accountAdded(bool showBackUp, int index); + +private: + void initQmlObject() override final; + void setSelectedAccount(const QString &accountId, int index); + void backToWelcomePage(int index); + void deselectConversation(); + + /* + * Make account signal connections. + */ + void connectAccount(const QString &accountId); + /* + * Implement what to do when creat accout fails. + */ + void connectFailure(); + + QMetaObject::Connection accountStatusChangedConnection_; + QMetaObject::Connection contactAddedConnection_; + QMetaObject::Connection addedToConferenceConnection_; +}; +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) +Q_DECLARE_METATYPE(AccountAdapter *) +#endif diff --git a/src/accountlistmodel.cpp b/src/accountlistmodel.cpp new file mode 100644 index 00000000..9b3eed41 --- /dev/null +++ b/src/accountlistmodel.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "accountlistmodel.h" + +#include + +#include "globalinstances.h" + +#include "lrcinstance.h" +#include "pixbufmanipulator.h" +#include "utils.h" + +AccountListModel::AccountListModel(QObject *parent) + : QAbstractListModel(parent) +{} + +AccountListModel::~AccountListModel() {} + +int +AccountListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + return LRCInstance::accountModel().getAccountList().size(); + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +AccountListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +AccountListModel::data(const QModelIndex &index, int role) const +{ + auto accountList = LRCInstance::accountModel().getAccountList(); + if (!index.isValid() || accountList.size() <= index.row()) { + return QVariant(); + } + + auto &accountInfo = LRCInstance::accountModel().getAccountInfo(accountList.at(index.row())); + + switch (role) { + case Role::Alias: + return QVariant(Utils::bestNameForAccount(accountInfo)); + case Role::Username: + return QVariant(Utils::secondBestNameForAccount(accountInfo)); + case Role::Type: + return QVariant( + Utils::toUnderlyingValue(accountInfo.profileInfo.type)); + case Role::Status: + return QVariant(Utils::toUnderlyingValue(accountInfo.status)); + case Role::Picture: + return QString::fromLatin1( + Utils::QImageToByteArray(Utils::accountPhoto(accountInfo)).toBase64().data()); + case Role::ID: + return QVariant(accountInfo.id); + } + return QVariant(); +} + +QHash +AccountListModel::roleNames() const +{ + QHash roles; + roles[Alias] = "Alias"; + roles[Username] = "Username"; + roles[Picture] = "Picture"; + roles[Type] = "Type"; + roles[Status] = "Status"; + roles[ID] = "ID"; + return roles; +} + +QModelIndex +AccountListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +AccountListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +AccountListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +AccountListModel::reset() +{ + beginResetModel(); + endResetModel(); +} diff --git a/src/accountlistmodel.h b/src/accountlistmodel.h new file mode 100644 index 00000000..134a31e2 --- /dev/null +++ b/src/accountlistmodel.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" + +class AccountListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Role { Alias = Qt::UserRole + 1, Username, Picture, Type, Status, ID }; + + explicit AccountListModel(QObject *parent = 0); + ~AccountListModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); +}; diff --git a/src/accountstomigratelistmodel.cpp b/src/accountstomigratelistmodel.cpp new file mode 100644 index 00000000..8123eec1 --- /dev/null +++ b/src/accountstomigratelistmodel.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "accountstomigratelistmodel.h" + +AccountsToMigrateListModel::AccountsToMigrateListModel(QObject *parent) + : QAbstractListModel(parent) +{} + +AccountsToMigrateListModel::~AccountsToMigrateListModel() {} + +int +AccountsToMigrateListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + auto accountList = LRCInstance::accountModel().getAccountList(); + + int countAccountToMigrate = 0; + + for (const QString &i : accountList) { + auto accountStatus = LRCInstance::accountModel().getAccountInfo(i).status; + if (accountStatus == lrc::api::account::Status::ERROR_NEED_MIGRATION) { + countAccountToMigrate++; + } + } + + return countAccountToMigrate; + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +AccountsToMigrateListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +AccountsToMigrateListModel::data(const QModelIndex &index, int role) const +{ + auto accountList = LRCInstance::accountModel().getAccountList(); + if (!index.isValid() || accountList.size() <= index.row()) { + return QVariant(); + } + + QList accountToMigrateList; + + for (QString i : accountList) { + auto accountStatus = LRCInstance::accountModel().getAccountInfo(i).status; + if (accountStatus == lrc::api::account::Status::ERROR_NEED_MIGRATION) { + accountToMigrateList.append(i); + } + } + + QString accountId = accountToMigrateList.at(index.row()); + + auto &avatarInfo = LRCInstance::accountModel().getAccountInfo(accountId); + + switch (role) { + case Role::Account_ID: + return QVariant(accountId); + case Role::ManagerUsername: + return QVariant(avatarInfo.confProperties.managerUsername); + case Role::ManagerUri: + return QVariant(avatarInfo.confProperties.managerUri); + case Role::Username: + return QVariant(avatarInfo.confProperties.username); + case Role::Alias: + return QVariant(LRCInstance::accountModel().getAccountInfo(accountId).profileInfo.alias); + case Role::Picture: + return QString::fromLatin1( + Utils::QImageToByteArray(Utils::accountPhoto(avatarInfo)).toBase64().data()); + } + return QVariant(); +} + +QHash +AccountsToMigrateListModel::roleNames() const +{ + QHash roles; + roles[Account_ID] = "Account_ID"; + roles[ManagerUsername] = "ManagerUsername"; + roles[ManagerUri] = "ManagerUri"; + roles[Username] = "Username"; + roles[Alias] = "Alias"; + roles[Picture] = "Picture"; + return roles; +} + +QModelIndex +AccountsToMigrateListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +AccountsToMigrateListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +AccountsToMigrateListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +AccountsToMigrateListModel::reset() +{ + beginResetModel(); + endResetModel(); +} diff --git a/src/accountstomigratelistmodel.h b/src/accountstomigratelistmodel.h new file mode 100644 index 00000000..b865d00b --- /dev/null +++ b/src/accountstomigratelistmodel.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class AccountsToMigrateListModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Role { + Account_ID = Qt::UserRole + 1, + ManagerUsername, + ManagerUri, + Username, + Alias, + Picture + }; + Q_ENUM(Role) + + explicit AccountsToMigrateListModel(QObject *parent = 0); + ~AccountsToMigrateListModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); +}; diff --git a/src/audiocodeclistmodel.cpp b/src/audiocodeclistmodel.cpp new file mode 100644 index 00000000..cf097ad4 --- /dev/null +++ b/src/audiocodeclistmodel.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "audiocodeclistmodel.h" + +AudioCodecListModel::AudioCodecListModel(QObject *parent) + : QAbstractListModel(parent) +{} + +AudioCodecListModel::~AudioCodecListModel() {} + +int +AudioCodecListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return LRCInstance::getCurrentAccountInfo().codecModel->getAudioCodecs().size(); + } + return 0; +} + +int +AudioCodecListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +AudioCodecListModel::data(const QModelIndex &index, int role) const +{ + auto audioCodecList = LRCInstance::getCurrentAccountInfo().codecModel->getAudioCodecs(); + if (!index.isValid() || audioCodecList.size() <= index.row()) { + return QVariant(); + } + + switch (role) { + case Role::AudioCodecName: + return QVariant(audioCodecList.at(index.row()).name); + case Role::IsEnabled: + return QVariant(audioCodecList.at(index.row()).enabled); + case Role::AudioCodecID: + return QVariant(audioCodecList.at(index.row()).id); + case Role::Samplerate: + return QVariant(audioCodecList.at(index.row()).samplerate); + } + return QVariant(); +} + +QHash +AudioCodecListModel::roleNames() const +{ + QHash roles; + roles[AudioCodecName] = "AudioCodecName"; + roles[IsEnabled] = "IsEnabled"; + roles[AudioCodecID] = "AudioCodecID"; + roles[Samplerate] = "Samplerate"; + return roles; +} + +QModelIndex +AudioCodecListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +AudioCodecListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +AudioCodecListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable + | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} diff --git a/src/audiocodeclistmodel.h b/src/audiocodeclistmodel.h new file mode 100644 index 00000000..fc58ee1a --- /dev/null +++ b/src/audiocodeclistmodel.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class AudioCodecListModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Role { AudioCodecName = Qt::UserRole + 1, IsEnabled, AudioCodecID, Samplerate }; + Q_ENUM(Role) + + explicit AudioCodecListModel(QObject *parent = 0); + ~AudioCodecListModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; +}; diff --git a/src/audioinputdevicemodel.cpp b/src/audioinputdevicemodel.cpp new file mode 100644 index 00000000..0687c4e4 --- /dev/null +++ b/src/audioinputdevicemodel.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "audioinputdevicemodel.h" + +AudioInputDeviceModel::AudioInputDeviceModel(QObject *parent) + : QAbstractListModel(parent) +{} + +AudioInputDeviceModel::~AudioInputDeviceModel() {} + +int +AudioInputDeviceModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + return LRCInstance::avModel().getAudioInputDevices().size(); + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +AudioInputDeviceModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +AudioInputDeviceModel::data(const QModelIndex &index, int role) const +{ + auto deviceList = LRCInstance::avModel().getAudioInputDevices(); + if (!index.isValid() || deviceList.size() <= index.row()) { + return QVariant(); + } + + switch (role) { + case Role::Device_ID: + return QVariant(deviceList.at(index.row())); + case Role::ID_UTF8: + return QVariant(deviceList.at(index.row()).toUtf8()); + } + return QVariant(); +} + +QHash +AudioInputDeviceModel::roleNames() const +{ + QHash roles; + roles[Device_ID] = "Device_ID"; + roles[ID_UTF8] = "ID_UTF8"; + return roles; +} + +QModelIndex +AudioInputDeviceModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +AudioInputDeviceModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +AudioInputDeviceModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +AudioInputDeviceModel::reset() +{ + beginResetModel(); + endResetModel(); +} + +int +AudioInputDeviceModel::getCurrentSettingIndex() +{ + QString currentId = LRCInstance::avModel().getInputDevice(); + auto resultList = match(index(0, 0), Device_ID, QVariant(currentId)); + + int resultRowIndex = 0; + if (resultList.size() > 0) { + resultRowIndex = resultList[0].row(); + } + + return resultRowIndex; +} diff --git a/src/audioinputdevicemodel.h b/src/audioinputdevicemodel.h new file mode 100644 index 00000000..03836bed --- /dev/null +++ b/src/audioinputdevicemodel.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class AudioInputDeviceModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Role { Device_ID = Qt::UserRole + 1, ID_UTF8 }; + Q_ENUM(Role) + + explicit AudioInputDeviceModel(QObject *parent = 0); + ~AudioInputDeviceModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); + /* + * This function is to get the current device id in the demon. + */ + Q_INVOKABLE int getCurrentSettingIndex(); +}; diff --git a/src/audiomanagerlistmodel.cpp b/src/audiomanagerlistmodel.cpp new file mode 100644 index 00000000..a28480bf --- /dev/null +++ b/src/audiomanagerlistmodel.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "audiomanagerlistmodel.h" + +AudioManagerListModel::AudioManagerListModel(QObject *parent) + : QAbstractListModel(parent) +{} + +AudioManagerListModel::~AudioManagerListModel() {} + +int +AudioManagerListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + return LRCInstance::avModel().getSupportedAudioManagers().size(); + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +AudioManagerListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +AudioManagerListModel::data(const QModelIndex &index, int role) const +{ + auto managerList = LRCInstance::avModel().getSupportedAudioManagers(); + if (!index.isValid() || managerList.size() <= index.row()) { + return QVariant(); + } + + switch (role) { + case Role::AudioManagerID: + return QVariant(managerList.at(index.row())); + case Role::ID_UTF8: + return QVariant(managerList.at(index.row()).toUtf8()); + } + return QVariant(); +} + +QHash +AudioManagerListModel::roleNames() const +{ + QHash roles; + roles[AudioManagerID] = "AudioManagerID"; + roles[ID_UTF8] = "ID_UTF8"; + return roles; +} + +QModelIndex +AudioManagerListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +AudioManagerListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +AudioManagerListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +AudioManagerListModel::reset() +{ + beginResetModel(); + endResetModel(); +} + +int +AudioManagerListModel::getCurrentSettingIndex() +{ + QString currentId = LRCInstance::avModel().getAudioManager(); + auto resultList = match(index(0, 0), AudioManagerID, QVariant(currentId)); + + int resultRowIndex = 0; + if (resultList.size() > 0) { + resultRowIndex = resultList[0].row(); + } + + return resultRowIndex; +} diff --git a/src/audiomanagerlistmodel.h b/src/audiomanagerlistmodel.h new file mode 100644 index 00000000..56f0b3ba --- /dev/null +++ b/src/audiomanagerlistmodel.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class AudioManagerListModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Role { AudioManagerID = Qt::UserRole + 1, ID_UTF8 }; + Q_ENUM(Role) + + explicit AudioManagerListModel(QObject *parent = 0); + ~AudioManagerListModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); + /* + * This function is to get the current device id in the demon. + */ + Q_INVOKABLE int getCurrentSettingIndex(); +}; diff --git a/src/audiooutputdevicemodel.cpp b/src/audiooutputdevicemodel.cpp new file mode 100644 index 00000000..733cc6d7 --- /dev/null +++ b/src/audiooutputdevicemodel.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "audiooutputdevicemodel.h" + +AudioOutputDeviceModel::AudioOutputDeviceModel(QObject *parent) + : QAbstractListModel(parent) +{} + +AudioOutputDeviceModel::~AudioOutputDeviceModel() {} + +int +AudioOutputDeviceModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + return LRCInstance::avModel().getAudioOutputDevices().size(); + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +AudioOutputDeviceModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +AudioOutputDeviceModel::data(const QModelIndex &index, int role) const +{ + auto deviceList = LRCInstance::avModel().getAudioOutputDevices(); + if (!index.isValid() || deviceList.size() <= index.row()) { + return QVariant(); + } + + switch (role) { + case Role::Device_ID: + return QVariant(deviceList.at(index.row())); + case Role::ID_UTF8: + return QVariant(deviceList.at(index.row()).toUtf8()); + } + return QVariant(); +} + +QHash +AudioOutputDeviceModel::roleNames() const +{ + QHash roles; + roles[Device_ID] = "Device_ID"; + roles[ID_UTF8] = "ID_UTF8"; + return roles; +} + +QModelIndex +AudioOutputDeviceModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +AudioOutputDeviceModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +AudioOutputDeviceModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +AudioOutputDeviceModel::reset() +{ + beginResetModel(); + endResetModel(); +} + +int +AudioOutputDeviceModel::getCurrentSettingIndex() +{ + QString currentId = LRCInstance::avModel().getOutputDevice(); + auto resultList = match(index(0, 0), Device_ID, QVariant(currentId)); + + int resultRowIndex = 0; + if (resultList.size() > 0) { + resultRowIndex = resultList[0].row(); + } + + return resultRowIndex; +} + +int +AudioOutputDeviceModel::getCurrentRingtoneDeviceIndex() +{ + QString currentId = LRCInstance::avModel().getRingtoneDevice(); + auto resultList = match(index(0, 0), Device_ID, QVariant(currentId)); + + int resultRowIndex = 0; + if (resultList.size() > 0) { + resultRowIndex = resultList[0].row(); + } + + return resultRowIndex; +} diff --git a/src/audiooutputdevicemodel.h b/src/audiooutputdevicemodel.h new file mode 100644 index 00000000..e0148a1e --- /dev/null +++ b/src/audiooutputdevicemodel.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class AudioOutputDeviceModel : public QAbstractListModel +{ + Q_OBJECT +public: +public: + enum Role { Device_ID = Qt::UserRole + 1, ID_UTF8 }; + Q_ENUM(Role) + + explicit AudioOutputDeviceModel(QObject *parent = 0); + ~AudioOutputDeviceModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); + /* + * This function is to get the current device id in the demon. + */ + Q_INVOKABLE int getCurrentSettingIndex(); + /* + * This function is to get the current ringtone device id in the demon. + */ + Q_INVOKABLE int getCurrentRingtoneDeviceIndex(); +}; diff --git a/src/avadapter.cpp b/src/avadapter.cpp new file mode 100644 index 00000000..406e2085 --- /dev/null +++ b/src/avadapter.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author : Edric Ladent Milaret + * Author : Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "avadapter.h" + +#include "lrcinstance.h" + +#include + +AvAdapter::AvAdapter(QObject *parent) + : QmlAdapterBase(parent) +{} + +AvAdapter::~AvAdapter() {} + +void +AvAdapter::initQmlObject() +{} + +QVariantMap +AvAdapter::populateVideoDeviceContextMenuItem() +{ + auto conversation = LRCInstance::getCurrentConversation(); + auto call = LRCInstance::getCallInfoForConversation(conversation); + if (!call) { + return QVariantMap(); + } + + auto activeDevice = LRCInstance::avModel().getCurrentRenderedDevice(call->id); + + /* + * Create a list of video input devices. + */ + QVariantMap deciveContextMenuNeededInfo; + auto devices = LRCInstance::avModel().getDevices(); + for (int i = 0; i < devices.size(); i++) { + try { + auto settings = LRCInstance::avModel().getDeviceSettings(devices[i]); + deciveContextMenuNeededInfo[settings.name] = QVariant(devices[i] == activeDevice.name); + } catch (...) { + qDebug().noquote() << "Error in getting device settings"; + } + } + + /* + * Add size parameter into the map since in qml there is no way to get the size. + */ + deciveContextMenuNeededInfo["size"] = QVariant(deciveContextMenuNeededInfo.size()); + + return deciveContextMenuNeededInfo; +} + +void +AvAdapter::onVideoContextMenuDeviceItemClicked(const QString &deviceName) +{ + auto deviceId = LRCInstance::avModel().getDeviceIdFromName(deviceName); + if (deviceId.isEmpty()) { + qWarning() << "Couldn't find device: " << deviceName; + return; + } + LRCInstance::avModel().switchInputTo(deviceId); + LRCInstance::avModel().setCurrentVideoCaptureDevice(deviceId); +} + +void +AvAdapter::shareEntireScreen(int screenNumber) +{ + QScreen *screen = qApp->screens().at(screenNumber); + if (!screen) + return; + QRect rect = screen ? screen->geometry() : qApp->primaryScreen()->geometry(); + LRCInstance::avModel().setDisplay(screenNumber, rect.x(), rect.y(), rect.width(), rect.height()); +} + +const QString +AvAdapter::captureScreen(int screenNumber) +{ + QScreen *screen = qApp->screens().at(screenNumber); + if (!screen) + return QString(""); + /* + * The screen window id is always 0. + */ + auto pixmap = screen->grabWindow(0); + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + pixmap.save(&buffer, "PNG"); + return QString::fromLatin1(buffer.data().toBase64().data()); +} + +void +AvAdapter::shareFile(const QString &filePath) +{ + LRCInstance::avModel().setInputFile(filePath); +} + +void +AvAdapter::shareScreenArea(int screenNumber, int x, int y, int width, int height) +{ + QScreen *screen = qApp->screens().at(screenNumber); + if (!screen) + return; + QRect rect = screen ? screen->geometry() : qApp->primaryScreen()->geometry(); + + /* + * Provide minimum width, height. + * Need to add screen x, y initial value to the setDisplay api call. + */ + LRCInstance::avModel().setDisplay(screenNumber, + rect.x() + x, + rect.y() + y, + width < 128 ? 128 : width, + height < 128 ? 128 : height); +} \ No newline at end of file diff --git a/src/avadapter.h b/src/avadapter.h new file mode 100644 index 00000000..1c300e88 --- /dev/null +++ b/src/avadapter.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "qmladapterbase.h" + +#include +#include +#include + +class AvAdapter : public QmlAdapterBase +{ + Q_OBJECT + +public: + explicit AvAdapter(QObject *parent = nullptr); + ~AvAdapter(); + + /* + * Return needed info for populating video device context menu item. + */ + Q_INVOKABLE QVariantMap populateVideoDeviceContextMenuItem(); + + /* + * Preview video input switching. + */ + Q_INVOKABLE void onVideoContextMenuDeviceItemClicked(const QString &deviceName); + + /* + * Share the screen specificed by screen number. + */ + Q_INVOKABLE void shareEntireScreen(int screenNumber); + + /* + * Take snap shot of the screen by returning base64 image string. + */ + Q_INVOKABLE const QString captureScreen(int screenNumber); + + /* + * Share a media file. + */ + Q_INVOKABLE void shareFile(const QString &filePath); + + /* + * Select screen area to display. + */ + Q_INVOKABLE void shareScreenArea(int screenNumber, int x, int y, int width, int height); + +private: + void initQmlObject() override; +}; diff --git a/src/bannedlistmodel.cpp b/src/bannedlistmodel.cpp new file mode 100644 index 00000000..79fecd43 --- /dev/null +++ b/src/bannedlistmodel.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Isa Nanic + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "bannedlistmodel.h" +#include "lrcinstance.h" + +BannedListModel::BannedListModel(QObject *parent) + : QAbstractListModel(parent) +{} + +BannedListModel::~BannedListModel() {} + +int +BannedListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return LRCInstance::getCurrentAccountInfo().contactModel->getBannedContacts().size(); + } + return 0; +} + +int +BannedListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +BannedListModel::data(const QModelIndex &index, int role) const +{ + auto contactList = LRCInstance::getCurrentAccountInfo().contactModel->getBannedContacts(); + if (!index.isValid() || contactList.size() <= index.row()) { + return QVariant(); + } + + auto contactInfo = LRCInstance::getCurrentAccountInfo().contactModel->getContact( + contactList.at(index.row())); + + switch (role) { + case Role::ContactName: + return QVariant(contactInfo.registeredName); + case Role::ContactID: + return QVariant(contactInfo.profileInfo.uri); + case Role::ContactPicture: + QImage avatarImage = Utils::fallbackAvatar(QSize(48, 48), + contactInfo.profileInfo.uri, + contactInfo.registeredName); + + return QString::fromLatin1(Utils::QImageToByteArray(avatarImage).toBase64().data()); + } + return QVariant(); +} + +QHash +BannedListModel::roleNames() const +{ + QHash roles; + roles[ContactName] = "ContactName"; + roles[ContactID] = "ContactID"; + roles[ContactPicture] = "ContactPicture"; + return roles; +} + +QModelIndex +BannedListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +BannedListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +BannedListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +BannedListModel::reset() +{ + beginResetModel(); + endResetModel(); +} diff --git a/src/bannedlistmodel.h b/src/bannedlistmodel.h new file mode 100644 index 00000000..8dbb7a0f --- /dev/null +++ b/src/bannedlistmodel.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Isa Nanic + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +class BannedListModel : public QAbstractListModel +{ + Q_OBJECT + BannedListModel(const BannedListModel &cpy); + +public: + enum Role { ContactName = Qt::UserRole + 1, ContactID, ContactPicture }; + Q_ENUM(Role) + + explicit BannedListModel(QObject *parent = nullptr); + ~BannedListModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); +}; diff --git a/src/calladapter.cpp b/src/calladapter.cpp new file mode 100644 index 00000000..c1b32402 --- /dev/null +++ b/src/calladapter.cpp @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Anthony Lonard + * Author: Olivier Soldano + * Author: Andreas Traczyk + * Author: Isa Nanic + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "calladapter.h" + +#include "globalsystemtray.h" +#include "utils.h" + +CallAdapter::CallAdapter(QObject *parent) + : QmlAdapterBase(parent) + , oneSecondTimer_(new QTimer(this)) +{} + +CallAdapter::~CallAdapter() {} + +void +CallAdapter::initQmlObject() +{ + connectCallStatusChanged(LRCInstance::getCurrAccId()); + + connect(&LRCInstance::behaviorController(), + &BehaviorController::showIncomingCallView, + this, + &CallAdapter::slotShowIncomingCallView); + connect(&LRCInstance::behaviorController(), + &BehaviorController::showCallView, + this, + &CallAdapter::slotShowCallView); +} + +void +CallAdapter::placeAudioOnlyCall() +{ + auto convInfo = LRCInstance::getCurrentConversation(); + if (!convInfo.uid.isEmpty()) { + LRCInstance::getCurrentConversationModel()->placeAudioOnlyCall(convInfo.uid); + } +} + +void +CallAdapter::placeCall() +{ + auto convInfo = LRCInstance::getCurrentConversation(); + if (!convInfo.uid.isEmpty()) { + LRCInstance::getCurrentConversationModel()->placeCall(convInfo.uid); + } +} + +void +CallAdapter::hangUpACall(const QString &accountId, const QString &convUid) +{ + auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId); + if (!convInfo.uid.isEmpty()) { + LRCInstance::getAccountInfo(accountId).callModel->hangUp(convInfo.callId); + } +} + +void +CallAdapter::refuseACall(const QString &accountId, const QString &convUid) +{ + auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId); + if (!convInfo.uid.isEmpty()) { + LRCInstance::getAccountInfo(accountId).callModel->refuse(convInfo.callId); + } +} + +void +CallAdapter::acceptACall(const QString &accountId, const QString &convUid) +{ + emit incomingCallNeedToSetupMainView(accountId, convUid); + auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId); + if (!convInfo.uid.isEmpty()) { + LRCInstance::getAccountInfo(accountId).callModel->accept(convInfo.callId); + auto &accInfo = LRCInstance::getAccountInfo(convInfo.accountId); + accInfo.callModel->setCurrentCall(convInfo.callId); + } +} + +void +CallAdapter::slotShowIncomingCallView(const QString &accountId, const conversation::Info &convInfo) +{ + auto callModel = LRCInstance::getCurrentCallModel(); + + if (!callModel->hasCall(convInfo.callId)) { + /* + * Connection to close potential incoming call page when it is not current account. + */ + auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + + QObject::disconnect(closeIncomingCallPageConnection_); + + closeIncomingCallPageConnection_ + = QObject::connect(accInfo.callModel.get(), + &lrc::api::NewCallModel::callStatusChanged, + [this, accountId, uid = convInfo.uid](const QString &callId) { + auto &accInfo = LRCInstance::accountModel().getAccountInfo( + accountId); + auto &callModel = accInfo.callModel; + auto call = callModel->getCall(callId); + + switch (call.status) { + case lrc::api::call::Status::INVALID: + case lrc::api::call::Status::INACTIVE: + case lrc::api::call::Status::ENDED: + case lrc::api::call::Status::PEER_BUSY: + case lrc::api::call::Status::TIMEOUT: + case lrc::api::call::Status::TERMINATING: { + if (!uid.isEmpty()) + emit closePotentialIncomingCallPageWindow(accountId, uid); + break; + } + default: + break; + } + + emit updateConversationSmartList(); + QObject::disconnect(closeIncomingCallPageConnection_); + }); + /* + * Show incoming call page only. + */ + emit showIncomingCallPage(accountId, convInfo.uid); + return; + } + + auto call = callModel->getCall(convInfo.callId); + auto isCallSelected = LRCInstance::getCurrentConvUid() == convInfo.uid; + + if (call.isOutgoing) { + if (isCallSelected) { + emit showOutgoingCallPage(accountId, convInfo.uid); + emit showCallStack(accountId, convInfo.uid); + } + } else { + auto selectedAccountId = LRCInstance::getCurrentAccountInfo().id; + auto accountProperties = LRCInstance::accountModel().getAccountConfig(selectedAccountId); + if (accountProperties.autoAnswer) { + /* + * TODO: Auto answer + */ + } else { + emit showIncomingCallPage(accountId, convInfo.uid); + } + } + + emit callStatusChanged(lrc::api::call::to_string(call.status), accountId, convInfo.uid); + + emit updateConversationSmartList(); +} + +void +CallAdapter::slotShowCallView(const QString &accountId, const lrc::api::conversation::Info &convInfo) +{ + updateCall(convInfo.uid, accountId); +} + +void +CallAdapter::updateCall(const QString &convUid, const QString &accountId, bool forceCallOnly) +{ + accountId_ = accountId.isEmpty() ? accountId_ : accountId; + convUid_ = convUid.isEmpty() ? convUid_ : convUid; + + auto convInfo = LRCInstance::getConversationFromConvUid(convUid_, accountId_); + if (convInfo.uid.isEmpty()) { + return; + } + + auto call = LRCInstance::getCallInfoForConversation(convInfo, forceCallOnly); + if (!call) { + return; + } + + if (call->isAudioOnly) { + emit showAudioCallPage(accountId_, convUid_); + } else { + emit showVideoCallPage(accountId_, convUid_, call->id); + } + + updateCallOverlay(convInfo); + + /* + * Preview. + */ + emit previewVisibilityNeedToChange(shouldShowPreview(forceCallOnly)); + + emit showCallStack(accountId_, convUid_); +} + +bool +CallAdapter::shouldShowPreview(bool force) +{ + bool shouldShowPreview{false}; + auto convInfo = LRCInstance::getConversationFromConvUid(convUid_, accountId_); + if (convInfo.uid.isEmpty()) { + return shouldShowPreview; + } + auto call = LRCInstance::getCallInfoForConversation(convInfo, force); + if (call) { + shouldShowPreview = !call->isAudioOnly && !(call->status == lrc::api::call::Status::PAUSED) + && !call->videoMuted && call->type != lrc::api::call::Type::CONFERENCE; + } + return shouldShowPreview; +} + +void +CallAdapter::connectCallStatusChanged(const QString &accountId) +{ + auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + + QObject::disconnect(callStatusChangedConnection_); + + callStatusChangedConnection_ = QObject::connect( + accInfo.callModel.get(), + &lrc::api::NewCallModel::callStatusChanged, + [this, accountId](const QString &callId) { + auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + auto &callModel = accInfo.callModel; + auto call = callModel->getCall(callId); + + /* + * Change status label text. + */ + auto convInfo = LRCInstance::getConversationFromCallId(callId); + if (!convInfo.uid.isEmpty()) { + emit callStatusChanged(lrc::api::call::to_string(call.status), + accountId, + convInfo.uid); + } + + switch (call.status) { + case lrc::api::call::Status::INVALID: + case lrc::api::call::Status::INACTIVE: + case lrc::api::call::Status::ENDED: + case lrc::api::call::Status::PEER_BUSY: + case lrc::api::call::Status::TIMEOUT: + case lrc::api::call::Status::TERMINATING: { + LRCInstance::renderer()->removeDistantRenderer(callId); + if (convInfo.uid.isEmpty()) { + break; + } + /* + * If it's a conference, change the smartlist index + * to the next remaining participant. + */ + bool forceCallOnly{false}; + if (!convInfo.confId.isEmpty()) { + auto callList = LRCInstance::getAPI().getConferenceSubcalls(convInfo.confId); + if (callList.empty()) { + auto lastConferencee = LRCInstance::instance().popLastConferencee( + convInfo.confId); + callList.append(lastConferencee); + forceCallOnly = true; + } + for (const auto &callId : callList) { + if (!callModel->hasCall(callId)) { + continue; + } + auto otherConv = LRCInstance::getConversationFromCallId(callId); + if (!otherConv.uid.isEmpty() && otherConv.uid != convInfo.uid) { + /* + * Reset the call view corresponding accountId, uid. + */ + LRCInstance::setSelectedConvId(otherConv.uid); + showCallStack(otherConv.accountId, otherConv.uid, true); + updateCall(otherConv.uid, otherConv.accountId, forceCallOnly); + } + } + } else { + emit closeCallStack(accountId, convInfo.uid); + emit closePotentialIncomingCallPageWindow(accountId, convInfo.uid); + } + + break; + } + case lrc::api::call::Status::CONNECTED: + case lrc::api::call::Status::IN_PROGRESS: { + auto convInfo = LRCInstance::getConversationFromCallId(callId, accountId); + if (!convInfo.uid.isEmpty() && convInfo.uid == LRCInstance::getCurrentConvUid()) { + accInfo.conversationModel->selectConversation(convInfo.uid); + } + LRCInstance::renderer()->addDistantRenderer(callId); + updateCall(); + LRCInstance::getAccountInfo(accountId).callModel->setCurrentCall(callId); + break; + } + case lrc::api::call::Status::PAUSED: + updateCall(); + default: + break; + } + + emit updateConversationSmartList(); + }); +} + +/* + * For Call Overlay + */ +void +CallAdapter::updateCallOverlay(const lrc::api::conversation::Info &convInfo) +{ + setTime(accountId_, convUid_); + QObject::disconnect(oneSecondTimer_); + QObject::connect(oneSecondTimer_, &QTimer::timeout, [this] { setTime(accountId_, convUid_); }); + oneSecondTimer_->start(20); + + auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); + + auto call = LRCInstance::getCallInfoForConversation(convInfo); + if (!call) { + return; + } + + bool isPaused = call->status == lrc::api::call::Status::PAUSED; + bool isAudioOnly = call->isAudioOnly && !isPaused; + bool isAudioMuted = call->audioMuted && (call->status != lrc::api::call::Status::PAUSED); + bool isVideoMuted = call->videoMuted && !isPaused && !call->isAudioOnly; + bool isRecording = accInfo.callModel->isRecording(convInfo.callId); + + emit updateOverlay(isPaused, + isAudioOnly, + isAudioMuted, + isVideoMuted, + isRecording, + accInfo.profileInfo.type == lrc::api::profile::Type::SIP, + !convInfo.confId.isEmpty(), + Utils::bestNameForConversation(convInfo, + *LRCInstance::getCurrentConversationModel())); +} + +void +CallAdapter::hangUpThisCall() +{ + auto convInfo = LRCInstance::getConversationFromConvUid(convUid_, accountId_); + if (!convInfo.uid.isEmpty()) { + auto callModel = LRCInstance::getAccountInfo(accountId_).callModel.get(); + if (callModel->hasCall(convInfo.callId)) { + /* + * Store the last remaining participant of the conference, + * so we can switch the smartlist index after termination. + */ + if (!convInfo.confId.isEmpty()) { + auto callList = LRCInstance::getAPI().getConferenceSubcalls(convInfo.confId); + if (callList.size() == 2) { + for (const auto &cId : callList) { + if (cId != convInfo.callId) { + LRCInstance::instance().pushLastConferencee(convInfo.confId, cId); + } + } + } + } + callModel->hangUp(convInfo.callId); + } + } +} + +void +CallAdapter::holdThisCallToggle() +{ + auto callId = LRCInstance::getCallIdForConversationUid(convUid_, accountId_); + if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) { + return; + } + auto callModel = LRCInstance::getCurrentCallModel(); + if (callModel->hasCall(callId)) { + callModel->togglePause(callId); + } + emit showOnHoldLabel(true); +} + +void +CallAdapter::muteThisCallToggle() +{ + auto callId = LRCInstance::getCallIdForConversationUid(convUid_, accountId_); + if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) { + return; + } + auto callModel = LRCInstance::getCurrentCallModel(); + if (callModel->hasCall(callId)) { + callModel->toggleMedia(callId, lrc::api::NewCallModel::Media::AUDIO); + } +} + +void +CallAdapter::recordThisCallToggle() +{ + auto callId = LRCInstance::getCallIdForConversationUid(convUid_, accountId_); + if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) { + return; + } + auto callModel = LRCInstance::getCurrentCallModel(); + if (callModel->hasCall(callId)) { + callModel->toggleAudioRecord(callId); + } +} + +void +CallAdapter::videoPauseThisCallToggle() +{ + auto callId = LRCInstance::getCallIdForConversationUid(convUid_, accountId_); + if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) { + return; + } + auto callModel = LRCInstance::getCurrentCallModel(); + if (callModel->hasCall(callId)) { + callModel->toggleMedia(callId, lrc::api::NewCallModel::Media::VIDEO); + } + emit previewVisibilityNeedToChange(shouldShowPreview(false)); +} + +void +CallAdapter::setTime(const QString &accountId, const QString &convUid) +{ + auto callId = LRCInstance::getCallIdForConversationUid(convUid, accountId); + if (callId.isEmpty() || !LRCInstance::getCurrentCallModel()->hasCall(callId)) { + return; + } + auto callInfo = LRCInstance::getCurrentCallModel()->getCall(callId); + if (callInfo.status == lrc::api::call::Status::IN_PROGRESS + || callInfo.status == lrc::api::call::Status::PAUSED) { + auto timeString = LRCInstance::getCurrentCallModel()->getFormattedCallDuration(callId); + emit updateTimeText(timeString); + } +} \ No newline at end of file diff --git a/src/calladapter.h b/src/calladapter.h new file mode 100644 index 00000000..25768e18 --- /dev/null +++ b/src/calladapter.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "lrcinstance.h" +#include "qmladapterbase.h" + +#include +#include + +class CallAdapter : public QmlAdapterBase +{ + Q_OBJECT + +public: + explicit CallAdapter(QObject *parent = nullptr); + ~CallAdapter(); + + /* + * This is needed to be public since it has to be recognized by qml. + */ + Q_INVOKABLE void initQmlObject() override; + + Q_INVOKABLE void placeAudioOnlyCall(); + Q_INVOKABLE void placeCall(); + Q_INVOKABLE void hangUpACall(const QString &accountId, const QString &convUid); + Q_INVOKABLE void refuseACall(const QString &accountId, const QString &convUid); + Q_INVOKABLE void acceptACall(const QString &accountId, const QString &convUid); + + Q_INVOKABLE void connectCallStatusChanged(const QString &accountId); + + /* + * For Call Overlay + */ + Q_INVOKABLE void hangUpThisCall(); + Q_INVOKABLE void holdThisCallToggle(); + Q_INVOKABLE void muteThisCallToggle(); + Q_INVOKABLE void recordThisCallToggle(); + Q_INVOKABLE void videoPauseThisCallToggle(); + +signals: + void showOutgoingCallPage(const QString &accountId, const QString &convUid); + void showIncomingCallPage(const QString &accountId, const QString &convUid); + void showAudioCallPage(const QString &accountId, const QString &convUid); + void showVideoCallPage(const QString &accountId, const QString &convUid, const QString &callId); + void showCallStack(const QString &accountId, const QString &convUid, bool forceReset = false); + void closeCallStack(const QString &accountId, const QString &convUid); + void closePotentialIncomingCallPageWindow(const QString &accountId, const QString &convUid); + void callStatusChanged(const QString &status, const QString &accountId, const QString &convUid); + void updateConversationSmartList(); + + void incomingCallNeedToSetupMainView(const QString &accountId, const QString &convUid); + void previewVisibilityNeedToChange(bool visible); + + /* + * For Call Overlay + */ + void updateTimeText(const QString &time); + void showOnHoldLabel(bool isPaused); + void updateOverlay(bool isPaused, + bool isAudioOnly, + bool isAudioMuted, + bool isVideoMuted, + bool isRecording, + bool isSIP, + bool isConferenceCall, + const QString &bestName); + +public slots: + void slotShowIncomingCallView(const QString &accountId, + const lrc::api::conversation::Info &convInfo); + void slotShowCallView(const QString &accountId, const lrc::api::conversation::Info &convInfo); + +private: + void updateCall(const QString &convUid = {}, + const QString &accountId = {}, + bool forceCallOnly = false); + bool shouldShowPreview(bool force); + + /* + * Current conf/call info. + */ + QString accountId_; + QString convUid_; + + QMetaObject::Connection callStatusChangedConnection_; + QMetaObject::Connection closeIncomingCallPageConnection_; + + /* + * For Call Overlay + */ + void setTime(const QString &accountId, const QString &convUid); + void updateCallOverlay(const lrc::api::conversation::Info &convInfo); + + QTimer *oneSecondTimer_; +}; diff --git a/src/clientwrapper.cpp b/src/clientwrapper.cpp new file mode 100644 index 00000000..f099de65 --- /dev/null +++ b/src/clientwrapper.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "clientwrapper.h" + +ClientWrapper::ClientWrapper(QObject *parent) + : QObject(parent) +{ + connect(getAccountAdapter(), &AccountAdapter::accountSignalsReconnect, [this]() { + emit accountModelChanged(); + emit avmodelChanged(); + emit dataTransferModelChanged(); + emit contactModelChanged(); + emit deviceModelChanged(); + }); +} + +NameDirectory * +ClientWrapper::getNameDirectory() +{ + return &(NameDirectory::instance()); +} + +UtilsAdapter * +ClientWrapper::getUtilsAdapter() +{ + return &(UtilsAdapter::instance()); +} + +SettingsAdaptor * +ClientWrapper::getSettingsAdaptor() +{ + return &(SettingsAdaptor::instance()); +} + +LRCInstance * +ClientWrapper::getLRCInstance() +{ + return &(LRCInstance::instance()); +} + +AccountAdapter * +ClientWrapper::getAccountAdapter() +{ + return &(AccountAdapter::instance()); +} + +RenderManager * +ClientWrapper::getRenderManager() +{ + return LRCInstance::renderer(); +} + +lrc::api::NewAccountModel * +ClientWrapper::getAccountModel() +{ + return &(LRCInstance::accountModel()); +} + +lrc::api::AVModel * +ClientWrapper::getAvModel() +{ + return &(LRCInstance::avModel()); +} + +lrc::api::PluginModel * +ClientWrapper::getPluginModel() +{ + return &(LRCInstance::pluginModel()); +} + +lrc::api::DataTransferModel * +ClientWrapper::getDataTransferModel() +{ + return &(LRCInstance::dataTransferModel()); +} + +lrc::api::ContactModel * +ClientWrapper::getContactModel() +{ + return getSettingsAdaptor()->getCurrentAccountInfo().contactModel.get(); +} + +lrc::api::NewDeviceModel * +ClientWrapper::getDeviceModel() +{ + return getSettingsAdaptor()->getCurrentAccountInfo().deviceModel.get(); +} diff --git a/src/clientwrapper.h b/src/clientwrapper.h new file mode 100644 index 00000000..d6e321a9 --- /dev/null +++ b/src/clientwrapper.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "accountadapter.h" +#include "accountlistmodel.h" +#include "audiocodeclistmodel.h" +#include "avadapter.h" +#include "bannedlistmodel.h" +#include "calladapter.h" +#include "contactadapter.h" +#include "conversationsadapter.h" +#include "deviceitemlistmodel.h" +#include "pluginitemlistmodel.h" +#include "preferenceitemlistmodel.h" +#include "distantrenderer.h" +#include "globalinstances.h" +#include "globalsystemtray.h" +#include "messagesadapter.h" +#include "namedirectory.h" +#include "pixbufmanipulator.h" +#include "previewrenderer.h" +#include "qrimageprovider.h" +#include "settingsadaptor.h" +#include "utils.h" +#include "version.h" +#include "videocodeclistmodel.h" + +#include + +class ClientWrapper : public QObject +{ + Q_OBJECT + + Q_PROPERTY(UtilsAdapter *utilsAdaptor READ getUtilsAdapter NOTIFY utilsAdaptorChanged) + Q_PROPERTY(SettingsAdaptor *settingsAdaptor READ getSettingsAdaptor NOTIFY settingsAdaptorChanged) + Q_PROPERTY(NameDirectory *nameDirectory READ getNameDirectory NOTIFY nameDirectoryChanged) + Q_PROPERTY(LRCInstance *lrcInstance READ getLRCInstance NOTIFY lrcInstanceChanged) + Q_PROPERTY(AccountAdapter *accountAdaptor READ getAccountAdapter NOTIFY accountAdaptorChanged) + Q_PROPERTY(RenderManager *renderManager READ getRenderManager NOTIFY renderManagerChanged) + Q_PROPERTY(lrc::api::NewAccountModel *accountModel READ getAccountModel NOTIFY accountModelChanged) + Q_PROPERTY(lrc::api::AVModel *avmodel READ getAvModel NOTIFY avmodelChanged) + Q_PROPERTY(lrc::api::DataTransferModel *dataTransferModel READ getDataTransferModel NOTIFY dataTransferModelChanged) + Q_PROPERTY(lrc::api::ContactModel *contactModel READ getContactModel NOTIFY contactModelChanged) + Q_PROPERTY(lrc::api::NewDeviceModel *deviceModel READ getDeviceModel NOTIFY deviceModelChanged) + Q_PROPERTY(lrc::api::PluginModel *pluginModel READ getPluginModel) +public: + explicit ClientWrapper(QObject *parent = nullptr); + + NameDirectory *getNameDirectory(); + UtilsAdapter *getUtilsAdapter(); + SettingsAdaptor *getSettingsAdaptor(); + LRCInstance *getLRCInstance(); + AccountAdapter *getAccountAdapter(); + + RenderManager *getRenderManager(); + lrc::api::NewAccountModel *getAccountModel(); + lrc::api::AVModel *getAvModel(); + lrc::api::DataTransferModel *getDataTransferModel(); + + lrc::api::ContactModel *getContactModel(); + lrc::api::NewDeviceModel *getDeviceModel(); + lrc::api::PluginModel *getPluginModel(); + +signals: + void utilsAdaptorChanged(); + void settingsAdaptorChanged(); + void nameDirectoryChanged(); + void lrcInstanceChanged(); + void accountAdaptorChanged(); + void renderManagerChanged(); + void accountModelChanged(); + void avmodelChanged(); + void dataTransferModelChanged(); + void contactModelChanged(); + void deviceModelChanged(); +}; +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) +Q_DECLARE_METATYPE(ClientWrapper *) +#endif diff --git a/src/commoncomponents/AccountMigrationDialog.qml b/src/commoncomponents/AccountMigrationDialog.qml new file mode 100644 index 00000000..af289003 --- /dev/null +++ b/src/commoncomponents/AccountMigrationDialog.qml @@ -0,0 +1,969 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 + +import "../constant" +import "../wizardview/components" +/* + * Account Migration Dialog for migrating account + */ + +Window{ + id: accountMigrationDialog + + AccountsToMigrateListModel{ + id: accountsToMigrateListModel + } + + property string accountID: "" + property string password: "" + + property bool nonOperationClosing: true + property bool successState : true + property string imgBase64: "" + + signal accountMigrationFinished + + function startAccountMigrationOfTopStack(){ + passwordInputLineEdit.clear() + accountsToMigrateListModel.reset() + + if(accountsToMigrateListModel.rowCount() <= 0){ + closeWithoutOperation() + + return false + } + + var managerUsername = accountsToMigrateListModel.data(accountsToMigrateListModel.index( + 0, 0), AccountsToMigrateListModel.ManagerUsername) + var managerUri = accountsToMigrateListModel.data(accountsToMigrateListModel.index( + 0, 0), AccountsToMigrateListModel.ManagerUri) + var username = accountsToMigrateListModel.data(accountsToMigrateListModel.index( + 0, 0), AccountsToMigrateListModel.Username) + var alias = accountsToMigrateListModel.data(accountsToMigrateListModel.index( + 0, 0), AccountsToMigrateListModel.Alias) + + if(managerUri.length !== 0){ + managerUriInputLabel.text = managerUri + } else { + managerUriInputLabel.text = "N/A" + } + + if(username.length !== 0){ + usernameInputLabel.text = username + } else if(managerUsername.length !== 0){ + usernameInputLabel.text = managerUsername + } else { + usernameInputLabel.text = "N/A" + } + + if(alias.length !== 0){ + aliasInputLabel.text = alias + } else { + aliasInputLabel.text = "N/A" + } + + accountID = accountsToMigrateListModel.data(accountsToMigrateListModel.index( + 0, 0), AccountsToMigrateListModel.Account_ID) + + imgBase64 = accountsToMigrateListModel.data(accountsToMigrateListModel.index( + 0, 0), AccountsToMigrateListModel.Picture) + + connectionMigrationEnded.enabled = false + migrationPushButton.enabled = false + stackedWidget.currentIndex = 0 + + successState = true + nonOperationClosing = true + + accountMigrationDialog.show() + return true + } + + function checkIfAccountMigrationFinishedAndClose(){ + accountsToMigrateListModel.reset() + if(accountsToMigrateListModel.rowCount() > 0){ + startAccountMigrationOfTopStack() + } else { + accountMigrationFinished() + if(!nonOperationClosing){ + nonOperationClosing = true + accountMigrationDialog.close() + } + } + } + + function acceptMigration(){ + nonOperationClosing = false + accountsToMigrateListModel.dataChanged(accountsToMigrateListModel.index(0, 0), + accountsToMigrateListModel.index( + accountsToMigrateListModel.rowCount() - 1, 0)) + checkIfAccountMigrationFinishedAndClose() + } + + function refuseMigrationAndDeleteAccount(){ + ClientWrapper.accountModel.removeAccount(accountID) + acceptMigration() + } + + function closeWithoutOperation(){ + nonOperationClosing = false + accountMigrationDialog.close() + } + + Timer{ + id: timerFailureReturn + + interval: 1000 + repeat: false + + onTriggered: { + stackedWidget.currentIndex = 0 + successState = true + } + } + + Connections{ + id: connectionMigrationEnded + enabled: false + target: ClientWrapper.accountModel + + function onMigrationEnded(accountIdIn, ok){ + nonOperationClosing = true + connectionMigrationEnded.enabled = false + if(accountID !== accountIdIn){ + return + } + if(!ok){ + successState = false + timerFailureReturn.restart() + } else { + acceptMigration() + } + } + } + + function slotMigrationButtonClicked(){ + successState = true + stackedWidget.currentIndex = 1 + + connectionMigrationEnded.enabled = true + ClientWrapper.accountAdaptor.setArchivePasswordAsync(accountID,password) + } + + function slotDeleteButtonClicked(){ + nonOperationClosing = false + refuseMigrationAndDeleteAccount() + } + + onClosing: { + connectionMigrationEnded.enabled = false + stackedWidget.currentIndex = 0 + accountID = "" + password = "" + passwordInputLineEdit.clear() + managerUriInputLabel.text = "" + usernameInputLabel.text = "" + aliasInputLabel.text = "" + + if(nonOperationClosing){ + checkIfAccountMigrationFinishedAndClose() + } + nonOperationClosing = true + } + + visible: false + + width: 455 + height: 594 + + Component.onCompleted: { + setX(Screen.width / 2 - width / 2) + setY(Screen.height / 2 - height / 2) + } + + ColumnLayout { + anchors.fill: parent + Layout.alignment: Qt.AlignHCenter + + StackLayout{ + id: stackedWidget + + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + + currentIndex: 0 + + Rectangle{ + id: accountMigrationPage + + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + Layout.topMargin: 11 + Layout.bottomMargin: 11 + + x: (parent.width - width) /2 + y: (parent.height - height) /2 + + ScrollView { + id: accountMigrationPageScroll + anchors.fill: parent + + clip: true + + ScrollBar.horizontal.policy: ScrollBar.AsNeeded + ScrollBar.vertical.policy: ScrollBar.AsNeeded + + ColumnLayout{ + spacing: 7 + + width: stackedWidget.width + height: stackedWidget.height + Layout.alignment: Qt.AlignHCenter + + RowLayout{ + Layout.fillWidth: true + + Item{ + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: accountMigrationLabel + + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 27 + + font.pointSize: 13 + font.kerning: true + wrapMode:Text.Wrap + + text: qsTr("Account Migration Required") + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + } + + RowLayout{ + spacing: 7 + + Item{ + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: migrationReasonLabel + + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 21 + + font.pointSize: 10 + font.kerning: true + wrapMode:Text.Wrap + + text: qsTr("This account is malformed. Please enter your password") + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + } + + Item{ + Layout.minimumWidth: 20 + Layout.preferredWidth: 20 + Layout.maximumWidth: 20 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + ColumnLayout{ + spacing: 7 + + Layout.fillWidth: true + + RowLayout{ + Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter + + Item{ + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: avatarLabel + + Layout.maximumWidth: 200 + Layout.preferredWidth: 200 + Layout.minimumWidth: 200 + + Layout.maximumHeight: 200 + Layout.preferredHeight: 200 + Layout.minimumHeight: 200 + + Layout.alignment: Qt.AlignHCenter + + background: Rectangle { + id: avatarLabelBackground + + anchors.fill: parent + color: "transparent" + + Image{ + id: avatarImg + + anchors.fill: parent + source: { + if(imgBase64.length === 0){ + return "" + } else { + return "data:image/png;base64," + imgBase64 + } + } + fillMode: Image.PreserveAspectCrop + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle{ + width: avatarImg.width + height: avatarImg.height + radius: { + var size = ((avatarImg.width <= avatarImg.height)? avatarImg.width:avatarImg.height) + return size /2 + } + } + } + } + } + } + + Item{ + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + } + } + + Item{ + Layout.minimumWidth: 20 + Layout.preferredWidth: 20 + Layout.maximumWidth: 20 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + } + + GridLayout{ + rows: 4 + columns: 5 + rowSpacing: 7 + columnSpacing: 7 + + Layout.fillWidth: true + + // 1st Row + Item{ + Layout.row: 0 + Layout.column: 0 + + Layout.fillWidth: true + Layout.preferredWidth:40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: aliasLabel + + Layout.row: 0 + Layout.column: 1 + + Layout.preferredWidth: 92 + Layout.preferredHeight: 30 + + text: qsTr("Alias") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.row: 0 + Layout.column: 2 + + Layout.fillWidth: true + Layout.preferredWidth:40 + Layout.maximumWidth: 40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: aliasInputLabel + + Layout.row: 0 + Layout.column: 3 + + Layout.preferredWidth: 142 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.row: 0 + Layout.column: 4 + + Layout.fillWidth: true + Layout.preferredWidth:40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + // 2nd Row + Item{ + Layout.row: 1 + Layout.column: 0 + + Layout.fillWidth: true + Layout.preferredWidth:40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: usernameLabel + + Layout.row: 1 + Layout.column: 1 + + Layout.preferredWidth: 92 + Layout.preferredHeight: 30 + + text: qsTr("Username") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.row: 1 + Layout.column: 2 + + Layout.fillWidth: true + Layout.preferredWidth:40 + Layout.maximumWidth: 40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: usernameInputLabel + + Layout.row: 1 + Layout.column: 3 + + Layout.preferredWidth: 142 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.row: 1 + Layout.column: 4 + + Layout.fillWidth: true + Layout.preferredWidth:40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + // 3rd Row + Item{ + Layout.row: 2 + Layout.column: 0 + + Layout.fillWidth: true + Layout.preferredWidth:40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: managerUriLabel + + Layout.row: 2 + Layout.column: 1 + + Layout.preferredWidth: 92 + Layout.preferredHeight: 30 + + text: qsTr("Manager Uri") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.row: 2 + Layout.column: 2 + + Layout.fillWidth: true + Layout.preferredWidth:40 + Layout.maximumWidth: 40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: managerUriInputLabel + + Layout.row: 2 + Layout.column: 3 + + Layout.preferredWidth: 142 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.row: 2 + Layout.column: 4 + + Layout.fillWidth: true + Layout.preferredWidth:40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + // 4th Row + Item{ + Layout.row: 3 + Layout.column: 0 + + Layout.fillWidth: true + Layout.preferredWidth:40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + Label{ + id: passwordLabel + + Layout.row: 3 + Layout.column: 1 + + Layout.preferredWidth: 92 + Layout.preferredHeight: 30 + + text: qsTr("Password") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.row: 3 + Layout.column: 2 + + Layout.fillWidth: true + Layout.preferredWidth:40 + Layout.maximumWidth: 40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + InfoLineEdit{ + id: passwordInputLineEdit + + Layout.row: 3 + Layout.column: 3 + + Layout.preferredWidth: 142 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + echoMode: TextInput.Password + + placeholderText: qsTr("Password") + + onTextChanged: { + if(text.length === 0){ + migrationPushButton.enabled = false + } else { + migrationPushButton.enabled = true + } + password = text + } + + onEditingFinished: { + password = text + } + } + + Item{ + Layout.row: 3 + Layout.column: 4 + + Layout.fillWidth: true + Layout.preferredWidth:40 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + } + + Item{ + Layout.minimumWidth: 20 + Layout.preferredWidth: 20 + Layout.maximumWidth: 20 + + Layout.minimumHeight: 40 + Layout.preferredHeight: 40 + Layout.maximumHeight: 40 + } + + RowLayout{ + spacing: 0 + Layout.fillWidth: true + + Item { + Layout.minimumWidth: 20 + Layout.preferredWidth: 20 + Layout.maximumWidth: 20 + + Layout.minimumHeight: 40 + Layout.preferredHeight: 40 + Layout.maximumHeight: 40 + } + + HoverableGradientButton { + id: migrationPushButton + + Layout.alignment: Qt.AlignLeft + Layout.maximumWidth: 100 + Layout.preferredWidth: 100 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Migrate") + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + backgroundColor: JamiTheme.releaseColor + + onClicked: { + slotMigrationButtonClicked() + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 40 + Layout.preferredHeight: 40 + Layout.maximumHeight: 40 + } + + HoverableButtonTextItem { + id: deleteAccountPushButton + + backgroundColor: "red" + onEnterColor: Qt.rgba(150 / 256, 0, 0, 0.7) + onDisabledBackgroundColor: Qt.rgba( + 255 / 256, + 0, 0, 0.8) + onPressColor: backgroundColor + textColor: "white" + + Layout.alignment: Qt.AlignRight + Layout.maximumWidth: 100 + Layout.preferredWidth: 100 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Delete") + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + + onClicked: { + slotDeleteButtonClicked() + } + } + + Item { + Layout.minimumWidth: 20 + Layout.preferredWidth: 20 + Layout.maximumWidth: 20 + + Layout.minimumHeight: 40 + Layout.preferredHeight: 40 + Layout.maximumHeight: 40 + } + } + } + + } + } + + Rectangle{ + id: migrationWaitingPage + + Layout.fillWidth: true + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + Layout.topMargin: 11 + Layout.bottomMargin: 11 + + x: (parent.width - width) /2 + y: (parent.height - height) /2 + + ScrollView { + id: migrationWaitingPageScroll + anchors.fill: parent + + clip: true + + ScrollBar.horizontal.policy: ScrollBar.AsNeeded + ScrollBar.vertical.policy: ScrollBar.AsNeeded + + ColumnLayout{ + spacing: 7 + + width: stackedWidget.width + height: stackedWidget.height + Layout.alignment: Qt.AlignHCenter + + Item{ + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.preferredHeight: 211 + } + + RowLayout{ + spacing: 7 + + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + + Item{ + Layout.alignment: Qt.AlignLeft + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.minimumWidth: 20 + } + + Label{ + id: spinnerLabel + + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 200 + Layout.preferredWidth: 200 + Layout.minimumWidth: 200 + + Layout.maximumHeight: 200 + Layout.preferredHeight: 200 + Layout.minimumHeight: 200 + + property string spinnerDisplyState: successState ? "spinnerLabel_Regular" : "spinnerLabel_Failure" + onSpinnerDisplyStateChanged: { + switch (spinnerDisplyState) { + case "spinnerLabel_Regular": + background = Qt.createQmlObject("import QtQuick 2.14; + AnimatedImage { + source: \"qrc:/images/jami_eclipse_spinner.gif\" + + playing: true + paused: false + fillMode: Image.PreserveAspectFit + mipmap: true + }", spinnerLabel) + break + case "spinnerLabel_Failure": + background = Qt.createQmlObject("import QtQuick 2.14; + import \"qrc:/src/constant/\"; + Image { + anchors.fill: parent; + source:\"image://tintedPixmap/\" + (\"qrc:/images/icons/baseline-error_outline-24px.svg\").replace(\"qrc:/images/icons/\", \"\") + \"+\" + JamiTheme.urgentOrange_; + mipmap: true;}", spinnerLabel) + break + } + } + } + + Item { + Layout.alignment: Qt.AlignRight + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.minimumWidth: 20 + } + } + + Item{ + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.preferredHeight: 211 + } + + Label{ + id: progressLabel + + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + + color: successState? "black" : "red" + text: successState? qsTr("Migrating your Jami account...") : qsTr("Migration Failed") + font.pointSize: 11 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Label.WordWrap + } + + Item{ + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + } + } + } + } + } +} diff --git a/src/commoncomponents/CustomBorder.qml b/src/commoncomponents/CustomBorder.qml new file mode 100644 index 00000000..1a2843e7 --- /dev/null +++ b/src/commoncomponents/CustomBorder.qml @@ -0,0 +1,54 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 + + +/* + * Inspired by + * https://stackoverflow.com/questions/16534489/qml-control-border-width-and-color-on-any-one-side-of-rectangle-element + */ +Rectangle { + + property bool commonBorder: true + + property int lBorderwidth: 1 + property int rBorderwidth: 1 + property int tBorderwidth: 1 + property int bBorderwidth: 1 + + property int commonBorderWidth: 1 + + z: -1 + + property string borderColor: "white" + + color: borderColor + + anchors { + left: parent.left + right: parent.right + top: parent.top + bottom: parent.bottom + + topMargin: commonBorder ? -commonBorderWidth : -tBorderwidth + bottomMargin: commonBorder ? -commonBorderWidth : -bBorderwidth + leftMargin: commonBorder ? -commonBorderWidth : -lBorderwidth + rightMargin: commonBorder ? -commonBorderWidth : -rBorderwidth + } +} diff --git a/src/commoncomponents/DeleteAccountDialog.qml b/src/commoncomponents/DeleteAccountDialog.qml new file mode 100644 index 00000000..1db2813b --- /dev/null +++ b/src/commoncomponents/DeleteAccountDialog.qml @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +Dialog { + id: deleteAccountDialog + + property int profileType: ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Type() + + property bool isSIP: { + switch (profileType) { + case Profile.Type.SIP: + return true; + default: + return false; + } + } + + onOpened: { + profileType = ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Type() + labelBestId.text = ClientWrapper.settingsAdaptor.getAccountBestName() + labelAccountHash.text = ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Uri() + } + + onVisibleChanged: { + if(!visible){ + reject() + } + } + + visible: false + title: qsTr("Account deletion") + + contentItem: Rectangle{ + implicitWidth: 400 + implicitHeight: 300 + + ColumnLayout{ + anchors.fill: parent + spacing: 7 + + Layout.alignment: Qt.AlignCenter + + Label{ + id: labelDeletion + + Layout.topMargin: 11 + Layout.leftMargin: 11 + Layout.rightMargin: 11 + + Layout.alignment: Qt.AlignHCenter + font.pointSize: 8 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + text:qsTr("Do you really want to delete the following account?") + } + + Label{ + id: labelBestId + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + + Layout.alignment: Qt.AlignHCenter + font.pointSize: 8 + font.kerning: true + font.bold: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + + text: ClientWrapper.settingsAdaptor.getAccountBestName() + } + + Label{ + id: labelAccountHash + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + + Layout.alignment: Qt.AlignHCenter + font.pointSize: 8 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + text: ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Uri() + } + + Item{ + Layout.fillWidth: true + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + + Layout.maximumHeight: 5 + Layout.preferredHeight: 5 + Layout.minimumHeight: 5 + } + + Label{ + id: labelWarning + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + + Layout.preferredWidth: 300 + + visible: ! isSIP + + Layout.alignment: Qt.AlignHCenter + wrapMode: Text.Wrap + text: qsTr("If this account hasn't been exported, or added to another device, it will be irrevocably lost.") + font.pointSize: 8 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: "red" + } + + Item{ + Layout.fillWidth: true + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + + Layout.maximumHeight: 10 + Layout.preferredHeight: 10 + Layout.minimumHeight: 10 + } + + RowLayout{ + spacing: 7 + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + + Item{ + Layout.fillWidth: true + Layout.minimumWidth: 40 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableRadiusButton{ + id: btnDeleteAccept + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + radius: height /2 + + text: qsTr("Delete") + font.pointSize: 10 + font.kerning: true + + onClicked: { + ClientWrapper.accountAdaptor.deleteCurrentAccount() + accept() + } + } + + Item{ + Layout.fillWidth: true + Layout.minimumWidth: 40 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableButtonTextItem{ + id: btnDeleteCancel + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + backgroundColor: "red" + onEnterColor: Qt.rgba(150 / 256, 0, 0, 0.7) + onDisabledBackgroundColor: Qt.rgba( + 255 / 256, + 0, 0, 0.8) + onPressColor: backgroundColor + textColor: "white" + + radius: height /2 + + text: qsTr("Cancel") + font.pointSize: 10 + font.kerning: true + + onClicked: { + reject() + } + } + + Item{ + Layout.fillWidth: true + Layout.minimumWidth: 40 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 5 + Layout.preferredHeight: 5 + Layout.minimumHeight: 5 + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + Layout.bottomMargin: 11 + } + } + } +} diff --git a/src/commoncomponents/GeneralMenuItem.qml b/src/commoncomponents/GeneralMenuItem.qml new file mode 100644 index 00000000..677c4b0e --- /dev/null +++ b/src/commoncomponents/GeneralMenuItem.qml @@ -0,0 +1,140 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + + +/* + * General menu item. + * Can control top, bottom, left, right border width. + * Use onClicked slot to simulate item click event. + * Can have image icon at the left of the text. + */ +MenuItem { + id: menuItem + + property string itemName: "" + property string iconSource: "" + property int preferredWidth: 150 + property int preferredHeight: 30 + property int topBorderWidth: 0 + property int bottomBorderWidth: 0 + property int leftBorderWidth: 0 + property int rightBorderWidth: 0 + + signal clicked + + contentItem: Rectangle { + id: menuItemContentRect + + anchors.fill: parent + + Image { + id: contextMenuItemImage + + anchors.left: menuItemContentRect.left + anchors.leftMargin: 5 + anchors.verticalCenter: menuItemContentRect.verticalCenter + + width: 25 + height: 25 + + visible: false + fillMode: Image.PreserveAspectFit + mipmap: true + } + + Text { + id: contextMenuItemText + + anchors.left: contextMenuItemImage.right + anchors.leftMargin: 5 + anchors.verticalCenter: menuItemContentRect.verticalCenter + width: textMetrics.boundingRect.width + height: 30 + + TextMetrics { + id: textMetrics + font: contextMenuItemText.font + elide: Text.ElideMiddle + elideWidth: contextMenuItemImage.visible ? (preferredWidth - contextMenuItemImage.width - 5) : preferredWidth + text: itemName + } + + text: textMetrics.elidedText + font.pointSize: JamiTheme.textFontSize - 3 + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + color: "transparent" + } + + onIconSourceChanged: { + if (iconSource !== "") { + contextMenuItemImage.source = iconSource + contextMenuItemImage.visible = true + } + } + + background: Rectangle { + id: contextMenuBackgroundRect + + anchors.fill: parent + anchors.topMargin: topBorderWidth + anchors.bottomMargin: bottomBorderWidth + anchors.leftMargin: leftBorderWidth + anchors.rightMargin: rightBorderWidth + + implicitWidth: preferredWidth + implicitHeight: preferredHeight + + border.width: 0 + color: menuItem.down ? JamiTheme.releaseColor : "white" + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onPressed: { + contextMenuBackgroundRect.color = JamiTheme.pressColor + } + onReleased: { + contextMenuBackgroundRect.color = JamiTheme.releaseColor + menuItem.clicked() + } + onEntered: { + contextMenuBackgroundRect.color = JamiTheme.hoverColor + } + onExited: { + contextMenuBackgroundRect.color = "white" + } + } + + CustomBorder { + commonBorder: false + lBorderwidth: leftBorderWidth + rBorderwidth: rightBorderWidth + tBorderwidth: topBorderWidth + bBorderwidth: bottomBorderWidth + borderColor: JamiTheme.tabbarBorderColor + } + } +} diff --git a/src/commoncomponents/GeneralMenuSeparator.qml b/src/commoncomponents/GeneralMenuSeparator.qml new file mode 100644 index 00000000..1a2e93dd --- /dev/null +++ b/src/commoncomponents/GeneralMenuSeparator.qml @@ -0,0 +1,38 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + +MenuSeparator { + id: menuSeparator + + property int preferredWidth: 10 + property int preferredHeight: 1 + property string separatorColor: JamiTheme.tabbarBorderColor + + padding: 0 + topPadding: 1 + bottomPadding: 1 + contentItem: Rectangle { + implicitWidth: preferredWidth + implicitHeight: preferredHeight + color: separatorColor + } +} diff --git a/src/commoncomponents/HoverableButton.qml b/src/commoncomponents/HoverableButton.qml new file mode 100644 index 00000000..a7a4aaf9 --- /dev/null +++ b/src/commoncomponents/HoverableButton.qml @@ -0,0 +1,89 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + + +/* + * HoverableButton contains the following configurable properties: + * 1. Color changes on different button state + * 2. Radius control (rounded) + * 3. Text content or image content + * 4. Can use OnClicked slot to implement some click logic + */ +Button { + id: hoverableButton + + property int fontPointSize: 9 + property int buttonImageHeight: hoverableButtonBackground.height - 10 + property int buttonImageWidth: hoverableButtonBackground.width - 10 + + property string backgroundColor: JamiTheme.releaseColor + property string onPressColor: JamiTheme.pressColor + property string onReleaseColor: JamiTheme.releaseColor + property string onEnterColor: JamiTheme.hoverColor + property string onExitColor: JamiTheme.releaseColor + + property alias radius: hoverableButtonBackground.radius + property alias source: hoverableButtonImage.source + + font.pointSize: fontPointSize + + hoverEnabled: true + + background: Rectangle { + id: hoverableButtonBackground + + color: backgroundColor + + Image { + id: hoverableButtonImage + + anchors.centerIn: hoverableButtonBackground + + height: buttonImageHeight + width: buttonImageWidth + + fillMode: Image.PreserveAspectFit + mipmap: true + asynchronous: true + } + + MouseArea { + anchors.fill: parent + + hoverEnabled: true + + onPressed: { + hoverableButtonBackground.color = onPressColor + } + onReleased: { + hoverableButtonBackground.color = onReleaseColor + hoverableButton.clicked() + } + onEntered: { + hoverableButtonBackground.color = onEnterColor + } + onExited: { + hoverableButtonBackground.color = onExitColor + } + } + } +} diff --git a/src/commoncomponents/HoverableButtonTextItem.qml b/src/commoncomponents/HoverableButtonTextItem.qml new file mode 100644 index 00000000..fc1d6bb0 --- /dev/null +++ b/src/commoncomponents/HoverableButtonTextItem.qml @@ -0,0 +1,103 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + + +/* + * HoverableButton containes functionalites: + * 1. Color changes on different button state + * 2. Radius control (rounded) + * 3. Text content or image content + * 4. Can use OnClicked slot to implement some click logic + */ +Button { + id: hoverableButton + + property int fontPointSize: 9 + property int buttonImageHeight: hoverableButtonBackground.height - 10 + property int buttonImageWidth: hoverableButtonBackground.width - 10 + + property string backgroundColor: JamiTheme.releaseColor + property string onPressColor: JamiTheme.pressColor + property string onReleaseColor: backgroundColor + property string onEnterColor: JamiTheme.hoverColor + property string onExitColor: backgroundColor + property string onDisabledBackgroundColor: backgroundColor + property string textColor: "black" + + property alias radius: hoverableButtonBackground.radius + property alias source: hoverableButtonImage.source + + font.pointSize: fontPointSize + font.kerning: true + + hoverEnabled: true + + contentItem: Text { + text: hoverableButton.text + font: hoverableButton.font + opacity: enabled ? 1.0 : 0.3 + color: textColor + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + id: hoverableButtonBackground + + color: hoverableButton.enabled ? backgroundColor:onDisabledBackgroundColor + + Image { + id: hoverableButtonImage + + anchors.centerIn: hoverableButtonBackground + + height: buttonImageHeight + width: buttonImageWidth + + fillMode: Image.PreserveAspectFit + mipmap: true + asynchronous: true + } + + MouseArea { + enabled: hoverableButton.enabled + anchors.fill: parent + + hoverEnabled: true + + onPressed: { + hoverableButtonBackground.color = onPressColor + } + onReleased: { + hoverableButtonBackground.color = onReleaseColor + hoverableButton.clicked() + } + onEntered: { + hoverableButtonBackground.color = onEnterColor + } + onExited: { + hoverableButtonBackground.color = onExitColor + } + } + } +} diff --git a/src/commoncomponents/HoverableRadiusButton.qml b/src/commoncomponents/HoverableRadiusButton.qml new file mode 100644 index 00000000..ef98e075 --- /dev/null +++ b/src/commoncomponents/HoverableRadiusButton.qml @@ -0,0 +1,93 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.15 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + + +/* + * HoverableButton contains the following configurable properties: + * 1. Color changes on different button state + * 2. Radius control (rounded) + * 3. Text content or image content + * 4. Can use OnClicked slot to implement some click logic + */ +Button { + id: hoverableButton + property int fontPointSize: 9 + property int buttonImageHeight: hoverableButtonBackground.height - 10 + property int buttonImageWidth: hoverableButtonBackground.width - 10 + property string backgroundColor: JamiTheme.releaseColor + property string onPressColor: JamiTheme.pressColor + property string onReleaseColor: backgroundColor + property string onEnterColor: JamiTheme.hoverColor + property string onExitColor: backgroundColor + property alias radius: hoverableButtonBackground.radius + property alias source: hoverableButtonImage.source + property bool isHovering: false + radius: height / 2 + function enterBtn(){ + btnMouseArea.entered() + } + function exitBtn(){ + btnMouseArea.exited() + } + function pressBtn(){ + btnMouseArea.pressed() + } + function releaseBtn(){ + btnMouseArea.released() + } + font.pointSize: fontPointSize + font.kerning: true + hoverEnabled: true + background: Rectangle { + id: hoverableButtonBackground + color: backgroundColor + Image { + id: hoverableButtonImage + anchors.centerIn: hoverableButtonBackground + height: buttonImageHeight + width: buttonImageWidth + fillMode: Image.PreserveAspectFit + mipmap: true + asynchronous: true + } + MouseArea { + id: btnMouseArea + anchors.fill: parent + hoverEnabled: true + onPressed: { + hoverableButtonBackground.color = onPressColor + } + onReleased: { + hoverableButtonBackground.color = onReleaseColor + hoverableButton.clicked() + } + onEntered: { + hoverableButtonBackground.color = onEnterColor + isHovering = true + } + onExited: { + hoverableButtonBackground.color = onExitColor + isHovering = false + } + } + } +} diff --git a/src/commoncomponents/InfoLineEdit.qml b/src/commoncomponents/InfoLineEdit.qml new file mode 100644 index 00000000..cc50cdc8 --- /dev/null +++ b/src/commoncomponents/InfoLineEdit.qml @@ -0,0 +1,58 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Styles 1.4 + +import "../constant" + +TextField{ + enum BorderColorMode{ + NORMAL, + RIGHT, + ERROR + } + + property int fieldLayoutWidth: 256 + property int fieldLayoutHeight: 30 + property bool layoutFillwidth: false + + property int borderColorMode: InfoLineEdit.NORMAL + property var backgroundColor: JamiTheme.rgb256(240,240,240) + property var borderColor: { + switch(borderColorMode){ + case InfoLineEdit.NORMAL: + return "transparent" + case InfoLineEdit.RIGHT: + return "green" + case InfoLineEdit.ERROR: + return "red" + } + } + + Layout.minimumHeight: fieldLayoutHeight + Layout.preferredHeight: fieldLayoutHeight + Layout.maximumHeight: fieldLayoutHeight + + Layout.minimumWidth: fieldLayoutWidth + Layout.maximumWidth: fieldLayoutWidth + Layout.preferredWidth: fieldLayoutWidth + + Layout.fillWidth: layoutFillwidth + Layout.alignment: Qt.AlignHCenter + + wrapMode: Text.Wrap + readOnly: false + selectByMouse: true + font.pointSize: 10 + font.kerning: true + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + background: Rectangle { + anchors.fill: parent + radius: readOnly? 0 : height / 2 + border.color: readOnly? "transparent" : borderColor + border.width:readOnly? 0 : 2 + color: readOnly? "transparent" : backgroundColor + } +} diff --git a/src/commoncomponents/JamiFileDialog.qml b/src/commoncomponents/JamiFileDialog.qml new file mode 100644 index 00000000..2be31423 --- /dev/null +++ b/src/commoncomponents/JamiFileDialog.qml @@ -0,0 +1,51 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import Qt.labs.platform 1.1 + +FileDialog { + id: fileDialog + + + /* + * Use enum to avoid importing Qt.labs.platform when using JamiFileDialog. + */ + property int mode: JamiFileDialog.Mode.OpenFile + + enum Mode { + OpenFile = 0, + OpenFiles, + SaveFile + } + + title: "Please choose a file" + + onModeChanged: { + switch(mode) { + case JamiFileDialog.Mode.OpenFile: + fileDialog.fileMode = FileDialog.OpenFile + break + case JamiFileDialog.Mode.OpenFiles: + fileDialog.fileMode = FileDialog.OpenFiles + break + default: + fileDialog.fileMode = FileDialog.SaveFile + } + } +} diff --git a/src/commoncomponents/ListViewJami.qml b/src/commoncomponents/ListViewJami.qml new file mode 100644 index 00000000..c20cc8c3 --- /dev/null +++ b/src/commoncomponents/ListViewJami.qml @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +Rectangle{ + id: listViewBackground + + property alias model: listView.model + property alias delegate: listView.delegate + property alias currentIndex: listView.currentIndex + + border.width: 1 + border.color: JamiTheme.hoverColor + + color: JamiTheme.releaseColor + + ListView{ + id: listView + + anchors.fill: parent + + visible: listViewBackground.visible + layer.mipmap: false + clip: true + + ScrollIndicator.vertical: ScrollIndicator {} + } +} diff --git a/src/commoncomponents/LookupStatusLabel.qml b/src/commoncomponents/LookupStatusLabel.qml new file mode 100644 index 00000000..1a9cf52b --- /dev/null +++ b/src/commoncomponents/LookupStatusLabel.qml @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 + +Label { + id: lookupStatusLabel + + property int labelWidth : 30 + property int labelHeight : 30 + + Layout.maximumWidth: labelWidth + Layout.preferredWidth: labelWidth + Layout.minimumWidth: labelWidth + + Layout.maximumHeight: labelHeight + Layout.preferredHeight: labelHeight + Layout.minimumHeight: labelHeight + + property string lookupStatusState: "Blank" + + // whenever look up status state change, the backgroud weill be set to a cooresponding choice + onLookupStatusStateChanged: { + switch (lookupStatusState) { + case "Blank": + background = Qt.createQmlObject( + "import QtQuick 2.14; Rectangle { anchors.fill: parent; color: \"transparent\"; }", + lookupStatusLabel) + break + case "Invalid": + background = Qt.createQmlObject("import QtQuick 2.14; +import \"qrc:/src/constant/\"; +Rectangle { +anchors.fill: parent; +Image { +anchors.fill: parent; +source: \"image://tintedPixmap/\" + (\"qrc:/images/icons/baseline-error_outline-24px.svg\").replace(\"qrc:/images/icons/\",\"\") + \"+\" + JamiTheme.urgentOrange_; +mipmap: true; +} +}", lookupStatusLabel) + break + case "Taken": + background = Qt.createQmlObject("import QtQuick 2.14; +import \"qrc:/src/constant/\"; +Rectangle { +anchors.fill: parent; +Image{ +anchors.fill: parent; +source: \"image://tintedPixmap/\" + (\"qrc:/images/icons/baseline-close-24px.svg\").replace(\"qrc:/images/icons/\",\"\") + \"+\" + JamiTheme.red_; +mipmap: true; +} +}", lookupStatusLabel) + break + case "Free": + background = Qt.createQmlObject("import QtQuick 2.14; +import \"qrc:/src/constant/\"; +Rectangle { +anchors.fill: parent; +Image { +anchors.fill: parent; +source: \"image://tintedPixmap/\"+ (\"qrc:/images/icons/baseline-done-24px.svg\").replace(\"qrc:/images/icons/\",\"\") + \"+\" + JamiTheme.presenceGreen_; +mipmap: true; +} +}", lookupStatusLabel) + break + case "Searching": + background = Qt.createQmlObject("import QtQuick 2.14; +import \"qrc:/src/constant/\"; +Rectangle { +anchors.fill: parent; +AnimatedImage { +anchors.fill: parent +source: \"qrc:/images/jami_rolling_spinner.gif\"; + +playing: true +paused: false +fillMode: Image.PreserveAspectFit +mipmap: true +} +}", lookupStatusLabel) + break + } + } +} diff --git a/src/commoncomponents/MessageBox.qml b/src/commoncomponents/MessageBox.qml new file mode 100644 index 00000000..9e87c924 --- /dev/null +++ b/src/commoncomponents/MessageBox.qml @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.3 + +MessageDialog { + id: messageBox + + visible: false + modality: Qt.NonModal + width: 300 + height: 200 + + onRejected: {} + + function openWithParameters(titleToDisplay, infoToDisplay, infoIconMode = StandardIcon.Information, buttons = StandardButton.Ok){ + title = titleToDisplay + text = infoToDisplay + icon = infoIconMode + standardButtons = buttons + messageBox.open() + } +} diff --git a/src/commoncomponents/PasswordDialog.qml b/src/commoncomponents/PasswordDialog.qml new file mode 100644 index 00000000..28eced61 --- /dev/null +++ b/src/commoncomponents/PasswordDialog.qml @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +import "../constant" +/* + * PasswordDialog for changing password and exporting account + */ +Dialog { + id: passwordDialog + + enum PasswordEnteringPurpose { + ChangePassword, + ExportAccount, + SetPassword + } + readonly property int successCode: 200 + signal doneSignal(int code, int currentPurpose) + + property string path: "" + property int purpose: PasswordDialog.ChangePassword + + title: { + switch(purpose){ + case PasswordDialog.ExportAccount: + return qsTr("Enter the password of this account") + case PasswordDialog.ChangePassword: + return qsTr("Changing password") + case PasswordDialog.SetPassword: + return qsTr("Set password") + } + } + + function openDialog(purposeIn, exportPathIn = ""){ + purpose = purposeIn + path = exportPathIn + currentPasswordEdit.clear() + passwordEdit.clear() + confirmPasswordEdit.clear() + passwordDialog.open() + } + + function haveDone(code, currentPurpose) { + done(code) + doneSignal(code, currentPurpose) + } + + function validatePassword() { + var acceptablePassword = (passwordEdit.text === confirmPasswordEdit.text) + btnChangePasswordConfirm.enabled = acceptablePassword + + if (acceptablePassword) { + passwordEdit.borderColorMode = InfoLineEdit.RIGHT + confirmPasswordEdit.borderColorMode = InfoLineEdit.RIGHT + return + } + + passwordEdit.borderColorMode = InfoLineEdit.ERROR + confirmPasswordEdit.borderColorMode = InfoLineEdit.ERROR + } + + Timer{ + id: timerToOperate + + interval: 200 + repeat: false + + onTriggered: { + if ((purpose === PasswordDialog.ChangePassword) || (purpose === PasswordDialog.SetPassword)) { + savePasswordQML() + } else if(purpose === PasswordDialog.ExportAccount) { + exportAccountQML() + } + } + } + + function exportAccountQML() { + var success = false + if(path.length > 0){ + success = ClientWrapper.accountAdaptor.exportToFile(ClientWrapper.utilsAdaptor.getCurrAccId(),path,currentPasswordEdit.text) + } + + spinnerMovie.playing = false + spinnerLabel.visible = false + if (success) { + haveDone(successCode, passwordDialog.purpose) + } else { + currentPasswordEdit.clear() + btnChangePasswordConfirm.enabled = false + wrongPasswordLabel.visible = true + } + } + function savePasswordQML() { + var success = false + success = ClientWrapper.accountAdaptor.savePassword(ClientWrapper.utilsAdaptor.getCurrAccId(),currentPasswordEdit.text, passwordEdit.text) + + spinnerMovie.playing = false + spinnerLabel.visible = false + if (success) { + ClientWrapper.accountAdaptor.setArchiveHasPassword(passwordEdit.text.length !== 0) + haveDone(successCode, passwordDialog.purpose) + } else { + currentPasswordEdit.clear() + btnChangePasswordConfirm.enabled = false + wrongPasswordLabel.visible = true + } + } + + visible: false + + anchors.centerIn: parent.Center + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + contentItem: Rectangle{ + implicitWidth: 440 + implicitHeight: 270 + + ColumnLayout { + anchors.fill: parent + spacing: 7 + + ColumnLayout { + Layout.topMargin: 11 + Layout.leftMargin: 40 + Layout.rightMargin: 40 + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + + Layout.preferredWidth: 360 + Layout.maximumWidth: 360 + spacing: 7 + + ColumnLayout { + spacing: 0 + + Layout.alignment: Qt.AlignHCenter + + Layout.preferredWidth: 356 + Layout.maximumWidth: 356 + + InfoLineEdit { + id: currentPasswordEdit + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + Layout.fillWidth: true + + visible: purpose === PasswordDialog.ChangePassword + echoMode: TextInput.Password + font.pointSize: 10 + font.kerning: true + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + placeholderText: qsTr("Enter Current Password") + + onTextChanged: { + if (purpose === PasswordDialog.ChangePassword) { + validatePassword() + } + + if (currentPasswordEdit.text.length == 0) { + btnChangePasswordConfirm.enabled = false + } else { + wrongPasswordLabel.visible = false + btnChangePasswordConfirm.enabled = true + } + } + } + + Label { + id: wrongPasswordLabel + + Layout.alignment: Qt.AlignHCenter + + Layout.minimumHeight: 12 + Layout.preferredHeight: 12 + Layout.maximumHeight: 12 + Layout.fillWidth: true + + font.pointSize: 8 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: qsTr("Current Password Incorrect") + color: "red" + + visible: false + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 8 + Layout.preferredHeight: 8 + Layout.maximumHeight: 8 + } + + InfoLineEdit { + id: passwordEdit + + fieldLayoutHeight: 30 + layoutFillwidth: true + + visible: purpose === PasswordDialog.ChangePassword || purpose === PasswordDialog.SetPassword + echoMode: TextInput.Password + font.pointSize: 10 + font.kerning: true + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + placeholderText: qsTr("Enter New Password") + + onTextChanged: { + validatePassword() + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 8 + Layout.preferredHeight: 8 + Layout.maximumHeight: 8 + } + + InfoLineEdit { + id: confirmPasswordEdit + + fieldLayoutHeight: 30 + layoutFillwidth: true + + visible: purpose === PasswordDialog.ChangePassword || purpose === PasswordDialog.SetPassword + echoMode: TextInput.Password + font.pointSize: 10 + font.kerning: true + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + placeholderText: qsTr("Confirm New Password") + + onTextChanged: { + validatePassword() + } + } + } + + Label { + id: spinnerLabel + + visible: false + + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + + background: Rectangle { + anchors.fill: parent + AnimatedImage { + id: spinnerMovie + + anchors.fill: parent + + source: "qrc:/images/ajax-loader.gif" + + playing: spinnerLabel.visible + paused: false + fillMode: Image.PreserveAspectFit + mipmap: true + } + } + } + + RowLayout { + spacing: 7 + + Layout.bottomMargin: 11 + Layout.fillWidth: true + + Layout.leftMargin: 30 + Layout.rightMargin: 30 + + HoverableRadiusButton { + id: btnChangePasswordConfirm + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Confirm") + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + + enabled: false + + onClicked: { + spinnerLabel.visible = true + spinnerMovie.playing = true + timerToOperate.restart() + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + HoverableButtonTextItem { + id: btnChangePasswordCancel + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + backgroundColor: "red" + onEnterColor: Qt.rgba(150 / 256, 0, 0, 0.7) + onDisabledBackgroundColor: Qt.rgba( + 255 / 256, + 0, 0, 0.8) + onPressColor: backgroundColor + textColor: "white" + + text: qsTr("Cancel") + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + + onClicked: { + passwordDialog.reject() + } + } + } + } + } +} diff --git a/src/commoncomponents/PhotoboothView.qml b/src/commoncomponents/PhotoboothView.qml new file mode 100644 index 00000000..4b006fa9 --- /dev/null +++ b/src/commoncomponents/PhotoboothView.qml @@ -0,0 +1,280 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Styles 1.4 +import Qt.labs.platform 1.1 +import QtGraphicalEffects 1.0 +import net.jami.Models 1.0 + +ColumnLayout{ + property bool takePhotoState: false + property bool hasAvatar: false + property bool isDefaultIcon: false + property string imgBase64: "" + property string fileName: "" + + property int boothWidht: 224 + + signal imageAcquired + signal imageCleared + + function startBooth(force = false){ + hasAvatar = false + ClientWrapper.accountAdaptor.startPreviewing(force) + takePhotoState = true + } + + function stopBooth(){ + try{ + if(!ClientWrapper.accountAdaptor.hasVideoCall()) { + ClientWrapper.accountAdaptor.stopPreviewing() + } + } catch(erro){console.log("Exception: " + erro.message)} + + takePhotoState = false + } + + function setAvatarPixmap(avatarPixmapBase64, defaultValue = false){ + imgBase64 = avatarPixmapBase64 + stopBooth() + if(defaultValue){ + isDefaultIcon = defaultValue + } + } + + onVisibleChanged: { + if(!visible){ + stopBooth() + } + } + + JamiFileDialog{ + id: importFromFileToAvatar_Dialog + + mode: JamiFileDialog.OpenFile + title: qsTr("Choose an image to be the avatar") + folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation) + + nameFilters: [ qsTr("Image Files") + " (*.png *.jpg *.jpeg)",qsTr( + "All files") + " (*)"] + + onAccepted: { + fileName = file + if(fileName.length === 0) { + imageCleared() + return + } + imgBase64 = ClientWrapper.utilsAdaptor.getCroppedImageBase64FromFile( + ClientWrapper.utilsAdaptor.getAbsPath(fileName),boothWidht) + imageAcquired() + stopBooth() + } + } + + spacing: 0 + + Layout.maximumWidth: boothWidht + Layout.preferredWidth: boothWidht + Layout.minimumWidth: boothWidht + + Layout.maximumHeight: 0 + + Layout.alignment: Qt.AlignHCenter + + Label{ + id: avatarLabel + + visible: !takePhotoState + + Layout.maximumWidth: boothWidht + Layout.preferredWidth: boothWidht + Layout.minimumWidth: boothWidht + + Layout.maximumHeight: boothWidht + Layout.preferredHeight: boothWidht + Layout.minimumHeight: boothWidht + + Layout.alignment: Qt.AlignHCenter + + background: Rectangle { + id: avatarLabelBackground + + anchors.fill: parent + color: "transparent" + + Image{ + id: avatarImg + + anchors.fill: parent + source: { + if(imgBase64.length === 0){ + return "" + } else { + return "data:image/png;base64," + imgBase64 + } + } + fillMode: Image.PreserveAspectCrop + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle{ + width: avatarImg.width + height: avatarImg.height + radius: { + var size = ((avatarImg.width <= avatarImg.height)? avatarImg.width:avatarImg.height) + return size /2 + } + } + } + } + } + } + + PhotoboothPreviewRender{ + id:previewWidget + + onHideBooth:{ + stopBooth() + } + visible: takePhotoState + focus: visible + + Layout.alignment: Qt.AlignHCenter + Layout.maximumWidth: boothWidht + Layout.preferredWidth: boothWidht + Layout.minimumWidth: boothWidht + + Layout.maximumHeight: boothWidht + Layout.preferredHeight: boothWidht + Layout.minimumHeight: boothWidht + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle{ + width: previewWidget.width + height: previewWidget.height + radius: { + var size = ((previewWidget.width <= previewWidget.height)? previewWidget.width:previewWidget.height) + return size /2 + } + } + } + + Label{ + id: flashOverlay + + anchors.fill: previewWidget + visible: false + color: "#fff" + + OpacityAnimator on opacity{ + id: flashAnimation + + from: 1 + to: 0 + duration: 600 + } + } + } + + + RowLayout{ + Layout.fillWidth: true + Layout.minimumHeight: 30 + Layout.maximumHeight: 30 + + Item{ + Layout.fillWidth: true + Layout.fillHeight: true + } + + HoverableButton { + id: takePhotoButton + + property string cameraAltIconUrl: "qrc:/images/icons/baseline-camera_alt-24px.svg" + property string addPhotoIconUrl: "qrc:/images/icons/round-add_a_photo-24px.svg" + property string refreshIconUrl: "qrc:/images/icons/baseline-refresh-24px.svg" + + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: "" + font.pointSize: 10 + font.kerning: true + + radius: height / 6 + source: { + if(isDefaultIcon){ + return addPhotoIconUrl + } + + if(takePhotoState) { + return cameraAltIconUrl + } + + if(hasAvatar){ + return refreshIconUrl + } else { + return addPhotoIconUrl + } + } + onClicked: { + if(!takePhotoState){ + imageCleared() + startBooth() + return + } else { + // show flash overlay + flashOverlay.visible = true + flashAnimation.restart() + + // run concurrent function call to take photo + imgBase64 = previewWidget.takeCroppedPhotoToBase64(boothWidht) + hasAvatar = true + imageAcquired() + stopBooth() + } + } + } + + Item{ + Layout.fillHeight: true + + Layout.minimumWidth: 6 + Layout.preferredWidth: 6 + Layout.maximumWidth: 6 + } + + HoverableButton { + id: importButton + + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: "" + font.pointSize: 10 + font.kerning: true + + radius: height / 6 + source: "qrc:/images/icons/round-folder-24px.svg" + + onClicked: { + importFromFileToAvatar_Dialog.open() + } + } + + Item{ + Layout.fillWidth: true + Layout.fillHeight: true + } + } +} diff --git a/src/commoncomponents/TintedButton.qml b/src/commoncomponents/TintedButton.qml new file mode 100644 index 00000000..eaec9944 --- /dev/null +++ b/src/commoncomponents/TintedButton.qml @@ -0,0 +1,125 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + +Button { + id: tintedButton + + + /* + * TintColor, color for the pixmap when button is hovered. + */ + property string tintColor: "white" + + + /* + * NormalPixmapSource - icons in normal state (non-toggled). + * SelectedPixmapSource - icons once button is toggled. + */ + property string normalPixmapSource: "" + property string selectedPixmapSource: "" + + + /* + * IsSelected property is to help set button current state manually. + */ + property bool isSelected: false + + + /* + * ButtonEntered signal is to help call overlay change its opacity + */ + signal buttonEntered + + function setChecked(checked) { + isSelected = checked + if (isSelected) { + tintedButtonImage.source = selectedPixmapSource + } else { + tintedButtonImage.source = normalPixmapSource + } + } + + hoverEnabled: true + + background: Rectangle { + id: tintedButtonBackground + + radius: 30 + color: "transparent" + + Image { + id: tintedButtonImage + + anchors.centerIn: tintedButtonBackground + + height: tintedButtonBackground.height - 10 + width: tintedButtonBackground.width - 10 + + source: normalPixmapSource + fillMode: Image.PreserveAspectFit + mipmap: true + asynchronous: true + } + + MouseArea { + anchors.fill: parent + + hoverEnabled: true + + onReleased: { + isSelected = !isSelected + if (isSelected) { + tintedButtonImage.source = "image://tintedPixmap/" + + selectedPixmapSource.replace( + "qrc:/images/icons/", "") + "+" + tintColor + } else { + tintedButtonImage.source = "image://tintedPixmap/" + normalPixmapSource.replace( + "qrc:/images/icons/", "") + "+" + tintColor + } + tintedButton.clicked() + } + onEntered: { + + + /* + * Tinted. + */ + if (isSelected) { + tintedButtonImage.source = "image://tintedPixmap/" + + selectedPixmapSource.replace( + "qrc:/images/icons/", "") + "+" + tintColor + } else { + tintedButtonImage.source = "image://tintedPixmap/" + normalPixmapSource.replace( + "qrc:/images/icons/", "") + "+" + tintColor + } + tintedButton.buttonEntered() + } + onExited: { + if (isSelected) { + tintedButtonImage.source = selectedPixmapSource + } else { + tintedButtonImage.source = normalPixmapSource + } + } + } + } +} diff --git a/src/connectivitymonitor.cpp b/src/connectivitymonitor.cpp new file mode 100644 index 00000000..0446223d --- /dev/null +++ b/src/connectivitymonitor.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "connectivitymonitor.h" + +#include + +#ifdef Q_OS_WIN +#include +#include + +class NetworkEventHandler : public INetworkListManagerEvents +{ +public: + NetworkEventHandler() + : m_lRefCnt(1){}; + virtual ~NetworkEventHandler(){}; + + HRESULT STDMETHODCALLTYPE + QueryInterface(REFIID riid, void **ppvObject) + { + HRESULT hr = S_OK; + if (IsEqualIID(riid, IID_IUnknown)) { + *ppvObject = (IUnknown *) this; + } else if (IsEqualIID(riid, IID_INetworkListManagerEvents)) { + *ppvObject = (INetworkListManagerEvents *) this; + } else { + hr = E_NOINTERFACE; + } + + return hr; + }; + ULONG STDMETHODCALLTYPE + AddRef() + { + return (ULONG) InterlockedIncrement(&m_lRefCnt); + }; + ULONG STDMETHODCALLTYPE + Release() + { + LONG res = InterlockedDecrement(&m_lRefCnt); + if (res == 0) { + delete this; + } + return (ULONG) res; + }; + + virtual HRESULT STDMETHODCALLTYPE + ConnectivityChanged(NLM_CONNECTIVITY newConnectivity) + { + qDebug() << "connectivity changed: " << newConnectivity; + if (connectivityChangedCb_) { + connectivityChangedCb_(); + } + return S_OK; + }; + + void + setOnConnectivityChangedCallBack(std::function &&cb) + { + connectivityChangedCb_ = cb; + }; + +private: + LONG m_lRefCnt; + + std::function connectivityChangedCb_; +}; + +ConnectivityMonitor::ConnectivityMonitor(QObject *parent) + : QObject(parent) +{ + CoInitialize(NULL); + + IUnknown *pUnknown = NULL; + + HRESULT hr = CoCreateInstance(CLSID_NetworkListManager, + NULL, + CLSCTX_ALL, + IID_IUnknown, + (void **) &pUnknown); + if (FAILED(hr)) { + return; + } + + pNetworkListManager_ = NULL; + hr = pUnknown->QueryInterface(IID_INetworkListManager, (void **) &pNetworkListManager_); + if (FAILED(hr)) { + destroy(); + pUnknown->Release(); + return; + } + + pCPContainer_ = NULL; + hr = pNetworkListManager_->QueryInterface(IID_IConnectionPointContainer, + (void **) &pCPContainer_); + if (FAILED(hr)) { + destroy(); + pUnknown->Release(); + return; + } + + pConnectPoint_ = NULL; + hr = pCPContainer_->FindConnectionPoint(IID_INetworkListManagerEvents, &pConnectPoint_); + if (SUCCEEDED(hr)) { + cookie_ = NULL; + netEventHandler_ = new NetworkEventHandler; + netEventHandler_->setOnConnectivityChangedCallBack([this] { emit connectivityChanged(); }); + hr = pConnectPoint_->Advise((IUnknown *) netEventHandler_, &cookie_); + } else { + destroy(); + } + + pUnknown->Release(); +} + +bool +ConnectivityMonitor::isOnline() +{ + if (!pNetworkListManager_) { + return false; + } + VARIANT_BOOL IsConnect = VARIANT_FALSE; + HRESULT hr = pNetworkListManager_->get_IsConnectedToInternet(&IsConnect); + if (SUCCEEDED(hr)) { + return IsConnect == VARIANT_TRUE; + } + return false; +} + +void +ConnectivityMonitor::destroy() +{ + if (pConnectPoint_) { + pConnectPoint_->Unadvise(cookie_); + pConnectPoint_->Release(); + } + if (pCPContainer_) { + pCPContainer_->Release(); + } + if (pNetworkListManager_) { + pNetworkListManager_->Release(); + pNetworkListManager_ = NULL; + } +} + +ConnectivityMonitor::~ConnectivityMonitor() +{ + destroy(); + CoUninitialize(); +} +#endif // Q_OS_WIN \ No newline at end of file diff --git a/src/connectivitymonitor.h b/src/connectivitymonitor.h new file mode 100644 index 00000000..62865dd8 --- /dev/null +++ b/src/connectivitymonitor.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#ifdef Q_OS_WIN +class ConnectivityMonitor : public QObject +{ + Q_OBJECT + +public: + explicit ConnectivityMonitor(QObject *parent = 0); + ~ConnectivityMonitor(); + + bool isOnline(); + +signals: + void connectivityChanged(); + +private: + void destroy(); + + struct INetworkListManager *pNetworkListManager_; + struct IConnectionPointContainer *pCPContainer_; + struct IConnectionPoint *pConnectPoint_; + class NetworkEventHandler *netEventHandler_; + unsigned long cookie_; +}; +#endif // Q_OS_WIN \ No newline at end of file diff --git a/src/constant/JamiTheme.qml b/src/constant/JamiTheme.qml new file mode 100644 index 00000000..983ae0de --- /dev/null +++ b/src/constant/JamiTheme.qml @@ -0,0 +1,105 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/* + * JamiTheme as a singleton is to provide global property entry + * https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterSingletonType-3 + */ +pragma Singleton + +import QtQuick 2.14 + +Item { + + + /* + * Color strings. + */ + property string hoverColor: "#c7c7c7" + property string pressColor: "#c0c0c0" + property string releaseColor: "#e0e0e0" + property string tabbarBorderColor: "#e3e3e3" + property string transparentColor: "transparent" + property string presenceGreen: "#4cd964" + property string notificationRed: "#ff3b30" + + property string screenSelectionBorderGreen: "green" + + property string acceptButtonGreen: "#4caf50" + property string acceptButtonHoverGreen: "#5db761" + property string acceptButtonPressedGreen: "#449d48" + + property string declineButtonRed: "#f44336" + property string declineButtonHoverRed: "#f5554a" + property string declineButtonPressedRed: "#db3c30" + + property string hangUpButtonTintedRed: "#ff0000" + property string buttonTintedBlue: "#00aaff" + + property string selectionBlue: "#109ede" + property string selectionGreen: "#21be2b" + property string rubberBandSelectionBlue: "steelblue" + + property string closeButtonLighterBlack: "#4c4c4c" + + property string contactSearchBarPlaceHolderTextFontColor: "#767676" + property string contactSearchBarPlaceHolderGreyBackground: "#dddddd" + + property string draftRed: "#cf5300" + + + /* + * Font. + */ + property string faddedFontColor: "#c0c0c0" + property string faddedLastInteractionFontColor: "#505050" + + property int splitViewHandlePreferedWidth: 4 + property int textFontSize: 9 + + + /* + * Place holder text. + */ + property string contactSearchBarPlaceHolderConversationText: qsTr("Find or start a conversation") + property string contactSearchBarPlaceHolderInivitionText: qsTr("Search your received invitations") + + // Jami theme colors + function rgb256(r, g, b) { + return Qt.rgba(r / 256, g / 256, b / 256, 1) + } + + property color blue_: "#109ede" + property color lightBlue_: "#c1ebf0" + property color lightGrey_: rgb256(242, 242, 242) + property color imGrey_: "#dedee0" + property color imBlue_: "#cfebf5" + property color lightBlack_: rgb256(63, 63, 63) + property color grey_: rgb256(160, 160, 160) + property color red_: rgb256(251, 72, 71) + property color lightRed_: rgb256(252, 91, 90) + property color darkRed_: rgb256(219, 55, 54) + property color notificationRed_: rgb256(255, 59, 48) + property color urgentOrange_: rgb256(255, 165, 0) + property color green_: rgb256(127, 255, 0) + property color presenceGreen_: rgb256(76, 217, 100) + property color smartlistSelection_: rgb256(240, 240, 240) + property color smartlistHighlight_: rgb256(245, 245, 245) +} diff --git a/src/contactadapter.cpp b/src/contactadapter.cpp new file mode 100644 index 00000000..fb837aa4 --- /dev/null +++ b/src/contactadapter.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "contactadapter.h" + +#include "lrcinstance.h" + +ContactAdapter::ContactAdapter(QObject *parent) + : QmlAdapterBase(parent) +{ + selectableProxyModel_.reset(new SelectableProxyModel(smartListModel_.get())); +} + +ContactAdapter::~ContactAdapter() {} + +QVariant +ContactAdapter::getContactSelectableModel(int type) +{ + /* + * Called from qml every time contact picker refreshes. + */ + listModeltype_ = Utils::toEnum(type); + smartListModel_.reset(new SmartListModel(LRCInstance::getCurrAccId(), + this, + listModeltype_, + LRCInstance::getCurrentConvUid())); + selectableProxyModel_->setSourceModel(smartListModel_.get()); + + /* + * Adjust filter. + */ + switch (listModeltype_) { + case SmartListModel::Type::CONFERENCE: + selectableProxyModel_->setPredicate([this](const QModelIndex &index, const QRegExp &) { + return index.data(SmartListModel::Presence).toBool(); + }); + break; + case SmartListModel::Type::TRANSFER: + selectableProxyModel_->setPredicate([this](const QModelIndex &index, const QRegExp ®exp) { + /* + * Regex to remove current callee. + */ + QRegExp matchExcept = QRegExp(QString("\\b(?!" + calleeDisplayName_ + "\\b)\\w+")); + bool match = false; + bool match_non_self = matchExcept.indexIn( + index.data(SmartListModel::Role::DisplayName).toString()) + != -1; + if (match_non_self) { + match = regexp.indexIn(index.data(SmartListModel::Role::DisplayName).toString()) + != -1; + } + return match && !index.parent().isValid(); + }); + break; + default: + break; + } + selectableProxyModel_->invalidate(); + + return QVariant::fromValue(selectableProxyModel_.get()); +} + +void +ContactAdapter::setSearchFilter(const QString &filter) +{ + if (listModeltype_ == SmartListModel::Type::CONFERENCE) { + smartListModel_->setConferenceableFilter(filter); + } + selectableProxyModel_->setFilterRegExp( + QRegExp(filter, Qt::CaseInsensitive, QRegExp::FixedString)); +} + +void +ContactAdapter::contactSelected(int index) +{ + auto contactIndex = selectableProxyModel_->index(index, 0); + auto callModel = LRCInstance::getCurrentCallModel(); + auto conversation = LRCInstance::getCurrentConversation(); + + if (contactIndex.isValid()) { + switch (listModeltype_) { + case SmartListModel::Type::CONFERENCE: { + /* + * Conference. + */ + auto sectionName = contactIndex.data(SmartListModel::Role::SectionName).value(); + if (!sectionName.isEmpty()) { + smartListModel_->toggleSection(sectionName); + return; + } + + auto convUid = contactIndex.data(SmartListModel::Role::UID).value(); + auto accId = contactIndex.data(SmartListModel::Role::AccountId).value(); + auto callId = LRCInstance::getCallIdForConversationUid(convUid, accId); + + if (!callId.isEmpty()) { + if (conversation.uid.isEmpty()) { + return; + } + auto thisCallId = conversation.confId.isEmpty() ? conversation.callId + : conversation.confId; + + callModel->joinCalls(thisCallId, callId); + } else { + auto contactUri = contactIndex.data(SmartListModel::Role::URI).value(); + auto call = LRCInstance::getCallInfoForConversation(conversation); + if (!call) { + return; + } + callModel->callAndAddParticipant(contactUri, call->id, call->isAudioOnly); + } + } break; + case SmartListModel::Type::TRANSFER: { + /* + * SIP Transfer. + */ + auto contactUri = contactIndex.data(SmartListModel::Role::URI).value(); + + if (conversation.uid.isEmpty()) { + return; + } + auto callId = conversation.confId.isEmpty() ? conversation.callId : conversation.confId; + + QString destCallId; + + try { + /* + * Check if the call exist - (check non-finished calls). + */ + auto callInfo = callModel->getCallFromURI(contactUri, true); + destCallId = callInfo.id; + } catch (std::exception &e) { + qDebug().noquote() << e.what(); + destCallId = ""; + } + /* + * If no second call -> blind transfer. + * If there is a second call -> attended transfer. + */ + if (destCallId.size() == 0) { + callModel->transfer(callId, "sip:" + contactUri); + callModel->hangUp(callId); + } else { + callModel->transferToCall(callId, destCallId); + callModel->hangUp(callId); + callModel->hangUp(destCallId); + } + } break; + default: + break; + } + } +} + +void +ContactAdapter::setCalleeDisplayName(const QString &name) +{ + calleeDisplayName_ = name; +} + +void +ContactAdapter::initQmlObject() +{} diff --git a/src/contactadapter.h b/src/contactadapter.h new file mode 100644 index 00000000..4b694770 --- /dev/null +++ b/src/contactadapter.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "qmladapterbase.h" +#include "smartlistmodel.h" + +#include +#include +#include + +/* + * The SelectableProxyModel class + * provides support for sorting and filtering data passed between another model and a view. + * + * User can customize a function pointer to pass to FilterPredicate to ensure which row in + * the source model can be accepted. + * + * Additionally, user need to setFilterRegExp to be able to get input QRegExp from FilterPredicate. + */ +class SelectableProxyModel : public QSortFilterProxyModel +{ +public: + using FilterPredicate = std::function; + + explicit SelectableProxyModel(QAbstractItemModel *parent) + : QSortFilterProxyModel(parent) + { + setSourceModel(parent); + } + ~SelectableProxyModel() {} + + void + setPredicate(FilterPredicate filterPredicate) + { + filterPredicate_ = filterPredicate; + } + + virtual bool + filterAcceptsRow(int source_row, const QModelIndex &source_parent) const + { + /* + * Accept all contacts in conversation list filtered with account type, except those in a call. + */ + auto index = sourceModel()->index(source_row, 0, source_parent); + if (filterPredicate_) { + return filterPredicate_(index, filterRegExp()); + } + } + +private: + std::function filterPredicate_; +}; + +class ContactAdapter : public QmlAdapterBase +{ + Q_OBJECT + +public: + explicit ContactAdapter(QObject *parent = nullptr); + ~ContactAdapter(); + + Q_INVOKABLE QVariant getContactSelectableModel(int type); + Q_INVOKABLE void setSearchFilter(const QString &filter); + Q_INVOKABLE void contactSelected(int index); + Q_INVOKABLE void setCalleeDisplayName(const QString &name); + +private: + void initQmlObject() override; + + SmartListModel::Type listModeltype_; + + /* + * For sip call transfer, to exclude current sip callee. + */ + QString calleeDisplayName_; + + /* + * SmartListModel is the source model of SelectableProxyModel. + */ + std::unique_ptr smartListModel_; + std::unique_ptr selectableProxyModel_; +}; diff --git a/src/conversationsadapter.cpp b/src/conversationsadapter.cpp new file mode 100644 index 00000000..d5cd4b5d --- /dev/null +++ b/src/conversationsadapter.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Anthony Lonard + * Author: Olivier Soldano + * Author: Andreas Traczyk + * Author: Isa Nanic + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "conversationsadapter.h" + +#include "utils.h" + +ConversationsAdapter::ConversationsAdapter(QObject *parent) + : QmlAdapterBase(parent) +{} + +ConversationsAdapter::~ConversationsAdapter() {} + +void +ConversationsAdapter::initQmlObject() +{ + conversationSmartListModel_ = new SmartListModel(LRCInstance::getCurrAccId(), this); + + QMetaObject::invokeMethod(qmlObj_, + "setModel", + Q_ARG(QVariant, QVariant::fromValue(conversationSmartListModel_))); + + connect(&LRCInstance::behaviorController(), + &BehaviorController::showChatView, + [this](const QString &accountId, lrc::api::conversation::Info convInfo) { + emit showChatView(accountId, convInfo.uid); + }); + + connectConversationModel(); +} + +void +ConversationsAdapter::backToWelcomePage() +{ + deselectConversation(); + QMetaObject::invokeMethod(qmlObj_, "backToWelcomePage"); +} + +void +ConversationsAdapter::selectConversation(const QString &accountId, + const QString &convUid, + bool preventSendingSignal) +{ + selectConversation(LRCInstance::getConversationFromConvUid(convUid, accountId), + preventSendingSignal); +} + +void +ConversationsAdapter::selectConversation(int index) +{ + auto convModel = LRCInstance::getCurrentConversationModel(); + + if (convModel == nullptr) { + return; + } + + const auto item = convModel->filteredConversation(index); + + if (selectConversation(item, false)) { + auto convUid = conversationSmartListModel_ + ->data(conversationSmartListModel_->index(index, 0), + static_cast(SmartListModel::Role::UID)) + .toString(); + auto &conversation = LRCInstance::getConversationFromConvUid(convUid); + /* + * If it is calling, show callview (can use showChatView signal, since it will be determined on qml). + */ + if (!conversation.uid.isEmpty() + && LRCInstance::getCurrentCallModel()->hasCall(conversation.callId)) { + emit showChatView(LRCInstance::getCurrAccId(), conversation.uid); + } + } +} + +bool +ConversationsAdapter::selectConversation(const lrc::api::conversation::Info &item, + bool preventSendingSignal) +{ + /* + * accInfo.conversationModel->selectConversation(item.uid) only emit ui + * behavior control signals, but sometimes we do not want that, + * preventSendingSignal boolean can help us to determine. + */ + if (LRCInstance::getCurrentConvUid() == item.uid) { + return false; + } else if (item.participants.size() > 0) { + auto &accInfo = LRCInstance::getAccountInfo(item.accountId); + LRCInstance::setSelectedConvId(item.uid); + if (!preventSendingSignal) + accInfo.conversationModel->selectConversation(item.uid); + accInfo.conversationModel->clearUnreadInteractions(item.uid); + return true; + } +} + +void +ConversationsAdapter::deselectConversation() +{ + if (LRCInstance::getCurrentConvUid().isEmpty()) { + return; + } + + auto currentConversationModel = LRCInstance::getCurrentConversationModel(); + + if (currentConversationModel == nullptr) { + return; + } + + currentConversationModel->selectConversation(""); + LRCInstance::setSelectedConvId(); +} + +void +ConversationsAdapter::accountChangedSetUp(const QString &accountId) +{ + /* + * Should be called when current account is changed. + */ + auto &accountInfo = LRCInstance::accountModel().getAccountInfo(accountId); + currentTypeFilter_ = accountInfo.profileInfo.type; + LRCInstance::getCurrentConversationModel()->setFilter(accountInfo.profileInfo.type); + updateConversationsFilterWidget(); + + connectConversationModel(); +} + +void +ConversationsAdapter::updateConversationsFilterWidget() +{ + /* + * Update status of "Conversations" and "Invitations". + */ + auto invites = LRCInstance::getCurrentAccountInfo().contactModel->pendingRequestCount(); + if (invites == 0 && currentTypeFilter_ == lrc::api::profile::Type::PENDING) { + currentTypeFilter_ = lrc::api::profile::Type::RING; + LRCInstance::getCurrentConversationModel()->setFilter(currentTypeFilter_); + } + showConversationTabs(invites); +} + +void +ConversationsAdapter::setConversationFilter(const QString &type) +{ + /* + * Set conversation filter according to type, + * type needs to be recognizable by lrc::api::profile::to_type. + */ + if (type.isEmpty()) { + if (LRCInstance::getCurrentAccountInfo().profileInfo.type == lrc::api::profile::Type::RING) + setConversationFilter(lrc::api::profile::Type::RING); + else + setConversationFilter(lrc::api::profile::Type::SIP); + } else { + setConversationFilter(lrc::api::profile::to_type(type)); + } +} + +void +ConversationsAdapter::setConversationFilter(lrc::api::profile::Type filter) +{ + if (currentTypeFilter_ == filter) { + return; + } + currentTypeFilter_ = filter; + LRCInstance::getCurrentConversationModel()->setFilter(currentTypeFilter_); +} + +bool +ConversationsAdapter::connectConversationModel() +{ + /* + * Signal connections + */ + auto currentConversationModel = LRCInstance::getCurrentAccountInfo().conversationModel.get(); + + QObject::disconnect(modelSortedConnection_); + QObject::disconnect(modelUpdatedConnection_); + QObject::disconnect(filterChangedConnection_); + QObject::disconnect(newConversationConnection_); + QObject::disconnect(conversationRemovedConnection_); + QObject::disconnect(conversationClearedConnection); + QObject::disconnect(newInteractionConnection_); + QObject::disconnect(interactionRemovedConnection_); + + modelSortedConnection_ = QObject::connect( + currentConversationModel, &lrc::api::ConversationModel::modelSorted, [this]() { + updateConversationsFilterWidget(); + QMetaObject::invokeMethod(qmlObj_, "updateConversationSmartListView"); + auto convUid = LRCInstance::getCurrentConversation().uid; + auto convModel = LRCInstance::getCurrentConversationModel(); + auto &conversation = LRCInstance::getConversationFromConvUid(convUid); + if (conversation.uid.isEmpty()) { + return; + } + auto contactURI = conversation.participants[0]; + if (contactURI.isEmpty() + || convModel->owner.contactModel->getContact(contactURI).profileInfo.type + == lrc::api::profile::Type::TEMPORARY) { + return; + } + QMetaObject::invokeMethod(qmlObj_, "modelSorted", Q_ARG(QVariant, contactURI)); + }); + + modelUpdatedConnection_ + = QObject::connect(currentConversationModel, + &lrc::api::ConversationModel::conversationUpdated, + [this](const QString &convUid) { + Q_UNUSED(convUid); + updateConversationsFilterWidget(); + QMetaObject::invokeMethod(qmlObj_, "updateConversationSmartListView"); + }); + + filterChangedConnection_ = QObject::connect( + currentConversationModel, &lrc::api::ConversationModel::filterChanged, [this]() { + QMetaObject::invokeMethod(qmlObj_, + "updateSmartList", + Q_ARG(QVariant, LRCInstance::getCurrAccId())); + updateConversationsFilterWidget(); + QMetaObject::invokeMethod(qmlObj_, "updateConversationSmartListView"); + }); + + newConversationConnection_ + = QObject::connect(currentConversationModel, + &lrc::api::ConversationModel::newConversation, + [this](const QString &convUid) { + QMetaObject::invokeMethod(qmlObj_, + "updateSmartList", + Q_ARG(QVariant, + LRCInstance::getCurrAccId())); + updateConversationForNewContact(convUid); + }); + + conversationRemovedConnection_ + = QObject::connect(currentConversationModel, + &lrc::api::ConversationModel::conversationRemoved, + [this]() { backToWelcomePage(); }); + + conversationClearedConnection + = QObject::connect(currentConversationModel, + &lrc::api::ConversationModel::conversationCleared, + [this](const QString &convUid) { + /* + * If currently selected, + * switch to welcome screen (deselecting current smartlist item ). + */ + if (convUid != LRCInstance::getCurrentConvUid()) { + return; + } + backToWelcomePage(); + }); + + newInteractionConnection_ + = QObject::connect(currentConversationModel, + &lrc::api::ConversationModel::newInteraction, + [this] { + updateConversationsFilterWidget(); + QMetaObject::invokeMethod(qmlObj_, "updateConversationSmartListView"); + }); + + currentConversationModel->setFilter(""); + return true; +} + +void +ConversationsAdapter::updateConversationForNewContact(const QString &convUid) +{ + auto convModel = LRCInstance::getCurrentConversationModel(); + if (convModel == nullptr) { + return; + } + auto selectedUid = LRCInstance::getCurrentConvUid(); + auto &conversation = LRCInstance::getConversationFromConvUid(convUid, {}, true); + if (!conversation.uid.isEmpty()) { + try { + auto contact = convModel->owner.contactModel->getContact(conversation.participants[0]); + if (!contact.profileInfo.uri.isEmpty() && contact.profileInfo.uri == selectedUid) { + LRCInstance::setSelectedConvId(convUid); + convModel->selectConversation(convUid); + } + } catch (...) { + return; + } + } +} \ No newline at end of file diff --git a/src/conversationsadapter.h b/src/conversationsadapter.h new file mode 100644 index 00000000..33dea8fc --- /dev/null +++ b/src/conversationsadapter.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "lrcinstance.h" +#include "qmladapterbase.h" +#include "smartlistmodel.h" + +#include +#include + +class ConversationsAdapter : public QmlAdapterBase +{ + Q_OBJECT + +public: + explicit ConversationsAdapter(QObject *parent = nullptr); + ~ConversationsAdapter(); + + Q_INVOKABLE bool connectConversationModel(); + Q_INVOKABLE void selectConversation(const QString &accountId, + const QString &convUid, + bool preventSendingSignal = true); + Q_INVOKABLE void selectConversation(int index); + Q_INVOKABLE void deselectConversation(); + Q_INVOKABLE void accountChangedSetUp(const QString &accountId); + Q_INVOKABLE void updateConversationsFilterWidget(); + Q_INVOKABLE void setConversationFilter(const QString &type); + +signals: + void showChatView(const QString &accountId, const QString &convUid); + void showConversationTabs(bool visible); + +private: + void initQmlObject() override; + void setConversationFilter(lrc::api::profile::Type filter); + void backToWelcomePage(); + bool selectConversation(const lrc::api::conversation::Info &item, + bool preventSendingSignal = true); + void updateConversationForNewContact(const QString &convUid); + + SmartListModel *conversationSmartListModel_; + + lrc::api::profile::Type currentTypeFilter_{}; + + /* + * Connections. + */ + QMetaObject::Connection modelSortedConnection_; + QMetaObject::Connection modelUpdatedConnection_; + QMetaObject::Connection filterChangedConnection_; + QMetaObject::Connection newConversationConnection_; + QMetaObject::Connection conversationRemovedConnection_; + QMetaObject::Connection newInteractionConnection_; + QMetaObject::Connection conversationClearedConnection; + QMetaObject::Connection selectedCallChanged_; + QMetaObject::Connection smartlistSelectionConnection_; + QMetaObject::Connection interactionRemovedConnection_; +}; diff --git a/src/deviceitemlistmodel.cpp b/src/deviceitemlistmodel.cpp new file mode 100644 index 00000000..6b2739c0 --- /dev/null +++ b/src/deviceitemlistmodel.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "deviceitemlistmodel.h" + +DeviceItemListModel::DeviceItemListModel(QObject *parent) + : QAbstractListModel(parent) +{} + +DeviceItemListModel::~DeviceItemListModel() {} + +int +DeviceItemListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + return LRCInstance::getCurrentAccountInfo().deviceModel->getAllDevices().size(); + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +DeviceItemListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +DeviceItemListModel::data(const QModelIndex &index, int role) const +{ + auto deviceList = LRCInstance::getCurrentAccountInfo().deviceModel->getAllDevices(); + if (!index.isValid() || deviceList.size() <= index.row()) { + return QVariant(); + } + + switch (role) { + case Role::DeviceName: + return QVariant(deviceList.at(index.row()).name); + case Role::DeviceID: + return QVariant(deviceList.at(index.row()).id); + case Role::IsCurrent: + return QVariant(deviceList.at(index.row()).isCurrent); + } + return QVariant(); +} + +QHash +DeviceItemListModel::roleNames() const +{ + QHash roles; + roles[DeviceName] = "DeviceName"; + roles[DeviceID] = "DeviceID"; + roles[IsCurrent] = "IsCurrent"; + return roles; +} + +QModelIndex +DeviceItemListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +DeviceItemListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +DeviceItemListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +DeviceItemListModel::reset() +{ + beginResetModel(); + endResetModel(); +} diff --git a/src/deviceitemlistmodel.h b/src/deviceitemlistmodel.h new file mode 100644 index 00000000..ff560710 --- /dev/null +++ b/src/deviceitemlistmodel.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class DeviceItemListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Role { DeviceName = Qt::UserRole + 1, DeviceID, IsCurrent }; + Q_ENUM(Role) + + explicit DeviceItemListModel(QObject *parent = 0); + ~DeviceItemListModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); +}; diff --git a/src/distantrenderer.cpp b/src/distantrenderer.cpp new file mode 100644 index 00000000..023442b3 --- /dev/null +++ b/src/distantrenderer.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "distantrenderer.h" + +#include "lrcinstance.h" + +DistantRenderer::DistantRenderer(QQuickItem *parent) + : QQuickPaintedItem(parent) +{ + setAntialiasing(true); + setFillColor(Qt::black); + setRenderTarget(QQuickPaintedItem::FramebufferObject); + setPerformanceHint(QQuickPaintedItem::FastFBOResizing); + + connect(LRCInstance::renderer(), &RenderManager::distantFrameUpdated, [this](const QString &id) { + if (distantRenderId_ == id) + update(QRect(0, 0, width(), height())); + }); + + connect(LRCInstance::renderer(), + &RenderManager::distantRenderingStopped, + [this](const QString &id) { + if (distantRenderId_ == id) + update(QRect(0, 0, width(), height())); + }); +} + +DistantRenderer::~DistantRenderer() {} + +void +DistantRenderer::setRendererId(const QString &id) +{ + distantRenderId_ = id; + update(QRect(0, 0, width(), height())); +} + +void +DistantRenderer::paint(QPainter *painter) +{ + auto distantImage = LRCInstance::renderer()->getFrame(distantRenderId_); + if (distantImage) { + auto scaledDistant = distantImage->scaled(size().toSize(), Qt::KeepAspectRatio); + auto xDiff = (width() - scaledDistant.width()) / 2; + auto yDiff = (height() - scaledDistant.height()) / 2; + painter->drawImage(QRect(xDiff, yDiff, scaledDistant.width(), scaledDistant.height()), + scaledDistant); + } +} \ No newline at end of file diff --git a/src/distantrenderer.h b/src/distantrenderer.h new file mode 100644 index 00000000..d26f2c2c --- /dev/null +++ b/src/distantrenderer.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +/* + * Use QQuickPaintedItem so that QPainter apis can be used. + * Note: Old video pipeline. + */ + +class DistantRenderer : public QQuickPaintedItem +{ + Q_OBJECT +public: + explicit DistantRenderer(QQuickItem *parent = 0); + ~DistantRenderer(); + + Q_INVOKABLE void setRendererId(const QString &id); + +private: + void paint(QPainter *painter); + + /* + * Unique DistantRenderId for each call. + */ + QString distantRenderId_; +}; \ No newline at end of file diff --git a/src/globalsystemtray.cpp b/src/globalsystemtray.cpp new file mode 100644 index 00000000..a1271c5e --- /dev/null +++ b/src/globalsystemtray.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "globalsystemtray.h" + +GlobalSystemTray::GlobalSystemTray() {} + +void +GlobalSystemTray::setTriggeredAccountId(const QString &accountId) +{ + triggeredAccountId_ = accountId; +} + +const QString & +GlobalSystemTray::getTriggeredAccountId() +{ + return triggeredAccountId_; +} + +void +GlobalSystemTray::setPossibleOnGoingConversationInfo(const lrc::api::conversation::Info &convInfo) +{ + triggeredOnGoingConvInfo_ = convInfo; +} + +const lrc::api::conversation::Info & +GlobalSystemTray::getPossibleOnGoingConversationInfo() +{ + return triggeredOnGoingConvInfo_; +} diff --git a/src/globalsystemtray.h b/src/globalsystemtray.h new file mode 100644 index 00000000..e72713cb --- /dev/null +++ b/src/globalsystemtray.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "lrcinstance.h" + +#include + +class GlobalSystemTray : public QSystemTrayIcon +{ + Q_OBJECT + +public: + static GlobalSystemTray & + instance() + { + static GlobalSystemTray *instance_ = new GlobalSystemTray(); + + return *instance_; + } + + /* + * Remember the last triggering account for the notification, + * safe since user cannot activate previous notifications. + */ + void setTriggeredAccountId(const QString &accountId); + + const QString &getTriggeredAccountId(); + + void setPossibleOnGoingConversationInfo(const lrc::api::conversation::Info &convInfo); + + const lrc::api::conversation::Info &getPossibleOnGoingConversationInfo(); + +private: + GlobalSystemTray(); + + QString triggeredAccountId_; + lrc::api::conversation::Info triggeredOnGoingConvInfo_; +}; diff --git a/src/jamiavatartheme.h b/src/jamiavatartheme.h new file mode 100644 index 00000000..08e721a1 --- /dev/null +++ b/src/jamiavatartheme.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 + +#pragma once + +namespace JamiAvatarTheme { +static const QColor defaultAvatarColor_ = {"#ff9e9e9e"}; //Grey +static const QColor avatarColors_[]{ + {"#fff44336"}, //Red + {"#ffe91e63"}, //Pink + {"#ff9c27b0"}, //Purple + {"#ff673ab7"}, //Deep Purple + {"#ff3f51b5"}, //Indigo + {"#ff2196f3"}, //Blue + {"#ff00bcd4"}, //Cyan + {"#ff009688"}, //Teal + {"#ff4caf50"}, //Green + {"#ff8bc34a"}, //Light Green + {"#ff9e9e9e"}, //Grey + {"#ffcddc39"}, //Lime + {"#ffffc107"}, //Amber + {"#ffff5722"}, //Deep Orange + {"#ff795548"}, //Brown + {"#ff607d8b"} //Blue Grey +}; +} // namespace JamiAvatarTheme diff --git a/src/lrcinstance.h b/src/lrcinstance.h new file mode 100644 index 00000000..16762f14 --- /dev/null +++ b/src/lrcinstance.h @@ -0,0 +1,475 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * Author: Isa Nanic + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifdef _MSC_VER +#undef ERROR +#endif + +#include "accountlistmodel.h" +#include "rendermanager.h" +#include "settingskey.h" +#include "utils.h" + +#include "api/account.h" +#include "api/avmodel.h" +#include "api/pluginmodel.h" +#include "api/behaviorcontroller.h" +#include "api/contact.h" +#include "api/contactmodel.h" +#include "api/conversation.h" +#include "api/conversationmodel.h" +#include "api/datatransfermodel.h" +#include "api/lrc.h" +#include "api/newaccountmodel.h" +#include "api/newcallmodel.h" +#include "api/newcodecmodel.h" +#include "api/newdevicemodel.h" +#include "api/peerdiscoverymodel.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace lrc::api; + +using migrateCallback = std::function; +using getConvPredicate = std::function; + +class LRCInstance : public QObject +{ + Q_OBJECT + +public: + static LRCInstance & + instance(migrateCallback willMigrate = {}, migrateCallback didMigrate = {}) + { + static LRCInstance instance_(willMigrate, didMigrate); + return instance_; + }; + static void + init(migrateCallback willMigrate = {}, migrateCallback didMigrate = {}) + { + instance(willMigrate, didMigrate); + }; + static Lrc & + getAPI() + { + return *(instance().lrc_); + }; + static RenderManager * + renderer() + { + return instance().renderer_.get(); + } + static void + connectivityChanged() + { + instance().lrc_->connectivityChanged(); + }; + static NewAccountModel & + accountModel() + { + return instance().lrc_->getAccountModel(); + }; + static BehaviorController & + behaviorController() + { + return instance().lrc_->getBehaviorController(); + }; + static DataTransferModel & + dataTransferModel() + { + return instance().lrc_->getDataTransferModel(); + }; + static AVModel & + avModel() + { + return instance().lrc_->getAVModel(); + }; + static PluginModel & + pluginModel() + { + return instance().lrc_->getPluginModel(); + }; + static bool + isConnected() + { + return instance().lrc_->isConnected(); + }; + static VectorString + getActiveCalls() + { + return instance().lrc_->activeCalls(); + }; + static const account::Info & + getAccountInfo(const QString &accountId) + { + return accountModel().getAccountInfo(accountId); + }; + static const account::Info & + getCurrentAccountInfo() + { + return getAccountInfo(getCurrAccId()); + }; + static bool + hasVideoCall() + { + auto activeCalls = instance().lrc_->activeCalls(); + auto accountList = accountModel().getAccountList(); + bool result = false; + for (const auto &callId : activeCalls) { + for (const auto &accountId : accountList) { + auto &accountInfo = accountModel().getAccountInfo(accountId); + if (accountInfo.callModel->hasCall(callId)) { + auto call = accountInfo.callModel->getCall(callId); + result |= !(call.isAudioOnly || call.videoMuted); + } + } + } + return result; + }; + static QString + getCallIdForConversationUid(const QString &convUid, const QString &accountId) + { + auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId); + if (convInfo.uid.isEmpty()) { + return {}; + } + return convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId; + } + static const call::Info * + getCallInfo(const QString &callId, const QString &accountId) + { + try { + auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + if (!accInfo.callModel->hasCall(callId)) { + return nullptr; + } + return &accInfo.callModel->getCall(callId); + } catch (...) { + return nullptr; + } + } + static const call::Info * + getCallInfoForConversation(const conversation::Info &convInfo, bool forceCallOnly = {}) + { + try { + auto accountId = convInfo.accountId; + auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId); + auto callId = forceCallOnly + ? convInfo.callId + : (convInfo.confId.isEmpty() ? convInfo.callId : convInfo.confId); + if (!accInfo.callModel->hasCall(callId)) { + return nullptr; + } + return &accInfo.callModel->getCall(callId); + } catch (...) { + return nullptr; + } + } + static const conversation::Info & + getConversation(const QString &accountId, getConvPredicate pred = {}, bool filtered = false) + { + using namespace lrc::api; + static conversation::Info invalid = {}; + try { + auto &accInfo = LRCInstance::getAccountInfo(accountId); + auto &convModel = accInfo.conversationModel; + if (filtered) { + auto &convs = convModel->allFilteredConversations(); + auto conv = std::find_if(convs.begin(), convs.end(), pred); + if (conv != convs.end()) { + return *conv; + } + } else { + for (int i = Utils::toUnderlyingValue(profile::Type::RING); + i <= Utils::toUnderlyingValue(profile::Type::TEMPORARY); + ++i) { + auto filter = Utils::toEnum(i); + auto &convs = convModel->getFilteredConversations(filter); + auto conv = std::find_if(convs.begin(), convs.end(), pred); + if (conv != convs.end()) { + return *conv; + } + } + } + } catch (...) { + } + return invalid; + } + static const conversation::Info & + getConversationFromCallId(const QString &callId, + const QString &accountId = {}, + bool filtered = false) + { + return getConversation( + !accountId.isEmpty() ? accountId : getCurrAccId(), + [&](const conversation::Info &conv) -> bool { return callId == conv.callId; }, + filtered); + } + static const conversation::Info & + getConversationFromConvUid(const QString &convUid, + const QString &accountId = {}, + bool filtered = false) + { + return getConversation( + !accountId.isEmpty() ? accountId : getCurrAccId(), + [&](const conversation::Info &conv) -> bool { return convUid == conv.uid; }, + filtered); + } + static const conversation::Info & + getConversationFromPeerUri(const QString &peerUri, + const QString &accountId = {}, + bool filtered = false) + { + return getConversation( + !accountId.isEmpty() ? accountId : getCurrAccId(), + [&](const conversation::Info &conv) -> bool { return peerUri == conv.participants[0]; }, + filtered); + } + static const conversation::Info & + getCurrentConversation() + { + return getConversationFromConvUid(getCurrentConvUid()); + } + + static ConversationModel * + getCurrentConversationModel() + { + return getCurrentAccountInfo().conversationModel.get(); + }; + + static NewCallModel * + getCurrentCallModel() + { + return getCurrentAccountInfo().callModel.get(); + }; + + static const QString & + getCurrAccId() + { + auto accountList = accountModel().getAccountList(); + if (instance().selectedAccountId_.isEmpty() && accountList.size()) { + instance().selectedAccountId_ = accountList.at(0); + } + return instance().selectedAccountId_; + }; + + static void + setSelectedAccountId(const QString &accountId = {}) + { + instance().selectedAccountId_ = accountId; + QSettings settings("jami.net", "Jami"); + settings.setValue(SettingsKey::selectedAccount, accountId); + + // Last selected account should be set as preferred. + accountModel().setTopAccount(accountId); + }; + + static const QString & + getCurrentConvUid() + { + return instance().selectedConvUid_; + }; + + static void + setSelectedConvId(const QString &convUid = {}) + { + instance().selectedConvUid_ = convUid; + }; + + static void + reset(bool newInstance = false) + { + if (newInstance) { + instance().renderer_.reset(new RenderManager(avModel())); + instance().lrc_.reset(new Lrc()); + } else { + instance().renderer_.reset(); + instance().lrc_.reset(); + } + }; + + static const int + getCurrentAccountIndex() + { + for (int i = 0; i < accountModel().getAccountList().size(); i++) { + if (accountModel().getAccountList()[i] == getCurrAccId()) { + return i; + } + } + return -1; + }; + + static const QPixmap + getCurrAccPixmap() + { + return instance() + .accountListModel_ + .data(instance().accountListModel_.index(getCurrentAccountIndex()), + AccountListModel::Role::Picture) + .value(); + }; + + static void + setAvatarForAccount(const QPixmap &avatarPixmap, const QString &accountID) + { + QByteArray ba; + QBuffer bu(&ba); + bu.open(QIODevice::WriteOnly); + avatarPixmap.save(&bu, "PNG"); + auto str = QString::fromLocal8Bit(ba.toBase64()); + accountModel().setAvatar(accountID, str); + }; + + static void + setCurrAccAvatar(const QPixmap &avatarPixmap) + { + QByteArray ba; + QBuffer bu(&ba); + bu.open(QIODevice::WriteOnly); + avatarPixmap.save(&bu, "PNG"); + auto str = QString::fromLocal8Bit(ba.toBase64()); + accountModel().setAvatar(getCurrAccId(), str); + }; + + static void + setCurrAccAvatar(const QString &avatar) + { + accountModel().setAvatar(getCurrAccId(), avatar); + }; + + static void + setCurrAccDisplayName(const QString &displayName) + { + auto accountId = LRCInstance::getCurrAccId(); + accountModel().setAlias(accountId, displayName); + /* + * Force save to .yml. + */ + auto confProps = LRCInstance::accountModel().getAccountConfig(accountId); + LRCInstance::accountModel().setAccountConfig(accountId, confProps); + }; + + static const account::ConfProperties_t & + getCurrAccConfig() + { + return instance().getCurrentAccountInfo().confProperties; + } + + static void + subscribeToDebugReceived() + { + instance().lrc_->subscribeToDebugReceived(); + } + + static void + startAudioMeter(bool async) + { + auto f = [] { + if (!LRCInstance::getActiveCalls().size()) { + LRCInstance::avModel().startAudioDevice(); + } + LRCInstance::avModel().setAudioMeterState(true); + }; + if (async) { + QtConcurrent::run(f); + } else { + f(); + } + } + + static void + stopAudioMeter(bool async) + { + auto f = [] { + if (!LRCInstance::getActiveCalls().size()) { + LRCInstance::avModel().stopAudioDevice(); + } + LRCInstance::avModel().setAudioMeterState(false); + }; + if (async) { + QtConcurrent::run(f); + } else { + f(); + } + } + + static QString + getContentDraft(const QString &convUid, const QString &accountId) + { + auto draftKey = accountId + "_" + convUid; + return instance().contentDrafts_[draftKey]; + } + + static void + setContentDraft(const QString &convUid, const QString &accountId, const QString &content) + { + auto draftKey = accountId + "_" + convUid; + instance().contentDrafts_[draftKey] = content; + } + + static void + pushLastConferencee(const QString &confId, const QString &callId) + { + instance().lastConferencees_[confId] = callId; + } + + static QString + popLastConferencee(const QString &confId) + { + QString callId = {}; + auto iter = instance().lastConferencees_.find(confId); + if (iter != instance().lastConferencees_.end()) { + callId = iter.value(); + instance().lastConferencees_.erase(iter); + } + return callId; + } + +signals: + void accountListChanged(); + +private: + LRCInstance(migrateCallback willMigrateCb = {}, migrateCallback didMigrateCb = {}) + { + lrc_ = std::make_unique(willMigrateCb, didMigrateCb); + renderer_ = std::make_unique(lrc_->getAVModel()); + }; + + std::unique_ptr lrc_; + std::unique_ptr renderer_; + AccountListModel accountListModel_; + QString selectedAccountId_; + QString selectedConvUid_; + MapStringString contentDrafts_; + MapStringString lastConferencees_; +}; +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) +Q_DECLARE_METATYPE(LRCInstance *) +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..2b1349ce --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2015-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "mainapplication.h" +#include "runguard.h" + +#include +#include + +#include + +int +main(int argc, char *argv[]) +{ + setlocale(LC_ALL, "en_US.utf8"); +#ifdef Q_OS_LINUX + setenv("QT_QPA_PLATFORMTHEME", "gtk3", true); +#endif + + MainApplication::applicationInitialization(); + + char ARG_DISABLE_WEB_SECURITY[] = "--disable-web-security"; + auto newArgv = MainApplication::parseInputArgument(argc, argv, ARG_DISABLE_WEB_SECURITY); + + MainApplication a(argc, newArgv); + + /* + * Runguard to make sure that only one instance runs at a time. + * Note: needs to be after the creation of the application + */ + QCryptographicHash appData(QCryptographicHash::Sha256); + appData.addData(QApplication::applicationName().toUtf8()); + appData.addData(QApplication::organizationDomain().toUtf8()); + RunGuard guard(appData.result()); + if (!guard.tryToRun()) { + /* + * No need to exitApp since app is not set up. + */ + return 0; + } + + if (!a.applicationSetup()) { + guard.release(); + a.exitApp(); + return 0; + } + + /* + * Exec the application. + */ + auto ret = a.exec(); + + guard.release(); + return ret; +} diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp new file mode 100644 index 00000000..938a0eb5 --- /dev/null +++ b/src/mainapplication.cpp @@ -0,0 +1,450 @@ +/* + * Copyright (C) 2015-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "mainapplication.h" + +#include "accountadapter.h" +#include "accountlistmodel.h" +#include "accountstomigratelistmodel.h" +#include "audiocodeclistmodel.h" +#include "audioinputdevicemodel.h" +#include "audiomanagerlistmodel.h" +#include "audiooutputdevicemodel.h" +#include "avadapter.h" +#include "bannedlistmodel.h" +#include "calladapter.h" +#include "clientwrapper.h" +#include "contactadapter.h" +#include "conversationsadapter.h" +#include "deviceitemlistmodel.h" +#include "pluginitemlistmodel.h" +#include "preferenceitemlistmodel.h" +#include "distantrenderer.h" +#include "globalinstances.h" +#include "globalsystemtray.h" +#include "messagesadapter.h" +#include "namedirectory.h" +#include "pixbufmanipulator.h" +#include "previewrenderer.h" +#include "qrimageprovider.h" +#include "settingsadaptor.h" +#include "tintedbuttonimageprovider.h" +#include "utils.h" +#include "version.h" +#include "videocodeclistmodel.h" +#include "videoformatfpsmodel.h" +#include "videoformatresolutionmodel.h" +#include "videoinputdevicemodel.h" + +#include +#include +#include + +#include + +#ifdef Q_OS_WIN +#include +#endif + +#if defined _MSC_VER && !COMPILE_ONLY +#include +#endif + +MainApplication::MainApplication(int &argc, char **argv) + : QApplication(argc, argv) + , engine_(new QQmlApplicationEngine()) +{ + QObject::connect(this, &QApplication::aboutToQuit, [this] { exitApp(); }); +} + +void +MainApplication::applicationInitialization() +{ + /* + * Some attributes are needed to be set before the creation of the application. + */ + QApplication::setApplicationName("Ring"); + QApplication::setOrganizationDomain("jami.net"); + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QApplication::setQuitOnLastWindowClosed(false); + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) + QApplication::setHighDpiScaleFactorRoundingPolicy( + Qt::HighDpiScaleFactorRoundingPolicy::RoundPreferFloor); + /* + * Initialize QtWebEngine. + */ + QtWebEngine::initialize(); +#endif +} + +void +MainApplication::consoleDebug() +{ +#ifdef Q_OS_WIN + AllocConsole(); + SetConsoleCP(CP_UTF8); + + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + + COORD coordInfo; + coordInfo.X = 130; + coordInfo.Y = 9000; + + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coordInfo); + SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS); +#endif +} + +void +MainApplication::vsConsoleDebug() +{ +#ifdef _MSC_VER + /* + * Print debug to output window if using VS. + */ + QObject::connect(&LRCInstance::behaviorController(), + &lrc::api::BehaviorController::debugMessageReceived, + [](const QString &message) { + OutputDebugStringA((message + "\n").toStdString().c_str()); + }); +#endif +} + +void +MainApplication::fileDebug(QFile *debugFile) +{ + QObject::connect(&LRCInstance::behaviorController(), + &lrc::api::BehaviorController::debugMessageReceived, + [debugFile](const QString &message) { + if (debugFile->open(QIODevice::WriteOnly | QIODevice::Append)) { + auto msg = (message + "\n").toStdString().c_str(); + debugFile->write(msg, qstrlen(msg)); + debugFile->close(); + } + }); +} + +void +MainApplication::exitApp() +{ + GlobalSystemTray::instance().hide(); +#ifdef Q_OS_WIN + FreeConsole(); +#endif +} + +char ** +MainApplication::parseInputArgument(int &argc, char *argv[], char *argToParse) +{ + /* + * Forcefully append argToParse. + */ + int oldArgc = argc; + argc = argc + 1 + 1; + char **newArgv = new char *[argc]; + for (int i = 0; i < oldArgc; i++) { + newArgv[i] = argv[i]; + } + newArgv[oldArgc] = argToParse; + newArgv[oldArgc + 1] = nullptr; + return newArgv; +} + +QString +MainApplication::getDebugFilePath() +{ + QDir logPath(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); + /* + * Since logPath will be .../Ring, we use cdUp to remove it. + */ + logPath.cdUp(); + return QString(logPath.absolutePath() + "/jami/jami.log"); +} + +void +MainApplication::loadTranslations() +{ + auto appDir = qApp->applicationDirPath() + "/"; + const auto locale_name = QLocale::system().name(); + const auto locale_lang = locale_name.split('_')[0]; + + QTranslator *qtTranslator_lang = new QTranslator(this); + QTranslator *qtTranslator_name = new QTranslator(this); + if (locale_name != locale_lang) { + if (qtTranslator_lang->load("qt_" + locale_lang, + QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + installTranslator(qtTranslator_lang); + } + qtTranslator_name->load("qt_" + locale_name, + QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + installTranslator(qtTranslator_name); + + QTranslator *lrcTranslator_lang = new QTranslator(this); + QTranslator *lrcTranslator_name = new QTranslator(this); + if (locale_name != locale_lang) { + if (lrcTranslator_lang->load(appDir + "share/libringclient/translations/lrc_" + locale_lang)) + installTranslator(lrcTranslator_lang); + } + if (lrcTranslator_name->load(appDir + "share/libringclient/translations/lrc_" + locale_name)) + installTranslator(lrcTranslator_name); + + QTranslator *mainTranslator_lang = new QTranslator(this); + QTranslator *mainTranslator_name = new QTranslator(this); + if (locale_name != locale_lang) { + if (mainTranslator_lang->load(appDir + "share/ring/translations/ring_client_windows_" + + locale_lang)) + installTranslator(mainTranslator_lang); + } + if (mainTranslator_name->load(appDir + "share/ring/translations/ring_client_windows_" + + locale_name)) + installTranslator(mainTranslator_name); +} + +void +MainApplication::initLrc() +{ + /* + * Init mainwindow and finish splash when mainwindow shows up. + */ + std::atomic_bool isMigrating(false); + LRCInstance::init( + [this, &isMigrating] { + /* + * TODO: splash screen for account migration. + */ + isMigrating = true; + while (isMigrating) { + this->processEvents(); + } + }, + [this, &isMigrating] { + while (!isMigrating) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + isMigrating = false; + }); + LRCInstance::subscribeToDebugReceived(); + LRCInstance::getAPI().holdConferences = false; +} + +void +MainApplication::processInputArgument(bool &startMinimized) +{ + debugFile_ = std::make_unique(getDebugFilePath()); + QString uri = ""; + + for (auto string : QCoreApplication::arguments()) { + if (string.startsWith("jami:")) { + uri = string; + } else { + if (string == "-m" || string == "--minimized") { + startMinimized = true; + } + auto dbgFile = string == "-f" || string == "--file"; + auto dbgConsole = string == "-c" || string == "--vsconsole"; + if (dbgFile || dbgConsole) { + if (dbgFile) { + debugFile_->open(QIODevice::WriteOnly | QIODevice::Truncate); + debugFile_->close(); + fileDebug(debugFile_.get()); + } +#ifdef _MSC_VER + if (dbgConsole) { + vsConsoleDebug(); + } +#endif + } + } + } +} + +void +MainApplication::setApplicationFont() +{ + QFont font; + font.setFamily("Segoe UI"); + setFont(font); + QFontDatabase::addApplicationFont(":/images/FontAwesome.otf"); +} + +void +MainApplication::qmlInitialization() +{ + /* + * Register accountListModel type. + */ + QML_REGISTERTYPE(AccountListModel, 1, 0); + QML_REGISTERTYPE(DeviceItemListModel, 1, 0); + QML_REGISTERTYPE(PluginItemListModel, 1, 0); + QML_REGISTERTYPE(PreferenceItemListModel, 1, 0); + QML_REGISTERTYPE(BannedListModel, 1, 0); + QML_REGISTERTYPE(VideoCodecListModel, 1, 0); + QML_REGISTERTYPE(AudioCodecListModel, 1, 0); + QML_REGISTERTYPE(AccountsToMigrateListModel, 1, 0); + QML_REGISTERTYPE(AudioInputDeviceModel, 1, 0); + QML_REGISTERTYPE(AudioOutputDeviceModel, 1, 0); + QML_REGISTERTYPE(AudioManagerListModel, 1, 0); + QML_REGISTERTYPE(VideoInputDeviceModel, 1, 0); + QML_REGISTERTYPE(VideoFormatResolutionModel, 1, 0); + QML_REGISTERTYPE(VideoFormatFpsModel, 1, 0); + /* + * Register QQuickItem type. + */ + QML_REGISTERTYPE(PreviewRenderer, 1, 0); + QML_REGISTERTYPE(VideoCallPreviewRenderer, 1, 0); + QML_REGISTERTYPE(DistantRenderer, 1, 0); + QML_REGISTERTYPE(PhotoboothPreviewRender, 1, 0) + + /* + * Adapter - qmlRegisterSingletonType. + * Note: in future, if lrc is fully compatible with qml (C++ struct + * is readable in qml), the adapters can be optimized away. + */ + QML_REGISTERSINGLETONTYPE_URL(QStringLiteral("qrc:/src/constant/JamiTheme.qml"), + JamiTheme, + 1, + 0); + QML_REGISTERSINGLETONTYPE(CallAdapter, 1, 0); + + QML_REGISTERSINGLETONTYPE(MessagesAdapter, 1, 0); + QML_REGISTERSINGLETONTYPE(ConversationsAdapter, 1, 0); + QML_REGISTERSINGLETONTYPE(AvAdapter, 1, 0); + QML_REGISTERSINGLETONTYPE(ContactAdapter, 1, 0); + QML_REGISTERSINGLETONTYPE(ClientWrapper, 1, 0); + + //QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(AccountAdapter, 1, 0); + //QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(UtilsAdapter, 1, 0); + QML_REGISTERUNCREATABLE(AccountAdapter, 1, 0); + QML_REGISTERUNCREATABLE(UtilsAdapter, 1, 0); + QML_REGISTERUNCREATABLE(SettingsAdaptor, 1, 0); + QML_REGISTERUNCREATABLE(NameDirectory, 1, 0); + QML_REGISTERUNCREATABLE(LRCInstance, 1, 0); + + /* + * Lrc models - qmlRegisterUncreatableType & Q_DECLARE_METATYPE. + * This to make lrc models recognizable in qml. + */ + QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewAccountModel, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(BehaviorController, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(DataTransferModel, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(AVModel, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(ContactModel, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(ConversationModel, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewCallModel, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(PluginModel, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewDeviceModel, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(NewCodecModel, lrc::api, 1, 0); + QML_REGISTERUNCREATABLE_IN_NAMESPACE(PeerDiscoveryModel, lrc::api, 1, 0); + + /* + * Client models - qmlRegisterUncreatableType & Q_DECLARE_METATYPE. + * This to make client models recognizable in qml. + */ + QML_REGISTERUNCREATABLE(RenderManager, 1, 0); + + /* + * Namespaces - qmlRegisterUncreatableMetaObject. + */ + QML_REGISTERNAMESPACE(lrc::api::staticMetaObject, "Lrc", 1, 0); + QML_REGISTERNAMESPACE(lrc::api::account::staticMetaObject, "Account", 1, 0); + QML_REGISTERNAMESPACE(lrc::api::call::staticMetaObject, "Call", 1, 0); + QML_REGISTERNAMESPACE(lrc::api::datatransfer::staticMetaObject, "Datatransfer", 1, 0); + QML_REGISTERNAMESPACE(lrc::api::interaction::staticMetaObject, "Interaction", 1, 0); + QML_REGISTERNAMESPACE(lrc::api::video::staticMetaObject, "Video", 1, 0); + QML_REGISTERNAMESPACE(lrc::api::profile::staticMetaObject, "Profile", 1, 0); + + /* + * Add image provider. + */ + engine_->addImageProvider(QLatin1String("qrImage"), new QrImageProvider()); + engine_->addImageProvider(QLatin1String("tintedPixmap"), new TintedButtonImageProvider()); + + engine_->load(QUrl(QStringLiteral("qrc:/src/MainApplicationWindow.qml"))); +} + +MainApplication::~MainApplication() {} + +bool +MainApplication::applicationSetup() +{ +#ifdef Q_OS_LINUX + if (!getenv("QT_QPA_PLATFORMTHEME")) + setenv("QT_QPA_PLATFORMTHEME", "gtk3", true); +#endif + + /* + * Start debug console. + */ + for (auto string : QCoreApplication::arguments()) { + if (string == "-d" || string == "--debug") { + consoleDebug(); + } + } + + /* + * Remove old version files. + */ + Utils::removeOldVersions(); + + /* + * Load translations. + */ + loadTranslations(); + + /* + * Set font. + */ + setApplicationFont(); + +#if defined _MSC_VER && !COMPILE_ONLY + gnutls_global_init(); +#endif + + /* + * Init pixmap manipulator. + */ + GlobalInstances::setPixmapManipulator(std::make_unique()); + + /* + * Init lrc and its possible migration ui. + */ + initLrc(); + + /* + * Process input argument. + */ + bool startMinimized{false}; + processInputArgument(startMinimized); + + /* + * Create jami.net settings in Registry if it is not presented. + */ + QSettings settings("jami.net", "Jami"); + + /* + * Initialize qml components. + */ + qmlInitialization(); + + return true; +} diff --git a/src/mainapplication.h b/src/mainapplication.h new file mode 100644 index 00000000..ae51ac6a --- /dev/null +++ b/src/mainapplication.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +#include + +class MainApplication : public QApplication +{ + Q_OBJECT + +public: + explicit MainApplication(int &argc, char **argv); + ~MainApplication(); + + bool applicationSetup(); + void exitApp(); + + static void applicationInitialization(); + static QString getDebugFilePath(); + static char **parseInputArgument(int &argc, char *argv[], char *argToParse); + +protected: + void consoleDebug(); + void vsConsoleDebug(); + void fileDebug(QFile *debugFile); + +private: + void loadTranslations(); + void initLrc(); + void processInputArgument(bool &startMinimized); + void setApplicationFont(); + void qmlInitialization(); + + std::unique_ptr debugFile_; + QQmlApplicationEngine *engine_; +}; diff --git a/src/mainview/MainView.qml b/src/mainview/MainView.qml new file mode 100644 index 00000000..6bf2ff5d --- /dev/null +++ b/src/mainview/MainView.qml @@ -0,0 +1,506 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 + + +/* + * Import qml component files. + */ +import "components" +import "../settingsview" + +Window { + id: mainViewWindow + + property int minWidth: sidePanelViewStackPreferedWidth + property int minHeight: aboutPopUpDialog.contentHeight + + property int mainViewWindowPreferedWidth: 650 + property int mainViewWindowPreferedHeight: 600 + property int sidePanelViewStackPreferedWidth: 250 + property int welcomePageGroupPreferedWidth: 250 + property int aboutPopUpPreferedWidth: 250 + + + /* + * To calculate tab bar bottom border hidden rect left margin. + */ + property int tabBarLeftMargin: 8 + property int tabButtonShrinkSize: 8 + + signal noAccountIsAvailable + signal needToAddNewAccount + signal closeApp + + function newAccountAdded(index) { + mainViewWindowSidePanel.refreshAccountComboBox(index) + } + + function recursionStackViewItemMove(stackOne, stackTwo) { + + + /* + * Move all items (expect the bottom item) to stacktwo by the same order in stackone. + */ + if (stackOne.depth === 1) { + return + } + + var tempItem = stackOne.pop(StackView.Immediate) + recursionStackViewItemMove(stackOne, stackTwo) + stackTwo.push(tempItem, StackView.Immediate) + } + + title: "Jami" + visible: true + width: mainViewWindowPreferedWidth + height: mainViewWindowPreferedHeight + minimumWidth: minWidth + minimumHeight: minHeight + + Connections { + target: CallAdapter + + function onShowCallStack(accountId, convUid, forceReset) { + if (forceReset) { + callStackView.responsibleAccountId = accountId + callStackView.responsibleConvUid = convUid + } + + + /* + * Check if it is coming from the current responsible call, + * and push views onto the correct stackview + */ + if (callStackView.responsibleAccountId === accountId + && callStackView.responsibleConvUid === convUid) { + if (welcomeViewStack.visible) { + welcomeViewStack.pop(null, StackView.Immediate) + welcomeViewStack.push(callStackView, StackView.Immediate) + } else { + sidePanelViewStack.pop(null, StackView.Immediate) + sidePanelViewStack.push(callStackView, StackView.Immediate) + } + } + } + + function onCloseCallStack(accountId, convUid) { + + + /* + * Check if call stack view is on any of the stackview. + */ + if (callStackView.responsibleAccountId === accountId + && callStackView.responsibleConvUid === convUid) { + if (welcomeViewStack.find(function (item, index) { + return item.objectName === "callStackViewObject" + }) || sidePanelViewStack.find(function (item, index) { + return item.objectName === "callStackViewObject" + })) { + callStackView.needToCloseInCallConversationAndPotentialWindow() + if (welcomeViewStack.visible) { + welcomeViewStack.pop(null, StackView.Immediate) + welcomeViewStack.push(communicationPageMessageWebView, + StackView.Immediate) + } else { + sidePanelViewStack.pop(null, StackView.Immediate) + sidePanelViewStack.push( + communicationPageMessageWebView, + StackView.Immediate) + } + } + } + } + + function onIncomingCallNeedToSetupMainView(accountId, convUid) { + + + /* + * Set up the call stack view that is needed by call overlay. + */ + welcomeViewStack.pop(null, StackView.Immediate) + sidePanelViewStack.pop(null, StackView.Immediate) + + var index = ClientWrapper.utilsAdaptor.getCurrAccList().indexOf(accountId) + var name = ClientWrapper.utilsAdaptor.getBestName(accountId, convUid) + var id = ClientWrapper.utilsAdaptor.getBestId(accountId, convUid) + + communicationPageMessageWebView.headerUserAliasLabelText = name + communicationPageMessageWebView.headerUserUserNameLabelText = (name !== id) ? id : "" + + callStackView.needToCloseInCallConversationAndPotentialWindow() + callStackView.setCorrspondingMessageWebView( + communicationPageMessageWebView) + + callStackView.responsibleAccountId = accountId + callStackView.responsibleConvUid = convUid + callStackView.updateCorrspondingUI() + + mainViewWindowSidePanel.needToChangeToAccount(accountId, index) + ConversationsAdapter.selectConversation(accountId, convUid) + + MessagesAdapter.setupChatView(convUid) + } + } + + StackLayout { + id: mainViewStackLayout + + anchors.fill: parent + + currentIndex: 0 + + SplitView { + id: splitView + + Layout.fillWidth: true + Layout.fillHeight: true + + width: mainViewWindow.width + height: mainViewWindow.height + + handle: Rectangle { + implicitWidth: JamiTheme.splitViewHandlePreferedWidth + implicitHeight: splitView.height + color: SplitHandle.pressed ? JamiTheme.pressColor : (SplitHandle.hovered ? JamiTheme.hoverColor : JamiTheme.tabbarBorderColor) + } + + StackView { + id: sidePanelViewStack + + property int maximumWidth: sidePanelViewStackPreferedWidth + 100 + + initialItem: mainViewWindowSidePanel + + SplitView.minimumWidth: sidePanelViewStackPreferedWidth + SplitView.maximumWidth: maximumWidth + SplitView.fillHeight: true + + clip: true + } + + StackView { + id: welcomeViewStack + + initialItem: welcomePage + + SplitView.maximumWidth: splitView.width - sidePanelViewStack.width + SplitView.fillHeight: true + + clip: true + } + } + + SettingsView { + id: settingsView + + Layout.fillWidth: true + Layout.fillHeight: true + + onSettingsViewWindowNeedToShowMainViewWindow: { + mainViewWindowSidePanel.refreshAccountComboBox( + accountDeleted ? 0 : -1) + mainViewStackLayout.currentIndex = 0 + } + + onSettingsViewWindowNeedToShowNewWizardWindow: { + mainViewWindow.noAccountIsAvailable() + } + } + } + + AccountListModel { + id: accountListModel + } + + SidePanel { + id: mainViewWindowSidePanel + + onSettingBtnClicked_AccountComboBox: { + mainViewStackLayout.currentIndex = 1 + } + + onConversationSmartListNeedToAccessMessageWebView: { + + communicationPageMessageWebView.headerUserAliasLabelText = currentUserAlias + communicationPageMessageWebView.headerUserUserNameLabelText = currentUserDisplayName + + callStackView.needToCloseInCallConversationAndPotentialWindow() + callStackView.responsibleAccountId = ClientWrapper.utilsAdaptor.getCurrAccId() + callStackView.responsibleConvUid = currentUID + callStackView.updateCorrspondingUI() + + if (callStackViewShouldShow) { + if (callStateStr == "Talking" || callStateStr == "Hold") { + ClientWrapper.utilsAdaptor.setCurrentCall( + ClientWrapper.utilsAdaptor.getCurrAccId(), + currentUID) + if (isAudioOnly) + callStackView.showAudioCallPage() + else + callStackView.showVideoCallPage( + ClientWrapper.utilsAdaptor.getCallId( + callStackView.responsibleAccountId, + callStackView.responsibleConvUid)) + } else { + callStackView.showOutgoingCallPage(callStateStr) + } + } + + + /* + * Set up chatview. + */ + MessagesAdapter.setupChatView(currentUID) + callStackView.setCorrspondingMessageWebView( + communicationPageMessageWebView) + + if (welcomeViewStack.find(function (item, index) { + return item.objectName === "communicationPageMessageWebView" + }) || sidePanelViewStack.find(function (item, index) { + return item.objectName === "communicationPageMessageWebView" + })) { + if (!callStackViewShouldShow) + return + } + + + /* + * Push messageWebView or callStackView onto the correct stackview + */ + welcomeViewStack.pop(null, StackView.Immediate) + sidePanelViewStack.pop(null, StackView.Immediate) + + if (sidePanelViewStack.visible && welcomeViewStack.visible) { + if (callStackViewShouldShow) { + welcomeViewStack.push(callStackView) + } else { + welcomeViewStack.push(communicationPageMessageWebView) + } + } else if (sidePanelViewStack.visible + && !welcomeViewStack.visible) { + if (callStackViewShouldShow) { + sidePanelViewStack.push(callStackView) + } else { + sidePanelViewStack.push(communicationPageMessageWebView) + } + } else if (!sidePanelViewStack.visible + && !welcomeViewStack.visible) { + if (callStackViewShouldShow) { + sidePanelViewStack.push(callStackView) + } else { + sidePanelViewStack.push(communicationPageMessageWebView) + } + } + } + + onAccountComboBoxNeedToShowWelcomePage: { + + + /* + * If the item argument is specified, all items down to (but not including) item will be popped. + */ + welcomeViewStack.pop(welcomePage) + welcomePage.currentAccountIndex = index + qrDialog.currentAccountIndex = index + } + + onConversationSmartListViewNeedToShowWelcomePage: { + welcomeViewStack.pop(welcomePage) + welcomePage.currentAccountIndex = 0 + qrDialog.currentAccountIndex = 0 + } + + onAccountSignalsReconnect: { + MessagesAdapter.accountChangedSetUp(accountId) + } + + onNeedToUpdateConversationForAddedContact: { + MessagesAdapter.updateConversationForAddedContact() + mainViewWindowSidePanel.clearContactSearchBar() + mainViewWindowSidePanel.forceReselectConversationSmartListCurrentIndex() + } + + onNeedToAddNewAccount: { + mainViewWindow.needToAddNewAccount() + } + } + + CallStackView { + id: callStackView + + visible: false + + objectName: "callStackViewObject" + + onCallPageBackButtonIsClicked: { + mainViewWindowSidePanel.deselectConversationSmartList() + if (welcomeViewStack.visible) + welcomeViewStack.pop(welcomePage) + else if (sidePanelViewStack.visible) + sidePanelViewStack.pop(mainViewWindowSidePanel) + } + + onOutgoingCallPageBackButtonIsClicked: { + mainViewWindowSidePanel.deselectConversationSmartList() + if (welcomeViewStack.visible) + welcomeViewStack.pop(welcomePage) + else if (sidePanelViewStack.visible) + sidePanelViewStack.pop(mainViewWindowSidePanel) + } + } + + WelcomePage { + id: welcomePage + visible: false + } + + MessageWebView { + id: communicationPageMessageWebView + + objectName: "communicationPageMessageWebView" + + signal toSendMessageContentSaved(string arg) + signal toMessagesCleared + signal toMessagesLoaded + + visible: false + + Connections { + target: MessagesAdapter + + function onNeedToUpdateSmartList() { + mainViewWindowSidePanel.forceUpdateConversationSmartListView() + } + } + + onNeedToGoBackToWelcomeView: { + mainViewWindowSidePanel.deselectConversationSmartList() + if (communicationPageMessageWebView.visible + && !welcomeViewStack.visible) { + sidePanelViewStack.pop() + } else if (communicationPageMessageWebView.visible + && welcomeViewStack.visible) { + welcomeViewStack.pop() + } + } + + Component.onCompleted: { + + + /* + * Set qml MessageWebView object pointer to c++. + */ + MessagesAdapter.setQmlObject(this) + } + } + + onWidthChanged: { + + + /* + * Hide unnecessary stackview when width is changed. + */ + if (mainViewWindow.width < sidePanelViewStackPreferedWidth + + welcomePageGroupPreferedWidth - 5 + && welcomeViewStack.visible) { + welcomeViewStack.visible = false + + + /* + * The find callback function is called for each item in the stack. + */ + var inWelcomeViewStack = welcomeViewStack.find( + function (item, index) { + return index > 0 + }) + if (inWelcomeViewStack) { + recursionStackViewItemMove(welcomeViewStack, sidePanelViewStack) + } + + sidePanelViewStack.maximumWidth = splitView.width + + mainViewWindow.update() + } else if (mainViewWindow.width >= sidePanelViewStackPreferedWidth + + welcomePageGroupPreferedWidth + 5 + && !welcomeViewStack.visible) { + welcomeViewStack.visible = true + + var inSidePanelViewStack = sidePanelViewStack.find( + function (item, index) { + return index > 0 + }) + if (inSidePanelViewStack) { + recursionStackViewItemMove(sidePanelViewStack, welcomeViewStack) + } + + sidePanelViewStack.maximumWidth = sidePanelViewStackPreferedWidth + 100 + + mainViewWindow.update() + } + } + + AboutPopUp { + id: aboutPopUpDialog + + x: Math.round((mainViewWindow.width - width) / 2) + y: Math.round((mainViewWindow.height - height) / 2) + width: Math.max(mainViewWindow.width / 2, aboutPopUpPreferedWidth) + height: aboutPopUpDialog.contentHeight + } + + WelcomePageQrDialog { + id: qrDialog + + x: Math.round((mainViewWindow.width - width) / 2) + y: Math.round((mainViewWindow.height - height) / 2) + width: qrDialog.contentHeight + height: qrDialog.contentHeight + } + + RecordBox{ + id: recordBox + visible: false + } + + UserProfile { + id: userProfile + + x: Math.round((mainViewWindow.width - width) / 2) + y: Math.round((mainViewWindow.height - height) / 2) + width: Math.max(mainViewWindow.width / 2, aboutPopUpPreferedWidth) + height: userProfile.contentHeight + } + + Component.onCompleted: { + CallAdapter.initQmlObject() + } + + onClosing: { + close.accepted = false + mainViewWindow.hide() + mainViewWindow.closeApp() + } +} diff --git a/src/mainview/components/AboutPopUp.qml b/src/mainview/components/AboutPopUp.qml new file mode 100644 index 00000000..f13c46a0 --- /dev/null +++ b/src/mainview/components/AboutPopUp.qml @@ -0,0 +1,308 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Dialog { + id: aboutPopUp + + + /* + * When dialog is opened, trigger mainViewWindow overlay which is defined in overlay.model (model : true is necessary). + */ + modal: true + + + /* + * Content height + margin. + */ + contentHeight: aboutPopUpContentRectColumnLayout.height + 5 * 7 + + ProjectCreditsScrollView { + id: projectCreditsScrollView + + visible: false + } + + ChangeLogScrollView { + id: changeLogScrollView + + visible: false + } + + Rectangle { + id: aboutPopUpContentRect + + anchors.fill: parent + + ColumnLayout { + id: aboutPopUpContentRectColumnLayout + + Image { + id: aboutPopUPJamiLogoImage + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: aboutPopUpContentRect.width + Layout.preferredHeight: 100 + + fillMode: Image.PreserveAspectFit + source: "qrc:/images/logo-jami-standard-coul.png" + mipmap: true + } + + Label { + id: jamiVersionText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: aboutPopUpContentRect.width + Layout.preferredHeight: textMetricsjamiVersionText.boundingRect.height + + font.pointSize: JamiTheme.textFontSize - 2 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiVersionText.text + + TextMetrics { + id: textMetricsjamiVersionText + font: jamiVersionText.font + text: qsTr("version") + ": " + ClientWrapper.utilsAdaptor.getVersionStr() + } + } + + Label { + id: jamiSlogansText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: aboutPopUpContentRect.width + Layout.preferredHeight: textMetricsjamiSlogansText.boundingRect.height + Layout.topMargin: 5 + + wrapMode: Text.WordWrap + font.pointSize: JamiTheme.textFontSize - 2 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiSlogansText.text + + TextMetrics { + id: textMetricsjamiSlogansText + font: jamiSlogansText.font + text: qsTr("Free as in Freedom") + } + } + + Label { + id: jamiDeclarationText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: aboutPopUpContentRect.width + Layout.preferredHeight: 40 + Layout.topMargin: 5 + + wrapMode: Text.WordWrap + font.pointSize: JamiTheme.textFontSize - 2 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + + /* + * TextMetrics does not work for multi-line. + */ + text: qsTr("The Microsoft Windows client for Jami.\nJami is a secured and distributed communciation software.") + } + + Label { + id: jamiDeclarationHyperText + + Layout.alignment: Qt.AlignCenter + + + /* + * Strangely, hoveredLink works badly when width grows too large + */ + Layout.preferredWidth: 50 + Layout.preferredHeight: textMetricsjamiDeclarationHyperText.boundingRect.height + Layout.topMargin: 5 + Layout.bottomMargin: 5 + + font.pointSize: JamiTheme.textFontSize - 2 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiDeclarationHyperText.text + onLinkActivated: Qt.openUrlExternally(link) + + TextMetrics { + id: textMetricsjamiDeclarationHyperText + font: jamiDeclarationHyperText.font + text: '
jami.net' + } + + MouseArea { + anchors.fill: parent + + + /* + * We don't want to eat clicks on the Text. + */ + acceptedButtons: Qt.NoButton + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + } + + Label { + id: jamiDeclarationYearText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: aboutPopUpContentRect.width + Layout.preferredHeight: textMetricsjamiDeclarationYearText.boundingRect.height + Layout.bottomMargin: 5 + + font.pointSize: JamiTheme.textFontSize - 2 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiDeclarationYearText.text + + TextMetrics { + id: textMetricsjamiDeclarationYearText + font: jamiDeclarationYearText.font + text: "© 2015-2020 Savoir-faire Linux" + } + } + + Label { + id: jamiNoneWarrantyHyperText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: Math.min(300, + aboutPopUpContentRect.width) + Layout.preferredHeight: textMetricsjamiNoneWarrantyHyperText.boundingRect.height * 2 + Layout.bottomMargin: 10 + + wrapMode: Text.WordWrap + font.pointSize: JamiTheme.textFontSize - 3 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiNoneWarrantyHyperText.text + onLinkActivated: Qt.openUrlExternally(link) + + TextMetrics { + id: textMetricsjamiNoneWarrantyHyperText + font: jamiDeclarationHyperText.font + text: 'This program comes with absolutely no warranty.See the GNU General Public License, version 3 or later for details.' + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor + } + } + + Rectangle { + id: buttonGroupChangeLogAndCredits + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: aboutPopUpContentRect.width + Layout.preferredHeight: 30 + Layout.bottomMargin: 10 + + RowLayout { + id: buttonGroupChangeLogAndCreditsRowLayout + + anchors.fill: parent + + HoverableButton { + id: changeLogButton + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: textMetricschangeLogButton.boundingRect.width + 20 + Layout.preferredHeight: 30 + + radius: 10 + fontPointSize: JamiTheme.textFontSize - 2 + text: textMetricschangeLogButton.text + + TextMetrics { + id: textMetricschangeLogButton + font: changeLogButton.font + text: qsTr("Change Log") + } + + onClicked: { + if (changeLogOrCreditsStack.depth > 1) { + changeLogOrCreditsStack.pop() + } + } + } + + HoverableButton { + id: creditsButton + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: changeLogButton.width + Layout.preferredHeight: 30 + + radius: 10 + fontPointSize: JamiTheme.textFontSize - 2 + text: qsTr("Credits") + + onClicked: { + if (changeLogOrCreditsStack.depth == 1) { + changeLogOrCreditsStack.push( + projectCreditsScrollView) + } + } + } + } + } + + StackView { + id: changeLogOrCreditsStack + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: aboutPopUpContentRect.width + Layout.preferredHeight: 150 + Layout.bottomMargin: 5 + + initialItem: changeLogScrollView + + clip: true + } + } + } + + background: Rectangle { + border.width: 0 + radius: 10 + } +} diff --git a/src/mainview/components/AccountComboBox.qml b/src/mainview/components/AccountComboBox.qml new file mode 100644 index 00000000..23225f78 --- /dev/null +++ b/src/mainview/components/AccountComboBox.qml @@ -0,0 +1,303 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +ComboBox { + id: accountComboBox + + signal accountChanged(int index) + signal needToBackToWelcomePage(int index) + signal needToUpdateSmartList(string accountId) + signal newAccountButtonClicked + signal settingBtnClicked + + currentIndex: 0 + + function backToWelcomePage(index) { + accountComboBox.needToBackToWelcomePage(index) + } + + function updateSmartList(accountId) { + accountComboBox.needToUpdateSmartList(accountId) + } + + + /* + * Refresh every item in accountListModel. + */ + function updateAccountListModel() { + accountListModel.dataChanged(accountListModel.index(0, 0), + accountListModel.index( + accountListModel.rowCount() - 1, 0)) + } + + + /* + * Reset accountListModel. + */ + function resetAccountListModel() { + accountListModel.reset() + } + + Image { + id: userImageRoot + + anchors.left: accountComboBox.left + anchors.leftMargin: 5 + anchors.verticalCenter: accountComboBox.verticalCenter + + width: accountComboBox.height - 10 + height: accountComboBox.height - 10 + + fillMode: Image.PreserveAspectFit + + + /* + * Base 64 format + */ + source: { + if (currentIndex !== -1) + return "data:image/png;base64," + accountListModel.data( + accountListModel.index( + accountComboBox.currentIndex, 0), 259) + else + return source + } + mipmap: true + + Rectangle { + id: presenseRect + + anchors.right: userImageRoot.right + anchors.rightMargin: 1 + anchors.bottom: userImageRoot.bottom + anchors.bottomMargin: 2 + + width: 14 + height: 14 + + + /* + * Visible when account is registered, enum REGISTERED == 5. + */ + visible: { + if (currentIndex !== -1) + return accountListModel.data( + accountListModel.index( + accountComboBox.currentIndex, 0), 261) === 5 + else + return visible + } + + Rectangle { + id: presenseCycle + + anchors.centerIn: presenseRect + + width: 10 + height: 10 + + radius: 30 + color: JamiTheme.presenceGreen + } + + radius: 30 + color: "white" + } + } + + Text { + id: textUserAliasRoot + + anchors.left: userImageRoot.right + anchors.leftMargin: 10 + anchors.top: rootItemBackground.top + anchors.topMargin: 5 + + text: textMetricsUserAliasRoot.elidedText + font.pointSize: JamiTheme.textFontSize + } + + Text { + id: textUsernameRoot + + anchors.left: userImageRoot.right + anchors.leftMargin: 10 + anchors.top: textUserAliasRoot.bottom + anchors.topMargin: 5 + + text: textMetricsUsernameRoot.elidedText + font.pointSize: JamiTheme.textFontSize + color: JamiTheme.faddedFontColor + } + + TextMetrics { + id: textMetricsUserAliasRoot + + font: textUserAliasRoot.font + elide: Text.ElideMiddle + elideWidth: accountComboBox.width - userImageRoot.width - settingsButton.width - 25 + + + /* + * Role::Alias + */ + text: { + if (currentIndex !== -1) + return accountListModel.data(accountListModel.index( + accountComboBox.currentIndex, + 0), 257) + else + return text + } + } + + TextMetrics { + id: textMetricsUsernameRoot + + font: textUsernameRoot.font + elide: Text.ElideMiddle + elideWidth: accountComboBox.width - userImageRoot.width - settingsButton.width - 25 + + + /* + * Role::Username + */ + text: { + if (currentIndex !== -1) + return accountListModel.data(accountListModel.index( + accountComboBox.currentIndex, + 0), 258) + else + return text + } + } + + HoverableButton { + id: settingsButton + + anchors.right: accountComboBox.right + anchors.rightMargin: 10 + anchors.verticalCenter: accountComboBox.verticalCenter + + buttonImageHeight: height - 8 + buttonImageWidth: width - 8 + source: "qrc:/images/icons/round-settings-24px.svg" + radius: height / 2 + width: 25 + height: 25 + + onClicked: { + settingBtnClicked() + } + } + + background: Rectangle { + id: rootItemBackground + + implicitWidth: accountComboBox.width + implicitHeight: accountComboBox.height + + border.width: 0 + } + + MouseArea { + id: comboBoxRootMouseArea + + anchors.fill: parent + + hoverEnabled: true + propagateComposedEvents: true + + onPressed: { + if (isMouseOnSettingsButton(mouse)) { + settingsButton.backgroundColor = JamiTheme.pressColor + settingsButton.clicked() + } else + rootItemBackground.color = JamiTheme.pressColor + } + onReleased: { + if (isMouseOnSettingsButton(mouse)) { + settingsButton.backgroundColor = JamiTheme.releaseColor + } else { + rootItemBackground.color = JamiTheme.releaseColor + if (comboBoxPopup.opened) { + accountComboBox.popup.close() + } else { + accountComboBox.popup.open() + } + } + } + onEntered: { + rootItemBackground.color = JamiTheme.hoverColor + } + onExited: { + rootItemBackground.color = "white" + } + onMouseXChanged: { + + + /* + * Manually making settings button hover. + */ + if (isMouseOnSettingsButton(mouse)) { + settingsButton.backgroundColor = JamiTheme.hoverColor + rootItemBackground.color = "white" + } else { + settingsButton.backgroundColor = "white" + rootItemBackground.color = JamiTheme.hoverColor + } + } + function isMouseOnSettingsButton(mouse) { + var mousePos = mapToItem(comboBoxRootMouseArea, mouse.x, mouse.y) + var settingsButtonPos = mapToItem(comboBoxRootMouseArea, + settingsButton.x, + settingsButton.y) + if ((mousePos.x >= settingsButtonPos.x + && mousePos.x <= settingsButtonPos.x + settingsButton.width) + && (mousePos.y >= settingsButtonPos.y + && mousePos.y <= settingsButtonPos.y + settingsButton.height)) + return true + return false + } + } + + indicator: null + + + /* + * Overwrite the combo box pop up to add footer (for add accounts). + */ + popup: AccountComboBoxPopup { + id: comboBoxPopup + + onAccountNeedToChange: { + accountComboBox.accountChanged(index) + } + + onNewAccountButtonClicked: { + accountComboBox.newAccountButtonClicked() + } + } +} diff --git a/src/mainview/components/AccountComboBoxPopup.qml b/src/mainview/components/AccountComboBoxPopup.qml new file mode 100644 index 00000000..42a17fbc --- /dev/null +++ b/src/mainview/components/AccountComboBoxPopup.qml @@ -0,0 +1,180 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Popup { + id: comboBoxPopup + + property bool toogleUpdatePopupHeight: false + + signal accountNeedToChange(int index) + signal newAccountButtonClicked + + y: accountComboBox.height - 1 + implicitWidth: accountComboBox.width - 1 + + + /* + * Hack - limite the accounts that can be shown. + */ + implicitHeight: { + comboBoxPopup.visible + return Math.min(accountComboBox.height * Math.min( + 5, accountListModel.rowCount() + 1), + sidePanelRect.height) + } + padding: 0 + + contentItem: ListView { + id: comboBoxPopupListView + + + /* + * In list view, index is an interger. + */ + clip: true + model: accountListModel + implicitHeight: contentHeight + delegate: ItemDelegate { + + Image { + id: userImage + + anchors.left: parent.left + anchors.leftMargin: 5 + anchors.verticalCenter: parent.verticalCenter + + width: 30 + height: parent.height + + fillMode: Image.PreserveAspectFit + mipmap: true + + + /* + * Role::Picture + */ + source: "data:image/png;base64," + accountListModel.data( + accountListModel.index(index, 0), 259) + } + + Text { + id: textUserAliasPopup + + anchors.left: userImage.right + anchors.leftMargin: 10 + anchors.top: itemCoboBackground.top + anchors.topMargin: 5 + + text: textMetricsUserAliasPopup.elidedText + font.pointSize: JamiTheme.textFontSize + } + + Text { + id: textUsernamePopup + + anchors.left: userImage.right + anchors.leftMargin: 10 + anchors.top: textUserAliasPopup.bottom + anchors.topMargin: 5 + + text: textMetricsUsernamePopup.elidedText + font.pointSize: JamiTheme.textFontSize + color: JamiTheme.faddedFontColor + } + + TextMetrics { + id: textMetricsUserAliasPopup + elide: Text.ElideMiddle + elideWidth: accountComboBox.width - userImage.width - settingsButton.width - 30 + text: Alias + } + + TextMetrics { + id: textMetricsUsernamePopup + elide: Text.ElideMiddle + elideWidth: accountComboBox.width - userImage.width - settingsButton.width - 30 + text: Username + } + + background: Rectangle { + id: itemCoboBackground + + implicitWidth: accountComboBox.width + implicitHeight: accountComboBox.height + border.width: 0 + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onPressed: { + itemCoboBackground.color = JamiTheme.pressColor + } + onReleased: { + itemCoboBackground.color = JamiTheme.releaseColor + currentIndex = index + comboBoxPopup.close() + comboBoxPopup.accountNeedToChange(index) + } + onEntered: { + itemCoboBackground.color = JamiTheme.hoverColor + } + onExited: { + itemCoboBackground.color = "white" + } + } + } + + footer: HoverableButton { + id: comboBoxFooterItem + + implicitWidth: accountComboBox.width + implicitHeight: accountComboBox.height + + text: qsTr("Add Account") + "+" + backgroundColor: "white" + onExitColor: "white" + + onClicked: { + comboBoxPopup.close() + comboBoxPopup.newAccountButtonClicked() + } + } + + ScrollIndicator.vertical: ScrollIndicator {} + } + background: Rectangle { + id: accountComboBoxPopup + + CustomBorder { + commonBorder: false + lBorderwidth: 0 + rBorderwidth: 1 + tBorderwidth: 0 + bBorderwidth: 1 + borderColor: JamiTheme.tabbarBorderColor + } + } +} diff --git a/src/mainview/components/AudioCallPage.qml b/src/mainview/components/AudioCallPage.qml new file mode 100644 index 00000000..1292f30a --- /dev/null +++ b/src/mainview/components/AudioCallPage.qml @@ -0,0 +1,248 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Rectangle { + id: audioCallPageRect + + property string contactImgSource: "" + property string bestName: "Best Name" + property string bestId: "Best Id" + + property var corrspondingMessageWebView: null + + signal audioCallPageBackButtonIsClicked + + function updateUI(accountId, convUid) { + contactImgSource = "data:image/png;base64," + ClientWrapper.utilsAdaptor.getContactImageString( + accountId, convUid) + bestName = ClientWrapper.utilsAdaptor.getBestName(accountId, convUid) + + var id = ClientWrapper.utilsAdaptor.getBestId(accountId, convUid) + bestId = (bestName !== id) ? id : "" + } + + function setAudioCallPageCorrspondingMessageWebView(webViewId) { + corrspondingMessageWebView = webViewId + corrspondingMessageWebView.needToHideConversationInCall.disconnect( + closeInCallConversation) + corrspondingMessageWebView.needToHideConversationInCall.connect( + closeInCallConversation) + } + + function closeInCallConversation() { + if (inAudioCallMessageWebViewStack.visible) { + corrspondingMessageWebView.resetMessagingHeaderBackButtonSource( + true) + corrspondingMessageWebView.setMessagingHeaderButtonsVisible(true) + inAudioCallMessageWebViewStack.visible = false + inAudioCallMessageWebViewStack.clear() + } + } + + function closeContextMenuAndRelatedWindows() { + audioCallOverlay.closePotentialContactPicker() + } + + anchors.fill: parent + + SplitView { + id: mainColumnLayout + + anchors.fill: parent + + orientation: Qt.Vertical + + handle: Rectangle { + implicitWidth: audioCallPageRect.width + implicitHeight: JamiTheme.splitViewHandlePreferedWidth + color: SplitHandle.pressed ? JamiTheme.pressColor : (SplitHandle.hovered ? JamiTheme.hoverColor : JamiTheme.tabbarBorderColor) + } + + Rectangle { + id: audioCallPageMainRect + + SplitView.preferredHeight: (audioCallPageRect.height / 3) * 2 + SplitView.minimumHeight: audioCallPageRect.height / 2 + 20 + SplitView.fillWidth: true + + CallOverlay { + id: audioCallOverlay + + anchors.fill: parent + + Connections { + target: CallAdapter + + function onUpdateTimeText(time) { + audioCallOverlay.timeText = time + } + + function onUpdateOverlay(isPaused, isAudioOnly, isAudioMuted, isVideoMuted, isRecording, isSIP, isConferenceCall, bestName) { + audioCallOverlay.showOnHoldImage(isPaused) + audioCallPageRectCentralRect.visible = !isPaused + audioCallOverlay.updateButtonStatus(isPaused, + isAudioOnly, + isAudioMuted, + isVideoMuted, + isRecording, isSIP, + isConferenceCall) + audioCallOverlay.bestName = bestName + } + + function onShowOnHoldLabel(isPaused) { + audioCallOverlay.showOnHoldImage(isPaused) + audioCallPageRectCentralRect.visible = !isPaused + } + } + + onBackButtonIsClicked: { + if (inAudioCallMessageWebViewStack.visible) { + corrspondingMessageWebView.resetMessagingHeaderBackButtonSource( + true) + corrspondingMessageWebView.setMessagingHeaderButtonsVisible( + true) + inAudioCallMessageWebViewStack.visible = false + inAudioCallMessageWebViewStack.clear() + } + audioCallPageRect.audioCallPageBackButtonIsClicked() + } + + onOverlayChatButtonClicked: { + if (inAudioCallMessageWebViewStack.visible) { + corrspondingMessageWebView.resetMessagingHeaderBackButtonSource( + true) + corrspondingMessageWebView.setMessagingHeaderButtonsVisible( + true) + inAudioCallMessageWebViewStack.visible = false + inAudioCallMessageWebViewStack.clear() + } else { + corrspondingMessageWebView.resetMessagingHeaderBackButtonSource( + false) + corrspondingMessageWebView.setMessagingHeaderButtonsVisible( + false) + inAudioCallMessageWebViewStack.visible = true + inAudioCallMessageWebViewStack.push( + corrspondingMessageWebView) + } + } + } + + Rectangle { + id: audioCallPageRectCentralRect + + anchors.centerIn: parent + + width: audioCallPageRect.width + height: audioCallPageRegisteredNameText.height + + audioCallPageIdText.height + contactImage.height + 10 + + ColumnLayout { + id: audioCallPageRectColumnLayout + + Image { + id: contactImage + + Layout.alignment: Qt.AlignCenter + + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + + fillMode: Image.PreserveAspectFit + source: contactImgSource + asynchronous: true + } + + Text { + id: audioCallPageRegisteredNameText + + Layout.alignment: Qt.AlignCenter + + Layout.preferredWidth: audioCallPageRectCentralRect.width + Layout.preferredHeight: 50 + + font.pointSize: JamiTheme.textFontSize + 3 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsAudioCallPageRegisteredNameText.elidedText + color: "white" + + TextMetrics { + id: textMetricsAudioCallPageRegisteredNameText + font: audioCallPageRegisteredNameText.font + text: bestName + elideWidth: audioCallPageRectCentralRect.width - 50 + elide: Qt.ElideMiddle + } + } + + Text { + id: audioCallPageIdText + + Layout.alignment: Qt.AlignCenter + + Layout.preferredWidth: audioCallPageRectCentralRect.width + Layout.preferredHeight: 30 + + font.pointSize: JamiTheme.textFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsAudioCallPageIdText.elidedText + color: "white" + + TextMetrics { + id: textMetricsAudioCallPageIdText + font: audioCallPageIdText.font + text: bestId + elideWidth: audioCallPageRectCentralRect.width - 50 + elide: Qt.ElideMiddle + } + } + } + + color: "transparent" + } + + color: "transparent" + } + + StackView { + id: inAudioCallMessageWebViewStack + + SplitView.preferredHeight: audioCallPageRect.height / 3 + SplitView.fillWidth: true + + visible: false + + clip: true + } + } + + color: "black" +} diff --git a/src/mainview/components/CallOverlay.qml b/src/mainview/components/CallOverlay.qml new file mode 100644 index 00000000..dfd599af --- /dev/null +++ b/src/mainview/components/CallOverlay.qml @@ -0,0 +1,381 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import QtQml 2.14 +import net.jami.Models 1.0 + +import "../js/contactpickercreation.js" as ContactPickerCreation + +import "../../commoncomponents" + +Rectangle { + id: callOverlayRect + + property string bestName: "Best Name" + property string timeText: "00:00" + + signal backButtonIsClicked + signal overlayChatButtonClicked + + function updateButtonStatus(isPaused, isAudioOnly, isAudioMuted, isVideoMuted, isRecording, isSIP, isConferenceCall) { + callOverlayButtonGroup.setButtonStatus(isPaused, isAudioOnly, + isAudioMuted, isVideoMuted, + isRecording, isSIP, + isConferenceCall) + } + + function showOnHoldImage(visible) { + onHoldImage.visible = visible + } + + function closePotentialContactPicker() { + ContactPickerCreation.closeContactPicker() + } + + function setBackTintedButtonVisible(visible) { + backTintedButton.visible = visible + } + + anchors.fill: parent + + + /* + * Timer to decide when overlay fade out. + */ + Timer { + id: callOverlayTimer + interval: 5000 + onTriggered: { + if (overlayUpperPartRect.state !== 'freezed') { + overlayUpperPartRect.state = 'freezed' + } + if (callOverlayButtonGroup.state !== 'freezed') { + callOverlayButtonGroup.state = 'freezed' + } + } + } + + Rectangle { + id: overlayUpperPartRect + + anchors.top: callOverlayRect.top + + width: callOverlayRect.width + height: 50 + opacity: 0 + + RowLayout { + id: overlayUpperPartRectRowLayout + + anchors.fill: parent + + TintedButton { + id: backTintedButton + + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + Layout.leftMargin: 5 + Layout.preferredWidth: 30 + Layout.preferredHeight: 30 + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/ic_arrow_back_white_24dp.png" + selectedPixmapSource: "qrc:/images/icons/ic_arrow_back_white_24dp.png" + + onClicked: { + callOverlayRect.backButtonIsClicked() + } + + onButtonEntered: { + callOverlayRectMouseArea.entered() + } + } + + Text { + id: jamiBestNameText + + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + Layout.preferredWidth: overlayUpperPartRect.width / 3 + Layout.preferredHeight: 50 + + font.pointSize: JamiTheme.textFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiBestNameText.elidedText + color: "white" + + TextMetrics { + id: textMetricsjamiBestNameText + font: jamiBestNameText.font + text: bestName + elideWidth: overlayUpperPartRect.width / 3 + elide: Qt.ElideMiddle + } + } + + Text { + id: callTimerText + + Layout.alignment: Qt.AlignVCenter | Qt.AlignRight + Layout.preferredWidth: overlayUpperPartRect.width / 3 + Layout.preferredHeight: 50 + + font.pointSize: JamiTheme.textFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricscallTimerText.elidedText + color: "white" + + TextMetrics { + id: textMetricscallTimerText + font: callTimerText.font + text: timeText + elideWidth: overlayUpperPartRect.width / 3 + elide: Qt.ElideMiddle + } + } + } + + color: "transparent" + + + /* + * Rect states: "entered" state should make overlay fade in, + * "freezed" state should make overlay fade out. + * Combine with PropertyAnimation of opacity. + */ + states: [ + State { + name: "entered" + PropertyChanges { + target: overlayUpperPartRect + opacity: 1 + } + }, + State { + name: "freezed" + PropertyChanges { + target: overlayUpperPartRect + opacity: 0 + } + } + ] + + transitions: Transition { + PropertyAnimation { + target: overlayUpperPartRect + property: "opacity" + duration: 1000 + } + } + } + + Image { + id: onHoldImage + + anchors.verticalCenter: callOverlayRect.verticalCenter + anchors.horizontalCenter: callOverlayRect.horizontalCenter + + width: 200 + height: 200 + + visible: false + + fillMode: Image.PreserveAspectFit + source: "qrc:/images/icons/ic_pause_white_100px.png" + asynchronous: true + } + + CallOverlayButtonGroup { + id: callOverlayButtonGroup + + anchors.bottom: callOverlayRect.bottom + anchors.bottomMargin: 10 + anchors.horizontalCenter: callOverlayRect.horizontalCenter + + width: callOverlayRect.width / 3 * 2 + height: 60 + opacity: 0 + + onChatButtonClicked: { + callOverlayRect.overlayChatButtonClicked() + } + + onAddToConferenceButtonClicked: { + + + /* + * Create contact picker - conference. + */ + ContactPickerCreation.createContactPickerObjects( + ContactPicker.ContactPickerType.JAMICONFERENCE, + callOverlayRect) + ContactPickerCreation.calculateCurrentGeo( + callOverlayRect.width / 2, callOverlayRect.height / 2) + ContactPickerCreation.openContactPicker() + } + + onTransferCallButtonClicked: { + + + /* + * Create contact picker - sip transfer. + */ + ContactPickerCreation.createContactPickerObjects( + ContactPicker.ContactPickerType.SIPTRANSFER, + callOverlayRect) + ContactPickerCreation.calculateCurrentGeo( + callOverlayRect.width / 2, callOverlayRect.height / 2) + ContactPickerCreation.openContactPicker() + } + + onButtonEntered: { + callOverlayRectMouseArea.entered() + } + + states: [ + State { + name: "entered" + PropertyChanges { + target: callOverlayButtonGroup + opacity: 1 + } + }, + State { + name: "freezed" + PropertyChanges { + target: callOverlayButtonGroup + opacity: 0 + } + } + ] + + transitions: Transition { + PropertyAnimation { + target: callOverlayButtonGroup + property: "opacity" + duration: 1000 + } + } + } + + + /* + * MouseAreas to make sure that overlay states are correctly set. + */ + MouseArea { + id: callOverlayButtonGroupLeftSideMouseArea + + anchors.bottom: callOverlayRect.bottom + anchors.left: callOverlayRect.left + + width: callOverlayRect.width / 6 + height: 60 + + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + + onEntered: { + callOverlayRectMouseArea.entered() + } + + onMouseXChanged: { + callOverlayRectMouseArea.entered() + } + } + + MouseArea { + id: callOverlayButtonGroupRightSideMouseArea + + anchors.bottom: callOverlayRect.bottom + anchors.right: callOverlayRect.right + + width: callOverlayRect.width / 6 + height: 60 + + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + + onEntered: { + callOverlayRectMouseArea.entered() + } + + onMouseXChanged: { + callOverlayRectMouseArea.entered() + } + } + + MouseArea { + id: callOverlayRectMouseArea + + anchors.top: callOverlayRect.top + anchors.topMargin: 50 + + width: callOverlayRect.width + height: callOverlayRect.height - callOverlayButtonGroup.height - 50 + + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + + onEntered: { + if (overlayUpperPartRect.state !== 'entered') { + overlayUpperPartRect.state = 'entered' + } + if (callOverlayButtonGroup.state !== 'entered') { + callOverlayButtonGroup.state = 'entered' + } + callOverlayTimer.restart() + } + + onMouseXChanged: { + if (overlayUpperPartRect.state !== 'entered') { + overlayUpperPartRect.state = 'entered' + } + if (callOverlayButtonGroup.state !== 'entered') { + callOverlayButtonGroup.state = 'entered' + } + callOverlayTimer.restart() + } + } + + color: "transparent" + + onBestNameChanged: { + ContactAdapter.setCalleeDisplayName(bestName) + } + + onWidthChanged: { + ContactPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2, + callOverlayRect.height / 2) + } + + onHeightChanged: { + ContactPickerCreation.calculateCurrentGeo(callOverlayRect.width / 2, + callOverlayRect.height / 2) + } +} diff --git a/src/mainview/components/CallOverlayButtonGroup.qml b/src/mainview/components/CallOverlayButtonGroup.qml new file mode 100644 index 00000000..4309fa1b --- /dev/null +++ b/src/mainview/components/CallOverlayButtonGroup.qml @@ -0,0 +1,311 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import QtQml 2.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Rectangle { + id: callOverlayButtonGroupRect + + + /* + * ButtonCounts here is to make sure that flow layout margin is calculated correctly, + * since no other methods can make buttons at the layout center. + */ + property int buttonCounts: 9 + property int buttonPreferredSize: 30 + + signal buttonEntered + signal chatButtonClicked + signal addToConferenceButtonClicked + signal transferCallButtonClicked + + function setButtonStatus(isPaused, isAudioOnly, isAudioMuted, isVideoMuted, isRecording, isSIP, isConferenceCall) { + noVideoButton.visible = !isAudioOnly + addToConferenceButton.visible = !isSIP + transferCallButton.visible = isSIP + sipInputPanelButton.visible = isSIP + + noMicButton.setChecked(isAudioMuted) + noVideoButton.setChecked(isVideoMuted) + recButton.setChecked(isRecording) + holdButton.setChecked(isPaused) + + holdButton.visible = !isConferenceCall + } + + function calculateFlowMargin() { + return (callOverlayButtonGroupRect.width - buttonCounts * buttonPreferredSize + - callOverlayButtonGroupRectFlow.spacing * (buttonCounts - 1)) / 2 + } + + Flow { + id: callOverlayButtonGroupRectFlow + + anchors.fill: parent + + + /* + * Minus 1 is to make sure that button will not flick when doing flow layout. + */ + anchors.leftMargin: calculateFlowMargin( + ) < 0 ? 0 : calculateFlowMargin() - 1 + anchors.rightMargin: calculateFlowMargin( + ) < 0 ? 0 : calculateFlowMargin() - 1 + + spacing: 10 + + TintedButton { + id: hangUpButton + + width: buttonPreferredSize + height: buttonPreferredSize + + tintColor: JamiTheme.hangUpButtonTintedRed + normalPixmapSource: "qrc:/images/icons/ic_close_white_24dp.png" + selectedPixmapSource: "qrc:/images/icons/ic_close_white_24dp.png" + + onButtonEntered: { + callOverlayButtonGroupRect.buttonEntered() + } + + onClicked: { + CallAdapter.hangUpThisCall() + } + + onVisibleChanged: { + if (this.visible) + buttonCounts++ + else + buttonCounts-- + } + } + + TintedButton { + id: holdButton + + width: buttonPreferredSize + height: buttonPreferredSize + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/ic_pause_white_24dp.png" + selectedPixmapSource: "qrc:/images/icons/ic_play_white_24dp.png" + + onClicked: { + CallAdapter.holdThisCallToggle() + } + + onButtonEntered: { + callOverlayButtonGroupRect.buttonEntered() + } + + onVisibleChanged: { + if (this.visible) + buttonCounts++ + else + buttonCounts-- + } + } + + TintedButton { + id: addToConferenceButton + + width: buttonPreferredSize + height: buttonPreferredSize + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/ic_group_add_white_24dp.png" + selectedPixmapSource: "qrc:/images/icons/ic_group_add_white_24dp.png" + + onButtonEntered: { + callOverlayButtonGroupRect.buttonEntered() + } + + onClicked: { + callOverlayButtonGroupRect.addToConferenceButtonClicked() + } + + onVisibleChanged: { + if (this.visible) + buttonCounts++ + else + buttonCounts-- + } + } + + TintedButton { + id: transferCallButton + + width: buttonPreferredSize + height: buttonPreferredSize + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/ic_call_transfer_white_24px.png" + selectedPixmapSource: "qrc:/images/icons/ic_call_transfer_white_24px.png" + + onButtonEntered: { + callOverlayButtonGroupRect.buttonEntered() + } + + onClicked: { + callOverlayButtonGroupRect.transferCallButtonClicked() + } + + onVisibleChanged: { + if (this.visible) + buttonCounts++ + else + buttonCounts-- + } + } + + TintedButton { + id: chatButton + + width: buttonPreferredSize + height: buttonPreferredSize + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/ic_chat_white_24dp.png" + selectedPixmapSource: "qrc:/images/icons/ic_chat_white_24dp.png" + + onClicked: { + callOverlayButtonGroupRect.chatButtonClicked() + } + + onButtonEntered: { + callOverlayButtonGroupRect.buttonEntered() + } + + onVisibleChanged: { + if (this.visible) + buttonCounts++ + else + buttonCounts-- + } + } + + TintedButton { + id: noMicButton + + width: buttonPreferredSize + height: buttonPreferredSize + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/ic_mic_white_24dp.png" + selectedPixmapSource: "qrc:/images/icons/ic_mic_off_white_24dp.png" + + onClicked: { + CallAdapter.muteThisCallToggle() + } + + onButtonEntered: { + callOverlayButtonGroupRect.buttonEntered() + } + + onVisibleChanged: { + if (this.visible) + buttonCounts++ + else + buttonCounts-- + } + } + + TintedButton { + id: noVideoButton + + width: buttonPreferredSize + height: buttonPreferredSize + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/ic_videocam_white.png" + selectedPixmapSource: "qrc:/images/icons/ic_videocam_off_white_24dp.png" + + onButtonEntered: { + callOverlayButtonGroupRect.buttonEntered() + } + + onClicked: { + CallAdapter.videoPauseThisCallToggle() + } + + onVisibleChanged: { + if (this.visible) + buttonCounts++ + else + buttonCounts-- + } + } + + TintedButton { + id: recButton + + width: buttonPreferredSize + height: buttonPreferredSize + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/ic_voicemail_white_24dp_2x.png" + selectedPixmapSource: "qrc:/images/icons/ic_voicemail_white_24dp_2x.png" + + onButtonEntered: { + callOverlayButtonGroupRect.buttonEntered() + } + + onClicked: { + CallAdapter.recordThisCallToggle() + } + + onVisibleChanged: { + if (this.visible) + buttonCounts++ + else + buttonCounts-- + } + } + + TintedButton { + id: sipInputPanelButton + + width: buttonPreferredSize + height: buttonPreferredSize + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/icon-keypad-24-2x.png" + selectedPixmapSource: "qrc:/images/icons/icon-keypad-24-2x.png" + + onButtonEntered: { + callOverlayButtonGroupRect.buttonEntered() + } + + onVisibleChanged: { + if (this.visible) + buttonCounts++ + else + buttonCounts-- + } + } + } + + color: "transparent" +} diff --git a/src/mainview/components/CallStackView.qml b/src/mainview/components/CallStackView.qml new file mode 100644 index 00000000..61df06e3 --- /dev/null +++ b/src/mainview/components/CallStackView.qml @@ -0,0 +1,227 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import net.jami.Models 1.0 + +import "../js/incomingcallpagecreation.js" as IncomingCallPageCreation +import "../js/videocallfullscreenwindowcontainercreation.js" as VideoCallFullScreenWindowContainerCreation + +Rectangle { + id: callStackViewWindow + + anchors.fill: parent + + Shortcut { + sequence: "Ctrl+D" + context: Qt.ApplicationShortcut + onActivated: CallAdapter.hangUpThisCall() + } + + /* + * When selected conversation is changed, + * these values will also be changed. + */ + property string responsibleConvUid: "" + property string responsibleAccountId: "" + + signal outgoingCallPageBackButtonIsClicked + signal callPageBackButtonIsClicked + + function needToCloseInCallConversationAndPotentialWindow() { + audioCallPage.closeInCallConversation() + videoCallPage.closeInCallConversation() + + + /* + * Close potential window, context menu releated windows. + */ + audioCallPage.closeContextMenuAndRelatedWindows() + + VideoCallFullScreenWindowContainerCreation.closeVideoCallFullScreenWindowContainer() + videoCallPage.setCallOverlayBackButtonVisible(true) + videoCallPage.closeContextMenuAndRelatedWindows() + } + + function setCorrspondingMessageWebView(webViewId) { + audioCallPage.setAudioCallPageCorrspondingMessageWebView(webViewId) + videoCallPage.setVideoCallPageCorrspondingMessageWebView(webViewId) + } + + function updateCorrspondingUI() { + audioCallPage.updateUI(responsibleAccountId, responsibleConvUid) + outgoingCallPage.updateUI(responsibleAccountId, responsibleConvUid) + videoCallPage.updateUI(responsibleAccountId, responsibleConvUid) + } + + function showAudioCallPage() { + var itemToFind = callStackMainView.find(function (item) { + return item.stackNumber === 0 + }) + + if (!itemToFind) { + callStackMainView.push(audioCallPage, StackView.Immediate) + } else { + callStackMainView.pop(itemToFind, StackView.Immediate) + } + audioCallPage.updateUI(responsibleAccountId, responsibleConvUid) + } + + function showOutgoingCallPage(currentCallStatus) { + var itemToFind = callStackMainView.find(function (item) { + return item.stackNumber === 1 + }) + + if (!itemToFind) { + callStackMainView.push(outgoingCallPage, StackView.Immediate) + } else { + callStackMainView.pop(itemToFind, StackView.Immediate) + } + if (currentCallStatus) + outgoingCallPage.callStatusPresentation = currentCallStatus + } + + function showVideoCallPage(callId) { + var itemToFind = callStackMainView.find(function (item) { + return item.stackNumber === 2 + }) + + if (!itemToFind) { + callStackMainView.push(videoCallPage, StackView.Immediate) + } else { + callStackMainView.pop(itemToFind, StackView.Immediate) + } + videoCallPage.updateUI(responsibleAccountId, responsibleConvUid) + videoCallPage.setDistantRendererId(callId) + } + + Connections { + target: CallAdapter + + function onShowOutgoingCallPage(accountId, convUid) { + + + /* + * Need to check whether it is the current selected conversation. + */ + if (responsibleConvUid === convUid + && responsibleAccountId === accountId) { + showOutgoingCallPage() + } + } + + function onShowIncomingCallPage(accountId, convUid) { + + + /* + * Check is done within the js. + */ + IncomingCallPageCreation.createincomingCallPageWindowObjects( + accountId, convUid) + IncomingCallPageCreation.showIncomingCallPageWindow(accountId, + convUid) + } + + function onClosePotentialIncomingCallPageWindow(accountId, convUid) { + IncomingCallPageCreation.closeIncomingCallPageWindow(accountId, + convUid) + } + + function onShowAudioCallPage(accountId, convUid) { + if (responsibleConvUid === convUid + && responsibleAccountId === accountId) { + showAudioCallPage() + } + } + + function onShowVideoCallPage(accountId, convUid, callId) { + if (responsibleConvUid === convUid + && responsibleAccountId === accountId) { + showVideoCallPage(callId) + } + } + + function onCallStatusChanged(status, accountId, convUid) { + if (responsibleConvUid === convUid + && responsibleAccountId === accountId) { + outgoingCallPage.callStatusPresentation = status + } + } + } + + AudioCallPage { + id: audioCallPage + + property int stackNumber: 0 + + onAudioCallPageBackButtonIsClicked: { + callStackViewWindow.callPageBackButtonIsClicked() + } + } + + OutgoingCallPage { + id: outgoingCallPage + + property int stackNumber: 1 + + onCallCancelButtonIsClicked: { + CallAdapter.hangUpACall(responsibleAccountId, responsibleConvUid) + } + + onBackButtonIsClicked: { + callStackViewWindow.outgoingCallPageBackButtonIsClicked() + } + } + + VideoCallPage { + id: videoCallPage + + property int stackNumber: 2 + + onVideoCallPageBackButtonIsClicked: { + callStackViewWindow.callPageBackButtonIsClicked() + } + + onNeedToShowInFullScreen: { + VideoCallFullScreenWindowContainerCreation.createvideoCallFullScreenWindowContainerObject() + + if (!VideoCallFullScreenWindowContainerCreation.checkIfVisible()) { + VideoCallFullScreenWindowContainerCreation.setAsContainerChild( + videoCallPage) + videoCallPage.setCallOverlayBackButtonVisible(false) + VideoCallFullScreenWindowContainerCreation.showVideoCallFullScreenWindowContainer() + } else { + videoCallPage.parent = callStackMainView + videoCallPage.setCallOverlayBackButtonVisible(true) + VideoCallFullScreenWindowContainerCreation.closeVideoCallFullScreenWindowContainer() + } + } + } + + StackView { + id: callStackMainView + + anchors.fill: parent + + initialItem: outgoingCallPage + } +} diff --git a/src/mainview/components/ChangeLogScrollView.qml b/src/mainview/components/ChangeLogScrollView.qml new file mode 100644 index 00000000..f672e65d --- /dev/null +++ b/src/mainview/components/ChangeLogScrollView.qml @@ -0,0 +1,59 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + +ScrollView { + id: changeLogScrollView + + anchors.fill: parent + + clip: true + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + + TextEdit { + id: changeLogTextArea + + width: changeLogScrollView.width + + selectByMouse: false + readOnly: true + wrapMode: Text.WordWrap + + font.pointSize: JamiTheme.textFontSize - 3 + text: ClientWrapper.utilsAdaptor.getChangeLog() + textFormat: TextEdit.RichText + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + cursorShape: Qt.ArrowCursor + acceptedButtons: Qt.NoButton + } + } + + background: Rectangle { + id: changeLogScrollViewBackground + + radius: 5 + border.color: JamiTheme.tabbarBorderColor + } +} diff --git a/src/mainview/components/ContactPicker.qml b/src/mainview/components/ContactPicker.qml new file mode 100644 index 00000000..1d414062 --- /dev/null +++ b/src/mainview/components/ContactPicker.qml @@ -0,0 +1,149 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Popup { + id: contactPickerPopup + + property int type: ContactPicker.ContactPickerType.JAMICONFERENCE + + + /* + * Important to keep it one, since enum in c++ starts at one for conferences. + */ + enum ContactPickerType { + JAMICONFERENCE = 1, + SIPTRANSFER + } + + contentWidth: 250 + contentHeight: contactPickerPopupRectColumnLayout.height + 50 + + padding: 0 + + modal: true + + contentItem: Rectangle { + id: contactPickerPopupRect + + width: 250 + + HoverableButton { + id: closeButton + + anchors.top: contactPickerPopupRect.top + anchors.topMargin: 5 + anchors.right: contactPickerPopupRect.right + anchors.rightMargin: 5 + + width: 30 + height: 30 + + radius: 30 + source: "qrc:/images/icons/ic_close_black_24dp.png" + + onClicked: { + contactPickerPopup.close() + } + } + + ColumnLayout { + id: contactPickerPopupRectColumnLayout + + anchors.top: contactPickerPopupRect.top + anchors.topMargin: 15 + + Text { + id: contactPickerTitle + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: contactPickerPopupRect.width + Layout.preferredHeight: 30 + + font.pointSize: JamiTheme.textFontSize + font.bold: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: type === ContactPicker.ContactPickerType.JAMICONFERENCE ? qsTr("Add to conference") : qsTr("Transfer this call") + } + + ContactSearchBar { + id: contactPickerContactSearchBar + + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 5 + Layout.bottomMargin: 5 + Layout.preferredWidth: contactPickerPopupRect.width - 10 + Layout.preferredHeight: 35 + + onContactSearchBarTextChanged: { + ContactAdapter.setSearchFilter(text) + } + + Component.onCompleted: { + contactPickerContactSearchBar.setPlaceholderString( + qsTr("Search contacts")) + } + } + + ListView { + id: contactPickerListView + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: contactPickerPopupRect.width + Layout.preferredHeight: 200 + + model: ContactAdapter.getContactSelectableModel(type) + + clip: true + + delegate: ContactPickerItemDelegate { + id: contactPickerItemDelegate + } + + ScrollIndicator.vertical: ScrollIndicator {} + } + } + + radius: 10 + color: "white" + } + + onAboutToShow: { + + + /* + * Reset the model on each show. + */ + contactPickerListView.model = ContactAdapter.getContactSelectableModel( + type) + } + + background: Rectangle { + color: "transparent" + } +} diff --git a/src/mainview/components/ContactPickerItemDelegate.qml b/src/mainview/components/ContactPickerItemDelegate.qml new file mode 100644 index 00000000..0f2f6568 --- /dev/null +++ b/src/mainview/components/ContactPickerItemDelegate.qml @@ -0,0 +1,135 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +ItemDelegate { + id: contactPickerItemDelegate + + Image { + id: contactPickerContactImage + + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 10 + + width: 40 + height: 40 + + fillMode: Image.PreserveAspectFit + source: "data:image/png;base64," + Picture + mipmap: true + } + + Rectangle { + id: contactPickerContactInfoRect + + anchors.left: contactPickerContactImage.right + anchors.leftMargin: 10 + anchors.top: parent.top + + width: parent.width - contactPickerContactImage.width - 20 + height: parent.height + + color: "transparent" + + Text { + id: contactPickerContactName + + anchors.left: contactPickerContactInfoRect.left + anchors.bottom: contactPickerContactInfoRect.verticalCenter + + TextMetrics { + id: textMetricsContactPickerContactName + font: contactPickerContactName.font + elide: Text.ElideMiddle + elideWidth: contactPickerContactInfoRect.width + text: DisplayName + } + + text: textMetricsContactPickerContactName.elidedText + font.pointSize: JamiTheme.textFontSize + } + + Text { + id: contactPickerContactId + + anchors.left: contactPickerContactInfoRect.left + anchors.top: contactPickerContactInfoRect.verticalCenter + + fontSizeMode: Text.Fit + color: JamiTheme.faddedFontColor + + TextMetrics { + id: textMetricsContactPickerContactId + font: contactPickerContactId.font + elide: Text.ElideMiddle + elideWidth: contactPickerContactInfoRect.width + text: DisplayID == DisplayName ? "" : DisplayID + } + + text: textMetricsContactPickerContactId.elidedText + font.pointSize: JamiTheme.textFontSize + } + } + + background: Rectangle { + id: itemSmartListBackground + + color: "white" + + implicitWidth: contactPickerPopupRect.width + implicitHeight: Math.max( + contactPickerContactName.height + + textMetricsContactPickerContactId.height + 10, + contactPickerContactImage.height + 10) + border.width: 0 + } + + MouseArea { + id: mouseAreaContactPickerItemDelegate + + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton + + onPressed: { + itemSmartListBackground.color = JamiTheme.pressColor + } + + onReleased: { + itemSmartListBackground.color = JamiTheme.releaseColor + + ContactAdapter.contactSelected(index) + contactPickerPopup.close() + } + + onEntered: { + itemSmartListBackground.color = JamiTheme.hoverColor + } + + onExited: { + itemSmartListBackground.color = "white" + } + } +} diff --git a/src/mainview/components/ContactSearchBar.qml b/src/mainview/components/ContactSearchBar.qml new file mode 100644 index 00000000..896b48c7 --- /dev/null +++ b/src/mainview/components/ContactSearchBar.qml @@ -0,0 +1,107 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +Rectangle { + id: contactSearchBarRect + + signal contactSearchBarTextChanged(string text) + + function setPlaceholderString(str) { + placeholderTextForSearchBar.text = str + } + + + /* + * Hack - there is no real way now to make TextField lose its focus, + * unless transfer it to other component. + */ + function clearFocus() { + fakeFocus.forceActiveFocus() + } + + function clearText() { + contactSearchBar.clear() + fakeFocus.forceActiveFocus() + } + + border.color: JamiTheme.pressColor + radius: 10 + color: contactSearchBar.activeFocus ? "white" : JamiTheme.contactSearchBarPlaceHolderGreyBackground + + FocusScope { + id: fakeFocus + } + + Image { + id: searchIconImage + + anchors.verticalCenter: contactSearchBarRect.verticalCenter + anchors.left: contactSearchBarRect.left + anchors.leftMargin: 5 + + width: 20 + height: 20 + + fillMode: Image.PreserveAspectFit + mipmap: true + source: "qrc:/images/icons/ic_baseline-search-24px.svg" + } + + TextField { + id: contactSearchBar + + anchors.verticalCenter: contactSearchBarRect.verticalCenter + anchors.left: searchIconImage.right + + width: contactSearchBarRect.width - searchIconImage.width - 10 + height: contactSearchBarRect.height - 5 + + font.pointSize: JamiTheme.textFontSize - 1 + selectByMouse: true + selectionColor: JamiTheme.contactSearchBarPlaceHolderTextFontColor + + Text { + id: placeholderTextForSearchBar + + anchors.verticalCenter: contactSearchBar.verticalCenter + anchors.left: contactSearchBar.left + anchors.leftMargin: 5 + + text: qsTr("Find or start a conversation") + font.pointSize: JamiTheme.textFontSize - 1 + color: JamiTheme.contactSearchBarPlaceHolderTextFontColor + visible: !contactSearchBar.text && !contactSearchBar.activeFocus + } + + background: Rectangle { + id: searchBarBackground + + color: "transparent" + } + + onTextChanged: { + contactSearchBarRect.contactSearchBarTextChanged( + contactSearchBar.text) + } + } +} diff --git a/src/mainview/components/ConversationSmartListContextMenu.qml b/src/mainview/components/ConversationSmartListContextMenu.qml new file mode 100644 index 00000000..aece4b46 --- /dev/null +++ b/src/mainview/components/ConversationSmartListContextMenu.qml @@ -0,0 +1,151 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Menu { + id: contextMenu + + property string responsibleAccountId: "" + property string responsibleConvUid: "" + + property int generalMenuSeparatorCount: 0 + property int commonBorderWidth: 2 + + + /* + * All GeneralMenuItems should remain the same width / height. + */ + GeneralMenuItem { + id: startVideoCallItem + + itemName: qsTr("Start video call") + topBorderWidth: commonBorderWidth + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + ConversationsAdapter.selectConversation(responsibleAccountId, + responsibleConvUid, false) + CallAdapter.placeCall() + } + } + + GeneralMenuItem { + id: startAudioCallItem + + itemName: qsTr("Start audio call") + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + ConversationsAdapter.selectConversation(responsibleAccountId, + responsibleConvUid, false) + CallAdapter.placeAudioOnlyCall() + } + } + + GeneralMenuItem { + id: clearConversationItem + + itemName: qsTr("Clear conversation") + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + ClientWrapper.utilsAdaptor.clearConversationHistory(responsibleAccountId, + responsibleConvUid) + } + } + + GeneralMenuItem { + id: removeContactItem + + itemName: qsTr("Remove contact") + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + ClientWrapper.utilsAdaptor.removeConversation(responsibleAccountId, + responsibleConvUid) + } + } + + GeneralMenuSeparator { + preferredWidth: startVideoCallItem.preferredWidth + preferredHeight: commonBorderWidth + + Component.onCompleted: { + generalMenuSeparatorCount++ + } + } + + GeneralMenuItem { + id: blockContactItem + + itemName: qsTr("Block contact") + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + ClientWrapper.utilsAdaptor.removeConversation(responsibleAccountId, + responsibleConvUid, true) + } + } + + GeneralMenuSeparator { + preferredWidth: startVideoCallItem.preferredWidth + preferredHeight: commonBorderWidth + + Component.onCompleted: { + generalMenuSeparatorCount++ + } + } + + GeneralMenuItem { + id: profileItem + + itemName: qsTr("Profile") + bottomBorderWidth: commonBorderWidth + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + userProfile.open() + } + } + + background: Rectangle { + implicitWidth: startVideoCallItem.preferredWidth + implicitHeight: startVideoCallItem.preferredHeight + * (contextMenu.count - generalMenuSeparatorCount) + + border.width: commonBorderWidth + border.color: JamiTheme.tabbarBorderColor + } +} diff --git a/src/mainview/components/ConversationSmartListUserImage.qml b/src/mainview/components/ConversationSmartListUserImage.qml new file mode 100644 index 00000000..6419ec4e --- /dev/null +++ b/src/mainview/components/ConversationSmartListUserImage.qml @@ -0,0 +1,88 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +Image { + id: userImage + + width: 50 + height: 50 + + fillMode: Image.PreserveAspectFit + source: "data:image/png;base64," + Picture + mipmap: true + + Rectangle { + id: presenseRect + + anchors.right: userImage.right + anchors.rightMargin: 1 + anchors.bottom: userImage.bottom + anchors.bottomMargin: 2 + + width: 14 + height: 14 + + visible: Presence + + Rectangle { + id: presenseCycle + + anchors.centerIn: presenseRect + + width: 10 + height: 10 + + radius: 30 + color: JamiTheme.presenceGreen + } + + radius: 30 + color: "white" + } + + Rectangle { + id: unreadMessageCountRect + + anchors.right: userImage.right + anchors.rightMargin: 1 + anchors.top: userImage.top + anchors.topMargin: 2 + + width: 14 + height: 14 + + visible: UnreadMessagesCount > 0 + + Text { + id: unreadMessageCounttext + + anchors.centerIn: unreadMessageCountRect + + text: UnreadMessagesCount > 9 ? "···" : UnreadMessagesCount + color: "white" + } + + radius: 30 + color: JamiTheme.notificationRed + } +} diff --git a/src/mainview/components/ConversationSmartListView.qml b/src/mainview/components/ConversationSmartListView.qml new file mode 100644 index 00000000..0513f6fd --- /dev/null +++ b/src/mainview/components/ConversationSmartListView.qml @@ -0,0 +1,104 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +ListView { + id: conversationSmartListView + + signal needToAccessMessageWebView(string currentUserDisplayName, string currentUserAlias, string currentUID, bool callStackViewShouldShow, bool isAudioOnly, string callStateStr) + signal needToSelectItems(int index) + signal needToDeselectItems + signal needToBackToWelcomePage + signal needToGrabFocus + + signal needToShowChatView(string accountId, string convUid) + signal currentIndexIsChanged + signal forceUpdatePotentialInvalidItem + + + /* + * When model is sorted, we need to reset to focus (currentIndex) + * to the real conversation that we focused. + */ + function modelSorted(contactURIToCompare) { + var conversationSmartListViewModel = conversationSmartListView.model + conversationSmartListView.currentIndex = -1 + updateConversationSmartListView() + for (var i = 0; i < count; i++) { + if (conversationSmartListViewModel.data( + conversationSmartListViewModel.index(i, 0), + 261) === contactURIToCompare) { + conversationSmartListView.currentIndex = i + break + } + } + } + + + /* + * Refresh all item within model. + */ + function updateConversationSmartListView() { + var conversationSmartListViewModel = conversationSmartListView.model + conversationSmartListViewModel.dataChanged( + conversationSmartListViewModel.index(0, 0), + conversationSmartListViewModel.index( + conversationSmartListViewModel.rowCount() - 1, 0)) + conversationSmartListView.forceUpdatePotentialInvalidItem() + } + + function setModel(model) { + conversationSmartListView.model = model + } + + function backToWelcomePage() { + conversationSmartListView.needToBackToWelcomePage() + } + + + /* + * Update smartlist to accountId. + */ + function updateSmartList(accountId) { + conversationSmartListView.model.setAccount(accountId) + } + + Connections { + target: CallAdapter + + function onUpdateConversationSmartList() { + updateConversationSmartListView() + } + } + + onCurrentIndexChanged: { + conversationSmartListView.currentIndexIsChanged() + } + + clip: true + + delegate: ConversationSmartListViewItemDelegate { + id: smartListItemDelegate + } + + ScrollIndicator.vertical: ScrollIndicator {} +} diff --git a/src/mainview/components/ConversationSmartListViewItemDelegate.qml b/src/mainview/components/ConversationSmartListViewItemDelegate.qml new file mode 100644 index 00000000..7a766f06 --- /dev/null +++ b/src/mainview/components/ConversationSmartListViewItemDelegate.qml @@ -0,0 +1,280 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +ItemDelegate { + id: smartListItemDelegate + + Connections { + target: conversationSmartListView + + + /* + * Hack, make sure that smartListItemDelegate does not show extra item + * when searching new contacts. + */ + function onForceUpdatePotentialInvalidItem() { + smartListItemDelegate.visible = conversationSmartListView.model.rowCount( + ) <= index ? false : true + } + + + /* + * When currentIndex is -1, deselect items, if not, change select item + */ + function onCurrentIndexIsChanged() { + if (conversationSmartListView.currentIndex === -1 + || conversationSmartListView.currentIndex !== index) { + itemSmartListBackground.color = Qt.binding(function () { + return InCall ? Qt.lighter(JamiTheme.selectionBlue, + 1.8) : "white" + }) + } else { + itemSmartListBackground.color = Qt.binding(function () { + return InCall ? Qt.lighter(JamiTheme.selectionBlue, + 1.8) : JamiTheme.releaseColor + }) + } + } + + function onNeedToShowChatView(accountId, convUid) { + if (convUid === UID) { + conversationSmartListView.needToAccessMessageWebView( + DisplayID == DisplayName ? "" : DisplayID, + DisplayName, UID, CallStackViewShouldShow, + IsAudioOnly, CallStateStr) + } + } + } + + ConversationSmartListUserImage { + id: conversationSmartListUserImage + + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 10 + } + + Rectangle { + id: conversationSmartListUserInfoRect + + anchors.left: conversationSmartListUserImage.right + anchors.leftMargin: 10 + anchors.top: parent.top + + width: LastInteractionDate ? (parent.width - conversationSmartListUserImage.width - 20) + / 2 : parent.width - conversationSmartListUserImage.width - 20 + height: parent.height + + color: "transparent" + + ColumnLayout { + id: conversationSmartListUserInfoColumnLayout + + anchors.fill: parent + + Text { + id: conversationSmartListUserName + + Layout.alignment: Qt.AlignLeft + + TextMetrics { + id: textMetricsConversationSmartListUserName + font: conversationSmartListUserName.font + elide: Text.ElideMiddle + elideWidth: conversationSmartListUserInfoRect.width + text: DisplayName + } + + text: textMetricsConversationSmartListUserName.elidedText + font.pointSize: JamiTheme.textFontSize + } + + Text { + id: conversationSmartListUserId + + Layout.alignment: Qt.AlignLeft + + fontSizeMode: Text.Fit + color: JamiTheme.faddedFontColor + + TextMetrics { + id: textMetricsConversationSmartListUserId + font: conversationSmartListUserId.font + elide: Text.ElideMiddle + elideWidth: conversationSmartListUserInfoRect.width + text: DisplayID == DisplayName ? "" : DisplayID + } + + text: textMetricsConversationSmartListUserId.elidedText + font.pointSize: JamiTheme.textFontSize + } + } + } + + Rectangle { + id: conversationSmartListUserLastInteractionRect + + anchors.left: conversationSmartListUserInfoRect.right + anchors.top: parent.top + + width: (parent.width - conversationSmartListUserImage.width - 20) / 2 - 10 + height: parent.height + + color: "transparent" + + ColumnLayout { + id: conversationSmartListUserLastInteractionColumnLayout + + anchors.fill: parent + + Text { + id: conversationSmartListUserLastInteractionDate + + Layout.alignment: Qt.AlignRight + + TextMetrics { + id: textMetricsConversationSmartListUserLastInteractionDate + font: conversationSmartListUserLastInteractionDate.font + elideWidth: conversationSmartListUserLastInteractionRect.width + elide: Text.ElideRight + text: LastInteractionDate + } + + text: textMetricsConversationSmartListUserLastInteractionDate.elidedText + font.pointSize: JamiTheme.textFontSize + color: JamiTheme.faddedFontColor + } + + Text { + id: conversationSmartListUserLastInteractionMessage + + Layout.alignment: Qt.AlignRight + + fontSizeMode: Text.Fit + + TextMetrics { + id: textMetricsConversationSmartListUserLastInteractionMessage + font: conversationSmartListUserLastInteractionMessage.font + elideWidth: conversationSmartListUserLastInteractionRect.width + elide: Text.ElideRight + text: InCall ? CallStateStr : (Draft ? Draft : LastInteraction) + } + + font.family: "Segoe UI Emoji" + font.hintingPreference: Font.PreferNoHinting + text: textMetricsConversationSmartListUserLastInteractionMessage.elidedText + font.pointSize: JamiTheme.textFontSize + color: Draft ? JamiTheme.draftRed : JamiTheme.faddedLastInteractionFontColor + } + } + } + + background: Rectangle { + id: itemSmartListBackground + + color: InCall ? Qt.lighter(JamiTheme.selectionBlue, 1.8) : "white" + + implicitWidth: conversationSmartListView.width + implicitHeight: Math.max( + conversationSmartListUserName.height + + textMetricsConversationSmartListUserId.height + 10, + conversationSmartListUserImage.height + 10) + border.width: 0 + } + + MouseArea { + id: mouseAreaSmartListItemDelegate + + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onPressed: { + if (!InCall) { + itemSmartListBackground.color = JamiTheme.pressColor + } + } + onDoubleClicked: { + if (!InCall) { + ConversationsAdapter.selectConversation(ClientWrapper.utilsAdaptor.getCurrAccId(), + UID, false) + CallAdapter.placeCall() + } + } + onReleased: { + if (!InCall) { + itemSmartListBackground.color = JamiTheme.releaseColor + } + if (mouse.button === Qt.RightButton) { + + + /* + * Make menu pos at mouse. + */ + var relativeMousePos = mapToItem(itemSmartListBackground, + mouse.x, mouse.y) + smartListContextMenu.x = relativeMousePos.x + smartListContextMenu.y = relativeMousePos.y + smartListContextMenu.responsibleAccountId = ClientWrapper.utilsAdaptor.getCurrAccId() + smartListContextMenu.responsibleConvUid = UID + userProfile.responsibleConvUid = UID + userProfile.aliasText = DisplayName + userProfile.registeredNameText = DisplayID + userProfile.idText = URI + userProfile.contactPicBase64 = Picture + smartListContextMenu.open() + } else if (mouse.button === Qt.LeftButton) { + conversationSmartListView.currentIndex = index + conversationSmartListView.needToSelectItems(index) + conversationSmartListView.needToGrabFocus() + } + } + onEntered: { + if (!InCall) { + itemSmartListBackground.color = JamiTheme.hoverColor + } + } + onExited: { + if (!InCall) { + if (conversationSmartListView.currentIndex !== index + || conversationSmartListView.currentIndex === -1) { + itemSmartListBackground.color = Qt.binding(function () { + return InCall ? Qt.lighter(JamiTheme.selectionBlue, + 1.8) : "white" + }) + } else { + itemSmartListBackground.color = Qt.binding(function () { + return InCall ? Qt.lighter(JamiTheme.selectionBlue, + 1.8) : JamiTheme.releaseColor + }) + } + } + } + } + + ConversationSmartListContextMenu { + id: smartListContextMenu + } +} diff --git a/src/mainview/components/IncomingCallPage.qml b/src/mainview/components/IncomingCallPage.qml new file mode 100644 index 00000000..eafbaffe --- /dev/null +++ b/src/mainview/components/IncomingCallPage.qml @@ -0,0 +1,342 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import net.jami.Models 1.0 + +import "../../commoncomponents" + + +/* + * IncomingCallPage as a seperate window, + * exist at the right bottom, as a notification to user that + * a call is incoming. + */ +Window { + id: incomingCallPage + + property int minWidth: 300 + property int minHeight: 400 + + + /* + * The unique identifier for incomingCallPage + */ + property string responsibleAccountId: "" + property string responsibleConvUid: "" + + property string contactImgSource: "" + property string bestName: "Best Name" + property string bestId: "Best Id" + + property int buttonPreferredSize: 50 + property variant clickPos: "1,1" + + function updateUI() { + incomingCallPage.contactImgSource = "data:image/png;base64," + + ClientWrapper.utilsAdaptor.getContactImageString(responsibleAccountId, + responsibleConvUid) + incomingCallPage.bestName = ClientWrapper.utilsAdaptor.getBestName( + responsibleAccountId, responsibleConvUid) + var id = ClientWrapper.utilsAdaptor.getBestId(responsibleAccountId, + responsibleConvUid) + incomingCallPage.bestId = (incomingCallPage.bestName !== id) ? id : "" + } + + function updatePositionToRightBottom() { + + + /* + * Screen right bottom, + * since the qt screen.virtualY, virtualX does not work properly, + * we need to calculate the screen x, y ourselves, by + * using to fact that window will always be in the middle if no x or y + * specificed. + * ex: https://doc.qt.io/qt-5/qscreen.html#geometry-prop + */ + var virtualX = (incomingCallPage.x + width / 2) - screen.width / 2 + incomingCallPage.x = virtualX + screen.width - width + incomingCallPage.y = screen.height - height - 50 + } + + minimumWidth: minWidth + minimumHeight: minHeight + + maximumWidth: minWidth + 300 + maximumHeight: minHeight + 300 + + flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint + screen: Qt.application.screens[0] + + Rectangle { + id: incomingCallPageColumnLayoutMainRect + + anchors.fill: parent + + radius: 15 + color: "black" + + + /* + * Simulate window drag. (top with height 30). + */ + MouseArea { + id: dragMouseArea + + anchors.left: incomingCallPageColumnLayoutMainRect.left + anchors.top: incomingCallPageColumnLayoutMainRect.top + + width: incomingCallPageColumnLayoutMainRect.width - closeButton.width - 10 + height: 30 + + onPressed: { + clickPos = Qt.point(mouse.x, mouse.y) + } + + onPositionChanged: { + var delta = Qt.point(mouse.x - clickPos.x, mouse.y - clickPos.y) + incomingCallPage.x += delta.x + incomingCallPage.y += delta.y + } + } + + HoverableButton { + id: closeButton + + anchors.top: incomingCallPageColumnLayoutMainRect.top + anchors.topMargin: 10 + anchors.right: incomingCallPageColumnLayoutMainRect.right + anchors.rightMargin: 10 + + width: 30 + height: 30 + + radius: 30 + source: "qrc:/images/icons/ic_close_white_24dp.png" + backgroundColor: "black" + onEnterColor: JamiTheme.closeButtonLighterBlack + onExitColor: "black" + onPressColor: JamiTheme.declineButtonPressedRed + onReleaseColor: "black" + + onClicked: { + incomingCallPage.close() + CallAdapter.refuseACall(responsibleAccountId, + responsibleConvUid) + } + } + + ColumnLayout { + id: incomingCallPageColumnLayout + + anchors.fill: parent + + Image { + id: contactImg + + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 30 + + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + + fillMode: Image.PreserveAspectFit + source: contactImgSource + } + + Rectangle { + id: incomingCallPageTextRect + + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 5 + + Layout.preferredWidth: incomingCallPage.width + Layout.preferredHeight: jamiBestNameText.height + jamiBestIdText.height + + talkToYouText.height + 20 + + ColumnLayout { + id: incomingCallPageTextRectColumnLayout + + Text { + id: jamiBestNameText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: incomingCallPageTextRect.width + Layout.preferredHeight: 50 + + font.pointSize: JamiTheme.textFontSize + 3 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiBestNameText.elidedText + color: "white" + + TextMetrics { + id: textMetricsjamiBestNameText + font: jamiBestNameText.font + text: bestName + elideWidth: incomingCallPageTextRect.width - 30 + elide: Qt.ElideMiddle + } + } + + Text { + id: jamiBestIdText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: incomingCallPageTextRect.width + Layout.preferredHeight: 30 + + font.pointSize: JamiTheme.textFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiBestIdText.elidedText + color: "white" + + TextMetrics { + id: textMetricsjamiBestIdText + font: jamiBestIdText.font + text: bestId + elideWidth: incomingCallPageTextRect.width - 30 + elide: Qt.ElideMiddle + } + } + + Text { + id: talkToYouText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: incomingCallPageTextRect.width + Layout.preferredHeight: 30 + + font.pointSize: JamiTheme.textFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: "white" + + text: "is calling you" + } + } + + color: "transparent" + } + + RowLayout { + id: incomingCallPageRowLayout + + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + Layout.bottomMargin: 5 + + Layout.preferredWidth: incomingCallPage.width - 100 + Layout.preferredHeight: buttonPreferredSize + + ColumnLayout { + id: callAnswerButtonColumnLayout + + Layout.alignment: Qt.AlignLeft + + HoverableButton { + id: callAnswerButton + + Layout.alignment: Qt.AlignCenter + + Layout.preferredWidth: buttonPreferredSize + Layout.preferredHeight: buttonPreferredSize + + backgroundColor: JamiTheme.acceptButtonGreen + onEnterColor: JamiTheme.acceptButtonHoverGreen + onPressColor: JamiTheme.acceptButtonPressedGreen + onReleaseColor: JamiTheme.acceptButtonHoverGreen + onExitColor: JamiTheme.acceptButtonGreen + + buttonImageHeight: buttonPreferredSize / 2 + buttonImageWidth: buttonPreferredSize / 2 + source: "qrc:/images/icons/ic_check_white_18dp_2x.png" + radius: 30 + + onClicked: { + incomingCallPage.close() + CallAdapter.acceptACall(responsibleAccountId, + responsibleConvUid) + } + } + + Text { + id: answerText + + Layout.alignment: Qt.AlignCenter + + font.pointSize: JamiTheme.textFontSize - 2 + text: qsTr("Answer") + } + } + + ColumnLayout { + id: callDeclineButtonColumnLayout + + Layout.alignment: Qt.AlignRight + + HoverableButton { + id: callDeclineButton + + Layout.alignment: Qt.AlignCenter + + Layout.preferredWidth: buttonPreferredSize + Layout.preferredHeight: buttonPreferredSize + + backgroundColor: JamiTheme.declineButtonRed + onEnterColor: JamiTheme.declineButtonHoverRed + onPressColor: JamiTheme.declineButtonPressedRed + onReleaseColor: JamiTheme.declineButtonHoverRed + onExitColor: JamiTheme.declineButtonRed + + buttonImageHeight: buttonPreferredSize / 2 + buttonImageWidth: buttonPreferredSize / 2 + source: "qrc:/images/icons/ic_close_white_24dp.png" + radius: 30 + + onClicked: { + incomingCallPage.close() + CallAdapter.refuseACall(responsibleAccountId, + responsibleConvUid) + } + } + + Text { + id: ignoreText + + Layout.alignment: Qt.AlignCenter + + font.pointSize: JamiTheme.textFontSize - 2 + text: qsTr("Ignore") + } + } + } + } + } + + color: "transparent" +} diff --git a/src/mainview/components/MessageWebView.qml b/src/mainview/components/MessageWebView.qml new file mode 100644 index 00000000..2045edf8 --- /dev/null +++ b/src/mainview/components/MessageWebView.qml @@ -0,0 +1,293 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtWebEngine 1.10 +import QtWebChannel 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + + +Rectangle { + id: messageWebViewRect + + property int messageWebViewHeaderPreferredHeight: 60 + property string headerUserAliasLabelText: "" + property string headerUserUserNameLabelText: "" + + signal needToGoBackToWelcomeView + signal needToHideConversationInCall + signal needToSendContactRequest + + signal sendMessageContentSaved(string arg) + signal messagesCleared + signal messagesLoaded + + + function webViewRunJavaScript(arg) { + messageWebView.runJavaScript(arg) + } + + function setSendContactRequestButtonVisible(visible) { + messageWebViewHeader.sendContactRequestButtonVisible = visible + } + + function setMessagingHeaderButtonsVisible(visible) { + messageWebViewHeader.toggleMessagingHeaderButtonsVisible(visible) + } + + function resetMessagingHeaderBackButtonSource(reset) { + messageWebViewHeader.resetBackToWelcomeViewButtonSource(reset) + } + + anchors.fill: parent + + JamiFileDialog { + id: jamiFileDialog + + mode: JamiFileDialog.Mode.OpenFiles + + onAccepted: { + var filePaths = jamiFileDialog.files + for (var index = 0; index < filePaths.length; ++index) { + var path = ClientWrapper.utilsAdaptor.getAbsPath(filePaths[index]) + MessagesAdapter.setNewMessagesContent(path) + } + } + } + + MessageWebViewHeader { + + DropArea{ + anchors.fill: parent + onDropped: { + var path = ClientWrapper.utilsAdaptor.getAbsPath(drop.text.toString()) + MessagesAdapter.setNewMessagesContent(path) + } + } + + id: messageWebViewHeader + + anchors.top: messageWebViewRect.top + anchors.left: messageWebViewRect.left + + width: messageWebViewRect.width + height: messageWebViewHeaderPreferredHeight + + userAliasLabelText: headerUserAliasLabelText + userUserNameLabelText: headerUserUserNameLabelText + + onBackToWelcomeViewButtonClicked: { + MessagesAdapter.updateDraft() + messageWebViewRect.needToGoBackToWelcomeView() + } + + onNeedToHideConversationInCall: { + messageWebViewRect.needToHideConversationInCall() + } + + onSendContactRequestButtonClicked: { + MessagesAdapter.sendContactRequest() + } + } + + QtObject { + id: jsBridgeObject + + + /* + * ID, under which this object will be known at chatview.js side. + */ + WebChannel.id: "jsbridge" + + + /* + * Functions that are exposed, return code can be derived from js side + * by setting callback function. + */ + function deleteInteraction(arg) { + MessagesAdapter.deleteInteraction(arg) + } + + function retryInteraction(arg) { + MessagesAdapter.retryInteraction(arg) + } + + function openFile(arg) { + MessagesAdapter.openFile(arg) + } + + function acceptFile(arg) { + MessagesAdapter.acceptFile(arg) + } + + function refuseFile(arg) { + MessagesAdapter.refuseFile(arg) + } + + function sendMessage(arg) { + MessagesAdapter.sendMessage(arg) + } + + function sendImage(arg) { + MessagesAdapter.sendImage(arg) + } + + function sendFile(arg) { + MessagesAdapter.sendFile(arg) + } + + function selectFile() { + jamiFileDialog.open() + } + + function acceptInvitation() { + MessagesAdapter.acceptInvitation() + } + + function refuseInvitation() { + MessagesAdapter.refuseInvitation() + } + + function blockConversation() { + MessagesAdapter.blockConversation() + } + + function emitMessagesCleared() { + messageWebViewRect.messagesCleared() + } + + function emitMessagesLoaded() { + messageWebViewRect.messagesLoaded() + } + + function emitPasteKeyDetected() { + MessagesAdapter.pasteKeyDetected() + } + + function openAudioRecorder(spikePosX, spikePosY) { + recordBox.openRecorder(spikePosX, spikePosY, false) + } + + function openVideoRecorder(spikePosX, spikePosY) { + recordBox.openRecorder(spikePosX, spikePosY, true) + } + + function saveSendMessageContent(arg) { + messageWebViewRect.sendMessageContentSaved(arg) + } + + function onComposing(isComposing) { + MessagesAdapter.onComposing(isComposing) + } + } + + WebEngineView { + + id: messageWebView + + anchors.top: messageWebViewHeader.bottom + anchors.topMargin: 1 + anchors.left: messageWebViewRect.left + + width: messageWebViewRect.width + height: messageWebViewRect.height - messageWebViewHeaderPreferredHeight + + settings.javascriptEnabled: true + settings.javascriptCanOpenWindows: true + settings.fullScreenSupportEnabled: true + settings.allowRunningInsecureContent: true + settings.localContentCanAccessRemoteUrls: true + settings.localContentCanAccessFileUrls: true + settings.errorPageEnabled: false + settings.pluginsEnabled: false + settings.screenCaptureEnabled: false + settings.linksIncludedInFocusChain: false + settings.localStorageEnabled: false + + webChannel: messageWebViewChannel + profile: messageWebViewProfile + + DropArea{ + anchors.fill: parent + onDropped: { + var path = ClientWrapper.utilsAdaptor.getAbsPath(drop.text.toString()) + MessagesAdapter.setNewMessagesContent(path) + } + } + + onLoadingChanged: { + if (loadRequest.status == WebEngineView.LoadSucceededStatus) { + messageWebView.runJavaScript(ClientWrapper.utilsAdaptor.getStyleSheet( + "chatcss", + ClientWrapper.utilsAdaptor.qStringFromFile( + ":/chatview.css"))) + messageWebView.runJavaScript(ClientWrapper.utilsAdaptor.getStyleSheet( + "chatwin", + ClientWrapper.utilsAdaptor.qStringFromFile( + ":/chatview-windows.css"))) + + messageWebView.runJavaScript(ClientWrapper.utilsAdaptor.qStringFromFile( + ":/jed.js")) + messageWebView.runJavaScript(ClientWrapper.utilsAdaptor.qStringFromFile( + ":/linkify.js")) + messageWebView.runJavaScript(ClientWrapper.utilsAdaptor.qStringFromFile( + ":/linkify-html.js")) + messageWebView.runJavaScript(ClientWrapper.utilsAdaptor.qStringFromFile( + ":/linkify-string.js")) + messageWebView.runJavaScript(ClientWrapper.utilsAdaptor.qStringFromFile( + ":/qwebchannel.js")) + messageWebView.runJavaScript(ClientWrapper.utilsAdaptor.qStringFromFile( + ":/chatview.js")) + messageWebView.runJavaScript("init_i18n();") + messageWebView.runJavaScript("displayNavbar(false);") + } + } + Component.onCompleted: { + messageWebView.loadHtml(ClientWrapper.utilsAdaptor.qStringFromFile( + ":/chatview.html"), ":/chatview.html") + messageWebView.url = "qrc:/chatview.html" + } + } + + + /* + * Provide WebEngineProfile. + */ + WebEngineProfile { + id: messageWebViewProfile + + cachePath: ClientWrapper.utilsAdaptor.getCachePath() + persistentStoragePath: ClientWrapper.utilsAdaptor.getCachePath() + persistentCookiesPolicy: WebEngineProfile.NoPersistentCookies + httpCacheType: WebEngineProfile.NoCache + httpUserAgent: "jami-windows" + } + + + /* + * Provide WebChannel by registering jsBridgeObject. + */ + WebChannel { + id: messageWebViewChannel + registeredObjects: [jsBridgeObject] + } +} diff --git a/src/mainview/components/MessageWebViewHeader.qml b/src/mainview/components/MessageWebViewHeader.qml new file mode 100644 index 00000000..596c672a --- /dev/null +++ b/src/mainview/components/MessageWebViewHeader.qml @@ -0,0 +1,237 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Rectangle { + id: messagingHeaderRect + + property int buttonPreferredSize: 30 + property string userAliasLabelText: "" + property string userUserNameLabelText: "" + property string backToWelcomeViewButtonSource: "qrc:/images/icons/ic_arrow_back_24px.svg" + property bool sendContactRequestButtonVisible: true + + signal backToWelcomeViewButtonClicked + signal needToHideConversationInCall + signal sendContactRequestButtonClicked + + function resetBackToWelcomeViewButtonSource(reset) { + backToWelcomeViewButtonSource = reset ? "qrc:/images/icons/ic_arrow_back_24px.svg" : "qrc:/images/icons/round-close-24px.svg" + } + + function toggleMessagingHeaderButtonsVisible(visible) { + startAAudioCallButton.visible = visible + startAVideoCallButton.visible = visible + } + + RowLayout { + id: messagingHeaderRectRowLayout + + anchors.fill: parent + + HoverableButton { + id: backToWelcomeViewButton + + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + Layout.leftMargin: 10 + Layout.preferredWidth: buttonPreferredSize + Layout.preferredHeight: buttonPreferredSize + + radius: 30 + source: backToWelcomeViewButtonSource + backgroundColor: "white" + onExitColor: "white" + + onClicked: { + if (backToWelcomeViewButtonSource === "qrc:/images/icons/ic_arrow_back_24px.svg") + messagingHeaderRect.backToWelcomeViewButtonClicked() + else + messagingHeaderRect.needToHideConversationInCall() + } + } + + Rectangle { + id: userNameOrIdRect + + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + + + /* + * Width + margin. + */ + Layout.preferredWidth: messagingHeaderRect.width + - backToWelcomeViewButton.width - buttonGroup.width - 45 + Layout.preferredHeight: messagingHeaderRect.height + Layout.leftMargin: 10 + + color: "transparent" + + ColumnLayout { + id: userNameOrIdColumnLayout + + Label { + id: userAliasLabel + + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: userNameOrIdRect.width + Layout.preferredHeight: textMetricsuserAliasLabel.boundingRect.height + Layout.topMargin: userNameOrIdRect.height / 2 - userAliasLabel.height - 4 + + font.pointSize: JamiTheme.textFontSize - 1 + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + text: textMetricsuserAliasLabel.elidedText + } + + TextMetrics { + id: textMetricsuserAliasLabel + + font: userAliasLabel.font + text: userAliasLabelText + elideWidth: userNameOrIdRect.width + elide: Qt.ElideMiddle + } + + Label { + id: userUserNameLabel + + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: userNameOrIdRect.width + Layout.preferredHeight: textMetricsuserUserNameLabel.boundingRect.height + + font.pointSize: JamiTheme.textFontSize - 2 + color: JamiTheme.faddedFontColor + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + text: textMetricsuserUserNameLabel.elidedText + } + + TextMetrics { + id: textMetricsuserUserNameLabel + + font: userUserNameLabel.font + text: userUserNameLabelText + elideWidth: userNameOrIdRect.width + elide: Qt.ElideMiddle + } + } + } + + Rectangle { + id: buttonGroup + + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + Layout.preferredWidth: buttonPreferredSize * 3 + 18 + Layout.preferredHeight: buttonPreferredSize + Layout.rightMargin: 20 + + color: "transparent" + + HoverableButton { + id: startAAudioCallButton + + anchors.right: startAVideoCallButton.left + anchors.verticalCenter: buttonGroup.verticalCenter + + height: buttonPreferredSize + width: buttonPreferredSize + + radius: 30 + source: "qrc:/images/icons/ic_phone_24px.svg" + backgroundColor: "white" + onExitColor: "white" + + onClicked: { + messagingHeaderRect.sendContactRequestButtonClicked() + CallAdapter.placeAudioOnlyCall() + } + } + + HoverableButton { + id: startAVideoCallButton + + anchors.right: sendContactRequestButton.left + anchors.leftMargin: 5 + anchors.verticalCenter: buttonGroup.verticalCenter + + height: buttonPreferredSize + width: buttonPreferredSize + + radius: 30 + source: "qrc:/images/icons/ic_video_call_24px.svg" + backgroundColor: "white" + onExitColor: "white" + + onClicked: { + messagingHeaderRect.sendContactRequestButtonClicked() + CallAdapter.placeCall() + } + } + + HoverableButton { + id: sendContactRequestButton + + anchors.leftMargin: 5 + anchors.right: buttonGroup.right + anchors.rightMargin: 8 + anchors.verticalCenter: buttonGroup.verticalCenter + + height: buttonPreferredSize + width: buttonPreferredSize + + visible: sendContactRequestButtonVisible + radius: 30 + source: "qrc:/images/icons/ic_person_add_black_24dp_2x.png" + backgroundColor: "white" + onExitColor: "white" + + onClicked: { + messagingHeaderRect.sendContactRequestButtonClicked() + sendContactRequestButtonVisible = false + } + + onVisibleChanged: { + if (sendContactRequestButton.visible) { + sendContactRequestButton.width = buttonPreferredSize + } else { + sendContactRequestButton.width = 0 + } + } + } + } + } + + CustomBorder { + commonBorder: false + lBorderwidth: 0 + rBorderwidth: 0 + tBorderwidth: 0 + bBorderwidth: 1 + borderColor: JamiTheme.tabbarBorderColor + } +} diff --git a/src/mainview/components/OutgoingCallPage.qml b/src/mainview/components/OutgoingCallPage.qml new file mode 100644 index 00000000..6148d46e --- /dev/null +++ b/src/mainview/components/OutgoingCallPage.qml @@ -0,0 +1,229 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Rectangle { + id: outgoingCallPageRect + + property int buttonPreferredSize: 50 + property string callStatusPresentation: "outgoing" + property string contactImgSource: "" + property string bestName: "Best Name" + property string bestId: "Best Id" + + signal callCancelButtonIsClicked + signal backButtonIsClicked + + function updateUI(accountId, convUid) { + contactImgSource = "data:image/png;base64," + ClientWrapper.utilsAdaptor.getContactImageString( + accountId, convUid) + bestName = ClientWrapper.utilsAdaptor.getBestName(accountId, convUid) + var id = ClientWrapper.utilsAdaptor.getBestId(accountId, convUid) + bestId = (bestName !== id) ? id : "" + } + + anchors.fill: parent + + color: "black" + + + /* + * Prevent right click propagate to VideoCallPage. + */ + MouseArea { + anchors.fill: parent + propagateComposedEvents: false + acceptedButtons: Qt.RightButton + } + + TintedButton { + id: backTintedButton + + anchors.top: outgoingCallPageRect.top + anchors.topMargin: 10 + anchors.left: outgoingCallPageRect.left + anchors.leftMargin: 5 + + width: 30 + height: 30 + + tintColor: JamiTheme.buttonTintedBlue + normalPixmapSource: "qrc:/images/icons/ic_arrow_back_white_24dp.png" + selectedPixmapSource: "qrc:/images/icons/ic_arrow_back_white_24dp.png" + + onClicked: { + outgoingCallPageRect.backButtonIsClicked() + } + } + + ColumnLayout { + id: outgoingCallPageRectColumnLayout + + anchors.fill: parent + + Image { + id: contactImg + + Layout.alignment: Qt.AlignCenter + + Layout.preferredWidth: 100 + Layout.preferredHeight: 100 + + fillMode: Image.PreserveAspectFit + source: contactImgSource + asynchronous: true + } + + Rectangle { + id: outgoingCallPageTextRect + + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 5 + + Layout.preferredWidth: outgoingCallPageRect.width + Layout.preferredHeight: jamiBestNameText.height + jamiBestIdText.height + + callStatusText.height + spinnerImage.height + 20 + + color: "transparent" + + ColumnLayout { + id: outgoingCallPageTextRectColumnLayout + + Text { + id: jamiBestNameText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: outgoingCallPageTextRect.width + Layout.preferredHeight: 50 + + font.pointSize: JamiTheme.textFontSize + 3 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiBestNameText.elidedText + color: "white" + + TextMetrics { + id: textMetricsjamiBestNameText + font: jamiBestNameText.font + text: bestName + elideWidth: outgoingCallPageTextRect.width - 50 + elide: Qt.ElideMiddle + } + } + + Text { + id: jamiBestIdText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: outgoingCallPageTextRect.width + Layout.preferredHeight: 30 + + font.pointSize: JamiTheme.textFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiBestIdText.elidedText + color: Qt.lighter("white", 1.5) + + TextMetrics { + id: textMetricsjamiBestIdText + font: jamiBestIdText.font + text: bestId + elideWidth: outgoingCallPageTextRect.width - 50 + elide: Qt.ElideMiddle + } + } + + AnimatedImage { + id: spinnerImage + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: 20 + Layout.preferredHeight: 5 + + source: "qrc:/images/waiting.gif" + } + + Text { + id: callStatusText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: outgoingCallPageTextRect.width + Layout.preferredHeight: 30 + + font.pointSize: JamiTheme.textFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: callStatusPresentation + "..." + color: Qt.lighter("white", 1.5) + } + } + } + + ColumnLayout { + id: callCancelButtonColumnLayout + + Layout.alignment: Qt.AlignCenter + + HoverableButton { + id: callCancelButton + + Layout.alignment: Qt.AlignCenter + + Layout.preferredWidth: buttonPreferredSize + Layout.preferredHeight: buttonPreferredSize + + backgroundColor: JamiTheme.declineButtonRed + onEnterColor: JamiTheme.declineButtonHoverRed + onPressColor: JamiTheme.declineButtonPressedRed + onReleaseColor: JamiTheme.declineButtonHoverRed + onExitColor: JamiTheme.declineButtonRed + + buttonImageHeight: buttonPreferredSize / 2 + buttonImageWidth: buttonPreferredSize / 2 + source: "qrc:/images/icons/ic_close_white_24dp.png" + radius: 30 + + onClicked: { + outgoingCallPageRect.callCancelButtonIsClicked() + } + } + + Text { + id: cancelText + + Layout.alignment: Qt.AlignCenter + + font.pointSize: JamiTheme.textFontSize - 2 + text: qsTr("Cancel") + } + } + } +} diff --git a/src/mainview/components/ProjectCreditsScrollView.qml b/src/mainview/components/ProjectCreditsScrollView.qml new file mode 100644 index 00000000..0606f04c --- /dev/null +++ b/src/mainview/components/ProjectCreditsScrollView.qml @@ -0,0 +1,61 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + +ScrollView { + id: projectCreditsScrollView + + anchors.fill: parent + + clip: true + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + + TextEdit { + id: projectCreditsTextArea + + horizontalAlignment: Text.AlignHCenter + + width: projectCreditsScrollView.width + + selectByMouse: false + readOnly: true + wrapMode: Text.WordWrap + + font.pointSize: JamiTheme.textFontSize - 3 + text: ClientWrapper.utilsAdaptor.getProjectCredits() + textFormat: TextEdit.RichText + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + cursorShape: Qt.ArrowCursor + acceptedButtons: Qt.NoButton + } + } + + background: Rectangle { + id: projectCreditsScrollViewBackground + + radius: 5 + border.color: JamiTheme.tabbarBorderColor + } +} diff --git a/src/mainview/components/RecordBox.qml b/src/mainview/components/RecordBox.qml new file mode 100644 index 00000000..b6bc0f99 --- /dev/null +++ b/src/mainview/components/RecordBox.qml @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Albert Babí + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.3 +import QtGraphicalEffects 1.0 +import net.jami.Models 1.0 +import "../../commoncomponents" + + +Rectangle { + + enum States { + INIT, + RECORDING, + REC_SUCCESS + } + + id:recBox + color: "#FFFFFF" + width: 320 + height: 240 + radius: 5 + border.color: JamiTheme.tabbarBorderColor + + property string pathRecorder: "" + property string timeText: "00:00" + property int duration: 0 + property int state: RecordBox.States.INIT + property bool isVideo: false + property bool previewAvailable: false + + property int h_offset: -65 + property int v_offset: -65 + + function openRecorder(set_x, set_y, vid) { + focus = true + visible = true + x = set_x+(width/2)+h_offset + y = set_y-(height/2)+v_offset + + updateState(RecordBox.States.INIT) + + isVideo = vid + if (isVideo){ + ClientWrapper.accountAdaptor.startPreviewing(false) + previewAvailable = true + } + } + + onActiveFocusChanged: { + if (visible) { + closeRecorder() + } + } + + onVisibleChanged: { + if(!visible) { + closeRecorder() + } + } + + function closeRecorder() { + if (isVideo && ClientWrapper.accountAdaptor.isPreviewing()) { + ClientWrapper.accountAdaptor.stopPreviewing() + } + stopRecording() + visible = false + } + + function updateState(new_state) { + state = new_state + recordButton.visible = (state == RecordBox.States.INIT) + btnStop.visible = (state == RecordBox.States.RECORDING) + btnRestart.visible = (state == RecordBox.States.REC_SUCCESS) + btnSend.visible = (state == RecordBox.States.REC_SUCCESS) + + if (state == RecordBox.States.INIT) { + duration = 0 + time.text = "00:00" + timer.stop() + } else if (state == RecordBox.States.REC_SUCCESS) { + timer.stop() + } + } + + function startRecording() { + timer.start() + pathRecorder = ClientWrapper.avmodel.startLocalRecorder(!isVideo) + if (pathRecorder == "") { + timer.stop() + } + } + + function stopRecording() { + if (pathRecorder != "") { + ClientWrapper.avmodel.stopLocalRecorder(pathRecorder) + } + } + + function sendRecord() { + if (pathRecorder != "") { + MessagesAdapter.sendFile(pathRecorder) + } + } + + function updateTimer() { + + duration += 1 + + var m = Math.trunc(duration / 60) + var s = (duration % 60) + + var min = (m < 10) ? "0" + String(m) : String(m) + var sec = (s < 10) ? "0" + String(s) : String(s) + + time.text = min + ":" + sec; + } + + + Connections{ + target: ClientWrapper.renderManager + } + + Rectangle { + id: rectBox + visible: (isVideo && previewAvailable) + Layout.alignment: Qt.AlignHCenter + anchors.fill: parent + color: "black" + radius: 5 + + PreviewRenderer{ + id: previewWidget + anchors.fill: rectBox + anchors.centerIn: rectBox + layer.enabled: true + layer.effect: OpacityMask { + maskSource: rectBox + } + } + } + + Label { + visible: (isVideo && !previewAvailable) + + Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter + + text: qsTr("Preview unavailable") + font.pointSize: 10 + font.kerning: true + } + + Timer { + id: timer + interval: 1000; + running: false; + repeat: true + onTriggered: updateTimer() + } + + Text { + id: time + visible: true + text: "00:00" + color: (isVideo ? "white" : "black") + font.pointSize: (isVideo ? 12 : 20) + anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenterOffset: (isVideo ? 100 : 0) + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: (isVideo ? 100 : 0) + } + + + HoverableRadiusButton { + id: recordButton + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + backgroundColor: isVideo? "#000000cc" : "white" + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 5 + + radius: height / 2 + + icon.source: "qrc:/images/icons/av_icons/fiber_manual_record-24px.svg" + icon.height: 24 + icon.width: 24 + icon.color: "#dc2719" + onClicked: { + updateState(RecordBox.States.RECORDING) + startRecording() + } + } + + HoverableRadiusButton { + id: btnStop + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + backgroundColor: isVideo? "#000000cc" : "white" + + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 5 + + radius: height / 2 + + icon.source: "qrc:/images/icons/av_icons/stop-24px-red.svg" + icon.height: 24 + icon.width: 24 + icon.color: isVideo? "white" : "black" + onClicked: { + stopRecording() + updateState(RecordBox.States.REC_SUCCESS) + } + } + + HoverableRadiusButton { + id: btnRestart + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + backgroundColor: isVideo? "#000000cc" : "white" + + anchors.horizontalCenter: parent.horizontalCenter + anchors.right: btnRestart.left + anchors.rightMargin: 5 + anchors.bottom: parent.bottom + anchors.bottomMargin: 5 + + radius: height / 2 + + icon.source: "qrc:/images/icons/av_icons/re-record-24px.svg" + icon.height: 24 + icon.width: 24 + icon.color: isVideo? "white" : "black" + onClicked: { + stopRecording() + updateState(RecordBox.States.INIT) + } + } + + HoverableRadiusButton { + id: btnSend + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + backgroundColor: isVideo? "#000000cc" : "white" + + anchors.left: btnRestart.right + anchors.leftMargin: 5 + anchors.bottom: parent.bottom + anchors.bottomMargin: 5 + + radius: height / 2 + + icon.source: "qrc:/images/icons/av_icons/send-24px.svg" + icon.height: 24 + icon.width: 24 + icon.color: isVideo? "white" : "black" + onClicked: { + stopRecording() + sendRecord() + closeRecorder() + updateState(RecordBox.States.INIT) + } + } +} diff --git a/src/mainview/components/ScreenRubberBand.qml b/src/mainview/components/ScreenRubberBand.qml new file mode 100644 index 00000000..65c3266e --- /dev/null +++ b/src/mainview/components/ScreenRubberBand.qml @@ -0,0 +1,113 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import net.jami.Models 1.0 + + +/* + * ScreenRubberBand as a seperate frameless window, + * is to simulate the whole screen area and provide the user + * the ability to select certain area of it. + * + * Typically, it is used for video screen sharing. + */ +Window { + id: screenRubberBandWindow + + property int screenNumber: 0 + + flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WA_TranslucentBackground + + + /* + * Opacity with 0.7 window that will fill the entire screen, + * provide the users to select the area that they + * want to share. + */ + color: Qt.rgba(0, 0, 0, 0.7) + + + /* + * Rect for selection. + */ + Rectangle { + id: recSelect + + height: 0 + width: 0 + + border.color: JamiTheme.rubberBandSelectionBlue + border.width: 1 + color: JamiTheme.rubberBandSelectionBlue + opacity: 0.3 + visible: false + } + + MouseArea { + id: screenRubberBandMouseArea + + property int originalX: 0 + property int originalY: 0 + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.CrossCursor + + + /* + * Geo changing for user selection. + */ + onPressed: { + originalX = mouseX + originalY = mouseY + recSelect.x = mouseX + recSelect.y = mouseY + recSelect.visible = true + } + + onMouseXChanged: { + if (originalX - mouseX >= 0) { + recSelect.x = mouseX + recSelect.width = originalX - recSelect.x + } else if (mouseX - recSelect.x > 0) { + recSelect.width = mouseX - recSelect.x + } + } + + onMouseYChanged: { + if (originalY - mouseY >= 0) { + recSelect.y = mouseY + recSelect.height = originalY - recSelect.y + } else if (mouseY - recSelect.y > 0) { + recSelect.height = mouseY - recSelect.y + } + } + + onReleased: { + recSelect.visible = false + AvAdapter.shareScreenArea(screenNumber, recSelect.x, recSelect.y, + recSelect.width, recSelect.height) + screenRubberBandWindow.close() + } + } +} diff --git a/src/mainview/components/SelectScreen.qml b/src/mainview/components/SelectScreen.qml new file mode 100644 index 00000000..506af487 --- /dev/null +++ b/src/mainview/components/SelectScreen.qml @@ -0,0 +1,320 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import net.jami.Models 1.0 + +import "../js/screenrubberbandcreation.js" as ScreenRubberBandCreation + +import "../../commoncomponents" + + +/* + * SelectScreenWindow as a seperate window, + * is to make user aware of which screen they want to share, + * during the video call, if the context menu item is selected. + */ +Window { + id: selectScreenWindow + + property int minWidth: 650 + property int minHeight: 500 + + property int selectedScreenNumber: -1 + + + /* + * Decide whether to show screen area or entire screen. + */ + property bool selectArea: false + + + /* + * How many rows the ScrollView should have. + */ + function calculateRepeaterModel() { + var numberOfScreens = Qt.application.screens.length + + if (numberOfScreens % 2 === 1) + return numberOfScreens / 2 + 1 + else + return numberOfScreens / 2 + } + + function calculateScreenNumber(index) { + if (index === 0 || index === 1) + return index + if (index % 2 === 0) + return index * 2 + else + return index * 2 + 1 + } + + minimumWidth: minWidth + minimumHeight: minHeight + + title: "Screen sharing" + + + /* + * Note: Qt.application.screens[0] is the app's current existing screen. + */ + screen: Qt.application.screens[0] + + Rectangle { + id: selectScreenWindowRect + + anchors.fill: parent + + Text { + id: screenListText + + anchors.top: selectScreenWindowRect.top + anchors.topMargin: 20 + anchors.horizontalCenter: selectScreenWindowRect.horizontalCenter + + font.pointSize: JamiTheme.textFontSize + 2 + font.bold: true + text: qsTr("Choose A Screen to Share") + } + + ScrollView { + id: screenSelectionScrollView + + anchors.centerIn: selectScreenWindowRect + + width: selectScreenWindowRect.width - 50 + height: selectScreenWindowRect.height - 150 + + clip: true + + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + + + /* + * Column of rows repeater (two screen captures in a row). + */ + Column { + id: screenSelectionScrollViewColumn + + spacing: 10 + + Repeater { + id: screenInfo + + model: calculateRepeaterModel() + + Row { + id: screenInfoRow + + spacing: 20 + + Connections { + target: selectScreenWindow + + function onSelectedScreenNumberChanged() { + + + /* + * Recover from green state. + */ + screenSelectionRectOdd.borderColor = JamiTheme.tabbarBorderColor + screenSelectionRectEven.borderColor = JamiTheme.tabbarBorderColor + } + } + + + /* + * To make sure that two screen captures in one row, + * a repeater of two rect is needed, which one in charge + * of odd number screen, one in charge of even number screen. + */ + Rectangle { + id: screenSelectionRectOdd + + property string borderColor: JamiTheme.tabbarBorderColor + + height: screenSelectionScrollView.height + width: screenSelectionScrollView.width / 2 - screenInfoRow.spacing / 2 + + radius: 10 + border.color: borderColor + + Image { + id: screenShotOdd + + anchors.top: screenSelectionRectOdd.top + anchors.topMargin: 10 + anchors.horizontalCenter: screenSelectionRectOdd.horizontalCenter + + height: screenSelectionRectOdd.height - 50 + width: screenSelectionRectOdd.width - 50 + + fillMode: Image.PreserveAspectFit + mipmap: true + + Component.onCompleted: { + screenShotOdd.source = "data:image/png;base64," + + AvAdapter.captureScreen( + calculateScreenNumber(index)) + } + } + + Text { + id: screenNameOdd + + anchors.top: screenShotOdd.bottom + anchors.topMargin: 10 + anchors.horizontalCenter: screenSelectionRectOdd.horizontalCenter + + font.pointSize: JamiTheme.textFontSize - 2 + text: qsTr("Screen") + " " + (calculateScreenNumber( + index) + 1) + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + + onClicked: { + if (selectedScreenNumber == -1 + || selectedScreenNumber !== calculateScreenNumber( + index)) { + selectedScreenNumber = calculateScreenNumber( + index) + screenSelectionRectOdd.borderColor + = JamiTheme.screenSelectionBorderGreen + } + } + } + } + + Rectangle { + id: screenSelectionRectEven + + property string borderColor: JamiTheme.tabbarBorderColor + + height: screenSelectionScrollView.height + width: screenSelectionScrollView.width / 2 - screenInfoRow.spacing / 2 + + radius: 10 + border.color: borderColor + + visible: (Qt.application.screens.length) % 2 != 1 + + Image { + id: screenShotEven + + anchors.top: screenSelectionRectEven.top + anchors.topMargin: 10 + anchors.horizontalCenter: screenSelectionRectEven.horizontalCenter + + height: screenSelectionRectEven.height - 50 + width: screenSelectionRectEven.width - 50 + + fillMode: Image.PreserveAspectFit + mipmap: true + + Component.onCompleted: { + if (screenSelectionRectEven.visible) + screenShotEven.source = "data:image/png;base64," + + AvAdapter.captureScreen( + calculateScreenNumber( + index + 1)) + } + } + + Text { + id: screenNameEven + + anchors.top: screenShotEven.bottom + anchors.topMargin: 10 + anchors.horizontalCenter: screenSelectionRectEven.horizontalCenter + + font.pointSize: JamiTheme.textFontSize - 2 + text: qsTr( + "Screen") + " " + (calculateScreenNumber( + index + 1) + 1) + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton + + onClicked: { + if (selectedScreenNumber == -1 + || selectedScreenNumber !== calculateScreenNumber( + index + 1)) { + selectedScreenNumber = calculateScreenNumber( + index + 1) + screenSelectionRectEven.borderColor + = JamiTheme.screenSelectionBorderGreen + } + } + } + } + } + } + } + + background: Rectangle { + id: screenSelectionScrollViewBackground + + radius: 10 + border.color: JamiTheme.tabbarBorderColor + } + } + } + + HoverableButton { + id: selectButton + + anchors.bottom: selectScreenWindowRect.bottom + anchors.bottomMargin: 10 + anchors.horizontalCenter: selectScreenWindowRect.horizontalCenter + + visible: selectedScreenNumber != -1 + + text: qsTr("Share Screen") + radius: 10 + + onClicked: { + if (selectArea) { + selectScreenWindow.hide() + ScreenRubberBandCreation.createScreenRubberBandWindowObject( + selectScreenWindow, selectedScreenNumber) + ScreenRubberBandCreation.showScreenRubberBandWindow() + + + /* + * Destory selectScreenWindow once screenRubberBand is closed. + */ + ScreenRubberBandCreation.connectOnClosingEvent(function () { + selectScreenWindow.close() + }) + } else { + AvAdapter.shareEntireScreen(selectedScreenNumber) + selectScreenWindow.close() + } + } + } +} diff --git a/src/mainview/components/SidePanel.qml b/src/mainview/components/SidePanel.qml new file mode 100644 index 00000000..1a81d1ca --- /dev/null +++ b/src/mainview/components/SidePanel.qml @@ -0,0 +1,311 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Rectangle { + id: sidePanelRect + + property bool tabBarVisible: true + property int pendingRequestCount: 0 + property int totalUnreadMessagesCount: 0 + + signal conversationSmartListNeedToAccessMessageWebView(string currentUserDisplayName, string currentUserAlias, string currentUID, bool callStackViewShouldShow, bool isAudioOnly, string callStateStr) + signal accountComboBoxNeedToShowWelcomePage(int index) + signal conversationSmartListViewNeedToShowWelcomePage + signal accountSignalsReconnect(string accountId) + signal needToUpdateConversationForAddedContact + signal needToAddNewAccount + signal settingBtnClicked_AccountComboBox + + + /* + * Hack -> force redraw. + */ + function forceReselectConversationSmartListCurrentIndex() { + var index = conversationSmartListView.currentIndex + conversationSmartListView.currentIndex = -1 + conversationSmartListView.currentIndex = index + } + + + /* + * For contact request conv to be focused correctly. + */ + function setCurrentUidSmartListModelIndex() { + conversationSmartListView.currentIndex + = conversationSmartListView.model.currentUidSmartListModelIndex( + ) + } + + function updatePendingRequestCount() { + pendingRequestCount = ClientWrapper.utilsAdaptor.getTotalPendingRequest() + } + + function updateTotalUnreadMessagesCount() { + totalUnreadMessagesCount = ClientWrapper.utilsAdaptor.getTotalUnreadMessages() + } + + function clearContactSearchBar() { + contactSearchBar.clearText() + } + + function accountChangedUIReset() { + contactSearchBar.clearText() + contactSearchBar.setPlaceholderString( + JamiTheme.contactSearchBarPlaceHolderConversationText) + sidePanelTabBar.converstationTabDown = true + sidePanelTabBar.invitationTabDown = false + } + + function needToChangeToAccount(accountId, index) { + if (index !== -1) { + accountComboBox.currentIndex = index + ClientWrapper.accountAdaptor.accountChanged(index) + accountChangedUIReset() + } + } + + function refreshAccountComboBox(index = -1) { + accountComboBox.resetAccountListModel() + + + /* + * To make sure that the ui is refreshed for accountComboBox. + * Note: when index in -1, it means to maintain the + * current account selection. + */ + var currentIndex = accountComboBox.currentIndex + if (accountComboBox.currentIndex === index) + accountComboBox.currentIndex = -1 + accountComboBox.currentIndex = index + if (index !== -1) + ClientWrapper.accountAdaptor.accountChanged(index) + else + accountComboBox.currentIndex = currentIndex + accountComboBox.update() + accountChangedUIReset() + } + + function deselectConversationSmartList() { + ConversationsAdapter.deselectConversation() + conversationSmartListView.currentIndex = -1 + } + + function forceUpdateConversationSmartListView() { + conversationSmartListView.updateConversationSmartListView() + } + + + /* + * Intended -> since strange behavior will happen without this for stackview. + */ + anchors.fill: parent + + AccountComboBox { + id: accountComboBox + + anchors.top: sidePanelRect.top + + width: sidePanelRect.width + height: 60 + + currentIndex: 0 + + Connections { + target: ClientWrapper.accountAdaptor + + function onAccountSignalsReconnect(accountId) { + CallAdapter.connectCallStatusChanged(accountId) + ConversationsAdapter.accountChangedSetUp(accountId) + sidePanelRect.accountSignalsReconnect(accountId) + } + + function onUpdateConversationForAddedContact() { + sidePanelRect.needToUpdateConversationForAddedContact() + } + + function onAccountStatusChanged() { + accountComboBox.updateAccountListModel() + } + } + + onSettingBtnClicked: { + settingBtnClicked_AccountComboBox() + } + + onAccountChanged: { + ClientWrapper.accountAdaptor.accountChanged(index) + accountChangedUIReset() + } + + onNeedToUpdateSmartList: { + conversationSmartListView.currentIndex = -1 + conversationSmartListView.updateSmartList(accountId) + } + + onNeedToBackToWelcomePage: { + sidePanelRect.accountComboBoxNeedToShowWelcomePage(index) + } + + onNewAccountButtonClicked: { + sidePanelRect.needToAddNewAccount() + } + + Component.onCompleted: { + ClientWrapper.accountAdaptor.setQmlObject(this) + ClientWrapper.accountAdaptor.accountChanged(0) + } + } + + SidePanelTabBar { + id: sidePanelTabBar + + anchors.top: accountComboBox.bottom + anchors.topMargin: 20 + anchors.left: sidePanelRect.left + anchors.leftMargin: tabBarLeftMargin + + width: sidePanelRect.width + height: Math.max(sidePanelTabBar.converstationTabHeight, + sidePanelTabBar.invitationTabHeight) + } + + Rectangle { + id: sidePanelColumnRect + + anchors.top: sidePanelTabBar.bottom + anchors.topMargin: -12 + + width: sidePanelRect.width + height: sidePanelRect.height - accountComboBox.height - sidePanelTabBar.height + + border.color: JamiTheme.tabbarBorderColor + radius: 10 + + Rectangle { + id: hideTopBoarderRect + + anchors.top: sidePanelColumnRect.top + anchors.left: sidePanelColumnRect.left + anchors.leftMargin: tabBarLeftMargin + 5 + + width: sidePanelTabBar.converstationTabWidth + sidePanelTabBar.invitationTabWidth - 9 + height: 1 + + visible: tabBarVisible + + color: "white" + } + + ColumnLayout { + id: columnLayoutView + + anchors.centerIn: sidePanelColumnRect + + width: sidePanelColumnRect.width + height: sidePanelColumnRect.height - 20 + + + /* + * Search bar container to embed search label + */ + ContactSearchBar { + id: contactSearchBar + + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 10 + Layout.rightMargin: 5 + Layout.leftMargin: 5 + Layout.preferredWidth: parent.width - 10 + Layout.preferredHeight: 35 + + onContactSearchBarTextChanged: { + ClientWrapper.utilsAdaptor.setConversationFilter(text) + } + } + + ConversationSmartListView { + id: conversationSmartListView + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: parent.width + Layout.preferredHeight: parent.height - contactSearchBar.height - 30 + + Connections { + target: ConversationsAdapter + + function onShowChatView(accountId, convUid) { + conversationSmartListView.needToShowChatView(accountId, + convUid) + } + + function onShowConversationTabs(visible) { + tabBarVisible = visible + updatePendingRequestCount() + updateTotalUnreadMessagesCount() + } + } + + onNeedToSelectItems: { + ConversationsAdapter.selectConversation(index) + } + + onNeedToBackToWelcomePage: { + sidePanelRect.conversationSmartListViewNeedToShowWelcomePage() + } + + onNeedToAccessMessageWebView: { + sidePanelRect.conversationSmartListNeedToAccessMessageWebView( + currentUserDisplayName, currentUserAlias, + currentUID, callStackViewShouldShow, + isAudioOnly, callStateStr) + } + + onNeedToGrabFocus: { + contactSearchBar.clearFocus() + } + + Component.onCompleted: { + ConversationsAdapter.setQmlObject(this) + conversationSmartListView.currentIndex = -1 + } + } + } + } + + onTabBarVisibleChanged: { + if (!tabBarVisible) { + sidePanelTabBar.height = 0 + sidePanelTabBar.anchors.topMargin = 12 + sidePanelColumnRect.border.width = 0 + } else { + sidePanelTabBar.height = Qt.binding(function () { + return Math.max(sidePanelTabBar.converstationTabHeight, + sidePanelTabBar.invitationTabHeight) + }) + sidePanelTabBar.anchors.topMargin = 20 + sidePanelColumnRect.border.width = 1 + } + } +} diff --git a/src/mainview/components/SidePanelTabBar.qml b/src/mainview/components/SidePanelTabBar.qml new file mode 100644 index 00000000..d8197e1f --- /dev/null +++ b/src/mainview/components/SidePanelTabBar.qml @@ -0,0 +1,199 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +TabBar { + id: tabBar + + property alias converstationTabDown: pageOne.down + property alias invitationTabDown: pageTwo.down + property alias converstationTabWidth: pageOne.width + property alias invitationTabWidth: pageTwo.width + property alias converstationTabHeight: pageOne.height + property alias invitationTabHeight: pageTwo.height + + visible: tabBarVisible + + currentIndex: 0 + + TabButton { + id: pageOne + + width: tabBar.width / 2 - tabButtonShrinkSize + height: textConvElement.height + 10 + + down: true + + Rectangle { + id: totalUnreadMessagesCountRect + + anchors.right: pageOne.right + anchors.rightMargin: 5 + anchors.bottom: pageOne.bottom + anchors.bottomMargin: pageOne.height - totalUnreadMessagesCountRect.height / 2 + + width: 14 + height: 14 + + visible: totalUnreadMessagesCount > 0 + + Text { + id: totalUnreadMessagesCountText + + anchors.centerIn: totalUnreadMessagesCountRect + + text: totalUnreadMessagesCount > 9 ? "···" : totalUnreadMessagesCount + color: "white" + } + + radius: 30 + color: JamiTheme.notificationRed + } + + background: Rectangle { + id: buttonRectOne + + radius: 10 + width: pageOne.width + 2 + color: pageOne.down ? "white" : JamiTheme.releaseColor + border.color: JamiTheme.tabbarBorderColor + + Text { + id: textConvElement + + anchors.centerIn: buttonRectOne + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: qsTr("Converstation") + font.pointSize: JamiTheme.textFontSize + opacity: enabled ? 1.0 : 0.3 + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onPressed: { + buttonRectOne.color = JamiTheme.pressColor + ConversationsAdapter.setConversationFilter("") + contactSearchBar.setPlaceholderString( + JamiTheme.contactSearchBarPlaceHolderConversationText) + pageOne.down = true + pageTwo.down = false + setCurrentUidSmartListModelIndex() + forceReselectConversationSmartListCurrentIndex() + } + onReleased: { + buttonRectOne.color = JamiTheme.releaseColor + } + onEntered: { + buttonRectOne.color = JamiTheme.hoverColor + } + onExited: { + buttonRectOne.color = Qt.binding(function () { + return pageOne.down ? "white" : JamiTheme.releaseColor + }) + } + } + } + } + + TabButton { + id: pageTwo + + width: tabBar.width / 2 - tabButtonShrinkSize + height: textInvElement.height + 10 + + Rectangle { + id: pendingRequestCountRect + + anchors.right: pageTwo.right + anchors.rightMargin: 5 + anchors.bottom: pageTwo.bottom + anchors.bottomMargin: pageTwo.height - pendingRequestCountRect.height / 2 + + width: 14 + height: 14 + + visible: pendingRequestCount > 0 + + Text { + id: pendingRequestCountText + + anchors.centerIn: pendingRequestCountRect + + text: pendingRequestCount > 9 ? "···" : pendingRequestCount + color: "white" + } + + radius: 30 + color: JamiTheme.notificationRed + } + + background: Rectangle { + id: buttonRectTwo + + radius: 10 + color: pageTwo.down ? "white" : JamiTheme.releaseColor + border.color: JamiTheme.tabbarBorderColor + + Text { + id: textInvElement + + anchors.centerIn: buttonRectTwo + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + font.pointSize: JamiTheme.textFontSize + + text: qsTr("Invitation") + opacity: enabled ? 1.0 : 0.3 + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onPressed: { + buttonRectTwo.color = JamiTheme.pressColor + ConversationsAdapter.setConversationFilter("PENDING") + contactSearchBar.setPlaceholderString( + JamiTheme.contactSearchBarPlaceHolderInivitionText) + pageTwo.down = true + pageOne.down = false + } + onReleased: { + buttonRectTwo.color = JamiTheme.releaseColor + } + onEntered: { + buttonRectTwo.color = JamiTheme.hoverColor + } + onExited: { + buttonRectTwo.color = Qt.binding(function () { + return pageTwo.down ? "white" : JamiTheme.releaseColor + }) + } + } + } + } +} diff --git a/src/mainview/components/UserProfile.qml b/src/mainview/components/UserProfile.qml new file mode 100644 index 00000000..072d9a6e --- /dev/null +++ b/src/mainview/components/UserProfile.qml @@ -0,0 +1,170 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Dialog { + id: userProfileDialog + + property string responsibleConvUid: "" + property string contactPicBase64: "" + property string aliasText: "" + property string registeredNameText: "" + property string idText: "" + + modal: true + + + /* + * Content height + margin. + */ + contentHeight: userProfileDialogUpperPartColumnLayout.implicitHeight + 30 + + + /* + * Fake focus to make sure that text edit lose focus on close. + */ + FocusScope { + id: fakeFocusTextEdit + } + + ColumnLayout { + id: userProfileDialogUpperPartColumnLayout + + anchors.centerIn: parent + + spacing: 15 + + Image { + id: contactImage + + Layout.alignment: Qt.AlignCenter + + width: 150 + height: 150 + + fillMode: Image.PreserveAspectFit + mipmap: true + } + + + /* + * Visible when user alias is not empty or equals to id. + */ + Text { + id: contactAlias + + Layout.alignment: Qt.AlignCenter + + font.pointSize: JamiTheme.textFontSize + text: textMetricsContactAliasText.elidedText + visible: aliasText ? (aliasText === idText ? false : true) : false + TextMetrics { + id: textMetricsContactAliasText + font: contactAlias.font + text: aliasText + elideWidth: userProfileDialog.width - 30 + elide: Qt.ElideMiddle + } + } + + + /* + * Visible when user name is not empty or equals to alias. + */ + Text { + id: contactDisplayName + + Layout.alignment: Qt.AlignCenter + + font.pointSize: JamiTheme.textFontSize - 1 + text: textMetricsContactDisplayNameText.elidedText + visible: registeredNameText ? (registeredNameText === aliasText ? false : true) : false + color: JamiTheme.faddedFontColor + + TextMetrics { + id: textMetricsContactDisplayNameText + font: contactDisplayName.font + text: registeredNameText + elideWidth: userProfileDialog.width - 30 + elide: Qt.ElideMiddle + } + } + + TextEdit { + id: contactId + + Layout.alignment: Qt.AlignCenter + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + selectByMouse: true + readOnly: true + font.pointSize: JamiTheme.textFontSize - 1 + text: textMetricsContactIdText.elidedText + + TextMetrics { + id: textMetricsContactIdText + font: contactId.font + text: idText + elideWidth: userProfileDialog.width - 30 + elide: Qt.ElideMiddle + } + } + + Image { + id: contactQrImage + + Layout.alignment: Qt.AlignBottom | Qt.AlignCenter + + width: 150 + height: 150 + + fillMode: Image.PreserveAspectFit + sourceSize.width: 150 + sourceSize.height: 150 + mipmap: true + } + } + + background: Rectangle { + border.width: 0 + radius: 10 + } + + onClosed: { + contactId.deselect() + fakeFocusTextEdit.focus = true + } + + onResponsibleConvUidChanged: { + if (responsibleConvUid !== "") + contactQrImage.source = "image://qrImage/contact_" + responsibleConvUid + } + + onContactPicBase64Changed: { + if (contactPicBase64 !== "") + contactImage.source = "data:image/png;base64," + contactPicBase64 + } +} diff --git a/src/mainview/components/VideoCallFullScreenWindowContainer.qml b/src/mainview/components/VideoCallFullScreenWindowContainer.qml new file mode 100644 index 00000000..c63e574f --- /dev/null +++ b/src/mainview/components/VideoCallFullScreenWindowContainer.qml @@ -0,0 +1,40 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Window 2.14 + +Window { + id: videoWindow + + function setAsChild(obj) { + obj.parent = containerRect + } + + flags: Qt.FramelessWindowHint + + screen: Qt.application.screens[0] + + visible: false + + Rectangle { + id: containerRect + + anchors.fill: parent + } +} diff --git a/src/mainview/components/VideoCallPage.qml b/src/mainview/components/VideoCallPage.qml new file mode 100644 index 00000000..e1f29b9b --- /dev/null +++ b/src/mainview/components/VideoCallPage.qml @@ -0,0 +1,375 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Rectangle { + id: videoCallPageRect + + property string bestName: "Best Name" + property string bestId: "Best Id" + property variant clickPos: "1,1" + property int previewMargin: 15 + property int previewToX: 0 + property int previewToY: 0 + + property var corrspondingMessageWebView: null + + signal videoCallPageBackButtonIsClicked + signal needToShowInFullScreen + + function updateUI(accountId, convUid) { + bestName = ClientWrapper.utilsAdaptor.getBestName(accountId, convUid) + + var id = ClientWrapper.utilsAdaptor.getBestId(accountId, convUid) + bestId = (bestName !== id) ? id : "" + } + + function setDistantRendererId(id) { + distantRenderer.setRendererId(id) + } + + function setVideoCallPageCorrspondingMessageWebView(webViewId) { + corrspondingMessageWebView = webViewId + corrspondingMessageWebView.needToHideConversationInCall.disconnect( + closeInCallConversation) + corrspondingMessageWebView.needToHideConversationInCall.connect( + closeInCallConversation) + } + + function closeInCallConversation() { + if (inVideoCallMessageWebViewStack.visible) { + corrspondingMessageWebView.resetMessagingHeaderBackButtonSource( + true) + corrspondingMessageWebView.setMessagingHeaderButtonsVisible(true) + inVideoCallMessageWebViewStack.visible = false + inVideoCallMessageWebViewStack.clear() + } + } + + function closeContextMenuAndRelatedWindows() { + videoCallPageContextMenu.closePotentialWindows() + videoCallPageContextMenu.close() + videoCallOverlay.closePotentialContactPicker() + } + + function previewMagneticSnap() { + + + /* + * Calculate the position where the previewRenderer should attach to. + */ + var previewRendererCenter = Qt.point( + previewRenderer.x + previewRenderer.width / 2, + previewRenderer.y + previewRenderer.height / 2) + var distantRendererCenter = Qt.point( + distantRenderer.x + distantRenderer.width / 2, + distantRenderer.y + distantRenderer.height / 2) + + if (previewRendererCenter.x >= distantRendererCenter.x) { + if (previewRendererCenter.y >= distantRendererCenter.y) { + + + /* + * Bottom right. + */ + previewToX = Qt.binding(function () { + return videoCallPageMainRect.width - previewRenderer.width - previewMargin + }) + previewToY = Qt.binding(function () { + return videoCallPageMainRect.height - previewRenderer.height - previewMargin + }) + } else { + + + /* + * Top right. + */ + previewToX = Qt.binding(function () { + return videoCallPageMainRect.width - previewRenderer.width - previewMargin + }) + previewToY = previewMargin + } + } else { + if (previewRendererCenter.y >= distantRendererCenter.y) { + + + /* + * Bottom left. + */ + previewToX = previewMargin + previewToY = Qt.binding(function () { + return videoCallPageMainRect.height - previewRenderer.height - previewMargin + }) + } else { + + + /* + * Top left. + */ + previewToX = previewMargin + previewToY = previewMargin + } + } + previewRenderer.state = "geoChanging" + } + + function setCallOverlayBackButtonVisible(visible) { + videoCallOverlay.setBackTintedButtonVisible(visible) + } + + anchors.fill: parent + + SplitView { + id: mainColumnLayout + + anchors.fill: parent + + orientation: Qt.Vertical + + handle: Rectangle { + implicitWidth: videoCallPageRect.width + implicitHeight: JamiTheme.splitViewHandlePreferedWidth + color: SplitHandle.pressed ? JamiTheme.pressColor : (SplitHandle.hovered ? JamiTheme.hoverColor : JamiTheme.tabbarBorderColor) + } + + Rectangle { + id: videoCallPageMainRect + + SplitView.preferredHeight: (videoCallPageRect.height / 3) * 2 + SplitView.minimumHeight: videoCallPageRect.height / 2 + 20 + SplitView.fillWidth: true + + CallOverlay { + id: videoCallOverlay + + anchors.fill: parent + + Connections { + target: CallAdapter + + function onUpdateTimeText(time) { + videoCallOverlay.timeText = time + } + + function onUpdateOverlay(isPaused, isAudioOnly, isAudioMuted, isVideoMuted, isRecording, isSIP, isConferenceCall, bestName) { + videoCallOverlay.showOnHoldImage(isPaused) + videoCallOverlay.updateButtonStatus(isPaused, + isAudioOnly, + isAudioMuted, + isVideoMuted, + isRecording, isSIP, + isConferenceCall) + videoCallOverlay.bestName = bestName + } + + function onShowOnHoldLabel(isPaused) { + videoCallOverlay.showOnHoldImage(isPaused) + } + } + + onBackButtonIsClicked: { + if (inVideoCallMessageWebViewStack.visible) { + corrspondingMessageWebView.resetMessagingHeaderBackButtonSource( + true) + corrspondingMessageWebView.setMessagingHeaderButtonsVisible( + true) + inVideoCallMessageWebViewStack.visible = false + inVideoCallMessageWebViewStack.clear() + } + videoCallPageRect.videoCallPageBackButtonIsClicked() + } + + onOverlayChatButtonClicked: { + if (inVideoCallMessageWebViewStack.visible) { + corrspondingMessageWebView.resetMessagingHeaderBackButtonSource( + true) + corrspondingMessageWebView.setMessagingHeaderButtonsVisible( + true) + inVideoCallMessageWebViewStack.visible = false + inVideoCallMessageWebViewStack.clear() + } else { + corrspondingMessageWebView.resetMessagingHeaderBackButtonSource( + false) + corrspondingMessageWebView.setMessagingHeaderButtonsVisible( + false) + inVideoCallMessageWebViewStack.visible = true + inVideoCallMessageWebViewStack.push( + corrspondingMessageWebView) + } + } + } + + DistantRenderer { + id: distantRenderer + + anchors.centerIn: videoCallPageMainRect + z: -1 + + width: videoCallPageMainRect.width + height: videoCallPageMainRect.height + } + + VideoCallPreviewRenderer { + id: previewRenderer + + + /* + * Property is used in the {} expression for height (extra dependency), + * it will not affect the true height expression, since expression + * at last will be taken only, but it will force the height to update + * and reevaluate getPreviewImageScalingFactor(). + */ + property int previewImageScalingFactorUpdated: 0 + + Connections { + target: CallAdapter + + function onPreviewVisibilityNeedToChange(visible) { + previewRenderer.visible = visible + } + } + + width: videoCallPageMainRect.width / 4 + height: { + previewImageScalingFactorUpdated + return previewRenderer.width * previewRenderer.getPreviewImageScalingFactor() + } + x: videoCallPageMainRect.width - previewRenderer.width - previewMargin + y: videoCallPageMainRect.height - previewRenderer.height - previewMargin + z: -1 + + states: [ + State { + name: "geoChanging" + PropertyChanges { + target: previewRenderer + x: previewToX + y: previewToY + } + } + ] + + transitions: Transition { + PropertyAnimation { + properties: "x,y" + easing.type: Easing.OutExpo + duration: 250 + + onStopped: { + previewRenderer.state = "" + } + } + } + + MouseArea { + id: dragMouseArea + + anchors.fill: previewRenderer + + onPressed: { + clickPos = Qt.point(mouse.x, mouse.y) + } + + onReleased: { + previewRenderer.state = "" + previewMagneticSnap() + } + + onPositionChanged: { + + + /* + * Calculate mouse position relative change. + */ + var delta = Qt.point(mouse.x - clickPos.x, + mouse.y - clickPos.y) + var deltaW = previewRenderer.x + delta.x + previewRenderer.width + var deltaH = previewRenderer.y + delta.y + previewRenderer.height + + + /* + * Check if the previewRenderer exceeds the border of videoCallPageMainRect. + */ + if (deltaW < videoCallPageMainRect.width + && previewRenderer.x + delta.x > 1) + previewRenderer.x += delta.x + if (deltaH < videoCallPageMainRect.height + && previewRenderer.y + delta.y > 1) + previewRenderer.y += delta.y + } + } + + onPreviewImageAvailable: { + previewImageScalingFactorUpdated++ + previewImageScalingFactorUpdated-- + } + } + + color: "transparent" + } + + StackView { + id: inVideoCallMessageWebViewStack + + SplitView.preferredHeight: videoCallPageRect.height / 3 + SplitView.fillWidth: true + + visible: false + + clip: true + } + } + + VideoCallPageContextMenu { + id: videoCallPageContextMenu + + onFullScreenNeeded: { + videoCallPageRect.needToShowInFullScreen() + } + } + + MouseArea { + anchors.fill: parent + + propagateComposedEvents: true + acceptedButtons: Qt.RightButton + + onClicked: { + + + /* + * Make menu pos at mouse. + */ + var relativeMousePos = mapToItem(videoCallPageRect, + mouse.x, mouse.y) + videoCallPageContextMenu.x = relativeMousePos.x + videoCallPageContextMenu.y = relativeMousePos.y + videoCallPageContextMenu.activate() + } + } + + color: "black" +} diff --git a/src/mainview/components/VideoCallPageContextMenu.qml b/src/mainview/components/VideoCallPageContextMenu.qml new file mode 100644 index 00000000..642ec244 --- /dev/null +++ b/src/mainview/components/VideoCallPageContextMenu.qml @@ -0,0 +1,209 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +import "../js/videodevicecontextmenuitemcreation.js" as VideoDeviceContextMenuItemCreation +import "../js/selectscreenwindowcreation.js" as SelectScreenWindowCreation +import "../js/screenrubberbandcreation.js" as ScreenRubberBandCreation + +Menu { + id: contextMenu + + property string responsibleAccountId: "" + property string responsibleConvUid: "" + + property int generalMenuSeparatorCount: 0 + property int commonBorderWidth: 2 + + signal fullScreenNeeded + + function activate() { + var deviceContextMenuInfoMap = AvAdapter.populateVideoDeviceContextMenuItem() + + + /* + * Somehow, the map size is undefined, so use this instead. + */ + var mapSize = deviceContextMenuInfoMap["size"] + + var count = 1 + for (var deviceName in deviceContextMenuInfoMap) { + if (deviceName === "size") + continue + if (videoDeviceItem.itemName === "No video device") { + videoDeviceItem.checkable = true + videoDeviceItem.itemName = deviceName + videoDeviceItem.checked = deviceContextMenuInfoMap[deviceName] + if (count === mapSize) + contextMenu.open() + } else { + VideoDeviceContextMenuItemCreation.createVideoDeviceContextMenuItemObjects( + deviceName, deviceContextMenuInfoMap[deviceName], + count === mapSize) + } + count++ + } + } + + function closePotentialWindows() { + SelectScreenWindowCreation.destorySelectScreenWindow() + ScreenRubberBandCreation.destoryScreenRubberBandWindow() + } + + implicitWidth: 200 + + JamiFileDialog { + id: jamiFileDialog + + mode: JamiFileDialog.Mode.OpenFile + + onAccepted: { + var filePath = jamiFileDialog.file + + + /* + * No need to trim file:///. + */ + AvAdapter.shareFile(filePath) + } + } + + + /* + * All GeneralMenuItems should remain the same width / height. + * The first videoDeviceItem is to make sure the border is correct. + */ + VideoCallPageContextMenuDeviceItem { + id: videoDeviceItem + + topBorderWidth: commonBorderWidth + contextMenuPreferredWidth: contextMenu.implicitWidth + } + + GeneralMenuSeparator { + preferredWidth: videoDeviceItem.preferredWidth + preferredHeight: commonBorderWidth + + Component.onCompleted: { + generalMenuSeparatorCount++ + } + } + + GeneralMenuItem { + id: shareEntireScreenItem + + itemName: qsTr("Share entire screen") + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + if (Qt.application.screens.length === 1) { + AvAdapter.shareEntireScreen(0) + } else { + SelectScreenWindowCreation.createSelectScreenWindowObject() + SelectScreenWindowCreation.showSelectScreenWindow() + } + } + } + + GeneralMenuItem { + id: shareScreenAreaItem + + itemName: qsTr("Share screen area") + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + if (Qt.application.screens.length === 1) { + ScreenRubberBandCreation.createScreenRubberBandWindowObject( + null, 0) + ScreenRubberBandCreation.showScreenRubberBandWindow() + } else { + SelectScreenWindowCreation.createSelectScreenWindowObject(true) + SelectScreenWindowCreation.showSelectScreenWindow() + } + } + } + + GeneralMenuItem { + id: shareFileItem + + itemName: qsTr("Share file") + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + jamiFileDialog.open() + } + } + + GeneralMenuSeparator { + preferredWidth: videoDeviceItem.preferredWidth + preferredHeight: commonBorderWidth + + Component.onCompleted: { + generalMenuSeparatorCount++ + } + } + + GeneralMenuItem { + id: fullScreenItem + + property bool isFullScreen: false + + itemName: isFullScreen ? qsTr("Exit full screen") : qsTr( + "Full screen mode") + iconSource: isFullScreen ? "qrc:/images/icons/ic_exit_full_screen_black.png" : "qrc:/images/icons/ic_full_screen_black.png" + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + bottomBorderWidth: commonBorderWidth + + onClicked: { + contextMenu.close() + contextMenu.fullScreenNeeded() + isFullScreen = !isFullScreen + } + } + + onClosed: { + videoDeviceItem.itemName = "No video device" + VideoDeviceContextMenuItemCreation.removeCreatedItems() + } + + Component.onCompleted: { + VideoDeviceContextMenuItemCreation.setVideoContextMenuObject( + contextMenu) + } + + background: Rectangle { + implicitWidth: contextMenu.implicitWidth + implicitHeight: videoDeviceItem.preferredHeight + * (contextMenu.count - generalMenuSeparatorCount) + + border.width: commonBorderWidth + border.color: JamiTheme.tabbarBorderColor + } +} diff --git a/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml b/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml new file mode 100644 index 00000000..f94a9979 --- /dev/null +++ b/src/mainview/components/VideoCallPageContextMenuDeviceItem.qml @@ -0,0 +1,94 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + + +/* + * Take advantage of child can access parent's item (ex: contextMenu, commonBorderWidth). + */ +GeneralMenuItem { + id: videoCallPageContextMenuDeviceItem + + property int contextMenuPreferredWidth: 250 + + itemName: qsTr("No video device") + leftBorderWidth: commonBorderWidth + rightBorderWidth: commonBorderWidth + + TextMetrics { + id: textMetrics + font: deviceNameText.font + elide: Text.ElideMiddle + elideWidth: contextMenuPreferredWidth + - videoCallPageContextMenuDeviceItem.implicitIndicatorWidth + text: videoCallPageContextMenuDeviceItem.itemName + } + + contentItem: Text { + id: deviceNameText + + leftPadding: 30 + rightPadding: videoCallPageContextMenuDeviceItem.arrow.width + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + font.pointSize: JamiTheme.textFontSize - 3 + text: textMetrics.elidedText + } + + indicator: Item { + id: selectItem + + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + + implicitWidth: 32 + implicitHeight: 32 + + Rectangle { + id: selectRect + + width: selectItem.width / 2 + height: selectItem.height / 2 + anchors.centerIn: parent + visible: videoCallPageContextMenuDeviceItem.checkable + border.color: JamiTheme.selectionGreen + radius: 3 + Rectangle { + width: selectRect.width / 2 + height: selectRect.height / 2 + anchors.centerIn: parent + visible: videoCallPageContextMenuDeviceItem.checked + color: JamiTheme.selectionGreen + radius: 2 + } + } + } + + onClicked: { + var deviceName = videoCallPageContextMenuDeviceItem.itemName + contextMenu.close() + AvAdapter.onVideoContextMenuDeviceItemClicked(deviceName) + } +} diff --git a/src/mainview/components/WelcomePage.qml b/src/mainview/components/WelcomePage.qml new file mode 100644 index 00000000..a6250d00 --- /dev/null +++ b/src/mainview/components/WelcomePage.qml @@ -0,0 +1,234 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Rectangle { + id: welcomeRect + + property int currentAccountIndex: 0 + property int buttonPreferredSize: 30 + + anchors.fill: parent + + Rectangle { + id: welcomeRectComponentsGroup + + anchors.centerIn: parent + + width: Math.max(welcomePageGroupPreferedWidth, welcomeRect.width - 100) + height: mainViewWindow.minimumHeight + + ColumnLayout { + id: welcomeRectComponentsGroupColumnLayout + + Image { + id: jamiLogoImage + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: welcomeRectComponentsGroup.width + Layout.preferredHeight: 100 + Layout.topMargin: 50 + Layout.bottomMargin: 10 + + fillMode: Image.PreserveAspectFit + source: "qrc:/images/logo-jami-standard-coul.png" + mipmap: true + } + + Label { + id: jamiIntroText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: welcomeRectComponentsGroup.width + Layout.preferredHeight: 100 + Layout.bottomMargin: 5 + + wrapMode: Text.WordWrap + font.pointSize: JamiTheme.textFontSize + 1 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: qsTr("Jami is a free software for universal communication which repects the freedoms and privacy of its user.") + } + + Label { + id: jamiShareWithFriendText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: welcomeRectComponentsGroup.width + Layout.preferredHeight: 50 + + wrapMode: Text.WordWrap + font.pointSize: JamiTheme.textFontSize - 1 + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + visible: accountListModel.data(accountListModel.index( + currentAccountIndex, 0), + 260) === 1 + text: qsTr("This is your ID.\nCopy and share it with your friends") + color: JamiTheme.faddedFontColor + } + + Rectangle { + id: jamiRegisteredNameRect + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: welcomeRectComponentsGroup.width + Layout.preferredHeight: 65 + Layout.bottomMargin: 5 + + visible: accountListModel.data(accountListModel.index( + currentAccountIndex, 0), + 260) === 1 + + ColumnLayout { + id: jamiRegisteredNameRectColumnLayout + + spacing: 0 + + Text { + id: jamiRegisteredNameText + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: welcomeRectComponentsGroup.width + Layout.preferredHeight: 30 + + font.pointSize: JamiTheme.textFontSize + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + text: textMetricsjamiRegisteredNameText.elidedText + + TextMetrics { + id: textMetricsjamiRegisteredNameText + + font: jamiRegisteredNameText.font + text: accountListModel.data( + accountListModel.index( + currentAccountIndex, 0), 258) + elideWidth: welcomeRectComponentsGroup.width + elide: Qt.ElideMiddle + } + } + + RowLayout { + id: jamiRegisteredNameRowLayout + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: buttonPreferredSize * 2 + 20 + Layout.preferredHeight: 30 + + HoverableButton { + id: copyRegisterednameButton + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: buttonPreferredSize + Layout.preferredHeight: buttonPreferredSize + + radius: 30 + source: "qrc:/images/icons/ic_content_copy.svg" + + onClicked: { + ClientWrapper.utilsAdaptor.setText( + textMetricsjamiRegisteredNameText.text) + } + } + + HoverableButton { + id: qrCodeGenerateButton + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: buttonPreferredSize + Layout.preferredHeight: buttonPreferredSize + + radius: 30 + source: "qrc:/images/qrcode.png" + + onClicked: { + qrDialog.open() + } + } + } + } + } + + Image { + id: sipImage + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: 200 + Layout.preferredHeight: 200 + + + /* + * Check if account type is ring. + */ + visible: accountListModel.data(accountListModel.index( + currentAccountIndex, 0), + 260) === 1 ? false : true + fillMode: Image.PreserveAspectFit + + + /* + * Requested size. + */ + sourceSize.width: 200 + sourceSize.height: 200 + mipmap: true + } + } + + HoverableButton { + id: aboutButton + + anchors.horizontalCenter: welcomeRectComponentsGroup.horizontalCenter + anchors.bottom: welcomeRectComponentsGroup.bottom + anchors.bottomMargin: 5 + + width: 80 + height: buttonPreferredSize + + text: qsTr("About") + fontPointSize: JamiTheme.textFontSize + radius: 10 + + onClicked: { + aboutPopUpDialog.open() + } + } + } + + CustomBorder { + commonBorder: false + lBorderwidth: 1 + rBorderwidth: 0 + tBorderwidth: 0 + bBorderwidth: 0 + borderColor: JamiTheme.tabbarBorderColor + } +} diff --git a/src/mainview/components/WelcomePageQrDialog.qml b/src/mainview/components/WelcomePageQrDialog.qml new file mode 100644 index 00000000..45c8ff28 --- /dev/null +++ b/src/mainview/components/WelcomePageQrDialog.qml @@ -0,0 +1,61 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import net.jami.Models 1.0 + +Dialog { + id: userQrImageDialog + + property int currentAccountIndex: 0 + + + /* + * When dialog is opened, trigger mainViewWindow overlay which is defined in overlay.model. + * (model : true is necessary) + */ + modal: true + + + /* + * Content height + margin. + */ + contentHeight: userQrImage.height + 30 + + Image { + id: userQrImage + + anchors.centerIn: parent + + width: 250 + height: 250 + + fillMode: Image.PreserveAspectFit + source: "image://qrImage/account_" + currentAccountIndex + sourceSize.width: 260 + sourceSize.height: 260 + mipmap: true + } + + background: Rectangle { + border.width: 0 + radius: 10 + } +} diff --git a/src/mainview/js/contactpickercreation.js b/src/mainview/js/contactpickercreation.js new file mode 100644 index 00000000..7ce7b62e --- /dev/null +++ b/src/mainview/js/contactpickercreation.js @@ -0,0 +1,80 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/* + * Global contact picker component, object variable for creation. + */ +var contactPickerComponent +var contactPickerObject + +function createContactPickerObjects(type, parent) { + if (contactPickerObject) { + + + /* + * If already created, reset parameters, since object cannot be destroyed. + */ + contactPickerObject.parent = parent + contactPickerObject.type = type + return + } + contactPickerComponent = Qt.createComponent( + "../components/ContactPicker.qml") + if (contactPickerComponent.status === Component.Ready) + finishCreation(type, parent) + else if (contactPickerComponent.status === Component.Error) + console.log("Error loading component:", + contactPickerComponent.errorString()) +} + +function finishCreation(type, parent) { + contactPickerObject = contactPickerComponent.createObject(parent, { + "type": type + }) + if (contactPickerObject === null) { + + + /* + * Error Handling. + */ + console.log("Error creating object for contact picker") + } +} + + +/* + * Put contact picker in the middle of container. + */ +function calculateCurrentGeo(containerX, containerY) { + if (contactPickerObject) { + contactPickerObject.x = containerX - contactPickerObject.width / 2 + contactPickerObject.y = containerY - contactPickerObject.height / 2 + } +} + +function openContactPicker() { + if (contactPickerObject) + contactPickerObject.open() +} + +function closeContactPicker() { + if (contactPickerObject) + contactPickerObject.close() +} diff --git a/src/mainview/js/incomingcallpagecreation.js b/src/mainview/js/incomingcallpagecreation.js new file mode 100644 index 00000000..6e0fcb0d --- /dev/null +++ b/src/mainview/js/incomingcallpagecreation.js @@ -0,0 +1,120 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/* + * Global incomingCallPage storage map>. + */ +let incomingCallPageWindowMap = new Map() + + +/* + * Global incomingCallPage component, object variable for creation. + */ +var incomingCallPageWindowComponent +var incomingCallPageWindowObject + +function createincomingCallPageWindowObjects(accountId, convUid) { + + + /* + * Check if the corrsponding call exists or not. + */ + if (incomingCallPageWindowMap.has(accountId)) { + if (incomingCallPageWindowMap.get(accountId).has(convUid)) { + return + } + } + + incomingCallPageWindowComponent = Qt.createComponent( + "../components/IncomingCallPage.qml") + if (incomingCallPageWindowComponent.status === Component.Ready) + finishCreation(accountId, convUid) + else if (incomingCallPageWindowComponent.status === Component.Error) + console.log("Error loading component:", + incomingCallPageWindowComponent.errorString()) +} + +function finishCreation(accountId, convUid) { + incomingCallPageWindowObject = incomingCallPageWindowComponent.createObject( + ) + if (incomingCallPageWindowObject === null) { + + + /* + * Error Handling. + */ + console.log("Error creating object for accountId" + accountId) + } + + incomingCallPageWindowObject.responsibleConvUid = convUid + incomingCallPageWindowObject.responsibleAccountId = accountId + incomingCallPageWindowObject.updateUI() + + + /* + * Record in map. + */ + if (incomingCallPageWindowMap.has(accountId)) { + incomingCallPageWindowMap.get(accountId).set( + convUid, incomingCallPageWindowObject) + } else { + let incomingCallPageWindowTempMap = new Map() + incomingCallPageWindowTempMap.set(convUid, incomingCallPageWindowObject) + incomingCallPageWindowMap.set(accountId, incomingCallPageWindowTempMap) + } +} + +function showIncomingCallPageWindow(accountId, convUid) { + if (incomingCallPageWindowMap.has(accountId)) { + if (incomingCallPageWindowMap.get(accountId).has(convUid)) { + if (!incomingCallPageWindowMap.get(accountId).get( + convUid).visible) { + incomingCallPageWindowMap.get(accountId).get(convUid).show() + incomingCallPageWindowMap.get(accountId).get( + convUid).updatePositionToRightBottom() + } + } + } +} + +function closeIncomingCallPageWindow(accountId, convUid) { + if (incomingCallPageWindowMap.has(accountId)) { + let incomingCallPageWindowTempMap = incomingCallPageWindowMap.get( + accountId) + if (incomingCallPageWindowTempMap.has(convUid)) { + var incomingCallPageWindow = incomingCallPageWindowTempMap.get( + convUid) + + + /* + * Close incomingCallPageWindow and clear the memory + */ + incomingCallPageWindow.close() + incomingCallPageWindow.destroy() + incomingCallPageWindowTempMap.delete(convUid) + if (incomingCallPageWindowTempMap.size === 0) { + incomingCallPageWindowMap.delete(accountId) + } else { + incomingCallPageWindowMap.set(accountId, + incomingCallPageWindowTempMap) + } + } + } +} diff --git a/src/mainview/js/screenrubberbandcreation.js b/src/mainview/js/screenrubberbandcreation.js new file mode 100644 index 00000000..38a22fdb --- /dev/null +++ b/src/mainview/js/screenrubberbandcreation.js @@ -0,0 +1,80 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/* + * Global screen rubber band window component, object variable for creation. + */ +var screenRubberBandWindowComponent +var screenRubberBandWindowObject + +function createScreenRubberBandWindowObject(parent, screenNumber) { + if (screenRubberBandWindowObject) + return + screenRubberBandWindowComponent = Qt.createComponent( + "../components/ScreenRubberBand.qml") + if (screenRubberBandWindowComponent.status === Component.Ready) + finishCreation(parent, screenNumber) + else if (screenRubberBandWindowComponent.status === Component.Error) + console.log("Error loading component:", + screenRubberBandWindowComponent.errorString()) +} + +function finishCreation(parent, screenNumber) { + screenRubberBandWindowObject = screenRubberBandWindowComponent.createObject( + parent) + if (screenRubberBandWindowObject === null) { + + + /* + * Error Handling. + */ + console.log("Error creating screen rubber band object") + } + + screenRubberBandWindowObject.screenNumber = screenNumber + screenRubberBandWindowObject.screen = Qt.application.screens[screenNumber] + + + /* + * Signal connection. + */ + screenRubberBandWindowObject.onClosing.connect( + destoryScreenRubberBandWindow) +} + +function showScreenRubberBandWindow() { + screenRubberBandWindowObject.showFullScreen() +} + + +/* + * Destroy and reset screenRubberBandWindowObject when window is closed. + */ +function destoryScreenRubberBandWindow() { + if (!screenRubberBandWindowObject) + return + screenRubberBandWindowObject.destroy() + screenRubberBandWindowObject = false +} + +function connectOnClosingEvent(func) { + if (screenRubberBandWindowObject) + screenRubberBandWindowObject.onClosing.connect(func) +} diff --git a/src/mainview/js/selectscreenwindowcreation.js b/src/mainview/js/selectscreenwindowcreation.js new file mode 100644 index 00000000..15bfbd15 --- /dev/null +++ b/src/mainview/js/selectscreenwindowcreation.js @@ -0,0 +1,72 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/* + * Global select screen window component, object variable for creation. + */ +var selectScreenWindowComponent +var selectScreenWindowObject + +function createSelectScreenWindowObject(selectArea = false) { + if (selectScreenWindowObject) + return + selectScreenWindowComponent = Qt.createComponent( + "../components/SelectScreen.qml") + if (selectScreenWindowComponent.status === Component.Ready) + finishCreation(selectArea) + else if (selectScreenWindowComponent.status === Component.Error) + console.log("Error loading component:", + selectScreenWindowComponent.errorString()) +} + +function finishCreation(selectArea) { + selectScreenWindowObject = selectScreenWindowComponent.createObject() + if (selectScreenWindowObject === null) { + + + /* + * Error Handling. + */ + console.log("Error creating select screen object") + } + + selectScreenWindowObject.selectArea = selectArea + + + /* + * Signal connection. + */ + selectScreenWindowObject.onClosing.connect(destorySelectScreenWindow) +} + +function showSelectScreenWindow() { + selectScreenWindowObject.show() +} + + +/* + * Destroy and reset selectScreenWindowObject when window is closed. + */ +function destorySelectScreenWindow() { + if(!selectScreenWindowObject) + return + selectScreenWindowObject.destroy() + selectScreenWindowObject = false +} diff --git a/src/mainview/js/videocallfullscreenwindowcontainercreation.js b/src/mainview/js/videocallfullscreenwindowcontainercreation.js new file mode 100644 index 00000000..23a51d7b --- /dev/null +++ b/src/mainview/js/videocallfullscreenwindowcontainercreation.js @@ -0,0 +1,97 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/* + * Global video call full screen window container, object variable for creation. + */ +var videoCallFullScreenWindowContainerComponent +var videoCallFullScreenWindowContainerObject + +function createvideoCallFullScreenWindowContainerObject() { + if (videoCallFullScreenWindowContainerObject) + return + videoCallFullScreenWindowContainerComponent = Qt.createComponent( + "../components/VideoCallFullScreenWindowContainer.qml") + if (videoCallFullScreenWindowContainerComponent.status === Component.Ready) + finishCreation() + else if (videoCallFullScreenWindowContainerComponent.status === Component.Error) + console.log("Error loading component:", + videoCallFullScreenWindowContainerComponent.errorString()) +} + +function finishCreation() { + videoCallFullScreenWindowContainerObject + = videoCallFullScreenWindowContainerComponent.createObject() + if (videoCallFullScreenWindowContainerObject === null) { + + + /* + * Error Handling. + */ + console.log("Error creating video call full screen window container object") + } + + + /* + * Signal connection. + */ + videoCallFullScreenWindowContainerObject.onClosing.connect( + destoryVideoCallFullScreenWindowContainer) +} + +function checkIfVisible() { + if (!videoCallFullScreenWindowContainerObject) + return false + return videoCallFullScreenWindowContainerObject.visible +} + +function setAsContainerChild(obj) { + if (videoCallFullScreenWindowContainerObject) + videoCallFullScreenWindowContainerObject.setAsChild(obj) +} + + +/* + * Destroy and reset videoCallFullScreenWindowContainerObject when window is closed. + */ +function destoryVideoCallFullScreenWindowContainer() { + if (!videoCallFullScreenWindowContainerObject) + return + videoCallFullScreenWindowContainerObject.destroy() + videoCallFullScreenWindowContainerObject = false +} + +function showVideoCallFullScreenWindowContainer() { + if (videoCallFullScreenWindowContainerObject) { + + + /* + * Hack: show first, then showFullScreen to make sure that the showFullScreen + * display on the correct screen. + */ + videoCallFullScreenWindowContainerObject.show() + videoCallFullScreenWindowContainerObject.showFullScreen() + } +} + +function closeVideoCallFullScreenWindowContainer() { + if (videoCallFullScreenWindowContainerObject) + videoCallFullScreenWindowContainerObject.close() +} diff --git a/src/mainview/js/videodevicecontextmenuitemcreation.js b/src/mainview/js/videodevicecontextmenuitemcreation.js new file mode 100644 index 00000000..2bab369a --- /dev/null +++ b/src/mainview/js/videodevicecontextmenuitemcreation.js @@ -0,0 +1,92 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +/* + * Global storage for created video device context menu item, + * will be cleared once context menu is closed. + */ +var itemArray = [] + + +/* + * Global videoDeviceContextMenuItem component, object variable for creation. + */ +var videoContextMenuObject +var videoDeviceContextMenuItemComponent +var videoDeviceContextMenuItemObject + + +/* + * Init videoContextMenuObject. + */ +function setVideoContextMenuObject(obj) { + videoContextMenuObject = obj +} + +function createVideoDeviceContextMenuItemObjects(deviceName, setChecked, last) { + + videoDeviceContextMenuItemComponent = Qt.createComponent( + "../components/VideoCallPageContextMenuDeviceItem.qml") + if (videoDeviceContextMenuItemComponent.status === Component.Ready) + finishCreation(deviceName, setChecked, last) + else if (videoDeviceContextMenuItemComponent.status === Component.Error) + console.log("Error loading component:", + videoDeviceContextMenuItemComponent.errorString()) +} + +function finishCreation(deviceName, setChecked, last) { + videoDeviceContextMenuItemObject = videoDeviceContextMenuItemComponent.createObject() + if (videoDeviceContextMenuItemObject === null) { + + + /* + * Error Handling. + */ + console.log("Error creating video context menu object") + } + + videoDeviceContextMenuItemObject.itemName = deviceName + videoDeviceContextMenuItemObject.checkable = true + videoDeviceContextMenuItemObject.checked = setChecked + videoDeviceContextMenuItemObject.contextMenuPreferredWidth = videoContextMenuObject.implicitWidth + + + /* + * Push into the storage array, and insert it into context menu. + */ + itemArray.push(videoDeviceContextMenuItemObject) + videoContextMenuObject.insertItem(1, videoDeviceContextMenuItemObject) + + + /* + * If it is the last device context menu item, open the context menu. + */ + if (last) + videoContextMenuObject.open() +} + +function removeCreatedItems() { + var arrayLength = itemArray.length + for (var i = 0; i < arrayLength; i++) { + videoContextMenuObject.removeItem(itemArray[i]) + itemArray[i].destroy() + } + itemArray = [] +} diff --git a/src/messagesadapter.cpp b/src/messagesadapter.cpp new file mode 100644 index 00000000..1aa925be --- /dev/null +++ b/src/messagesadapter.cpp @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Anthony L�onard + * Author: Olivier Soldano + * Author: Andreas Traczyk + * Author: Isa Nanic + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "messagesadapter.h" +#include "webchathelpers.h" + +#include "utils.h" + +#include +#include +#include +#include +#include + +MessagesAdapter::MessagesAdapter(QObject *parent) + : QmlAdapterBase(parent) +{} + +MessagesAdapter::~MessagesAdapter() {} + +void +MessagesAdapter::initQmlObject() +{ + connectConversationModel(); +} + +void +MessagesAdapter::setupChatView(const QString &uid) +{ + auto &convInfo = LRCInstance::getConversationFromConvUid(uid); + if (convInfo.uid.isEmpty()) { + return; + } + + QString contactURI = convInfo.participants.at(0); + + bool isContact = false; + auto selectedAccountId = LRCInstance::getCurrAccId(); + auto &accountInfo = LRCInstance::accountModel().getAccountInfo(selectedAccountId); + + lrc::api::profile::Type contactType; + try { + auto contactInfo = accountInfo.contactModel->getContact(contactURI); + if (contactInfo.isTrusted) { + isContact = true; + } + contactType = contactInfo.profileInfo.type; + } catch (...) { + } + + bool shouldShowSendContactRequestBtn = !isContact + && contactType != lrc::api::profile::Type::SIP; + + QMetaObject::invokeMethod(qmlObj_, + "setSendContactRequestButtonVisible", + Q_ARG(QVariant, shouldShowSendContactRequestBtn)); + + setMessagesVisibility(false); + + /* + * Type Indicator (contact). + */ + contactIsComposing(convInfo.uid, "", false); + connect(LRCInstance::getCurrentConversationModel(), + &ConversationModel::composingStatusChanged, + [this](const QString &uid, const QString &contactUri, bool isComposing) { + contactIsComposing(uid, contactUri, isComposing); + }); + + /* + * Draft and message content set up. + */ + Utils::oneShotConnect(qmlObj_, + SIGNAL(sendMessageContentSaved(const QString &)), + this, + SLOT(slotSendMessageContentSaved(const QString &))); + + requestSendMessageContent(); +} + +void +MessagesAdapter::connectConversationModel() +{ + auto currentConversationModel = LRCInstance::getCurrentAccountInfo().conversationModel.get(); + + QObject::disconnect(newInteractionConnection_); + QObject::disconnect(interactionRemovedConnection_); + QObject::disconnect(interactionStatusUpdatedConnection_); + + newInteractionConnection_ + = QObject::connect(currentConversationModel, + &lrc::api::ConversationModel::newInteraction, + [this](const QString &convUid, + uint64_t interactionId, + const lrc::api::interaction::Info &interaction) { + auto accountId = LRCInstance::getCurrAccId(); + newInteraction(accountId, convUid, interactionId, interaction); + }); + + interactionStatusUpdatedConnection_ = QObject::connect( + currentConversationModel, + &lrc::api::ConversationModel::interactionStatusUpdated, + [this](const QString &convUid, + uint64_t interactionId, + const lrc::api::interaction::Info &interaction) { + if (convUid != LRCInstance::getCurrentConvUid()) { + return; + } + auto ¤tAccountInfo = LRCInstance::getCurrentAccountInfo(); + auto currentConversationModel = currentAccountInfo.conversationModel.get(); + currentConversationModel->clearUnreadInteractions(convUid); + updateInteraction(*currentConversationModel, interactionId, interaction); + }); + + interactionRemovedConnection_ + = QObject::connect(currentConversationModel, + &lrc::api::ConversationModel::interactionRemoved, + [this](const QString &convUid, uint64_t interactionId) { + Q_UNUSED(convUid); + removeInteraction(interactionId); + }); + + currentConversationModel->setFilter(""); +} + +void +MessagesAdapter::sendContactRequest() +{ + auto convInfo = LRCInstance::getCurrentConversation(); + if (!convInfo.uid.isEmpty()) { + LRCInstance::getCurrentConversationModel()->makePermanent(convInfo.uid); + } +} + +void +MessagesAdapter::accountChangedSetUp(const QString &accoountId) +{ + Q_UNUSED(accoountId) + + connectConversationModel(); +} + +void +MessagesAdapter::updateConversationForAddedContact() +{ + auto conversation = LRCInstance::getCurrentConversation(); + auto convModel = LRCInstance::getCurrentConversationModel(); + + clear(); + setConversationProfileData(conversation); + printHistory(*convModel, conversation.interactions); +} + +void +MessagesAdapter::slotSendMessageContentSaved(const QString &content) +{ + if (!LastConvUid_.isEmpty()) { + LRCInstance::setContentDraft(LastConvUid_, LRCInstance::getCurrAccId(), content); + } + LastConvUid_ = LRCInstance::getCurrentConvUid(); + + Utils::oneShotConnect(qmlObj_, SIGNAL(messagesCleared()), this, SLOT(slotMessagesCleared())); + + setInvitation(false); + clear(); + auto restoredContent = LRCInstance::getContentDraft(LRCInstance::getCurrentConvUid(), + LRCInstance::getCurrAccId()); + setSendMessageContent(restoredContent); + emit needToUpdateSmartList(); +} + +void +MessagesAdapter::slotUpdateDraft(const QString &content) +{ + if (!LastConvUid_.isEmpty()) { + LRCInstance::setContentDraft(LastConvUid_, LRCInstance::getCurrAccId(), content); + } + emit needToUpdateSmartList(); +} + +void +MessagesAdapter::slotMessagesCleared() +{ + auto &convInfo = LRCInstance::getConversationFromConvUid(LRCInstance::getCurrentConvUid()); + auto convModel = LRCInstance::getCurrentConversationModel(); + + printHistory(*convModel, convInfo.interactions); + + Utils::oneShotConnect(qmlObj_, SIGNAL(messagesLoaded()), this, SLOT(slotMessagesLoaded())); + + setConversationProfileData(convInfo); +} + +void +MessagesAdapter::slotMessagesLoaded() +{ + setMessagesVisibility(true); +} + +void +MessagesAdapter::sendMessage(const QString &message) +{ + try { + auto convUid = LRCInstance::getCurrentConvUid(); + LRCInstance::getCurrentConversationModel()->sendMessage(convUid, message); + } catch (...) { + qDebug() << "Exception during sendMessage:" << message; + } +} + +void +MessagesAdapter::sendImage(const QString &message) +{ + if (message.startsWith("data:image/png;base64,")) { + /* + * Img tag contains base64 data, trim "data:image/png;base64," from data. + */ + QByteArray data = QByteArray::fromStdString(message.toStdString().substr(22)); + auto img_name_hash = QString::fromStdString( + QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex().toStdString()); + QString fileName = "\\img_" + img_name_hash + ".png"; + + QPixmap image_to_save; + if (!image_to_save.loadFromData(QByteArray::fromBase64(data))) { + qDebug().noquote() << "Errors during loadFromData" + << "\n"; + } + + QString path = QString(Utils::WinGetEnv("TEMP")) + fileName; + if (!image_to_save.save(path, "PNG")) { + qDebug().noquote() << "Errors during QPixmap save" + << "\n"; + } + + try { + auto convUid = LRCInstance::getCurrentConvUid(); + LRCInstance::getCurrentConversationModel()->sendFile(convUid, path, fileName); + } catch (...) { + qDebug().noquote() << "Exception during sendFile - base64 img" + << "\n"; + } + + } else { + /* + * Img tag contains file paths. + */ + + QString msg(message); +#ifdef Q_OS_WIN + msg = msg.replace("file:///", ""); +#else + msg = msg.replace("file:///", "/"); +#endif + QFileInfo fi(msg); + QString fileName = fi.fileName(); + + try { + auto convUid = LRCInstance::getCurrentConvUid(); + LRCInstance::getCurrentConversationModel()->sendFile(convUid, msg, fileName); + } catch (...) { + qDebug().noquote() << "Exception during sendFile - image from path" + << "\n"; + } + } +} + +void +MessagesAdapter::sendFile(const QString &message) +{ + QFileInfo fi(message); + QString fileName = fi.fileName(); + try { + auto convUid = LRCInstance::getCurrentConvUid(); + LRCInstance::getCurrentConversationModel()->sendFile(convUid, message, fileName); + } catch (...) { + qDebug() << "Exception during sendFile"; + } +} + +void +MessagesAdapter::retryInteraction(const QString &arg) +{ + bool ok; + uint64_t interactionUid = arg.toULongLong(&ok); + if (ok) { + LRCInstance::getCurrentConversationModel() + ->retryInteraction(LRCInstance::getCurrentConvUid(), interactionUid); + } else { + qDebug() << "retryInteraction - invalid arg" << arg; + } +} + +void +MessagesAdapter::setNewMessagesContent(const QString &path) +{ + if (path.length() == 0) + return; + QByteArray imageFormat = QImageReader::imageFormat(path); + + if (!imageFormat.isEmpty()) { + setMessagesImageContent(path); + } else { + setMessagesFileContent(path); + } +} + +void +MessagesAdapter::deleteInteraction(const QString &arg) +{ + bool ok; + uint64_t interactionUid = arg.toULongLong(&ok); + if (ok) { + LRCInstance::getCurrentConversationModel() + ->clearInteractionFromConversation(LRCInstance::getCurrentConvUid(), interactionUid); + } else { + qDebug() << "DeleteInteraction - invalid arg" << arg; + } +} + +void +MessagesAdapter::openFile(const QString &arg) +{ + QUrl fileUrl("file:///" + arg); + if (!QDesktopServices::openUrl(fileUrl)) { + qDebug() << "Couldn't open file: " << fileUrl; + } +} + +void +MessagesAdapter::acceptFile(const QString &arg) +{ + try { + auto interactionUid = arg.toLongLong(); + auto convUid = LRCInstance::getCurrentConvUid(); + LRCInstance::getCurrentConversationModel()->acceptTransfer(convUid, interactionUid); + } catch (...) { + qDebug() << "JS bridging - exception during acceptFile: " << arg; + } +} + +void +MessagesAdapter::refuseFile(const QString &arg) +{ + try { + auto interactionUid = arg.toLongLong(); + auto convUid = LRCInstance::getCurrentConvUid(); + LRCInstance::getCurrentConversationModel()->cancelTransfer(convUid, interactionUid); + } catch (...) { + qDebug() << "JS bridging - exception during refuseFile:" << arg; + } +} + +void +MessagesAdapter::pasteKeyDetected() +{ + const QMimeData *mimeData = QApplication::clipboard()->mimeData(); + + if (mimeData->hasImage()) { + /* + * Save temp data into base64 format. + */ + QPixmap pixmap = qvariant_cast(mimeData->imageData()); + QByteArray ba; + QBuffer bu(&ba); + bu.open(QIODevice::WriteOnly); + pixmap.save(&bu, "PNG"); + auto str = QString::fromLocal8Bit(ba.toBase64()); + + setMessagesImageContent(str, true); + } else if (mimeData->hasUrls()) { + QList urlList = mimeData->urls(); + /* + * Extract the local paths of the files. + */ + for (int i = 0; i < urlList.size(); ++i) { + /* + * Trim file:/// from url. + */ + QString filePath = urlList.at(i).toString().remove(0, 8); + QByteArray imageFormat = QImageReader::imageFormat(filePath); + + /* + * Check if file is qt supported image file type. + */ + if (!imageFormat.isEmpty()) { + setMessagesImageContent(filePath); + } else { + setMessagesFileContent(filePath); + } + } + } else { + QMetaObject::invokeMethod(qmlObj_, + "webViewRunJavaScript", + Q_ARG(QVariant, + QStringLiteral("replaceText(`%1`)").arg(mimeData->text()))); + } +} + +void +MessagesAdapter::onComposing(bool isComposing) +{ + LRCInstance::getCurrentConversationModel()->setIsComposing(LRCInstance::getCurrentConvUid(), + isComposing); +} + +void +MessagesAdapter::setConversationProfileData(const lrc::api::conversation::Info &convInfo) +{ + auto convModel = LRCInstance::getCurrentConversationModel(); + auto accInfo = &LRCInstance::getCurrentAccountInfo(); + auto contactUri = convInfo.participants.front(); + + if (contactUri.isEmpty()) { + return; + } + try { + auto &contact = accInfo->contactModel->getContact(contactUri); + auto bestName = Utils::bestNameForConversation(convInfo, *convModel); + setInvitation(contact.profileInfo.type == lrc::api::profile::Type::PENDING, + bestName, + contactUri); + + if (!contact.profileInfo.avatar.isEmpty()) { + setSenderImage(contactUri, contact.profileInfo.avatar); + } else { + auto avatar = Utils::conversationPhoto(convInfo.uid, *accInfo, true); + QByteArray ba; + QBuffer bu(&ba); + avatar.save(&bu, "PNG"); + setSenderImage(contactUri, QString::fromLocal8Bit(ba.toBase64())); + } + } catch (...) { + } +} + +void +MessagesAdapter::newInteraction(const QString &accountId, + const QString &convUid, + uint64_t interactionId, + const interaction::Info &interaction) +{ + Q_UNUSED(interactionId); + try { + auto &accountInfo = LRCInstance::getAccountInfo(accountId); + auto &convModel = accountInfo.conversationModel; + auto &conversation = LRCInstance::getConversationFromConvUid(convUid, accountId); + + if (conversation.uid.isEmpty()) { + return; + } + if (!interaction.authorUri.isEmpty() + && (!QApplication::focusWindow() || LRCInstance::getCurrAccId() != accountId)) { + /* + * TODO: Notification from other accounts. + */ + } + if (convUid != LRCInstance::getCurrentConvUid()) { + return; + } + convModel->clearUnreadInteractions(convUid); + printNewInteraction(*convModel, interactionId, interaction); + } catch (...) { + } +} + +void +MessagesAdapter::updateDraft() +{ + Utils::oneShotConnect(qmlObj_, + SIGNAL(sendMessageContentSaved(const QString &)), + this, + SLOT(slotUpdateDraft(const QString &))); + + requestSendMessageContent(); +} + +/* + * JS invoke. + */ +void +MessagesAdapter::setMessagesVisibility(bool visible) +{ + QString s = QString::fromLatin1(visible ? "showMessagesDiv();" : "hideMessagesDiv();"); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::requestSendMessageContent() +{ + QString s = QString::fromLatin1("requestSendMessageContent();"); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::setInvitation(bool show, const QString &contactUri, const QString &contactId) +{ + QString s + = show + ? QString::fromLatin1("showInvitation(\"%1\", \"%2\")").arg(contactUri).arg(contactId) + : QString::fromLatin1("showInvitation()"); + + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::clear() +{ + QString s = QString::fromLatin1("clearMessages();"); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::printHistory(lrc::api::ConversationModel &conversationModel, + const std::map interactions) +{ + auto interactionsStr = interactionsToJsonArrayObject(conversationModel, interactions).toUtf8(); + QString s = QString::fromLatin1("printHistory(%1);").arg(interactionsStr.constData()); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::setSenderImage(const QString &sender, const QString &senderImage) +{ + QJsonObject setSenderImageObject = QJsonObject(); + setSenderImageObject.insert("sender_contact_method", QJsonValue(sender)); + setSenderImageObject.insert("sender_image", QJsonValue(senderImage)); + + auto setSenderImageObjectString = QString( + QJsonDocument(setSenderImageObject).toJson(QJsonDocument::Compact)); + QString s = QString::fromLatin1("setSenderImage(%1);") + .arg(setSenderImageObjectString.toUtf8().constData()); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::printNewInteraction(lrc::api::ConversationModel &conversationModel, + uint64_t msgId, + const lrc::api::interaction::Info &interaction) +{ + auto interactionObject + = interactionToJsonInteractionObject(conversationModel, msgId, interaction).toUtf8(); + if (interactionObject.isEmpty()) { + return; + } + QString s = QString::fromLatin1("addMessage(%1);").arg(interactionObject.constData()); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::updateInteraction(lrc::api::ConversationModel &conversationModel, + uint64_t msgId, + const lrc::api::interaction::Info &interaction) +{ + auto interactionObject + = interactionToJsonInteractionObject(conversationModel, msgId, interaction).toUtf8(); + if (interactionObject.isEmpty()) { + return; + } + QString s = QString::fromLatin1("updateMessage(%1);").arg(interactionObject.constData()); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::setMessagesImageContent(const QString &path, bool isBased64) +{ + if (isBased64) { + QString param = QString("addImage_base64('file://%1')").arg(path); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, param)); + } else { + QString param = QString("addImage_path('file://%1')").arg(path); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, param)); + } +} + +void +MessagesAdapter::setMessagesFileContent(const QString &path) +{ + qint64 fileSize = QFileInfo(path).size(); + QString fileName = QFileInfo(path).fileName(); + /* + * If file name is too large, trim it. + */ + if (fileName.length() > 15) { + fileName = fileName.remove(12, fileName.length() - 12) + "..."; + } + QString param = QString("addFile_path('%1','%2','%3')") + .arg(path, fileName, Utils::humanFileSize(fileSize)); + + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, param)); +} + +void +MessagesAdapter::removeInteraction(uint64_t interactionId) +{ + QString s = QString::fromLatin1("removeInteraction(%1);").arg(QString::number(interactionId)); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::setSendMessageContent(const QString &content) +{ + QString s = QString::fromLatin1("setSendMessageContent(`%1`);").arg(content); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); +} + +void +MessagesAdapter::contactIsComposing(const QString &uid, const QString &contactUri, bool isComposing) +{ + if (LRCInstance::getCurrentConvUid() == uid) { + QString s + = QString::fromLatin1("showTypingIndicator(`%1`, %2);").arg(contactUri).arg(isComposing); + QMetaObject::invokeMethod(qmlObj_, "webViewRunJavaScript", Q_ARG(QVariant, s)); + } +} + +void +MessagesAdapter::acceptInvitation() +{ + auto convUid = LRCInstance::getCurrentConvUid(); + LRCInstance::getCurrentConversationModel()->makePermanent(convUid); +} + +void +MessagesAdapter::refuseInvitation() +{ + auto convUid = LRCInstance::getCurrentConvUid(); + LRCInstance::getCurrentConversationModel()->removeConversation(convUid, false); + setInvitation(false); +} + +void +MessagesAdapter::blockConversation() +{ + auto convUid = LRCInstance::getCurrentConvUid(); + LRCInstance::getCurrentConversationModel()->removeConversation(convUid, true); + setInvitation(false); +} diff --git a/src/messagesadapter.h b/src/messagesadapter.h new file mode 100644 index 00000000..f3247661 --- /dev/null +++ b/src/messagesadapter.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "lrcinstance.h" +#include "qmladapterbase.h" + +#include +#include + +class MessagesAdapter : public QmlAdapterBase +{ + Q_OBJECT + +public: + explicit MessagesAdapter(QObject *parent = 0); + ~MessagesAdapter(); + + Q_INVOKABLE void setupChatView(const QString &uid); + Q_INVOKABLE void connectConversationModel(); + Q_INVOKABLE void sendContactRequest(); + Q_INVOKABLE void accountChangedSetUp(const QString &accoountId); + Q_INVOKABLE void updateConversationForAddedContact(); + + /* + * JS Q_INVOKABLE. + */ + Q_INVOKABLE void acceptInvitation(); + Q_INVOKABLE void refuseInvitation(); + Q_INVOKABLE void blockConversation(); + Q_INVOKABLE void setNewMessagesContent(const QString &path); + Q_INVOKABLE void sendMessage(const QString &message); + Q_INVOKABLE void sendImage(const QString &message); + Q_INVOKABLE void sendFile(const QString &message); + Q_INVOKABLE void retryInteraction(const QString &arg); + Q_INVOKABLE void deleteInteraction(const QString &arg); + Q_INVOKABLE void openFile(const QString &arg); + Q_INVOKABLE void acceptFile(const QString &arg); + Q_INVOKABLE void refuseFile(const QString &arg); + Q_INVOKABLE void pasteKeyDetected(); + Q_INVOKABLE void onComposing(bool isComposing); + + /* + * Manually update draft when hiding message web view (Back to welcome page). + */ + Q_INVOKABLE void updateDraft(); + + /* + * Run corrsponding js functions, c++ to qml. + */ + void setMessagesVisibility(bool visible); + void requestSendMessageContent(); + void setInvitation(bool show, const QString &contactUri = "", const QString &contactId = ""); + void clear(); + void printHistory(lrc::api::ConversationModel &conversationModel, + const std::map interactions); + void setSenderImage(const QString &sender, const QString &senderImage); + void printNewInteraction(lrc::api::ConversationModel &conversationModel, + uint64_t msgId, + const lrc::api::interaction::Info &interaction); + void updateInteraction(lrc::api::ConversationModel &conversationModel, + uint64_t msgId, + const lrc::api::interaction::Info &interaction); + void setMessagesImageContent(const QString &path, bool isBased64 = false); + void setMessagesFileContent(const QString &path); + void removeInteraction(uint64_t interactionId); + void setSendMessageContent(const QString &content); + void contactIsComposing(const QString &uid, const QString &contactUri, bool isComposing); + +signals: + void needToUpdateSmartList(); + +public slots: + void slotSendMessageContentSaved(const QString &content); + void slotUpdateDraft(const QString &content); + void slotMessagesCleared(); + void slotMessagesLoaded(); + +private: + void initQmlObject() override final; + void setConversationProfileData(const lrc::api::conversation::Info &convInfo); + void newInteraction(const QString &accountId, + const QString &convUid, + uint64_t interactionId, + const interaction::Info &interaction); + + QString LastConvUid_; + + /* + * Interaction connections. + */ + QMetaObject::Connection newInteractionConnection_; + QMetaObject::Connection interactionStatusUpdatedConnection_; + QMetaObject::Connection interactionRemovedConnection_; +}; \ No newline at end of file diff --git a/src/pixbufmanipulator.cpp b/src/pixbufmanipulator.cpp new file mode 100644 index 00000000..e621808e --- /dev/null +++ b/src/pixbufmanipulator.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Anthony Léonard + * Author: Olivier Soldano + * Author: Andreas Traczyk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "pixbufmanipulator.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "globalinstances.h" + +#include +#include +#include +#include + +#include "utils.h" +#undef interface + +QVariant +PixbufManipulator::personPhoto(const QByteArray &data, const QString &type) +{ + QImage avatar; + const bool ret = avatar.loadFromData(QByteArray::fromBase64(data), type.toLatin1()); + if (!ret) { + qDebug() << "vCard image loading failed"; + return QVariant(); + } + return QPixmap::fromImage(Utils::getCirclePhoto(avatar, avatar.size().width())); +} + +QVariant +PixbufManipulator::numberCategoryIcon(const QVariant &p, + const QSize &size, + bool displayPresence, + bool isPresent) +{ + Q_UNUSED(p) + Q_UNUSED(size) + Q_UNUSED(displayPresence) + Q_UNUSED(isPresent) + return QVariant(); +} + +QByteArray +PixbufManipulator::toByteArray(const QVariant &pxm) +{ + auto image = pxm.value(); + QByteArray ba = Utils::QImageToByteArray(image); + return ba; +} + +QVariant +PixbufManipulator::userActionIcon(const UserActionElement &state) const +{ + Q_UNUSED(state) + return QVariant(); +} + +QVariant +PixbufManipulator::decorationRole(const QModelIndex &index) +{ + Q_UNUSED(index) + return QVariant(); +} + +QVariant +PixbufManipulator::decorationRole(const lrc::api::conversation::Info &conversationInfo, + const lrc::api::account::Info &accountInfo) +{ + QImage photo; + auto contacts = conversationInfo.participants; + if (contacts.empty()) { + return QVariant::fromValue(photo); + } + try { + /* + * Get first contact photo. + */ + auto contactUri = contacts.front(); + auto contactInfo = accountInfo.contactModel->getContact(contactUri); + auto contactPhoto = contactInfo.profileInfo.avatar; + auto bestName = Utils::bestNameForContact(contactInfo); + auto bestId = Utils::bestIdForContact(contactInfo); + if (accountInfo.profileInfo.type == lrc::api::profile::Type::SIP + && contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY) { + photo = Utils::fallbackAvatar(IMAGE_SIZE, QString(), QString()); + } else if (contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY + && contactInfo.profileInfo.uri.isEmpty()) { + photo = Utils::fallbackAvatar(IMAGE_SIZE, QString(), QString()); + } else if (!contactPhoto.isEmpty()) { + QByteArray byteArray = contactPhoto.toLocal8Bit(); + photo = personPhoto(byteArray, nullptr).value(); + if (photo.isNull()) { + auto avatarName = contactInfo.profileInfo.uri == bestName ? QString() : bestName; + photo = Utils::fallbackAvatar(IMAGE_SIZE, + "ring:" + contactInfo.profileInfo.uri, + avatarName); + } + } else { + auto avatarName = contactInfo.profileInfo.uri == bestName ? QString() : bestName; + photo = Utils::fallbackAvatar(IMAGE_SIZE, + "ring:" + contactInfo.profileInfo.uri, + avatarName); + } + } catch (...) { + } + return QVariant::fromValue(Utils::scaleAndFrame(photo, IMAGE_SIZE)); +} \ No newline at end of file diff --git a/src/pixbufmanipulator.h b/src/pixbufmanipulator.h new file mode 100644 index 00000000..5325f15b --- /dev/null +++ b/src/pixbufmanipulator.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include +#include + +Q_DECLARE_METATYPE(QImage); + +class Person; + +QByteArray QImageToByteArray(QImage image); + +class PixbufManipulator : public Interfaces::PixmapManipulatorI +{ +public: + QVariant personPhoto(const QByteArray &data, const QString &type = "PNG") override; + + /* + * TODO: the following methods return an empty QVariant/QByteArray. + */ + QVariant numberCategoryIcon(const QVariant &p, + const QSize &size, + bool displayPresence = false, + bool isPresent = false) override; + QByteArray toByteArray(const QVariant &pxm) override; + QVariant userActionIcon(const UserActionElement &state) const override; + QVariant decorationRole(const QModelIndex &index) override; + QVariant decorationRole(const lrc::api::conversation::Info &conversation, + const lrc::api::account::Info &accountInfo) override; +}; diff --git a/src/pluginitemlistmodel.cpp b/src/pluginitemlistmodel.cpp new file mode 100644 index 00000000..dcc072f8 --- /dev/null +++ b/src/pluginitemlistmodel.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "pluginitemlistmodel.h" + +PluginItemListModel::PluginItemListModel(QObject *parent) + : QAbstractListModel(parent) +{} + +PluginItemListModel::~PluginItemListModel() {} + +int +PluginItemListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + return LRCInstance::pluginModel().listAvailablePlugins().size(); + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +PluginItemListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +PluginItemListModel::data(const QModelIndex &index, int role) const +{ + auto pluginList = LRCInstance::pluginModel().listAvailablePlugins(); + if (!index.isValid() || pluginList.size() <= index.row()) { + return QVariant(); + } + + auto details = LRCInstance::pluginModel().getPluginDetails(pluginList.at(index.row())); + + switch (role) { + case Role::PluginName: + return QVariant(details.name); + case Role::PluginId: + return QVariant(pluginList.at(index.row())); + case Role::PluginIcon: + return QVariant(details.iconPath); + case Role::IsLoaded: + return QVariant(details.loaded); + } + return QVariant(); +} + +QHash +PluginItemListModel::roleNames() const +{ + QHash roles; + roles[PluginName] = "PluginName"; + roles[PluginId] = "PluginId"; + roles[PluginIcon] = "PluginIcon"; + roles[IsLoaded] = "IsLoaded"; + + return roles; +} + +QModelIndex +PluginItemListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +PluginItemListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +PluginItemListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +PluginItemListModel::reset() +{ + beginResetModel(); + endResetModel(); +} diff --git a/src/pluginitemlistmodel.h b/src/pluginitemlistmodel.h new file mode 100644 index 00000000..da399f7c --- /dev/null +++ b/src/pluginitemlistmodel.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/pluginmodel.h" + +#include "lrcinstance.h" + +class PluginItemListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum Role { PluginName = Qt::UserRole + 1, PluginId, PluginIcon, IsLoaded }; + Q_ENUM(Role) + + explicit PluginItemListModel(QObject *parent = 0); + ~PluginItemListModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); +}; diff --git a/src/preferenceitemlistmodel.cpp b/src/preferenceitemlistmodel.cpp new file mode 100644 index 00000000..8e90426e --- /dev/null +++ b/src/preferenceitemlistmodel.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "preferenceitemlistmodel.h" +#include + +std::map mapType {{QString("List"), PreferenceItemListModel::Type::LIST}}; + +PreferenceItemListModel::PreferenceItemListModel(QObject *parent) + : QAbstractListModel(parent) +{} + +PreferenceItemListModel::~PreferenceItemListModel() {} + +int +PreferenceItemListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + return LRCInstance::pluginModel().getPluginPreferences(pluginId_).size(); + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +PreferenceItemListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +PreferenceItemListModel::data(const QModelIndex &index, int role) const +{ + auto preferenceList = LRCInstance::pluginModel().getPluginPreferences(pluginId_); + if (!index.isValid() || preferenceList.size() <= index.row()) { + return QVariant(); + } + + auto details = preferenceList.at(index.row()); + int type = Type::DEFAULT; + auto it = mapType.find(details["type"]); + if (it != mapType.end()) + { + type = mapType[details["type"]]; + } + + switch (role) { + case Role::PreferenceKey: + return QVariant(details["key"]); + case Role::PreferenceName: + return QVariant(details["title"]); + case Role::PreferenceSummary: + return QVariant(details["summary"]); + case Role::PreferenceType: + return QVariant(type); + case Role::PreferenceDefaultValue: + return QVariant(details["defaultValue"]); + case Role::PreferenceEntries: + return QVariant(details["entries"]); + case Role::PreferenceEntryValues: + return QVariant(details["entryValues"]); + } + return QVariant(); +} + +QHash +PreferenceItemListModel::roleNames() const +{ + QHash roles; + roles[PreferenceKey] = "PreferenceKey"; + roles[PreferenceName] = "PreferenceName"; + roles[PreferenceSummary] = "PreferenceSummary"; + roles[PreferenceType] = "PreferenceType"; + roles[PreferenceDefaultValue] = "PreferenceDefaultValue"; + roles[PreferenceEntries] = "PreferenceEntries"; + roles[PreferenceEntryValues] = "PreferenceEntryValues"; + + return roles; +} + +QModelIndex +PreferenceItemListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +PreferenceItemListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +PreferenceItemListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +PreferenceItemListModel::reset() +{ + beginResetModel(); + endResetModel(); +} + +QString +PreferenceItemListModel::pluginId() const +{ + return pluginId_; +} + +void +PreferenceItemListModel::setPluginId(const QString &pluginId) +{ + pluginId_ = pluginId; +} diff --git a/src/preferenceitemlistmodel.h b/src/preferenceitemlistmodel.h new file mode 100644 index 00000000..c51a0701 --- /dev/null +++ b/src/preferenceitemlistmodel.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/pluginmodel.h" + +#include "lrcinstance.h" + +class PreferenceItemListModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(QString pluginId READ pluginId WRITE setPluginId) +public: + enum Role { PreferenceKey = Qt::UserRole + 1, PreferenceName, PreferenceSummary, PreferenceType, PreferenceDefaultValue, PreferenceEntries, PreferenceEntryValues}; + + typedef enum { + LIST, + DEFAULT, + } Type; + + Q_ENUM(Role) + + explicit PreferenceItemListModel(QObject *parent = 0); + ~PreferenceItemListModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); + + QString pluginId() const; + void setPluginId(const QString &pluginId); +// signals: +// void pluginIdChanged(); +private: + QString pluginId_; +}; diff --git a/src/previewrenderer.cpp b/src/previewrenderer.cpp new file mode 100644 index 00000000..773b23fd --- /dev/null +++ b/src/previewrenderer.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "previewrenderer.h" + +#include "lrcinstance.h" + +PreviewRenderer::PreviewRenderer(QQuickItem *parent) + : QQuickPaintedItem(parent) +{ + setAntialiasing(true); + setFillColor(Qt::black); + setRenderTarget(QQuickPaintedItem::FramebufferObject); + setPerformanceHint(QQuickPaintedItem::FastFBOResizing); + + previewFrameUpdatedConnection_ = connect(LRCInstance::renderer(), + &RenderManager::previewFrameUpdated, + [this]() { update(QRect(0, 0, width(), height())); }); + previewRenderingStopped_ = connect(LRCInstance::renderer(), + &RenderManager::previewRenderingStopped, + [this]() { update(QRect(0, 0, width(), height())); }); +} + +PreviewRenderer::~PreviewRenderer() +{ + disconnect(previewFrameUpdatedConnection_); + disconnect(previewRenderingStopped_); +} + +void +PreviewRenderer::paint(QPainter *painter) +{ + auto previewImage = LRCInstance::renderer()->getPreviewFrame(); + if (previewImage) { + QImage scaledPreview; + auto aspectRatio = static_cast(previewImage->width()) + / static_cast(previewImage->height()); + auto previewHeight = height(); + auto previewWidth = previewHeight * aspectRatio; + + /* Instead of setting fixed size, we could get an x offset for the preview + * but this would render the horizontal spacers in the parent widget useless. + * e.g. + * auto parent = qobject_cast(this->parent()); + * auto xPos = (parent->width() - previewWidth) / 2; + * setGeometry(QRect(QPoint(xPos, this->pos().y()), QSize(previewWidth, previewHeight))); + */ + setWidth(previewWidth); + setHeight(previewHeight); + + scaledPreview = previewImage->scaled(size().toSize(), Qt::KeepAspectRatio); + painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()), + scaledPreview); + } +} + +VideoCallPreviewRenderer::VideoCallPreviewRenderer(QQuickItem *parent) + : PreviewRenderer(parent) +{} + +VideoCallPreviewRenderer::~VideoCallPreviewRenderer() {} + +qreal +VideoCallPreviewRenderer::getPreviewImageScalingFactor() +{ + auto previewImage = LRCInstance::renderer()->getPreviewFrame(); + if (previewImage) { + return static_cast(previewImage->height()) + / static_cast(previewImage->width()); + } + return 1.0; +} + +void +VideoCallPreviewRenderer::paint(QPainter *painter) +{ + auto previewImage = LRCInstance::renderer()->getPreviewFrame(); + + if (previewImage) { + emit previewImageAvailable(); + + QImage scaledPreview; + scaledPreview = previewImage->scaled(size().toSize(), Qt::KeepAspectRatio); + painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()), + scaledPreview); + } +} + +PhotoboothPreviewRender::PhotoboothPreviewRender(QQuickItem *parent) + : PreviewRenderer(parent) +{ + connect(LRCInstance::renderer(), &RenderManager::previewRenderingStopped, [this]() { + emit hideBooth(); + }); +} + +PhotoboothPreviewRender::~PhotoboothPreviewRender() {} + +QImage +PhotoboothPreviewRender::takePhoto() +{ + if (auto previewImage = LRCInstance::renderer()->getPreviewFrame()) { + return previewImage->copy(); + } + return QImage(); +} + +QString +PhotoboothPreviewRender::takeCroppedPhotoToBase64(int size) +{ + auto image = Utils::cropImage(takePhoto()); + auto avatar = image.scaled(size, size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); + + return QString::fromLatin1(Utils::QImageToByteArray(avatar).toBase64().data()); +} + +void +PhotoboothPreviewRender::paint(QPainter *painter) +{ + painter->setRenderHint(QPainter::Antialiasing, true); + + auto previewImage = LRCInstance::renderer()->getPreviewFrame(); + if (previewImage) { + QImage scaledPreview; + scaledPreview = Utils::getCirclePhoto(*previewImage, + height() <= width() ? height() : width()); + painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()), + scaledPreview); + } +} diff --git a/src/previewrenderer.h b/src/previewrenderer.h new file mode 100644 index 00000000..408416c2 --- /dev/null +++ b/src/previewrenderer.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +/* + * Use QQuickPaintedItem so that QPainter apis can be used. + * Note: Old video pipeline. + */ +class PreviewRenderer : public QQuickPaintedItem +{ + Q_OBJECT +public: + explicit PreviewRenderer(QQuickItem *parent = 0); + ~PreviewRenderer(); + +protected: + void paint(QPainter *painter) override; + +private: + QMetaObject::Connection previewFrameUpdatedConnection_; + QMetaObject::Connection previewRenderingStopped_; +}; + +class VideoCallPreviewRenderer : public PreviewRenderer +{ + Q_OBJECT +public: + explicit VideoCallPreviewRenderer(QQuickItem *parent = 0); + virtual ~VideoCallPreviewRenderer(); + + Q_INVOKABLE qreal getPreviewImageScalingFactor(); + +signals: + void previewImageAvailable(); + +private: + void paint(QPainter *painter) override final; +}; + +class PhotoboothPreviewRender : public PreviewRenderer +{ + Q_OBJECT +public: + explicit PhotoboothPreviewRender(QQuickItem *parent = 0); + virtual ~PhotoboothPreviewRender(); + + QImage takePhoto(); + Q_INVOKABLE QString takeCroppedPhotoToBase64(int size); + +signals: + void hideBooth(); + +private: + void paint(QPainter *painter) override final; +}; diff --git a/src/qmladapterbase.cpp b/src/qmladapterbase.cpp new file mode 100644 index 00000000..6b8d3f68 --- /dev/null +++ b/src/qmladapterbase.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "qmladapterbase.h" + +QmlAdapterBase::QmlAdapterBase(QObject *parent) + : QObject(parent) +{ + qmlObj_ = nullptr; +} + +QmlAdapterBase::~QmlAdapterBase() {} + +void +QmlAdapterBase::setQmlObject(QObject *obj) +{ + qmlObj_ = obj; + + initQmlObject(); +} diff --git a/src/qmladapterbase.h b/src/qmladapterbase.h new file mode 100644 index 00000000..e476359e --- /dev/null +++ b/src/qmladapterbase.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +/* + * The main purpose of this class is to operate on qml objects, + * or provide api calls to qml objects that cannot be done directly in qml. + */ +class QmlAdapterBase : public QObject +{ + Q_OBJECT +public: + explicit QmlAdapterBase(QObject *parent = nullptr); + ~QmlAdapterBase(); + + /* + * This function should be called in the Component.onCompleted slot + * in the qml component that this adapter should attach to. + */ + Q_INVOKABLE void setQmlObject(QObject *obj); + +protected: + /* + *Once the qml object is set, custom actions can be done in this function. + */ + virtual void initQmlObject() = 0; + + /* + * Object pointer. + */ + QObject *qmlObj_; +}; diff --git a/src/qrimageprovider.h b/src/qrimageprovider.h new file mode 100644 index 00000000..cc8837eb --- /dev/null +++ b/src/qrimageprovider.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "accountlistmodel.h" +#include "lrcinstance.h" + +#include +#include +#include +#include +#include + +class QrImageProvider : public QObject, public QQuickImageProvider +{ +public: + QrImageProvider() + : QQuickImageProvider(QQuickImageProvider::Image, + QQmlImageProviderBase::ForceAsynchronousImageLoading) + {} + + enum class QrType { Account, Contact }; + + /* + * Id should be string like account_0 (account index), + * or contact_xxx (uid). + * Cannot use getCurrentAccId to replace account index, + * since we need to keep each image id unique. + */ + QPair + getIndexFromID(const QString &id) + { + auto list = id.split('_', QString::SkipEmptyParts); + if (list.contains("account")) { + return QPair(QrType::Account, list[1]); + } else if (list.contains("contact") && list.size() > 1) { + /* + * For contact_xxx, xxx is "" initially + */ + auto convInfo = LRCInstance::getConversationFromConvUid(list[1]); + auto contact = LRCInstance::getCurrentAccountInfo().contactModel->getContact( + convInfo.participants.at(0)); + return QPair(QrType::Contact, contact.profileInfo.uri); + } + return QPair(QrType::Account, ""); + } + + QImage + requestImage(const QString &id, QSize *size, const QSize &requestedSize) override + { + Q_UNUSED(size); + + QString uri; + auto indexPair = getIndexFromID(id); + + if (indexPair.first == QrType::Contact) { + uri = indexPair.second; + } else { + if (indexPair.second.isEmpty()) + return QImage(); + + auto accountList = LRCInstance::accountModel().getAccountList(); + auto accountIndex = indexPair.second.toInt(); + if (accountList.size() <= accountIndex) + return QImage(); + + auto &accountInfo = LRCInstance::accountModel().getAccountInfo( + accountList.at(accountIndex)); + uri = accountInfo.profileInfo.uri; + } + + if (!requestedSize.isEmpty()) + return Utils::setupQRCode(uri, 0).scaled(requestedSize, Qt::KeepAspectRatio); + else + return Utils::setupQRCode(uri, 0); + } +}; \ No newline at end of file diff --git a/src/rendermanager.cpp b/src/rendermanager.cpp new file mode 100644 index 00000000..dfd095bb --- /dev/null +++ b/src/rendermanager.cpp @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "rendermanager.h" + +#include + +#include + +using namespace lrc::api; + +FrameWrapper::FrameWrapper(AVModel &avModel, const QString &id) + : avModel_(avModel) + , id_(id) + , isRendering_(false) +{} + +FrameWrapper::~FrameWrapper() +{ + if (id_ == video::PREVIEW_RENDERER_ID) { + avModel_.stopPreview(); + } +} + +void +FrameWrapper::connectStartRendering() +{ + QObject::disconnect(renderConnections_.started); + renderConnections_.started = QObject::connect(&avModel_, + &AVModel::rendererStarted, + this, + &FrameWrapper::slotRenderingStarted); +} + +bool +FrameWrapper::startRendering() +{ + try { + renderer_ = const_cast(&avModel_.getRenderer(id_)); + } catch (std::out_of_range &e) { + qWarning() << e.what(); + return false; + } + + QObject::disconnect(renderConnections_.updated); + QObject::disconnect(renderConnections_.stopped); + + renderConnections_.updated = QObject::connect(&avModel_, + &AVModel::frameUpdated, + this, + &FrameWrapper::slotFrameUpdated); + + renderConnections_.stopped = QObject::connect(&avModel_, + &AVModel::rendererStopped, + this, + &FrameWrapper::slotRenderingStopped); + + return true; +} + +QImage * +FrameWrapper::getFrame() +{ + return image_.get(); +} + +bool +FrameWrapper::isRendering() +{ + return isRendering_; +} + +void +FrameWrapper::slotRenderingStarted(const QString &id) +{ + if (id != id_) { + return; + } + + if (!startRendering()) { + qWarning() << "Couldn't start rendering for id: " << id_; + return; + } + + isRendering_ = true; + + emit renderingStarted(id); +} + +void +FrameWrapper::slotFrameUpdated(const QString &id) +{ + if (id != id_) { + return; + } + + if (!renderer_ || !renderer_->isRendering()) { + return; + } + + { + QMutexLocker lock(&mutex_); + + frame_ = renderer_->currentFrame(); + + unsigned int width = renderer_->size().width(); + unsigned int height = renderer_->size().height(); + +#ifndef Q_OS_LINUX + unsigned int size = frame_.storage.size(); + /* + * If the frame is empty or not the expected size, + * do nothing and keep the last rendered QImage. + */ + if (size != 0 && size == width * height * 4) { + buffer_ = std::move(frame_.storage); + image_.reset(new QImage((uchar *) buffer_.data(), + width, + height, + QImage::Format_ARGB32_Premultiplied)); +#else + if (frame_.ptr) { + image_.reset(new QImage(frame_.ptr, width, height, QImage::Format_ARGB32)); +#endif + } + } + + emit frameUpdated(id); +} + +void +FrameWrapper::slotRenderingStopped(const QString &id) +{ + if (id != id_) { + return; + } + + QObject::disconnect(renderConnections_.updated); + QObject::disconnect(renderConnections_.stopped); + renderer_ = nullptr; + + /* + * The object's QImage pointer is reset before renderingStopped + * is emitted, allowing the listener to invoke specific behavior + * like clearing the widget or changing the UI entirely. + */ + image_.reset(); + + isRendering_ = false; + + emit renderingStopped(id); +} + +RenderManager::RenderManager(AVModel &avModel) + : avModel_(avModel) +{ + deviceListSize_ = avModel_.getDevices().size(); + connect(&avModel_, &lrc::api::AVModel::deviceEvent, this, &RenderManager::slotDeviceEvent); + + previewFrameWrapper_ = std::make_unique(avModel_); + + QObject::connect(previewFrameWrapper_.get(), + &FrameWrapper::renderingStarted, + [this](const QString &id) { + Q_UNUSED(id); + emit previewRenderingStarted(); + }); + QObject::connect(previewFrameWrapper_.get(), + &FrameWrapper::frameUpdated, + [this](const QString &id) { + Q_UNUSED(id); + emit previewFrameUpdated(); + }); + QObject::connect(previewFrameWrapper_.get(), + &FrameWrapper::renderingStopped, + [this](const QString &id) { + Q_UNUSED(id); + emit previewRenderingStopped(); + }); + + previewFrameWrapper_->connectStartRendering(); +} + +RenderManager::~RenderManager() +{ + previewFrameWrapper_.reset(); + + for (auto &dfw : distantFrameWrapperMap_) { + dfw.second.reset(); + } +} + +bool +RenderManager::isPreviewing() +{ + return previewFrameWrapper_->isRendering(); +} + +QImage * +RenderManager::getPreviewFrame() +{ + return previewFrameWrapper_->getFrame(); +} + +void +RenderManager::stopPreviewing(bool async) +{ + if (!previewFrameWrapper_->isRendering()) { + return; + } + + if (async) { + QtConcurrent::run([this] { avModel_.stopPreview(); }); + } else { + avModel_.stopPreview(); + } +} + +void +RenderManager::startPreviewing(bool force, bool async) +{ + if (previewFrameWrapper_->isRendering() && !force) { + return; + } + + auto restart = [this] { + if (previewFrameWrapper_->isRendering()) { + avModel_.stopPreview(); + } + avModel_.startPreview(); + }; + if (async) { + QtConcurrent::run(restart); + } else { + restart(); + } +} + +QImage * +RenderManager::getFrame(const QString &id) +{ + auto dfwIt = distantFrameWrapperMap_.find(id); + if (dfwIt != distantFrameWrapperMap_.end()) { + return dfwIt->second->getFrame(); + } + return nullptr; +} + +void +RenderManager::addDistantRenderer(const QString &id) +{ + /* + * Check if a FrameWrapper with this id exists. + */ + auto dfwIt = distantFrameWrapperMap_.find(id); + if (dfwIt != distantFrameWrapperMap_.end()) { + if (!dfwIt->second->startRendering()) { + qWarning() << "Couldn't start rendering for id: " << id; + } + } else { + auto dfw = std::make_unique(avModel_, id); + + /* + * Connect this to the FrameWrapper. + */ + distantConnectionMap_[id].started = QObject::connect(dfw.get(), + &FrameWrapper::renderingStarted, + [this](const QString &id) { + emit distantRenderingStarted(id); + }); + distantConnectionMap_[id].updated = QObject::connect(dfw.get(), + &FrameWrapper::frameUpdated, + [this](const QString &id) { + emit distantFrameUpdated(id); + }); + distantConnectionMap_[id].stopped = QObject::connect(dfw.get(), + &FrameWrapper::renderingStopped, + [this](const QString &id) { + emit distantRenderingStopped(id); + }); + + /* + * Connect FrameWrapper to avmodel. + */ + dfw->connectStartRendering(); + + /* + * Add to map. + */ + distantFrameWrapperMap_.insert(std::make_pair(id, std::move(dfw))); + } +} + +void +RenderManager::removeDistantRenderer(const QString &id) +{ + auto dfwIt = distantFrameWrapperMap_.find(id); + if (dfwIt != distantFrameWrapperMap_.end()) { + /* + * Disconnect FrameWrapper from this. + */ + auto dcIt = distantConnectionMap_.find(id); + if (dcIt != distantConnectionMap_.end()) { + QObject::disconnect(dcIt->second.started); + QObject::disconnect(dcIt->second.updated); + QObject::disconnect(dcIt->second.stopped); + } + + /* + * Erase. + */ + distantFrameWrapperMap_.erase(dfwIt); + } +} + +void +RenderManager::slotDeviceEvent() +{ + auto defaultDevice = avModel_.getDefaultDevice(); + auto currentCaptureDevice = avModel_.getCurrentVideoCaptureDevice(); + /* + * Decide whether a device has plugged, unplugged, or nothing has changed. + */ + auto deviceList = avModel_.getDevices(); + auto currentDeviceListSize = deviceList.size(); + + DeviceEvent deviceEvent{DeviceEvent::None}; + if (currentDeviceListSize > deviceListSize_) { + deviceEvent = DeviceEvent::Added; + } else if (currentDeviceListSize < deviceListSize_) { + /* + * Check if the currentCaptureDevice is still in the device list. + */ + if (std::find(std::begin(deviceList), std::end(deviceList), currentCaptureDevice) + == std::end(deviceList)) { + deviceEvent = DeviceEvent::RemovedCurrent; + } + } + + if (previewFrameWrapper_->isRendering()) { + if (currentDeviceListSize == 0) { + avModel_.clearCurrentVideoCaptureDevice(); + avModel_.switchInputTo({}); + stopPreviewing(); + } else if (deviceEvent == DeviceEvent::RemovedCurrent && currentDeviceListSize > 0) { + avModel_.setCurrentVideoCaptureDevice(defaultDevice); + startPreviewing(true); + } else { + startPreviewing(); + } + } else if (deviceEvent == DeviceEvent::Added && currentDeviceListSize == 1) { + avModel_.setCurrentVideoCaptureDevice(defaultDevice); + } + + emit videoDeviceListChanged(); + + deviceListSize_ = currentDeviceListSize; +} diff --git a/src/rendermanager.h b/src/rendermanager.h new file mode 100644 index 00000000..edb6017e --- /dev/null +++ b/src/rendermanager.h @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "api/avmodel.h" +#include "api/lrc.h" + +#include +#include +#include + +using namespace lrc::api; + +/* + * This class acts as a QImage rendering sink and manages + * signal/slot connections to it's underlying (AVModel) renderer + * corresponding to the object's renderer id. + * A QImage pointer is provisioned and updated once rendering + * starts. + */ + +struct RenderConnections +{ + QMetaObject::Connection started, stopped, updated; +}; + +class FrameWrapper final : public QObject +{ + Q_OBJECT; + +public: + FrameWrapper(AVModel &avModel, const QString &id = video::PREVIEW_RENDERER_ID); + ~FrameWrapper(); + + /* + * Reconnect the started rendering connection for this object. + */ + void connectStartRendering(); + + /* + * Get a pointer to the renderer and reconnect the update/stopped + * rendering connections for this object. + * @return whether the start succeeded or not + */ + bool startRendering(); + + /* + * Get the most recently rendered frame as a QImage. + * @return the rendered image of this object's id + */ + QImage *getFrame(); + + /* + * Check if the object is updating actively. + */ + bool isRendering(); + +signals: + /* + * Emitted once in slotRenderingStarted. + * @param id of the renderer + */ + void renderingStarted(const QString &id); + /* + * Emitted each time a frame is ready to be displayed. + * @param id of the renderer + */ + void frameUpdated(const QString &id); + /* + * Emitted once in slotRenderingStopped. + * @param id of the renderer + */ + void renderingStopped(const QString &id); + +private slots: + /* + * Used to listen to AVModel::rendererStarted. + * @param id of the renderer + */ + void slotRenderingStarted(const QString &id = video::PREVIEW_RENDERER_ID); + /* + * Used to listen to AVModel::frameUpdated. + * @param id of the renderer + */ + void slotFrameUpdated(const QString &id = video::PREVIEW_RENDERER_ID); + /* + * Used to listen to AVModel::renderingStopped. + * @param id of the renderer + */ + void slotRenderingStopped(const QString &id = video::PREVIEW_RENDERER_ID); + +private: + /* + * The id of the renderer. + */ + QString id_; + + /* + * A pointer to the lrc renderer object. + */ + video::Renderer *renderer_; + + /* + * A local copy of the renderer's current frame. + */ + video::Frame frame_; + + /* + * A the frame's storage data used to set the image. + */ + std::vector buffer_; + + /* + * The frame's paint ready QImage. + */ + std::unique_ptr image_; + + /* + * Used to protect the buffer during QImage creation routine. + */ + QMutex mutex_; + + /* + * True if the object is rendering + */ + std::atomic_bool isRendering_; + + /* + * Convenience ref to avmodel + */ + AVModel &avModel_; + + /* + * Connections to the underlying renderer signals in avmodel + */ + RenderConnections renderConnections_; +}; + +/** + * RenderManager filters signals and ecapsulates preview and distant + * frame wrappers, providing access to QImages for each and simplified + * start/stop mechanisms for renderers. It should contain as much + * renderer control logic as possible and prevent ui widgets from directly + * interfacing the rendering logic. + */ +class RenderManager final : public QObject +{ + Q_OBJECT; + +public: + explicit RenderManager(AVModel &avModel); + ~RenderManager(); + + /* + * Check if the preview is active. + */ + bool isPreviewing(); + /* + * Get the most recently rendered preview frame as a QImage. + * @return the rendered preview image + */ + QImage *getPreviewFrame(); + /* + * Start capturing and rendering preview frames. + * @param force if the capture device should be started + * @param async + */ + void startPreviewing(bool force = false, bool async = true); + /* + * Stop capturing. + * @param async + */ + void stopPreviewing(bool async = true); + + /* + * Get the most recently rendered distant frame for a given id + * as a QImage. + * @return the rendered preview image + */ + QImage *getFrame(const QString &id); + /* + * Add and connect a distant renderer for a given id + * to a FrameWrapper object + * @param id + */ + void addDistantRenderer(const QString &id); + /* + * Disconnect and remove a FrameWrapper object connected to a + * distant renderer for a given id + * @param id + */ + void removeDistantRenderer(const QString &id); + +signals: + /* + * Emitted when the size of the video capture device list changes. + */ + void videoDeviceListChanged(); + + /* + * Emitted when the preview is started. + */ + void previewRenderingStarted(); + + /* + * Emitted when the preview has a new frame ready. + */ + void previewFrameUpdated(); + + /* + * Emitted when the preview is stopped. + */ + void previewRenderingStopped(); + + /* + * Emitted when a distant renderer is started for a given id. + */ + void distantRenderingStarted(const QString &id); + + /* + * Emitted when a distant renderer has a new frame ready for a given id. + */ + void distantFrameUpdated(const QString &id); + + /* + * Emitted when a distant renderer is stopped for a given id. + */ + void distantRenderingStopped(const QString &id); + +private slots: + /* + * Used to listen to AVModel::deviceEvent. + */ + void slotDeviceEvent(); + +private: + /* + * Used to classify capture device events. + */ + enum class DeviceEvent { Added, RemovedCurrent, None }; + + /* + * Used to track the capture device count. + */ + int deviceListSize_; + + /* + * One preview frame. + */ + std::unique_ptr previewFrameWrapper_; + + /* + * Distant for each call/conf/conversation. + */ + std::map> distantFrameWrapperMap_; + std::map distantConnectionMap_; + + /* + * Convenience ref to avmodel. + */ + AVModel &avModel_; +}; +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) +Q_DECLARE_METATYPE(RenderManager *) +#endif diff --git a/src/runguard.cpp b/src/runguard.cpp new file mode 100644 index 00000000..4d22959d --- /dev/null +++ b/src/runguard.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +// Based on: https://stackoverflow.com/a/28172162 + +#include "runguard.h" +#include + +namespace { + +QString +generateKeyHash(const QString &key, const QString &salt) +{ + QByteArray data; + + data.append(key.toUtf8()); + data.append(salt.toUtf8()); + data = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex(); + + return data; +} + +} // namespace + +RunGuard::RunGuard(const QString &key) + : key_(key) + , memLockKey_(generateKeyHash(key, "_memLockKey")) + , sharedmemKey_(generateKeyHash(key, "_sharedmemKey")) + , sharedMem_(sharedmemKey_) + , memLock_(memLockKey_, 1) +{} + +RunGuard::~RunGuard() +{ + release(); +} + +void +RunGuard::tryRestorePrimaryInstance() +{ + /* + * TODO: relaunch application + */ +} + +bool +RunGuard::isAnotherRunning() +{ + if (sharedMem_.isAttached()) + return false; + + memLock_.acquire(); + const bool isRunning = sharedMem_.attach(); + if (isRunning) + sharedMem_.detach(); + memLock_.release(); + + return isRunning; +} + +bool +RunGuard::tryToRun() +{ +#ifdef Q_OS_WIN + if (isAnotherRunning()) { + /* + * This is a secondary instance, + * connect to the primary instance to trigger a restore + * then fail. + */ + if (socket_ == nullptr) { + socket_ = new QLocalSocket(); + } + if (socket_->state() == QLocalSocket::UnconnectedState + || socket_->state() == QLocalSocket::ClosingState) { + socket_->connectToServer(key_); + } + if (socket_->state() == QLocalSocket::ConnectingState) { + socket_->waitForConnected(); + } + return false; + } + + memLock_.acquire(); + const bool result = sharedMem_.create(sizeof(quint64)); + memLock_.release(); + if (!result) { + release(); + return false; + } + + /* + * This is the primary instance, + * listen for subsequent instances. + */ + QLocalServer::removeServer(key_); + server_ = new QLocalServer(); + server_->setSocketOptions(QLocalServer::UserAccessOption); + server_->listen(key_); + QObject::connect(server_, + &QLocalServer::newConnection, + this, + &RunGuard::tryRestorePrimaryInstance); +#endif + + return true; +} + +void +RunGuard::release() +{ + memLock_.acquire(); + if (sharedMem_.isAttached()) + sharedMem_.detach(); + memLock_.release(); +} \ No newline at end of file diff --git a/src/runguard.h b/src/runguard.h new file mode 100644 index 00000000..6e0f0d76 --- /dev/null +++ b/src/runguard.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Andreas Traczyk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +// Based on: https://stackoverflow.com/a/28172162 + +#pragma once + +#include +#include +#include +#include +#include + +class RunGuard : public QObject +{ + Q_OBJECT; + +public: + RunGuard(const QString &key); + ~RunGuard(); + + bool isAnotherRunning(); + bool tryToRun(); + void release(); + +private slots: + void tryRestorePrimaryInstance(); + +private: + const QString key_; + const QString memLockKey_; + const QString sharedmemKey_; + + QSharedMemory sharedMem_; + QSystemSemaphore memLock_; + + QLocalSocket *socket_; + QLocalServer *server_; + + Q_DISABLE_COPY(RunGuard) +}; \ No newline at end of file diff --git a/src/settingsadaptor.cpp b/src/settingsadaptor.cpp new file mode 100644 index 00000000..471b25f5 --- /dev/null +++ b/src/settingsadaptor.cpp @@ -0,0 +1,1047 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "settingsadaptor.h" + +#include "api/newdevicemodel.h" + +SettingsAdaptor::SettingsAdaptor(QObject *parent) + : QObject(parent) +{} + +///Singleton +SettingsAdaptor & +SettingsAdaptor::instance() +{ + static auto instance = new SettingsAdaptor; + return *instance; +} + +QString +SettingsAdaptor::getDir_Document() +{ + return QDir::toNativeSeparators( + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); +} + +QString +SettingsAdaptor::getDir_Download() +{ + QString downloadPath = QDir::toNativeSeparators(LRCInstance::dataTransferModel().downloadDirectory); + if (downloadPath.isEmpty()) { + downloadPath = lrc::api::DataTransferModel::createDefaultDirectory(); + setDownloadPath(downloadPath); + LRCInstance::dataTransferModel().downloadDirectory = downloadPath; + } +#ifdef Q_OS_WIN + int pos = downloadPath.lastIndexOf(QChar('\\')); +#else + int pos = downloadPath.lastIndexOf(QChar('/')); +#endif + if (pos == downloadPath.length() - 1) + downloadPath.truncate(pos); + return downloadPath; +} + +bool +SettingsAdaptor::getSettingsValue_CloseOrMinimized() +{ + QSettings settings("jami.net", "Jami"); + return settings.value(SettingsKey::closeOrMinimized).toBool(); +} + +bool +SettingsAdaptor::getSettingsValue_EnableNotifications() +{ + QSettings settings("jami.net", "Jami"); + return settings.value(SettingsKey::enableNotifications).toBool(); +} + +bool +SettingsAdaptor::getSettingsValue_AutoUpdate() +{ + QSettings settings("jami.net", "Jami"); + return settings.value(SettingsKey::autoUpdate).toBool(); +} + +void +SettingsAdaptor::setClosedOrMin(bool state) +{ + QSettings settings("jami.net", "Jami"); + settings.setValue(SettingsKey::closeOrMinimized, state); +} + +void +SettingsAdaptor::setNotifications(bool state) +{ + QSettings settings("jami.net", "Jami"); + settings.setValue(SettingsKey::enableNotifications, state); +} + +void +SettingsAdaptor::setUpdateAutomatic(bool state) +{ +#ifdef Q_OS_WIN + QSettings settings("jami.net", "Jami"); + settings.setValue(SettingsKey::autoUpdate, state); +#endif +} + +void +SettingsAdaptor::setRunOnStartUp(bool state) +{ + if (Utils::CheckStartupLink(L"Jami")) { + if (!state) { + Utils::DeleteStartupLink(L"Jami"); + } + } else if (state) { + Utils::CreateStartupLink(L"Jami"); + } +} + +void +SettingsAdaptor::setDownloadPath(QString dir) +{ + QSettings settings("jami.net", "Jami"); + settings.setValue(SettingsKey::downloadPath, dir); + LRCInstance::dataTransferModel().downloadDirectory = dir + "/"; +} + +lrc::api::video::ResRateList +SettingsAdaptor::get_ResRateList(lrc::api::video::Channel channel, QString device) +{ + auto deviceCapabilities = get_DeviceCapabilities(device); + + return deviceCapabilities[channel]; +} + +int +SettingsAdaptor::get_DeviceCapabilitiesSize(const QString &device) +{ + return get_DeviceCapabilities(device).size(); +} + +QVector +SettingsAdaptor::getResolutions(const QString &device) +{ + QVector resolutions; + + auto currentSettings = LRCInstance::avModel().getDeviceSettings(device); + + auto currentChannel = currentSettings.channel.isEmpty() ? "default" : currentSettings.channel; + auto channelCaps = get_ResRateList(currentChannel, device); + for (auto [resolution, frameRateList] : channelCaps) { + for (auto rate : frameRateList) { + resolutions.append(resolution); + } + } + + return resolutions; +} + +QVector +SettingsAdaptor::getFrameRates(const QString &device) +{ + QVector rates; + + auto currentSettings = LRCInstance::avModel().getDeviceSettings(device); + + auto currentChannel = currentSettings.channel.isEmpty() ? "default" : currentSettings.channel; + auto channelCaps = get_ResRateList(currentChannel, device); + for (auto [resolution, frameRateList] : channelCaps) { + for (auto rate : frameRateList) { + rates.append((int) rate); + } + } + + return rates; +} + +lrc::api::video::Capabilities +SettingsAdaptor::get_DeviceCapabilities(const QString &device) +{ + return LRCInstance::avModel().getDeviceCapabilities(device); +} + +QString +SettingsAdaptor::get_Video_Settings_Channel(const QString &deviceId) +{ + auto settings = LRCInstance::avModel().getDeviceSettings(deviceId); + + return (QString) settings.channel; +} + +QString +SettingsAdaptor::get_Video_Settings_Name(const QString &deviceId) +{ + auto settings = LRCInstance::avModel().getDeviceSettings(deviceId); + + return (QString) settings.name; +} + +QString +SettingsAdaptor::get_Video_Settings_Id(const QString &deviceId) +{ + auto settings = LRCInstance::avModel().getDeviceSettings(deviceId); + + return (QString) settings.id; +} + +qreal +SettingsAdaptor::get_Video_Settings_Rate(const QString &deviceId) +{ + auto settings = LRCInstance::avModel().getDeviceSettings(deviceId); + + return (qreal) settings.rate; +} + +QString +SettingsAdaptor::get_Video_Settings_Size(const QString &deviceId) +{ + auto settings = LRCInstance::avModel().getDeviceSettings(deviceId); + + return (QString) settings.size; +} + +void +SettingsAdaptor::set_Video_Settings_Rate_And_Resolution(const QString &deviceId, + qreal rate, + const QString &resolution) +{ + auto settings = LRCInstance::avModel().getDeviceSettings(deviceId); + settings.rate = rate; + settings.size = resolution; + LRCInstance::avModel().setDeviceSettings(settings); +} + +const lrc::api::account::Info & +SettingsAdaptor::getCurrentAccountInfo() +{ + return LRCInstance::getCurrentAccountInfo(); +} + +const Q_INVOKABLE lrc::api::profile::Info & +SettingsAdaptor::getCurrentAccount_Profile_Info() +{ + return LRCInstance::getCurrentAccountInfo().profileInfo; +} + +lrc::api::ContactModel * +SettingsAdaptor::getContactModel() +{ + return getCurrentAccountInfo().contactModel.get(); +} + +lrc::api::NewDeviceModel * +SettingsAdaptor::getDeviceModel() +{ + return getCurrentAccountInfo().deviceModel.get(); +} + +QString +SettingsAdaptor::get_CurrentAccountInfo_RegisteredName() +{ + return LRCInstance::getCurrentAccountInfo().registeredName; +} + +QString +SettingsAdaptor::get_CurrentAccountInfo_Id() +{ + return LRCInstance::getCurrentAccountInfo().id; +} + +bool +SettingsAdaptor::get_CurrentAccountInfo_Enabled() +{ + return LRCInstance::getCurrentAccountInfo().enabled; +} + +QString +SettingsAdaptor::getCurrentAccount_Profile_Info_Uri() +{ + return getCurrentAccount_Profile_Info().uri; +} + +QString +SettingsAdaptor::getCurrentAccount_Profile_Info_Alias() +{ + return getCurrentAccount_Profile_Info().alias; +} + +int +SettingsAdaptor::getCurrentAccount_Profile_Info_Type() +{ + return (int) (getCurrentAccount_Profile_Info().type); +} + +QString +SettingsAdaptor::getAccountBestName() +{ + return Utils::bestNameForAccount(LRCInstance::getCurrentAccountInfo()); +} + +QString +SettingsAdaptor::getAvatarImage_Base64(int avatarSize) +{ + auto &accountInfo = LRCInstance::getCurrentAccountInfo(); + auto avatar = Utils::accountPhoto(accountInfo, {avatarSize, avatarSize}); + + return QString::fromLatin1(Utils::QImageToByteArray(avatar).toBase64().data()); +} + +bool +SettingsAdaptor::getIsDefaultAvatar() +{ + auto &accountInfo = LRCInstance::getCurrentAccountInfo(); + + return accountInfo.profileInfo.avatar.isEmpty(); +} + +bool +SettingsAdaptor::setCurrAccAvatar(QString avatarImgBase64) +{ + QImage avatarImg; + const bool ret = avatarImg.loadFromData(QByteArray::fromBase64(avatarImgBase64.toLatin1())); + if (!ret) { + qDebug() << "Current avatar loading from base64 fail"; + return false; + } else { + LRCInstance::setCurrAccAvatar(QPixmap::fromImage(avatarImg)); + } + return true; +} + +void +SettingsAdaptor::clearCurrentAvatar() +{ + LRCInstance::setCurrAccAvatar(QPixmap()); +} + +lrc::api::account::ConfProperties_t +SettingsAdaptor::getAccountConfig() +{ + return LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); +} + +QString +SettingsAdaptor::getAccountConfig_Manageruri() +{ + return getAccountConfig().managerUri; +} + +QString +SettingsAdaptor::getAccountConfig_Username() +{ + return getAccountConfig().username; +} + +QString +SettingsAdaptor::getAccountConfig_Hostname() +{ + return getAccountConfig().hostname; +} + +QString +SettingsAdaptor::getAccountConfig_Password() +{ + return getAccountConfig().password; +} + +QString +SettingsAdaptor::getAccountConfig_ProxyServer() +{ + return getAccountConfig().proxyServer; +} + +bool +SettingsAdaptor::getAccountConfig_PeerDiscovery() +{ + return getAccountConfig().peerDiscovery; +} + +bool +SettingsAdaptor::getAccountConfig_DHT_PublicInCalls() +{ + return getAccountConfig().DHT.PublicInCalls; +} + +bool +SettingsAdaptor::getAccountConfig_AutoAnswer() +{ + return getAccountConfig().autoAnswer; +} + +QString +SettingsAdaptor::getAccountConfig_RingNS_Uri() +{ + return getAccountConfig().RingNS.uri; +} + +bool +SettingsAdaptor::getAccountConfig_ProxyEnabled() +{ + return getAccountConfig().proxyEnabled; +} + +QString +SettingsAdaptor::getAccountConfig_TLS_CertificateListFile() +{ + return getAccountConfig().TLS.certificateListFile; +} + +QString +SettingsAdaptor::getAccountConfig_TLS_CertificateFile() +{ + return getAccountConfig().TLS.certificateFile; +} + +QString +SettingsAdaptor::getAccountConfig_TLS_PrivateKeyFile() +{ + return getAccountConfig().TLS.privateKeyFile; +} + +bool +SettingsAdaptor::getAccountConfig_TLS_Enable() +{ + return getAccountConfig().TLS.enable; +} + +QString +SettingsAdaptor::getAccountConfig_TLS_Password() +{ + return getAccountConfig().TLS.password; +} + +bool +SettingsAdaptor::getAccountConfig_TLS_VerifyServer() +{ + return getAccountConfig().TLS.verifyServer; +} + +bool +SettingsAdaptor::getAccountConfig_TLS_VerifyClient() +{ + return getAccountConfig().TLS.verifyClient; +} + +bool +SettingsAdaptor::getAccountConfig_TLS_RequireClientCertificate() +{ + return getAccountConfig().TLS.requireClientCertificate; +} + +int +SettingsAdaptor::getAccountConfig_TLS_Method_inInt() +{ + return (int) getAccountConfig().TLS.method; +} + +QString +SettingsAdaptor::getAccountConfig_TLS_Servername() +{ + return getAccountConfig().TLS.serverName; +} + +int +SettingsAdaptor::getAccountConfig_TLS_NegotiationTimeoutSec() +{ + return getAccountConfig().TLS.negotiationTimeoutSec; +} + +bool +SettingsAdaptor::getAccountConfig_SRTP_Enabled() +{ + return getAccountConfig().SRTP.enable; +} + +int +SettingsAdaptor::getAccountConfig_SRTP_KeyExchange() +{ + return (int) getAccountConfig().SRTP.keyExchange; +} + +bool +SettingsAdaptor::getAccountConfig_SRTP_RtpFallback() +{ + return getAccountConfig().SRTP.rtpFallback; +} + +bool +SettingsAdaptor::getAccountConfig_UpnpEnabled() +{ + return getAccountConfig().upnpEnabled; +} + +bool +SettingsAdaptor::getAccountConfig_TURN_Enabled() +{ + return getAccountConfig().TURN.enable; +} + +QString +SettingsAdaptor::getAccountConfig_TURN_Server() +{ + return getAccountConfig().TURN.server; +} + +QString +SettingsAdaptor::getAccountConfig_TURN_Username() +{ + return getAccountConfig().TURN.username; +} + +QString +SettingsAdaptor::getAccountConfig_TURN_Password() +{ + return getAccountConfig().TURN.password; +} + +QString +SettingsAdaptor::getAccountConfig_TURN_Realm() +{ + return getAccountConfig().TURN.realm; +} + +bool +SettingsAdaptor::getAccountConfig_STUN_Enabled() +{ + return getAccountConfig().STUN.enable; +} + +QString +SettingsAdaptor::getAccountConfig_STUN_Server() +{ + return getAccountConfig().STUN.server; +} + +bool +SettingsAdaptor::getAccountConfig_Video_Enabled() +{ + return getAccountConfig().Video.videoEnabled; +} + +int +SettingsAdaptor::getAccountConfig_Video_VideoPortMin() +{ + return getAccountConfig().Video.videoPortMin; +} + +int +SettingsAdaptor::getAccountConfig_Video_VideoPortMax() +{ + return getAccountConfig().Video.videoPortMax; +} + +int +SettingsAdaptor::getAccountConfig_Audio_AudioPortMin() +{ + return getAccountConfig().Audio.audioPortMin; +} + +int +SettingsAdaptor::getAccountConfig_Audio_AudioPortMax() +{ + return getAccountConfig().Audio.audioPortMax; +} + +bool +SettingsAdaptor::getAccountConfig_Ringtone_RingtoneEnabled() +{ + return getAccountConfig().Ringtone.ringtoneEnabled; +} + +QString +SettingsAdaptor::getAccountConfig_Ringtone_RingtonePath() +{ + return getAccountConfig().Ringtone.ringtonePath; +} + +int +SettingsAdaptor::getAccountConfig_Registration_Expire() +{ + return getAccountConfig().Registration.expire; +} + +int +SettingsAdaptor::getAccountConfig_Localport() +{ + return getAccountConfig().localPort; +} + +bool +SettingsAdaptor::getAccountConfig_PublishedSameAsLocal() +{ + return getAccountConfig().publishedSameAsLocal; +} + +QString +SettingsAdaptor::getAccountConfig_PublishedAddress() +{ + return getAccountConfig().publishedAddress; +} + +int +SettingsAdaptor::getAccountConfig_PublishedPort() +{ + return getAccountConfig().publishedPort; +} + +QString +SettingsAdaptor::getAccountConfig_Mailbox() +{ + return getAccountConfig().mailbox; +} + +void +SettingsAdaptor::setAccountConfig_Username(QString input) +{ + auto confProps = getAccountConfig(); + confProps.username = input; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setAccountConfig_Hostname(QString input) +{ + auto confProps = getAccountConfig(); + confProps.hostname = input; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setAccountConfig_Password(QString input) +{ + auto confProps = getAccountConfig(); + confProps.password = input; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setAccountConfig_ProxyServer(QString input) +{ + auto confProps = getAccountConfig(); + confProps.proxyServer = input; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setAutoConnectOnLocalNetwork(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.peerDiscovery = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setCallsUntrusted(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.DHT.PublicInCalls = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setAutoAnswerCalls(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.autoAnswer = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setEnableRingtone(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.Ringtone.ringtoneEnabled = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setEnableProxy(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.proxyEnabled = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setUseUPnP(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.upnpEnabled = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setUseTURN(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TURN.enable = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setUseSTUN(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.STUN.enable = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setVideoState(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.Video.videoEnabled = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setUseSRTP(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.SRTP.enable = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setUseSDES(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.SRTP.keyExchange = state ? lrc::api::account::KeyExchangeProtocol::SDES + : lrc::api::account::KeyExchangeProtocol::NONE; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setUseRTPFallback(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.SRTP.rtpFallback = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setUseTLS(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.enable = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setVerifyCertificatesServer(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.verifyServer = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setVerifyCertificatesClient(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.verifyClient = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setRequireCertificatesIncomingTLS(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.requireClientCertificate = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setUseCustomAddressAndPort(bool state) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.publishedSameAsLocal = state; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setNameServer(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.RingNS.uri = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setProxyAddress(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.proxyServer = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setBootstrapAddress(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.hostname = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setTURNAddress(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TURN.server = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setTURNUsername(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TURN.username = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setTURNPassword(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TURN.password = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setTURNRealm(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TURN.realm = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::setSTUNAddress(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.STUN.server = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::lineEditVoiceMailDialCodeEditFinished(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.mailbox = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::outgoingTLSServerNameLineEditTextChanged(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.serverName = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::lineEditSIPCertPasswordLineEditTextChanged(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.password = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::lineEditSIPCustomAddressLineEditTextChanged(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.publishedAddress = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::customPortSIPSpinBoxValueChanged(int value) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.publishedPort = value; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::negotiationTimeoutSpinBoxValueChanged(int value) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.negotiationTimeoutSec = value; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::registrationTimeoutSpinBoxValueChanged(int value) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.Registration.expire = value; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::networkInterfaceSpinBoxValueChanged(int value) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.localPort = value; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::audioRTPMinPortSpinBoxEditFinished(int value) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.Audio.audioPortMin = value; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::audioRTPMaxPortSpinBoxEditFinished(int value) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.Audio.audioPortMax = value; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::videoRTPMinPortSpinBoxEditFinished(int value) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.Video.videoPortMin = value; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::videoRTPMaxPortSpinBoxEditFinished(int value) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.Video.videoPortMax = value; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::tlsProtocolComboBoxIndexChanged(const int &index) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + + if (static_cast(confProps.TLS.method) != index) { + if (index == 0) { + confProps.TLS.method = lrc::api::account::TlsMethod::DEFAULT; + } else if (index == 1) { + confProps.TLS.method = lrc::api::account::TlsMethod::TLSv1; + } else if (index == 2) { + confProps.TLS.method = lrc::api::account::TlsMethod::TLSv1_1; + } else { + confProps.TLS.method = lrc::api::account::TlsMethod::TLSv1_2; + } + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); + } +} + +void +SettingsAdaptor::setDeviceName(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.deviceName = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::unbanContact(int index) +{ + auto bannedContactList = LRCInstance::getCurrentAccountInfo().contactModel->getBannedContacts(); + auto it = bannedContactList.begin(); + std::advance(it, index); + + auto contactInfo = LRCInstance::getCurrentAccountInfo().contactModel->getContact(*it); + + LRCInstance::getCurrentAccountInfo().contactModel->addContact(contactInfo); +} + +void +SettingsAdaptor::audioCodecsStateChange(unsigned int id, bool isToEnable) +{ + auto audioCodecList = LRCInstance::getCurrentAccountInfo().codecModel->getAudioCodecs(); + LRCInstance::getCurrentAccountInfo().codecModel->enable(id, isToEnable); +} + +void +SettingsAdaptor::videoCodecsStateChange(unsigned int id, bool isToEnable) +{ + auto videoCodecList = LRCInstance::getCurrentAccountInfo().codecModel->getVideoCodecs(); + LRCInstance::getCurrentAccountInfo().codecModel->enable(id, isToEnable); +} + +void +SettingsAdaptor::decreaseAudioCodecPriority(unsigned int id) +{ + LRCInstance::getCurrentAccountInfo().codecModel->decreasePriority(id, false); +} + +void +SettingsAdaptor::increaseAudioCodecPriority(unsigned int id) +{ + LRCInstance::getCurrentAccountInfo().codecModel->increasePriority(id, false); +} + +void +SettingsAdaptor::decreaseVideoCodecPriority(unsigned int id) +{ + LRCInstance::getCurrentAccountInfo().codecModel->decreasePriority(id, true); +} + +void +SettingsAdaptor::increaseVideoCodecPriority(unsigned int id) +{ + LRCInstance::getCurrentAccountInfo().codecModel->increasePriority(id, true); +} + +void +SettingsAdaptor::set_RingtonePath(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.Ringtone.ringtonePath = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::set_FileCACert(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.certificateListFile = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::set_FileUserCert(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.certificateFile = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} + +void +SettingsAdaptor::set_FilePrivateKey(QString text) +{ + auto confProps = LRCInstance::accountModel().getAccountConfig(LRCInstance::getCurrAccId()); + confProps.TLS.privateKeyFile = text; + LRCInstance::accountModel().setAccountConfig(LRCInstance::getCurrAccId(), confProps); +} diff --git a/src/settingsadaptor.h b/src/settingsadaptor.h new file mode 100644 index 00000000..eec7784d --- /dev/null +++ b/src/settingsadaptor.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +#include "api/account.h" +#include "api/datatransfermodel.h" +#include "lrcinstance.h" +#include "typedefs.h" +#include "utils.h" + +class SettingsAdaptor : public QObject +{ + Q_OBJECT +public: + explicit SettingsAdaptor(QObject *parent = nullptr); + + //Singleton + static SettingsAdaptor &instance(); + /* + * getters of directories + */ + Q_INVOKABLE QString getDir_Document(); + Q_INVOKABLE QString getDir_Download(); + + /* + * getters and setters of app settings options + */ + Q_INVOKABLE bool getSettingsValue_CloseOrMinimized(); + Q_INVOKABLE bool getSettingsValue_EnableNotifications(); + Q_INVOKABLE bool getSettingsValue_AutoUpdate(); + + Q_INVOKABLE void setClosedOrMin(bool state); + Q_INVOKABLE void setNotifications(bool state); + Q_INVOKABLE void setUpdateAutomatic(bool state); + Q_INVOKABLE void setRunOnStartUp(bool state); + Q_INVOKABLE void setDownloadPath(QString dir); + + /* + * getters of devices' Info and options + */ + Q_INVOKABLE lrc::api::video::Capabilities get_DeviceCapabilities(const QString &device); + Q_INVOKABLE lrc::api::video::ResRateList get_ResRateList(lrc::api::video::Channel channel, + QString device); + Q_INVOKABLE int get_DeviceCapabilitiesSize(const QString &device); + + /* + * getters of resolution and frame rates of current device + */ + Q_INVOKABLE QVector getResolutions(const QString &device); + Q_INVOKABLE QVector getFrameRates(const QString &device); + + /* + * getters and setters: lrc video::setting + */ + Q_INVOKABLE QString get_Video_Settings_Channel(const QString &deviceId); + Q_INVOKABLE QString get_Video_Settings_Name(const QString &deviceId); + Q_INVOKABLE QString get_Video_Settings_Id(const QString &deviceId); + Q_INVOKABLE qreal get_Video_Settings_Rate(const QString &deviceId); + Q_INVOKABLE QString get_Video_Settings_Size(const QString &deviceId); + + Q_INVOKABLE void set_Video_Settings_Rate_And_Resolution(const QString &deviceId, + qreal rate, + const QString &resolution); + + /* + * getters and setters of current account Info + */ + const Q_INVOKABLE lrc::api::account::Info &getCurrentAccountInfo(); + const Q_INVOKABLE lrc::api::profile::Info &getCurrentAccount_Profile_Info(); + + Q_INVOKABLE lrc::api::ContactModel *getContactModel(); + Q_INVOKABLE lrc::api::NewDeviceModel *getDeviceModel(); + + Q_INVOKABLE QString get_CurrentAccountInfo_RegisteredName(); + Q_INVOKABLE QString get_CurrentAccountInfo_Id(); + Q_INVOKABLE bool get_CurrentAccountInfo_Enabled(); + + // profile info + Q_INVOKABLE QString getCurrentAccount_Profile_Info_Uri(); + Q_INVOKABLE QString getCurrentAccount_Profile_Info_Alias(); + Q_INVOKABLE int getCurrentAccount_Profile_Info_Type(); + Q_INVOKABLE QString getAccountBestName(); + + // getters and setters of avatar image + Q_INVOKABLE QString getAvatarImage_Base64(int avatarSize); + Q_INVOKABLE bool getIsDefaultAvatar(); + Q_INVOKABLE bool setCurrAccAvatar(QString avatarImgBase64); + Q_INVOKABLE void clearCurrentAvatar(); + + /* + * getters and setters of ConfProperties_t + */ + // getters + Q_INVOKABLE lrc::api::account::ConfProperties_t getAccountConfig(); + Q_INVOKABLE QString getAccountConfig_Manageruri(); + Q_INVOKABLE QString getAccountConfig_Username(); + Q_INVOKABLE QString getAccountConfig_Hostname(); + Q_INVOKABLE QString getAccountConfig_Password(); + + Q_INVOKABLE QString getAccountConfig_ProxyServer(); + Q_INVOKABLE bool getAccountConfig_ProxyEnabled(); + + Q_INVOKABLE bool getAccountConfig_PeerDiscovery(); + Q_INVOKABLE bool getAccountConfig_DHT_PublicInCalls(); + Q_INVOKABLE bool getAccountConfig_AutoAnswer(); + + Q_INVOKABLE QString getAccountConfig_RingNS_Uri(); + + Q_INVOKABLE QString getAccountConfig_TLS_CertificateListFile(); + Q_INVOKABLE QString getAccountConfig_TLS_CertificateFile(); + Q_INVOKABLE QString getAccountConfig_TLS_PrivateKeyFile(); + Q_INVOKABLE bool getAccountConfig_TLS_Enable(); + Q_INVOKABLE QString getAccountConfig_TLS_Password(); + Q_INVOKABLE bool getAccountConfig_TLS_VerifyServer(); + Q_INVOKABLE bool getAccountConfig_TLS_VerifyClient(); + Q_INVOKABLE bool getAccountConfig_TLS_RequireClientCertificate(); + Q_INVOKABLE int getAccountConfig_TLS_Method_inInt(); + Q_INVOKABLE QString getAccountConfig_TLS_Servername(); + Q_INVOKABLE int getAccountConfig_TLS_NegotiationTimeoutSec(); + + Q_INVOKABLE bool getAccountConfig_SRTP_Enabled(); + Q_INVOKABLE int getAccountConfig_SRTP_KeyExchange(); + Q_INVOKABLE bool getAccountConfig_SRTP_RtpFallback(); + + Q_INVOKABLE bool getAccountConfig_UpnpEnabled(); + Q_INVOKABLE bool getAccountConfig_TURN_Enabled(); + Q_INVOKABLE QString getAccountConfig_TURN_Server(); + Q_INVOKABLE QString getAccountConfig_TURN_Username(); + Q_INVOKABLE QString getAccountConfig_TURN_Password(); + Q_INVOKABLE QString getAccountConfig_TURN_Realm(); + + Q_INVOKABLE bool getAccountConfig_STUN_Enabled(); + Q_INVOKABLE QString getAccountConfig_STUN_Server(); + + Q_INVOKABLE bool getAccountConfig_Video_Enabled(); + Q_INVOKABLE int getAccountConfig_Video_VideoPortMin(); + Q_INVOKABLE int getAccountConfig_Video_VideoPortMax(); + + Q_INVOKABLE int getAccountConfig_Audio_AudioPortMin(); + Q_INVOKABLE int getAccountConfig_Audio_AudioPortMax(); + + Q_INVOKABLE bool getAccountConfig_Ringtone_RingtoneEnabled(); + Q_INVOKABLE QString getAccountConfig_Ringtone_RingtonePath(); + + Q_INVOKABLE int getAccountConfig_Registration_Expire(); + Q_INVOKABLE int getAccountConfig_Localport(); + Q_INVOKABLE bool getAccountConfig_PublishedSameAsLocal(); + Q_INVOKABLE QString getAccountConfig_PublishedAddress(); + Q_INVOKABLE int getAccountConfig_PublishedPort(); + + Q_INVOKABLE QString getAccountConfig_Mailbox(); + + // setters + Q_INVOKABLE void setAccountConfig_Username(QString input); + Q_INVOKABLE void setAccountConfig_Hostname(QString input); + Q_INVOKABLE void setAccountConfig_Password(QString input); + Q_INVOKABLE void setAccountConfig_ProxyServer(QString input); + + Q_INVOKABLE void setAutoConnectOnLocalNetwork(bool state); + Q_INVOKABLE void setCallsUntrusted(bool state); + Q_INVOKABLE void setAutoAnswerCalls(bool state); + Q_INVOKABLE void setEnableRingtone(bool state); + Q_INVOKABLE void setEnableProxy(bool state); + Q_INVOKABLE void setUseUPnP(bool state); + Q_INVOKABLE void setUseTURN(bool state); + Q_INVOKABLE void setUseSTUN(bool state); + Q_INVOKABLE void setVideoState(bool state); + Q_INVOKABLE void setUseSRTP(bool state); + Q_INVOKABLE void setUseSDES(bool state); + Q_INVOKABLE void setUseRTPFallback(bool state); + Q_INVOKABLE void setUseTLS(bool state); + Q_INVOKABLE void setVerifyCertificatesServer(bool state); + Q_INVOKABLE void setVerifyCertificatesClient(bool state); + Q_INVOKABLE void setRequireCertificatesIncomingTLS(bool state); + Q_INVOKABLE void setUseCustomAddressAndPort(bool state); + + Q_INVOKABLE void setNameServer(QString text); + Q_INVOKABLE void setProxyAddress(QString text); + Q_INVOKABLE void setBootstrapAddress(QString text); + Q_INVOKABLE void setTURNAddress(QString text); + Q_INVOKABLE void setTURNUsername(QString text); + Q_INVOKABLE void setTURNPassword(QString text); + Q_INVOKABLE void setTURNRealm(QString text); + Q_INVOKABLE void setSTUNAddress(QString text); + + Q_INVOKABLE void lineEditVoiceMailDialCodeEditFinished(QString text); + Q_INVOKABLE void outgoingTLSServerNameLineEditTextChanged(QString text); + Q_INVOKABLE void lineEditSIPCertPasswordLineEditTextChanged(QString text); + Q_INVOKABLE void lineEditSIPCustomAddressLineEditTextChanged(QString text); + + Q_INVOKABLE void customPortSIPSpinBoxValueChanged(int value); + Q_INVOKABLE void negotiationTimeoutSpinBoxValueChanged(int value); + Q_INVOKABLE void registrationTimeoutSpinBoxValueChanged(int value); + Q_INVOKABLE void networkInterfaceSpinBoxValueChanged(int value); + Q_INVOKABLE void audioRTPMinPortSpinBoxEditFinished(int value); + Q_INVOKABLE void audioRTPMaxPortSpinBoxEditFinished(int value); + Q_INVOKABLE void videoRTPMinPortSpinBoxEditFinished(int value); + Q_INVOKABLE void videoRTPMaxPortSpinBoxEditFinished(int value); + + Q_INVOKABLE void tlsProtocolComboBoxIndexChanged(const int &index); + + Q_INVOKABLE void setDeviceName(QString text); + + Q_INVOKABLE void unbanContact(int index); + + Q_INVOKABLE void audioCodecsStateChange(unsigned int id, bool isToEnable); + Q_INVOKABLE void videoCodecsStateChange(unsigned int id, bool isToEnable); + + Q_INVOKABLE void decreaseAudioCodecPriority(unsigned int id); + Q_INVOKABLE void increaseAudioCodecPriority(unsigned int id); + + Q_INVOKABLE void decreaseVideoCodecPriority(unsigned int id); + Q_INVOKABLE void increaseVideoCodecPriority(unsigned int id); + + Q_INVOKABLE void set_RingtonePath(QString text); + Q_INVOKABLE void set_FileCACert(QString text); + Q_INVOKABLE void set_FileUserCert(QString text); + Q_INVOKABLE void set_FilePrivateKey(QString text); +}; +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) +Q_DECLARE_METATYPE(SettingsAdaptor *) +#endif diff --git a/src/settingskey.h b/src/settingskey.h new file mode 100644 index 00000000..6c82598f --- /dev/null +++ b/src/settingskey.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +namespace SettingsKey { + +constexpr static char closeOrMinimized[] = "closeOrMin"; +constexpr static char downloadPath[] = "downloadPath"; +constexpr static char enableNotifications[] = "enableNotifications"; +constexpr static char geometry[] = "geometry"; +constexpr static char selectedAccount[] = "selectedAccount"; +constexpr static char mainSplitterState[] = "mainSplitterState"; +constexpr static char smartListToWebviewSplitterState[] = "smartListToWebviewSplitterState"; +constexpr static char windowState[] = "windowState"; +constexpr static char autoUpdate[] = "autoUpdate"; +constexpr static char neverShowMeAgain[] = "neverShowMeAgain"; +constexpr static char hasRun[] = "hasRun"; +} // namespace SettingsKey diff --git a/src/settingsview/SettingsView.qml b/src/settingsview/SettingsView.qml new file mode 100644 index 00000000..3cc0f777 --- /dev/null +++ b/src/settingsview/SettingsView.qml @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 + +import "components" + +Rectangle { + id: settingsViewWindow + + enum SettingsMenu{ + Account, + General, + Media, + Plugin + } + + onVisibleChanged: { + if(visible){ + setSelected(selectedMenu,true) + } + } + + function setSelected(sel, recovery = false){ + profileType = ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Type() + + if(selectedMenu === sel && (!recovery)){return} + switch(sel){ + case SettingsView.Account: + currentAccountSettingsScrollWidget.connectCurrentAccount() + + avSettings.stopAudioMeter() + avSettings.stopPreviewing() + + selectedMenu = sel + + if(!settingsViewRect.isSIP){ + if(currentAccountSettingsScrollWidget.isPhotoBoothOpened()) + { + currentAccountSettingsScrollWidget.setAvatar() + } + + currentAccountSettingsScrollWidget.updateAccountInfoDisplayed() + } else { + if(currentSIPAccountSettingsScrollWidget.isPhotoBoothOpened()) { + currentSIPAccountSettingsScrollWidget.setAvatar() + } + currentSIPAccountSettingsScrollWidget.updateAccountInfoDisplayed() + } + break + case SettingsView.General: + try{ + avSettings.stopAudioMeter() + avSettings.stopPreviewing() + } catch(erro){} + + selectedMenu = sel + generalSettings.populateGeneralSettings() + break + case SettingsView.Media: + selectedMenu = sel + + avSettings.stopPreviewing() + avSettings.populateAVSettings() + avSettings.startAudioMeter() + break + case SettingsView.Plugin: + try{ + avSettings.stopAudioMeter() + avSettings.stopPreviewing() + } catch(erro){} + + selectedMenu = sel + pluginSettings.populatePluginSettings() + break + } + } + + Connections{ + id: accountListChangedConnection + target: ClientWrapper.lrcInstance + + function onAccountListChanged(){ + slotAccountListChanged() + accountListChangedConnection.enabled = false + } + } + + // slots + function leaveSettingsSlot(accountDeleted = false, showMainView = true){ + avSettings.stopAudioMeter() + avSettings.stopPreviewing() + if(!settingsViewRect.isSIP){ + currentAccountSettingsScrollWidget.stopBooth() + } else { + currentSIPAccountSettingsScrollWidget.stopBooth() + } + if (showMainView) + settingsViewWindowNeedToShowMainViewWindow(accountDeleted) + else + settingsViewWindowNeedToShowNewWizardWindow() + } + + function slotAccountListChanged(){ + var accountList = ClientWrapper.accountModel.getAccountList() + if(accountList.length === 0) { + setSelected(SettingsView.Account) + } else { + currentAccountSettingsScrollWidget.disconnectAccountConnections() + } + var device = ClientWrapper.avmodel.getDefaultDevice() + if(device.length === 0){ + ClientWrapper.avmodel.setCurrentVideoCaptureDevice(device) + } + } + property int profileType: ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Type() + + + property int selectedMenu: SettingsView.Account + /* + * signal to redirect the page to main view + */ + signal settingsViewWindowNeedToShowMainViewWindow(bool accountDeleted) + signal settingsViewWindowNeedToShowNewWizardWindow + + property int textFontSize: 9 + + visible: true + + Rectangle { + id: settingsViewRect + anchors.fill: parent + + property bool isSIP: { + switch (profileType) { + case Profile.Type.SIP: + return true; + default: + return false; + } + } + + SplitView { + anchors.fill: parent + orientation: Qt.Horizontal + + handle: Rectangle { + implicitWidth: 3 + implicitHeight: 3 + color: JamiTheme.lightGrey_ + } + + Rectangle { + id: leftSettingsWidget + + SplitView.minimumWidth: 200 + SplitView.preferredWidth: 200 + SplitView.maximumWidth: parent.width / 2 + SplitView.fillHeight: true + LeftPanelView { + id: leftPanelView + + contentViewportWidth: leftSettingsWidget.width + contentViewPortHeight: leftSettingsWidget.height + + onBtnExitClicked:{ + leaveSettingsSlot() + } + + Connections { + target: leftPanelView.btnAccountSettings + function onCheckedToggledForRightPanel(checked) { + setSelected(SettingsView.Account) + } + } + Connections { + target: leftPanelView.btnGeneralSettings + function onCheckedToggledForRightPanel(checked) { + setSelected(SettingsView.General) + } + } + Connections { + target: leftPanelView.btnMediaSettings + function onCheckedToggledForRightPanel(checked) { + setSelected(SettingsView.Media) + } + } + Connections { + target: leftPanelView.btnPluginSettings + function onCheckedToggledForRightPanel(checked) { + setSelected(SettingsView.Plugin) + } + } + } + } + + StackLayout { + id: rightSettingsWidget + + property int pageIdCurrentAccountSettingsScrollPage: 0 + property int pageIdCurrentSIPAccountSettingScrollPage: 1 + property int pageIdGeneralSettingsPage: 2 + property int pageIdAvSettingPage: 3 + property int pageIdPluginSettingsPage: 4 + + currentIndex: { + switch(selectedMenu){ + case SettingsView.Account: + if(settingsViewRect.isSIP){ + return pageIdCurrentSIPAccountSettingScrollPage + } else { + return pageIdCurrentAccountSettingsScrollPage + } + case SettingsView.General: + return pageIdGeneralSettingsPage + case SettingsView.Media: + return pageIdAvSettingPage + case SettingsView.Plugin: + return pageIdPluginSettingsPage + } + } + + SplitView.fillWidth: true + SplitView.fillHeight: true + + // current account setting scroll page, index 0 + CurrentAccountSettingsScrollPage { + id: currentAccountSettingsScrollWidget + + onNavigateToMainView:{ + leaveSettingsSlot(true) + } + + onNavigateToNewWizardView: { + leaveSettingsSlot(true, false) + } + } + + // current SIP account setting scroll page, index 1 + CurrentSIPAccountSettingScrollPage { + id: currentSIPAccountSettingsScrollWidget + + onNavigateToMainView: { + leaveSettingsSlot(true) + } + + onNavigateToNewWizardView: { + leaveSettingsSlot(true, false) + } + } + + // general setting page, index 2 + GeneralSettingsPage { + id: generalSettings + } + + // av setting page, index 3 + AvSettingPage { + id: avSettings + } + + // plugin setting page, index 4 + PluginSettingsPage { + id: pluginSettings + } + } + } + } +} diff --git a/src/settingsview/components/AdvancedSIPSettingsView.qml b/src/settingsview/components/AdvancedSIPSettingsView.qml new file mode 100644 index 00000000..5ebcf84a --- /dev/null +++ b/src/settingsview/components/AdvancedSIPSettingsView.qml @@ -0,0 +1,2554 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.3 +import Qt.labs.platform 1.1 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +ColumnLayout { + function updateAccountInfoDisplayedAdvanceSIP(){ + // Call Settings + checkBoxAutoAnswerSIP.checked = ClientWrapper.settingsAdaptor.getAccountConfig_AutoAnswer() + checkBoxCustomRingtoneSIP.checked = ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtoneEnabled() + + // security + btnSIPCACert.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_Enable() + btnSIPUserCert.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_Enable() + btnSIPPrivateKey.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_Enable() + lineEditSIPCertPassword.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_Enable() + enableSDESToggle.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_SRTP_Enabled() + fallbackRTPToggle.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_SRTP_Enabled() + + btnSIPCACert.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateListFile()) + btnSIPUserCert.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateFile()) + btnSIPPrivateKey.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_PrivateKeyFile()) + lineEditSIPCertPassword.text = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_Password() + + encryptMediaStreamsToggle.checked = ClientWrapper.settingsAdaptor.getAccountConfig_SRTP_Enabled() + enableSDESToggle.checked = (ClientWrapper.settingsAdaptor.getAccountConfig_SRTP_KeyExchange() === Account.KeyExchangeProtocol.SDES) + fallbackRTPToggle.checked = ClientWrapper.settingsAdaptor.getAccountConfig_SRTP_RtpFallback() + encryptNegotitationToggle.checked = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_Enable() + verifyIncomingCertificatesServerToogle.checked = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_VerifyServer() + verifyIncomingCertificatesClientToogle.checked = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_VerifyClient() + requireCeritificateForTLSIncomingToggle.checked = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_RequireClientCertificate() + + var method = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_Method_inInt() + tlsProtocolComboBox.currentIndex = method + + outgoingTLSServerNameLineEdit.text = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_Servername() + negotiationTimeoutSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_TLS_NegotiationTimeoutSec() + + // Connectivity + checkBoxUPnPSIP.checked = ClientWrapper.settingsAdaptor.getAccountConfig_UpnpEnabled() + checkBoxTurnEnableSIP.checked = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Enabled() + lineEditTurnAddressSIP.text = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Server() + lineEditTurnUsernameSIP.text = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Username() + lineEditTurnPsswdSIP.text = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Password() + lineEditTurnRealmSIP.text = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Realm() + lineEditTurnAddressSIP.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Enabled() + lineEditTurnUsernameSIP.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Enabled() + lineEditTurnPsswdSIP.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Enabled() + lineEditTurnRealmSIP.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Enabled() + + checkBoxSTUNEnableSIP.checked = ClientWrapper.settingsAdaptor.getAccountConfig_STUN_Enabled() + lineEditSTUNAddressSIP.text = ClientWrapper.settingsAdaptor.getAccountConfig_STUN_Server() + lineEditSTUNAddressSIP.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_STUN_Enabled() + + registrationExpireTimeoutSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Registration_Expire() + networkInterfaceSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Localport() + + // published address + checkBoxCustomAddressPort.checked = ClientWrapper.settingsAdaptor.getAccountConfig_PublishedSameAsLocal() + lineEditSIPCustomAddress.text = ClientWrapper.settingsAdaptor.getAccountConfig_PublishedAddress() + customPortSIPSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_PublishedPort() + + // codecs + videoCheckBoxSIP.checked = ClientWrapper.settingsAdaptor.getAccountConfig_Video_Enabled() + updateAudioCodecs() + updateVideoCodecs() + btnRingtoneSIP.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtoneEnabled() + btnRingtoneSIP.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtonePath()) + lineEditSTUNAddressSIP.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_STUN_Enabled() + + // SDP session negotiation ports + audioRTPMinPortSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Audio_AudioPortMin() + audioRTPMaxPortSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Audio_AudioPortMax() + videoRTPMinPortSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Video_VideoPortMin() + videoRTPMaxPortSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Video_VideoPortMax() + + // voicemail + lineEditVoiceMailDialCode.text = ClientWrapper.settingsAdaptor.getAccountConfig_Mailbox() + } + + function updateAudioCodecs(){ + audioCodecListModelSIP.layoutAboutToBeChanged() + audioCodecListModelSIP.dataChanged(audioCodecListModelSIP.index(0, 0), + audioCodecListModelSIP.index(audioCodecListModelSIP.rowCount() - 1, 0)) + audioCodecListModelSIP.layoutChanged() + } + + function updateVideoCodecs(){ + videoCodecListModelSIP.layoutAboutToBeChanged() + videoCodecListModelSIP.dataChanged(videoCodecListModelSIP.index(0, 0), + videoCodecListModelSIP.index(videoCodecListModelSIP.rowCount() - 1, 0)) + videoCodecListModelSIP.layoutChanged() + } + + function decreaseAudioCodecPriority(){ + var index = audioListWidgetSIP.currentIndex + var codecId = audioCodecListModelSIP.data(audioCodecListModelSIP.index(index,0), AudioCodecListModel.AudioCodecID) + + ClientWrapper.settingsAdaptor.decreaseAudioCodecPriority(codecId) + audioListWidgetSIP.currentIndex = index + 1 + updateAudioCodecs() + } + + function increaseAudioCodecPriority(){ + var index = audioListWidgetSIP.currentIndex + var codecId = audioCodecListModelSIP.data(audioCodecListModelSIP.index(index,0), AudioCodecListModel.AudioCodecID) + + ClientWrapper.settingsAdaptor.increaseAudioCodecPriority(codecId) + audioListWidgetSIP.currentIndex = index - 1 + updateAudioCodecs() + } + + function decreaseVideoCodecPriority(){ + var index = videoListWidgetSIP.currentIndex + var codecId = videoCodecListModelSIP.data(videoCodecListModelSIP.index(index,0), VideoCodecListModel.VideoCodecID) + + ClientWrapper.settingsAdaptor.decreaseVideoCodecPriority(codecId) + videoListWidgetSIP.currentIndex = index + 1 + updateVideoCodecs() + } + + function increaseVideoCodecPriority(){ + var index = videoListWidgetSIP.currentIndex + var codecId = videoCodecListModelSIP.data(videoCodecListModelSIP.index(index,0), VideoCodecListModel.VideoCodecID) + + ClientWrapper.settingsAdaptor.increaseVideoCodecPriority(codecId) + videoListWidgetSIP.currentIndex = index - 1 + updateVideoCodecs() + } + + VideoCodecListModel{ + id: videoCodecListModelSIP + } + + AudioCodecListModel{ + id: audioCodecListModelSIP + } + + + // slots + function audioRTPMinPortSpinBoxEditFinished(value){ + if (ClientWrapper.settingsAdaptor.getAccountConfig_Audio_AudioPortMax() < value) { + audioRTPMinPortSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Audio_AudioPortMin() + return + } + ClientWrapper.settingsAdaptor.audioRTPMinPortSpinBoxEditFinished(value) + } + + function audioRTPMaxPortSpinBoxEditFinished(value){ + if (value < ClientWrapper.settingsAdaptor.getAccountConfig_Audio_AudioPortMin()) { + audioRTPMaxPortSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Audio_AudioPortMax() + return + } + ClientWrapper.settingsAdaptor.audioRTPMaxPortSpinBoxEditFinished(value) + } + + function videoRTPMinPortSpinBoxEditFinished(value){ + if (ClientWrapper.settingsAdaptor.getAccountConfig_Video_VideoPortMax() < value) { + videoRTPMinPortSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Video_VideoPortMin() + return + } + ClientWrapper.settingsAdaptor.videoRTPMinPortSpinBoxEditFinished(value) + } + + function videoRTPMaxPortSpinBoxEditFinished(value){ + if (value < ClientWrapper.settingsAdaptor.getAccountConfig_Video_VideoPortMin()) { + videoRTPMinPortSpinBox.value = ClientWrapper.settingsAdaptor.getAccountConfig_Video_VideoPortMin() + return + } + ClientWrapper.settingsAdaptor.videoRTPMaxPortSpinBoxEditFinished(value) + } + + + function changeRingtonePath(url){ + if(url.length !== 0) { + ClientWrapper.settingsAdaptor.set_RingtonePath(url) + btnRingtoneSIP.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtonePath()) + } else if (ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtonePath().length === 0){ + btnRingtoneSIP.text = qsTr("Add a custom ringtone") + } + } + + function changeFileCACert(url){ + if(url.length !== 0) { + ClientWrapper.settingsAdaptor.set_FileCACert(url) + btnSIPCACert.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateListFile()) + } + } + + function changeFileUserCert(url){ + if(url.length !== 0) { + ClientWrapper.settingsAdaptor.set_FileUserCert(url) + btnSIPUserCert.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateFile()) + } + } + + function changeFilePrivateKey(url){ + if(url.length !== 0) { + ClientWrapper.settingsAdaptor.set_FilePrivateKey(url) + btnSIPPrivateKey.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_PrivateKeyFile()) + } + } + + JamiFileDialog { + id: ringtonePath_Dialog_SIP + + property string oldPath : ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtonePath() + property string openPath : oldPath === "" ? (ClientWrapper.utilsAdaptor.getCurrentPath() + "/ringtones/") : (ClientWrapper.utilsAdaptor.toFileAbsolutepath(oldPath)) + + mode: JamiFileDialog.OpenFile + title: qsTr("Select a new ringtone") + folder: openPath + + nameFilters: [qsTr("Audio Files") + " (*.wav *.ogg *.opus *.mp3 *.aiff *.wma)", qsTr( + "All files") + " (*)"] + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + + onAccepted: { + var url = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + changeRingtonePath(url) + } + } + + JamiFileDialog { + id: caCert_Dialog_SIP + + property string oldPath : ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateListFile() + property string openPath : oldPath === "" ? (ClientWrapper.utilsAdaptor.getCurrentPath() + "/ringtones/") : (ClientWrapper.utilsAdaptor.toFileAbsolutepath(oldPath)) + + mode: JamiFileDialog.OpenFile + title: qsTr("Select a CA certificate") + folder: openPath + nameFilters: [qsTr("Certificate File") + " (*.crt)", qsTr( + "All files") + " (*)"] + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + + onAccepted: { + var url = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + changeFileCACert(url) + } + } + + JamiFileDialog { + id: userCert_Dialog_SIP + + property string oldPath : ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateFile() + property string openPath : oldPath === "" ? (ClientWrapper.utilsAdaptor.getCurrentPath() + "/ringtones/") : (ClientWrapper.utilsAdaptor.toFileAbsolutepath(oldPath)) + + mode: JamiFileDialog.OpenFile + title: qsTr("Select a user certificate") + folder: openPath + nameFilters: [qsTr("Certificate File") + " (*.crt)", qsTr( + "All files") + " (*)"] + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + + onAccepted: { + var url = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + changeFileUserCert(url) + } + } + + JamiFileDialog { + id: privateKey_Dialog_SIP + + property string oldPath : ClientWrapper.settingsAdaptor.getAccountConfig_TLS_PrivateKeyFile() + property string openPath : oldPath === "" ? (ClientWrapper.utilsAdaptor.getCurrentPath() + "/ringtones/") : (ClientWrapper.utilsAdaptor.toFileAbsolutepath(oldPath)) + + mode: JamiFileDialog.OpenFile + title: qsTr("Select a private key") + folder: openPath + nameFilters: [qsTr("Key File") + " (*.key)", qsTr( + "All files") + " (*)"] + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + + onAccepted: { + var url = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + changeFilePrivateKey(url) + } + } + + spacing: 6 + Layout.preferredWidth: 532 + Layout.maximumWidth: 532 + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 24 + Layout.preferredHeight: 24 + Layout.maximumHeight: 24 + } + + // call setting section + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Call Settings") + + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + Layout.leftMargin: 20 + + ToggleSwitch { + id: checkBoxAutoAnswerSIP + labelText: qsTr("Auto Answer Call") + fontPointSize: 10 + + Layout.leftMargin: 20 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setAutoAnswerCalls(checked) + } + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 20 + Layout.maximumHeight: 30 + + ToggleSwitch { + id: checkBoxCustomRingtoneSIP + labelText: qsTr("Enable Custom Ringtone") + fontPointSize: 10 + + Layout.maximumWidth: 164 + Layout.preferredWidth: 164 + Layout.minimumWidth: 164 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setEnableRingtone(checked) + btnRingtoneSIP.enabled = checked + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + HoverableRadiusButton { + id: btnRingtoneSIP + + radius: height / 2 + + Layout.maximumWidth: 164 + Layout.preferredWidth: 164 + Layout.minimumWidth: 164 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.width: 16 + icon.height: 16 + + onClicked: { + ringtonePath_Dialog_SIP.open() + } + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + // voice mail section + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Voicemail") + + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 13 + Layout.preferredHeight: 13 + Layout.maximumHeight: 13 + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 20 + Layout.maximumHeight: 30 + + Label { + Layout.maximumWidth: 162 + Layout.preferredWidth: 162 + Layout.minimumWidth: 162 + + Layout.minimumHeight: 28 + Layout.preferredHeight: 28 + Layout.maximumHeight: 28 + + text: qsTr("Voicemail Dial Code") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + InfoLineEdit { + id: lineEditVoiceMailDialCode + + fieldLayoutWidth: 250 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.lineEditVoiceMailDialCodeEditFinished(text) + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + // security section + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Security") + + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 13 + Layout.preferredHeight: 13 + Layout.minimumHeight: 13 + } + + GridLayout { + Layout.leftMargin: 20 + Layout.fillWidth: true + + rowSpacing: 6 + columnSpacing: 6 + + rows: 14 + columns: 3 + + // First row + ToggleSwitch{ + id: encryptMediaStreamsToggle + + labelText: qsTr("Encrypt Media Streams(SRTP)") + fontPointSize: 10 + + Layout.row: 0 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseSRTP(checked) + enableSDESToggle.enabled = checked + fallbackRTPToggle.enabled = checked + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 0 + Layout.column: 1 + } + + // second row + ToggleSwitch{ + id: enableSDESToggle + + labelText: qsTr("Enable SDES(Key Exchange)") + fontPointSize: 10 + + Layout.row: 1 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseSDES(checked) + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 1 + Layout.column: 1 + } + + // third row + ToggleSwitch{ + id: fallbackRTPToggle + + labelText: qsTr("Can Fallback on RTP") + fontPointSize: 10 + + Layout.row: 2 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseRTPFallback(checked) + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 2 + Layout.column: 1 + } + + // fourth row + ToggleSwitch{ + id: encryptNegotitationToggle + + labelText: qsTr("Encrypt Negotiation(TLS)") + fontPointSize: 10 + + Layout.row: 3 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseTLS(checked) + btnSIPCACert.enabled = checked + btnSIPUserCert.enabled = checked + btnSIPPrivateKey.enabled = checked + lineEditSIPCertPassword.enabled = checked + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 3 + Layout.column: 1 + } + + // fifth row + RowLayout{ + spacing: 6 + Layout.maximumHeight: 30 + + Layout.row: 4 + Layout.column: 0 + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + Label{ + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + Layout.maximumWidth: 209 + Layout.preferredWidth: 209 + Layout.minimumWidth: 209 + + text: qsTr("CA Certificate") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 4 + Layout.column: 1 + } + + HoverableRadiusButton{ + id: btnSIPCACert + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + Layout.maximumWidth: 250 + Layout.preferredWidth: 250 + Layout.minimumWidth: 250 + + radius: height / 2 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.width: 16 + icon.height: 16 + + Layout.row: 4 + Layout.column: 2 + + onClicked: { + caCert_Dialog_SIP.open() + } + } + + // sixth row + RowLayout{ + spacing: 6 + Layout.maximumHeight: 30 + + Layout.row: 5 + Layout.column: 0 + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + Label{ + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + Layout.maximumWidth: 209 + Layout.preferredWidth: 209 + Layout.minimumWidth: 209 + + text: qsTr("User Certificate") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 5 + Layout.column: 1 + } + + HoverableRadiusButton{ + id: btnSIPUserCert + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + Layout.maximumWidth: 250 + Layout.preferredWidth: 250 + Layout.minimumWidth: 250 + + radius: height / 2 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.width: 16 + icon.height: 16 + + Layout.row: 5 + Layout.column: 2 + + onClicked: { + userCert_Dialog_SIP.open() + } + } + + // seventh row + RowLayout{ + spacing: 6 + Layout.maximumHeight: 30 + + Layout.row: 6 + Layout.column: 0 + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + Label{ + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + Layout.maximumWidth: 209 + Layout.preferredWidth: 209 + Layout.minimumWidth: 209 + + text: qsTr("Private Key") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 6 + Layout.column: 1 + } + + HoverableRadiusButton{ + id: btnSIPPrivateKey + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + Layout.maximumWidth: 250 + Layout.preferredWidth: 250 + Layout.minimumWidth: 250 + + radius: height / 2 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.width: 16 + icon.height: 16 + + Layout.row: 6 + Layout.column: 2 + + onClicked: { + privateKey_Dialog_SIP.open() + } + } + + // eight row + RowLayout{ + spacing: 6 + Layout.maximumHeight: 30 + + Layout.row: 7 + Layout.column: 0 + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + Label{ + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + Layout.maximumWidth: 209 + Layout.preferredWidth: 209 + Layout.minimumWidth: 209 + + text: qsTr("Private Key Password") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 7 + Layout.column: 1 + } + + InfoLineEdit { + id: lineEditSIPCertPassword + + Layout.alignment: Qt.AlignCenter + + fieldLayoutWidth: 250 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + echoMode: TextInput.Password + + Layout.row: 7 + Layout.column: 2 + + onEditingFinished: { + ClientWrapper.settingsAdaptor.lineEditSIPCertPasswordLineEditTextChanged(text) + } + } + + // nineth row + ToggleSwitch{ + id: verifyIncomingCertificatesServerToogle + + labelText: qsTr("Verify Certificates(Server Side)") + fontPointSize: 10 + + Layout.row: 8 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setVerifyCertificatesServer(checked) + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 8 + Layout.column: 1 + } + + // tenth row + ToggleSwitch{ + id: verifyIncomingCertificatesClientToogle + + labelText: qsTr("Verify Certificates(Client Side)") + fontPointSize: 10 + + Layout.row: 9 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setVerifyCertificatesClient(checked) + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 9 + Layout.column: 1 + } + + //eleventh row + ToggleSwitch{ + id: requireCeritificateForTLSIncomingToggle + + labelText: qsTr("TLS Connections Require Certificate") + fontPointSize: 10 + + Layout.row: 10 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setRequireCertificatesIncomingTLS(checked) + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row:10 + Layout.column: 1 + } + + // twelveth row + Label{ + Layout.fillWidth: true + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("TLS Protocol Method") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 11 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 11 + Layout.column: 1 + } + + SettingParaCombobox{ + id:tlsProtocolComboBox + + Layout.maximumWidth: 252 + Layout.preferredWidth: 252 + Layout.minimumWidth: 252 + + Layout.maximumHeight: 29 + Layout.minimumHeight: 29 + Layout.preferredHeight: 29 + + Layout.alignment: Qt.AlignCenter + + font.pointSize: 10 + font.kerning: true + + Layout.row: 11 + Layout.column: 2 + + textRole: "textDisplay" + + model: ListModel{ + ListElement{textDisplay: "Default"; firstArg: "Default"; secondArg: 0} + ListElement{textDisplay: "TLSv1"; firstArg: "TLSv1"; secondArg: 1} + ListElement{textDisplay: "TLSv1.1"; firstArg: "TLSv1.1"; secondArg: 2} + ListElement{textDisplay: "TLSv1.2"; firstArg: "TLSv1.2"; secondArg: 3} + } + + onActivated: { + var indexOfOption = tlsProtocolComboBox.model.get(index).secondArg + ClientWrapper.settingsAdaptor.tlsProtocolComboBoxIndexChanged(parseInt(indexOfOption)) + } + } + + // 13th row + Label{ + Layout.fillWidth: true + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Outgoing TLS Server Name") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 12 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 12 + Layout.column: 1 + } + + InfoLineEdit { + id: outgoingTLSServerNameLineEdit + + Layout.alignment: Qt.AlignCenter + + fieldLayoutWidth: 250 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 12 + Layout.column: 2 + + onEditingFinished: { + ClientWrapper.settingsAdaptor.outgoingTLSServerNameLineEditTextChanged(text) + } + } + + // 14th row + Label{ + Layout.fillWidth: true + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Negotiation Timeout(seconds)") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 13 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 13 + Layout.column: 1 + } + + SpinBox{ + id:negotiationTimeoutSpinBox + + Layout.maximumWidth: 252 + Layout.preferredWidth: 252 + Layout.minimumWidth: 252 + + Layout.maximumHeight: 30 + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + + Layout.alignment: Qt.AlignCenter + + font.pointSize: 10 + font.kerning: true + + from: 0 + to: 3000 + stepSize: 1 + + Layout.row: 13 + Layout.column: 2 + + onValueModified: { + ClientWrapper.settingsAdaptor.negotiationTimeoutSpinBoxValueChanged(value) + } + } + } + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + // connectivity section + ColumnLayout{ + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Connectivity") + + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.preferredHeight: 10 + Layout.minimumHeight: 10 + } + + GridLayout{ + Layout.leftMargin: 20 + Layout.fillWidth: true + + rowSpacing: 6 + columnSpacing: 6 + + rows: 9 + columns: 3 + + // 1st row + Label{ + Layout.minimumWidth: 286 + Layout.preferredWidth: 286 + + Layout.minimumHeight: 28 + Layout.preferredHeight: 28 + Layout.maximumHeight: 28 + + text: qsTr("Registration Expire Timeout(seconds)") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 0 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 0 + Layout.column: 1 + } + + SpinBox{ + id: registrationExpireTimeoutSpinBox + + Layout.maximumWidth: 250 + Layout.preferredWidth: 250 + Layout.minimumWidth: 250 + + Layout.maximumHeight: 30 + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + + Layout.alignment: Qt.AlignCenter + + font.pointSize: 10 + font.kerning: true + + from: 0 + to: 3000 + stepSize: 1 + + Layout.row: 0 + Layout.column: 2 + + onValueModified: { + ClientWrapper.settingsAdaptor.registrationTimeoutSpinBoxValueChanged(value) + } + } + + // 2nd row + Label{ + Layout.minimumWidth: 286 + Layout.preferredWidth: 286 + + Layout.minimumHeight: 28 + Layout.preferredHeight: 28 + Layout.maximumHeight: 28 + + text: qsTr("Newtwork interface") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 1 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 1 + Layout.column: 1 + } + + SpinBox{ + id: networkInterfaceSpinBox + + Layout.maximumWidth: 250 + Layout.preferredWidth: 250 + Layout.minimumWidth: 250 + + Layout.maximumHeight: 30 + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + + Layout.alignment: Qt.AlignCenter + + font.pointSize: 10 + font.kerning: true + + from: 0 + to: 65536 + stepSize: 1 + + Layout.row: 1 + Layout.column: 2 + + onValueModified: { + ClientWrapper.settingsAdaptor.networkInterfaceSpinBoxValueChanged(value) + } + } + + // 3rd row + ToggleSwitch{ + id: checkBoxUPnPSIP + + labelText: qsTr("Use UPnP") + fontPointSize: 10 + + Layout.row: 2 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseUPnP(checked) + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 2 + Layout.column: 1 + } + + // 4th row + ToggleSwitch{ + id: checkBoxTurnEnableSIP + + labelText: qsTr("Use TURN") + fontPointSize: 10 + + Layout.row: 3 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseTURN(checked) + lineEditTurnAddressSIP.enabled = checked + lineEditTurnUsernameSIP.enabled = checked + lineEditTurnPsswdSIP.enabled = checked + lineEditTurnRealmSIP.enabled = checked + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 3 + Layout.column: 1 + } + + // 5th row + RowLayout{ + spacing: 6 + Layout.maximumHeight: 30 + + Layout.row: 4 + Layout.column: 0 + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + Label{ + Layout.maximumHeight: 27 + Layout.preferredHeight: 27 + Layout.minimumHeight: 27 + + Layout.maximumWidth: 260 + Layout.preferredWidth: 260 + Layout.minimumWidth: 260 + + text: qsTr("TURN Address") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 4 + Layout.column: 1 + } + + + InfoLineEdit { + id: lineEditTurnAddressSIP + + Layout.alignment: Qt.AlignCenter + + fieldLayoutWidth: 250 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 4 + Layout.column: 2 + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setTURNAddress(text) + } + } + + // 6th row + RowLayout{ + spacing: 6 + Layout.maximumHeight: 30 + + Layout.row: 5 + Layout.column: 0 + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + Label{ + Layout.maximumHeight: 27 + Layout.preferredHeight: 27 + Layout.minimumHeight: 27 + + Layout.maximumWidth: 260 + Layout.preferredWidth: 260 + Layout.minimumWidth: 260 + + text: qsTr("TURN Username") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 5 + Layout.column: 1 + } + + InfoLineEdit { + id: lineEditTurnUsernameSIP + + Layout.alignment: Qt.AlignCenter + + fieldLayoutWidth: 250 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 5 + Layout.column: 2 + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setTURNUsername(text) + } + } + + // 7th row + RowLayout{ + spacing: 6 + Layout.maximumHeight: 30 + + Layout.row: 6 + Layout.column: 0 + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + Label{ + Layout.maximumHeight: 27 + Layout.preferredHeight: 27 + Layout.minimumHeight: 27 + + Layout.maximumWidth: 260 + Layout.preferredWidth: 260 + Layout.minimumWidth: 260 + + text: qsTr("TURN Password") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 6 + Layout.column: 1 + } + + InfoLineEdit { + id: lineEditTurnPsswdSIP + + Layout.alignment: Qt.AlignCenter + + fieldLayoutWidth: 250 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 6 + Layout.column: 2 + + echoMode: TextInput.Password + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setTURNPassword(text) + } + } + + // 8th row + RowLayout{ + spacing: 6 + Layout.maximumHeight: 30 + + Layout.row: 7 + Layout.column: 0 + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + Label{ + Layout.maximumHeight: 27 + Layout.preferredHeight: 27 + Layout.minimumHeight: 27 + + Layout.maximumWidth: 260 + Layout.preferredWidth: 260 + Layout.minimumWidth: 260 + + text: qsTr("TURN Realm") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 7 + Layout.column: 1 + } + + InfoLineEdit { + id: lineEditTurnRealmSIP + + Layout.alignment: Qt.AlignCenter + + fieldLayoutWidth: 250 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 7 + Layout.column: 2 + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setTURNRealm(text) + } + } + + // 9th row + ToggleSwitch{ + id: checkBoxSTUNEnableSIP + + labelText: qsTr("Use STUN") + fontPointSize: 10 + + Layout.row: 8 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseSTUN(checked) + lineEditSTUNAddressSIP.enabled = checked + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.row: 8 + Layout.column: 1 + } + + InfoLineEdit { + id: lineEditSTUNAddressSIP + + Layout.alignment: Qt.AlignCenter + + fieldLayoutWidth: 250 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 8 + Layout.column: 2 + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setSTUNAddress(text) + } + } + } + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + // public address section + ColumnLayout{ + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Public Address") + + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.preferredHeight: 10 + Layout.minimumHeight: 10 + } + + GridLayout{ + Layout.leftMargin: 20 + Layout.fillWidth: true + + rowSpacing: 6 + columnSpacing: 6 + + rows: 3 + columns: 3 + + // 1st row + ToggleSwitch{ + id: checkBoxCustomAddressPort + + Layout.maximumWidth: 88 + labelText: qsTr("Use Custom Address/Port") + fontPointSize: 10 + + Layout.row: 0 + Layout.column: 0 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseCustomAddressAndPort(checked) + lineEditSIPCustomAddress.enabled = checked + customPortSIPSpinBox.enabled = checked + } + } + + Item{ + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.row: 0 + Layout.column: 1 + } + + //2nd row + Label{ + Layout.leftMargin: 26 + + Layout.maximumHeight: 27 + Layout.preferredHeight: 27 + Layout.minimumHeight: 27 + + Layout.maximumWidth: 60 + Layout.preferredWidth: 60 + Layout.minimumWidth: 60 + + text: qsTr("Address") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 1 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.row: 1 + Layout.column: 1 + } + + InfoLineEdit { + id: lineEditSIPCustomAddress + + Layout.alignment: Qt.AlignCenter + + fieldLayoutWidth: 250 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 1 + Layout.column: 2 + + onEditingFinished: { + ClientWrapper.settingsAdaptor.lineEditSIPCustomAddressLineEditTextChanged(text) + } + } + + //3rd row + Label{ + Layout.leftMargin: 26 + + Layout.maximumHeight: 27 + Layout.preferredHeight: 27 + Layout.minimumHeight: 27 + + Layout.maximumWidth: 60 + Layout.preferredWidth: 60 + Layout.minimumWidth: 60 + + text: qsTr("Port") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 2 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.row: 2 + Layout.column: 1 + } + + SpinBox{ + id: customPortSIPSpinBox + + Layout.maximumWidth: 250 + Layout.preferredWidth: 250 + Layout.minimumWidth: 250 + + Layout.maximumHeight: 30 + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + + Layout.alignment: Qt.AlignCenter + + font.pointSize: 10 + font.kerning: true + + from: 0 + to: 65535 + stepSize: 1 + + Layout.row: 2 + Layout.column: 2 + + onValueModified: { + ClientWrapper.settingsAdaptor.customPortSIPSpinBoxValueChanged(value) + } + } + } + } + + // media section + ColumnLayout{ + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Media") + + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.preferredHeight: 10 + Layout.minimumHeight: 10 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + ToggleSwitch { + id: videoCheckBoxSIP + + Layout.leftMargin: 20 + + labelText: qsTr("Enable Video") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setVideoState(checked) + } + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.leftMargin: 20 + + ColumnLayout { + spacing: 6 + Layout.maximumWidth: 348 + + RowLayout { + spacing: 6 + Layout.fillWidth: true + + Layout.maximumHeight: 30 + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Video Codecs") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + + Layout.minimumWidth: 20 + Layout.preferredWidth: 20 + Layout.maximumWidth: 20 + } + + HoverableRadiusButton { + id: videoDownPushButtonSIP + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + scale: 1 + + buttonImageHeight: height + buttonImageWidth: height + + font.pointSize: 9 + font.kerning: true + + icon.source: "qrc:/images/icons/round-arrow_drop_down-24px.svg" + icon.width: 32 + icon.height: 32 + + onClicked: { + decreaseVideoCodecPriority() + } + } + + HoverableRadiusButton { + id: videoUpPushButtonSIP + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + buttonImageHeight: height + buttonImageWidth: height + + font.pointSize: 9 + font.kerning: true + + icon.source: "qrc:/images/icons/round-arrow_drop_up-24px.svg" + icon.width: 32 + icon.height: 32 + + onClicked: { + increaseVideoCodecPriority() + } + } + } + + ListViewJami { + id: videoListWidgetSIP + + Layout.minimumWidth: 348 + Layout.preferredWidth: 348 + Layout.maximumWidth: 348 + + Layout.minimumHeight: 192 + Layout.preferredHeight: 192 + Layout.maximumHeight: 192 + + model: videoCodecListModelSIP + + delegate: VideoCodecDelegate { + id: videoCodecDelegate + + width: videoListWidgetSIP.width + height: videoListWidgetSIP.height / 4 + + videoCodecName : VideoCodecName + isEnabled : IsEnabled + videoCodecId: VideoCodecID + + onClicked: { + videoListWidgetSIP.currentIndex = index + } + + onVideoCodecStateChange:{ + ClientWrapper.settingsAdaptor.videoCodecsStateChange(idToSet , isToBeEnabled) + updateVideoCodecs() + } + } + } + } + + ColumnLayout { + spacing: 6 + Layout.maximumWidth: 348 + + RowLayout { + spacing: 6 + Layout.fillWidth: true + + Layout.maximumHeight: 30 + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Audio Codecs") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + + Layout.minimumWidth: 20 + Layout.preferredWidth: 20 + Layout.maximumWidth: 20 + } + + HoverableRadiusButton { + id: audioDownPushButtonSIP + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + buttonImageHeight: height + buttonImageWidth: height + + font.pointSize: 9 + font.kerning: true + + icon.source: "qrc:/images/icons/round-arrow_drop_down-24px.svg" + icon.width: 32 + icon.height: 32 + + onClicked: { + decreaseAudioCodecPriority() + } + } + + HoverableRadiusButton { + id: audioUpPushButtonSIP + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + buttonImageHeight: height + buttonImageWidth: height + + font.pointSize: 9 + font.kerning: true + + icon.source: "qrc:/images/icons/round-arrow_drop_up-24px.svg" + icon.width: 32 + icon.height: 32 + + onClicked: { + increaseAudioCodecPriority() + } + } + } + + ListViewJami { + id: audioListWidgetSIP + + Layout.minimumWidth: 348 + Layout.preferredWidth: 348 + Layout.maximumWidth: 348 + + Layout.minimumHeight: 192 + Layout.preferredHeight: 192 + Layout.maximumHeight: 192 + + model: audioCodecListModelSIP + + delegate: AudioCodecDelegate { + id: audioCodecDelegate + + width: audioListWidgetSIP.width + height: audioListWidgetSIP.height / 4 + + layer.mipmap: false + clip: true + + audioCodecName : AudioCodecName + isEnabled : IsEnabled + audioCodecId: AudioCodecID + samplerRate: Samplerate + + onClicked: { + audioListWidgetSIP.currentIndex = index + } + + onAudioCodecStateChange:{ + ClientWrapper.settingsAdaptor.audioCodecsStateChange(idToSet , isToBeEnabled) + updateAudioCodecs() + } + } + } + } + } + } + + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + ColumnLayout{ + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("SDP Session Negotiation(ICE Fallback)") + + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 21 + Layout.preferredHeight: 21 + Layout.maximumHeight: 21 + + text: qsTr("Only used during negotiation in case ICE is not supported") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.preferredHeight: 10 + Layout.minimumHeight: 10 + } + + GridLayout{ + Layout.leftMargin: 20 + Layout.fillWidth: true + + rowSpacing: 6 + columnSpacing: 6 + + rows: 4 + columns: 3 + + // 1st row + Label{ + Layout.minimumWidth: 162 + Layout.preferredWidth: 162 + Layout.maximumWidth: 162 + + Layout.minimumHeight: 28 + Layout.preferredHeight: 28 + Layout.maximumHeight: 28 + + text: qsTr("Audio RTP Min Port") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 0 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.row: 0 + Layout.column: 1 + } + + SpinBox{ + id:audioRTPMinPortSpinBox + + Layout.minimumWidth: 250 + Layout.preferredWidth: 250 + Layout.maximumWidth: 250 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + from: 0 + to: 65535 + stepSize: 1 + + Layout.row: 0 + Layout.column: 2 + + onValueModified: { + audioRTPMinPortSpinBoxEditFinished(value) + } + } + + // 2nd row + Label{ + Layout.minimumWidth: 162 + Layout.preferredWidth: 162 + Layout.maximumWidth: 162 + + Layout.minimumHeight: 28 + Layout.preferredHeight: 28 + Layout.maximumHeight: 28 + + text: qsTr("Audio RTP Max Port") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 1 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.row: 1 + Layout.column: 1 + } + + SpinBox{ + id:audioRTPMaxPortSpinBox + + Layout.minimumWidth: 250 + Layout.preferredWidth: 250 + Layout.maximumWidth: 250 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + from: 0 + to: 65535 + stepSize: 1 + + Layout.row: 1 + Layout.column: 2 + + onValueModified: { + audioRTPMaxPortSpinBoxEditFinished(value) + } + } + + // 3rd row + Label{ + Layout.minimumWidth: 162 + Layout.preferredWidth: 162 + Layout.maximumWidth: 162 + + Layout.minimumHeight: 28 + Layout.preferredHeight: 28 + Layout.maximumHeight: 28 + + text: qsTr("Video RTP Min Port") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 2 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.row: 2 + Layout.column: 1 + } + + SpinBox{ + id:videoRTPMinPortSpinBox + + Layout.minimumWidth: 250 + Layout.preferredWidth: 250 + Layout.maximumWidth: 250 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + from: 0 + to: 65535 + stepSize: 1 + + Layout.row: 2 + Layout.column: 2 + + onValueModified: { + videoRTPMinPortSpinBoxEditFinished(value) + } + } + + // 4th row + Label{ + Layout.minimumWidth: 162 + Layout.preferredWidth: 162 + Layout.maximumWidth: 162 + + Layout.minimumHeight: 28 + Layout.preferredHeight: 28 + Layout.maximumHeight: 28 + + text: qsTr("Video RTP Max Port") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.row: 3 + Layout.column: 0 + } + + Item{ + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.row: 3 + Layout.column: 1 + } + + SpinBox{ + id:videoRTPMaxPortSpinBox + + Layout.minimumWidth: 250 + Layout.preferredWidth: 250 + Layout.maximumWidth: 250 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + from: 0 + to: 65535 + stepSize: 1 + + Layout.row: 3 + Layout.column: 2 + + onValueModified: { + videoRTPMaxPortSpinBoxEditFinished(value) + } + } + } + } + + // spacers + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 48 + Layout.preferredHeight: 48 + Layout.minimumHeight: 48 + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } +} + + diff --git a/src/settingsview/components/AdvancedSettingsView.qml b/src/settingsview/components/AdvancedSettingsView.qml new file mode 100644 index 00000000..003869b9 --- /dev/null +++ b/src/settingsview/components/AdvancedSettingsView.qml @@ -0,0 +1,1370 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.3 +import Qt.labs.platform 1.1 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +ColumnLayout { + function updateAccountInfoDisplayedAdvance() { + //Call Settings + checkAutoConnectOnLocalNetwork.checked = ClientWrapper.settingsAdaptor.getAccountConfig_PeerDiscovery() + checkBoxUntrusted.checked = ClientWrapper.settingsAdaptor.getAccountConfig_DHT_PublicInCalls() + checkBoxAutoAnswer.checked = ClientWrapper.settingsAdaptor.getAccountConfig_AutoAnswer() + checkBoxCustomRingtone.checked = ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtoneEnabled() + + // Name Server + lineEditNameServer.text = ClientWrapper.settingsAdaptor.getAccountConfig_RingNS_Uri() + + //OpenDHT Config + checkBoxEnableProxy.checked = ClientWrapper.settingsAdaptor.getAccountConfig_ProxyEnabled() + lineEditProxy.text = ClientWrapper.settingsAdaptor.getAccountConfig_ProxyServer() + lineEditBootstrap.text = ClientWrapper.settingsAdaptor.getAccountConfig_Hostname() + + // Security + btnCACert.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateListFile()) + btnUserCert.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateFile()) + btnPrivateKey.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_PrivateKeyFile()) + + // Connectivity + checkBoxUPnP.checked = ClientWrapper.settingsAdaptor.getAccountConfig_UpnpEnabled() + checkBoxTurnEnable.checked = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Enabled() + lineEditTurnAddress.text = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Server() + lineEditTurnUsername.text = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Username() + lineEditTurnPassword.text = ClientWrapper.settingsAdaptor.getAccountConfig_TURN_Password() + checkBoxSTUNEnable.checked = ClientWrapper.settingsAdaptor.getAccountConfig_STUN_Enabled() + lineEditSTUNAddress.text = ClientWrapper.settingsAdaptor.getAccountConfig_STUN_Server() + // codecs + videoCheckBox.checked = ClientWrapper.settingsAdaptor.getAccountConfig_Video_Enabled() + // update audio and video codec, make sure this change does not trigger item change events + updateAudioCodecs(); + updateVideoCodecs(); + btnRingtone.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtoneEnabled() + btnRingtone.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtonePath()) + lineEditProxy.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_ProxyEnabled() + lineEditSTUNAddress.enabled = ClientWrapper.settingsAdaptor.getAccountConfig_STUN_Enabled() + } + + function updateAudioCodecs(){ + audioCodecListModel.layoutAboutToBeChanged() + audioCodecListModel.dataChanged(audioCodecListModel.index(0, 0), + audioCodecListModel.index(audioCodecListModel.rowCount() - 1, 0)) + audioCodecListModel.layoutChanged() + } + + function updateVideoCodecs(){ + videoCodecListModel.layoutAboutToBeChanged() + videoCodecListModel.dataChanged(videoCodecListModel.index(0, 0), + videoCodecListModel.index(videoCodecListModel.rowCount() - 1, 0)) + videoCodecListModel.layoutChanged() + } + + function decreaseAudioCodecPriority(){ + var index = audioListWidget.currentIndex + var codecId = audioCodecListModel.data(audioCodecListModel.index(index,0), AudioCodecListModel.AudioCodecID) + + ClientWrapper.settingsAdaptor.decreaseAudioCodecPriority(codecId) + audioListWidget.currentIndex = index + 1 + updateAudioCodecs() + } + + function increaseAudioCodecPriority(){ + var index = audioListWidget.currentIndex + var codecId = audioCodecListModel.data(audioCodecListModel.index(index,0), AudioCodecListModel.AudioCodecID) + + ClientWrapper.settingsAdaptor.increaseAudioCodecPriority(codecId) + audioListWidget.currentIndex = index - 1 + updateAudioCodecs() + } + + function decreaseVideoCodecPriority(){ + var index = videoListWidget.currentIndex + var codecId = videoCodecListModel.data(videoCodecListModel.index(index,0), VideoCodecListModel.VideoCodecID) + + ClientWrapper.settingsAdaptor.decreaseVideoCodecPriority(codecId) + videoListWidget.currentIndex = index + 1 + updateVideoCodecs() + } + + function increaseVideoCodecPriority(){ + var index = videoListWidget.currentIndex + var codecId = videoCodecListModel.data(videoCodecListModel.index(index,0), VideoCodecListModel.VideoCodecID) + + ClientWrapper.settingsAdaptor.increaseVideoCodecPriority(codecId) + videoListWidget.currentIndex = index - 1 + updateVideoCodecs() + } + + VideoCodecListModel{ + id: videoCodecListModel + } + + AudioCodecListModel{ + id: audioCodecListModel + } + + function changeRingtonePath(url){ + if(url.length !== 0) { + ClientWrapper.settingsAdaptor.set_RingtonePath(url) + btnRingtone.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtonePath()) + } else if (ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtonePath().length === 0){ + btnRingtone.text = qsTr("Add a custom ringtone") + } + } + + function changeFileCACert(url){ + if(url.length !== 0) { + ClientWrapper.settingsAdaptor.set_FileCACert(url) + btnCACert.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateListFile()) + } + } + + function changeFileUserCert(url){ + if(url.length !== 0) { + ClientWrapper.settingsAdaptor.set_FileUserCert(url) + btnUserCert.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateFile()) + } + } + + function changeFilePrivateKey(url){ + if(url.length !== 0) { + ClientWrapper.settingsAdaptor.set_FilePrivateKey(url) + btnPrivateKey.text = ClientWrapper.utilsAdaptor.toFileInfoName(ClientWrapper.settingsAdaptor.getAccountConfig_TLS_PrivateKeyFile()) + } + } + + JamiFileDialog { + id: ringtonePath_Dialog + + property string oldPath : ClientWrapper.settingsAdaptor.getAccountConfig_Ringtone_RingtonePath() + property string openPath : oldPath === "" ? (ClientWrapper.utilsAdaptor.getCurrentPath() + "/ringtones/") : (ClientWrapper.utilsAdaptor.toFileAbsolutepath(oldPath)) + + mode: JamiFileDialog.OpenFile + title: qsTr("Select a new ringtone") + folder: openPath + + nameFilters: [qsTr("Audio Files") + " (*.wav *.ogg *.opus *.mp3 *.aiff *.wma)", qsTr( + "All files") + " (*)"] + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + + onAccepted: { + var url = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + changeRingtonePath(url) + } + } + + JamiFileDialog { + id: caCert_Dialog + + property string oldPath : ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateListFile() + property string openPath : oldPath === "" ? (ClientWrapper.utilsAdaptor.getCurrentPath() + "/ringtones/") : (ClientWrapper.utilsAdaptor.toFileAbsolutepath(oldPath)) + + mode: JamiFileDialog.OpenFile + title: qsTr("Select a CA certificate") + folder: openPath + nameFilters: [qsTr("Certificate File") + " (*.crt)", qsTr( + "All files") + " (*)"] + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + + onAccepted: { + var url = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + changeFileCACert(url) + } + } + + JamiFileDialog { + id: userCert_Dialog + + property string oldPath : ClientWrapper.settingsAdaptor.getAccountConfig_TLS_CertificateFile() + property string openPath : oldPath === "" ? (ClientWrapper.utilsAdaptor.getCurrentPath() + "/ringtones/") : (ClientWrapper.utilsAdaptor.toFileAbsolutepath(oldPath)) + + mode: JamiFileDialog.OpenFile + title: qsTr("Select a user certificate") + folder: openPath + nameFilters: [qsTr("Certificate File") + " (*.crt)", qsTr( + "All files") + " (*)"] + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + + onAccepted: { + var url = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + changeFileUserCert(url) + } + } + + JamiFileDialog { + id: privateKey_Dialog + + property string oldPath : ClientWrapper.settingsAdaptor.getAccountConfig_TLS_PrivateKeyFile() + property string openPath : oldPath === "" ? (ClientWrapper.utilsAdaptor.getCurrentPath() + "/ringtones/") : (ClientWrapper.utilsAdaptor.toFileAbsolutepath(oldPath)) + + mode: JamiFileDialog.OpenFile + title: qsTr("Select a private key") + folder: openPath + nameFilters: [qsTr("Key File") + " (*.key)", qsTr( + "All files") + " (*)"] + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + + onAccepted: { + var url = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + changeFilePrivateKey(url) + } + } + + spacing: 6 + + Layout.preferredWidth: 532 + Layout.maximumWidth: 532 + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 24 + Layout.preferredHeight: 24 + Layout.maximumHeight: 24 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Call Settings") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + ToggleSwitch { + id: checkBoxUntrusted + + Layout.leftMargin: 20 + + labelText: qsTr("Allow incoming calls from unknown contacts") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setCallsUntrusted(checked) + } + } + + ToggleSwitch { + id: checkBoxAutoAnswer + + Layout.fillWidth: true + Layout.leftMargin: 20 + + labelText: qsTr("Auto Answer Calls") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setAutoAnswerCalls(checked) + } + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.leftMargin: 20 + Layout.maximumHeight: 30 + + ToggleSwitch { + id: checkBoxCustomRingtone + + labelText: qsTr("Enable Custom Ringtone") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setEnableRingtone(checked) + btnRingtone.enabled = checked + } + } + + HoverableRadiusButton { + id: btnRingtone + + Layout.minimumWidth: 300 + Layout.preferredWidth: 300 + Layout.maximumWidth: 300 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.width: 16 + icon.height: 16 + + onClicked: { + ringtonePath_Dialog.open() + } + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Name Server") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 20 + Layout.maximumHeight: 29 + + Label { + Layout.minimumWidth: 60 + + Layout.minimumHeight: 29 + Layout.preferredHeight: 29 + Layout.maximumHeight: 29 + + text: qsTr("Address") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + InfoLineEdit { + id: lineEditNameServer + + fieldLayoutWidth: 300 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setNameServer(text) + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("OpenDHT Configuration") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.leftMargin: 20 + + ToggleSwitch { + id: checkBoxEnableProxy + + labelText: qsTr("Enable proxy") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setEnableProxy(checked) + lineEditProxy.enabled = checked + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + InfoLineEdit { + id: lineEditProxy + + fieldLayoutWidth: 300 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setProxyAddress(text) + } + } + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.leftMargin: 20 + + Label { + id: labelBootstrap + + Layout.minimumWidth: 72 + Layout.preferredWidth: 72 + + Layout.minimumHeight: 29 + Layout.preferredHeight: 29 + Layout.maximumHeight: 29 + + text: qsTr("Bootstrap") + font.pointSize: 10 + font.kerning: true + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + InfoLineEdit { + id: lineEditBootstrap + + fieldLayoutWidth: 300 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setBootstrapAddress(text) + } + } + } + } + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Security") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + GridLayout { + rows: 4 + columns: 2 + rowSpacing: 0 + columnSpacing: 6 + + Layout.fillWidth: true + Layout.leftMargin: 20 + + // CA Certificate + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 32 + Layout.preferredHeight: 32 + Layout.maximumHeight: 32 + + text: qsTr("CA Certificate") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + HoverableRadiusButton { + id: btnCACert + + radius: height / 2 + + Layout.minimumWidth: 298 + Layout.preferredWidth: 298 + Layout.maximumWidth: 298 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.width: 16 + icon.height: 16 + + onClicked: { + caCert_Dialog.open() + } + } + + // User Certificate + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 32 + Layout.preferredHeight: 32 + Layout.maximumHeight: 32 + + text: qsTr("User Certificate") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + HoverableRadiusButton { + id: btnUserCert + + radius: height / 2 + + Layout.minimumWidth: 298 + Layout.preferredWidth: 298 + Layout.maximumWidth: 298 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.width: 16 + icon.height: 16 + + onClicked: { + userCert_Dialog.open() + } + } + + // Private Key + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 32 + Layout.preferredHeight: 32 + Layout.maximumHeight: 32 + + text: qsTr("Private Key") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + HoverableRadiusButton { + id: btnPrivateKey + + radius: height / 2 + + Layout.minimumWidth: 298 + Layout.preferredWidth: 298 + Layout.maximumWidth: 298 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.width: 16 + icon.height: 16 + + onClicked: { + privateKey_Dialog.open() + } + } + + // Private key password + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 29 + Layout.preferredHeight: 29 + Layout.maximumHeight: 29 + + text: qsTr("Private Key Password") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + InfoLineEdit { + id: lineEditCertPassword + + fieldLayoutWidth: 300 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + echoMode: TextInput.Password + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + Layout.topMargin: 10 + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Connectivity") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + GridLayout { + Layout.leftMargin: 20 + Layout.fillWidth: true + + rows: 6 + columns: 3 + rowSpacing: 6 + columnSpacing: 6 + + // row 2 + ToggleSwitch { + id: checkAutoConnectOnLocalNetwork + + Layout.row: 0 + Layout.column: 0 + + labelText: qsTr("Auto Connect On Local Network") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setAutoConnectOnLocalNetwork(checked) + } + } + + Item { + Layout.row: 0 + Layout.column: 1 + + Layout.fillHeight: true + + Layout.minimumWidth: 40 + Layout.preferredWidth: 40 + Layout.maximumWidth: 40 + } + + // row 2 + ToggleSwitch { + id: checkBoxUPnP + + Layout.row: 1 + Layout.column: 0 + + labelText: qsTr("Use UPnP") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseUPnP(checked) + } + } + + Item { + Layout.row: 1 + Layout.column: 1 + + Layout.fillHeight: true + + Layout.minimumWidth: 40 + Layout.preferredWidth: 40 + Layout.maximumWidth: 40 + } + + // row 3 + ToggleSwitch { + id: checkBoxTurnEnable + + Layout.row: 2 + Layout.column: 0 + + labelText: qsTr("Use TURN") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseTURN(checked) + } + } + + // row 4 + Label { + Layout.row: 3 + Layout.column: 0 + + Layout.minimumWidth: 124 + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("TURN Address") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + InfoLineEdit { + id: lineEditTurnAddress + + Layout.row: 3 + Layout.column: 2 + + fieldLayoutWidth: 300 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setTURNAddress(text) + } + } + + //row 5 + Label { + Layout.row: 4 + Layout.column: 0 + + Layout.minimumWidth: 124 + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("TURN Username") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + InfoLineEdit { + id: lineEditTurnUsername + + Layout.row: 4 + Layout.column: 2 + + fieldLayoutWidth: 300 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setTURNUsername(text) + } + } + + //row 6 + Label { + Layout.row: 5 + Layout.column: 0 + + Layout.minimumWidth: 124 + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("TURN Password") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + InfoLineEdit { + id: lineEditTurnPassword + layer.mipmap: false + + Layout.row: 5 + Layout.column: 2 + + fieldLayoutWidth: 300 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + echoMode: TextInput.Password + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setTURNPassword(text) + } + } + + // row 7 + ToggleSwitch { + id: checkBoxSTUNEnable + + Layout.row: 6 + Layout.column: 0 + + labelText: qsTr("Use STUN") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setUseSTUN(checked) + lineEditSTUNAddress.enabled = checked + } + } + + InfoLineEdit { + id: lineEditSTUNAddress + + Layout.row: 6 + Layout.column: 2 + + fieldLayoutWidth: 300 + fieldLayoutHeight: 29 + + font.pointSize: 10 + font.kerning: true + + placeholderText: qsTr("STUN Address") + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setSTUNAddress(text) + } + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + Layout.topMargin: 10 + + Layout.minimumHeight: 27 + Layout.preferredHeight: 27 + Layout.maximumHeight: 27 + + text: qsTr("Media") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + ToggleSwitch { + id: videoCheckBox + + Layout.leftMargin: 20 + + labelText: qsTr("Enable Video") + fontPointSize: 10 + + onSwitchToggled: { + ClientWrapper.settingsAdaptor.setVideoState(checked) + } + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.leftMargin: 20 + + ColumnLayout { + spacing: 6 + //Layout.fillWidth: true + Layout.maximumWidth: 348 + + RowLayout { + spacing: 6 + Layout.fillWidth: true + + Layout.maximumHeight: 30 + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Video Codecs") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + + Layout.minimumWidth: 20 + Layout.preferredWidth: 20 + Layout.maximumWidth: 20 + } + + HoverableRadiusButton { + id: videoDownPushButton + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + radius: height / 2 + scale: 1 + + font.pointSize: 9 + font.kerning: true + + icon.source: "qrc:/images/icons/round-arrow_drop_down-24px.svg" + icon.width: 32 + icon.height: 32 + + onClicked: { + decreaseVideoCodecPriority() + } + } + + HoverableRadiusButton { + id: videoUpPushButton + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + radius: height / 2 + + font.pointSize: 9 + font.kerning: true + + icon.source: "qrc:/images/icons/round-arrow_drop_up-24px.svg" + icon.width: 32 + icon.height: 32 + + onClicked: { + increaseVideoCodecPriority() + } + } + } + + ListViewJami { + id: videoListWidget + + Layout.minimumWidth: 348 + Layout.preferredWidth: 348 + Layout.maximumWidth: 348 + + Layout.minimumHeight: 192 + Layout.preferredHeight: 192 + Layout.maximumHeight: 192 + + model: videoCodecListModel + + delegate: VideoCodecDelegate { + id: videoCodecDelegate + + width: videoListWidget.width + height: videoListWidget.height / 4 + + videoCodecName : VideoCodecName + isEnabled : IsEnabled + videoCodecId: VideoCodecID + + onClicked: { + videoListWidget.currentIndex = index + } + + onVideoCodecStateChange:{ + ClientWrapper.settingsAdaptor.videoCodecsStateChange(idToSet , isToBeEnabled) + updateVideoCodecs() + } + } + } + } + + ColumnLayout { + spacing: 6 + Layout.maximumWidth: 348 + + RowLayout { + spacing: 6 + Layout.fillWidth: true + + Layout.maximumHeight: 30 + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Audio Codecs") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + + Layout.minimumWidth: 20 + Layout.preferredWidth: 20 + Layout.maximumWidth: 20 + } + + HoverableRadiusButton { + id: audioDownPushButton + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + buttonImageHeight: height + buttonImageWidth: height + + font.pointSize: 9 + font.kerning: true + + icon.source: "qrc:/images/icons/round-arrow_drop_down-24px.svg" + icon.width: 32 + icon.height: 32 + + onClicked: { + decreaseAudioCodecPriority() + } + } + + HoverableRadiusButton { + id: audioUpPushButton + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + + radius: height / 2 + + font.pointSize: 9 + font.kerning: true + + icon.source: "qrc:/images/icons/round-arrow_drop_up-24px.svg" + icon.width: 32 + icon.height: 32 + + onClicked: { + increaseAudioCodecPriority() + } + } + } + + ListViewJami { + id: audioListWidget + + Layout.minimumWidth: 348 + Layout.preferredWidth: 348 + Layout.maximumWidth: 348 + + Layout.minimumHeight: 192 + Layout.preferredHeight: 192 + Layout.maximumHeight: 192 + + model: audioCodecListModel + + delegate: AudioCodecDelegate { + id: audioCodecDelegate + + width: audioListWidget.width + height: audioListWidget.height / 4 + + layer.mipmap: false + clip: true + + audioCodecName : AudioCodecName + isEnabled : IsEnabled + audioCodecId: AudioCodecID + samplerRate: Samplerate + + onClicked: { + audioListWidget.currentIndex = index + } + + onAudioCodecStateChange:{ + ClientWrapper.settingsAdaptor.audioCodecsStateChange(idToSet , isToBeEnabled) + updateAudioCodecs() + } + } + } + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 48 + Layout.preferredHeight: 48 + Layout.maximumHeight: 48 + } +} diff --git a/src/settingsview/components/AudioCodecDelegate.qml b/src/settingsview/components/AudioCodecDelegate.qml new file mode 100644 index 00000000..9f80a2e1 --- /dev/null +++ b/src/settingsview/components/AudioCodecDelegate.qml @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +ItemDelegate { + id: videoCodecDelegate + + property string audioCodecName : "" + property bool isEnabled : false + property int audioCodecId + property string samplerRate: "" + + signal audioCodecStateChange(string idToSet , bool isToBeEnabled) + + property int checkBoxWidth: 10 + + highlighted: ListView.isCurrentItem + + RowLayout{ + anchors.fill: parent + z: 1 + + spacing: 10 + + CheckBox{ + id: checkBoxIsEnabled + + Layout.leftMargin: 20 + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillHeight: true + + Layout.minimumWidth: checkBoxWidth + Layout.preferredWidth: checkBoxWidth + Layout.maximumWidth: checkBoxWidth + + tristate: false + checkState: isEnabled ? Qt.Checked : Qt.Unchecked + + indicator.implicitWidth: checkBoxWidth + indicator.implicitHeight:checkBoxWidth + + indicator.layer.textureSize.width: checkBoxWidth + indicator.layer.textureSize.height: checkBoxWidth + + text: "" + + nextCheckState: function() { + var result + var result_bool + + if (checkState === Qt.Checked){ + result = Qt.Unchecked + result_bool = false + } else { + result = Qt.Checked + result_bool = true + } + audioCodecStateChange(audioCodecId,result_bool) + return result + } + } + + Label{ + id: formatNameLabel + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.fillWidth: true + Layout.fillHeight: true + + text: audioCodecName + " " + samplerRate + " Hz" + font.pointSize: 8 + font.kerning: true + } + } +} diff --git a/src/settingsview/components/AvSettingPage.qml b/src/settingsview/components/AvSettingPage.qml new file mode 100644 index 00000000..a999e32f --- /dev/null +++ b/src/settingsview/components/AvSettingPage.qml @@ -0,0 +1,832 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +Rectangle { + id: avSettingPage + + AudioInputDeviceModel{ + id: audioInputDeviceModel + } + + AudioOutputDeviceModel{ + id: audioOutputDeviceModel + } + + AudioManagerListModel{ + id: audioManagerListModel + } + + VideoInputDeviceModel{ + id: videoInputDeviceModel + } + + VideoFormatResolutionModel{ + id: videoFormatResolutionModel + } + + VideoFormatFpsModel{ + id: videoFormatFpsModel + } + + function populateAVSettings(){ + audioInputDeviceModel.reset() + audioOutputDeviceModel.reset() + + inputComboBox.currentIndex = audioInputDeviceModel.getCurrentSettingIndex() + outputComboBox.currentIndex = audioOutputDeviceModel.getCurrentSettingIndex() + ringtoneDeviceComboBox.currentIndex = audioOutputDeviceModel.getCurrentRingtoneDeviceIndex() + + audioManagerRowLayout.visible = (audioManagerListModel.rowCount() > 0) + if(audioManagerListModel.rowCount() > 0){ + audioManagerComboBox.currentIndex = audioManagerListModel.getCurrentSettingIndex() + } + + populateVideoSettings() + var encodeAccel = ClientWrapper.avmodel.getHardwareAcceleration() + hardwareAccelControl.checked = encodeAccel + } + + function populateVideoSettings() { + videoInputDeviceModel.reset() + + deviceBox.enabled = (videoInputDeviceModel.deviceCount() > 0) + resolutionBox.enabled = (videoInputDeviceModel.deviceCount() > 0) + fpsBox.enabled = (videoInputDeviceModel.deviceCount() > 0) + labelVideoDevice.enabled = (videoInputDeviceModel.deviceCount() > 0) + labelVideoResolution.enabled = (videoInputDeviceModel.deviceCount() > 0) + labelVideoFps.enabled = (videoInputDeviceModel.deviceCount() > 0) + + deviceBox.currentIndex = videoInputDeviceModel.getCurrentSettingIndex() + slotDeviceBoxCurrentIndexChanged(deviceBox.currentIndex) + + try{ + startPreviewing(false) + } catch (err2){ console.log("Start preview fail when populate video settings, exception: "+ err2.message)} + + } + + function setFormatListForCurrentDevice(){ + var device = ClientWrapper.avmodel.getCurrentVideoCaptureDevice() + if(ClientWrapper.settingsAdaptor.get_DeviceCapabilitiesSize(device) === 0){ + return + } + + try{ + videoFormatResolutionModel.reset() + resolutionBox.currentIndex = videoFormatResolutionModel.getCurrentSettingIndex() + slotFormatCurrentIndexChanged(resolutionBox.currentIndex,true) + } catch(err){console.warn("Exception: " + err.message)} + } + + function startPreviewing(force = false){ + ClientWrapper.accountAdaptor.startPreviewing(force) + previewAvailable = true + } + + function stopPreviewing(){ + ClientWrapper.accountAdaptor.stopPreviewing() + } + + function startAudioMeter(async = true){ + audioInputMeter.start() + ClientWrapper.accountAdaptor.startAudioMeter(async) + } + + function stopAudioMeter(async = true){ + audioInputMeter.stop() + ClientWrapper.accountAdaptor.stopAudioMeter(async) + } + + // slots for av page + function slotAudioMeter(id, level){ + if (id === "audiolayer_id") { + audioInputMeter.setLevel(level) + } + } + + function slotSetHardwareAccel(state){ + ClientWrapper.accountAdaptor.avModel().setHardwareAcceleration(state) + startPreviewing(true) + } + + function slotAudioManagerIndexChanged(index){ + stopAudioMeter(false) + var selectedAudioManager = audioManagerListModel.data(audioManagerListModel.index( + index, 0), AudioManagerListModel.AudioManagerID) + ClientWrapper.avmodel.setAudioManager(selectedAudioManager) + startAudioMeter(false) + } + + function slotRingtoneDeviceIndexChanged(index){ + stopAudioMeter(false) + var selectedRingtoneDeviceName = audioOutputDeviceModel.data(audioOutputDeviceModel.index( + index, 0), AudioOutputDeviceModel.Device_ID) + ClientWrapper.avmodel.setRingtoneDevice(selectedRingtoneDeviceName) + startAudioMeter(false) + } + + function slotAudioOutputIndexChanged(index){ + stopAudioMeter(false) + var selectedOutputDeviceName = audioOutputDeviceModel.data(audioOutputDeviceModel.index( + index, 0), AudioOutputDeviceModel.Device_ID) + ClientWrapper.avmodel.setOutputDevice(selectedOutputDeviceName) + startAudioMeter(false) + } + + function slotAudioInputIndexChanged(index){ + stopAudioMeter(false) + var selectedInputDeviceName = audioInputDeviceModel.data(audioInputDeviceModel.index( + index, 0), AudioInputDeviceModel.Device_ID) + + ClientWrapper.avmodel.setInputDevice(selectedInputDeviceName) + startAudioMeter(false) + } + + function slotDeviceBoxCurrentIndexChanged(index){ + if(videoInputDeviceModel.deviceCount() <= 0){ + return + } + + try{ + var deviceId = videoInputDeviceModel.data(videoInputDeviceModel.index( + index, 0), VideoInputDeviceModel.DeviceId) + var deviceName = videoInputDeviceModel.data(videoInputDeviceModel.index( + index, 0), VideoInputDeviceModel.DeviceName) + if(deviceId.length === 0){ + console.warn("Couldn't find device: " + deviceName) + return + } + + ClientWrapper.avmodel.setCurrentVideoCaptureDevice(deviceId) + ClientWrapper.avmodel.setDefaultDevice(deviceId) + setFormatListForCurrentDevice() + startPreviewing(true) + } catch(err){console.warn(err.message)} + } + + function slotFormatCurrentIndexChanged(index, isResolutionIndex){ + var resolution + var rate + if(isResolutionIndex){ + resolution = videoFormatResolutionModel.data(videoFormatResolutionModel.index( + index, 0), VideoFormatResolutionModel.Resolution) + videoFormatFpsModel.currentResolution = resolution + fpsBox.currentIndex = videoFormatFpsModel.getCurrentSettingIndex() + rate = videoFormatFpsModel.data(videoFormatFpsModel.index( + fpsBox.currentIndex, 0), VideoFormatFpsModel.FPS) + } else { + resolution = videoFormatResolutionModel.data(videoFormatResolutionModel.index( + resolutionBox.currentIndex, 0), VideoFormatResolutionModel.Resolution) + videoFormatFpsModel.currentResolution = resolution + rate = videoFormatFpsModel.data(videoFormatFpsModel.index( + index, 0), VideoFormatFpsModel.FPS) + } + + try{ + ClientWrapper.settingsAdaptor.set_Video_Settings_Rate_And_Resolution(ClientWrapper.avmodel.getCurrentVideoCaptureDevice(),rate,resolution) + } catch(error){console.warn(error.message)} + } + + function slotVideoDeviceListChanged(){ + populateVideoSettings() + } + + property bool previewAvailable: false + + Connections{ + target: ClientWrapper.avmodel + + function onAudioMeter(id, level){ + slotAudioMeter(id,level) + } + } + + Connections{ + target: ClientWrapper.renderManager + + function onVideoDeviceListChanged(){ + slotVideoDeviceListChanged() + } + } + + Layout.fillHeight: true + Layout.fillWidth: true + + ScrollView{ + anchors.fill: parent + clip: true + + RowLayout { + width: avSettingPage.width + height: avSettingPage.height + + spacing: 0 + Item { + Layout.fillHeight: true + Layout.maximumWidth: 48 + Layout.preferredWidth: 48 + Layout.minimumWidth: 48 + } + + ColumnLayout { + spacing: 7 + + Layout.fillHeight: true + Layout.maximumWidth: 580 + Layout.preferredWidth: 580 + Layout.minimumWidth: 580 + + Item { + Layout.fillWidth: true + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + Label { + Layout.fillWidth: true + Layout.minimumHeight: 25 + Layout.preferredHeight: 25 + Layout.maximumHeight: 25 + + text: qsTr("Audio / Video") + font.pointSize: 15 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.minimumHeight: 24 + Layout.preferredHeight: 24 + Layout.maximumHeight: 24 + } + + ColumnLayout { + spacing: 0 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + Layout.minimumHeight: 21 + Layout.preferredHeight: 21 + Layout.maximumHeight: 21 + + text: qsTr("Audio") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 7 + Layout.fillWidth: true + + RowLayout { + spacing: 7 + Layout.leftMargin: 20 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Label { + Layout.maximumWidth: 77 + Layout.preferredWidth: 77 + Layout.minimumWidth: 77 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Microphone") + font.pointSize: 11 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + SettingParaCombobox { + id: inputComboBox + + Layout.maximumWidth: 360 + Layout.preferredWidth: 360 + Layout.minimumWidth: 360 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + model: audioInputDeviceModel + + textRole: "ID_UTF8" + + onActivated: { + slotAudioInputIndexChanged(index) + } + } + } + + // the audio level meter + LevelMeter { + id: audioInputMeter + + Layout.leftMargin: 20 + + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + + indeterminate: false + from: 0 + to: 100 + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 5 + Layout.preferredHeight: 5 + Layout.maximumHeight: 5 + } + + RowLayout { + spacing: 7 + Layout.leftMargin: 20 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Label { + Layout.maximumWidth: 95 + Layout.preferredWidth: 95 + Layout.minimumWidth: 95 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Output Device") + font.pointSize: 11 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + SettingParaCombobox { + id: outputComboBox + + Layout.maximumWidth: 360 + Layout.preferredWidth: 360 + Layout.minimumWidth: 360 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + model: audioOutputDeviceModel + + textRole: "ID_UTF8" + + onActivated: { + slotAudioOutputIndexChanged(index) + } + } + } + + RowLayout { + spacing: 7 + Layout.leftMargin: 20 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Label { + Layout.maximumWidth: 77 + Layout.preferredWidth: 77 + Layout.minimumWidth: 77 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Ringtone Device") + font.pointSize: 11 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + SettingParaCombobox { + id: ringtoneDeviceComboBox + + Layout.maximumWidth: 360 + Layout.preferredWidth: 360 + Layout.minimumWidth: 360 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + model: audioOutputDeviceModel + + textRole: "ID_UTF8" + + onActivated: { + slotRingtoneDeviceIndexChanged(index) + } + } + } + + RowLayout { + id: audioManagerRowLayout + + spacing: 7 + Layout.leftMargin: 20 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Label { + Layout.maximumWidth: 77 + Layout.preferredWidth: 77 + Layout.minimumWidth: 77 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Audio Manager") + font.pointSize: 11 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + SettingParaCombobox { + id: audioManagerComboBox + + Layout.maximumWidth: 360 + Layout.preferredWidth: 360 + Layout.minimumWidth: 360 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + model: audioManagerListModel + + textRole: "ID_UTF8" + + onActivated: { + slotAudioManagerIndexChanged(index) + } + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + ColumnLayout { + spacing: 7 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Video") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 6 + RowLayout { + spacing: 7 + Layout.fillWidth: true + Layout.leftMargin: 20 + Layout.maximumHeight: 30 + + Label { + id: labelVideoDevice + + Layout.maximumWidth: 44 + Layout.preferredWidth: 44 + Layout.minimumWidth: 44 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Device") + font.pointSize: 11 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + SettingParaCombobox { + id: deviceBox + + Layout.maximumWidth: 360 + Layout.preferredWidth: 360 + Layout.minimumWidth: 360 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + model: videoInputDeviceModel + + textRole: "DeviceName_UTF8" + + onActivated: { + slotDeviceBoxCurrentIndexChanged(index) + } + } + } + + RowLayout { + spacing: 7 + Layout.fillWidth: true + Layout.leftMargin: 20 + Layout.maximumHeight: 30 + + Label { + id: labelVideoResolution + + Layout.maximumWidth: 47 + Layout.preferredWidth: 47 + Layout.minimumWidth: 47 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Resolution") + font.pointSize: 11 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + SettingParaCombobox { + id: resolutionBox + + Layout.maximumWidth: 360 + Layout.preferredWidth: 360 + Layout.minimumWidth: 360 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + model: videoFormatResolutionModel + textRole: "Resolution_UTF8" + + onActivated: { + slotFormatCurrentIndexChanged(index,true) + } + } + } + + RowLayout { + spacing: 7 + Layout.fillWidth: true + Layout.leftMargin: 20 + Layout.maximumHeight: 30 + + Label { + id: labelVideoFps + + Layout.maximumWidth: 47 + Layout.preferredWidth: 47 + Layout.minimumWidth: 47 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Fps") + font.pointSize: 11 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + SettingParaCombobox { + id: fpsBox + + Layout.maximumWidth: 360 + Layout.preferredWidth: 360 + Layout.minimumWidth: 360 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + + model: videoFormatFpsModel + textRole: "FPS_ToDisplay_UTF8" + + onActivated: { + slotFormatCurrentIndexChanged(index,false) + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + RowLayout{ + Layout.alignment: Qt.AlignHCenter + + Layout.preferredWidth: 580 + Layout.minimumWidth: 580 + + Layout.minimumHeight: 224 + Layout.preferredHeight: 224 + Layout.maximumHeight: 224 + + Rectangle { + Layout.alignment: Qt.AlignHCenter + Layout.fillHeight: true + Layout.minimumWidth: 580 + + color: "black" + + PreviewRenderer{ + id: peviewWidget + + visible: previewAvailable + height: parent.height + width: 224 + x: (parent.width - width) /2 + y: 0 + } + } + } + + Label { + visible: !previewAvailable + + Layout.fillWidth: true + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Preview unavailable") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + // Toggle switch to enable hardware acceleration + ToggleSwitch { + id: hardwareAccelControl + + labelText: "Enable hardware acceleration" + + onSwitchToggled: { + slotSetHardwareAccel(checked) + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + + } +} diff --git a/src/settingsview/components/BannedItemDelegate.qml b/src/settingsview/components/BannedItemDelegate.qml new file mode 100644 index 00000000..980d539a --- /dev/null +++ b/src/settingsview/components/BannedItemDelegate.qml @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +ItemDelegate { + id: deviceItemDelegate + + property string contactName : "" + property string contactID: "" + property string contactPicture_base64:"" + + signal btnReAddContactClicked + + function btnReAddContactEnter(){ + btnReAddContact.enterBtn() + } + + function btnReAddContactExit(){ + btnReAddContact.exitBtn() + } + + function btnReAddContactPress(){ + btnReAddContact.pressBtn() + } + + function btnReAddContactRelease(){ + btnReAddContact.releaseBtn() + } + + highlighted: ListView.isCurrentItem + + RowLayout{ + anchors.fill: parent + + spacing: 7 + + Label{ + id: labelContactAvatar + + Layout.alignment: Qt.AlignVCenter + + Layout.leftMargin: 7 + Layout.topMargin: 7 + Layout.bottomMargin: 7 + + Layout.minimumWidth: 48 + Layout.preferredWidth: 48 + Layout.maximumWidth: 48 + + Layout.minimumHeight: 48 + Layout.preferredHeight: 48 + Layout.maximumHeight: 48 + + background: Rectangle{ + anchors.fill: parent + color: "transparent" + Image { + id: avatarImg + + anchors.fill: parent + source: "data:image/png;base64," + contactPicture_base64 + fillMode: Image.PreserveAspectCrop + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle{ + width: avatarImg.width + height: avatarImg.height + radius: { + var size = ((avatarImg.width <= avatarImg.height)? avatarImg.width:avatarImg.height) + return size /2 + } + } + } + } + } + } + + Item{ + Layout.minimumWidth: 8 + Layout.preferredWidth: 8 + Layout.maximumWidth: 8 + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + ColumnLayout{ + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.topMargin: 7 + Layout.bottomMargin: 7 + + Layout.alignment: Qt.AlignVCenter + + spacing: 7 + + Label{ + id: labelContactName + + Layout.fillWidth: true + Layout.minimumWidth: 0 + Layout.maximumWidth: 16777215 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 8 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + text: contactName === "" ? qsTr("name") : contactName + } + + Label{ + id: labelContactId + + Layout.fillWidth: true + Layout.minimumWidth: 0 + Layout.maximumWidth: 16777215 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 8 + font.kerning: true + + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + + text: contactID === "" ? qsTr("id") : contactID + } + } + + HoverableRadiusButton{ + id: btnReAddContact + + Layout.topMargin: 7 + Layout.bottomMargin: 7 + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 8 + font.kerning: true + + buttonImageHeight: height + buttonImageWidth: height + + source:"qrc:/images/icons/ic_person_add_black_24dp_2x.png" + + ToolTip.visible: isHovering + ToolTip.text: qsTr("Add as contact") + + onClicked: { + btnReAddContactClicked() + } + } + + Item{ + Layout.rightMargin: 7 + + Layout.minimumWidth: 8 + Layout.preferredWidth: 8 + Layout.maximumWidth: 8 + + Layout.minimumHeight: 20 + } + } +} + diff --git a/src/settingsview/components/CurrentAccountSettingsScrollPage.qml b/src/settingsview/components/CurrentAccountSettingsScrollPage.qml new file mode 100644 index 00000000..b3d43923 --- /dev/null +++ b/src/settingsview/components/CurrentAccountSettingsScrollPage.qml @@ -0,0 +1,1376 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.3 +import Qt.labs.platform 1.1 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Rectangle { + id: accountViewRect + + enum RegName { + BLANK, + INVALIDFORM, + TAKEN, + FREE, + SEARCHING + } + + property int regNameUi: CurrentAccountSettingsScrollPage.BLANK + property string registeredName: "" + property bool registeredIdNeedsSet: false + + property int refreshVariable : 0 + + signal navigateToMainView + signal navigateToNewWizardView + + function refreshRelevantUI(){ + refreshVariable++ + refreshVariable-- + } + + Connections { + id: btnRegisterNameClickConnection + target: btnRegisterName + + enabled: { + refreshVariable + switch (regNameUi) { + case CurrentAccountSettingsScrollPage.FREE: + return true + default: + return false + } + } + + function onClicked() { + slotRegisterName() + } + } + + function updateAccountInfoDisplayed() { + setAvatar() + + accountEnableCheckBox.checked = ClientWrapper.settingsAdaptor.get_CurrentAccountInfo_Enabled() + displayNameLineEdit.text = ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Alias() + + var showLocalAccountConfig = (ClientWrapper.settingsAdaptor.getAccountConfig_Manageruri() === "") + passwdPushButton.visible = showLocalAccountConfig + btnExportAccount.visible = showLocalAccountConfig + linkDevPushButton.visible = showLocalAccountConfig + + registeredIdNeedsSet = (ClientWrapper.settingsAdaptor.get_CurrentAccountInfo_RegisteredName() === "") + + if(!registeredIdNeedsSet){ + currentRegisteredID.text = ClientWrapper.settingsAdaptor.get_CurrentAccountInfo_RegisteredName() + } else { + currentRegisteredID.text = "" + } + + currentRingID.text = ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Uri() + + // update device list view + updateAndShowDevicesSlot() + + bannedContactsListWidget.visible = false + bannedContactsLayoutWidget.visible = (bannedListModel.rowCount() > 0) + + if (advanceSettingsView.visible) { + advanceSettingsView.updateAccountInfoDisplayedAdvance() + } + refreshRelevantUI() + } + + function connectCurrentAccount() { + accountConnections_ContactModel.enabled = true + accountConnections_DeviceModel.enabled = true + } + + function disconnectAccountConnections() { + accountConnections_ContactModel.enabled = false + accountConnections_DeviceModel.enabled = false + } + + function isPhotoBoothOpened() { + return currentAccountAvatar.takePhotoState + } + + function setAvatar() { + currentAccountAvatar.setAvatarPixmap( + ClientWrapper.settingsAdaptor.getAvatarImage_Base64( + currentAccountAvatar.boothWidht), + ClientWrapper.settingsAdaptor.getIsDefaultAvatar()) + } + + function stopBooth() { + currentAccountAvatar.stopBooth() + } + + function toggleBannedContacts(){ + var bannedContactsVisible = bannedContactsListWidget.visible + bannedContactsListWidget.visible = !bannedContactsVisible + updateAndShowBannedContactsSlot() + } + + function unban(index){ + ClientWrapper.settingsAdaptor.unbanContact(index) + updateAndShowBannedContactsSlot() + } + + Connections { + id: accountConnections_ContactModel + target: ClientWrapper.contactModel + + function onModelUpdated(uri, needsSorted) { + updateAndShowBannedContactsSlot() + } + + function onContactAdded(contactUri){ + updateAndShowBannedContactsSlot() + } + + function onContactRemoved(contactUri){ + updateAndShowBannedContactsSlot() + } + } + + Connections { + id: accountConnections_DeviceModel + target: ClientWrapper.deviceModel + + function onDeviceAdded(id) { + updateAndShowDevicesSlot() + } + + function onDeviceRevoked(id, status) { + updateAndShowDevicesSlot() + } + + function onDeviceUpdated(id) { + updateAndShowDevicesSlot() + } + } + + // slots + function verifyRegisteredNameSlot() { + if (ClientWrapper.settingsAdaptor.get_CurrentAccountInfo_RegisteredName() !== "") { + regNameUi = CurrentAccountSettingsScrollPage.BLANK + } else { + registeredName = ClientWrapper.utilsAdaptor.stringSimplifier( + currentRegisteredID.text) + if (registeredName !== "") { + if (ClientWrapper.utilsAdaptor.validateRegNameForm(registeredName)) { + regNameUi = CurrentAccountSettingsScrollPage.SEARCHING + lookUpLabelTimer.restart() + } else { + regNameUi = CurrentAccountSettingsScrollPage.INVALIDFORM + } + } else { + regNameUi = CurrentAccountSettingsScrollPage.BLANK + } + } + } + + Timer { + id: lookUpLabelTimer + + interval: 300 + onTriggered: { + beforeNameLookup() + } + } + + function beforeNameLookup() { + ClientWrapper.nameDirectory.lookupName("", registeredName) + } + + Connections { + target: ClientWrapper.nameDirectory + enabled: true + + function onRegisteredNameFound(status, address, name) { + afterNameLookup(status, name) + } + } + + function afterNameLookup(status, regName) { + if (registeredName === regName && regName.length > 2) { + switch (status) { + case NameDirectory.LookupStatus.NOT_FOUND: + regNameUi = CurrentAccountSettingsScrollPage.FREE + break + default: + regNameUi = CurrentAccountSettingsScrollPage.TAKEN + break + } + } else { + regNameUi = CurrentAccountSettingsScrollPage.BLANK + } + } + + function setAccEnableSlot(state) { + ClientWrapper.accountModel.setAccountEnabled(ClientWrapper.utilsAdaptor.getCurrAccId(), state) + } + + /* + * JamiFileDialog for exporting account + */ + JamiFileDialog { + id: exportBtn_Dialog + + mode: JamiFileDialog.SaveFile + + title: qsTr("Export Account Here") + folder: StandardPaths.writableLocation(StandardPaths.DesktopLocation) + + nameFilters: [qsTr("Jami archive files") + " (*.gz)", qsTr( + "All files") + " (*)"] + + onAccepted: { + // is there password? If so, go to password dialog, else, go to following directly + var exportPath = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + if (ClientWrapper.accountAdaptor.hasPassword()) { + passwordDialog.openDialog(PasswordDialog.ExportAccount,exportPath) + return + } else { + if (exportPath.length > 0) { + var isSuccessful = ClientWrapper.accountAdaptor.accoundModel().exportToFile(ClientWrapper.utilsAdaptor.getCurrAccId(), exportPath,"") + var title = isSuccessful ? qsTr("Success") : qsTr("Error") + var iconMode = isSuccessful ? StandardIcon.Information : StandardIcon.Critical + var info = isSuccessful ? qsTr("Export Successful") : qsTr("Export Failed") + msgDialog.openWithParameters(title,info, iconMode, StandardButton.Ok) + } + } + } + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + } + + function exportAccountSlot() { + exportBtn_Dialog.open() + } + + PasswordDialog { + id: passwordDialog + + onDoneSignal: { + var success = (code === successCode) + var title = success ? qsTr("Success") : qsTr("Error") + var iconMode = success ? StandardIcon.Information : StandardIcon.Critical + + var info + switch(currentPurpose){ + case PasswordDialog.ExportAccount: + info = success ? qsTr("Export Successful") : qsTr("Export Failed") + break + case PasswordDialog.ChangePassword: + info = success ? qsTr("Password Changed Successfully") : qsTr("Password Change Failed") + break + case PasswordDialog.SetPassword: + info = success ? qsTr("Password Set Successfully") : qsTr("Password Set Failed") + passwdPushButton.text = success ? qsTr("Change Password") : qsTr("Set Password") + break + } + + msgDialog.openWithParameters(title,info, iconMode, StandardButton.Ok) + } + } + + MessageBox { + id: msgDialog + } + + function passwordClicked() { + if (ClientWrapper.accountAdaptor.hasPassword()){ + passwordDialog.openDialog(PasswordDialog.ChangePassword) + } else { + passwordDialog.openDialog(PasswordDialog.SetPassword) + } + } + + function delAccountSlot() { + deleteAccountDialog.open() + } + + DeleteAccountDialog{ + id: deleteAccountDialog + + anchors.centerIn: parent.Center + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + onAccepted: { + ClientWrapper.accountAdaptor.setSelectedAccountId() + ClientWrapper.accountAdaptor.setSelectedConvId() + + if(ClientWrapper.utilsAdaptor.getAccountListSize() > 0){ + navigateToMainView() + } else { + navigateToNewWizardView() + } + } + } + + NameRegistrationDialog{ + id : nameRegistrationDialog + + onAccepted: { + registeredIdNeedsSet = false + } + } + + function slotRegisterName() { + refreshRelevantUI() + nameRegistrationDialog.openNameRegistrationDialog(registeredName) + } + + LinkDeviceDialog{ + id: linkDeviceDialog + + onAccepted: { + updateAndShowDevicesSlot() + } + } + + function showLinkDevSlot() { + linkDeviceDialog.openLinkDeviceDialog() + } + + RevokeDevicePasswordDialog{ + id: revokeDevicePasswordDialog + + onRevokeDeviceWithPassword:{ + revokeDeviceWithIDAndPassword(idOfDevice, password) + } + } + + MessageBox{ + id: revokeDeviceMessageBox + + property string idOfDev: "" + + title:qsTr("Remove Device") + text :qsTr("Are you sure you wish to remove this device?") + icon :StandardIcon.Information + standardButtons: StandardButton.Ok | StandardButton.Cancel + + onYes: { + accepted() + } + + onNo:{ + rejected() + } + + onDiscard: { + rejected() + } + + onAccepted: { + revokeDeviceWithIDAndPassword(idOfDev,"") + } + + onRejected: {} + } + + function removeDeviceSlot(index){ + var idOfDevice = deviceItemListModel.data(deviceItemListModel.index(index,0), DeviceItemListModel.DeviceID) + if(ClientWrapper.accountAdaptor.hasPassword()){ + revokeDevicePasswordDialog.openRevokeDeviceDialog(idOfDevice) + } else { + revokeDeviceMessageBox.idOfDev = idOfDevice + revokeDeviceMessageBox.open() + } + } + + function revokeDeviceWithIDAndPassword(idDevice, password){ + ClientWrapper.deviceModel.revokeDevice(idDevice, password) + updateAndShowDevicesSlot() + } + + function updateAndShowBannedContactsSlot() { + if(bannedListModel.rowCount() <= 0){ + bannedContactsLayoutWidget.visible = false + return + } + + bannedListModel.reset() + } + + function updateAndShowDevicesSlot() { + if(ClientWrapper.settingsAdaptor.getAccountConfig_Manageruri() === ""){ + linkDevPushButton.visible = true + } + + deviceItemListModel.reset() + } + + DeviceItemListModel { + id: deviceItemListModel + } + + BannedListModel{ + id: bannedListModel + } + + Layout.fillHeight: true + Layout.fillWidth: true + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + + Layout.alignment: Qt.AlignTop + } + + RowLayout { + spacing: 6 + + Layout.alignment: Qt.AlignTop + + Layout.fillWidth: true + Layout.maximumHeight: 30 + Layout.minimumHeight: 30 + Layout.preferredHeight: accountPageTitle.height + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + } + + Label { + id: accountPageTitle + + Layout.preferredWidth: 117 + + Layout.maximumHeight: 25 + Layout.preferredHeight: 25 + Layout.minimumHeight: 25 + + text: qsTr("Jami Account") + + font.pointSize: 15 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + + ScrollView { + id: accoutScrollView + + property ScrollBar hScrollBar: ScrollBar.horizontal + property ScrollBar vScrollBar: ScrollBar.vertical + + Layout.fillHeight: true + Layout.fillWidth: true + + ScrollBar.horizontal.policy: ScrollBar.AsNeeded + ScrollBar.vertical.policy: ScrollBar.AsNeeded + + font.pointSize: 8 + font.kerning: true + clip: true + + ColumnLayout { + id: accoutnViewLayout + + Layout.fillHeight: true + Layout.maximumWidth: 625 + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + } + + ColumnLayout { + spacing: 6 + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.leftMargin: 30 + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 24 + Layout.preferredWidth: 24 + Layout.minimumWidth: 24 + } + + ToggleSwitch { + id: accountEnableCheckBox + + labelText: qsTr("Enable") + fontPointSize: 11 + + onSwitchToggled: { + setAccEnableSlot(checked) + } + } + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + ColumnLayout { + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.maximumHeight: 21 + Layout.preferredHeight: 21 + Layout.minimumHeight: 21 + + text: qsTr("Profile") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.preferredHeight: 10 + Layout.minimumHeight: 10 + } + + ColumnLayout { + Layout.fillWidth: true + layoutDirection: Qt.LeftToRight + + spacing: 6 + + PhotoboothView { + id: currentAccountAvatar + + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + Layout.maximumHeight: 261 + Layout.preferredHeight: 261 + Layout.minimumHeight: 261 + + Layout.leftMargin: 20 + + onImageAcquired: { + ClientWrapper.settingsAdaptor.setCurrAccAvatar(imgBase64) + } + + onImageCleared: { + ClientWrapper.settingsAdaptor.clearCurrentAvatar() + setAvatar() + } + } + + InfoLineEdit { + id: displayNameLineEdit + + fieldLayoutWidth: 261 + + Layout.leftMargin: 20 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.accountAdaptor.setCurrAccDisplayName( + displayNameLineEdit.text) + } + } + } + } + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + ColumnLayout { + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.maximumHeight: 21 + Layout.preferredHeight: 21 + Layout.minimumHeight: 21 + + text: qsTr("Identity") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 10 + Layout.preferredWidth: 10 + Layout.minimumWidth: 10 + } + + ColumnLayout { + spacing: 7 + + Layout.fillWidth: true + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Layout.leftMargin: 20 + + Layout.maximumWidth: 625 + + Label { + Layout.maximumWidth: 13 + Layout.preferredWidth: 13 + Layout.minimumWidth: 13 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Id") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + TextField { + id: currentRingID + + property var backgroundColor: "transparent" + property var borderColor: "transparent" + + Layout.fillWidth: true + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 10 + font.kerning: true + font.bold: true + + readOnly: true + selectByMouse: true + + text: { refreshVariable + return ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Uri()} + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + background: Rectangle { + anchors.fill: parent + radius: 0 + border.color: currentRingID.borderColor + border.width: 0 + color: currentRingID.backgroundColor + } + } + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.maximumHeight: 32 + + Layout.leftMargin: 20 + + layoutDirection: Qt.LeftToRight + + Label { + id: lblRegisteredName + + Layout.maximumWidth: 127 + Layout.preferredWidth: 127 + Layout.minimumWidth: 127 + + Layout.minimumHeight: 32 + Layout.preferredHeight: 32 + Layout.maximumHeight: 32 + + text: qsTr("Registered name") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.maximumHeight: 30 + Layout.alignment: Qt.AlignVCenter + + TextField { + id: currentRegisteredID + + Layout.maximumWidth: 300 + Layout.preferredWidth: 300 + Layout.minimumWidth: 300 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + placeholderText: { refreshVariable + var result = registeredIdNeedsSet ? qsTr("Type here to register a username") : "" + return result} + + text: { + refreshVariable + if (!registeredIdNeedsSet){ + return ClientWrapper.settingsAdaptor.get_CurrentAccountInfo_RegisteredName() + } else { + return "" + } + } + selectByMouse: true + readOnly: { refreshVariable + return !registeredIdNeedsSet} + + font.pointSize: 10 + font.kerning: true + font.bold: { refreshVariable + return !registeredIdNeedsSet} + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + background: Rectangle { + anchors.fill: parent + radius: {refreshVariable + var result = registeredIdNeedsSet ? height / 2 : 0 + return result} + border.color: "transparent" + border.width: {refreshVariable + var result = registeredIdNeedsSet ? 2 : 0 + return result} + color: {refreshVariable + var result = registeredIdNeedsSet ? Qt.rgba( + 240 / 256, 240 / 256, + 240 / 256, + 1.0) : "transparent" + return result} + } + + onTextEdited: { + verifyRegisteredNameSlot() + } + + onEditingFinished: { + verifyRegisteredNameSlot() + } + } + + LookupStatusLabel { + id: lookupStatusLabel + + visible:{refreshVariable + var result = registeredIdNeedsSet + && (regNameUi + !== CurrentAccountSettingsScrollPage.BLANK) + return result} + + MouseArea { + id: lookupStatusLabelArea + anchors.fill: parent + property bool isHovering: false + + onEntered: isHovering = true + onExited: isHovering = false + + hoverEnabled: true + } + + ToolTip.visible: lookupStatusLabelArea.isHovering + ToolTip.text: { + switch (regNameUi) { + case CurrentAccountSettingsScrollPage.BLANK: + return qsTr("") + case CurrentAccountSettingsScrollPage.INVALIDFORM: + return qsTr("A registered name should not have any spaces and must be at least three letters long") + case CurrentAccountSettingsScrollPage.TAKEN: + return qsTr("This name is already taken") + case CurrentAccountSettingsScrollPage.FREE: + return qsTr("Register this name") + case CurrentAccountSettingsScrollPage.SEARCHING: + return qsTr("") + default: + return qsTr("") + } + } + + lookupStatusState: { + switch (regNameUi) { + case CurrentAccountSettingsScrollPage.BLANK: + return "Blank" + case CurrentAccountSettingsScrollPage.INVALIDFORM: + return "Invalid" + case CurrentAccountSettingsScrollPage.TAKEN: + return "Taken" + case CurrentAccountSettingsScrollPage.FREE: + return "Free" + case CurrentAccountSettingsScrollPage.SEARCHING: + return "Searching" + default: + return "Blank" + } + } + } + + HoverableRadiusButton { + id: btnRegisterName + + visible: {refreshVariable + var result = registeredIdNeedsSet + && (regNameUi + === CurrentAccountSettingsScrollPage.FREE) + return result} + + Layout.maximumWidth: 80 + Layout.preferredWidth: 80 + Layout.minimumWidth: 80 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Register") + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Layout.leftMargin: 20 + + HoverableButtonTextItem { + id: passwdPushButton + + visible: ClientWrapper.settingsAdaptor.getAccountConfig_Manageruri() === "" + + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + text: ClientWrapper.accountAdaptor.hasPassword() ? qsTr("Change Password") : qsTr("Set Password") + + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + + onClicked: { + passwordClicked() + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Layout.leftMargin: 20 + + HoverableButtonTextItem { + id: btnExportAccount + + visible: ClientWrapper.settingsAdaptor.getAccountConfig_Manageruri() === "" + + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Export Account") + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + + onClicked: { + exportAccountSlot() + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Layout.leftMargin: 20 + + HoverableButtonTextItem { + id: btnDeletAccount + + backgroundColor: "red" + onEnterColor: Qt.rgba(150 / 256, 0, 0, 0.7) + onDisabledBackgroundColor: Qt.rgba( + 255 / 256, + 0, 0, 0.8) + onPressColor: backgroundColor + textColor: "white" + + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Delete Account") + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + + onClicked: { + delAccountSlot() + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + ColumnLayout { + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.maximumHeight: 27 + Layout.preferredHeight: 27 + Layout.minimumHeight: 27 + + text: qsTr("Linked Device") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 10 + Layout.preferredWidth: 10 + Layout.minimumWidth: 10 + } + + ColumnLayout { + spacing: 7 + + Layout.fillWidth: true + + ListViewJami { + id: settingsListView + + Layout.leftMargin: 20 + + Layout.fillWidth: true + + Layout.minimumWidth: 580 + Layout.preferredWidth: 605 + + Layout.minimumHeight: 164 + Layout.preferredHeight: 164 + Layout.maximumHeight: 164 + + model: deviceItemListModel + + delegate: DeviceItemDelegate{ + id: settingsListDelegate + + width: settingsListView.width + height: 85 + + deviceName : DeviceName + deviceId: DeviceID + isCurrent: IsCurrent + + onClicked: { + settingsListView.currentIndex = index + } + + onBtnRemoveDeviceClicked:{ + removeDeviceSlot(index) + } + } + } + + HoverableRadiusButton { + id: linkDevPushButton + + visible: ClientWrapper.settingsAdaptor.getAccountConfig_Manageruri() === "" + + Layout.leftMargin: 20 + + Layout.fillWidth: true + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + radius: height / 2 + + text: qsTr("+Link Another Device") + font.pointSize: 10 + font.kerning: true + + onClicked: { + showLinkDevSlot() + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + // banned list view + ColumnLayout { + id: bannedContactsLayoutWidget + + Layout.fillWidth: true + spacing: 6 + + RowLayout { + Layout.leftMargin: 9 + Layout.rightMargin: 8 + Layout.topMargin: 1 + + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Label { + Layout.preferredWidth: 164 + Layout.minimumWidth: 164 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Banned Contact") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 10 + Layout.preferredWidth: 10 + Layout.minimumWidth: 10 + } + + HoverableRadiusButton { + id: bannedContactsBtn + + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + + radius: height / 2 + + icon.source: bannedContactsListWidget.visible? "qrc:/images/icons/round-arrow_drop_up-24px.svg" : "qrc:/images/icons/round-arrow_drop_down-24px.svg" + icon.height: 32 + icon.width: 32 + + onClicked: { + toggleBannedContacts() + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + + ColumnLayout { + id: bannedContactsListWidget + + spacing: 6 + + Layout.leftMargin: 9 + Layout.rightMargin: 8 + Layout.bottomMargin: 9 + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ListViewJami { + id: bannedListWidget + + Layout.leftMargin: 20 + Layout.fillWidth: true + + Layout.minimumWidth: 580 + + Layout.minimumHeight: 150 + Layout.preferredHeight: 150 + Layout.maximumHeight: 150 + + model: bannedListModel + + delegate: BannedItemDelegate{ + id: bannedListDelegate + + width: bannedListWidget.width + height: 74 + + contactName : ContactName + contactID: ContactID + contactPicture_base64: ContactPicture + + onClicked: { + bannedListWidget.currentIndex = index + } + + onBtnReAddContactClicked: { + unban(index) + } + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + RowLayout { + Layout.fillWidth: true + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + HoverableRadiusButton { + id: advancedAccountSettingsPButton + + Layout.minimumWidth: 180 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + text: qsTr("Advanced Account Settings") + font.pointSize: 10 + font.kerning: true + + icon.source: { + if (advanceSettingsView.visible) { + return "qrc:/images/icons/round-arrow_drop_up-24px.svg" + } else { + return "qrc:/images/icons/round-arrow_drop_down-24px.svg" + } + } + + icon.height: 24 + icon.width: 24 + + onClicked: { + advanceSettingsView.visible = !advanceSettingsView.visible + if (advanceSettingsView.visible) { + advanceSettingsView.updateAccountInfoDisplayedAdvance() + var mappedCoor = advancedAccountSettingsPButton.mapToItem(accoutnViewLayout,advancedAccountSettingsPButton.x,advancedAccountSettingsPButton.y) + accoutScrollView.vScrollBar.position = mappedCoor.y / accoutnViewLayout.height + } else { + accoutScrollView.vScrollBar.position = 0 + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 48 + Layout.preferredHeight: 48 + Layout.maximumHeight: 48 + } + + ColumnLayout { + spacing: 6 + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.leftMargin: 30 + + // instantiate advance setting page + AdvancedSettingsView { + id: advanceSettingsView + + Layout.leftMargin: 10 + visible: false + } + } + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } +} diff --git a/src/settingsview/components/CurrentSIPAccountSettingScrollPage.qml b/src/settingsview/components/CurrentSIPAccountSettingScrollPage.qml new file mode 100644 index 00000000..b5d1d2c0 --- /dev/null +++ b/src/settingsview/components/CurrentSIPAccountSettingScrollPage.qml @@ -0,0 +1,633 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Rectangle { + signal navigateToMainView + signal navigateToNewWizardView + + function updateAccountInfoDisplayed() { + displaySIPNameLineEdit.text = ClientWrapper.settingsAdaptor.getCurrentAccount_Profile_Info_Alias() + usernameSIP.text = ClientWrapper.settingsAdaptor.getAccountConfig_Username() + hostnameSIP.text = ClientWrapper.settingsAdaptor.getAccountConfig_Hostname() + passSIPlineEdit.text = ClientWrapper.settingsAdaptor.getAccountConfig_Password() + proxySIP.text = ClientWrapper.settingsAdaptor.getAccountConfig_ProxyServer() + + accountSIPEnableCheckBox.checked = ClientWrapper.settingsAdaptor.get_CurrentAccountInfo_Enabled() + + setAvatar() + + if (advanceSIPSettingsView.visible) { + advanceSIPSettingsView.updateAccountInfoDisplayedAdvanceSIP() + } + } + + function isPhotoBoothOpened() { + return currentSIPAccountAvatar.takePhotoState + } + + function setAvatar() { + currentSIPAccountAvatar.setAvatarPixmap( + ClientWrapper.settingsAdaptor.getAvatarImage_Base64( + currentSIPAccountAvatar.boothWidht), + ClientWrapper.settingsAdaptor.getIsDefaultAvatar()) + } + + function stopBooth() { + currentSIPAccountAvatar.stopBooth() + } + + // slots + function setAccEnableSlot(state) { + ClientWrapper.accountModel.setAccountEnabled(ClientWrapper.utilsAdaptor.getCurrAccId(), state) + } + + function delAccountSlot() { + deleteAccountDialog_SIP.open() + } + + DeleteAccountDialog{ + id: deleteAccountDialog_SIP + + anchors.centerIn: parent.Center + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + onAccepted: { + ClientWrapper.accountAdaptor.setSelectedAccountId() + ClientWrapper.accountAdaptor.setSelectedConvId() + + if(ClientWrapper.utilsAdaptor.getAccountListSize() > 0){ + navigateToMainView() + } else { + navigateToNewWizardView() + } + } + } + + Layout.fillHeight: true + Layout.fillWidth: true + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + + Layout.alignment: Qt.AlignTop + } + + RowLayout { + spacing: 6 + + Layout.alignment: Qt.AlignTop + + Layout.fillWidth: true + Layout.maximumHeight: 31 + Layout.minimumHeight: 0 + Layout.preferredHeight: accountPageTitleSIP.height + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 48 + Layout.preferredWidth: 48 + Layout.minimumWidth: 48 + } + + Label { + id: accountPageTitleSIP + + Layout.preferredWidth: 133 + + Layout.preferredHeight: 31 + Layout.minimumHeight: 25 + + text: qsTr("SIP Account") + + font.pointSize: 15 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + + ScrollView { + id: accountSIPScrollView + + property ScrollBar hScrollBar: ScrollBar.horizontal + property ScrollBar vScrollBar: ScrollBar.vertical + + Layout.fillHeight: true + Layout.fillWidth: true + + ScrollBar.horizontal.policy: ScrollBar.AsNeeded + ScrollBar.vertical.policy: ScrollBar.AsNeeded + + font.pointSize: 8 + font.kerning: true + clip: true + + ColumnLayout { + id: accountSIPLayout + + Layout.fillHeight: true + Layout.maximumWidth: 598 + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + } + + ColumnLayout { + spacing: 6 + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.leftMargin: 48 + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 24 + Layout.preferredWidth: 24 + Layout.minimumWidth: 24 + } + + ToggleSwitch { + id: accountSIPEnableCheckBox + + labelText: qsTr("Enable") + fontPointSize: 10 + + onSwitchToggled: { + setAccEnableSlot(checked) + } + } + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + ColumnLayout { + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + + Layout.maximumHeight: 21 + Layout.preferredHeight: 21 + Layout.minimumHeight: 21 + + text: qsTr("Profile") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.preferredHeight: 10 + Layout.minimumHeight: 10 + } + + ColumnLayout { + Layout.fillWidth: true + layoutDirection: Qt.LeftToRight + + spacing: 6 + + PhotoboothView { + id: currentSIPAccountAvatar + + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + Layout.maximumHeight: 261 + Layout.preferredHeight: 261 + Layout.minimumHeight: 261 + + Layout.leftMargin: 20 + + onImageAcquired: { + ClientWrapper.settingsAdaptor.setCurrAccAvatar(imgBase64) + } + + onImageCleared: { + ClientWrapper.settingsAdaptor.clearCurrentAvatar() + setAvatar() + } + } + + InfoLineEdit { + id: displaySIPNameLineEdit + + fieldLayoutWidth: 261 + + Layout.leftMargin: 20 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.accountAdaptor.setCurrAccDisplayName( + displaySIPNameLineEdit.text) + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 6 + + Label { + Layout.fillWidth: true + + Layout.maximumHeight: 27 + Layout.preferredHeight: 27 + Layout.minimumHeight: 27 + + text: qsTr("Identity") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.preferredHeight: 10 + Layout.minimumHeight: 10 + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 6 + + GridLayout { + rows: 4 + columns: 2 + flow: GridLayout.LeftToRight + rowSpacing: 14 + columnSpacing: 6 + + Layout.fillWidth: true + + Layout.leftMargin: 20 + + // user name + Label { + Layout.maximumWidth: 76 + Layout.preferredWidth: 76 + Layout.minimumWidth: 76 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Username") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + InfoLineEdit { + id: usernameSIP + + fieldLayoutWidth: 300 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setAccountConfig_Username( + usernameSIP.text) + } + } + + // host name + Label { + Layout.maximumWidth: 76 + Layout.preferredWidth: 76 + Layout.minimumWidth: 76 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Hostname") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + InfoLineEdit { + id: hostnameSIP + + fieldLayoutWidth: 300 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setAccountConfig_Hostname( + hostnameSIP.text) + } + } + + // proxy + Label { + Layout.maximumWidth: 76 + Layout.preferredWidth: 76 + Layout.minimumWidth: 76 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Proxy") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + InfoLineEdit { + id: proxySIP + + fieldLayoutWidth: 300 + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setAccountConfig_ProxyServer( + proxySIP.text) + } + } + + // password + Label { + Layout.maximumWidth: 76 + Layout.preferredWidth: 76 + Layout.minimumWidth: 76 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Password") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + InfoLineEdit { + id: passSIPlineEdit + + fieldLayoutWidth: 300 + + font.pointSize: 10 + font.kerning: true + + echoMode: TextInput.Password + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + onEditingFinished: { + ClientWrapper.settingsAdaptor.setAccountConfig_Password( + passSIPlineEdit.text) + } + } + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 10 + Layout.preferredHeight: 10 + Layout.minimumHeight: 10 + } + + RowLayout { + Layout.fillWidth: true + Layout.maximumHeight: 30 + Layout.leftMargin: 20 + + HoverableButtonTextItem { + id: btnSIPDeletAccount + + backgroundColor: "red" + onEnterColor: Qt.rgba(150 / 256, 0, 0, 0.7) + onDisabledBackgroundColor: Qt.rgba( + 255 / 256, + 0, 0, 0.8) + onPressColor: backgroundColor + textColor: "white" + + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + radius: height / 2 + + text: qsTr("Delete Account") + font.pointSize: 10 + font.kerning: true + + onClicked: { + delAccountSlot() + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } + } + + Item { + Layout.fillWidth: true + + Layout.maximumHeight: 40 + Layout.preferredHeight: 40 + Layout.minimumHeight: 40 + } + + RowLayout { + Layout.fillWidth: true + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + Layout.minimumWidth: 598 + Layout.preferredWidth: 598 + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + HoverableRadiusButton { + id: advancedAccountSettingsSIPButton + + Layout.minimumWidth: 180 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + text: qsTr("Advanced Account Settings") + font.pointSize: 10 + font.kerning: true + + icon.source: { + if (advanceSIPSettingsView.visible) { + return "qrc:/images/icons/round-arrow_drop_up-24px.svg" + } else { + return "qrc:/images/icons/round-arrow_drop_down-24px.svg" + } + } + + icon.height: 24 + icon.width: 24 + + onClicked: { + advanceSIPSettingsView.visible = !advanceSIPSettingsView.visible + if(advanceSIPSettingsView.visible){ + advanceSIPSettingsView.updateAccountInfoDisplayedAdvanceSIP() + var coor = advancedAccountSettingsSIPButton.mapToItem(accountSIPLayout,advancedAccountSettingsSIPButton.x,advancedAccountSettingsSIPButton.y) + accountSIPScrollView.vScrollBar.position = coor.y / accountSIPLayout.height + } else { + accountSIPScrollView.vScrollBar.position = 0 + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 48 + Layout.preferredHeight: 48 + Layout.maximumHeight: 48 + } + + ColumnLayout { + spacing: 6 + Layout.fillHeight: true + Layout.fillWidth: true + + Layout.leftMargin: 30 + + // instantiate advance setting page + AdvancedSIPSettingsView { + id: advanceSIPSettingsView + + Layout.leftMargin: 10 + visible: false + } + } + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } +} diff --git a/src/settingsview/components/DeviceItemDelegate.qml b/src/settingsview/components/DeviceItemDelegate.qml new file mode 100644 index 00000000..f2502610 --- /dev/null +++ b/src/settingsview/components/DeviceItemDelegate.qml @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +ItemDelegate { + id: deviceItemDelegate + + property string deviceName : "" + property string deviceId: "" + property bool isCurrent : false + + property bool editable : false + + signal btnRemoveDeviceClicked + + function btnEditDeviceEnter(){ + btnEditDevice.enterBtn() + } + + function btnEditDeviceExit(){ + btnEditDevice.exitBtn() + } + + function btnEditPress(){ + btnEditDevice.pressBtn() + } + + function btnEditRelease(){ + btnEditDevice.releaseBtn() + } + + function toggleEditable(){ + editable = !editable + if(editable){ + ClientWrapper.settingsAdaptor.setDeviceName(editDeviceName.text) + } + } + + highlighted: ListView.isCurrentItem + + RowLayout{ + anchors.fill: parent + + spacing: 7 + + Label{ + Layout.leftMargin: 7 + Layout.topMargin: 7 + Layout.bottomMargin: 7 + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + background: Rectangle{ + anchors.fill: parent + Image { + anchors.fill: parent + source: "qrc:/images/icons/baseline-desktop_windows-24px.svg" + } + } + } + + ColumnLayout{ + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.topMargin: 7 + Layout.bottomMargin: 7 + + InfoLineEdit{ + id: editDeviceName + + Layout.fillWidth: true + Layout.minimumWidth: 0 + Layout.maximumWidth: 16777215 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + font.pointSize: 8 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + readOnly: !editable + + text: deviceName + } + + RowLayout{ + Layout.maximumWidth: editDeviceName.fieldLayoutWidth + + Layout.minimumHeight: 30 + + Label{ + id: labelDeviceId + + //Layout.minimumWidth: 71 + Layout.minimumHeight: 30 + + font.pointSize: 8 + font.kerning: true + text: deviceId === "" ? qsTr("Device Id") : deviceId + } + + Item{ + Layout.fillWidth: true + + Layout.minimumWidth: 0 + Layout.minimumHeight: 20 + } + + Label{ + id: labelThisDevice + + //Layout.minimumWidth: 80 + Layout.minimumHeight: 30 + + visible: isCurrent + + font.pointSize: 8 + font.kerning: true + font.italic: true + color: "green" + text: qsTr("this device") + } + } + } + + HoverableRadiusButton{ + id: btnEditDevice + + Layout.topMargin: 7 + Layout.bottomMargin: 7 + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + + source:{ + if(isCurrent) { + var path = editable ? "qrc:/images/icons/round-edit-24px.svg" : "qrc:/images/icons/round-save_alt-24px.svg" + return path + } else { + return "qrc:/images/icons/round-remove_circle-24px.svg" + } + } + + ToolTip.visible: isHovering + ToolTip.text: { + if(isCurrent) { + if(editable){ + return qsTr("Edit Device Name") + } else { + return qsTr("Save new device name") + } + } else { + return qsTr("Unlink Device From Account") + } + } + + onClicked: { + if(isCurrent) { + toggleEditable() + } else { + btnRemoveDeviceClicked() + } + } + } + + Item{ + Layout.rightMargin: 7 + + Layout.minimumWidth: 8 + Layout.preferredWidth: 8 + Layout.maximumWidth: 8 + + Layout.minimumHeight: 20 + } + } +} diff --git a/src/settingsview/components/GeneralSettingsPage.qml b/src/settingsview/components/GeneralSettingsPage.qml new file mode 100644 index 00000000..72eb4d19 --- /dev/null +++ b/src/settingsview/components/GeneralSettingsPage.qml @@ -0,0 +1,678 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import Qt.labs.platform 1.1 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 +import "../../commoncomponents" + +Rectangle { + id: generalSettingsRect + + function populateGeneralSettings(){ + // settings + closeOrMinCheckBox.checked = ClientWrapper.settingsAdaptor.getSettingsValue_CloseOrMinimized() + applicationOnStartUpCheckBox.checked = ClientWrapper.utilsAdaptor.checkStartupLink() + notificationCheckBox.checked = ClientWrapper.settingsAdaptor.getSettingsValue_EnableNotifications() + + alwaysRecordingCheckBox.checked = ClientWrapper.avmodel.getAlwaysRecord() + recordPreviewCheckBox.checked = ClientWrapper.avmodel.getRecordPreview() + recordQualityValueLabel.text = ClientWrapper.utilsAdaptor.getRecordQualityString(ClientWrapper.avmodel.getRecordQuality() / 100) + recordQualitySlider.value = ClientWrapper.avmodel.getRecordQuality() / 100 + + ClientWrapper.avmodel.setRecordPath(ClientWrapper.settingsAdaptor.getDir_Document()) + + autoUpdateCheckBox.checked = ClientWrapper.settingsAdaptor.getSettingsValue_AutoUpdate() + } + + function slotSetNotifications(state){ + ClientWrapper.settingsAdaptor.setNotifications(state) + } + + function slotSetClosedOrMin(state){ + ClientWrapper.settingsAdaptor.setClosedOrMin(state) + } + + function slotSetRunOnStartUp(state){ + ClientWrapper.settingsAdaptor.setRunOnStartUp(state) + } + + function slotSetUpdateAutomatic(state){ + ClientWrapper.settingsAdaptor.setUpdateAutomatic(state) + } + + function slotAlwaysRecordingClicked(state){ + ClientWrapper.avmodel.setAlwaysRecord(state) + } + + function slotRecordPreviewClicked(state){ + ClientWrapper.avmodel.setRecordPreview(state) + } + + function slotRecordQualitySliderValueChanged(value){ + recordQualityValueLabel.text = ClientWrapper.utilsAdaptor.getRecordQualityString(value) + updateRecordQualityTimer.restart() + } + + Timer{ + id: updateRecordQualityTimer + + interval: 500 + + onTriggered: { + slotRecordQualitySliderSliderReleased() + } + } + + function slotRecordQualitySliderSliderReleased(){ + var value = recordQualitySlider.value + ClientWrapper.avmodel.setRecordQuality(value * 100) + } + + function openDownloadFolderSlot(){ + downloadPathDialog.open() + } + + FolderDialog { + id: downloadPathDialog + + title: qsTr("Select A Folder For Your Downloads") + currentFolder: StandardPaths.writableLocation(StandardPaths.DownloadLocation) + + onAccepted: { + var dir = ClientWrapper.utilsAdaptor.getAbsPath(folder.toString()) + downloadPath = dir + } + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + } + + function openRecordFolderSlot(){ + recordPathDialog.open() + } + + FolderDialog { + id: recordPathDialog + + title: qsTr("Select A Folder For Your Recordings") + currentFolder: StandardPaths.writableLocation(StandardPaths.HomeLocation) + + onAccepted: { + var dir = ClientWrapper.utilsAdaptor.getAbsPath(folder.toString()) + recordPath = dir + } + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + } + + //TODO: complete check for update and check for Beta slot functions + function checkForUpdateSlot(){} + function installBetaSlot(){} + + // settings + property string downloadPath: ClientWrapper.settingsAdaptor.getDir_Download() + + // recording + //property AVModel avmodel: ClientWrapper.accountAdaptor.avModel() + property string recordPath: ClientWrapper.settingsAdaptor.getDir_Document() + + onDownloadPathChanged: { + if(downloadPath === "") return + ClientWrapper.settingsAdaptor.setDownloadPath(downloadPath) + } + + onRecordPathChanged: { + if(recordPath === "") return + + if(ClientWrapper.avmodel){ + ClientWrapper.avmodel.setRecordPath(recordPath) + } + } + + Layout.fillHeight: true + Layout.fillWidth: true + + ScrollView{ + anchors.fill: parent + clip: true + + RowLayout { + width: generalSettingsRect.width + height: generalSettingsRect.height + + spacing: 0 + + Item { + Layout.fillHeight: true + Layout.maximumWidth: 48 + Layout.preferredWidth: 48 + Layout.minimumWidth: 48 + } + + ColumnLayout { + spacing: 6 + + Layout.fillHeight: true + Layout.maximumWidth: 580 + Layout.preferredWidth: 580 + Layout.minimumWidth: 580 + + Item { + Layout.fillWidth: true + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + Label { + Layout.fillWidth: true + Layout.minimumHeight: 25 + Layout.preferredHeight: 25 + Layout.maximumHeight: 25 + + text: qsTr("General") + font.pointSize: 15 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.minimumHeight: 24 + Layout.preferredHeight: 24 + Layout.maximumHeight: 24 + } + + // system setting panel + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + Layout.minimumHeight: 21 + Layout.preferredHeight: 21 + Layout.maximumHeight: 21 + + text: qsTr("System") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + ToggleSwitch { + id: notificationCheckBox + + Layout.leftMargin: 20 + + labelText: "Enable desktop notifications" + fontPointSize: 11 + + onSwitchToggled: { + slotSetNotifications(checked) + } + } + + ToggleSwitch { + id: closeOrMinCheckBox + + Layout.leftMargin: 20 + + labelText: "Keep minimize on close" + fontPointSize: 11 + + onSwitchToggled: { + slotSetClosedOrMin(checked) + } + } + + ToggleSwitch { + id: applicationOnStartUpCheckBox + + Layout.leftMargin: 20 + + labelText: "Run on Startup" + fontPointSize: 11 + + onSwitchToggled: { + slotSetRunOnStartUp(checked) + } + } + + RowLayout { + spacing: 6 + + Layout.leftMargin: 20 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Label { + Layout.fillHeight: true + + Layout.maximumWidth: 94 + Layout.preferredWidth: 94 + Layout.minimumWidth: 94 + + text: qsTr("Download folder") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + HoverableRadiusButton { + id: downloadButton + + Layout.maximumWidth: 320 + Layout.preferredWidth: 320 + Layout.minimumWidth: 320 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.height: 24 + icon.width: 24 + + text: downloadPath + fontPointSize: 10 + + onClicked: { + openDownloadFolderSlot() + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + // call recording setting panel + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + Label { + Layout.fillWidth: true + Layout.minimumHeight: 21 + Layout.preferredHeight: 21 + Layout.maximumHeight: 21 + + text: qsTr("Call Recording") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + ToggleSwitch { + id: alwaysRecordingCheckBox + + Layout.leftMargin: 20 + + labelText: "Always record calls" + fontPointSize: 11 + + onSwitchToggled: { + slotAlwaysRecordingClicked(checked) + } + } + + ToggleSwitch { + id: recordPreviewCheckBox + + Layout.leftMargin: 20 + + labelText: "Record preview video for a call" + fontPointSize: 11 + + onSwitchToggled: { + slotRecordPreviewClicked(checked) + } + } + + RowLayout { + spacing: 6 + Layout.leftMargin: 20 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Label { + Layout.fillHeight: true + + Layout.maximumWidth: 42 + Layout.preferredWidth: 42 + Layout.minimumWidth: 42 + + text: qsTr("Quality") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + ColumnLayout { + spacing: 0 + Layout.fillHeight: true + + Layout.maximumWidth: recordQualityValueLabel.width + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + Label { + id: recordQualityValueLabel + + Layout.minimumWidth: 40 + + Layout.minimumHeight: 16 + Layout.preferredHeight: 16 + Layout.maximumHeight: 16 + + text: qsTr("VALUE ") + + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + + Slider{ + id: recordQualitySlider + + Layout.fillHeight: true + + Layout.maximumWidth: 320 + Layout.preferredWidth: 320 + Layout.minimumWidth: 320 + + from: 0 + to: 500 + stepSize: 1 + + onMoved: { + slotRecordQualitySliderValueChanged(value) + } + } + } + + RowLayout { + spacing: 6 + + Layout.leftMargin: 20 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Label { + Layout.fillHeight: true + + Layout.maximumWidth: 42 + Layout.preferredWidth: 42 + Layout.minimumWidth: 42 + + text: qsTr("Save in") + font.pointSize: 10 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + HoverableRadiusButton { + id: recordPathButton + + Layout.maximumWidth: 320 + Layout.preferredWidth: 320 + Layout.minimumWidth: 320 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + icon.source: "qrc:/images/icons/round-folder-24px.svg" + icon.height: 24 + icon.width: 24 + + text: recordPath + fontPointSize: 10 + + onClicked: { + openRecordFolderSlot() + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.minimumHeight: 20 + Layout.preferredHeight: 20 + Layout.maximumHeight: 20 + } + + // update setting panel + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + visible: Qt.platform.os == "windows"? true : false + + Label { + Layout.fillWidth: true + Layout.minimumHeight: 21 + Layout.preferredHeight: 21 + Layout.maximumHeight: 21 + + text: qsTr("Updates") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + + Layout.minimumHeight: 10 + Layout.preferredHeight: 10 + Layout.maximumHeight: 10 + } + + ColumnLayout { + spacing: 6 + Layout.fillWidth: true + + ToggleSwitch { + id: autoUpdateCheckBox + + Layout.leftMargin: 20 + + labelText: "Check for updates automatically" + fontPointSize: 11 + + onSwitchToggled: { + slotSetUpdateAutomatic(checked) + } + } + + RowLayout { + spacing: 6 + + Layout.leftMargin: 20 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + HoverableRadiusButton { + id: checkUpdateButton + + Layout.maximumWidth: 275 + Layout.preferredWidth: 275 + Layout.minimumWidth: 275 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + text: "Check for updates now" + fontPointSize: 10 + + onClicked: { + checkForUpdateSlot() + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + + RowLayout { + spacing: 6 + + Layout.leftMargin: 20 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + HoverableRadiusButton { + id: installBetaButton + + Layout.maximumWidth: 275 + Layout.preferredWidth: 275 + Layout.minimumWidth: 275 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + text: "Install the latest beta version" + fontPointSize: 10 + + onClicked: { + installBetaSlot() + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } + } + + // spacer on the bottom + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } +} diff --git a/src/settingsview/components/IconButton.qml b/src/settingsview/components/IconButton.qml new file mode 100644 index 00000000..7e992a9e --- /dev/null +++ b/src/settingsview/components/IconButton.qml @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Universal 2.12 +import QtGraphicalEffects 1.14 + +import "../../constant" + +Button { + id: button + checkable: true + hoverEnabled: true + + property alias imageSource: buttonPix.source + property alias buttonText: buttonText.text + + property string backgroundColor: JamiTheme.releaseColor + property string onPressColor: JamiTheme.pressColor + property string onReleaseColor: JamiTheme.releaseColor + property string onEnterColor: JamiTheme.hoverColor + property string onExitColor: JamiTheme.transparentColor + property string checkedColor: JamiTheme.releaseColor + + signal checkedToggledForLeftPanel(var checked) + signal checkedToggledForRightPanel(var checked) + + function setCheckedState(check, triggerSignal) { + button.checked = check + if (triggerSignal) { + checkedToggledForLeftPanel(check) + checkedToggledForRightPanel(check) + } + button.background.color = check ? button.checkedColor : button.onExitColor + } + + onClicked: { + setCheckedState(true, true) + } + + Layout.minimumHeight: 60 + Layout.preferredHeight: 60 + Layout.maximumHeight: 60 + + Layout.fillWidth: true + + background: Rectangle { + anchors.fill: parent + color: parent.checked ? button.checkedColor : button.onExitColor + + RowLayout { + anchors.fill: parent + spacing: 24 + Image { + id: buttonPix + Layout.minimumHeight: 24 + Layout.preferredHeight: 24 + Layout.maximumHeight: 24 + + Layout.minimumWidth: 24 + Layout.preferredWidth: 24 + Layout.maximumWidth: 24 + + Layout.leftMargin: 24 + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + } + + Label { + id: buttonText + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + + Layout.fillHeight: true + Layout.fillWidth: true + + font.pointSize: 11 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + } + + MouseArea { + anchors.fill: parent + + hoverEnabled: true + + onPressed: { + if (!button.checked) { + parent.color = button.onPressColor + } + } + onReleased: { + button.clicked() + if (!button.checked) { + parent.color = button.onExitColor + } + } + onEntered: { + if (!button.checked) { + parent.color = button.onEnterColor + } + } + onExited: { + if (!button.checked) { + parent.color = button.onExitColor + } + } + } + } +} diff --git a/src/settingsview/components/LeftPanelView.qml b/src/settingsview/components/LeftPanelView.qml new file mode 100644 index 00000000..f11fe237 --- /dev/null +++ b/src/settingsview/components/LeftPanelView.qml @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 1.4 as CT +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 + +import "../../commoncomponents" + +ScrollView{ + id: leftPanelView + + property int contentViewportWidth: 200 + property int contentViewPortHeight: 768 + + property alias btnAccountSettings: accountSettingsButton + property alias btnGeneralSettings: generalSettingsButton + property alias btnMediaSettings: mediaSettingsButton + property alias btnPluginSettings: pluginSettingsButton + + signal btnExitClicked + + Component.onCompleted: { + accountSettingsButton.setCheckedState(true, true) + } + + anchors.fill: parent + clip: true + + ColumnLayout { + spacing: 0 + + width: contentViewportWidth + height: contentViewPortHeight + + Item { + Layout.fillWidth: true + Layout.maximumHeight: 13 + Layout.preferredHeight: 13 + Layout.minimumHeight: 13 + } + + RowLayout { + Layout.fillWidth: true + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + + Layout.rightMargin: 14 + + Item { + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + Label { + Layout.maximumWidth: 57 + Layout.preferredWidth: 57 + Layout.minimumWidth: 57 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Settings") + font.pointSize: 12 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + HoverableRadiusButton { + id: btnExitSettings + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + backgroundColor: "transparent" + + radius: height / 2 + + icon.source: "qrc:/images/icons/round-close-24px.svg" + icon.height: 24 + icon.width: 24 + + onClicked: { + btnExitClicked() + } + } + } + Item { + Layout.fillWidth: true + Layout.maximumHeight: 13 + Layout.preferredHeight: 13 + Layout.minimumHeight: 13 + } + + IconButton { + id: accountSettingsButton + + buttonText: qsTr("Account") + imageSource: "qrc:/images/icons/baseline-people-24px.svg" + + onCheckedToggledForLeftPanel: { + generalSettingsButton.setCheckedState(!checked, false) + mediaSettingsButton.setCheckedState(!checked, false) + pluginSettingsButton.setCheckedState(!checked, false) + } + } + + IconButton { + id: generalSettingsButton + + buttonText: qsTr("General") + imageSource: "qrc:/images/icons/round-settings-24px.svg" + + onCheckedToggledForLeftPanel: { + accountSettingsButton.setCheckedState(!checked, false) + mediaSettingsButton.setCheckedState(!checked, false) + pluginSettingsButton.setCheckedState(!checked, false) + } + } + + IconButton { + id: mediaSettingsButton + + buttonText: qsTr("Audio/Video") + imageSource: "qrc:/images/icons/baseline-desktop_windows-24px.svg" + + onCheckedToggledForLeftPanel: { + generalSettingsButton.setCheckedState(!checked, false) + accountSettingsButton.setCheckedState(!checked, false) + pluginSettingsButton.setCheckedState(!checked, false) + } + } + + IconButton { + id: pluginSettingsButton + + buttonText: qsTr("Plugins") + imageSource: "qrc:/images/icons/extension_24dp.svg" + + onCheckedToggledForLeftPanel: { + generalSettingsButton.setCheckedState(!checked, false) + accountSettingsButton.setCheckedState(!checked, false) + mediaSettingsButton.setCheckedState(!checked, false) + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + +} + diff --git a/src/settingsview/components/LevelMeter.qml b/src/settingsview/components/LevelMeter.qml new file mode 100644 index 00000000..045d329a --- /dev/null +++ b/src/settingsview/components/LevelMeter.qml @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +ProgressBar { + id: levelMeter + + value: { + return clamp(rmsLevel * 300.0, 0.0, 100.0) + } + + property real rmsLevel: 0 + + function clamp(num,a,b){ + return Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b)) + } + + function start(){ + rmsLevel = 0 + } + + function stop(){ + + } + + function setLevel(rmsLevelIn){ + rmsLevel = rmsLevelIn + } + +} diff --git a/src/settingsview/components/LinkDeviceDialog.qml b/src/settingsview/components/LinkDeviceDialog.qml new file mode 100644 index 00000000..c1caca16 --- /dev/null +++ b/src/settingsview/components/LinkDeviceDialog.qml @@ -0,0 +1,678 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Dialog { + id: linkDeviceDialog + + function openLinkDeviceDialog(){ + infoLabel.text = qsTr("This pin and the account password should be entered in your device within 10 minutes.") + passwordEdit.clear() + linkDeviceDialog.open() + if(ClientWrapper.accountAdaptor.hasPassword()){ + stackedWidget.currentIndex = 0 + } else { + setGeneratingPage() + } + } + + function setGeneratingPage(){ + if(passwordEdit.length === 0 && ClientWrapper.accountAdaptor.hasPassword()){ + setExportPage(NameDirectory.ExportOnRingStatus.WRONG_PASSWORD, "") + return + } + + stackedWidget.currentIndex = 1 + spinnerMovie.playing = true + + timerForExport.restart() + } + + function slotExportOnRing(){ + ClientWrapper.accountModel.exportOnRing(ClientWrapper.utilsAdaptor.getCurrAccId(),passwordEdit.text) + } + + Timer{ + id: timerForExport + + repeat: false + interval: 200 + + onTriggered: { + timeOut.restart() + slotExportOnRing() + } + } + + Timer{ + id: timeOut + + repeat: false + interval: exportTimeout + + onTriggered: { + setExportPage(NameDirectory.ExportOnRingStatus.NETWORK_ERROR, "") + } + } + + function setExportPage(status, pin){ + timeOut.stop() + + if(status === NameDirectory.ExportOnRingStatus.SUCCESS){ + infoLabel.isSucessState = true + yourPinLabel.visible = true + exportedPIN.visible = true + infoLabel.text = qsTr("This pin and the account password should be entered in your device within 10 minutes.") + exportedPIN.text = pin + } else { + infoLabel.isSucessState = false + yourPinLabel.visible = false + exportedPIN.visible = false + + switch(status){ + case NameDirectory.ExportOnRingStatus.WRONG_PASSWORD: + infoLabel.text = qsTr("Incorrect password") + + break + case NameDirectory.ExportOnRingStatus.NETWORK_ERROR: + infoLabel.text = qsTr("Error connecting to the network.\nPlease try again later.") + + break + case NameDirectory.ExportOnRingStatus.INVALID: + infoLabel.text = qsTr("Something went wrong.\n") + + break + } + } + stackedWidget.currentIndex = 2 + } + + property int exportTimeout : 20000 + + Connections{ + target: ClientWrapper.nameDirectory + + function onExportOnRingEnded(status, pin){ + setExportPage(status, pin) + } + } + + visible: false + + anchors.centerIn: parent.Center + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + title: qsTr("Link another device") + + onClosed: { + if(infoLabel.isSucessState){ + accept() + } else { + reject() + } + } + + contentItem: Rectangle{ + implicitWidth: 365 + implicitHeight: 208 + + StackLayout{ + id: stackedWidget + anchors.fill: parent + + currentIndex: 2 + + Rectangle{ + id: passwordConfirmPage + + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + Layout.topMargin: 11 + Layout.bottomMargin: 11 + + ColumnLayout{ + anchors.fill: parent + spacing: 7 + + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + Layout.preferredWidth: 219 + Layout.alignment: Qt.AlignHCenter + wrapMode: Text.Wrap + text: qsTr("Enter your account password") + font.pointSize: 8 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + InfoLineEdit{ + id: passwordEdit + + Layout.alignment: Qt.AlignHCenter + + Layout.minimumWidth: 294 + Layout.preferredWidth: 294 + + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + echoMode: TextInput.Password + + placeholderText: qsTr("Password") + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + RowLayout{ + spacing: 7 + + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableRadiusButton{ + id: btnPasswordOk + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + radius: height /2 + + text: qsTr("Register") + font.pointSize: 10 + font.kerning: true + + onClicked: { + setGeneratingPage() + } + } + + Item{ + Layout.fillWidth: true + Layout.minimumWidth: 40 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableButtonTextItem { + id: btnCancel + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + backgroundColor: "red" + onEnterColor: Qt.rgba(150 / 256, 0, 0, 0.7) + onDisabledBackgroundColor: Qt.rgba( + 255 / 256, + 0, 0, 0.8) + onPressColor: backgroundColor + textColor: "white" + + radius: height /2 + + text: qsTr("Cancel") + font.pointSize: 10 + font.kerning: true + + onClicked: { + reject() + } + } + + Item{ + Layout.fillWidth: true + Layout.minimumWidth: 40 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + } + + Rectangle{ + id: exportingPage + + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + Layout.topMargin: 11 + Layout.bottomMargin: 11 + + ColumnLayout{ + anchors.fill: parent + spacing: 7 + + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + Layout.minimumHeight: 40 + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + RowLayout{ + Layout.fillWidth: true + spacing: 0 + + Layout.maximumHeight: 30 + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 0 + Layout.preferredWidth: 341 + + Layout.minimumHeight: 0 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + wrapMode: Text.Wrap + text: qsTr("Exporting Account") + font.pointSize: 8 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + RowLayout{ + spacing: 7 + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + id: exportingSpinner + + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 96 + Layout.preferredWidth: 96 + Layout.minimumWidth: 96 + + Layout.maximumHeight: 96 + Layout.preferredHeight: 96 + Layout.minimumHeight: 96 + + background: Rectangle { + anchors.fill: parent + AnimatedImage { + id: spinnerMovie + + anchors.fill: parent + + source: "qrc:/images/jami_eclipse_spinner.gif" + + playing: exportingSpinner.visible + paused: false + fillMode: Image.PreserveAspectFit + mipmap: true + } + } + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + Layout.minimumHeight: 40 + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + } + } + + Rectangle{ + id: exportedPage + + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + Layout.topMargin: 11 + Layout.bottomMargin: 11 + + ColumnLayout{ + anchors.fill: parent + spacing: 7 + + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + RowLayout{ + spacing: 7 + + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + id: yourPinLabel + + Layout.alignment: Qt.AlignHCenter + + Layout.preferredHeight: 25 + + wrapMode: Text.Wrap + text: "Your PIN is:" + font.pointSize: 8 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + id: exportedPIN + + Layout.alignment: Qt.AlignHCenter + + Layout.preferredHeight: 25 + + wrapMode: Text.Wrap + text: "PIN" + font.pointSize: 12 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + RowLayout{ + spacing: 7 + + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + id: infoLabel + + property bool isSucessState: false + property int borderWidth : isSucessState? 1 : 0 + property int borderRadius : isSucessState? 15 : 0 + property string backgroundColor : isSucessState? "whitesmoke" : "transparent" + property string borderColor : isSucessState? "lightgray" : "transparent" + color: isSucessState ? "#2b5084" : "black" + padding: isSucessState ? 8 : 0 + + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: 320 + Layout.preferredHeight: 50 + + wrapMode: Text.Wrap + text: qsTr("This pin and the account password should be entered in your device within 10 minutes.") + font.pointSize: 8 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + background: Rectangle{ + id: infoLabelBackground + + anchors.fill: parent + border.width: infoLabel.borderWidth + border.color: infoLabel.borderColor + radius: infoLabel.borderRadius + color: infoLabel.backgroundColor + } + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + RowLayout{ + spacing: 7 + + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableRadiusButton{ + id: btnCloseExportDialog + + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + radius: height /2 + + text: qsTr("Close") + font.pointSize: 10 + font.kerning: true + + onClicked: { + if(infoLabel.isSucessState){ + accept() + } else { + reject() + } + } + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + } + } + } + } +} diff --git a/src/settingsview/components/NameRegistrationDialog.qml b/src/settingsview/components/NameRegistrationDialog.qml new file mode 100644 index 00000000..ab9988d3 --- /dev/null +++ b/src/settingsview/components/NameRegistrationDialog.qml @@ -0,0 +1,555 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +Dialog { + id: nameRegistrationDialog + + property string registerdName : "" + + function openNameRegistrationDialog(registerNameIn){ + registerdName = registerNameIn + lblRegistrationError.text = qsTr("Something went wrong") + passwordEdit.clear() + if(ClientWrapper.accountAdaptor.hasPassword()){ + stackedWidget.currentIndex = 0 + } else { + startRegistration() + } + + nameRegistrationDialog.open() + } + + function startRegistration(){ + startSpinner() + timerForStartRegistration.restart() + } + + function slotStartNameRegistration(){ + var password = passwordEdit.text + ClientWrapper.accountModel.registerName(ClientWrapper.utilsAdaptor.getCurrAccId(), password, registerdName) + } + + function startSpinner(){ + stackedWidget.currentIndex = 1 + spinnerLabel.visible = true + spinnerMovie.playing = true + } + + Timer{ + id: timerForStartRegistration + + interval: 100 + repeat: false + + onTriggered: { + slotStartNameRegistration() + } + } + + Connections{ + target: ClientWrapper.nameDirectory + + function onNameRegistrationEnded(status, name){ + if(status === NameDirectory.RegisterNameStatus.SUCCESS){ + accept() + } else { + switch(status){ + case NameDirectory.RegisterNameStatus.WRONG_PASSWORD: + lblRegistrationError.text = qsTr("Incorrect password") + break + + case NameDirectory.RegisterNameStatus.NETWORK_ERROR: + lblRegistrationError.text = qsTr("Network error") + break + default: + break + } + stackedWidget.currentIndex = 2 + } + } + } + + visible: false + + anchors.centerIn: parent.Center + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + title: qsTr("Set Registered Name") + + onClosed: { + reject() + } + + contentItem: Rectangle{ + implicitWidth: 365 + implicitHeight: 208 + + StackLayout{ + id: stackedWidget + anchors.fill: parent + + currentIndex: 0 + + Rectangle{ + id: passwordConfirmPage + + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + Layout.topMargin: 11 + Layout.bottomMargin: 11 + + ColumnLayout{ + anchors.fill: parent + spacing: 7 + + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + Layout.preferredWidth: 219 + Layout.alignment: Qt.AlignHCenter + wrapMode: Text.Wrap + text: qsTr("Enter your account password") + font.pointSize: 8 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + InfoLineEdit{ + id: passwordEdit + + Layout.alignment: Qt.AlignHCenter + + Layout.minimumWidth: 294 + Layout.preferredWidth: 294 + + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + echoMode: TextInput.Password + + placeholderText: qsTr("Password") + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + RowLayout{ + spacing: 7 + + Layout.alignment: Qt.AlignHCenter + + Layout.fillWidth: true + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableRadiusButton{ + id: btnRegister + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + radius: height /2 + + text: qsTr("Register") + font.pointSize: 10 + font.kerning: true + + onClicked: { + startRegistration() + } + } + + Item{ + Layout.fillWidth: true + Layout.minimumWidth: 40 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableButtonTextItem { + id: btnCancel + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + backgroundColor: "red" + onEnterColor: Qt.rgba(150 / 256, 0, 0, 0.7) + onDisabledBackgroundColor: Qt.rgba( + 255 / 256, + 0, 0, 0.8) + onPressColor: backgroundColor + textColor: "white" + + radius: height /2 + + text: qsTr("Cancel") + font.pointSize: 10 + font.kerning: true + + onClicked: { + reject() + } + } + + Item{ + Layout.fillWidth: true + Layout.minimumWidth: 40 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + } + + Rectangle{ + id: registeringPage + + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.leftMargin: 11 + Layout.rightMargin: 11 + Layout.topMargin: 11 + Layout.bottomMargin: 11 + + ColumnLayout{ + anchors.fill: parent + spacing: 7 + + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + Layout.minimumHeight: 40 + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + RowLayout{ + Layout.fillWidth: true + spacing: 0 + + Layout.maximumHeight: 30 + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 0 + Layout.preferredWidth: 341 + + Layout.minimumHeight: 0 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + wrapMode: Text.Wrap + text: qsTr("Registering Name") + font.pointSize: 8 + font.kerning: true + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + RowLayout{ + spacing: 7 + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + id: spinnerLabel + + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 96 + Layout.preferredWidth: 96 + Layout.minimumWidth: 96 + + Layout.maximumHeight: 96 + Layout.preferredHeight: 96 + Layout.minimumHeight: 96 + + background: Rectangle { + anchors.fill: parent + AnimatedImage { + id: spinnerMovie + + anchors.fill: parent + + source: "qrc:/images/jami_eclipse_spinner.gif" + + playing: spinnerLabel.visible + paused: false + fillMode: Image.PreserveAspectFit + mipmap: true + } + } + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + } + + Item{ + Layout.alignment: Qt.AlignHCenter + + Layout.fillHeight: true + Layout.minimumHeight: 40 + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + } + } + + Rectangle{ + id: nameNotRegisteredPage + + Layout.fillWidth: true + Layout.fillHeight: true + + ColumnLayout{ + anchors.fill: parent + + Item{ + Layout.fillHeight: true + Layout.minimumHeight: 40 + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + RowLayout{ + spacing: 7 + Layout.fillWidth: true + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + Label{ + id: lblRegistrationError + + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 0 + Layout.preferredWidth: 341 + + Layout.minimumHeight: 0 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + wrapMode: Text.Wrap + text: qsTr("Something went wrong") + font.pointSize: 8 + font.kerning: true + color: "red" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + Item{ + Layout.fillHeight: true + Layout.minimumHeight: 40 + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + RowLayout{ + spacing: 7 + Layout.fillWidth: true + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableRadiusButton{ + id: btnCloseRegisterDialog + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + radius: height /2 + + text: qsTr("Close") + font.pointSize: 10 + font.kerning: true + + onClicked: { + reject() + } + } + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + } + + Item{ + Layout.fillHeight: true + Layout.minimumHeight: 40 + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + } + } + } + } +} diff --git a/src/settingsview/components/PluginItemDelegate.qml b/src/settingsview/components/PluginItemDelegate.qml new file mode 100644 index 00000000..3946a150 --- /dev/null +++ b/src/settingsview/components/PluginItemDelegate.qml @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +ItemDelegate { + id: pluginItemDelegate + + property string pluginName : "" + property string pluginId: "" + property string pluginIcon: "" + property bool isLoaded: false + + signal btnLoadPluginToggled + signal btnPreferencesPluginClicked + + highlighted: ListView.isCurrentItem + + RowLayout{ + anchors.fill: parent + + Label{ + Layout.leftMargin: 7 + Layout.bottomMargin: 7 + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + background: Rectangle{ + anchors.fill: parent + Image { + anchors.fill: parent + source: "file:"+pluginIcon + } + } + } + + ColumnLayout{ + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.leftMargin: 7 + Layout.topMargin: 7 + Layout.bottomMargin: 7 + + RowLayout{ + + Layout.minimumHeight: 30 + + Label{ + id: labelDeviceId + + Layout.minimumHeight: 20 + + font.pointSize: 10 + font.kerning: true + text: pluginName === "" ? pluginId : pluginName + } + + Item{ + Layout.fillWidth: true + + Layout.minimumWidth: 0 + Layout.minimumHeight: 20 + } + } + } + + Switch { + id: loadSwitch + property bool isHovering: false + + Layout.bottomMargin: 7 + Layout.rightMargin: 15 + + Layout.maximumWidth: 30 + Layout.preferredWidth: 30 + Layout.minimumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + ToolTip.visible: isHovering + ToolTip.text: { + return qsTr("Load/Unload") + } + + checked: isLoaded + onClicked: { + btnLoadPluginToggled() + } + + background: Rectangle { + id: switchBackground + MouseArea { + id: btnMouseArea + anchors.fill: parent + hoverEnabled: true + onPressed: { + } + onReleased: { + loadSwitch.clicked() + } + onEntered: { + loadSwitch.isHovering = true + } + onExited: { + loadSwitch.isHovering = false + } + } + } + } + + HoverableRadiusButton{ + id: btnPreferencesPlugin + + Layout.bottomMargin: 7 + Layout.rightMargin: 7 + Layout.alignment: Qt.AlignRight + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + + source:{ + return "qrc:/images/icons/round-settings-24px.svg" + } + + ToolTip.visible: isHovering + ToolTip.text: { + return qsTr("Edit preferences") + } + + onClicked: { + btnPreferencesPluginClicked() + } + } + } +} diff --git a/src/settingsview/components/PluginListPreferencesView.qml b/src/settingsview/components/PluginListPreferencesView.qml new file mode 100644 index 00000000..9b8c03af --- /dev/null +++ b/src/settingsview/components/PluginListPreferencesView.qml @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import Qt.labs.platform 1.1 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 +import "../../commoncomponents" + +Rectangle { + id: pluginListPreferencesViewRect + + enum Type { + LIST, + DEFAULT + } + + signal updatePluginList + + property string pluginName: "" + property string pluginIcon: "" + property string pluginId: "" + property bool isLoaded: false + property int size: 0 + + visible: false + + function updatePreferenceListDisplayed(show){ + // settings + getSize(pluginId, show) + preferenceItemListModel.pluginId = pluginId + preferenceItemListModel.reset() + } + + function resetPluginSlot(){ + resetPluginMessageBox.open() + } + + function resetPlugin(){ + ClientWrapper.pluginModel.resetPluginPreferencesValues(pluginId, isLoaded) + updatePluginList() + } + + function uninstallPluginSlot(){ + uninstallPluginMessageBox.open() + } + + function uninstallPlugin(){ + ClientWrapper.pluginModel.uninstallPlugin(pluginId) + updatePluginList() + } + + function getSize(pluginId, show){ + size = 50 * ClientWrapper.pluginModel.getPluginPreferences(pluginId).length + if (show) { + height = 200 + size + pluginPreferenceView.height = size + } else { + height = 25 + } + } + + function editPreferenceSlot(preferenceType, preferenceName, preferenceEntryValues){ + switch (preferenceType){ + case PluginListPreferencesView.LIST: + console.log("LIST") + editListMessageBox.preferenceName = preferenceName + editListMessageBox.preferenceEntryValues = preferenceEntryValues + editListMessageBox.open() + break + case PluginListPreferencesView.DEFAULT: + console.log("Unrecognizable Type") + break + default: + console.log("Unrecognizable Type") + break + } + } + + function setPreference(pluginId, preferenceKey, preferenceNewValue) + { + ClientWrapper.pluginModel.setPluginPreferences(pluginId, preferenceKey, preferenceNewValue, isLoaded) + preferenceItemListModel.reset() + } + + MessageBox{ + id: uninstallPluginMessageBox + + title:qsTr("Uninstall plugin") + text :qsTr("Are you sure you wish to uninstall " + pluginName + " ?") + standardButtons: StandardButton.Ok | StandardButton.Cancel + + onYes: { + accepted() + } + + onNo:{ + rejected() + } + + onDiscard: { + rejected() + } + + onAccepted: { + uninstallPlugin() + pluginListPreferencesViewRect.visible = false + } + + onRejected: {} + } + + MessageBox{ + id: resetPluginMessageBox + + title:qsTr("Reset preferences") + text :qsTr("Are you sure you wish to reset "+ pluginName + " preferences?") + + standardButtons: StandardButton.Ok | StandardButton.Cancel + + onYes: { + accepted() + } + + onNo:{ + rejected() + } + + onDiscard: { + rejected() + } + + onAccepted: { + resetPlugin() + } + + onRejected: {} + } + + MessageBox{ + id: editListMessageBox + + property string preferenceName: "" + property var preferenceEntryValues: [] + + title:qsTr("Edit " + preferenceName) + text :qsTr(preferenceName + " options: " + preferenceEntryValues) + + standardButtons: StandardButton.Ok | StandardButton.Cancel + + onYes: { + accepted() + } + + onNo:{ + rejected() + } + + onDiscard: { + rejected() + } + + onAccepted: { + // setPreference(pluginId, preferenceItemDelegate.preferenceKey, preferenceItemDelegate.preferenceNewValue) + } + + onRejected: {} + } + + PreferenceItemListModel { + id: preferenceItemListModel + } + + Layout.fillHeight: true + Layout.fillWidth: true + + ColumnLayout { + spacing: 6 + Layout.fillHeight: true + Layout.maximumWidth: 580 + Layout.preferredWidth: 580 + Layout.minimumWidth: 580 + + Label{ + Layout.alignment: Qt.AlignHCenter + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + background: Rectangle{ + anchors.fill: parent + Image { + anchors.fill: parent + source: "file:"+pluginIcon + } + } + } + + Label { + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 10 + Layout.fillWidth: true + Layout.minimumHeight: 25 + Layout.preferredHeight: 25 + Layout.maximumHeight: 25 + + text: qsTr(pluginName + "\npreferences") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.topMargin: 10 + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + HoverableRadiusButton { + id: resetButton + + Layout.maximumWidth: 157 + Layout.preferredWidth: 157 + Layout.minimumWidth: 157 + + Layout.fillHeight: true + + radius: height / 2 + + icon.source: "qrc:/images/icons/settings_backup_restore-black-18dp.svg" + icon.height: 24 + icon.width: 24 + + text: qsTr("Reset") + fontPointSize: 10 + font.kerning: true + + onClicked: { + resetPluginSlot() + } + } + + HoverableRadiusButton { + id: uninstallButton + + Layout.maximumWidth: 157 + Layout.preferredWidth: 157 + Layout.minimumWidth: 157 + + Layout.fillHeight: true + + radius: height / 2 + + icon.source: "qrc:/images/icons/ic_delete_black_18dp_2x.png" + icon.height: 24 + icon.width: 24 + + text: qsTr("Uninstall") + fontPointSize: 10 + font.kerning: true + + onClicked: { + uninstallPluginSlot() + } + } + } + + ListViewJami { + id: pluginPreferenceView + + Layout.minimumWidth: 320 + Layout.preferredWidth: 320 + Layout.maximumWidth: 320 + + Layout.minimumHeight: 0 + Layout.preferredHeight: height + Layout.maximumHeight: 1000 + + model: preferenceItemListModel + + delegate: PreferenceItemDelegate{ + id: preferenceItemDelegate + + width: pluginPreferenceView.width + height: 50 + + preferenceKey : PreferenceKey + preferenceName: PreferenceName + preferenceSummary: PreferenceSummary + preferenceType: PreferenceType + preferenceDefaultValue: PreferenceDefaultValue + preferenceEntries: PreferenceEntries + preferenceEntryValues: PreferenceEntryValues + + onClicked: { + pluginPreferenceView.currentIndex = index + } + onBtnPreferenceClicked: { + console.log("edit preference ", preferenceName) + console.log("preference type ", preferenceType) + console.log("preference entry values ", preferenceEntryValues.length) + editPreferenceSlot(preferenceType, preferenceName, preferenceEntryValues) + } + } + } + } +} diff --git a/src/settingsview/components/PluginListSettingsView.qml b/src/settingsview/components/PluginListSettingsView.qml new file mode 100644 index 00000000..09434a31 --- /dev/null +++ b/src/settingsview/components/PluginListSettingsView.qml @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import Qt.labs.platform 1.1 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 +import "../../commoncomponents" + +Rectangle { + id: pluginListSettingsViewRect + + property PluginListPreferencesView pluginListPreferencesView + visible: false + signal scrollView + + function updatePluginListDisplayed() { + // settings + } + + function openPluginFileSlot(){ + pluginPathDialog.open() + } + + function updateAndShowPluginsSlot() + { + pluginItemListModel.reset() + } + + function loadPluginSlot(pluginId, isLoaded){ + var loaded = false + if (isLoaded) + ClientWrapper.pluginModel.unloadPlugin(pluginId) + else + loaded = ClientWrapper.pluginModel.loadPlugin(pluginId) + if(pluginListPreferencesView.pluginId === pluginId) + pluginListPreferencesView.isLoaded = loaded + updateAndShowPluginsSlot() + } + + function openPreferencesPluginSlot(pluginName, pluginIcon, pluginId, isLoaded){ + updateAndShowPluginPreferenceSlot(pluginName, pluginIcon, pluginId, isLoaded) + } + + function updateAndShowPluginPreferenceSlot(pluginName, pluginIcon, pluginId, isLoaded){ + pluginListPreferencesView.pluginName = pluginName + pluginListPreferencesView.pluginIcon = pluginIcon + pluginListPreferencesView.pluginId = pluginId + pluginListPreferencesView.isLoaded = isLoaded + pluginListPreferencesView.updatePreferenceListDisplayed(!pluginListPreferencesView.visible) + pluginListPreferencesView.visible = !pluginListPreferencesView.visible + scrollView() + } + + JamiFileDialog { + id: pluginPathDialog + + mode: JamiFileDialog.OpenFile + title: qsTr("Select A Plugin to Install") + folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation) + + nameFilters: [qsTr("Plugin Files") + " (*.jpl)", qsTr( + "All files") + " (*)"] + + onRejected: {} + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + + onAccepted: { + var url = ClientWrapper.utilsAdaptor.getAbsPath(file.toString()) + ClientWrapper.pluginModel.installPlugin(url, true) + updateAndShowPluginsSlot() + } + } + + PluginItemListModel { + id: pluginItemListModel + } + + Layout.fillHeight: true + Layout.fillWidth: true + + ColumnLayout { + id: pluginListViewLayout + + Layout.fillHeight: true + Layout.maximumWidth: 580 + Layout.preferredWidth: 580 + Layout.minimumWidth: 580 + + Label { + Layout.fillWidth: true + Layout.minimumHeight: 25 + Layout.preferredHeight: 25 + Layout.maximumHeight: 25 + + text: qsTr("Installed plugins") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } + + ColumnLayout { + spacing: 6 + + Layout.fillWidth: true + Layout.topMargin: 6 + + HoverableRadiusButton { + id: installButton + + Layout.leftMargin: 20 + + Layout.maximumWidth: 320 + Layout.preferredWidth: 320 + Layout.minimumWidth: 320 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + radius: height / 2 + + text: qsTr("+ Install plugin") + fontPointSize: 10 + font.kerning: true + + onClicked: { + openPluginFileSlot() + } + } + + ListViewJami { + id: pluginListView + + Layout.leftMargin: 20 + + Layout.minimumWidth: 320 + Layout.preferredWidth: 320 + Layout.maximumWidth: 320 + + Layout.minimumHeight: 175 + Layout.preferredHeight: 175 + Layout.maximumHeight: 175 + + model: pluginItemListModel + + delegate: PluginItemDelegate{ + id: pluginItemDelegate + + width: pluginListView.width + height: 50 + + pluginName : PluginName + pluginId: PluginId + pluginIcon: PluginIcon + isLoaded: IsLoaded + + onClicked: { + pluginListView.currentIndex = index + } + + onBtnLoadPluginToggled:{ + loadPluginSlot(pluginId, isLoaded) + } + + onBtnPreferencesPluginClicked:{ + openPreferencesPluginSlot(pluginName, pluginIcon, pluginId, isLoaded) + } + } + } + } + } +} + \ No newline at end of file diff --git a/src/settingsview/components/PluginSettingsPage.qml b/src/settingsview/components/PluginSettingsPage.qml new file mode 100644 index 00000000..b71f7aa4 --- /dev/null +++ b/src/settingsview/components/PluginSettingsPage.qml @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import Qt.labs.platform 1.1 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 +import "../../commoncomponents" + +Rectangle { + id: pluginSettingsRect + + function populatePluginSettings(){ + // settings + enabledplugin.checked = ClientWrapper.pluginModel.getPluginsEnabled() + pluginListSettingsView.visible = enabledplugin.checked + if (pluginListSettingsView.visible) { + pluginListSettingsView.updatePluginListDisplayed() + } + } + + function slotSetPluginEnabled(state){ + ClientWrapper.pluginModel.setPluginsEnabled(state) + } + + Layout.fillHeight: true + Layout.fillWidth: true + + ColumnLayout { + anchors.fill: parent + spacing: 6 + + width: parent.width + height: parent.height + + Label { + width: parent.width + height: parent.height + + Layout.leftMargin: 35 + Layout.topMargin: 15 + Layout.alignment: Qt.AlignTop + + text: qsTr("Plugin") + + font.pointSize: 15 + font.kerning: true + + horizontalAlignment: Text.AlignBottom + verticalAlignment: Text.AlignVCenter + } + + ScrollView { + id: pluginScrollView + Layout.fillHeight: true + Layout.fillWidth: true + + width: parent.width + height: parent.height + focus: true + + clip: true + + ColumnLayout { + id: pluginViewLayout + Layout.fillHeight: true + Layout.fillWidth: true + + ToggleSwitch { + id: enabledplugin + + Layout.topMargin: 15 + Layout.leftMargin: 36 + + labelText: "Enable" + fontPointSize: 13 + + onSwitchToggled: { + slotSetPluginEnabled(checked) + + pluginListSettingsView.visible = checked + if (!checked) { + pluginListPreferencesView.visible = checked + } + if (pluginListSettingsView.visible) { + pluginListSettingsView.updatePluginListDisplayed() + } + } + } + ColumnLayout { + spacing: 6 + Layout.fillHeight: true + width:380 + height:100 + + // instantiate plugin list setting page + PluginListSettingsView { + id: pluginListSettingsView + + width:380 + height:265 + Layout.leftMargin: 35 + Layout.topMargin: 15 + Layout.alignment: Qt.AlignHCenter + + pluginListPreferencesView: pluginListPreferencesView + + onScrollView:{ } + } + + PluginListPreferencesView { + id: pluginListPreferencesView + + width:380 + Layout.minimumHeight: 175 + Layout.preferredHeight: height + Layout.maximumHeight: 1000 + Layout.alignment: Qt.AlignHCenter + Layout.leftMargin: 55 + + onUpdatePluginList:{ + pluginListSettingsView.updateAndShowPluginsSlot() + } + } + } + } + } + } +} diff --git a/src/settingsview/components/PreferenceItemDelegate.qml b/src/settingsview/components/PreferenceItemDelegate.qml new file mode 100644 index 00000000..88574f27 --- /dev/null +++ b/src/settingsview/components/PreferenceItemDelegate.qml @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +import "../../commoncomponents" + +ItemDelegate { + id: preferenceItemDelegate + + property string preferenceKey: "" + property string preferenceName: "" + property string preferenceSummary: "" + property int preferenceType: -1 + property string preferenceDefaultValue: "" + property var preferenceEntries: [] + property var preferenceEntryValues: [] + property string preferenceNewValue: "" + + signal btnPreferenceClicked + + highlighted: ListView.isCurrentItem + + RowLayout{ + anchors.fill: parent + + ColumnLayout{ + Layout.fillWidth: true + Layout.fillHeight: true + + Layout.topMargin: 7 + Layout.bottomMargin: 7 + Layout.leftMargin: 7 + + Layout.minimumHeight: 30 + + Label{ + Layout.minimumHeight: 10 + width: 320 - 36 + + font.pointSize: 10 + font.kerning: true + font.bold: true + text: preferenceName + } + } + + HoverableRadiusButton{ + id: btnPreference + + Layout.alignment: Qt.AlignRight + Layout.bottomMargin: 7 + Layout.rightMargin: 7 + + Layout.minimumWidth: 30 + Layout.preferredWidth: 30 + Layout.maximumWidth: 30 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + buttonImageHeight: height + buttonImageWidth: height + + source:{ + return "qrc:/images/icons/round-settings-24px.svg" + } + + ToolTip.visible: isHovering + ToolTip.text: { + return qsTr("Modify preference") + } + + onClicked: { + btnPreferenceClicked() + } + } + } +} diff --git a/src/settingsview/components/RevokeDevicePasswordDialog.qml b/src/settingsview/components/RevokeDevicePasswordDialog.qml new file mode 100644 index 00000000..69d433f9 --- /dev/null +++ b/src/settingsview/components/RevokeDevicePasswordDialog.qml @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 +import QtQuick.Controls.Styles 1.4 + +import "../../commoncomponents" + +Dialog { + id: revokeDevicePasswordDialog + + property string deviceId : "" + + signal revokeDeviceWithPassword(string idOfDevice, string password) + + function openRevokeDeviceDialog(deviceIdIn){ + deviceId = deviceIdIn + passwordEdit.clear() + revokeDevicePasswordDialog.open() + } + + visible: false + + anchors.centerIn: parent.Center + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + title: qsTr("Enter this account's password to confirm the removal of this device") + + onClosed: { + reject() + } + + onAccepted:{ + revokeDeviceWithPassword(deviceId,passwordEdit.text) + } + + contentItem: Rectangle{ + implicitWidth: 365 + implicitHeight: 120 + + ColumnLayout{ + anchors.fill: parent + spacing: 7 + + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + InfoLineEdit{ + id: passwordEdit + + Layout.alignment: Qt.AlignHCenter + + Layout.minimumWidth: 294 + Layout.preferredWidth: 294 + + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + echoMode: TextInput.Password + + placeholderText: qsTr("Password") + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + + RowLayout{ + spacing: 7 + + Layout.alignment: Qt.AlignHCenter + + Layout.fillWidth: true + + Item{ + Layout.fillWidth: true + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableRadiusButton{ + id: btnOkay + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + radius: height /2 + + text: qsTr("Okay") + font.pointSize: 10 + font.kerning: true + + onClicked: { + accept() + } + } + + Item{ + Layout.fillWidth: true + Layout.minimumWidth: 40 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + HoverableButtonTextItem { + id: btnCancel + + Layout.maximumWidth: 130 + Layout.preferredWidth: 130 + Layout.minimumWidth: 130 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + backgroundColor: "red" + onEnterColor: Qt.rgba(150 / 256, 0, 0, 0.7) + onDisabledBackgroundColor: Qt.rgba( + 255 / 256, + 0, 0, 0.8) + onPressColor: backgroundColor + textColor: "white" + + radius: height /2 + + text: qsTr("Cancel") + font.pointSize: 10 + font.kerning: true + + onClicked: { + reject() + } + } + + Item{ + Layout.fillWidth: true + Layout.minimumWidth: 40 + + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.minimumHeight: 20 + } + + } + + Item{ + Layout.fillHeight: true + + Layout.maximumWidth: 20 + Layout.preferredWidth: 20 + Layout.minimumWidth: 20 + } + } + } +} diff --git a/src/settingsview/components/SettingParaCombobox.qml b/src/settingsview/components/SettingParaCombobox.qml new file mode 100644 index 00000000..b691acb2 --- /dev/null +++ b/src/settingsview/components/SettingParaCombobox.qml @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 + +ComboBox { + id: control + + delegate: ItemDelegate { + width: control.width + contentItem: Text { + text: { + var currentItem = control.delegateModel.items.get(index) + return currentItem.model[control.textRole].toString() + } + color: "black" + font: control.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + highlighted: control.highlightedIndex === index + } + + indicator: Canvas { + id: canvas + x: control.width - width - control.rightPadding + y: control.topPadding + (control.availableHeight - height) / 2 + width: 12 + height: 8 + contextType: "2d" + + Connections { + target: control + function onPressedChanged(){ + canvas.requestPaint() + } + } + + onPaint: { + context.reset(); + context.moveTo(0, 0); + context.lineTo(width, 0); + context.lineTo(width / 2, height); + context.closePath(); + context.fillStyle = control.pressed ? "#17a81a" : "#21be2b"; + context.fill(); + } + } + + contentItem: Text { + leftPadding: 0 + rightPadding: control.indicator.width + control.spacing + + text: control.displayText + font: control.font + color: "black" + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + border.color: "white" + border.width: control.visualFocus ? 2 : 1 + radius: 2 + } + + popup: Popup { + y: control.height - 1 + width: control.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: control.delegateModel + currentIndex: control.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + border.color: "gray" + radius: 2 + } + } +} diff --git a/src/settingsview/components/ToggleSwitch.qml b/src/settingsview/components/ToggleSwitch.qml new file mode 100644 index 00000000..27c84bdc --- /dev/null +++ b/src/settingsview/components/ToggleSwitch.qml @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 + +RowLayout { + property string labelText: value + property int widthOfSwitch: 50 + property int heightOfSwitch: 10 + property int heightOfLayout: 30 + property int fontPointSize: 13 + + property alias toggleSwitch: switchOfLayout + property alias checked: switchOfLayout.checked + + signal switchToggled + + spacing: 18 + Layout.fillWidth: true + Layout.maximumHeight: 30 + + Switch { + id: switchOfLayout + Layout.alignment: Qt.AlignVCenter + + Layout.maximumWidth: widthOfSwitch + Layout.preferredWidth: widthOfSwitch + Layout.minimumWidth: widthOfSwitch + + Layout.minimumHeight: heightOfSwitch + Layout.preferredHeight: heightOfSwitch + Layout.maximumHeight: heightOfSwitch + + onToggled: { + switchToggled() + } + } + + Label { + Layout.fillWidth: true + + Layout.minimumHeight: heightOfLayout + Layout.preferredHeight: heightOfLayout + Layout.maximumHeight: heightOfLayout + + text: qsTr(labelText) + font.pointSize: fontPointSize + font.kerning: true + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + } +} diff --git a/src/settingsview/components/VideoCodecDelegate.qml b/src/settingsview/components/VideoCodecDelegate.qml new file mode 100644 index 00000000..a01fd287 --- /dev/null +++ b/src/settingsview/components/VideoCodecDelegate.qml @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.15 +import QtQuick.Window 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import QtQuick.Controls.Styles 1.4 +import net.jami.Models 1.0 + +ItemDelegate { + id: videoCodecDelegate + + property string videoCodecName : "" + property bool isEnabled : false + property int videoCodecId + + signal videoCodecStateChange(string idToSet , bool isToBeEnabled) + + property int checkBoxWidth: 10 + + highlighted: ListView.isCurrentItem + + RowLayout{ + anchors.fill: parent + + spacing: 10 + + CheckBox{ + id: checkBoxIsEnabled + + Layout.leftMargin: 20 + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.fillHeight: true + + Layout.minimumWidth: checkBoxWidth + Layout.preferredWidth: checkBoxWidth + Layout.maximumWidth: checkBoxWidth + + tristate: false + checkState: isEnabled ? Qt.Checked : Qt.Unchecked + + indicator.implicitWidth: checkBoxWidth + indicator.implicitHeight:checkBoxWidth + + indicator.layer.textureSize.width: checkBoxWidth + indicator.layer.textureSize.height: checkBoxWidth + + text: "" + + nextCheckState: function() { + var result + var result_bool + + if (checkState === Qt.Checked){ + result = Qt.Unchecked + result_bool = false + } else { + result = Qt.Checked + result_bool = true + } + videoCodecStateChange(videoCodecId,result_bool) + return result + } + } + + Label{ + id: formatNameLabel + + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + + Layout.fillWidth: true + Layout.fillHeight: true + + text: videoCodecName + font.pointSize: 8 + font.kerning: true + } + } +} diff --git a/src/smartlistmodel.cpp b/src/smartlistmodel.cpp new file mode 100644 index 00000000..8fd0d5cb --- /dev/null +++ b/src/smartlistmodel.cpp @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2017-2020 by Savoir-faire Linux + * Author: Anthony Léonard + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "smartlistmodel.h" + +#include "lrcinstance.h" +#include "pixbufmanipulator.h" +#include "utils.h" + +#include "api/contactmodel.h" +#include "globalinstances.h" + +#include + +SmartListModel::SmartListModel(const QString &accId, + QObject *parent, + SmartListModel::Type listModelType, + const QString &convUid) + : QAbstractListModel(parent) + , accountId_(accId) + , listModelType_(listModelType) + , convUid_(convUid) +{ + if (listModelType_ == Type::CONFERENCE) { + setConferenceableFilter(); + } +} + +SmartListModel::~SmartListModel() {} + +int +SmartListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + auto &accInfo = LRCInstance::accountModel().getAccountInfo(accountId_); + auto &convModel = accInfo.conversationModel; + if (listModelType_ == Type::TRANSFER) { + auto filterType = accInfo.profileInfo.type; + return convModel->getFilteredConversations(filterType).size(); + } else if (listModelType_ == Type::CONFERENCE) { + auto calls = conferenceables_[ConferenceableItem::CALL]; + auto contacts = conferenceables_[ConferenceableItem::CONTACT]; + auto rowCount = contacts.size(); + if (calls.size()) { + rowCount = 2; + rowCount += sectionState_[tr("Calls")] ? calls.size() : 0; + rowCount += sectionState_[tr("Contacts")] ? contacts.size() : 0; + } + return rowCount; + } + return accInfo.conversationModel->allFilteredConversations().size(); + } + return 0; +} + +int +SmartListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + return 1; +} + +QVariant +SmartListModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + try { + auto &accountInfo = LRCInstance::accountModel().getAccountInfo(accountId_); + auto &convModel = accountInfo.conversationModel; + lrc::api::conversation::Info item; + if (listModelType_ == Type::TRANSFER) { + auto filterType = accountInfo.profileInfo.type; + item = convModel->getFilteredConversations(filterType).at(index.row()); + return getConversationItemData(item, accountInfo, role); + } else if (listModelType_ == Type::CONFERENCE) { + auto calls = conferenceables_[ConferenceableItem::CALL]; + auto contacts = conferenceables_[ConferenceableItem::CONTACT]; + QString itemConvUid{}, itemAccId{}; + if (calls.size() == 0) { + itemConvUid = contacts.at(index.row()).at(0).convId; + itemAccId = contacts.at(index.row()).at(0).accountId; + } else { + bool callsOpen = sectionState_[tr("Calls")]; + bool contactsOpen = sectionState_[tr("Contacts")]; + auto callSectionEnd = callsOpen ? calls.size() + 1 : 1; + auto contactSectionEnd = contactsOpen ? callSectionEnd + contacts.size() + 1 + : callSectionEnd + 1; + if (index.row() < callSectionEnd) { + if (index.row() == 0) { + return QVariant(role == Role::SectionName + ? (callsOpen ? "➖ " : "➕ ") + QString(tr("Calls")) + : ""); + } else { + auto idx = index.row() - 1; + itemConvUid = calls.at(idx).at(0).convId; + itemAccId = calls.at(idx).at(0).accountId; + } + } else if (index.row() < contactSectionEnd) { + if (index.row() == callSectionEnd) { + return QVariant(role == Role::SectionName + ? (contactsOpen ? "➖ " : "➕ ") + QString(tr("Contacts")) + : ""); + } else { + auto idx = index.row() - (callSectionEnd + 1); + itemConvUid = contacts.at(idx).at(0).convId; + itemAccId = contacts.at(idx).at(0).accountId; + } + } + } + if (role == Role::AccountId) { + return QVariant(itemAccId); + } + item = LRCInstance::getConversationFromConvUid(itemConvUid, itemAccId); + auto &itemAccountInfo = LRCInstance::accountModel().getAccountInfo(itemAccId); + return getConversationItemData(item, itemAccountInfo, role); + } else if (listModelType_ == Type::CONVERSATION) { + item = convModel->filteredConversation(index.row()); + return getConversationItemData(item, accountInfo, role); + } + } catch (const std::exception &e) { + qWarning() << e.what(); + } + return QVariant(); +} + +QHash +SmartListModel::roleNames() const +{ + QHash roles; + roles[DisplayName] = "DisplayName"; + roles[DisplayID] = "DisplayID"; + roles[Picture] = "Picture"; + roles[Presence] = "Presence"; + roles[URI] = "URI"; + roles[UnreadMessagesCount] = "UnreadMessagesCount"; + roles[LastInteractionDate] = "LastInteractionDate"; + roles[LastInteraction] = "LastInteraction"; + roles[ContactType] = "ContactType"; + roles[UID] = "UID"; + roles[InCall] = "InCall"; + roles[IsAudioOnly] = "IsAudioOnly"; + roles[CallStackViewShouldShow] = "CallStackViewShouldShow"; + roles[CallStateStr] = "CallStateStr"; + roles[SectionName] = "SectionName"; + roles[AccountId] = "AccountId"; + roles[Draft] = "Draft"; + return roles; +} + +void +SmartListModel::setConferenceableFilter(const QString &filter) +{ + beginResetModel(); + auto &accountInfo = LRCInstance::accountModel().getAccountInfo(accountId_); + auto &convModel = accountInfo.conversationModel; + conferenceables_ = convModel->getConferenceableConversations(convUid_, filter); + sectionState_[tr("Calls")] = true; + sectionState_[tr("Contacts")] = true; + endResetModel(); +} + +void +SmartListModel::toggleSection(const QString §ion) +{ + beginResetModel(); + if (section.contains(tr("Calls"))) { + sectionState_[tr("Calls")] ^= true; + } else if (section.contains(tr("Contacts"))) { + sectionState_[tr("Contacts")] ^= true; + } + endResetModel(); +} + +int +SmartListModel::currentUidSmartListModelIndex() +{ + auto convUid = LRCInstance::getCurrentConvUid(); + for (int i = 0; i < rowCount(); i++) { + if (convUid == data(index(i, 0), Role::UID)) + return i; + } + + return -1; +} + +QVariant +SmartListModel::getConversationItemData(const conversation::Info &item, + const account::Info &accountInfo, + int role) const +{ + if (item.participants.size() <= 0) { + return QVariant(); + } + auto &contactModel = accountInfo.contactModel; + switch (role) { + case Role::Picture: { + auto contactImage + = GlobalInstances::pixmapManipulator().decorationRole(item, accountInfo).value(); + return QString::fromLatin1(Utils::QImageToByteArray(contactImage).toBase64().data()); + } + case Role::DisplayName: { + if (!item.participants.isEmpty()) { + auto &contact = contactModel->getContact(item.participants[0]); + return QVariant(Utils::bestNameForContact(contact)); + } + return QVariant(""); + } + case Role::DisplayID: { + if (!item.participants.isEmpty()) { + auto &contact = contactModel->getContact(item.participants[0]); + return QVariant(Utils::bestIdForContact(contact)); + } + return QVariant(""); + } + case Role::Presence: { + if (!item.participants.isEmpty()) { + auto &contact = contactModel->getContact(item.participants[0]); + return QVariant(contact.isPresent); + } + return QVariant(false); + } + case Role::URI: { + if (!item.participants.isEmpty()) { + auto &contact = contactModel->getContact(item.participants[0]); + return QVariant(contact.profileInfo.uri); + } + return QVariant(""); + } + case Role::UnreadMessagesCount: + return QVariant(item.unreadMessages); + case Role::LastInteractionDate: { + if (!item.interactions.empty()) { + auto &date = item.interactions.at(item.lastMessageUid).timestamp; + return QVariant(QString::fromStdString(Utils::formatTimeString(date))); + } + return QVariant(""); + } + case Role::LastInteraction: { + if (!item.interactions.empty()) { + return QVariant(item.interactions.at(item.lastMessageUid).body); + } + return QVariant(""); + } + case Role::LastInteractionType: { + if (!item.interactions.empty()) { + return QVariant( + Utils::toUnderlyingValue(item.interactions.at(item.lastMessageUid).type)); + } + return QVariant(0); + } + case Role::ContactType: { + if (!item.participants.isEmpty()) { + auto &contact = contactModel->getContact(item.participants[0]); + return QVariant(Utils::toUnderlyingValue(contact.profileInfo.type)); + } + return QVariant(0); + } + case Role::UID: + return QVariant(item.uid); + case Role::InCall: { + auto &convInfo = LRCInstance::getConversationFromConvUid(item.uid); + if (!convInfo.uid.isEmpty()) { + auto callModel = LRCInstance::getCurrentCallModel(); + return QVariant(callModel->hasCall(convInfo.callId)); + } + return QVariant(false); + } + case Role::IsAudioOnly: { + auto &convInfo = LRCInstance::getConversationFromConvUid(item.uid); + if (!convInfo.uid.isEmpty()) { + auto call = LRCInstance::getCallInfoForConversation(convInfo); + if (call) { + return QVariant(call->isAudioOnly); + } + } + return QVariant(); + } + case Role::CallStackViewShouldShow: { + auto &convInfo = LRCInstance::getConversationFromConvUid(item.uid); + if (!convInfo.uid.isEmpty()) { + auto callModel = LRCInstance::getCurrentCallModel(); + auto call = callModel->getCall(convInfo.callId); + return QVariant(callModel->hasCall(convInfo.callId) + && ((!call.isOutgoing + && (call.status == lrc::api::call::Status::IN_PROGRESS + || call.status == lrc::api::call::Status::PAUSED)) + || call.isOutgoing)); + } + return QVariant(false); + } + case Role::CallStateStr: { + auto &convInfo = LRCInstance::getConversationFromConvUid(item.uid); + if (!convInfo.uid.isEmpty()) { + auto call = LRCInstance::getCallInfoForConversation(convInfo); + if (call) { + auto statusString = call::to_string(call->status); + return QVariant(statusString); + } + } + return QVariant(); + } + case Role::SectionName: + return QVariant(QString()); + case Role::Draft: { + if (!item.uid.isEmpty()) { + auto draft = LRCInstance::getContentDraft(item.uid, accountInfo.id); + if (!draft.isEmpty()) { + /* + * Pencil Emoji + */ + uint cp = 0x270F; + auto emojiString = QString::fromUcs4(&cp, 1); + return emojiString + LRCInstance::getContentDraft(item.uid, accountInfo.id); + } + } + return QVariant(""); + } + } + return QVariant(); +} + +QModelIndex +SmartListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +SmartListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +SmartListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + auto type = Utils::toEnum(data(index, Role::ContactType).value()); + auto uid = data(index, Role::UID).value(); + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } else if ((type == lrc::api::profile::Type::TEMPORARY && uid.isEmpty())) { + flags &= ~(Qt::ItemIsSelectable); + } + return flags; +} + +void +SmartListModel::setAccount(const QString &accountId) +{ + beginResetModel(); + accountId_ = accountId; + endResetModel(); +} diff --git a/src/smartlistmodel.h b/src/smartlistmodel.h new file mode 100644 index 00000000..8ae7dca2 --- /dev/null +++ b/src/smartlistmodel.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017-2020 by Savoir-faire Linux + * Author: Anthony Léonard + * Author: Andreas Traczyk + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/conversationmodel.h" + +#include + +using namespace lrc::api; + +class SmartListModel : public QAbstractListModel +{ + Q_OBJECT +public: + using AccountInfo = lrc::api::account::Info; + using ConversationInfo = lrc::api::conversation::Info; + using ContactInfo = lrc::api::contact::Info; + + enum class Type { CONVERSATION, CONFERENCE, TRANSFER, COUNT__ }; + + enum Role { + DisplayName = Qt::UserRole + 1, + DisplayID, + Picture, + Presence, + URI, + UnreadMessagesCount, + LastInteractionDate, + LastInteraction, + LastInteractionType, + ContactType, + UID, + ContextMenuOpen, + InCall, + IsAudioOnly, + CallStackViewShouldShow, + CallStateStr, + SectionName, + AccountId, + Draft + }; + + explicit SmartListModel(const QString &accId, + QObject *parent = 0, + SmartListModel::Type listModelType = Type::CONVERSATION, + const QString &convUid = {}); + ~SmartListModel(); + + /* + * QAbstractListModel. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + Q_INVOKABLE void setAccount(const QString &accId); + Q_INVOKABLE void setConferenceableFilter(const QString &filter = {}); + Q_INVOKABLE void toggleSection(const QString §ion); + Q_INVOKABLE int currentUidSmartListModelIndex(); + +private: + QString accountId_; + + QVariant getConversationItemData(const ConversationInfo &item, + const AccountInfo &accountInfo, + int role) const; + /* + * List sectioning. + */ + QString convUid_; + Type listModelType_; + QMap sectionState_; + QMap conferenceables_; +}; \ No newline at end of file diff --git a/src/tintedbuttonimageprovider.h b/src/tintedbuttonimageprovider.h new file mode 100644 index 00000000..384989be --- /dev/null +++ b/src/tintedbuttonimageprovider.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "lrcinstance.h" +#include "utils.h" + +#include +#include +#include +#include +#include + +class TintedButtonImageProvider : public QObject, public QQuickImageProvider +{ +public: + TintedButtonImageProvider() + : QQuickImageProvider(QQuickImageProvider::Pixmap, + QQmlImageProviderBase::ForceAsynchronousImageLoading) + {} + + QPixmap + requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override + { + Q_UNUSED(size); + + QColor tintedColor; + + auto list = id.split('+', QString::SkipEmptyParts); + + if (list.size() == 2) { + QPixmap pixmapToSend(":/images/icons/" + list[0]); + if (!requestedSize.isEmpty()) { + pixmapToSend = pixmapToSend.scaled(requestedSize, Qt::KeepAspectRatio); + } else { + pixmapToSend = pixmapToSend.scaled(QSize(30, 30), Qt::KeepAspectRatio); + } + tintedColor.setNamedColor(list[1]); + + return Utils::generateTintedPixmap(pixmapToSend, tintedColor); + } + + return QPixmap(); + } +}; \ No newline at end of file diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 00000000..a397cab7 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,1082 @@ +/* + * Copyright (C) 2015-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Andreas Traczyk + * Author: Isa Nanic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "utils.h" + +#ifdef Q_OS_WIN +#include +#include +#include +#include +#include +#include +#endif + +#include "globalsystemtray.h" +#include "jamiavatartheme.h" +#include "lrcinstance.h" +#include "pixbufmanipulator.h" +#include "version.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool +Utils::CreateStartupLink(const std::wstring &wstrAppName) +{ +#ifdef Q_OS_WIN + TCHAR szPath[MAX_PATH]; + GetModuleFileName(NULL, szPath, MAX_PATH); + + std::wstring programPath(szPath); + + TCHAR startupPath[MAX_PATH]; + SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath); + + std::wstring linkPath(startupPath); + linkPath += std::wstring(TEXT("\\") + wstrAppName + TEXT(".lnk")); + + return Utils::CreateLink(programPath.c_str(), linkPath.c_str()); +#else + return true; +#endif +} + +bool +Utils::CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink) +{ +#ifdef Q_OS_WIN + HRESULT hres; + IShellLink *psl; + + hres = CoCreateInstance(CLSID_ShellLink, + NULL, + CLSCTX_INPROC_SERVER, + IID_IShellLink, + (LPVOID *) &psl); + if (SUCCEEDED(hres)) { + IPersistFile *ppf; + psl->SetPath(lpszPathObj); + psl->SetArguments(TEXT("--minimized")); + + hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *) &ppf); + if (SUCCEEDED(hres)) { + hres = ppf->Save(lpszPathLink, TRUE); + ppf->Release(); + } + psl->Release(); + } + return hres; +#else + Q_UNUSED(lpszPathObj) + Q_UNUSED(lpszPathLink) + return true; +#endif +} + +void +Utils::DeleteStartupLink(const std::wstring &wstrAppName) +{ +#ifdef Q_OS_WIN + TCHAR startupPath[MAX_PATH]; + SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath); + + std::wstring linkPath(startupPath); + linkPath += std::wstring(TEXT("\\") + wstrAppName + TEXT(".lnk")); + + DeleteFile(linkPath.c_str()); +#endif +} + +bool +Utils::CheckStartupLink(const std::wstring &wstrAppName) +{ +#ifdef Q_OS_WIN + TCHAR startupPath[MAX_PATH]; + SHGetFolderPathW(NULL, CSIDL_STARTUP, NULL, 0, startupPath); + + std::wstring linkPath(startupPath); + linkPath += std::wstring(TEXT("\\") + wstrAppName + TEXT(".lnk")); + return PathFileExists(linkPath.c_str()); +#else + return true; +#endif +} + +const char * +Utils::WinGetEnv(const char *name) +{ +#ifdef Q_OS_WIN + const DWORD buffSize = 65535; + static char buffer[buffSize]; + if (GetEnvironmentVariableA(name, buffer, buffSize)) { + return buffer; + } else { + return 0; + } +#else + return 0; +#endif +} + +void +Utils::removeOldVersions() +{ +#ifdef Q_OS_WIN + /* + * As per: https://git.jami.net/savoirfairelinux/ring-client-windows/issues/429 + * NB: As only the 64-bit version of this application is distributed, we will only + * remove 1. the configuration reg keys for Ring-x64, 2. the startup links for Ring, + * 3. the winsparkle reg keys. The NSIS uninstall reg keys for Jami-x64 are removed + * by the MSI installer. + * Uninstallation of Ring, either 32 or 64 bit, is left to the user. + * The current version of Jami will attempt to kill Ring.exe upon start if a startup + * link is found. + */ + QString node64 = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node"; + QString hkcuSoftwareKey = "HKEY_CURRENT_USER\\Software\\"; + QString uninstKey = "\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"; + QString company = "Savoir-Faire Linux"; + + /* + * 1. Configuration reg keys for Ring-x64. + */ + QSettings(hkcuSoftwareKey + "jami.net\\Ring", QSettings::NativeFormat).remove(""); + QSettings(hkcuSoftwareKey + "ring.cx", QSettings::NativeFormat).remove(""); + /* + * 2. Unset Ring as a startup application. + */ + if (Utils::CheckStartupLink(TEXT("Ring"))) { + qDebug() << "Found startup link for Ring. Removing it and killing Ring.exe."; + Utils::DeleteStartupLink(TEXT("Ring")); + QProcess::execute("taskkill /im Ring.exe /f"); + } + /* + * 3. Remove registry entries for winsparkle(both Jami-x64 and Ring-x64). + */ + QSettings(hkcuSoftwareKey + company, QSettings::NativeFormat).remove(""); +#else + return; +#endif +} + +QString +Utils::GetRingtonePath() +{ +#ifdef Q_OS_WIN + TCHAR workingDirectory[MAX_PATH]; + GetCurrentDirectory(MAX_PATH, workingDirectory); + + QString ringtonePath = QString::fromWCharArray(workingDirectory); + ringtonePath += QStringLiteral("\\ringtones\\default.opus"); + return ringtonePath; +#else + return QString("/usr/local"); +#endif +} + +QString +Utils::GenGUID() +{ +#ifdef Q_OS_WIN + GUID gidReference; + wchar_t *str; + HRESULT hCreateGuid = CoCreateGuid(&gidReference); + if (hCreateGuid == S_OK) { + StringFromCLSID(gidReference, &str); + auto gStr = QString::fromWCharArray(str); + return gStr.remove("{").remove("}").toLower(); + } else + return QString(); +#else + return QString(""); +#endif +} + +QString +Utils::GetISODate() +{ +#ifdef Q_OS_WIN + SYSTEMTIME lt; + GetSystemTime(<); + return QString("%1-%2-%3T%4:%5:%6Z") + .arg(lt.wYear) + .arg(lt.wMonth, 2, 10, QChar('0')) + .arg(lt.wDay, 2, 10, QChar('0')) + .arg(lt.wHour, 2, 10, QChar('0')) + .arg(lt.wMinute, 2, 10, QChar('0')) + .arg(lt.wSecond, 2, 10, QChar('0')); +#else + return QString(); +#endif +} + +void +Utils::InvokeMailto(const QString &subject, const QString &body, const QString &attachement) +{ +#ifdef Q_OS_WIN + HKEY hKey; + LONG lRes = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"mailto", 0, KEY_READ, &hKey); + if (lRes != ERROR_FILE_NOT_FOUND) { + auto addr = QString("mailto:?subject=%1&body=%2").arg(subject).arg(body); + if (not attachement.isEmpty()) + addr += QString("&attachement=%1").arg(attachement); + ShellExecute(nullptr, L"open", addr.toStdWString().c_str(), NULL, NULL, SW_SHOWNORMAL); + } else { + QErrorMessage errorMessage; + errorMessage.showMessage(QObject::tr("No default mail client found")); + } +#endif +} + +QString +Utils::getContactImageString(const QString &accountId, const QString &uid) +{ + return QString::fromLatin1( + Utils::QImageToByteArray( + Utils::conversationPhoto(uid, LRCInstance::getAccountInfo(accountId))) + .toBase64() + .data()); +} + +QImage +Utils::getCirclePhoto(const QImage original, int sizePhoto) +{ + QImage target(sizePhoto, sizePhoto, QImage::Format_ARGB32_Premultiplied); + target.fill(Qt::transparent); + + QPainter painter(&target); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter.setBrush(QBrush(Qt::white)); + auto scaledPhoto = original + .scaled(sizePhoto, + sizePhoto, + Qt::KeepAspectRatioByExpanding, + Qt::SmoothTransformation) + .convertToFormat(QImage::Format_ARGB32_Premultiplied); + int margin = 0; + if (scaledPhoto.width() > sizePhoto) { + margin = (scaledPhoto.width() - sizePhoto) / 2; + } + painter.drawEllipse(0, 0, sizePhoto, sizePhoto); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + painter.drawImage(0, 0, scaledPhoto, margin, 0); + return target; +} + +void +Utils::setStackWidget(QStackedWidget *stack, QWidget *widget) +{ + if (stack->indexOf(widget) != -1 && stack->currentWidget() != widget) { + stack->setCurrentWidget(widget); + } +} + +void +Utils::showSystemNotification(QWidget *widget, + const QString &message, + long delay, + const QString &triggeredAccountId) +{ + QSettings settings("jami.net", "Jami"); + if (settings.value(SettingsKey::enableNotifications).toBool()) { + GlobalSystemTray::instance().setTriggeredAccountId(triggeredAccountId); + GlobalSystemTray::instance().showMessage(message, "", QIcon(":images/jami.png")); + QApplication::alert(widget, delay); + } +} + +void +Utils::showSystemNotification(QWidget *widget, + const QString &sender, + const QString &message, + long delay, + const QString &triggeredAccountId) +{ + QSettings settings("jami.net", "Jami"); + if (settings.value(SettingsKey::enableNotifications).toBool()) { + GlobalSystemTray::instance().setTriggeredAccountId(triggeredAccountId); + GlobalSystemTray::instance().showMessage(sender, message, QIcon(":images/jami.png")); + QApplication::alert(widget, delay); + } +} + +QSize +Utils::getRealSize(QScreen *screen) +{ +#ifdef Q_OS_WIN + DEVMODE dmThisScreen; + ZeroMemory(&dmThisScreen, sizeof(dmThisScreen)); + EnumDisplaySettings((const wchar_t *) screen->name().utf16(), + ENUM_CURRENT_SETTINGS, + (DEVMODE *) &dmThisScreen); + return QSize(dmThisScreen.dmPelsWidth, dmThisScreen.dmPelsHeight); +#else + return {}; +#endif +} + +void +Utils::forceDeleteAsync(const QString &path) +{ + /* + * Keep deleting file until the process holding it let go, + * or the file itself does not exist anymore. + */ + QtConcurrent::run([path] { + QFile file(path); + if (!QFile::exists(path)) + return; + int retries{0}; + while (!file.remove() && retries < 5) { + qDebug().noquote() << "\n" << file.errorString() << "\n"; + QThread::msleep(10); + ++retries; + } + }); +} + +UtilsAdapter & +UtilsAdapter::instance() +{ + static auto instance = new UtilsAdapter; + return *instance; +} + +QString +Utils::getChangeLog() +{ + QString logs; + QFile changeLogFile(":/changelog.html"); + if (!changeLogFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug().noquote() << " Change log file failed to load"; + return {}; + } + QTextStream in(&changeLogFile); + in.setCodec("UTF-8"); + while (!in.atEnd()) { + logs += in.readLine(); + } + return logs; +} + +QString +Utils::getProjectCredits() +{ + QString credits; + QFile projectCreditsFile(":/projectcredits.html"); + if (!projectCreditsFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug().noquote() << " Project Credits failed to load"; + return {}; + } + QTextStream in(&projectCreditsFile); + in.setCodec("UTF-8"); + while (!in.atEnd()) { + QString currentLine = in.readLine(); + if (credits.isEmpty()) { + credits += "

" + + UtilsAdapter::tr("Created by:") + "

"; + } else if (currentLine.contains("Marianne Forget")) { + credits + += "

") + + "" + UtilsAdapter::tr("Artwork by:") + + "

"; + } + credits += currentLine; + } + credits += "

") + + UtilsAdapter::tr("Based on the SFLPhone project") + "

"; + + return credits; +} + +void +Utils::cleanUpdateFiles() +{ + /* + * Delete all logs and msi in the %TEMP% directory before launching. + */ + QString dir = QString(Utils::WinGetEnv("TEMP")); + QDir log_dir(dir, {"jami*.log"}); + for (const QString &filename : log_dir.entryList()) { + log_dir.remove(filename); + } + QDir msi_dir(dir, {"jami*.msi"}); + for (const QString &filename : msi_dir.entryList()) { + msi_dir.remove(filename); + } + QDir version_dir(dir, {"version"}); + for (const QString &filename : version_dir.entryList()) { + version_dir.remove(filename); + } +} + +void +Utils::checkForUpdates(bool withUI, QWidget *parent) +{ + Q_UNUSED(withUI) + Q_UNUSED(parent) + /* + * TODO: check update logic. + */ +} + +void +Utils::applyUpdates(bool updateToBeta, QWidget *parent) +{ + Q_UNUSED(updateToBeta) + Q_UNUSED(parent) + /* + * TODO: update logic. + */ +} + +inline QString +removeEndlines(const QString &str) +{ + QString trimmed(str); + trimmed.remove(QChar('\n')); + trimmed.remove(QChar('\r')); + return trimmed; +} + +QString +Utils::bestIdForConversation(const lrc::api::conversation::Info &conv, + const lrc::api::ConversationModel &model) +{ + auto contact = model.owner.contactModel->getContact(conv.participants[0]); + if (!contact.registeredName.isEmpty()) { + return removeEndlines(contact.registeredName); + } + return removeEndlines(contact.profileInfo.uri); +} + +QString +Utils::bestIdForAccount(const lrc::api::account::Info &account) +{ + if (!account.registeredName.isEmpty()) { + return removeEndlines(account.registeredName); + } + return removeEndlines(account.profileInfo.uri); +} + +QString +Utils::bestNameForAccount(const lrc::api::account::Info &account) +{ + if (account.profileInfo.alias.isEmpty()) { + return bestIdForAccount(account); + } + return account.profileInfo.alias; +} + +QString +Utils::bestIdForContact(const lrc::api::contact::Info &contact) +{ + if (!contact.registeredName.isEmpty()) { + return removeEndlines(contact.registeredName); + } + return removeEndlines(contact.profileInfo.uri); +} + +QString +Utils::bestNameForContact(const lrc::api::contact::Info &contact) +{ + auto alias = removeEndlines(contact.profileInfo.alias); + if (alias.length() == 0) { + return bestIdForContact(contact); + } + return alias; +} + +QString +Utils::bestNameForConversation(const lrc::api::conversation::Info &conv, + const lrc::api::ConversationModel &model) +{ + try { + auto contact = model.owner.contactModel->getContact(conv.participants[0]); + auto alias = removeEndlines(contact.profileInfo.alias); + if (alias.length() == 0) { + return bestIdForConversation(conv, model); + } + return alias; + } catch (...) { + } + return {}; +} + +/* + * Returns empty string if only infoHash is available, second best identifier otherwise. + */ +QString +Utils::secondBestNameForAccount(const lrc::api::account::Info &account) +{ + auto alias = removeEndlines(account.profileInfo.alias); + auto registeredName = removeEndlines(account.registeredName); + auto infoHash = account.profileInfo.uri; + + if (!alias.length() == 0) { + /* + * If alias exists. + */ + if (!registeredName.length() == 0) { + /* + * If registeredName exists. + */ + return registeredName; + } else { + return infoHash; + } + } else { + if (!registeredName.length() == 0) { + /* + * If registeredName exists. + */ + return infoHash; + } else { + return ""; + } + } +} + +lrc::api::profile::Type +Utils::profileType(const lrc::api::conversation::Info &conv, + const lrc::api::ConversationModel &model) +{ + try { + auto contact = model.owner.contactModel->getContact(conv.participants[0]); + return contact.profileInfo.type; + } catch (...) { + return lrc::api::profile::Type::INVALID; + } +} + +std::string +Utils::formatTimeString(const std::time_t ×tamp) +{ + std::time_t now = std::time(nullptr); + char interactionDay[64]; + char nowDay[64]; + std::strftime(interactionDay, sizeof(interactionDay), "%D", std::localtime(×tamp)); + std::strftime(nowDay, sizeof(nowDay), "%D", std::localtime(&now)); + if (std::string(interactionDay) == std::string(nowDay)) { + char interactionTime[64]; + std::strftime(interactionTime, sizeof(interactionTime), "%R", std::localtime(×tamp)); + return interactionTime; + } else { + return interactionDay; + } +} + +bool +Utils::isInteractionGenerated(const lrc::api::interaction::Type &type) +{ + return type == lrc::api::interaction::Type::CALL + || type == lrc::api::interaction::Type::CONTACT; +} + +bool +Utils::isContactValid(const QString &contactUid, const lrc::api::ConversationModel &model) +{ + auto contact = model.owner.contactModel->getContact(contactUid); + return (contact.profileInfo.type == lrc::api::profile::Type::PENDING + || contact.profileInfo.type == lrc::api::profile::Type::TEMPORARY + || contact.profileInfo.type == lrc::api::profile::Type::RING + || contact.profileInfo.type == lrc::api::profile::Type::SIP) + && !contact.profileInfo.uri.isEmpty(); +} + +bool +Utils::getReplyMessageBox(QWidget *widget, const QString &title, const QString &text) +{ + if (QMessageBox::question(widget, title, text, QMessageBox::Yes | QMessageBox::No) + == QMessageBox::Yes) + return true; + return false; +} + +QImage +Utils::conversationPhoto(const QString &convUid, + const lrc::api::account::Info &accountInfo, + bool filtered) +{ + auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountInfo.id, filtered); + if (!convInfo.uid.isEmpty()) { + return GlobalInstances::pixmapManipulator() + .decorationRole(convInfo, accountInfo) + .value(); + } + return QImage(); +} + +QColor +Utils::getAvatarColor(const QString &canonicalUri) +{ + if (canonicalUri.isEmpty()) { + return JamiAvatarTheme::defaultAvatarColor_; + } + auto h = QString( + QCryptographicHash::hash(canonicalUri.toLocal8Bit(), QCryptographicHash::Md5).toHex()); + if (h.isEmpty() || h.isNull()) { + return JamiAvatarTheme::defaultAvatarColor_; + } + auto colorIndex = std::string("0123456789abcdef").find(h.at(0).toLatin1()); + return JamiAvatarTheme::avatarColors_[colorIndex]; +} + +/* Generate a QImage representing a dummy user avatar, when user doesn't provide it. + * Current rendering is a flat colored circle with a centered letter. + * The color of the letter is computed from the circle color to be visible whaterver be the circle color. + */ +QImage +Utils::fallbackAvatar(const QSize size, const QString &canonicalUriStr, const QString &letterStr) +{ + /* + * We start with a transparent avatar. + */ + QImage avatar(size, QImage::Format_ARGB32); + avatar.fill(Qt::transparent); + + /* + * We pick a color based on the passed character. + */ + QColor avColor = getAvatarColor(canonicalUriStr); + + /* + * We draw a circle with this color. + */ + QPainter painter(&avatar); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter.setPen(Qt::transparent); + painter.setBrush(avColor.lighter(110)); + painter.drawEllipse(avatar.rect()); + + /* + * If a letter was passed, then we paint a letter in the circle, + * otherwise we draw the default avatar icon. + */ + QString letterStrCleaned(letterStr); + letterStrCleaned.remove(QRegExp("[\\n\\t\\r]")); + if (!letterStr.isEmpty()) { + auto unicode = letterStr.toUcs4().at(0); + if (unicode >= 0x1F000 && unicode <= 0x1FFFF) { + /* + * Is Emoticon. + */ + auto letter = QString::fromUcs4(&unicode, 1); + QFont font(QStringLiteral("Segoe UI Emoji"), avatar.height() / 2.66667, QFont::Medium); + painter.setFont(font); + QRect emojiRect(avatar.rect()); + emojiRect.moveTop(-6); + painter.drawText(emojiRect, letter, QTextOption(Qt::AlignCenter)); + } else if (unicode >= 0x0000 && unicode <= 0x00FF) { + /* + * Is Basic Latin. + */ + auto letter = letterStr.at(0).toUpper(); + QFont font("Arial", avatar.height() / 2.66667, QFont::Medium); + painter.setFont(font); + painter.setPen(Qt::white); + painter.drawText(avatar.rect(), QString(letter), QTextOption(Qt::AlignCenter)); + } else { + auto letter = QString::fromUcs4(&unicode, 1); + QFont font("Arial", avatar.height() / 2.66667, QFont::Medium); + painter.setFont(font); + painter.setPen(Qt::white); + painter.drawText(avatar.rect(), QString(letter), QTextOption(Qt::AlignCenter)); + } + } else { + QRect overlayRect = avatar.rect(); + qreal margin = (0.05 * overlayRect.width()); + overlayRect.moveLeft(overlayRect.left() + margin * 0.5); + overlayRect.moveTop(overlayRect.top() + margin * 0.5); + overlayRect.setWidth(overlayRect.width() - margin); + overlayRect.setHeight(overlayRect.height() - margin); + painter.drawPixmap(overlayRect, QPixmap(":/images/default_avatar_overlay.svg")); + } + + return avatar; +} + +QImage +Utils::fallbackAvatar(const QSize size, const std::string &alias, const std::string &uri) +{ + return fallbackAvatar(size, QString::fromStdString(uri), QString::fromStdString(alias)); +} + +QByteArray +Utils::QImageToByteArray(QImage image) +{ + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + image.save(&buffer, "PNG"); + return ba; +} + +QImage +Utils::cropImage(const QImage &img) +{ + QRect rect; + auto w = img.width(); + auto h = img.height(); + if (w > h) { + return img.copy({(w - h) / 2, 0, h, h}); + } + return img.copy({0, (h - w) / 2, w, w}); +} + +QPixmap +Utils::pixmapFromSvg(const QString &svg_resource, const QSize &size) +{ + QSvgRenderer svgRenderer(svg_resource); + QPixmap pixmap(size); + pixmap.fill(Qt::transparent); + QPainter pixPainter(&pixmap); + svgRenderer.render(&pixPainter); + return pixmap; +} + +QImage +Utils::setupQRCode(QString ringID, int margin) +{ + auto rcode = QRcode_encodeString(ringID.toStdString().c_str(), + 0, // Let the version be decided by libqrencode + QR_ECLEVEL_L, // Lowest level of error correction + QR_MODE_8, // 8-bit data mode + 1); + if (not rcode) { + qWarning() << "Failed to generate QR code: " << strerror(errno); + return QImage(); + } + + int qrwidth = rcode->width + margin * 2; + QImage result(QSize(qrwidth, qrwidth), QImage::Format_Mono); + QPainter painter; + painter.begin(&result); + painter.setClipRect(QRect(0, 0, qrwidth, qrwidth)); + painter.setPen(QPen(Qt::black, 0.1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin)); + painter.setBrush(Qt::black); + painter.fillRect(QRect(0, 0, qrwidth, qrwidth), Qt::white); + unsigned char *p; + p = rcode->data; + for (int y = 0; y < rcode->width; y++) { + unsigned char *row = (p + (y * rcode->width)); + for (int x = 0; x < rcode->width; x++) { + if (*(row + x) & 0x1) { + painter.drawRect(margin + x, margin + y, 1, 1); + } + } + } + painter.end(); + QRcode_free(rcode); + return result; +} + +float +Utils::getCurrentScalingRatio() +{ + return CURRENT_SCALING_RATIO; +} + +void +Utils::setCurrentScalingRatio(float ratio) +{ + CURRENT_SCALING_RATIO = ratio; +} + +QString +Utils::formattedTime(int duration) +{ + if (duration == 0) + return {}; + std::string formattedString; + auto minutes = duration / 60; + auto seconds = duration % 60; + if (minutes > 0) { + formattedString += std::to_string(minutes) + ":"; + if (formattedString.length() == 2) { + formattedString = "0" + formattedString; + } + } else { + formattedString += "00:"; + } + if (seconds < 10) + formattedString += "0"; + formattedString += std::to_string(seconds); + return QString::fromStdString(formattedString); +} + +QByteArray +Utils::QByteArrayFromFile(const QString &filename) +{ + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { + return file.readAll(); + } else { + qDebug() << "can't open file"; + return QByteArray(); + } +} + +QPixmap +Utils::generateTintedPixmap(const QString &filename, QColor color) +{ + QPixmap px(filename); + QImage tmpImage = px.toImage(); + for (int y = 0; y < tmpImage.height(); y++) { + for (int x = 0; x < tmpImage.width(); x++) { + color.setAlpha(tmpImage.pixelColor(x, y).alpha()); + tmpImage.setPixelColor(x, y, color); + } + } + return QPixmap::fromImage(tmpImage); +} + +QPixmap +Utils::generateTintedPixmap(const QPixmap &pix, QColor color) +{ + QPixmap px = pix; + QImage tmpImage = px.toImage(); + for (int y = 0; y < tmpImage.height(); y++) { + for (int x = 0; x < tmpImage.width(); x++) { + color.setAlpha(tmpImage.pixelColor(x, y).alpha()); + tmpImage.setPixelColor(x, y, color); + } + } + return QPixmap::fromImage(tmpImage); +} + +QImage +Utils::scaleAndFrame(const QImage photo, const QSize &size) +{ + return photo.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); +} + +QImage +Utils::accountPhoto(const lrc::api::account::Info &accountInfo, const QSize &size) +{ + QImage photo; + if (!accountInfo.profileInfo.avatar.isEmpty()) { + QByteArray ba = accountInfo.profileInfo.avatar.toLocal8Bit(); + photo = GlobalInstances::pixmapManipulator().personPhoto(ba, nullptr).value(); + } else { + auto bestId = bestIdForAccount(accountInfo); + auto bestName = bestNameForAccount(accountInfo); + QString letterStr = bestId == bestName ? QString() : bestName; + QString prefix = accountInfo.profileInfo.type == lrc::api::profile::Type::RING ? "ring:" + : "sip:"; + photo = fallbackAvatar(size, prefix + accountInfo.profileInfo.uri, letterStr); + } + return scaleAndFrame(photo, size); +} + +QString +Utils::humanFileSize(qint64 fileSize) +{ + float fileSizeF = static_cast(fileSize); + float thresh = 1024; + + if (abs(fileSizeF) < thresh) { + return QString::number(fileSizeF) + " B"; + } + QString units[] = {"kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}; + int unit_position = -1; + do { + fileSizeF /= thresh; + ++unit_position; + } while (abs(fileSizeF) >= thresh && unit_position < units->size() - 1); + /* + * Round up to two decimal. + */ + fileSizeF = roundf(fileSizeF * 100) / 100; + return QString::number(fileSizeF) + " " + units[unit_position]; +} + +const QString +UtilsAdapter::getBestName(const QString &accountId, const QString &uid) +{ + auto convModel = LRCInstance::getAccountInfo(accountId).conversationModel.get(); + return Utils::bestNameForConversation(LRCInstance::getConversationFromConvUid(uid, accountId), + *convModel); +} + +const QString +UtilsAdapter::getBestId(const QString &accountId, const QString &uid) +{ + auto convModel = LRCInstance::getAccountInfo(accountId).conversationModel.get(); + return Utils::bestIdForConversation(LRCInstance::getConversationFromConvUid(uid, accountId), + *convModel); +} + +int +UtilsAdapter::getTotalUnreadMessages() +{ + int totalUnreadMessages{0}; + if (LRCInstance::getCurrentAccountInfo().profileInfo.type != lrc::api::profile::Type::SIP) { + auto convModel = LRCInstance::getCurrentConversationModel(); + auto ringConversations = convModel->getFilteredConversations(lrc::api::profile::Type::RING); + std::for_each(ringConversations.begin(), + ringConversations.end(), + [&totalUnreadMessages, convModel](const auto &conversation) { + totalUnreadMessages += conversation.unreadMessages; + }); + } + return totalUnreadMessages; +} + +int +UtilsAdapter::getTotalPendingRequest() +{ + auto &accountInfo = LRCInstance::getCurrentAccountInfo(); + return accountInfo.contactModel->pendingRequestCount(); +} + +void +UtilsAdapter::setConversationFilter(const QString &filter) +{ + LRCInstance::getCurrentConversationModel()->setFilter(filter); +} + +void +UtilsAdapter::clearConversationHistory(const QString &accountId, const QString &uid) +{ + LRCInstance::getAccountInfo(accountId).conversationModel->clearHistory(uid); +} + +void +UtilsAdapter::removeConversation(const QString &accountId, const QString &uid, bool banContact) +{ + LRCInstance::getAccountInfo(accountId).conversationModel->removeConversation(uid, banContact); +} + +const QString +UtilsAdapter::getCurrAccId() +{ + return LRCInstance::getCurrAccId(); +} + +const QStringList +UtilsAdapter::getCurrAccList() +{ + return LRCInstance::accountModel().getAccountList(); +} + +int +UtilsAdapter::getAccountListSize() +{ + return getCurrAccList().size(); +} + +void +UtilsAdapter::setCurrentCall(const QString &accountId, const QString &convUid) +{ + auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId); + auto &accInfo = LRCInstance::getAccountInfo(accountId); + accInfo.callModel->setCurrentCall(convInfo.callId); +} + +void +UtilsAdapter::startPreviewing(bool force) +{ + LRCInstance::renderer()->startPreviewing(force); +} + +void +UtilsAdapter::stopPreviewing() +{ + if (!LRCInstance::hasVideoCall()) { + LRCInstance::renderer()->stopPreviewing(); + } +} + +bool +UtilsAdapter::hasVideoCall() +{ + return LRCInstance::hasVideoCall(); +} + +const QString +UtilsAdapter::getCallId(const QString &accountId, const QString &convUid) +{ + auto convInfo = LRCInstance::getConversationFromConvUid(convUid, accountId); + if (convInfo.uid.isEmpty()) { + return ""; + } + + auto call = LRCInstance::getCallInfoForConversation(convInfo, false); + if (!call) { + return ""; + } + + return call->id; +} + +// returns true if name is valid registered name +bool +UtilsAdapter::validateRegNameForm(const QString ®Name) +{ + QRegularExpression regExp(" "); + + if (regName.size() > 2 && !regName.contains(regExp)) { + return true; + + } else { + return false; + } +} + +QString +UtilsAdapter::getStringUTF8(QString string) +{ + return string.toUtf8(); +} + +QString +UtilsAdapter::getRecordQualityString(int value) +{ + return value ? QString::number(static_cast(value) / 100, 'f', 1) + " Mbps" : "Default"; +} + +QString +UtilsAdapter::getCurrentPath() +{ + return QDir::currentPath(); +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 00000000..64db4517 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,562 @@ +/* + * Copyright (C) 2015-2020 by Savoir-faire Linux + * Author: Edric Ladent Milaret + * Author: Andreas Traczyk + * Author: Isa Nanic + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "version.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#include +#undef ERROR +#else +#define LPCWSTR char * +#endif + +#include "api/account.h" +#include "api/contact.h" +#include "api/contactmodel.h" +#include "api/conversationmodel.h" + +static const QSize IMAGE_SIZE{128, 128}; +static float CURRENT_SCALING_RATIO{1.0}; + +#ifdef BETA +static constexpr bool isBeta = true; +#else +static constexpr bool isBeta = false; +#endif + +namespace Utils { + +/* + * Qml type register. + */ +#define QML_REGISTERSINGLETONTYPE(T, MAJ, MIN) \ + qmlRegisterSingletonType("net.jami.Models", \ + MAJ, \ + MIN, \ + #T, \ + [](QQmlEngine *e, QJSEngine *se) -> QObject * { \ + Q_UNUSED(e); \ + Q_UNUSED(se); \ + T *obj = new T(); \ + return obj; \ + }); +#define QML_REGISTERSINGLETONTYPE_WITH_INSTANCE(T, MAJ, MIN) \ + qmlRegisterSingletonType("net.jami.Models", \ + MAJ, \ + MIN, \ + #T, \ + [](QQmlEngine *e, QJSEngine *se) -> QObject * { \ + Q_UNUSED(e); \ + Q_UNUSED(se); \ + return &(T::instance()); \ + }); + +#define QML_REGISTERSINGLETONTYPE_URL(URL, T, MAJ, MIN) \ + qmlRegisterSingletonType(QUrl(URL), "net.jami.Models", MAJ, MIN, #T); + +#define QML_REGISTERTYPE(T, MAJ, MIN) qmlRegisterType("net.jami.Models", MAJ, MIN, #T); + +#define QML_REGISTERNAMESPACE(T, NAME, MAJ, MIN) \ + qmlRegisterUncreatableMetaObject(T, "net.jami.Models", MAJ, MIN, NAME, "") + +#define QML_REGISTERUNCREATABLE(T, MAJ, MIN) \ + qmlRegisterUncreatableType("net.jami.Models", \ + MAJ, \ + MIN, \ + #T, \ + "Don't try to add to a qml definition of " #T); + +#define QML_REGISTERUNCREATABLE_IN_NAMESPACE(T, NAMESPACE, MAJ, MIN) \ + qmlRegisterUncreatableType("net.jami.Models", \ + MAJ, \ + MIN, \ + #T, \ + "Don't try to add to a qml definition of " #T); + +/* + * System. + */ +bool CreateStartupLink(const std::wstring &wstrAppName); +void DeleteStartupLink(const std::wstring &wstrAppName); +bool CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszPathLink); +bool CheckStartupLink(const std::wstring &wstrAppName); +void removeOldVersions(); +const char *WinGetEnv(const char *name); +QString GetRingtonePath(); +QString GenGUID(); +QString GetISODate(); +void InvokeMailto(const QString &subject, + const QString &body, + const QString &attachement = QString()); +void setStackWidget(QStackedWidget *stack, QWidget *widget); +void showSystemNotification(QWidget *widget, + const QString &message, + long delay = 5000, + const QString &triggeredAccountId = ""); +void showSystemNotification(QWidget *widget, + const QString &sender, + const QString &message, + long delay = 5000, + const QString &triggeredAccountId = ""); +QSize getRealSize(QScreen *screen); +void forceDeleteAsync(const QString &path); +QString getChangeLog(); +QString getProjectCredits(); +float getCurrentScalingRatio(); +void setCurrentScalingRatio(float ratio); + +/* + * Updates. + */ +void cleanUpdateFiles(); +void checkForUpdates(bool withUI, QWidget *parent = nullptr); +void applyUpdates(bool updateToBeta, QWidget *parent = nullptr); + +/* + * Names. + */ +QString bestIdForConversation(const lrc::api::conversation::Info &conv, + const lrc::api::ConversationModel &model); +QString bestIdForAccount(const lrc::api::account::Info &account); +QString bestNameForAccount(const lrc::api::account::Info &account); +QString bestIdForContact(const lrc::api::contact::Info &contact); +QString bestNameForContact(const lrc::api::contact::Info &contact); +QString bestNameForConversation(const lrc::api::conversation::Info &conv, + const lrc::api::ConversationModel &model); +/* + * Returns empty string if only infoHash is available. + */ +QString secondBestNameForAccount(const lrc::api::account::Info &account); +lrc::api::profile::Type profileType(const lrc::api::conversation::Info &conv, + const lrc::api::ConversationModel &model); + +/* + * Interactions. + */ +std::string formatTimeString(const std::time_t ×tamp); +bool isInteractionGenerated(const lrc::api::interaction::Type &interaction); +bool isContactValid(const QString &contactUid, const lrc::api::ConversationModel &model); +bool getReplyMessageBox(QWidget *widget, const QString &title, const QString &text); + +/* + * Image. + */ +QString getContactImageString(const QString &accountId, const QString &uid); +QImage getCirclePhoto(const QImage original, int sizePhoto); +QImage conversationPhoto(const QString &convUid, + const lrc::api::account::Info &accountInfo, + bool filtered = false); +QColor getAvatarColor(const QString &canonicalUri); +QImage fallbackAvatar(const QSize size, + const QString &canonicalUriStr, + const QString &letterStr = QString()); +QImage fallbackAvatar(const QSize size, const std::string &alias, const std::string &uri); +QByteArray QImageToByteArray(QImage image); +QByteArray QByteArrayFromFile(const QString &filename); +QPixmap generateTintedPixmap(const QString &filename, QColor color); +QPixmap generateTintedPixmap(const QPixmap &pix, QColor color); +QImage scaleAndFrame(const QImage photo, const QSize &size = IMAGE_SIZE); +QImage accountPhoto(const lrc::api::account::Info &accountInfo, const QSize &size = IMAGE_SIZE); +QImage cropImage(const QImage &img); +QPixmap pixmapFromSvg(const QString &svg_resource, const QSize &size); +QImage setupQRCode(QString ringID, int margin); + +/* + * Rounded corner. + */ +template +void +fillRoundRectPath(QPainter &painter, + const T &brushType, + const QRect &rectToDraw, + qreal cornerRadius, + int xTransFormOffset = 0, + int yTransFormOffset = 0) +{ + QBrush brush(brushType); + brush.setTransform(QTransform::fromTranslate(rectToDraw.x() + xTransFormOffset, + rectToDraw.y() + yTransFormOffset)); + QPainterPath painterPath; + painterPath.addRoundRect(rectToDraw, cornerRadius); + painter.fillPath(painterPath, brush); +} + +/* + * Time. + */ +QString formattedTime(int seconds); + +/* + * Byte to human readable size. + */ +QString humanFileSize(qint64 fileSize); + +/* + * Device plug or unplug enum. + */ +enum class DevicePlugStatus { Plugged, Unplugged, Unchanged }; + +class OneShotDisconnectConnection : public QObject +{ + Q_OBJECT + +public: + explicit OneShotDisconnectConnection(const QObject *sender, + const char *signal, + QMetaObject::Connection *connection, + QObject *parent = nullptr) + : QObject(parent) + { + connection_ = connection; + disconnectConnection_ = new QMetaObject::Connection; + *disconnectConnection_ = QObject::connect(sender, + signal, + this, + SLOT(slotOneShotDisconnectConnection())); + } + ~OneShotDisconnectConnection() + { + if (!connection_) { + delete connection_; + } + if (!disconnectConnection_) { + delete disconnectConnection_; + } + if (!this) { + delete this; + } + } + +public slots: + void + slotOneShotDisconnectConnection() + { + if (connection_) { + QObject::disconnect(*connection_); + delete connection_; + } + if (disconnectConnection_) { + QObject::disconnect(*disconnectConnection_); + delete disconnectConnection_; + } + delete this; + } + +private: + QMetaObject::Connection *connection_; + QMetaObject::Connection *disconnectConnection_; +}; + +template +void +oneShotConnect(const typename QtPrivate::FunctionPointer::Object *sender, + Func1 signal, + Func2 slot) +{ + QMetaObject::Connection *const connection = new QMetaObject::Connection; + *connection = QObject::connect(sender, signal, slot); + QMetaObject::Connection *const disconnectConnection = new QMetaObject::Connection; + *disconnectConnection = QObject::connect(sender, signal, [connection, disconnectConnection] { + if (connection) { + QObject::disconnect(*connection); + delete connection; + } + if (disconnectConnection) { + QObject::disconnect(*disconnectConnection); + delete disconnectConnection; + } + }); +} + +template +void +oneShotConnect(const typename QtPrivate::FunctionPointer::Object *sender, + Func1 signal, + const typename QtPrivate::FunctionPointer::Object *receiver, + Func2 slot) +{ + QMetaObject::Connection *const connection = new QMetaObject::Connection; + *connection = QObject::connect(sender, signal, receiver, slot); + QMetaObject::Connection *const disconnectConnection = new QMetaObject::Connection; + *disconnectConnection = QObject::connect(sender, signal, [connection, disconnectConnection] { + if (connection) { + QObject::disconnect(*connection); + delete connection; + } + if (disconnectConnection) { + QObject::disconnect(*disconnectConnection); + delete disconnectConnection; + } + }); +} + +inline void +oneShotConnect(const QObject *sender, const char *signal, const QObject *receiver, const char *slot) +{ + QMetaObject::Connection *const connection = new QMetaObject::Connection; + *connection = QObject::connect(sender, signal, receiver, slot); + OneShotDisconnectConnection *disconnectConnection = new OneShotDisconnectConnection(sender, + signal, + connection); + Q_UNUSED(disconnectConnection) +} + +template +class Blocker +{ + T *blocked; + bool previous; + +public: + Blocker(T *blocked) + : blocked(blocked) + , previous(blocked->blockSignals(true)) + {} + ~Blocker() { blocked->blockSignals(previous); } + T * + operator->() + { + return blocked; + } +}; + +template +inline Blocker +whileBlocking(T *blocked) +{ + return Blocker(blocked); +} + +template +void +setElidedText(T *object, + const QString &text, + Qt::TextElideMode mode = Qt::ElideMiddle, + int padding = 32) +{ + QFontMetrics metrics(object->font()); + QString clippedText = metrics.elidedText(text, mode, object->width() - padding); + object->setText(clippedText); +} + +template +constexpr inline + typename std::enable_if::value, typename std::underlying_type::type>::type + toUnderlyingValue(E e) noexcept +{ + return static_cast::type>(e); +} + +template +constexpr inline + typename std::enable_if::value && std::is_integral::value, E>::type + toEnum(T value) noexcept +{ + return static_cast(value); +} +} // namespace Utils + +class UtilsAdapter : public QObject +{ + Q_OBJECT +public: + explicit UtilsAdapter(QObject *parent = nullptr) + : QObject(parent) + { + clipboard_ = QApplication::clipboard(); + } + ~UtilsAdapter() {} + + ///Singleton + static UtilsAdapter &instance(); + + Q_INVOKABLE const QString + getChangeLog() + { + return Utils::getChangeLog(); + } + + Q_INVOKABLE const QString + getProjectCredits() + { + return Utils::getProjectCredits(); + } + + Q_INVOKABLE const QString + getVersionStr() + { + return QString(VERSION_STRING); + } + + Q_INVOKABLE void + setText(QString text) + { + clipboard_->setText(text, QClipboard::Clipboard); + } + + Q_INVOKABLE const QString + qStringFromFile(const QString &filename) + { + return Utils::QByteArrayFromFile(filename); + } + + Q_INVOKABLE const QString + getStyleSheet(const QString &name, const QString &source) + { + auto simplifiedCSS = source.simplified().replace("'", "\""); + QString s = QString::fromLatin1("(function() {" + " var node = document.createElement('style');" + " node.id = '%1';" + " node.innerHTML = '%2';" + " document.head.appendChild(node);" + "})()") + .arg(name) + .arg(simplifiedCSS); + return s; + } + + Q_INVOKABLE const QString + getCachePath() + { + QDir dataDir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); + dataDir.cdUp(); + return dataDir.absolutePath() + "/jami"; + } + Q_INVOKABLE bool + createStartupLink() + { + return Utils::CreateStartupLink(L"Jami"); + } + Q_INVOKABLE QString + GetRingtonePath() + { + return Utils::GetRingtonePath(); + } + Q_INVOKABLE bool + checkStartupLink() + { + return Utils::CheckStartupLink(L"Jami"); + } + + Q_INVOKABLE const QString + getContactImageString(const QString &accountId, const QString &uid) + { + return Utils::getContactImageString(accountId, uid); + } + + Q_INVOKABLE void removeConversation(const QString &accountId, + const QString &uid, + bool banContact = false); + Q_INVOKABLE void clearConversationHistory(const QString &accountId, const QString &uid); + Q_INVOKABLE void setConversationFilter(const QString &filter); + Q_INVOKABLE int getTotalUnreadMessages(); + Q_INVOKABLE int getTotalPendingRequest(); + Q_INVOKABLE const QString getBestName(const QString &accountId, const QString &uid); + Q_INVOKABLE const QString getBestId(const QString &accountId, const QString &uid); + + Q_INVOKABLE const QString getCurrAccId(); + Q_INVOKABLE const QStringList getCurrAccList(); + Q_INVOKABLE int getAccountListSize(); + Q_INVOKABLE void setCurrentCall(const QString &accountId, const QString &convUid); + Q_INVOKABLE void startPreviewing(bool force); + Q_INVOKABLE void stopPreviewing(); + Q_INVOKABLE bool hasVideoCall(); + Q_INVOKABLE const QString getCallId(const QString &accountId, const QString &convUid); + Q_INVOKABLE QString getStringUTF8(QString string); + Q_INVOKABLE bool validateRegNameForm(const QString ®Name); + Q_INVOKABLE QString getRecordQualityString(int value); + Q_INVOKABLE QString getCurrentPath(); + Q_INVOKABLE QString + stringSimplifier(QString input) + { + return input.simplified(); + } + + Q_INVOKABLE QString + toNativeSeparators(QString inputDir) + { + return QDir::toNativeSeparators(inputDir); + } + + Q_INVOKABLE QString + toFileInfoName(QString inputFileName) + { + QFileInfo fi(inputFileName); + return fi.fileName(); + } + + Q_INVOKABLE QString + toFileAbsolutepath(QString inputFileName) + { + QFileInfo fi(inputFileName); + return fi.absolutePath(); + } + + Q_INVOKABLE QString + getAbsPath(QString path) + { +#ifdef Q_OS_WIN + return path.replace("file:///", "").replace("\n", "").replace("\r", ""); +#else + return path.replace("file:///", "/").replace("\n", "").replace("\r", ""); +#endif + } + + Q_INVOKABLE QString + getCroppedImageBase64FromFile(QString fileName, int size) + { + auto image = Utils::cropImage(QImage(fileName)); + auto croppedImage = image.scaled(size, + size, + Qt::KeepAspectRatioByExpanding, + Qt::SmoothTransformation); + return QString::fromLatin1(Utils::QImageToByteArray(croppedImage).toBase64().data()); + } + +private: + QClipboard *clipboard_; +}; +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) +Q_DECLARE_METATYPE(UtilsAdapter *) +#endif diff --git a/src/version.h b/src/version.h new file mode 100644 index 00000000..d5eea6dc --- /dev/null +++ b/src/version.h @@ -0,0 +1,57 @@ +#pragma once + +#define BUILD_YEAR_CH0 (__DATE__[ 7]) +#define BUILD_YEAR_CH1 (__DATE__[ 8]) +#define BUILD_YEAR_CH2 (__DATE__[ 9]) +#define BUILD_YEAR_CH3 (__DATE__[10]) + +#define BUILD_MONTH_IS_JAN (__DATE__[0] == 'J' && __DATE__[1] == 'a' && __DATE__[2] == 'n') +#define BUILD_MONTH_IS_FEB (__DATE__[0] == 'F') +#define BUILD_MONTH_IS_MAR (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'r') +#define BUILD_MONTH_IS_APR (__DATE__[0] == 'A' && __DATE__[1] == 'p') +#define BUILD_MONTH_IS_MAY (__DATE__[0] == 'M' && __DATE__[1] == 'a' && __DATE__[2] == 'y') +#define BUILD_MONTH_IS_JUN (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'n') +#define BUILD_MONTH_IS_JUL (__DATE__[0] == 'J' && __DATE__[1] == 'u' && __DATE__[2] == 'l') +#define BUILD_MONTH_IS_AUG (__DATE__[0] == 'A' && __DATE__[1] == 'u') +#define BUILD_MONTH_IS_SEP (__DATE__[0] == 'S') +#define BUILD_MONTH_IS_OCT (__DATE__[0] == 'O') +#define BUILD_MONTH_IS_NOV (__DATE__[0] == 'N') +#define BUILD_MONTH_IS_DEC (__DATE__[0] == 'D') + +#define BUILD_MONTH_CH0 \ + ((BUILD_MONTH_IS_OCT || BUILD_MONTH_IS_NOV || BUILD_MONTH_IS_DEC) ? '1' : '0') + +#define BUILD_MONTH_CH1 \ + ( \ + (BUILD_MONTH_IS_JAN) ? '1' : \ + (BUILD_MONTH_IS_FEB) ? '2' : \ + (BUILD_MONTH_IS_MAR) ? '3' : \ + (BUILD_MONTH_IS_APR) ? '4' : \ + (BUILD_MONTH_IS_MAY) ? '5' : \ + (BUILD_MONTH_IS_JUN) ? '6' : \ + (BUILD_MONTH_IS_JUL) ? '7' : \ + (BUILD_MONTH_IS_AUG) ? '8' : \ + (BUILD_MONTH_IS_SEP) ? '9' : \ + (BUILD_MONTH_IS_OCT) ? '0' : \ + (BUILD_MONTH_IS_NOV) ? '1' : \ + (BUILD_MONTH_IS_DEC) ? '2' : \ + /* error default */ '?' \ + ) + +#define BUILD_DAY_CH0 ((__DATE__[4] >= '0') ? (__DATE__[4]) : '0') +#define BUILD_DAY_CH1 (__DATE__[ 5]) + +#define BUILD_HOUR_CH0 (__TIME__[0]) +#define BUILD_HOUR_CH1 (__TIME__[1]) + +#define BUILD_MIN_CH0 (__TIME__[ 3]) +#define BUILD_MIN_CH1 (__TIME__[ 4]) + +const char VERSION_STRING[] = { + BUILD_YEAR_CH0, BUILD_YEAR_CH1, BUILD_YEAR_CH2, BUILD_YEAR_CH3, + BUILD_MONTH_CH0, BUILD_MONTH_CH1, + BUILD_DAY_CH0, BUILD_DAY_CH1, + BUILD_HOUR_CH0, BUILD_HOUR_CH1, + BUILD_MIN_CH0, BUILD_MIN_CH1, + '\0' +}; \ No newline at end of file diff --git a/src/videocodeclistmodel.cpp b/src/videocodeclistmodel.cpp new file mode 100644 index 00000000..d77a207c --- /dev/null +++ b/src/videocodeclistmodel.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "videocodeclistmodel.h" + +#include + +VideoCodecListModel::VideoCodecListModel(QObject *parent) + : QAbstractListModel(parent) +{} + +VideoCodecListModel::~VideoCodecListModel() {} + +int +VideoCodecListModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + QList realCodecList; + auto videoCodecListOld = LRCInstance::getCurrentAccountInfo().codecModel->getVideoCodecs(); + + for (auto codec : videoCodecListOld) { + if (codec.name.length()) { + realCodecList.append(codec); + } + } + + return realCodecList.size(); + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +VideoCodecListModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +VideoCodecListModel::data(const QModelIndex &index, int role) const +{ + auto videoCodecList = LRCInstance::getCurrentAccountInfo().codecModel->getVideoCodecs(); + if (!index.isValid() || videoCodecList.size() <= index.row()) { + return QVariant(); + } + + QList realCodecList; + + for (auto codec : videoCodecList) { + if (codec.name.length()) { + realCodecList.append(codec); + } + } + + switch (role) { + case Role::VideoCodecName: + return QVariant(realCodecList.at(index.row()).name); + case Role::IsEnabled: + return QVariant(realCodecList.at(index.row()).enabled); + case Role::VideoCodecID: + return QVariant(realCodecList.at(index.row()).id); + } + return QVariant(); +} + +QHash +VideoCodecListModel::roleNames() const +{ + QHash roles; + roles[VideoCodecName] = "VideoCodecName"; + roles[IsEnabled] = "IsEnabled"; + roles[VideoCodecID] = "VideoCodecID"; + return roles; +} + +QModelIndex +VideoCodecListModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +VideoCodecListModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +VideoCodecListModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable + | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} diff --git a/src/videocodeclistmodel.h b/src/videocodeclistmodel.h new file mode 100644 index 00000000..96a34a71 --- /dev/null +++ b/src/videocodeclistmodel.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class VideoCodecListModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Role { VideoCodecName = Qt::UserRole + 1, IsEnabled, VideoCodecID }; + Q_ENUM(Role) + + explicit VideoCodecListModel(QObject *parent = 0); + ~VideoCodecListModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; +}; diff --git a/src/videoformatfpsmodel.cpp b/src/videoformatfpsmodel.cpp new file mode 100644 index 00000000..4c5fe15e --- /dev/null +++ b/src/videoformatfpsmodel.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "videoformatfpsmodel.h" + +VideoFormatFpsModel::VideoFormatFpsModel(QObject *parent) + : QAbstractListModel(parent) +{ + try { + QString currentDeviceId = LRCInstance::avModel().getCurrentVideoCaptureDevice(); + auto currentSettings = LRCInstance::avModel().getDeviceSettings(currentDeviceId); + currentResolution_ = currentSettings.size; + } catch (const std::exception &e) { + qWarning() << "Constructor of VideoFormatFpsModel, exception: " << e.what(); + } +} + +VideoFormatFpsModel::~VideoFormatFpsModel() {} + +int +VideoFormatFpsModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + QString currentDeviceId = LRCInstance::avModel().getCurrentVideoCaptureDevice(); + auto deviceCapabilities = LRCInstance::avModel().getDeviceCapabilities(currentDeviceId); + if (deviceCapabilities.size() == 0) { + return 0; + } + try { + auto currentSettings = LRCInstance::avModel().getDeviceSettings(currentDeviceId); + auto currentChannel = currentSettings.channel; + currentChannel = currentChannel.isEmpty() ? "default" : currentChannel; + auto channelCaps = deviceCapabilities[currentChannel]; + + bool resolutionFound = false; + int indexOfCurrentResolutionInResRateList = 0; + + for (int i = 0; i < channelCaps.size(); i++) { + if (channelCaps[i].first == currentResolution_) { + indexOfCurrentResolutionInResRateList = i; + resolutionFound = true; + break; + } + } + + if (resolutionFound) { + auto fpsList = channelCaps[indexOfCurrentResolutionInResRateList].second; + return fpsList.size(); + } + } catch (const std::exception &e) { + qWarning() << e.what(); + } + return 0; + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +VideoFormatFpsModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +VideoFormatFpsModel::data(const QModelIndex &index, int role) const +{ + try { + QString currentDeviceId = LRCInstance::avModel().getCurrentVideoCaptureDevice(); + auto deviceCapabilities = LRCInstance::avModel().getDeviceCapabilities(currentDeviceId); + + auto currentSettings = LRCInstance::avModel().getDeviceSettings(currentDeviceId); + auto currentChannel = currentSettings.channel; + currentChannel = currentChannel.isEmpty() ? "default" : currentChannel; + auto channelCaps = deviceCapabilities[currentChannel]; + + bool resolutionFound = false; + int indexOfCurrentResolutionInResRateList = 0; + + for (int i = 0; i < channelCaps.size(); i++) { + if (channelCaps[i].first == currentResolution_) { + indexOfCurrentResolutionInResRateList = i; + resolutionFound = true; + break; + } + } + + if (!index.isValid() || channelCaps.size() <= index.row() || deviceCapabilities.size() == 0 + || !resolutionFound) { + return QVariant(); + } + + auto fpsList = channelCaps[indexOfCurrentResolutionInResRateList].second; + + switch (role) { + case Role::FPS: + return QVariant(fpsList[index.row()]); + case Role::FPS_ToDisplay_UTF8: + QString rateDisplayUtf8 = QString("%1 fps").arg((int) fpsList[index.row()]); + return QVariant(rateDisplayUtf8.toUtf8()); + } + + } catch (const std::exception &e) { + qWarning() << e.what(); + } + + return QVariant(); +} + +QHash +VideoFormatFpsModel::roleNames() const +{ + QHash roles; + roles[FPS] = "FPS"; + roles[FPS_ToDisplay_UTF8] = "FPS_ToDisplay_UTF8"; + return roles; +} + +QModelIndex +VideoFormatFpsModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +VideoFormatFpsModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +VideoFormatFpsModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +VideoFormatFpsModel::reset() +{ + beginResetModel(); + endResetModel(); +} + +int +VideoFormatFpsModel::getCurrentSettingIndex() +{ + int resultRowIndex = 0; + try { + QString currentDeviceId = LRCInstance::avModel().getCurrentVideoCaptureDevice(); + auto currentSettings = LRCInstance::avModel().getDeviceSettings(currentDeviceId); + float currentFps = currentSettings.rate; + auto resultList = match(index(0, 0), FPS, QVariant(currentFps)); + + if (resultList.size() > 0) { + resultRowIndex = resultList[0].row(); + } + + } catch (const std::exception &e) { + qWarning() << e.what(); + } + + return resultRowIndex; +} + +QString +VideoFormatFpsModel::getCurrentResolution() +{ + return currentResolution_; +} + +void +VideoFormatFpsModel::setCurrentResolution(QString resNew) +{ + if (currentResolution_ != resNew) { + currentResolution_ = resNew; + reset(); + emit currentResolutionChanged(resNew); + } +} diff --git a/src/videoformatfpsmodel.h b/src/videoformatfpsmodel.h new file mode 100644 index 00000000..cd165ccf --- /dev/null +++ b/src/videoformatfpsmodel.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class VideoFormatFpsModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(QString currentResolution READ getCurrentResolution WRITE setCurrentResolution NOTIFY + currentResolutionChanged); + +public: + enum Role { FPS = Qt::UserRole + 1, FPS_ToDisplay_UTF8 }; + Q_ENUM(Role) + + explicit VideoFormatFpsModel(QObject *parent = 0); + ~VideoFormatFpsModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); + /* + * This function is to get the current device id in the demon. + */ + Q_INVOKABLE int getCurrentSettingIndex(); + + /* + * Getters and setters + */ + QString getCurrentResolution(); + void setCurrentResolution(QString resNew); + +signals: + void currentResolutionChanged(QString resNew); + +private: + QString currentResolution_; +}; diff --git a/src/videoformatresolutionmodel.cpp b/src/videoformatresolutionmodel.cpp new file mode 100644 index 00000000..dea96358 --- /dev/null +++ b/src/videoformatresolutionmodel.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "videoformatresolutionmodel.h" + +VideoFormatResolutionModel::VideoFormatResolutionModel(QObject *parent) + : QAbstractListModel(parent) +{} + +VideoFormatResolutionModel::~VideoFormatResolutionModel() {} + +int +VideoFormatResolutionModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + QString currentDeviceId = LRCInstance::avModel().getCurrentVideoCaptureDevice(); + auto deviceCapabilities = LRCInstance::avModel().getDeviceCapabilities(currentDeviceId); + if (deviceCapabilities.size() == 0) { + return 0; + } + try { + auto currentSettings = LRCInstance::avModel().getDeviceSettings(currentDeviceId); + auto currentChannel = currentSettings.channel; + currentChannel = currentChannel.isEmpty() ? "default" : currentChannel; + auto channelCaps = deviceCapabilities[currentChannel]; + + return channelCaps.size(); + } catch (const std::exception &e) { + qWarning() << e.what(); + return 0; + } + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +VideoFormatResolutionModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +VideoFormatResolutionModel::data(const QModelIndex &index, int role) const +{ + try { + QString currentDeviceId = LRCInstance::avModel().getCurrentVideoCaptureDevice(); + auto deviceCapabilities = LRCInstance::avModel().getDeviceCapabilities(currentDeviceId); + + auto currentSettings = LRCInstance::avModel().getDeviceSettings(currentDeviceId); + auto currentChannel = currentSettings.channel; + currentChannel = currentChannel.isEmpty() ? "default" : currentChannel; + auto channelCaps = deviceCapabilities[currentChannel]; + + if (!index.isValid() || channelCaps.size() <= index.row() + || deviceCapabilities.size() == 0) { + return QVariant(); + } + + switch (role) { + case Role::Resolution: + return QVariant(channelCaps.at(index.row()).first); + case Role::Resolution_UTF8: + return QVariant(channelCaps.at(index.row()).first.toUtf8()); + } + + } catch (const std::exception &e) { + qWarning() << e.what(); + } + + return QVariant(); +} + +QHash +VideoFormatResolutionModel::roleNames() const +{ + QHash roles; + roles[Resolution] = "Resolution"; + roles[Resolution_UTF8] = "Resolution_UTF8"; + return roles; +} + +QModelIndex +VideoFormatResolutionModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +VideoFormatResolutionModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +VideoFormatResolutionModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +VideoFormatResolutionModel::reset() +{ + beginResetModel(); + endResetModel(); +} + +int +VideoFormatResolutionModel::getCurrentSettingIndex() +{ + int resultRowIndex = 0; + try { + QString currentDeviceId = LRCInstance::avModel().getCurrentVideoCaptureDevice(); + auto currentSettings = LRCInstance::avModel().getDeviceSettings(currentDeviceId); + QString currentResolution = currentSettings.size; + auto resultList = match(index(0, 0), Resolution, QVariant(currentResolution)); + + if (resultList.size() > 0) { + resultRowIndex = resultList[0].row(); + } + + } catch (const std::exception &e) { + qWarning() << e.what(); + } + + return resultRowIndex; +} diff --git a/src/videoformatresolutionmodel.h b/src/videoformatresolutionmodel.h new file mode 100644 index 00000000..8a3c8032 --- /dev/null +++ b/src/videoformatresolutionmodel.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class VideoFormatResolutionModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Role { Resolution = Qt::UserRole + 1, Resolution_UTF8 }; + Q_ENUM(Role) + + explicit VideoFormatResolutionModel(QObject *parent = 0); + ~VideoFormatResolutionModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); + /* + * This function is to get the current device id in the demon. + */ + Q_INVOKABLE int getCurrentSettingIndex(); +}; diff --git a/src/videoinputdevicemodel.cpp b/src/videoinputdevicemodel.cpp new file mode 100644 index 00000000..dfc349b3 --- /dev/null +++ b/src/videoinputdevicemodel.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "videoinputdevicemodel.h" + +VideoInputDeviceModel::VideoInputDeviceModel(QObject *parent) + : QAbstractListModel(parent) +{} + +VideoInputDeviceModel::~VideoInputDeviceModel() {} + +int +VideoInputDeviceModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + /* + * Count. + */ + int deviceListSize = LRCInstance::avModel().getDevices().size(); + if (deviceListSize > 0) { + return deviceListSize; + } else { + return 1; + } + } + /* + * A valid QModelIndex returns 0 as no entry has sub-elements. + */ + return 0; +} + +int +VideoInputDeviceModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + /* + * Only need one column. + */ + return 1; +} + +QVariant +VideoInputDeviceModel::data(const QModelIndex &index, int role) const +{ + auto deviceList = LRCInstance::avModel().getDevices(); + if (!index.isValid()) { + return QVariant(); + } + + if (deviceList.size() == 0 && index.row() == 0) { + switch (role) { + case Role::DeviceName: + return QVariant(QObject::tr("No Device")); + case Role::DeviceName_UTF8: + return QVariant(QObject::tr("No Device").toUtf8()); + } + } + + if (deviceList.size() <= index.row()) { + return QVariant(); + } + + auto currentDeviceSetting = LRCInstance::avModel().getDeviceSettings(deviceList[index.row()]); + + switch (role) { + case Role::DeviceChannel: + return QVariant((QString) currentDeviceSetting.channel); + case Role::DeviceName: + return QVariant(currentDeviceSetting.name); + case Role::DeviceId: + return QVariant(currentDeviceSetting.id); + case Role::CurrentFrameRate: + return QVariant((float) currentDeviceSetting.rate); + case Role::CurrentResolution: + return QVariant((QString) currentDeviceSetting.size); + case Role::DeviceName_UTF8: + return QVariant(currentDeviceSetting.name.toUtf8()); + } + return QVariant(); +} + +QHash +VideoInputDeviceModel::roleNames() const +{ + QHash roles; + roles[DeviceChannel] = "DeviceChannel"; + roles[DeviceName] = "DeviceName"; + roles[DeviceId] = "DeviceId"; + roles[CurrentFrameRate] = "CurrentFrameRate"; + roles[CurrentResolution] = "CurrentResolution"; + roles[DeviceName_UTF8] = "DeviceName_UTF8"; + return roles; +} + +QModelIndex +VideoInputDeviceModel::index(int row, int column, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (column != 0) { + return QModelIndex(); + } + + if (row >= 0 && row < rowCount()) { + return createIndex(row, column); + } + return QModelIndex(); +} + +QModelIndex +VideoInputDeviceModel::parent(const QModelIndex &child) const +{ + Q_UNUSED(child); + return QModelIndex(); +} + +Qt::ItemFlags +VideoInputDeviceModel::flags(const QModelIndex &index) const +{ + auto flags = QAbstractItemModel::flags(index) | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; + if (!index.isValid()) { + return QAbstractItemModel::flags(index); + } + return flags; +} + +void +VideoInputDeviceModel::reset() +{ + beginResetModel(); + endResetModel(); +} + +int +VideoInputDeviceModel::deviceCount() +{ + return LRCInstance::avModel().getDevices().size(); +} + +int +VideoInputDeviceModel::getCurrentSettingIndex() +{ + QString currentId = LRCInstance::avModel().getCurrentVideoCaptureDevice(); + auto resultList = match(index(0, 0), DeviceId, QVariant(currentId)); + + int resultRowIndex = 0; + if (resultList.size() > 0) { + resultRowIndex = resultList[0].row(); + } + + return resultRowIndex; +} diff --git a/src/videoinputdevicemodel.h b/src/videoinputdevicemodel.h new file mode 100644 index 00000000..99d5898e --- /dev/null +++ b/src/videoinputdevicemodel.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019-2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "api/account.h" +#include "api/contact.h" +#include "api/conversation.h" +#include "api/newdevicemodel.h" + +#include "lrcinstance.h" + +class VideoInputDeviceModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Role { + DeviceChannel = Qt::UserRole + 1, + DeviceName, + DeviceId, + CurrentFrameRate, + CurrentResolution, + DeviceName_UTF8 + }; + Q_ENUM(Role) + + explicit VideoInputDeviceModel(QObject *parent = 0); + ~VideoInputDeviceModel(); + + /* + * QAbstractListModel override. + */ + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + /* + * Override role name as access point in qml. + */ + QHash roleNames() const override; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE void reset(); + /* + * This function is to reset the model when there's new account added. + */ + Q_INVOKABLE int deviceCount(); + /* + * This function is to get the current device id in the demon. + */ + Q_INVOKABLE int getCurrentSettingIndex(); +}; diff --git a/src/webchathelpers.cpp b/src/webchathelpers.cpp new file mode 100644 index 00000000..273948c3 --- /dev/null +++ b/src/webchathelpers.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017-2020 by Savoir-faire Linux + * Author: Alexandre Viau + * Author: S�bastien Blin + * Author: Hugo Lefeuvre + * Author: Andreas Traczyk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "webchathelpers.h" + +QJsonObject +buildInteractionJson(lrc::api::ConversationModel &conversationModel, + const uint64_t msgId, + const lrc::api::interaction::Info &inter) +{ + + QRegExp reg(".(jpeg|jpg|gif|png)$"); + auto interaction = inter; + if (interaction.type == lrc::api::interaction::Type::DATA_TRANSFER && + interaction.body.toLower().contains(reg)) { + interaction.body = "file://" + interaction.body; + } + + auto sender = interaction.authorUri; + auto timestamp = QString::number(interaction.timestamp); + auto direction = lrc::api::interaction::isOutgoing(interaction) ? QString("out") + : QString("in"); + + QJsonObject interactionObject = QJsonObject(); + interactionObject.insert("text", QJsonValue(interaction.body)); + interactionObject.insert("id", QJsonValue(QString::number(msgId))); + interactionObject.insert("sender", QJsonValue(sender)); + interactionObject.insert("sender_contact_method", QJsonValue(sender)); + interactionObject.insert("timestamp", QJsonValue(timestamp)); + interactionObject.insert("direction", QJsonValue(direction)); + interactionObject.insert("duration", QJsonValue(static_cast(interaction.duration))); + + switch (interaction.type) { + case lrc::api::interaction::Type::TEXT: + interactionObject.insert("type", QJsonValue("text")); + break; + case lrc::api::interaction::Type::CALL: + interactionObject.insert("type", QJsonValue("call")); + break; + case lrc::api::interaction::Type::CONTACT: + interactionObject.insert("type", QJsonValue("contact")); + break; + case lrc::api::interaction::Type::DATA_TRANSFER: { + interactionObject.insert("type", QJsonValue("data_transfer")); + lrc::api::datatransfer::Info info = {}; + conversationModel.getTransferInfo(msgId, info); + if (info.status != lrc::api::datatransfer::Status::INVALID) { + interactionObject.insert("totalSize", QJsonValue(qint64(info.totalSize))); + interactionObject.insert("progress", QJsonValue(qint64(info.progress))); + } + break; + } + case lrc::api::interaction::Type::INVALID: + default: + return {}; + } + + if (interaction.isRead) { + interactionObject.insert("delivery_status", QJsonValue("read")); + } + + switch (interaction.status) { + case lrc::api::interaction::Status::SUCCESS: + interactionObject.insert("delivery_status", QJsonValue("sent")); + break; + case lrc::api::interaction::Status::FAILURE: + case lrc::api::interaction::Status::TRANSFER_ERROR: + interactionObject.insert("delivery_status", QJsonValue("failure")); + break; + case lrc::api::interaction::Status::TRANSFER_UNJOINABLE_PEER: + interactionObject.insert("delivery_status", QJsonValue("unjoinable peer")); + break; + case lrc::api::interaction::Status::SENDING: + interactionObject.insert("delivery_status", QJsonValue("sending")); + break; + case lrc::api::interaction::Status::TRANSFER_CREATED: + interactionObject.insert("delivery_status", QJsonValue("connecting")); + break; + case lrc::api::interaction::Status::TRANSFER_ACCEPTED: + interactionObject.insert("delivery_status", QJsonValue("accepted")); + break; + case lrc::api::interaction::Status::TRANSFER_CANCELED: + interactionObject.insert("delivery_status", QJsonValue("canceled")); + break; + case lrc::api::interaction::Status::TRANSFER_ONGOING: + interactionObject.insert("delivery_status", QJsonValue("ongoing")); + break; + case lrc::api::interaction::Status::TRANSFER_AWAITING_PEER: + interactionObject.insert("delivery_status", QJsonValue("awaiting peer")); + break; + case lrc::api::interaction::Status::TRANSFER_AWAITING_HOST: + interactionObject.insert("delivery_status", QJsonValue("awaiting host")); + break; + case lrc::api::interaction::Status::TRANSFER_TIMEOUT_EXPIRED: + interactionObject.insert("delivery_status", QJsonValue("awaiting peer timeout")); + break; + case lrc::api::interaction::Status::TRANSFER_FINISHED: + interactionObject.insert("delivery_status", QJsonValue("finished")); + break; + case lrc::api::interaction::Status::INVALID: + case lrc::api::interaction::Status::UNKNOWN: + default: + interactionObject.insert("delivery_status", QJsonValue("unknown")); + break; + } + return interactionObject; +} + +QString +interactionToJsonInteractionObject(lrc::api::ConversationModel &conversationModel, + const uint64_t msgId, + const lrc::api::interaction::Info &interaction) +{ + auto interactionObject = buildInteractionJson(conversationModel, msgId, interaction); + return QString(QJsonDocument(interactionObject).toJson(QJsonDocument::Compact)); +} + +QString +interactionsToJsonArrayObject(lrc::api::ConversationModel &conversationModel, + const std::map interactions) +{ + QJsonArray array; + for (const auto &interaction : interactions) { + auto interactionObject = buildInteractionJson(conversationModel, + interaction.first, + interaction.second); + if (!interactionObject.isEmpty()) { + array.append(interactionObject); + } + } + return QString(QJsonDocument(array).toJson(QJsonDocument::Compact)); +} diff --git a/src/webchathelpers.h b/src/webchathelpers.h new file mode 100644 index 00000000..f44dd620 --- /dev/null +++ b/src/webchathelpers.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017-2020 by Savoir-faire Linux + * Author: Alexandre Viau + * Author: Sébastien Blin + * Author: Hugo Lefeuvre + * Author: Andreas Traczyk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include + +#include "api/conversationmodel.h" + +QJsonObject buildInteractionJson(lrc::api::ConversationModel &conversationModel, + const uint64_t msgId, + const lrc::api::interaction::Info &interaction); +QString interactionToJsonInteractionObject(lrc::api::ConversationModel &conversationModel, + const uint64_t msgId, + const lrc::api::interaction::Info &interaction); +QString interactionsToJsonArrayObject( + lrc::api::ConversationModel &conversationModel, + const std::map interactions); diff --git a/src/wizardview/WizardView.qml b/src/wizardview/WizardView.qml new file mode 100644 index 00000000..0f3c29da --- /dev/null +++ b/src/wizardview/WizardView.qml @@ -0,0 +1,736 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Window 2.14 +import QtQuick.Controls 1.4 as CT +import QtQuick.Controls 2.14 +import QtQuick.Controls.Universal 2.12 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.14 +import net.jami.Models 1.0 + +import "../commoncomponents" +import "../constant" +import "components" + +Window { + id: wizardViewWindow + + enum Mode { + CREATE, + IMPORT, + MIGRATE, + CREATESIP, + CONNECTMANAGER + } + + enum NameRegistrationState { + BLANK, + INVALID, + TAKEN, + FREE, + SEARCHING + } + + property int layoutWidth: 768 + property int layoutHeight: 768 + property int textFontSize: 9 + property int wizardMode: WizardView.CREATE + property int addedAccountIndex: -1 + property bool registrationStateOk: false + property string fileToImport: "" + property string registedName: "" + + property var inputParaObject: ({}) + + /* + * signal to redirect the page to main view + */ + signal needToShowMainViewWindow(int accountIndex) + signal wizardViewIsClosed + + title: "Jami" + visible: true + width: layoutWidth + height: layoutHeight + + onClosing: { + close.accepted = false + changePageQML(controlPanelStackView.welcomePageStackId) + wizardViewWindow.hide() + wizardViewWindow.wizardViewIsClosed() + } + + Component.onCompleted: { + changePageQML( + controlPanelStackView.welcomePageStackId) + } + + Connections{ + target: ClientWrapper.accountAdaptor + + function onAccountAdded(showBackUp, index) { + addedAccountIndex = index + if (showBackUp) { + changePageQML(controlPanelStackView.backupKeysPageId) + } else { + wizardViewWindow.hide() + changePageQML(controlPanelStackView.welcomePageStackId) + needToShowMainViewWindow(addedAccountIndex) + ClientWrapper.lrcInstance.accountListChanged() + } + } + + // reportFailure + function onReportFailure() { + reportFailureQML() + } + } + + Connections { + id: registeredNameFoundConnection + target: ClientWrapper.nameDirectory + enabled: false + + function onRegisteredNameFound(status, address, name) { + slotRegisteredNameFound(status, address, name) + } + } + + // failure redirect timer and qml object holder + Timer { + id: failureRedirectPageTimer + + repeat: false + triggeredOnStart: false + interval: 1000 + + onTriggered: { + spinnerPage.successState = true + } + } + + function reportFailureQML() { + spinnerPage.successState = false + failureRedirectPageTimer.restart() + } + + function createAccountQML() { + switch (wizardMode) { + case WizardView.CONNECTMANAGER: + ClientWrapper.accountAdaptor.createJAMSAccount(inputParaObject) + break + case WizardView.CREATE: + case WizardView.IMPORT: + ClientWrapper.accountAdaptor.createJamiAccount(registedName, + inputParaObject, + createAccountPage.boothImgBase64, + (wizardMode === WizardView.CREATE)) + break + default: + ClientWrapper.accountAdaptor.createSIPAccount(inputParaObject,createSIPAccountPage.boothImgBase64) + } + + changePageQML(controlPanelStackView.spinnerPageId) + update() + } + + function slotRegisteredNameFound(status, address, name) { + if (name.length < 3) { + registrationStateOk = false + createAccountPage.nameRegistrationUIState = WizardView.INVALID + } else if (registedName === name) { + switch (status) { + case NameDirectory.LookupStatus.NOT_FOUND: + case NameDirectory.LookupStatus.ERROR: + registrationStateOk = true + createAccountPage.nameRegistrationUIState = WizardView.FREE + break + case NameDirectory.LookupStatus.INVALID_NAME: + case NameDirectory.LookupStatus.INVALID: + registrationStateOk = false + createAccountPage.nameRegistrationUIState = WizardView.INVALID + break + case NameDirectory.LookupStatus.SUCCESS: + registrationStateOk = false + createAccountPage.nameRegistrationUIState = WizardView.TAKEN + break + } + } + validateWizardProgressionQML() + } + + // function to set up nav bar visibility and the three buttons' visibiliy + function setNavBarVisibility(navVisible, back) { + navBarView.visible = (navVisible == true) || (back == true) + btnNext.visible = (navVisible == true) + btnPevious.visible = (navVisible == true) + btnBack.visible = (back == true) + && (ClientWrapper.utilsAdaptor.getAccountListSize() != 0) + } + + function processWizardInformationsQML() { + inputParaObject = {} + switch (wizardMode) { + case WizardView.CREATE: + spinnerPage.progressLabelEditText = qsTr( + "Generating your Jami account...") + inputParaObject["alias"] = createAccountPage.text_fullNameEditAlias + + inputParaObject["password"] = createAccountPage.text_confirmPasswordEditAlias + + createAccountPage.clearAllTextFields() + break + case WizardView.IMPORT: + registedName = "" + spinnerPage.progressLabelEditText = qsTr( + "Importing account archive...") + // should only work in import from backup page or import from device page + if (controlPanelStackView.currentIndex + == controlPanelStackView.importFromBackupPageId) { + inputParaObject["password"] + = importFromBackupPage.text_passwordFromDeviceAlias + importFromBackupPage.clearAllTextFields() + } else if (controlPanelStackView.currentIndex + == controlPanelStackView.importFromDevicePageId) { + inputParaObject["archivePin"] = importFromBackupPage.text_pinFromDeviceAlias + inputParaObject["password"] + = importFromDevicePage.text_passwordFromDeviceAlias + importFromDevicePage.clearAllTextFields() + } + break + case WizardView.MIGRATE: + spinnerPage.progressLabelEditText = qsTr( + "Migrating your Jami account...") + break + case WizardView.CREATESIP: + spinnerPage.progressLabelEditText = qsTr( + "Generating your SIP account...") + if (createSIPAccountPage.text_sipFullNameEditAlias.length == 0) { + inputParaObject["alias"] = "SIP" + } else { + inputParaObject["alias"] = createSIPAccountPage.text_sipFullNameEditAlias + } + + inputParaObject["hostname"] = createSIPAccountPage.text_sipServernameEditAlias + inputParaObject["username"] = createSIPAccountPage.text_sipUsernameEditAlias + inputParaObject["password"] = createSIPAccountPage.text_sipPasswordEditAlias + inputParaObject["proxy"] = createSIPAccountPage.text_sipProxyEditAlias + + break + case WizardView.CONNECTMANAGER: + spinnerPage.progressLabelEditText = qsTr( + "Connecting to account manager...") + inputParaObject["username"] + = connectToAccountManagerPage.text_usernameManagerEditAlias + inputParaObject["password"] + = connectToAccountManagerPage.text_passwordManagerEditAlias + inputParaObject["manager"] + = connectToAccountManagerPage.text_accountManagerEditAlias + connectToAccountManagerPage.clearAllTextFields() + break + } + + inputParaObject["archivePath"] = fileToImport + + if (!("archivePin" in inputParaObject)) { + inputParaObject["archivePath"] = "" + } + + // change page to spinner page + changePageQML(controlPanelStackView.spinnerPageId) + //create account + createAccountQML() + ClientWrapper.utilsAdaptor.createStartupLink() + } + + function changePageQML(pageIndex) { + if (pageIndex == controlPanelStackView.spinnerPageId) { + setNavBarVisibility(false) + } + controlPanelStackView.currentIndex = pageIndex + if (pageIndex == controlPanelStackView.welcomePageStackId) { + fileToImport = "" + setNavBarVisibility(false, true) + createAccountPage.nameRegistrationUIState = WizardView.BLANK + } else if (pageIndex == controlPanelStackView.createAccountPageId) { + createAccountPage.initializeOnShowUp() + setNavBarVisibility(true) + // connection between register name found and its slot + registeredNameFoundConnection.enabled = true + // validate wizard progression + validateWizardProgressionQML() + // start photobooth + createAccountPage.startBooth() + } else if (pageIndex == controlPanelStackView.createSIPAccountPageId) { + createSIPAccountPage.initializeOnShowUp() + setNavBarVisibility(true) + btnNext.enabled = true + // start photo booth + createSIPAccountPage.startBooth() + } else if (pageIndex == controlPanelStackView.importFromDevicePageId) { + importFromDevicePage.initializeOnShowUp() + setNavBarVisibility(true) + } else if (pageIndex == controlPanelStackView.spinnerPageId) { + createAccountPage.nameRegistrationUIState = WizardView.BLANK + createAccountPage.isToSetPassword_checkState_choosePasswordCheckBox = false + } else if (pageIndex == controlPanelStackView.connectToAccountManagerPageId) { + setNavBarVisibility(true) + connectToAccountManagerPage.initializeOnShowUp() + btnNext.enabled = false + } else if (pageIndex == controlPanelStackView.importFromBackupPageId) { + setNavBarVisibility(true) + importFromBackupPage.clearAllTextFields() + fileToImport = "" + btnNext.enabled = false + } else if (pageIndex == controlPanelStackView.backupKeysPageId) { + setNavBarVisibility(false) + } + } + + function validateWizardProgressionQML() { + if (controlPanelStackView.currentIndex + == controlPanelStackView.importFromDevicePageId) { + var validPin = !(importFromDevicePage.text_pinFromDeviceAlias.length == 0) + btnNext.enabled = validPin + return + } else if (controlPanelStackView.currentIndex + == controlPanelStackView.connectToAccountManagerPageId) { + var validUsername = !(connectToAccountManagerPage.text_usernameManagerEditAlias.length == 0) + var validPassword = !(connectToAccountManagerPage.text_passwordManagerEditAlias.length == 0) + var validManager = !(connectToAccountManagerPage.text_accountManagerEditAlias.length == 0) + btnNext.enabled = validUsername && validPassword + && validManager + return + } else if (controlPanelStackView.currentIndex + == controlPanelStackView.importFromBackupPageId) { + var validImport = !(fileToImport.length == 0) + btnNext.enabled = validImport + return + } + + var usernameOk = !createAccountPage.checkState_signUpCheckboxAlias + || (createAccountPage.checkState_signUpCheckboxAlias + && !(registedName.length == 0) + && (registedName == createAccountPage.text_usernameEditAlias) + && (registrationStateOk == true)) + var passwordOk = (createAccountPage.text_passwordEditAlias + == createAccountPage.text_confirmPasswordEditAlias) + + // set password status label + if (passwordOk + && !(createAccountPage.text_passwordEditAlias.length == 0)) { + createAccountPage.displayState_passwordStatusLabelAlias = "Success" + } else if (!passwordOk) { + createAccountPage.displayState_passwordStatusLabelAlias = "Fail" + } else { + createAccountPage.displayState_passwordStatusLabelAlias = "Hide" + } + //set enable state of next button + btnNext.enabled = (usernameOk && passwordOk) + } + + PasswordDialog { + id: passwordDialog + + anchors.centerIn: parent.Center + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + visible: false + purpose: PasswordDialog.ExportAccount + + onDoneSignal: { + if (currentPurpose === passwordDialog.ExportAccount) { + var success = (code === successCode) + + var title = success ? qsTr("Success") : qsTr("Error") + var info = success ? qsTr("Export Successful") : qsTr( + "Export Failed") + + ClientWrapper.accountAdaptor.passwordSetStatusMessageBox(success, + title, info) + if (success) { + console.log("Account Export Succeed") + wizardViewWindow.hide() + needToShowMainViewWindow(addedAccountIndex) + ClientWrapper.lrcInstance.accountListChanged() + } + } + } + } + + MouseArea { + anchors.fill: parent + onClicked: forceActiveFocus() + } + + // main frame rectangle + ScrollView { + id: wizardViewRect + anchors.fill: parent + + clip: true + + ColumnLayout { + id: content + width: wizardViewWindow.width // ensure correct width + height: wizardViewWindow.height // ensure correct height + + Layout.alignment: Qt.AlignHCenter + RowLayout { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.fillHeight: true + + StackLayout { + id: controlPanelStackView + currentIndex: welcomePageStackId + Layout.fillWidth: true + Layout.fillHeight: true + + property int welcomePageStackId: 0 + property int createAccountPageId: 1 + property int createSIPAccountPageId: 2 + property int importFromBackupPageId: 3 + property int backupKeysPageId: 4 + property int importFromDevicePageId: 5 + property int connectToAccountManagerPageId: 6 + property int spinnerPageId: 7 + + WelcomePageLayout { + // welcome page, index 0 + id: welcomePage + + onWelcomePageRedirectPage: { + changePageQML(toPageIndex) + } + + onVisibleChanged: { + if (visible) + setNavBarVisibility(false, + true) + } + + Component.onCompleted: { + setNavBarVisibility(false, true) + } + } + + CreateAccountPage { + // create account page, index 1 + id: createAccountPage + + onText_usernameEditAliasChanged: { + registrationStateOk = false + if (createAccountPage.checkState_signUpCheckboxAlias + && (createAccountPage.text_usernameEditAlias.length != 0)) { + registedName = ClientWrapper.utilsAdaptor.stringSimplifier( + createAccountPage.text_usernameEditAlias) + lookupTimer.restart() + } else { + createAccountPage.nameRegistrationUIState = WizardView.BLANK + lookupTimer.stop() + if (createAccountPage.text_usernameEditAlias.length == 0) { + lookupTimer.restart() + } + } + validateWizardProgressionQML() + } + + onValidateWizardProgressionCreateAccountPage: { + validateWizardProgressionQML() + } + + onText_passwordEditAliasChanged: { + validateWizardProgressionQML() + } + + onText_confirmPasswordEditAliasChanged: { + validateWizardProgressionQML() + } + + Timer { + id: lookupTimer + + repeat: false + triggeredOnStart: false + interval: 200 + + onTriggered: { + if (createAccountPage.checkState_signUpCheckboxAlias + && (createAccountPage.text_usernameEditAlias.length != 0)) { + createAccountPage.nameRegistrationUIState = WizardView.SEARCHING + ClientWrapper.nameDirectory.lookupName("", registedName) + } + } + } + } + + CreateSIPAccountPage { + // create SIP account page, index 2 + id: createSIPAccountPage + } + + ImportFromBackupPage { + // import from backup page, index 3 + id: importFromBackupPage + + onText_passwordFromBackupEditAliasChanged: { + validateWizardProgressionQML() + } + + onImportFromFile_Dialog_Accepted: { + fileToImport = ClientWrapper.utilsAdaptor.toNativeSeparators(fileDir) + inputParaObject[""] + + if (fileToImport.length != 0) { + importFromBackupPage.fileImportBtnText = ClientWrapper.utilsAdaptor.toFileInfoName( + fileToImport) + } else { + importFromBackupPage.fileImportBtnText = qsTr( + "Archive(none)") + } + validateWizardProgressionQML() + } + } + + BackupKeyPage { + // backup keys page, index 4 + id: backupKeysPage + + onNeverShowAgainBoxClicked: { + ClientWrapper.accountAdaptor.settingsNeverShowAgain(isChecked) + } + + onExport_Btn_FileDialogAccepted: { + if (accepted) { + // is there password? If so, go to password dialog, else, go to following directly + if (ClientWrapper.accountAdaptor.hasPassword()) { + passwordDialog.path = ClientWrapper.utilsAdaptor.getAbsPath(folderDir) + passwordDialog.open() + return + } else { + if (folderDir.length > 0) { + ClientWrapper.accountAdaptor.exportToFile( + ClientWrapper.utilsAdaptor.getCurrAccId(), + ClientWrapper.utilsAdaptor.getAbsPath(folderDir)) + } + } + } + + wizardViewWindow.hide() + changePageQML(controlPanelStackView.welcomePageStackId) + needToShowMainViewWindow(addedAccountIndex) + ClientWrapper.lrcInstance.accountListChanged() + } + + + onSkip_Btn_Clicked: { + wizardViewWindow.hide() + changePageQML(controlPanelStackView.welcomePageStackId) + needToShowMainViewWindow(addedAccountIndex) + ClientWrapper.lrcInstance.accountListChanged() + } + } + + ImportFromDevicePage { + // import from device page, index 5 + id: importFromDevicePage + + onText_pinFromDeviceAliasChanged: { + validateWizardProgressionQML() + } + + onText_passwordFromDeviceAliasChanged: { + validateWizardProgressionQML() + } + } + + ConnectToAccountManagerPage { + // connect to account manager Page, index 6 + id: connectToAccountManagerPage + + onText_usernameManagerEditAliasChanged: { + validateWizardProgressionQML() + } + + onText_passwordManagerEditAliasChanged: { + validateWizardProgressionQML() + } + + onText_accountManagerEditAliasChanged: { + validateWizardProgressionQML() + } + } + + SpinnerPage { + // spinner Page, index 7 + id: spinnerPage + } + } + } + + RowLayout { + id: navBarView + + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + Layout.fillWidth: true + + Layout.maximumHeight: 52 + Layout.preferredHeight: 52 + + HoverableGradientButton { + id: btnPevious + Layout.alignment: Qt.AlignLeft + width: 85 + height: 30 + radius: height / 2 + + Layout.leftMargin: 11 + + Layout.preferredWidth: 85 + Layout.preferredHeight: 30 + text: qsTr("Previous") + font.pointSize: 10 + font.kerning: true + + onClicked: { + // stop photobooth previewing + if(controlPanelStackView.currentIndex == controlPanelStackView.createAccountPageId) { + createAccountPage.stopBooth() + } + if(controlPanelStackView.currentIndex == controlPanelStackView.createSIPAccountPageId) { + createSIPAccountPage.stopBooth() + } + + // Disconnect registered name found Connection + registeredNameFoundConnection.enabled = false + // deal with look up status label and collapsible password widget + createAccountPage.nameRegistrationUIState = WizardView.BLANK + createAccountPage.isToSetPassword_checkState_choosePasswordCheckBox = false + // switch to welcome page + if (controlPanelStackView.currentIndex + == controlPanelStackView.createAccountPageId + || controlPanelStackView.currentIndex + == controlPanelStackView.createSIPAccountPageId + || controlPanelStackView.currentIndex + == controlPanelStackView.importFromBackupPageId + || controlPanelStackView.currentIndex + == controlPanelStackView.importFromDevicePageId + || controlPanelStackView.currentIndex + == controlPanelStackView.connectToAccountManagerPageId) { + changePageQML( + controlPanelStackView.welcomePageStackId) + } + } + } + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.fillHeight: true + Layout.minimumWidth: 40 + Layout.fillWidth: true + } + + HoverableGradientButton { + id: btnBack + Layout.alignment: Qt.AlignLeft + width: 85 + height: 30 + radius: height / 2 + + Layout.preferredWidth: 85 + Layout.preferredHeight: 30 + text: qsTr("Back") + font.pointSize: 10 + font.kerning: true + + onClicked: { + wizardViewWindow.hide() + needToShowMainViewWindow(addedAccountIndex) + } + } + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.fillHeight: true + Layout.minimumWidth: 40 + Layout.fillWidth: true + } + + HoverableGradientButton { + id: btnNext + Layout.alignment: Qt.AlignRight + width: 85 + height: 30 + radius: height / 2 + + Layout.rightMargin: 11 + + Layout.minimumWidth: 85 + Layout.preferredWidth: 85 + Layout.maximumWidth: 85 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Next") + font.pointSize: 10 + font.kerning: true + + onClicked: { + // stop photobooth previewing + if(controlPanelStackView.currentIndex == controlPanelStackView.createAccountPageId) { + createAccountPage.stopBooth() + } + if(controlPanelStackView.currentIndex == controlPanelStackView.createSIPAccountPageId) { + createSIPAccountPage.stopBooth() + } + + registeredNameFoundConnection.enabled = false + + if (controlPanelStackView.currentIndex + == controlPanelStackView.createAccountPageId) { + wizardMode = WizardView.CREATE + processWizardInformationsQML() + } else if (controlPanelStackView.currentIndex + == controlPanelStackView.importFromDevicePageId) { + wizardMode = WizardView.IMPORT + processWizardInformationsQML() + } else if (controlPanelStackView.currentIndex + == controlPanelStackView.createSIPAccountPageId) { + wizardMode = WizardView.CREATESIP + processWizardInformationsQML() + } else if (controlPanelStackView.currentIndex + == controlPanelStackView.connectToAccountManagerPageId) { + wizardMode = WizardView.CONNECTMANAGER + processWizardInformationsQML() + } else if (controlPanelStackView.currentIndex + == controlPanelStackView.importFromBackupPageId) { + wizardMode = WizardView.IMPORT + processWizardInformationsQML() + } + } + } + } + } + } +} diff --git a/src/wizardview/components/BackupKeyPage.qml b/src/wizardview/components/BackupKeyPage.qml new file mode 100644 index 00000000..59ec3ef7 --- /dev/null +++ b/src/wizardview/components/BackupKeyPage.qml @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 +import Qt.labs.platform 1.1 + +import "../../constant" +import "../../commoncomponents" + +ColumnLayout { + signal neverShowAgainBoxClicked(bool isChecked) + signal skip_Btn_Clicked + signal export_Btn_FileDialogAccepted(bool accepted, string folderDir) + + /* + * JamiFileDialog for exporting account + */ + JamiFileDialog { + id: exportBtn_Dialog + + mode: JamiFileDialog.SaveFile + + title: qsTr("Export Account Here") + folder: StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/Desktop" + + nameFilters: [qsTr("Jami archive files") + " (*.gz)", qsTr( + "All files") + " (*)"] + + onAccepted: { + export_Btn_FileDialogAccepted(true, file) + } + + onRejected: { + export_Btn_FileDialogAccepted(false, folder) + } + + onVisibleChanged: { + if (!visible) { + rejected() + } + } + } + + Layout.fillWidth: true + Layout.fillHeight: true + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.fillHeight: true + } + + /* + * Main layout for BackupKeyPage which consists of the buttons and "never show again" check box + */ + ColumnLayout { + Layout.alignment: Qt.AlignCenter + Layout.maximumWidth: 366 + + spacing: 12 + + Label { + id: backupKeysLabel + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 366 + Layout.maximumHeight: 21 + Layout.preferredWidth: 366 + Layout.preferredHeight: 21 + + text: qsTr("Backup your account") + font.pointSize: 13 + font.kerning: true + } + Label { + id: backupInfoLabel1 + Layout.maximumWidth: 366 + Layout.maximumHeight: 57 + Layout.preferredWidth: 366 + Layout.preferredHeight: 57 + + text: qsTr("This account only exists on this device. If you lost your device or uninstall the application,your account will be deleted. You can backup your account now or later.") + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignJustify + verticalAlignment: Text.AlignVCenter + } + CheckBox { + id: neverShowAgainBox + checked: false + + Layout.maximumWidth: 366 + Layout.maximumHeight: 19 + Layout.preferredWidth: 366 + Layout.preferredHeight: 19 + + indicator.implicitWidth: 10 + indicator.implicitHeight:10 + + text: qsTr("Never show me this again") + font.pointSize: 8 + + onClicked: { + neverShowAgainBoxClicked(checked) + } + } + RowLayout { + Layout.fillWidth: true + Layout.maximumHeight: 20 + + Item { + Layout.alignment: Qt.AlignVCenter + Layout.fillHeight: true + Layout.maximumWidth: 40 + Layout.minimumWidth: 10 + } + + HoverableGradientButton { + id: exportBtn + + Layout.alignment: Qt.AlignVCenter + Layout.minimumWidth: 85 + Layout.preferredWidth: 85 + Layout.maximumWidth: 85 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Export") + font.kerning: true + fontPointSize: 10 + radius: height / 2 + backgroundColor: JamiTheme.releaseColor + + onClicked: { + exportBtn_Dialog.open() + } + } + + Item { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + Layout.fillHeight: true + } + + HoverableGradientButton { + id: skipBtn + + Layout.alignment: Qt.AlignVCenter + Layout.minimumWidth: 85 + Layout.preferredWidth: 85 + Layout.maximumWidth: 85 + + Layout.minimumHeight: 30 + Layout.preferredHeight: 30 + Layout.maximumHeight: 30 + + text: qsTr("Skip") + fontPointSize: 10 + font.kerning: true + radius: height / 2 + backgroundColor: JamiTheme.releaseColor + + onClicked: { + skip_Btn_Clicked() + } + } + + Item { + Layout.alignment: Qt.AlignVCenter + Layout.fillHeight: true + Layout.maximumWidth: 40 + Layout.minimumWidth: 10 + } + } + } + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.fillHeight: true + } +} diff --git a/src/wizardview/components/CollapsiblePasswordWidget.qml b/src/wizardview/components/CollapsiblePasswordWidget.qml new file mode 100644 index 00000000..82af6e2b --- /dev/null +++ b/src/wizardview/components/CollapsiblePasswordWidget.qml @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 + +import "../../constant" +import "../../commoncomponents" + +/* + * an independent widget that keeps the password's textfields, including password field and confirm password field + */ + +GridLayout { + id: root + + property alias text_passwordEditAlias: passwordEdit.text + property alias text_confirmPasswordEditAlias: confirmPasswordEdit.text + property alias state_passwordStatusLabelAlias: passwordStatusLabel.passwordStatusState + + property bool visibleCollapsble: false + + function clearAllTextFields() { + passwordEdit.clear() + confirmPasswordEdit.clear() + } + + visible: visibleCollapsble + Layout.fillWidth: true + rowSpacing: 6 + columnSpacing: 6 + + rows: 2 + columns: 2 + + Layout.leftMargin: 32 + + InfoLineEdit { + id: passwordEdit + + visible: visibleCollapsble + + Layout.row: 0 + Layout.column: 0 + + fieldLayoutWidth: 261 + + Layout.alignment: Qt.AlignHCenter + + selectByMouse: true + echoMode: TextInput.Password + placeholderText: qsTr("Password") + font.pointSize: 10 + font.kerning: true + } + + Item { + Layout.row: 0 + Layout.column: 1 + + Layout.maximumWidth: 32 + Layout.preferredWidth: 32 + Layout.minimumWidth: 32 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + } + + InfoLineEdit { + id: confirmPasswordEdit + + visible: visibleCollapsble + + Layout.row: 1 + Layout.column: 0 + + fieldLayoutWidth: 261 + + Layout.alignment: Qt.AlignHCenter + + selectByMouse: true + echoMode: TextInput.Password + placeholderText: qsTr("Confirm Password") + font.pointSize: 10 + font.kerning: true + } + + Label { + id: passwordStatusLabel + + visible: visibleCollapsble + + Layout.row: 1 + Layout.column: 1 + + Layout.maximumWidth: 32 + Layout.preferredWidth: 32 + Layout.minimumWidth: 32 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + Layout.alignment: Qt.AlignRight + + property string passwordStatusState: "Hide" + + background: { + switch (passwordStatusState) { + case "Hide": + return Qt.createQmlObject("import QtQuick 2.14; +import \"qrc:/src/constant/\"; +Rectangle { +anchors.fill: parent; +color: \"transparent\"; }", passwordStatusLabel) + case "Fail": + return Qt.createQmlObject("import QtQuick 2.14; +import \"qrc:/src/constant/\"; +Rectangle { +anchors.fill: parent; +Image{ +anchors.fill: parent; +source: \"image://tintedPixmap/\"+ (\"qrc:/images/icons/baseline-close-24px.svg\").replace(\"qrc:/images/icons/\",\"\") + \"+\" + JamiTheme.red_; +mipmap: true;} +}", passwordStatusLabel) + case "Success": + return Qt.createQmlObject("import QtQuick 2.14; +import \"qrc:/src/constant/\"; +Rectangle { +anchors.fill: parent; +Image { +anchors.fill: parent; +source: \"image://tintedPixmap/\"+ (\"qrc:/images/icons/baseline-done-24px.svg\").replace(\"qrc:/images/icons/\",\"\") + \"+\" + JamiTheme.presenceGreen_; +mipmap: true;} +}", passwordStatusLabel) + } + } + } +} diff --git a/src/wizardview/components/ConnectToAccountManagerPage.qml b/src/wizardview/components/ConnectToAccountManagerPage.qml new file mode 100644 index 00000000..c41dcef1 --- /dev/null +++ b/src/wizardview/components/ConnectToAccountManagerPage.qml @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 + +import "../../constant" +import "../../commoncomponents" + +ColumnLayout { + property alias text_usernameManagerEditAlias: usernameManagerEdit.text + property alias text_passwordManagerEditAlias: passwordManagerEdit.text + property alias text_accountManagerEditAlias: accountManagerEdit.text + + function initializeOnShowUp() { + clearAllTextFields() + } + + function clearAllTextFields() { + usernameManagerEdit.clear() + passwordManagerEdit.clear() + accountManagerEdit.clear() + } + + Layout.fillWidth: true + Layout.fillHeight: true + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 40 + Layout.fillWidth: true + Layout.fillHeight: true + } + + ColumnLayout { + Layout.alignment: Qt.AlignCenter + Layout.fillWidth: true + + spacing: 12 + + Label { + id: signInLabel + + Layout.alignment: Qt.AlignHCenter + Layout.minimumWidth: 256 + Layout.preferredHeight: 21 + text: qsTr("Sign in") + font.pointSize: 13 + font.kerning: true + } + + InfoLineEdit { + id: usernameManagerEdit + + Layout.alignment: Qt.AlignHCenter + + selectByMouse: true + placeholderText: qsTr("Username") + } + + InfoLineEdit { + id: passwordManagerEdit + + Layout.alignment: Qt.AlignHCenter + selectByMouse: true + echoMode: TextInput.Password + placeholderText: qsTr("Password") + } + + InfoLineEdit { + id: accountManagerEdit + + Layout.alignment: Qt.AlignHCenter + + selectByMouse: true + placeholderText: qsTr("Account Manager") + } + } + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 40 + Layout.fillWidth: true + Layout.fillHeight: true + } +} diff --git a/src/wizardview/components/CreateAccountPage.qml b/src/wizardview/components/CreateAccountPage.qml new file mode 100644 index 00000000..f59b2c6e --- /dev/null +++ b/src/wizardview/components/CreateAccountPage.qml @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 + +import "../" +import "../../constant" +import "../../commoncomponents" + +ColumnLayout { + property alias text_fullNameEditAlias: fullNameEdit.text + property alias text_usernameEditAlias: usernameEdit.text + + property int nameRegistrationUIState: WizardView.BLANK + + property alias checkState_signUpCheckboxAlias: signUpCheckbox.checked + property alias isToSetPassword_checkState_choosePasswordCheckBox: choosePasswordCheckBox.checked + + // photo booth alias + property alias boothImgBase64: setAvatarWidget.imgBase64 + + // collapse password widget property aliases + property alias text_passwordEditAlias: collapsiblePasswordWidget.text_passwordEditAlias + property alias text_confirmPasswordEditAlias: collapsiblePasswordWidget.text_confirmPasswordEditAlias + property alias displayState_passwordStatusLabelAlias: collapsiblePasswordWidget.state_passwordStatusLabelAlias + + signal validateWizardProgressionCreateAccountPage + + function initializeOnShowUp() { + clearAllTextFields() + + signUpCheckbox.checked = true + choosePasswordCheckBox.checked = false + usernameEdit.enabled = true + fullNameEdit.enabled = true + } + + function clearAllTextFields() { + usernameEdit.clear() + fullNameEdit.clear() + + collapsiblePasswordWidget.clearAllTextFields() + } + + function setCollapsiblePasswordWidgetVisibility(visible) { + choosePasswordCheckBox.checked = visible + if (visible) { + choosePasswordCheckBox.visible = true + } + } + + function startBooth(){ + setAvatarWidget.startBooth() + } + + function stopBooth(){ + setAvatarWidget.stopBooth() + } + + Layout.fillWidth: true + Layout.fillHeight: true + + spacing: 6 + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + + spacing: 5 + + ColumnLayout { + Layout.fillWidth: true + spacing: 6 + + Layout.alignment: Qt.AlignHCenter + + Label { + id: profileSectionLabel + + + Layout.alignment: Qt.AlignHCenter + + text: qsTr("Profile") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + PhotoboothView{ + id: setAvatarWidget + + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + Layout.maximumHeight: 261 + Layout.preferredHeight: 261 + Layout.minimumHeight: 261 + } + + RowLayout { + spacing: 6 + Layout.alignment: Qt.AlignHCenter + Layout.maximumHeight: 30 + + Item { + Layout.fillWidth: true + Layout.maximumHeight: 10 + } + + InfoLineEdit { + id: fullNameEdit + + fieldLayoutWidth: 261 + + Layout.alignment: Qt.AlignCenter + + selectByMouse: true + placeholderText: qsTr("Profile name") + font.pointSize: 10 + font.kerning: true + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + + spacing: 5 + Label { + id: accountSectionLabel + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: qsTr("Account") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 6 + + CheckBox { + id: signUpCheckbox + checked: true + + indicator.width: 10 + indicator.height: 10 + + Layout.leftMargin: 32 + + Layout.minimumWidth: 261 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 25 + + Layout.alignment: Qt.AlignLeft + + text: qsTr("Register public username") + font.pointSize: 10 + font.kerning: true + + indicator.implicitWidth: 20 + indicator.implicitHeight:20 + + onClicked: { + if (!checked) { + usernameEdit.clear() + } + + validateWizardProgressionCreateAccountPage() + } + } + } + + RowLayout { + spacing: 6 + Layout.fillWidth: true + + Layout.leftMargin: 32 + + InfoLineEdit { + id: usernameEdit + + fieldLayoutWidth: 261 + + Layout.alignment: Qt.AlignHCenter + + selectByMouse: true + placeholderText: qsTr("Choose your username") + font.pointSize: 10 + font.kerning: true + + enabled: signUpCheckbox.visible && signUpCheckbox.checked + } + + LookupStatusLabel{ + id: lookupStatusLabel + + visible: true + + lookupStatusState: { + switch (nameRegistrationUIState) { + case WizardView.BLANK: + return "Blank" + case WizardView.INVALID: + return "Invalid" + case WizardView.TAKEN: + return "Taken" + case WizardView.FREE: + return "Free" + case WizardView.SEARCHING: + return "Searching" + default: + return "Blank" + } + } + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 6 + + CheckBox { + id: choosePasswordCheckBox + checked: false + + indicator.width: 10 + indicator.height: 10 + + Layout.leftMargin: 32 + + Layout.minimumWidth: 261 + + Layout.preferredHeight: 30 + Layout.minimumHeight: 25 + + indicator.implicitWidth: 20 + indicator.implicitHeight:20 + + Layout.alignment: Qt.AlignLeft + + text: qsTr("Choose a password for enhanced security") + font.pointSize: 8 + font.kerning: true + + onClicked: { + if (!checked) { + collapsiblePasswordWidget.clearAllTextFields() + } + + validateWizardProgressionCreateAccountPage() + } + } + + CollapsiblePasswordWidget { + id: collapsiblePasswordWidget + + Layout.alignment: Qt.AlignHCenter + + visibleCollapsble: choosePasswordCheckBox.checked + && choosePasswordCheckBox.visible + } + } + + Item { + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + Layout.alignment: Qt.AlignHCenter + } + } +} diff --git a/src/wizardview/components/CreateSIPAccountPage.qml b/src/wizardview/components/CreateSIPAccountPage.qml new file mode 100644 index 00000000..3f3da172 --- /dev/null +++ b/src/wizardview/components/CreateSIPAccountPage.qml @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 + +import "../../constant" +import "../../commoncomponents" + +ColumnLayout { + property alias text_sipFullNameEditAlias: sipFullNameEdit.text + property alias text_sipServernameEditAlias: sipServernameEdit.text + property alias text_sipProxyEditAlias: sipProxyEdit.text + property alias text_sipUsernameEditAlias: sipUsernameEdit.text + property alias text_sipPasswordEditAlias: sipPasswordEdit.text + + property alias boothImgBase64: setSIPAvatarWidget.imgBase64 + + function initializeOnShowUp() { + clearAllTextFields() + } + + function clearAllTextFields() { + sipUsernameEdit.clear() + sipPasswordEdit.clear() + sipServernameEdit.clear() + sipProxyEdit.clear() + sipFullNameEdit.clear() + sipUsernameEdit.clear() + } + + function startBooth(){ + setSIPAvatarWidget.startBooth() + } + + function stopBooth(){ + setSIPAvatarWidget.stopBooth() + } + + Layout.fillWidth: true + Layout.fillHeight: true + + spacing: 6 + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + Label { + id: sipProfileSectionLabel + + Layout.maximumWidth: 368 + Layout.preferredWidth: 368 + Layout.maximumHeight: 21 + Layout.preferredHeight: 21 + + Layout.alignment: Qt.AlignHCenter + + text: qsTr("Profile") + font.pointSize: 13 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + PhotoboothView{ + id: setSIPAvatarWidget + + Layout.alignment: Qt.AlignHCenter + + Layout.maximumWidth: 261 + Layout.preferredWidth: 261 + Layout.minimumWidth: 261 + Layout.maximumHeight: 261 + Layout.preferredHeight: 261 + Layout.minimumHeight: 261 + } + + RowLayout { + spacing: 0 + Layout.alignment: Qt.AlignHCenter + Layout.maximumHeight: 30 + + Item { + Layout.fillWidth: true + Layout.maximumHeight: 10 + } + + InfoLineEdit { + id: sipFullNameEdit + + fieldLayoutWidth : 261 + Layout.alignment: Qt.AlignCenter + selectByMouse: true + placeholderText: qsTr("Profile name") + font.pointSize: 10 + font.kerning: true + } + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + } + Item { + Layout.fillHeight: true + Layout.fillWidth: true + } + + Label { + id: sipAccountSectionLabel + Layout.maximumWidth: 368 + Layout.preferredWidth: 368 + Layout.maximumHeight: 21 + Layout.preferredHeight: 21 + + Layout.alignment: Qt.AlignHCenter + + text: qsTr("SIP Account") + font.pointSize: 12 + font.kerning: true + + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + spacing: 7 + + Item { + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + Layout.maximumHeight: 40 + Layout.minimumHeight: 30 + Layout.preferredHeight: 40 + } + + InfoLineEdit { + id: sipServernameEdit + Layout.alignment: Qt.AlignHCenter + fieldLayoutWidth: 261 + + selectByMouse: true + placeholderText: qsTr("Server") + font.pointSize: 10 + font.kerning: true + } + + InfoLineEdit { + id: sipProxyEdit + Layout.alignment: Qt.AlignHCenter + fieldLayoutWidth: 261 + + selectByMouse: true + placeholderText: qsTr("Proxy") + font.pointSize: 10 + font.kerning: true + } + + InfoLineEdit { + id: sipUsernameEdit + Layout.alignment: Qt.AlignHCenter + fieldLayoutWidth: 261 + + selectByMouse: true + placeholderText: qsTr("Username") + font.pointSize: 10 + font.kerning: true + } + + InfoLineEdit { + id: sipPasswordEdit + Layout.alignment: Qt.AlignHCenter + fieldLayoutWidth: 261 + + selectByMouse: true + echoMode: TextInput.Password + placeholderText: qsTr("Password") + font.pointSize: 10 + font.kerning: true + } + } +} diff --git a/src/wizardview/components/HoverableGradientButton.qml b/src/wizardview/components/HoverableGradientButton.qml new file mode 100644 index 00000000..905bd41b --- /dev/null +++ b/src/wizardview/components/HoverableGradientButton.qml @@ -0,0 +1,142 @@ + +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Mingrui Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.15 +import QtQuick.Controls 2.14 +import QtGraphicalEffects 1.15 +import net.jami.Models 1.0 + + +/* + * HoverableButton contains the following configurable properties: + * 1. Color changes on different button state + * 2. Radius control (rounded) + * 3. Text content or image content + * 4. Can use OnClicked slot to implement some click logic + */ +Button { + id: hoverableButton + property int fontPointSize: 9 + property string backgroundColor: JamiTheme.releaseColor + property string backgroundColorDisabled : Qt.rgba(242/256, 242/256, 242/256, 0.8) + + property string startColor :"#109ede" + property string endColor : "#2b5084" + + property string startColorPressed :"#043161" + property string endColorPressed : "#00113f" + + property string startColorHovered :"#2b4b7e" + property string endColorHovered : "#001d4d" + + property string onPressColor: JamiTheme.pressColor + property string onReleaseColor: backgroundColor + property string onEnterColor: JamiTheme.hoverColor + property string onExitColor: backgroundColor + property string textColor: "white" + + property alias radius: hoverableButtonBackground.radius + + property bool isHovering: false + property bool isBeingPressed: false + + radius: height / 2 + font.pointSize: fontPointSize + font.kerning: true + hoverEnabled: true + + contentItem: Text { + text: hoverableButton.text + font: hoverableButton.font + opacity: enabled ? 1.0 : 0.3 + color: enabled? textColor : "grey" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + id: hoverableButtonBackground + color: backgroundColor + + MouseArea { + id: btnMouseArea + anchors.fill: hoverableButtonBackground + hoverEnabled: true + onPressed: { + hoverableButtonBackground.color = onPressColor + isBeingPressed = true + } + onReleased: { + hoverableButtonBackground.color = onReleaseColor + isBeingPressed = false + hoverableButton.clicked() + } + onEntered: { + hoverableButtonBackground.color = onEnterColor + isHovering = true + } + onExited: { + hoverableButtonBackground.color = onExitColor + isHovering = false + } + } + } + + LinearGradient { + id: backgroundGradient + + source: hoverableButtonBackground + anchors.fill: hoverableButtonBackground + start: Qt.point(0, 0) + end: Qt.point(width, 0) + gradient: Gradient { + GradientStop { position: 0.0; color: { + if(!hoverableButton.enabled){ + return backgroundColorDisabled + } + + if(isBeingPressed){ + return startColorPressed + } else { + if(isHovering){ + return startColorHovered + } else { + return startColor + } + } + } } + + GradientStop { position: 1.0; color: { + if(!hoverableButton.enabled){ + return backgroundColorDisabled + } + + if(isBeingPressed){ + return endColorPressed + } else { + if(isHovering){ + return endColorHovered + } else { + return endColor + } + } + } } + } + } +} diff --git a/src/wizardview/components/ImportFromBackupPage.qml b/src/wizardview/components/ImportFromBackupPage.qml new file mode 100644 index 00000000..bd7d26e2 --- /dev/null +++ b/src/wizardview/components/ImportFromBackupPage.qml @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 +import Qt.labs.platform 1.1 + +import "../../constant" +import "../../commoncomponents" + +ColumnLayout { + property alias text_passwordFromBackupEditAlias: passwordFromBackupEdit.text + property string fileImportBtnText: qsTr("Archive(none)") + + signal importFromFile_Dialog_Accepted(string fileDir) + + function clearAllTextFields() { + passwordFromBackupEdit.clear() + fileImportBtnText = qsTr("Archive(none)") + } + + JamiFileDialog { + id: importFromFile_Dialog + + mode: JamiFileDialog.OpenFile + title: qsTr("Open File") + folder: StandardPaths.writableLocation(StandardPaths.HomeLocation) + "/Desktop" + + nameFilters: [qsTr("Jami archive files") + " (*.gz)", qsTr( + "All files") + " (*)"] + + onAccepted: { + importFromFile_Dialog_Accepted(file) + } + } + + Layout.fillWidth: true + Layout.fillHeight: true + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.fillHeight: true + } + + ColumnLayout { + Layout.alignment: Qt.AlignCenter + Layout.maximumWidth: 366 + + spacing: 12 + + RowLayout { + Layout.fillWidth: true + Layout.maximumHeight: 24 + spacing: 0 + + Item { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + Layout.fillHeight: true + } + + Label { + id: importFromBackupLabel + Layout.minimumHeight: 24 + Layout.minimumWidth: 234 + text: qsTr("Import from backup") + font.pointSize: 13 + font.kerning: true + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + } + + HoverableRadiusButton { + id: backupInfoBtn + + buttonImageHeight: height + buttonImageWidth: width + + Layout.alignment: Qt.AlignVCenter + Layout.minimumWidth: 24 + Layout.preferredWidth: 24 + Layout.maximumWidth: 24 + + Layout.minimumHeight: 24 + Layout.preferredHeight: 24 + Layout.maximumHeight: 24 + + radius: height / 2 + icon.source: "/images/icons/info-24px.svg" + icon.height: 24 + icon.width: 24 + + backgroundColor: JamiTheme.releaseColor + onClicked: { + backupInfoLabel.visible = !backupInfoLabel.visible + } + } + Item { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + Layout.fillHeight: true + } + } + + HoverableGradientButton { + id: fileImportBtn + + Layout.alignment: Qt.AlignHCenter + Layout.maximumWidth: 256 + Layout.preferredWidth: 256 + + Layout.maximumHeight: 30 + Layout.preferredHeight: 30 + Layout.minimumHeight: 30 + + text: fileImportBtnText + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + backgroundColor: JamiTheme.releaseColor + + onClicked: { + importFromFile_Dialog.open() + } + } + + InfoLineEdit { + id: passwordFromBackupEdit + + Layout.alignment: Qt.AlignHCenter + + selectByMouse: true + echoMode: TextInput.Password + placeholderText: qsTr("Password") + } + + Label { + id: backupInfoLabel + + Layout.alignment: Qt.AlignHCenter + Layout.maximumWidth: 366 + Layout.preferredWidth: 366 + + text: qsTr("You can obtain an archive by clicking on \"Export account\" in the account settings. This will create a .gz file on your device.") + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + visible: false + } + } + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.fillHeight: true + } +} diff --git a/src/wizardview/components/ImportFromDevicePage.qml b/src/wizardview/components/ImportFromDevicePage.qml new file mode 100644 index 00000000..51816356 --- /dev/null +++ b/src/wizardview/components/ImportFromDevicePage.qml @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 + +import "../../constant" +import "../../commoncomponents" + +ColumnLayout { + property alias text_pinFromDeviceAlias: pinFromDevice.text + property alias text_passwordFromDeviceAlias: passwordFromDevice.text + + function initializeOnShowUp() { + clearAllTextFields() + } + + function clearAllTextFields() { + pinFromDevice.clear() + passwordFromDevice.clear() + } + + Layout.fillWidth: true + Layout.fillHeight: true + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 40 + Layout.fillWidth: true + Layout.fillHeight: true + } + + ColumnLayout { + Layout.alignment: Qt.AlignCenter + Layout.fillWidth: true + + spacing: 12 + + RowLayout { + Layout.fillWidth: true + Layout.maximumHeight: 24 + spacing: 0 + + Item { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + Layout.fillHeight: true + } + + Label { + id: importFromDeviceLabel + Layout.minimumHeight: 24 + Layout.minimumWidth: 234 + text: qsTr("Import from device") + font.pointSize: 13 + font.kerning: true + } + + HoverableRadiusButton { + id: pinInfoBtn + + buttonImageHeight: height + buttonImageWidth: width + + Layout.alignment: Qt.AlignVCenter + Layout.minimumWidth: 24 + Layout.maximumWidth: 24 + Layout.minimumHeight: 24 + Layout.maximumHeight: 24 + + radius: height / 2 + icon.source: "/images/icons/info-24px.svg" + backgroundColor: JamiTheme.releaseColor + + onClicked: { + pinInfoLabel.visible = !pinInfoLabel.visible + } + } + Item { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + Layout.fillHeight: true + } + } + InfoLineEdit { + id: pinFromDevice + + Layout.alignment: Qt.AlignHCenter + + selectByMouse: true + placeholderText: qsTr("PIN") + } + + InfoLineEdit { + id: passwordFromDevice + + Layout.alignment: Qt.AlignHCenter + + selectByMouse: true + echoMode: TextInput.Password + placeholderText: qsTr("Password") + } + + Label { + id: pinInfoLabel + + Layout.alignment: Qt.AlignHCenter + Layout.minimumWidth: 256 + Layout.maximumWidth: 256 + + text: qsTr("To obtain a PIN (valid for 10 minutes), you need to open the account settings on the other device and click on \"Link to another device\".") + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + visible: false + } + } + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 40 + Layout.fillWidth: true + Layout.fillHeight: true + } +} diff --git a/src/wizardview/components/SpinnerPage.qml b/src/wizardview/components/SpinnerPage.qml new file mode 100644 index 00000000..2fb3f8d0 --- /dev/null +++ b/src/wizardview/components/SpinnerPage.qml @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 + +import "../../constant" + +ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 6 + + property bool successState: true + property string progressLabelEditText: "Generating your Jami account" + + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 40 + Layout.fillWidth: true + Layout.fillHeight: true + } + Label { + id: spinnerLabel + Layout.alignment: Qt.AlignHCenter + Layout.minimumWidth: 200 + Layout.minimumHeight: 200 + + Layout.maximumWidth: 16777215 + Layout.maximumHeight: 16777215 + + property string spinnerDisplyState: successState ? "spinnerLabel_Regular" : "spinnerLabel_Failure" + onSpinnerDisplyStateChanged: { + switch (spinnerDisplyState) { + case "spinnerLabel_Regular": + background = Qt.createQmlObject("import QtQuick 2.14; +AnimatedImage { +source: \"qrc:/images/jami_eclipse_spinner.gif\" + +playing: true +paused: false +fillMode: Image.PreserveAspectFit +mipmap: true +}", spinnerLabel) + break + case "spinnerLabel_Failure": + background = Qt.createQmlObject("import QtQuick 2.14; +import \"qrc:/src/constant/\"; +Image { +anchors.fill: parent; +source:\"image://tintedPixmap/\" + (\"qrc:/images/icons/baseline-error_outline-24px.svg\").replace(\"qrc:/images/icons/\", \"\") + \"+\" + JamiTheme.urgentOrange_; +mipmap: true;}", spinnerLabel) + break + } + } + } + Item { + Layout.alignment: Qt.AlignHCenter + Layout.preferredHeight: 40 + Layout.fillWidth: true + Layout.fillHeight: true + } + Label { + id: progressLabel + Layout.alignment: Qt.AlignHCenter + text: successState ? progressLabelEditText : "Error creating account" + font.pointSize: 11 + font.kerning: true + + property string progressLabelState: successState ? "color_success" : "color_fail" + onProgressLabelStateChanged: { + switch (progressLabelState) { + case "color_success": + background = Qt.createQmlObject( + "import QtQuick 2.14; Rectangle { anchors.fill: parent; color: \"transparent\"; }", + progressLabel) + break + case "color_fail": + background = Qt.createQmlObject( + "import QtQuick 2.14; Rectangle { anchors.fill: parent; color: \"red\"; }", + progressLabel) + break + } + } + } + Item { + Layout.alignment: Qt.AlignHCenter + Layout.minimumHeight: 20 + Layout.maximumHeight: 20 + Layout.preferredHeight: 20 + Layout.fillWidth: true + Layout.fillHeight: false + } +} diff --git a/src/wizardview/components/WelcomePageLayout.qml b/src/wizardview/components/WelcomePageLayout.qml new file mode 100644 index 00000000..3c7190b0 --- /dev/null +++ b/src/wizardview/components/WelcomePageLayout.qml @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2020 by Savoir-faire Linux + * Author: Yang Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import QtQuick 2.14 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.14 + +import "../../constant" +import "../../commoncomponents" + +ColumnLayout { + property alias connectAccountManagerButtonAlias: connectAccountManagerButton + property alias newSIPAccountButtonAlias: newSIPAccountButton + + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 6 + + signal welcomePageRedirectPage(int toPageIndex) + + Item { + // put a spacer to make the buttons closs to the middle + Layout.minimumHeight: 57 + Layout.maximumHeight: 57 + Layout.preferredHeight: 57 + Layout.fillWidth: true + } + RowLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + Label { + id: welcomeLabel + Layout.maximumHeight: 40 + Layout.alignment: Qt.AlignCenter + text: qsTr("Welcome to") + font.pointSize: 30 + font.kerning: true + } + } + Item { + Layout.minimumHeight: 17 + Layout.maximumHeight: 17 + Layout.preferredHeight: 17 + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + Label { + id: welcomeLogo + Layout.alignment: Qt.AlignCenter + Layout.minimumWidth: 100 + Layout.minimumHeight: 100 + Layout.maximumWidth: 16777215 + Layout.maximumHeight: 16777215 + Layout.preferredWidth: 300 + Layout.preferredHeight: 150 + color: "transparent" + background: Image { + id: logoIMG + source: "qrc:/images/logo-jami-standard-coul.png" + fillMode: Image.PreserveAspectFit + mipmap: true + } + } + } + Item { + // put a spacer to make the buttons closs to the middle + Layout.preferredHeight: 57 + Layout.fillWidth: true + Layout.fillHeight: true + } + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.maximumHeight: 30 + Layout.alignment: Qt.AlignHCenter + HoverableGradientButton { + id: newAccountButton + + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: 256 + Layout.preferredHeight: 30 + text: qsTr("Create local account") + font.pointSize: 10 + font.kerning: true + radius: height / 2 + + onClicked: { + welcomePageRedirectPage(1) + } + } + } + RowLayout { + spacing: 6 + Layout.fillWidth: true + + Layout.maximumHeight: 30 + Layout.alignment: Qt.AlignHCenter + HoverableGradientButton { + id: fromDeviceButton + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: 256 + Layout.preferredHeight: 30 + text: qsTr("Import from device") + font.pointSize: 10 + font.kerning: true + + backgroundColor: JamiTheme.releaseColor + radius: height / 2 + + onClicked: { + welcomePageRedirectPage(5) + } + } + } + RowLayout { + spacing: 6 + Layout.fillWidth: true + + Layout.maximumHeight: 30 + Layout.alignment: Qt.AlignHCenter + HoverableGradientButton { + id: fromBackupButton + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: 256 + Layout.preferredHeight: 30 + text: qsTr("Import from backup") + font.pointSize: 10 + font.kerning: true + + backgroundColor: JamiTheme.releaseColor + radius: height / 2 + + onClicked: { + welcomePageRedirectPage(3) + } + } + } + RowLayout { + spacing: 6 + Layout.fillWidth: true + + Layout.maximumHeight: 30 + Layout.alignment: Qt.AlignHCenter + Button { + id: showAdvancedButton + Layout.preferredWidth: 256 + Layout.preferredHeight: 30 + Layout.alignment: Qt.AlignCenter + text: qsTr("Show Advanced") + font.pointSize: 8 + font.kerning: true + + background: Rectangle{ + anchors.fill: parent + + color: "transparent" + radius: height /2 + } + + onClicked: { + connectAccountManagerButton.visible = !connectAccountManagerButton.visible + newSIPAccountButton.visible = !newSIPAccountButton.visible + } + } + } + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + + Layout.maximumHeight: 30 + HoverableGradientButton { + id: connectAccountManagerButton + Layout.preferredWidth: 256 + Layout.preferredHeight: 30 + Layout.alignment: Qt.AlignCenter + text: qsTr("Connect to account manager") + visible: false + font.pointSize: 10 + font.kerning: true + + backgroundColor: JamiTheme.releaseColor + radius: height / 2 + + onClicked: { + welcomePageRedirectPage(6) + } + } + } + RowLayout { + spacing: 6 + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + Layout.maximumHeight: 30 + + HoverableGradientButton { + id: newSIPAccountButton + Layout.preferredWidth: 256 + Layout.preferredHeight: 30 + Layout.alignment: Qt.AlignCenter + text: qsTr("Add a new SIP account") + visible: false + font.pointSize: 10 + font.kerning: true + + radius: height / 2 + backgroundColor: JamiTheme.releaseColor + + onClicked: { + welcomePageRedirectPage(2) + } + } + } + Item { + // put a spacer to make the buttons closs to the middle + Layout.fillHeight: true + Layout.preferredHeight: 65 + Layout.fillWidth: true + } +} diff --git a/stylesheet.css b/stylesheet.css new file mode 100644 index 00000000..c4f8198f --- /dev/null +++ b/stylesheet.css @@ -0,0 +1,963 @@ +QToolTip { + color: white; + background-color: #109ede; + border: 1px solid white; +} + +QPushButton { + background-color: rgb(242, 242, 242); + border-style: solid; + border-width: 0px; + border-radius: 15px; + color: rgb(32, 32, 32); +} + +QPushButton#pinInfoBtn, QPushButton#backupInfoBtn { + background: transparent; +} + +QPushButton#takePhotoButton, QPushButton#importButton { + background-color: rgb(242, 242, 242); + border-radius: 5px; + border:solid 0px; +} + +QPushButton#acceptButton{ + background-color: #4caf50; + border-radius: 28px; + border:solid 1px; +} + +QPushButton#refuseButton, QPushButton#cancelButton{ + background-color: #f44336; + border-radius: 28px; + border:solid 1px; +} + +QPushButton#acceptButton:hover{ + background-color: #5db761; +} + +QPushButton#acceptButton:pressed{ + background-color: #449d48; +} + +QPushButton#refuseButton:hover, QPushButton#cancelButton:hover{ + background-color: #f5554a; +} + +QPushButton#refuseButton:pressed, QPushButton#cancelButton:pressed{ + background-color: #db3c30; +} + +QWidget#smartListOuterWidget { + background-color: rgb(255, 255, 255); +} + +QWidget#previewContainer { + background-color: black; +} + +QPushButton#btnConversations, QPushButton#btnInvites { + border-radius: 0px; + border-top-left-radius: 15px; + border-top-right-radius: 15px; + color: rgb(32, 32, 32); + height: 28px; + padding: 0px; + background-color: rgb(240, 240, 240); + border-top: 2px solid rgb(240, 240, 240); + border-right: 2px solid rgb(240, 240, 240); + border-bottom: 2px solid rgb(240, 240, 240); + border-left: 2px solid rgb(240, 240, 240); +} + +QPushButton#btnConversations:hover, QPushButton#btnInvites:hover { + background-color: rgb(188, 185, 184); + border-top: 2px solid rgb(188, 185, 184); + border-right: 2px solid rgb(188, 185, 184); + border-bottom: 2px solid rgb(188, 185, 184); + border-left: 2px solid rgb(188, 185, 184); +} + +QPushButton#btnConversations:checked, QPushButton#btnInvites:checked { + background-color: rgb(255, 255, 255); + border-bottom: 2px solid rgb(255, 255, 255); +} + +QPushButton#btnConversations:hover:checked, QPushButton#btnInvites:hover:checked { + border-top: 2px solid rgb(240, 240, 240); + border-right: 2px solid rgb(240, 240, 240); + border-bottom: 2px solid rgb(255, 255, 255); + border-left: 2px solid rgb(240, 240, 240); +} + +QPushButton#imBackButton, QPushButton#btnAcceptInvite, QPushButton#btnIgnoreInvite, +QPushButton#btnBlockInvite, QPushButton#btnAudioCall, QPushButton#btnVideoCall, +QPushButton#sendContactRequestButton, QPushButton#sendButton, QPushButton#sendIMButton, +QPushButton#btnExitSettings { + background-color: transparent; + border-style: solid; + border-width: 0px; + border-radius: 15px; + padding: 8px; + color: rgb(32, 32, 32); +} + +QPushButton#btnAcceptInvite:hover, QPushButton#btnIgnoreInvite:hover, QPushButton#btnBlockInvite:hover { + background-color: rgb(212, 212, 212); +} + +QPushButton#btnAcceptInvite:pressed, QPushButton#btnIgnoreInvite:pressed, QPushButton#btnBlockInvite:pressed { + background-color: rgb(187, 187, 187); +} + + QPushButton#imBackButton:hover, QPushButton#btnAudioCall:hover, QPushButton#btnVideoCall:hover, + QPushButton#sendContactRequestButton:hover, QPushButton#sendButton:hover, QPushButton#sendIMButton:hover, + QPushButton#btnExitSettings:hover, QPushButton#btnDeleteAccept:hover, QPushButton#btnDeleteCancel:hover, + QPushButton#changelogButton:hover, QPushButton#creditsButton:hover, QPushButton#changelogButton:hover, QPushButton#updateCancelBtn:hover, + QPushButton#updateAcceptBtn:hover, QPushButton#installBetaButton:hover, QPushButton#closeAboutDialogButton:hover { + background-color: rgb(237, 237, 237); + } + + QPushButton#imBackButton:pressed, QPushButton#btnAudioCall:pressed, QPushButton#btnVideoCall:pressed, + QPushButton#sendContactRequestButton:pressed, QPushButton#sendButton:pressed, QPushButton#sendIMButton:pressed, + QPushButton#btnExitSettings:pressed, QPushButton#btnDeleteAccept:pressed, QPushButton#btnDeleteCancel:pressed, + QPushButton#changelogButton:pressed, QPushButton#creditsButton:pressed, QPushButton#changelogButton:pressed, QPushButton#updateCancelBtn:pressed, + QPushButton#updateAcceptBtn:pressed, QPushButton#installBetaButton:pressed, QPushButton#closeAboutDialogButton:pressed { + background-color: rgb(212, 212, 212); + } + +QWidget#searchBarLayoutWidget, QWidget#smartList, +QWidget#sidePanelLayoutWidget, QWidget#leftSettingsWidget { + border-right: 2px solid rgb(240, 240, 240); +} + +RingContactLineEdit{ + border-radius: 15px; + border: 2px solid rgb(240, 240, 240); + background-color: rgb(240, 240, 240); +} + +RingContactLineEdit:focus{ + border: 2px solid rgb(240, 240, 240); + background-color: rgb(255, 255, 255); +} + +.QLineEdit{ + border-color: rgb(240, 240, 240); + border-radius: 15px; + border-width: 2px; + background-color: rgb(240, 240, 240); + padding-left: 4px; + padding-right: 4px; + height: 30px; +} + +QScrollBar:vertical { + border: none; + background: white; + width: 0px; +} + +QScrollBar::handle:vertical { + background: white; + max-width: 0px; +} + +QScrollBar:horizontal { + border: 2px solid grey; + background: grey; + height: 10px; +} + +QScrollBar::handle:horizontal { + background: white; + min-width: 20px; + border-radius: 10px; +} + +QScrollBar::add-line:horizontal { + height: 0px; +} + +QScrollBar::sub-line:horizontal { + height: 0px; +} + +QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal { + height: 0px; +} + +SmartListView { + background-color: white; + border: none; +} + +SmartListView::item:selected, QListView#BannedList::item:selected { + background-color: rgba(220, 220, 220, 255); + border: none; +} + +SmartListView::item:hover, QListView#BannedList::item:hover { + background-color: rgba(242, 242, 242, 255); +} + +QWidget#messagingHeaderWidget { + background-color: rgba(255, 255, 255, 255); + border-bottom: 2px solid rgb(240, 240, 240); +} + +QWidget#messageViewLayoutWidget, QWidget#welcomePage { + background-color: rgba(255, 255, 255, 255); +} + +QWidget#newWizardWidgetScrollAreaContents, QWidget#settingsWidgetScrollAreaContents { + background-color: rgba(255, 255, 255, 255); + border: none; +} + +QScrollArea#newWizardWidgetScrollArea, QScrollArea#settingsWidgetScrollArea { + border: none; +} + +QPushButton#holdButton, QPushButton#chatButton, QPushButton#noMicButton, QPushButton#noVideoButton, QPushButton#hangupButton, +QPushButton#transferButton, QPushButton#addPersonButton, QPushButton#joinButton, +QPushButton#qualityButton, QPushButton#recButton, QPushButton#transferCallButton, +QPushButton#sipInputPanelButton, QPushButton#addToConferenceButton { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + + QPushButton#holdButton:hover, QPushButton#chatButton:hover, QPushButton#noMicButton:hover, QPushButton#hangupButton:hover, + QPushButton#noVideoButton:hover, QPushButton#transferButton:hover, QPushButton#addPersonButton:hover, + QPushButton#joinButton:hover, QPushButton#qualityButton:hover, QPushButton#addToContactButton:hover, + QPushButton#recButton:hover, QPushButton#transferCallButton:hover, + QPushButton#sipInputPanelButton:hover, QPushButton#addToConferenceButton:hover { + background-color: transparent; + border-radius: 18px; + border: solid 1px; + } + + QPushButton#holdButton:pressed, QPushButton#chatButton:pressed, QPushButton#noMicButton:pressed, QPushButton#hangupButton:pressed, + QPushButton#noVideoButton:pressed, QPushButton#transferButton:pressed, QPushButton#addPersonButton:pressed, + QPushButton#joinButton:pressed, QPushButton#qualityButton:pressed, QPushButton#addToContactButton:pressed, + QPushButton#recButton:pressed, QPushButton#addToConferenceButton:pressed { + background-color: transparent; + border-radius: 18px; + border: solid 1px; + } + + QPushButton#holdButton:checked, QPushButton#noMicButton:checked, QPushButton#hangupButton:checked, + QPushButton#noVideoButton:checked, QPushButton#recButton:checked, + QPushButton#chatButton:checked, QPushButton#transferCallButton:checked, + QPushButton#sipInputPanelButton:checked, QPushButton#addToConferenceButton:checked { + background-color: transparent; + border-radius: 18px; + border: solid 1px; + } + +QPushButton#updateCancelButton { + padding: 4px; + background: #369; + color: white; + font-size: 12px; + border: 0; + border-radius: 3px; + outline: 0px; + width: 50px; +} + +QPushButton#updateCancelButton:hover { + background: #47a; +} + +QPushButton#updateCancelButton:pressed { + background: #58b; +} + +QMessageBox QPushButton { + padding: 4px; + background: #369; + color: white; + font-size: 12px; + border: 0; + border-radius: 3px; + outline: 0px; + width: 50px; +} + +QMessageBox QPushButton:hover { + background: #47a; +} + +QMessageBox QPushButton:pressed { + background: #58b; +} + +QToolButton#qrButton, QToolButton#shareButton { + background-color: rgb(242, 242, 242); + border-style: solid; + border-width: 0px; + border-radius: 15px; + padding: 8px; + color: rgb(32, 32, 32); +} + + QToolButton#qrButton:hover, QToolButton#shareButton:hover { + background-color: rgb(237, 237, 237); + } + + QToolButton#qrButton:pressed, QToolButton#shareButton:pressed { + background-color: rgb(212, 212, 212); + } + +QToolButton#qrButton:checked { + background-color: rgb(237, 237, 237); +} + +QPushButton#btnvideo{ + background-color: #109ede; + border-radius: 15px; + border:solid 1px; +} + +QWidget#messagingPage, QWidget#contactRequestPage, +QDialog#DeleteAccountDialog, QDialog#UpdateDownloadDialog, +QDialog#UpdateConfirmDialog, QDialog#AccountMigrationDialog { + background: rgb(255, 255, 255); +} + +QWidget#messagingPage { + background: rgb(255, 255, 255); +} + +QDialog#PasswordDialog, +QDialog#LinkDeviceDialog, +QDialog#DeleteAccountDialog { + background: rgb(255, 255, 255); +} + +QPushButton#deleteAccountPushButton { + background-color: red; + border: 0px; + border-radius: 15px; + height: 30px; + color: white; +} + +QPushButton#deleteAccountPushButton:hover { + background-color: rgb(230, 0, 0); +} + +QPushButton#deleteAccountPushButton:disabled { + background-color: rgba(255, 0, 0, 0.8); +} + +QPushButton#nextButton, QPushButton#playButton, QPushButton#clearHistoryButton, QPushButton#doTransferButton, +QPushButton#photoButton, QPushButton#migrationPushButton, +QPushButton#fromDeviceButton, QPushButton#newAccountButton, QPushButton#previousButton, QPushButton#exportButton, QPushButton#newSIPAccountButton, QPushButton#fromBackupButton, QPushButton#connectAccountManagerButton, +QPushButton#cancelAddButton, QPushButton#exportOnRingButton, QPushButton#addDeviceButton, QPushButton#exportEndedOkButton, +QPushButton#errorPushButton, QPushButton#registerButton, QPushButton#acceptCRButton, QPushButton#discardCRButton, QPushButton#deleteCancelBtn, +QPushButton#skipBtn, QPushButton#exportBtn, +QPushButton#dhtImportBtn, QPushButton#fileImportBtn, QPushButton#changePassBtn, QPushButton#confirmChangeBtn, QPushButton#backButton { + background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #109ede, stop: 1.0 #2b5084); + border: 0px; + border-radius: 15px; + height: 30px; + color: white; +} + +QPushButton#nextButton:disabled, QPushButton#playButton:disabled, QPushButton#clearHistoryButton:disabled, +QPushButton#doTransferButton:disabled, QPushButton#photoButton:disabled, QPushButton#fromBackupButton:disabled +QPushButton#existingPushButton:disabled, QPushButton#newAccountButton:disabled, QPushButton#previousButton:disabled, QPushButton#newSIPAccountButton:disabled, +QPushButton#exportButton:disabled, QPushButton#cancelAddButton:disabled, QPushButton#exportOnRingButton:disabled, +QPushButton#addDeviceButton:disabled, QPushButton#exportEndedOkButton:disabled, QPushButton#errorPushButton:disabled, +QPushButton#registerButton:disabled, QPushButton#acceptCRButton:disabled, QPushButton#discardCRButton:disabled, +QPushButton#deleteCancelBtn:disabled, QPushButton#dhtImportBtn:disabled, QPushButton#fileImportBtn:disabled, +QPushButton#changePassBtn:disabled, QPushButton#confirmChangeBtn:disabled, QPushButton#backButton:disabled, +QPushButton#fromDeviceButton:disabled, QPushButton#connectAccountManagerButton:disabled, QPushButton#exportBtn:disabled, +QPushButton#skipBtn:disabled, QPushButton#migrationPushButton:disabled { + background: rgba(242, 242, 242, 0.8); + color: grey; +} + +QPushButton#blockCRButton, QPushButton#debanButton, QPushButton#deleteBanBtn, +QPushButton#cancelChangeBtn { + background-color: rgb(251, 72, 71); + border: 0px; + color: white; +} + +QPushButton#nextButton:hover, QPushButton#playButton:hover, QPushButton#clearHistoryButton:hover, +QPushButton#doTransferButton:hover, QPushButton#photoButton:hover, QPushButton#fromBackupButton:hover, +QPushButton#existingPushButton:hover, QPushButton#newAccountButton:hover, QPushButton#previousButton:hover, QPushButton#newSIPAccountButton:hover, +QPushButton#exportButton:hover, QPushButton#cancelAddButton:hover, QPushButton#exportOnRingButton:hover, +QPushButton#addDeviceButton:hover, QPushButton#exportEndedOkButton:hover, QPushButton#errorPushButton:hover, +QPushButton#registerButton:hover, QPushButton#acceptCRButton:hover, QPushButton#discardCRButton:hover, +QPushButton#deleteCancelBtn:hover, QPushButton#dhtImportBtn:hover, QPushButton#fileImportBtn:hover, QPushButton#migrationPushButton:hover, +QPushButton#changePassBtn:hover, QPushButton#confirmChangeBtn:hover, QPushButton#backButton:hover, QPushButton#skipBtn:hover, +QPushButton#fromDeviceButton:hover, QPushButton#connectAccountManagerButton:hover, QPushButton#exportBtn:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #2b4b7e, stop: 1.0 #001d4d); + color: white; +} + +QPushButton#blockCRButton:hover, QPushButton#deleteAcceptBtn:hover, QPushButton#debanButton:hover, +QPushButton#deleteButton:hover, QPushButton#deleteBanBtn:hover, QPushButton#cancelChangeBtn:hover { + background-color: rgb(252, 91, 90); +} + +QPushButton#nextButton:pressed, QPushButton#playButton:pressed, QPushButton#clearHistoryButton:pressed, +QPushButton#doTransferButton:pressed, QPushButton#photoButton:pressed, QPushButton#skipBtn:pressed, QPushButton#migrationPushButton:pressed, +QPushButton#existingPushButton:pressed, QPushButton#newAccountButton:pressed, QPushButton#previousButton:pressed, QPushButton#newSIPAccountButton:pressed, +QPushButton#exportButton:pressed, QPushButton#cancelAddButton:pressed, QPushButton#exportOnRingButton:pressed, +QPushButton#addDeviceButton:pressed, QPushButton#exportEndedOkButton:pressed, QPushButton#errorPushButton:pressed, +QPushButton#registerButton:pressed, QPushButton#acceptCRButton:pressed, QPushButton#discardCRButton:pressed, +QPushButton#deleteCancelBtn:pressed, QPushButton#dhtImportBtn:pressed, QPushButton#fileImportBtn:pressed, +QPushButton#changePassBtn:pressed, QPushButton#confirmChangeBtn:pressed, QPushButton#backButton:pressed, QPushButton#exportBtn:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #043161, stop: 1.0 #00113f); + color: white; +} + +QPushButton#blockCRButton:pressed, QPushButton#deleteAcceptBtn:pressed, QPushButton#debanButton:pressed, +QPushButton#deleteBanBtn:pressed, QPushButton#cancelChangeBtn:pressed{ + background-color: rgb(219, 55, 54); +} + +QPushButton#btnChangePasswordCancel { + background-color: rgb(252, 91, 90); + color: white; +} + +QPushButton#btnChangePasswordCancel:hover { + background-color: red; + color: white; +} + +QPushButton#btnChangePasswordCancel:pressed { + background-color: darkred; + color: white; +} + +QComboBox{ + background: transparent; + border-radius: 0px; + border-style: solid; + border-width: 2px; + border-color: #414141; + border-bottom: transparent; + border-top: transparent; + border-left: transparent; + border-right: transparent; + font-family: "Sans Serif"; + padding: 2px; +} + +QComboBox::down-arrow{ + border-radius: 0px; + border-style: solid; + border-width: 2px; + height: 36; + width : 36; + image: url(":/images/icons/ic_arrow_drop_down_black_18dp_2x.png"); +} + +QComboBox::drop-down{ + border: 0; + width: 36px; +} + +QRadioButton::indicator{ + width: 1 ; + height: 12px; + border-radius: 8px; + border-width: 2px; + border-style: solid; +} + +QRadioButton::indicator:unchecked{ + border-color: rgb(77, 77, 77); + background: transparent; +} + +QRadioButton::indicator:checked{ + background: #109ede; + border-color: #109ede; + image: url(":/images/icons/ic_check_white_18dp_2x.png"); +} + +QWidget#horizontalWidget{ + background: transparent; +} + +QCheckBox::indicator{ + height : 12 px; + width : 12 px; + border: 2px solid rgb(77, 77, 77); + border-radius: 4px; + background: none; +} + +QCheckBox::indicator:checked{ + border-color: #2b4b7e; + background: #2b4b7e; + image: url(":/images/icons/ic_check_white_18dp_2x.png"); +} + +QSpinBox::down-button{ + height: 9; + width : 9; + image: url(":/images/icons/ic_arrow_drop_down_black_9dp_2x.png"); +} + +QSpinBox::up-button{ + height: 9; + width : 9; + image: url(":/images/icons/ic_arrow_drop_up_black_9dp_2x.png"); +} + +QLineEdit#lrcfg_alias:focus, QLineEdit#lrcfg_hostname:focus, QLineEdit#lrcfg_password:focus, +QLineEdit#lrcfg_proxy:focus, QLineEdit#lrcfg_mailbox:focus, QLineEdit#lrcfg_tlsPassword:focus, +QLineEdit#lrcfg_tlsServerName:focus, QLineEdit#lrcfg_turnServerRealm:focus, QLineEdit#lrcfg_turnServerUsername:focus, +QLineEdit#lrcfg_turnServerPassword:focus, QLineEdit#lrcfg_turnServer:focus, QLineEdit#lrcfg_publishedAddress:focus, +QLineEdit#lrcfg_registeredName:focus, QLineEdit#lrcfg_nameServiceURL:focus, QLineEdit#lrcfg_username:focus, +QLineEdit#profileNameEdit:focus{ + border-color: #109ede; +} + +QTabWidget#tabWidget{ + border-color: transparent; +} + +QPushButton#upAudioButton, QPushButton#downAudioButton, QPushButton#upVideoButton, QPushButton#downVideoButton{ + background: transparent; +} + +QPushButton#avatarButton:hover { + border: 2px solid rgb(77, 77, 77); + border-radius: 50px; +} + +QWidget#leftPannel{ + background: white; +} + +QSlider::groove:vertical{ + background: red; + position: absolute; /* absolutely position 4px from the left and right of the widget. setting margins on the widget should work too... */ + width:2px; +} + +QSlider::handle:vertical{ + height: 10px; + background: #109ede; + border-radius: 5px; + margin: 0 -4px; +} + +QSlider::sub-page:vertical{ + background: #777777; +} + +QSlider::add-page:vertical{ + background: #109ede; +} + +QToolButton::menu-button {image:none;} + +QToolButton::menu-arrow {image:none;} + +QLabel#accountIdLabel, QLabel#accountAliasLabel, QLabel#deletionLabel1, QLabel#deletionLabel2, +QLabel#warningLabel{ + background: transparent; +} + +QLabel#accountAliasLabel, QLabel#deletionLabel1, QLabel#deletionLabel2{ + color: black; +} + +QLabel#warningLabel{ + color: red; +} + +QLabel#labelWarning { + color: darkorange; +} + +QLabel#accountIdLabel{ + color: LightSlateGrey; + font-style: italic; +} + +QLabel#accountAliasLabel{ + font-weight: bold; +} + +QLabel#dragDropLabel { + /*rgba 0.95 opacity (transparency)*/ + font-size: 20px; + border: 2px dashed black; + border-radius: 10px; + background: rgba(216, 234, 252, 0.95); +} + +QLabel#wrongPasswordLabel { + color: red; + margin-left: 8px; +} + +/* setAvatarDialog { */ +QPushButton#pictureButton:default, QPushButton#fileButton:default { + background-color: rgb(0, 192, 213,); +} + +QPushButton#pictureButton:checked, QPushButton#fileButton:checked { + background-color: rgb(0, 0, 213); +} +/* } setAvatarDialog */ + +QSpinBox { + background-color: rgb(240, 240, 240); + border-radius: 14px; + height: 28px; + padding: 2px; + margin-bottom: 4px; +} + +QSpinBox::up-button { + subcontrol-origin: border; + subcontrol-position: top right; + width: 16px; + height: 18px; + padding-right: 4px; + margin-top: 3px; +} + +QSpinBox::down-button { + subcontrol-origin: border; + subcontrol-position: bottom right; + width: 16px; + height: 18px; + padding-right: 4px; + margin-bottom: 3px; +} + +/* SettingsWidget { */ +SettingsWidget QLabel#exportedPIN { + color: darkblue; + margin-bottom: 4px; +} + +SettingsWidget QPushButton { + padding-right: 16px; + padding-left: 16px; + border-style: solid; + border-width: 0px; + border-radius: 15px; + color: rgb(32, 32, 32); + background-color: rgb(242, 242, 242); +} + +SettingsWidget QPushButton:hover { + background-color: rgb(237, 237, 237); +} + +SettingsWidget QPushButton:pressed { + background-color: rgb(212, 212, 212); +} + +SettingsWidget QPushButton:disabled { + color: gray; +} + +SettingsWidget QLabel:disabled { + color: rgb(160, 160, 160); +} + +SettingsWidget QWidget#leftSettingsWidget, +SettingsWidget QWidget#rightSettingsWidget, +SettingsWidget QWidget#DeleteAccountDialog, +SettingsWidget QWidget#advancedSettingsWidget, +SettingsWidget QWidget#advancedSIPSettingsWidget, +SettingsWidget QWidget#NameRegistrationDialog, +SettingsWidget QWidget#centralWidget, +SettingsWidget QWidget#centralSIPWidget, +SettingsWidget QWidget#scrollAreaWidgetContents, +SettingsWidget QWidget#scrollAreaSIPWidgetContents { + background-color: rgba(255, 255, 255, 255); +} + +SettingsWidget .QLineEdit { + border-radius: 15px; + background-color: rgb(240, 240, 240); + padding-left: 8px; + padding-right: 8px; + height: 30px; +} + +SettingsWidget QLineEdit#currentRingID { + border: 0px; + border-radius: 0px; + padding: 0px; + background-color: transparent; + font-weight: bold; +} + +SettingsWidget QLineEdit#currentRegisteredID { + border: 2px; + border-radius: 15px; + padding: 1px; + padding-left: 8px; + margin-right: 4px; + border-style: solid; + background-color: rgb(240, 240, 240); +} + +SettingsWidget QPushButton#regNameButton { + background-color: transparent; +} + +SettingsWidget QWidget#videoLayoutWidget { + background-color: black; +} + +SettingsWidget QPushButton#btnDeletAccount, +SettingsWidget QPushButton#btnSIPDeletAccount { + background-color: rgb(252, 91, 90); + color: white; +} + +SettingsWidget QPushButton#btnDeletAccount:hover, +SettingsWidget QPushButton#btnSIPDeletAccount:hover { + background-color: red; + color: white; +} + +SettingsWidget QPushButton#btnDeletAccount:pressed, +SettingsWidget QPushButton#btnSIPDeletAccount:pressed { + background-color: darkred; + color: white; +} + +QListView .QLabel#labelDeviceId { + padding-left: 4px; + font-weight: 300; +} + +QListView .QLabel#labelThisDevice { + margin-right: 16px; + font-style: italic; + color: green; +} + +QListWidget .QLabel { + background-color: transparent; +} + +QListWidget .QLineEdit { + background-color: transparent; +} + +QListWidget .QPushButton { + background-color: transparent; +} + QListWidget .QPushButton:hover { + background-color: transparent; + } + QListWidget .QPushButton:pressed { + background-color: transparent; + } + +QListView::item:selected { + background-color: rgb(220, 220, 220); +} + +SettingsWidget QListView { + background-color: whitesmoke; + border: 1px solid rgb(212, 212, 212); + font-size: 16px; +} + +SettingsWidget QListView::item { + height: 30px; + font: 30px; + padding: 8px; +} + +SettingsWidget QListView::indicator { + height: 12 px; + width: 12 px; + border: 2px solid rgb(77, 77, 77); + border-radius: 4px; + background: none; +} + +SettingsWidget QListView::indicator:checked { + border-color: #2b4b7e; + background: #2b4b7e; + image: url(":/images/icons/ic_check_white_18dp_2x.png"); +} + +SettingsWidget QListView::item:selected { + background-color: rgba(220, 220, 220, 255); + border: none; + color: black; +} + +SettingsWidget QListView::item:hover { + background-color: rgba(229, 229, 229, 255); +} + +SettingsWidget QListView::item:disabled { + background-color: transparent; +} + +/* } SettingsWidget */ + +QPushButton#panelButton_0, QPushButton#panelButton_1, QPushButton#panelButton_2, +QPushButton#panelButton_3, QPushButton#panelButton_4, QPushButton#panelButton_5, +QPushButton#panelButton_6, QPushButton#panelButton_7, QPushButton#panelButton_8, +QPushButton#panelButton_9, QPushButton#panelButton_hash, QPushButton#panelButton_A, +QPushButton#panelButton_B, QPushButton#panelButton_C, QPushButton#panelButton_D { + text-align: center; + text-decoration: none; + background: #369; + color: white; + border: 0; + border-radius: 19px; + padding-bottom: 4px; +} + +QPushButton#panelButton_star { + text-align: center; + text-decoration: none; + background: #369; + color: white; + border: 0; + border-radius: 19px; + padding-top: 4px; +} + +QPushButton#panelButton_0:hover, QPushButton#panelButton_1:hover, QPushButton#panelButton_2:hover, +QPushButton#panelButton_3:hover, QPushButton#panelButton_4:hover, QPushButton#panelButton_5:hover, +QPushButton#panelButton_6:hover, QPushButton#panelButton_7:hover, QPushButton#panelButton_8:hover, +QPushButton#panelButton_9:hover, QPushButton#panelButton_hash:hover, QPushButton#panelButton_star:hover, +QPushButton#panelButton_A:hover, QPushButton#panelButton_B:hover, QPushButton#panelButton_C:hover, +QPushButton#panelButton_D:hover { + background: #47a; +} + +QPushButton#panelButton_0:pressed, QPushButton#panelButton_1:pressed, QPushButton#panelButton_2:pressed, +QPushButton#panelButton_3:pressed, QPushButton#panelButton_4:pressed, QPushButton#panelButton_5:pressed, +QPushButton#panelButton_6:pressed, QPushButton#panelButton_7:pressed, QPushButton#panelButton_8:pressed, +QPushButton#panelButton_9:pressed, QPushButton#panelButton_hash:pressed, QPushButton#panelButton_star:pressed, +QPushButton#panelButton_A:pressed, QPushButton#panelButton_B:pressed, QPushButton#panelButton_C:pressed, +QPushButton#panelButton_D:pressed { + background: #58b; +} + +QProgressBar#audioInputMeter { + border: 0px solid #606060; + background: lightgrey; +} + + QProgressBar#audioInputMeter::chunk { + background-color: #0c79aa; + margin: 2px; + } + +QLabel#nameLabel { + font-size: 20px; + font-style: initial; + qproperty-alignment: AlignCenter; + color: white; +} + +QWidget#videoPage { + background: black; +} + +QPushButton#recordOverlayStartOrFinishRecordingBtn, QPushButton#recordOverlayDeleteBtn, QPushButton#recordOverlayPlayBtn, QPushButton#recordOverlayRerecordBtn, QPushButton#recordOverlaySendBtn, +QPushButton#recordOverlayStopPlayingBtn { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + +QPushButton#recordOverlayStartOrFinishRecordingBtn:hover, QPushButton#recordOverlayDeleteBtn:hover, QPushButton#recordOverlayPlayBtn:hover, QPushButton#recordOverlayRerecordBtn:hover, QPushButton#recordOverlaySendBtn:hover, +QPushButton#recordOverlayStopPlayingBtn:hover { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + +QPushButton#recordOverlayStartOrFinishRecordingBtn:pressed, QPushButton#recordOverlayDeleteBtn:pressed, QPushButton#recordOverlayPlayBtn:pressed, QPushButton#recordOverlayRerecordBtn:pressed, QPushButton#recordOverlaySendBtn:pressed, +QPushButton#recordOverlayStopPlayingBtn:pressed { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + +QPushButton#recordOverlayStartOrFinishRecordingBtn:checked, QPushButton#recordOverlayDeleteBtn:checked, QPushButton#recordOverlayPlayBtn:checked, QPushButton#recordOverlayRerecordBtn:checked, QPushButton#recordOverlaySendBtn:checked, +QPushButton#recordOverlayStopPlayingBtn:checked { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + +QWidget#RecordWidget { + background-color: transparent; +} + +QLabel#timerLabel { + background-color: transparent; + color: white; +} + +QProgressBar#levelMeter { + border: 2px solid white; + text-align: center; + background: transparent; +} + + QProgressBar#levelMeter::chunk { + background-color: white; + margin: 2px; + } + +QScrollArea#changeLogScrollArea { + background-color: transparent; + color: white; + border: none; +} + +QWidget#changeLogContents { + background-color: transparent; + color: white; + border: none; +} + +QTextEdit#logTextEdit { + background-color: white; + color: black; +} + +QPushButton#logCloseButton { + background-color: rgb(189, 189, 189); +} + + QPushButton#logCloseButton:hover { + background-color: rgb(170, 170, 170); + } + QPushButton#logCloseButton:pressed { + background-color: rgb(180, 180, 180); + } + +FullScreenNotification { + font-size: 24px; + color: white; + background-color: black; + border: 2px solid white; + padding: 100px; +} \ No newline at end of file diff --git a/stylesheet.linux.css b/stylesheet.linux.css new file mode 100644 index 00000000..44eeecf5 --- /dev/null +++ b/stylesheet.linux.css @@ -0,0 +1,930 @@ + +QPushButton { + background-color: rgb(242, 242, 242); + border-style: solid; + border-width: 0px; + border-radius: 15px; + color: rgb(32, 32, 32); +} + +QPushButton#pinInfoBtn, QPushButton#backupInfoBtn { + background: transparent; +} + +QPushButton#takePhotoButton, QPushButton#importButton { + background-color: rgb(242, 242, 242); + border-radius: 5px; + border:solid 0px; +} + +QPushButton#acceptButton{ + background-color: #4caf50; + border-radius: 28px; + border:solid 1px; +} + +QPushButton#refuseButton, QPushButton#cancelButton{ + background-color: #f44336; + border-radius: 28px; + border:solid 1px; +} + +QPushButton#acceptButton:hover{ + background-color: #5db761; +} + +QPushButton#acceptButton:pressed{ + background-color: #449d48; +} + +QPushButton#refuseButton:hover, QPushButton#cancelButton:hover{ + background-color: #f5554a; +} + +QPushButton#refuseButton:pressed, QPushButton#cancelButton:pressed{ + background-color: #db3c30; +} + +QWidget#smartListOuterWidget { + background-color: rgb(255, 255, 255); +} + +QWidget#previewContainer { + background-color: black; +} + +QPushButton#btnConversations, QPushButton#btnInvites { + border-radius: 0px; + border-top-left-radius: 15px; + border-top-right-radius: 15px; + color: rgb(32, 32, 32); + height: 28px; + padding: 0px; + background-color: rgb(240, 240, 240); + border-top: 2px solid rgb(240, 240, 240); + border-right: 2px solid rgb(240, 240, 240); + border-bottom: 2px solid rgb(240, 240, 240); + border-left: 2px solid rgb(240, 240, 240); +} + +QPushButton#btnConversations:hover, QPushButton#btnInvites:hover { + background-color: rgb(188, 185, 184); + border-top: 2px solid rgb(188, 185, 184); + border-right: 2px solid rgb(188, 185, 184); + border-bottom: 2px solid rgb(188, 185, 184); + border-left: 2px solid rgb(188, 185, 184); +} + +QPushButton#btnConversations:checked, QPushButton#btnInvites:checked { + background-color: rgb(255, 255, 255); + border-bottom: 2px solid rgb(255, 255, 255); +} + +QPushButton#btnConversations:hover:checked, QPushButton#btnInvites:hover:checked { + border-top: 2px solid rgb(240, 240, 240); + border-right: 2px solid rgb(240, 240, 240); + border-bottom: 2px solid rgb(255, 255, 255); + border-left: 2px solid rgb(240, 240, 240); +} + +QPushButton#imBackButton, QPushButton#btnAcceptInvite, QPushButton#btnIgnoreInvite, +QPushButton#btnBlockInvite, QPushButton#btnAudioCall, QPushButton#btnVideoCall, +QPushButton#sendContactRequestButton, QPushButton#sendButton, QPushButton#sendIMButton, +QPushButton#btnExitSettings { + background-color: transparent; + border-style: solid; + border-width: 0px; + border-radius: 15px; + padding: 8px; + color: rgb(32, 32, 32); +} + +QPushButton#btnAcceptInvite:hover, QPushButton#btnIgnoreInvite:hover, QPushButton#btnBlockInvite:hover { + background-color: rgb(212, 212, 212); +} + +QPushButton#btnAcceptInvite:pressed, QPushButton#btnIgnoreInvite:pressed, QPushButton#btnBlockInvite:pressed { + background-color: rgb(187, 187, 187); +} + + QPushButton#imBackButton:hover, QPushButton#btnAudioCall:hover, QPushButton#btnVideoCall:hover, + QPushButton#sendContactRequestButton:hover, QPushButton#sendButton:hover, QPushButton#sendIMButton:hover, + QPushButton#btnExitSettings:hover, QPushButton#btnDeleteAccept:hover, QPushButton#btnDeleteCancel:hover, + QPushButton#changelogButton:hover, QPushButton#creditsButton:hover, QPushButton#changelogButton:hover, QPushButton#updateCancelBtn:hover, + QPushButton#updateAcceptBtn:hover, QPushButton#installBetaButton:hover, QPushButton#closeAboutDialogButton:hover { + background-color: rgb(237, 237, 237); + } + + QPushButton#imBackButton:pressed, QPushButton#btnAudioCall:pressed, QPushButton#btnVideoCall:pressed, + QPushButton#sendContactRequestButton:pressed, QPushButton#sendButton:pressed, QPushButton#sendIMButton:pressed, + QPushButton#btnExitSettings:pressed, QPushButton#btnDeleteAccept:pressed, QPushButton#btnDeleteCancel:pressed, + QPushButton#changelogButton:pressed, QPushButton#creditsButton:pressed, QPushButton#changelogButton:pressed, QPushButton#updateCancelBtn:pressed, + QPushButton#updateAcceptBtn:pressed, QPushButton#installBetaButton:pressed, QPushButton#closeAboutDialogButton:pressed { + background-color: rgb(212, 212, 212); + } + +QWidget#searchBarLayoutWidget, QWidget#smartList, +QWidget#sidePanelLayoutWidget, QWidget#leftSettingsWidget { + border-right: 2px solid rgb(240, 240, 240); +} + +RingContactLineEdit{ + border-radius: 15px; + border: 2px solid rgb(240, 240, 240); + background-color: rgb(240, 240, 240); +} + +RingContactLineEdit:focus{ + border: 2px solid rgb(240, 240, 240); + background-color: rgb(255, 255, 255); +} + +.QLineEdit{ + border-color: rgb(240, 240, 240); + border-radius: 15px; + border-width: 2px; + background-color: rgb(240, 240, 240); + padding-left: 4px; + padding-right: 4px; + height: 30px; +} + +QScrollBar:vertical, QScrollBar:horizontal{ + background:white; width:0px; +} + +QScrollBar::handle:vertical, QScrollBar::handle:horizontal{ + background: rgb(255, 255, 255); +} + +SmartListView { + background-color: white; + border: none; +} + +SmartListView::item:selected, QListView#BannedList::item:selected { + background-color: rgba(220, 220, 220, 255); + border: none; +} + +SmartListView::item:hover, QListView#BannedList::item:hover { + background-color: rgba(242, 242, 242, 255); +} + +QWidget#messagingHeaderWidget { + background-color: rgba(255, 255, 255, 255); + border-bottom: 2px solid rgb(240, 240, 240); +} + +QWidget#messageViewLayoutWidget, QWidget#welcomePage { + background-color: rgba(255, 255, 255, 255); +} + +QPushButton#holdButton, QPushButton#chatButton, QPushButton#noMicButton, QPushButton#noVideoButton, QPushButton#hangupButton, +QPushButton#transferButton, QPushButton#addPersonButton, QPushButton#joinButton, +QPushButton#qualityButton, QPushButton#recButton, QPushButton#transferCallButton, +QPushButton#sipInputPanelButton, QPushButton#addToConferenceButton { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + + QPushButton#holdButton:hover, QPushButton#chatButton:hover, QPushButton#noMicButton:hover, QPushButton#hangupButton:hover, + QPushButton#noVideoButton:hover, QPushButton#transferButton:hover, QPushButton#addPersonButton:hover, + QPushButton#joinButton:hover, QPushButton#qualityButton:hover, QPushButton#addToContactButton:hover, + QPushButton#recButton:hover, QPushButton#transferCallButton:hover, + QPushButton#sipInputPanelButton:hover, QPushButton#addToConferenceButton:hover { + background-color: transparent; + border-radius: 18px; + border: solid 1px; + } + + QPushButton#holdButton:pressed, QPushButton#chatButton:pressed, QPushButton#noMicButton:pressed, QPushButton#hangupButton:pressed, + QPushButton#noVideoButton:pressed, QPushButton#transferButton:pressed, QPushButton#addPersonButton:pressed, + QPushButton#joinButton:pressed, QPushButton#qualityButton:pressed, QPushButton#addToContactButton:pressed, + QPushButton#recButton:pressed, QPushButton#addToConferenceButton:pressed { + background-color: transparent; + border-radius: 18px; + border: solid 1px; + } + + QPushButton#holdButton:checked, QPushButton#noMicButton:checked, QPushButton#hangupButton:checked, + QPushButton#noVideoButton:checked, QPushButton#recButton:checked, + QPushButton#chatButton:checked, QPushButton#transferCallButton:checked, + QPushButton#sipInputPanelButton:checked, QPushButton#addToConferenceButton:checked { + background-color: transparent; + border-radius: 18px; + border: solid 1px; + } + +QPushButton#updateCancelButton { + padding: 4px; + background: #369; + color: white; + font-size: 12px; + border: 0; + border-radius: 3px; + outline: 0px; + width: 50px; +} + +QPushButton#updateCancelButton:hover { + background: #47a; +} + +QPushButton#updateCancelButton:pressed { + background: #58b; +} + +QMessageBox QPushButton { + padding: 4px; + background: #369; + color: white; + font-size: 12px; + border: 0; + border-radius: 3px; + outline: 0px; + width: 50px; +} + +QMessageBox QPushButton:hover { + background: #47a; +} + +QMessageBox QPushButton:pressed { + background: #58b; +} + +QToolButton#qrButton, QToolButton#shareButton { + background-color: rgb(242, 242, 242); + border-style: solid; + border-width: 0px; + border-radius: 15px; + padding: 8px; + color: rgb(32, 32, 32); +} + + QToolButton#qrButton:hover, QToolButton#shareButton:hover { + background-color: rgb(237, 237, 237); + } + + QToolButton#qrButton:pressed, QToolButton#shareButton:pressed { + background-color: rgb(212, 212, 212); + } + +QToolButton#qrButton:checked { + background-color: rgb(237, 237, 237); +} + +QPushButton#btnvideo{ + background-color: #109ede; + border-radius: 15px; + border:solid 1px; +} + +QScrollBar:vertical{ + background: rgb(242, 242, 242); + width:10px; +} + +QScrollBar::handle:vertical{ + background: rgb(77, 77, 77); +} + +QWidget#messagingPage, QWidget#contactRequestPage, +QDialog#DeleteAccountDialog, QDialog#UpdateDownloadDialog, +QDialog#UpdateConfirmDialog, QDialog#AccountMigrationDialog { + background: rgb(255, 255, 255); +} + +QWidget#messagingPage { + background: rgb(255, 255, 255); +} + +QDialog#PasswordDialog, +QDialog#LinkDeviceDialog, +QDialog#DeleteAccountDialog { + background: rgb(255, 255, 255); +} + +QPushButton#deleteAccountPushButton { + background-color: red; + border: 0px; + border-radius: 15px; + height: 30px; + color: white; +} + +QPushButton#deleteAccountPushButton:hover { + background-color: rgb(230, 0, 0); +} + +QPushButton#deleteAccountPushButton:disabled { + background-color: rgba(255, 0, 0, 0.8); +} + +QPushButton#nextButton, QPushButton#playButton, QPushButton#clearHistoryButton, QPushButton#doTransferButton, +QPushButton#photoButton, QPushButton#migrationPushButton, +QPushButton#fromDeviceButton, QPushButton#newAccountButton, QPushButton#previousButton, QPushButton#exportButton, QPushButton#newSIPAccountButton, QPushButton#fromBackupButton, QPushButton#connectAccountManagerButton, +QPushButton#cancelAddButton, QPushButton#exportOnRingButton, QPushButton#addDeviceButton, QPushButton#exportEndedOkButton, +QPushButton#errorPushButton, QPushButton#registerButton, QPushButton#acceptCRButton, QPushButton#discardCRButton, QPushButton#deleteCancelBtn, +QPushButton#skipBtn, QPushButton#exportBtn, +QPushButton#dhtImportBtn, QPushButton#fileImportBtn, QPushButton#changePassBtn, QPushButton#confirmChangeBtn, QPushButton#backButton { + background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #109ede, stop: 1.0 #2b5084); + border: 0px; + border-radius: 15px; + height: 30px; + color: white; +} + +QPushButton#nextButton:disabled, QPushButton#playButton:disabled, QPushButton#clearHistoryButton:disabled, +QPushButton#doTransferButton:disabled, QPushButton#photoButton:disabled, QPushButton#fromBackupButton:disabled +QPushButton#existingPushButton:disabled, QPushButton#newAccountButton:disabled, QPushButton#previousButton:disabled, QPushButton#newSIPAccountButton:disabled, +QPushButton#exportButton:disabled, QPushButton#cancelAddButton:disabled, QPushButton#exportOnRingButton:disabled, +QPushButton#addDeviceButton:disabled, QPushButton#exportEndedOkButton:disabled, QPushButton#errorPushButton:disabled, +QPushButton#registerButton:disabled, QPushButton#acceptCRButton:disabled, QPushButton#discardCRButton:disabled, +QPushButton#deleteCancelBtn:disabled, QPushButton#dhtImportBtn:disabled, QPushButton#fileImportBtn:disabled, +QPushButton#changePassBtn:disabled, QPushButton#confirmChangeBtn:disabled, QPushButton#backButton:disabled, +QPushButton#fromDeviceButton:disabled, QPushButton#connectAccountManagerButton:disabled, QPushButton#exportBtn:disabled, +QPushButton#skipBtn:disabled, QPushButton#migrationPushButton:disabled { + background: rgba(242, 242, 242, 0.8); + color: grey; +} + +QPushButton#blockCRButton, QPushButton#debanButton, QPushButton#deleteBanBtn, +QPushButton#cancelChangeBtn { + background-color: rgb(251, 72, 71); + border: 0px; + color: white; +} + +QPushButton#nextButton:hover, QPushButton#playButton:hover, QPushButton#clearHistoryButton:hover, +QPushButton#doTransferButton:hover, QPushButton#photoButton:hover, QPushButton#fromBackupButton:hover, +QPushButton#existingPushButton:hover, QPushButton#newAccountButton:hover, QPushButton#previousButton:hover, QPushButton#newSIPAccountButton:hover, +QPushButton#exportButton:hover, QPushButton#cancelAddButton:hover, QPushButton#exportOnRingButton:hover, +QPushButton#addDeviceButton:hover, QPushButton#exportEndedOkButton:hover, QPushButton#errorPushButton:hover, +QPushButton#registerButton:hover, QPushButton#acceptCRButton:hover, QPushButton#discardCRButton:hover, +QPushButton#deleteCancelBtn:hover, QPushButton#dhtImportBtn:hover, QPushButton#fileImportBtn:hover, QPushButton#migrationPushButton:hover, +QPushButton#changePassBtn:hover, QPushButton#confirmChangeBtn:hover, QPushButton#backButton:hover, QPushButton#skipBtn:hover, +QPushButton#fromDeviceButton:hover, QPushButton#connectAccountManagerButton:hover, QPushButton#exportBtn:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #2b4b7e, stop: 1.0 #001d4d); + color: white; +} + +QPushButton#blockCRButton:hover, QPushButton#deleteAcceptBtn:hover, QPushButton#debanButton:hover, +QPushButton#deleteButton:hover, QPushButton#deleteBanBtn:hover, QPushButton#cancelChangeBtn:hover { + background-color: rgb(252, 91, 90); +} + +QPushButton#nextButton:pressed, QPushButton#playButton:pressed, QPushButton#clearHistoryButton:pressed, +QPushButton#doTransferButton:pressed, QPushButton#photoButton:pressed, QPushButton#skipBtn:pressed, QPushButton#migrationPushButton:pressed, +QPushButton#existingPushButton:pressed, QPushButton#newAccountButton:pressed, QPushButton#previousButton:pressed, QPushButton#newSIPAccountButton:pressed, +QPushButton#exportButton:pressed, QPushButton#cancelAddButton:pressed, QPushButton#exportOnRingButton:pressed, +QPushButton#addDeviceButton:pressed, QPushButton#exportEndedOkButton:pressed, QPushButton#errorPushButton:pressed, +QPushButton#registerButton:pressed, QPushButton#acceptCRButton:pressed, QPushButton#discardCRButton:pressed, +QPushButton#deleteCancelBtn:pressed, QPushButton#dhtImportBtn:pressed, QPushButton#fileImportBtn:pressed, +QPushButton#changePassBtn:pressed, QPushButton#confirmChangeBtn:pressed, QPushButton#backButton:pressed, QPushButton#exportBtn:pressed { + background: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #043161, stop: 1.0 #00113f); + color: white; +} + +QPushButton#blockCRButton:pressed, QPushButton#deleteAcceptBtn:pressed, QPushButton#debanButton:pressed, +QPushButton#deleteBanBtn:pressed, QPushButton#cancelChangeBtn:pressed{ + background-color: rgb(219, 55, 54); +} + +QPushButton#btnChangePasswordCancel { + background-color: rgb(252, 91, 90); + color: white; +} + +QPushButton#btnChangePasswordCancel:hover { + background-color: red; + color: white; +} + +QPushButton#btnChangePasswordCancel:pressed { + background-color: darkred; + color: white; +} + +QComboBox{ + background: transparent; + border-radius: 0px; + border-style: solid; + border-width: 2px; + border-color: #414141; + border-bottom: transparent; + border-top: transparent; + border-left: transparent; + border-right: transparent; + font-family: "Sans Serif"; + padding: 2px; +} + +QComboBox::down-arrow{ + border-radius: 0px; + border-style: solid; + border-width: 2px; + height: 36; + width : 36; + image: url(":/images/icons/ic_arrow_drop_down_black_18dp_2x.png"); +} + +QComboBox::drop-down{ + border: 0; + width: 36px; +} + +QRadioButton::indicator{ + width: 1 ; + height: 12px; + border-radius: 8px; + border-width: 2px; + border-style: solid; +} + +QRadioButton::indicator:unchecked{ + border-color: rgb(77, 77, 77); + background: transparent; +} + +QRadioButton::indicator:checked{ + background: #109ede; + border-color: #109ede; + image: url(":/images/icons/ic_check_white_18dp_2x.png"); +} + +QWidget#horizontalWidget{ + background: transparent; +} + +QCheckBox::indicator{ + height : 12 px; + width : 12 px; + border: 2px solid rgb(77, 77, 77); + border-radius: 4px; + background: none; +} + +QCheckBox::indicator:checked{ + border-color: #2b4b7e; + background: #2b4b7e; + image: url(":/images/icons/ic_check_white_18dp_2x.png"); +} + +QSpinBox::down-button{ + height: 9; + width : 9; + image: url(":/images/icons/ic_arrow_drop_down_black_9dp_2x.png"); +} + +QSpinBox::up-button{ + height: 9; + width : 9; + image: url(":/images/icons/ic_arrow_drop_up_black_9dp_2x.png"); +} + +QLineEdit#lrcfg_alias:focus, QLineEdit#lrcfg_hostname:focus, QLineEdit#lrcfg_password:focus, +QLineEdit#lrcfg_proxy:focus, QLineEdit#lrcfg_mailbox:focus, QLineEdit#lrcfg_tlsPassword:focus, +QLineEdit#lrcfg_tlsServerName:focus, QLineEdit#lrcfg_turnServerRealm:focus, QLineEdit#lrcfg_turnServerUsername:focus, +QLineEdit#lrcfg_turnServerPassword:focus, QLineEdit#lrcfg_turnServer:focus, QLineEdit#lrcfg_publishedAddress:focus, +QLineEdit#lrcfg_registeredName:focus, QLineEdit#lrcfg_nameServiceURL:focus, QLineEdit#lrcfg_username:focus, +QLineEdit#profileNameEdit:focus{ + border-color: #109ede; +} + +QTabWidget#tabWidget{ + border-color: transparent; +} + +QPushButton#upAudioButton, QPushButton#downAudioButton, QPushButton#upVideoButton, QPushButton#downVideoButton{ + background: transparent; +} + +QPushButton#avatarButton:hover { + border: 2px solid rgb(77, 77, 77); + border-radius: 50px; +} + +QWidget#leftPannel{ + background: white; +} + +QSlider::groove:vertical{ + background: red; + position: absolute; /* absolutely position 4px from the left and right of the widget. setting margins on the widget should work too... */ + width:2px; +} + +QSlider::handle:vertical{ + height: 10px; + background: #109ede; + border-radius: 5px; + margin: 0 -4px; +} + +QSlider::sub-page:vertical{ + background: #777777; +} + +QSlider::add-page:vertical{ + background: #109ede; +} + +QToolButton::menu-button {image:none;} + +QToolButton::menu-arrow {image:none;} + +QLabel#accountIdLabel, QLabel#accountAliasLabel, QLabel#deletionLabel1, QLabel#deletionLabel2, +QLabel#warningLabel{ + background: transparent; +} + +QLabel#accountAliasLabel, QLabel#deletionLabel1, QLabel#deletionLabel2{ + color: black; +} + +QLabel#warningLabel{ + color: red; +} + +QLabel#labelWarning { + color: darkorange; +} + +QLabel#accountIdLabel{ + color: LightSlateGrey; + font-style: italic; +} + +QLabel#accountAliasLabel{ + font-weight: bold; +} + +QLabel#dragDropLabel { + /*rgba 0.95 opacity (transparency)*/ + font-size: 20px; + border: 2px dashed black; + border-radius: 10px; + background: rgba(216, 234, 252, 0.95); +} + +/* setAvatarDialog { */ +QPushButton#pictureButton:default, QPushButton#fileButton:default { + background-color: rgb(0, 192, 213,); +} + +QPushButton#pictureButton:checked, QPushButton#fileButton:checked { + background-color: rgb(0, 0, 213); +} +/* } setAvatarDialog */ + +QSpinBox { + background-color: rgb(240, 240, 240); + border-radius: 14px; + height: 28px; + padding: 2px; + margin-bottom: 4px; +} + +QSpinBox::up-button { + subcontrol-origin: border; + subcontrol-position: top right; + width: 16px; + height: 18px; + padding-right: 4px; + margin-top: 3px; +} + +QSpinBox::down-button { + subcontrol-origin: border; + subcontrol-position: bottom right; + width: 16px; + height: 18px; + padding-right: 4px; + margin-bottom: 3px; +} + +/* SettingsWidget { */ +SettingsWidget QLabel#wrongPasswordLabel { + color: red; + margin-left: 8px; +} +SettingsWidget QLabel#exportedPIN { + color: darkblue; + margin-bottom: 4px; +} + +SettingsWidget QPushButton { + padding-right: 16px; + padding-left: 16px; + border-style: solid; + border-width: 0px; + border-radius: 15px; + color: rgb(32, 32, 32); + background-color: rgb(242, 242, 242); +} + +SettingsWidget QPushButton:hover { + background-color: rgb(237, 237, 237); +} + +SettingsWidget QPushButton:pressed { + background-color: rgb(212, 212, 212); +} + +SettingsWidget QPushButton:disabled { + color: gray; +} + +SettingsWidget QLabel:disabled { + color: rgb(160, 160, 160); +} + +SettingsWidget QWidget#leftSettingsWidget, +SettingsWidget QWidget#rightSettingsWidget, +SettingsWidget QWidget#DeleteAccountDialog, +SettingsWidget QWidget#advancedSettingsWidget, +SettingsWidget QWidget#advancedSIPSettingsWidget, +SettingsWidget QWidget#NameRegistrationDialog, +SettingsWidget QWidget#centralWidget, +SettingsWidget QWidget#centralSIPWidget, +SettingsWidget QWidget#scrollAreaWidgetContents, +SettingsWidget QWidget#scrollAreaSIPWidgetContents { + background-color: rgba(255, 255, 255, 255); +} + +SettingsWidget .QLineEdit { + border-radius: 15px; + background-color: rgb(240, 240, 240); + padding-left: 8px; + padding-right: 8px; + height: 30px; +} + +SettingsWidget QLineEdit#currentRingID { + border: 0px; + border-radius: 0px; + padding: 0px; + background-color: transparent; + font-weight: bold; +} + +SettingsWidget QLineEdit#currentRegisteredID { + border: 2px; + border-radius: 15px; + padding: 1px; + padding-left: 8px; + margin-right: 4px; + border-style: solid; + background-color: rgb(240, 240, 240); +} + +SettingsWidget QPushButton#regNameButton { + background-color: transparent; +} + +SettingsWidget QWidget#videoLayoutWidget { + background-color: black; +} + +SettingsWidget QPushButton#btnDeletAccount, +SettingsWidget QPushButton#btnSIPDeletAccount { + background-color: rgb(252, 91, 90); + color: white; +} + +SettingsWidget QPushButton#btnDeletAccount:hover, +SettingsWidget QPushButton#btnSIPDeletAccount:hover { + background-color: red; + color: white; +} + +SettingsWidget QPushButton#btnDeletAccount:pressed, +SettingsWidget QPushButton#btnSIPDeletAccount:pressed { + background-color: darkred; + color: white; +} + +QListView .QLabel#labelDeviceId { + padding-left: 4px; + font-weight: 300; +} + +QListView .QLabel#labelThisDevice { + margin-right: 16px; + font-style: italic; + color: green; +} + +QListWidget .QLabel { + background-color: transparent; +} + +QListWidget .QLineEdit { + background-color: transparent; +} + +QListWidget .QPushButton { + background-color: transparent; +} + QListWidget .QPushButton:hover { + background-color: transparent; + } + QListWidget .QPushButton:pressed { + background-color: transparent; + } + +QListView::item:selected { + background-color: rgb(220, 220, 220); +} + +SettingsWidget QListView { + background-color: whitesmoke; + border: 1px solid rgb(212, 212, 212); + font-size: 16px; +} + +SettingsWidget QListView::item { + height: 30px; + font: 30px; + padding: 8px; +} + +SettingsWidget QListView::indicator { + height: 12 px; + width: 12 px; + border: 2px solid rgb(77, 77, 77); + border-radius: 4px; + background: none; +} + +SettingsWidget QListView::indicator:checked { + border-color: #2b4b7e; + background: #2b4b7e; + image: url(":/images/icons/ic_check_white_18dp_2x.png"); +} + +SettingsWidget QListView::item:selected { + background-color: rgba(220, 220, 220, 255); + border: none; + color: black; +} + +SettingsWidget QListView::item:hover { + background-color: rgba(229, 229, 229, 255); +} + +SettingsWidget QListView::item:disabled { + background-color: transparent; +} + +/* } SettingsWidget */ + +QPushButton#panelButton_0, QPushButton#panelButton_1, QPushButton#panelButton_2, +QPushButton#panelButton_3, QPushButton#panelButton_4, QPushButton#panelButton_5, +QPushButton#panelButton_6, QPushButton#panelButton_7, QPushButton#panelButton_8, +QPushButton#panelButton_9, QPushButton#panelButton_hash, QPushButton#panelButton_A, +QPushButton#panelButton_B, QPushButton#panelButton_C, QPushButton#panelButton_D { + text-align: center; + text-decoration: none; + background: #369; + color: white; + border: 0; + border-radius: 19px; + padding-bottom: 4px; +} + +QPushButton#panelButton_star { + text-align: center; + text-decoration: none; + background: #369; + color: white; + border: 0; + border-radius: 19px; + padding-top: 4px; +} + +QPushButton#panelButton_0:hover, QPushButton#panelButton_1:hover, QPushButton#panelButton_2:hover, +QPushButton#panelButton_3:hover, QPushButton#panelButton_4:hover, QPushButton#panelButton_5:hover, +QPushButton#panelButton_6:hover, QPushButton#panelButton_7:hover, QPushButton#panelButton_8:hover, +QPushButton#panelButton_9:hover, QPushButton#panelButton_hash:hover, QPushButton#panelButton_star:hover, +QPushButton#panelButton_A:hover, QPushButton#panelButton_B:hover, QPushButton#panelButton_C:hover, +QPushButton#panelButton_D:hover { + background: #47a; +} + +QPushButton#panelButton_0:pressed, QPushButton#panelButton_1:pressed, QPushButton#panelButton_2:pressed, +QPushButton#panelButton_3:pressed, QPushButton#panelButton_4:pressed, QPushButton#panelButton_5:pressed, +QPushButton#panelButton_6:pressed, QPushButton#panelButton_7:pressed, QPushButton#panelButton_8:pressed, +QPushButton#panelButton_9:pressed, QPushButton#panelButton_hash:pressed, QPushButton#panelButton_star:pressed, +QPushButton#panelButton_A:pressed, QPushButton#panelButton_B:pressed, QPushButton#panelButton_C:pressed, +QPushButton#panelButton_D:pressed { + background: #58b; +} + +QProgressBar#audioInputMeter { + border: 0px solid #606060; + background: lightgrey; +} + + QProgressBar#audioInputMeter::chunk { + background-color: #0c79aa; + margin: 2px; + } + +QLabel#nameLabel { + font-size: 20px; + font-style: initial; + qproperty-alignment: AlignCenter; + color: white; +} + +QWidget#videoPage { + background: black; +} + +QPushButton#recordOverlayStartOrFinishRecordingBtn, QPushButton#recordOverlayDeleteBtn, QPushButton#recordOverlayPlayBtn, QPushButton#recordOverlayRerecordBtn, QPushButton#recordOverlaySendBtn, +QPushButton#recordOverlayStopPlayingBtn { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + +QPushButton#recordOverlayStartOrFinishRecordingBtn:hover, QPushButton#recordOverlayDeleteBtn:hover, QPushButton#recordOverlayPlayBtn:hover, QPushButton#recordOverlayRerecordBtn:hover, QPushButton#recordOverlaySendBtn:hover, +QPushButton#recordOverlayStopPlayingBtn:hover { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + +QPushButton#recordOverlayStartOrFinishRecordingBtn:pressed, QPushButton#recordOverlayDeleteBtn:pressed, QPushButton#recordOverlayPlayBtn:pressed, QPushButton#recordOverlayRerecordBtn:pressed, QPushButton#recordOverlaySendBtn:pressed, +QPushButton#recordOverlayStopPlayingBtn:pressed { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + +QPushButton#recordOverlayStartOrFinishRecordingBtn:checked, QPushButton#recordOverlayDeleteBtn:checked, QPushButton#recordOverlayPlayBtn:checked, QPushButton#recordOverlayRerecordBtn:checked, QPushButton#recordOverlaySendBtn:checked, +QPushButton#recordOverlayStopPlayingBtn:checked { + background-color: transparent; + border-radius: 18px; + border: solid 1px; +} + +QWidget#RecordWidget { + background-color: transparent; +} + +QLabel#timerLabel { + background-color: transparent; + color: white; +} + +QProgressBar#levelMeter { + border: 2px solid white; + text-align: center; + background: transparent; +} + + QProgressBar#levelMeter::chunk { + background-color: white; + margin: 2px; + } + +QScrollArea#changeLogScrollArea { + background-color: transparent; + color: white; + border: none; +} + +QWidget#changeLogContents { + background-color: transparent; + color: white; + border: none; +} + +QTextEdit#logTextEdit { + background-color: white; + color: black; +} + +QPushButton#logCloseButton { + background-color: rgb(189, 189, 189); +} + + QPushButton#logCloseButton:hover { + background-color: rgb(170, 170, 170); + } + QPushButton#logCloseButton:pressed { + background-color: rgb(180, 180, 180); + } + +FullScreenNotification { + font-size: 24px; + color: white; + background-color: black; + border: 2px solid white; + padding: 100px; +} \ No newline at end of file diff --git a/translations/ring_client_windows.ts b/translations/ring_client_windows.ts new file mode 100644 index 00000000..d6ca7236 --- /dev/null +++ b/translations/ring_client_windows.ts @@ -0,0 +1,1591 @@ + + + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + + + + + Cancel + + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + + + + + Cancel + + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + + + + + Account + + + + + + General + + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + diff --git a/translations/ring_client_windows_ar.ts b/translations/ring_client_windows_ar.ts new file mode 100644 index 00000000..c30e9633 --- /dev/null +++ b/translations/ring_client_windows_ar.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + عن RING + + + + about button + + + + + credits button + + + + + Credits + الأطراف المصممة + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + الإصدار + + + + Created by: + أحدثه: + + + + Artwork by: + مصمم العمل الفني: + + + + Based on the SFLPhone project + يستند على مشروع SFLPhone + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + استمارة + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + الاتصال + + + + STUN Address + + + + + Use STUN + إستعمل STUN + + + + Use UPnP + + + + + Use TURN + إستعمل TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + ميديا + + + + Enable Video + فعل الفيديو + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + استمارة + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + العنوان + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + تشغيل ذاتي + + + + Security + تأمين + + + + Private Key Password + كلمة سر المفتاح الخاص + + + + User Certificate + شهادة المستخدم + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + الاتصال + + + + Use STUN + إستعمل STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + إستعمل TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + ميديا + + + + Enable Video + فعل الفيديو + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + استمارة + + + + BannedItemWidget + + + Form + استمارة + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + محادثات + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + جامي هو برنامج حر للتواصل العالمي الذي يحترم حريات و خصوصيات مستخدميه. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + خطأ عند محاولة تولد الرمز الشريطي + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + أضف إلى الاتصالات المعروفة + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + يريد التحدث إليك! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + أجب + + + + Ignore + تجاهل + + + + Cancel outgoing call + + + + + Cancel + ألغِ + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + حظر جهة إتصال + + + + Copy number + نسخ الرقم + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + مكالمة قادمة من %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + إحذف + + + + Cancel account deletion + + + + + Cancel + ألغِ + + + + DeviceItemWidget + + + Form + استمارة + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + اقبل + + + + Refuse + أرفض + + + + Block + حظر + + + + LinkDeviceDialog + + + Dialog + الحوار + + + + Enter your account password + + + + + Password + كلمة السر + + + + Ok + موافق + + + + Cancel + ألغِ + + + + Exporting account + + + + + Your PIN is + + + + + PIN + رمز الأمان + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + إعدادات + + + + Exit + غادر + + + + About + عن RING + + + + Jami + جامي + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + كلمة السر + + + + Register + سجل + + + + Cancel + ألغِ + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + استمارة + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + كوّن حساب جامي + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + اربط هذا الجهاز مع حساب + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + كلمة السر: + + + + + Profile + خذ لمحة + + + + + Profile name + + + + + Account + حساب + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + سجل اسم المستخدم العام + + + + Public username edit + + + + + Choose your username + اختر اسم مستخدمك + + + + Password text input + + + + + Password text entry + + + + + + Password + كلمة السر + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + حساب SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + الوكيل + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + اسم المستخدم + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + السابق + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + التالي + + + + Open File + فتح ملف + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + ألغِ + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + استمارة + + + + Photobooth display + + + + + Choose File + إختار ملف + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + إختار ملف + + + + Files (*) + وثائق (*) + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + استمارة + + + + Settings + إعدادات + + + + Account + حساب + + + + + General + عام + + + + + Audio / Video + + + + + System + النظام + + + + Enable desktop notifications + + + + + Keep minimized on close + أبقي مصغر عند الغلق + + + + Download folder + + + + + Save in + سجل في + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + كلمة السر + + + + + Enable + تفعيل + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + خذ لمحة + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + سجل + + + + Change Password + + + + + Export Account + تصدير الحساب + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + جهات الاتصال المحظورة + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + حساب SIP + + + + Username + اسم المستخدم + + + + Hostname + اسم المضيف + + + + Proxy + الوكيل + + + + Audio + صوت + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + جهاز الإخراج + + + + Choose the output device + + + + + Video + فيديو + + + + Device + جهاز + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + دردشة + + + + Mute Mic + كتم اللاقط + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + 00:00 + + + + Hangup + أنهي المحادثة + + + + Mute Video + أبكم الفيديو + + + + VideoView + + + Share entire screen + تقاسم كل الشاشة + + + + Share screen area + تقاسم منطقة من الشاشة + + + + Share file + تقاسم الوثيقة + + + \ No newline at end of file diff --git a/translations/ring_client_windows_bg.ts b/translations/ring_client_windows_bg.ts new file mode 100644 index 00000000..78ca4e32 --- /dev/null +++ b/translations/ring_client_windows_bg.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + Относно + + + + about button + + + + + credits button + + + + + Credits + Заслуги + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + версия + + + + Created by: + Създаден от: + + + + Artwork by: + С изображения от: + + + + Based on the SFLPhone project + Основан на проекта „SFLPhone“ + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + От + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + Използване на STUN + + + + Use UPnP + + + + + Use TURN + Използване на TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Звук и видео + + + + Enable Video + Активиране на видео + + + + Video Codecs + Видео кодеци + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + От + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Адрес + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Начална установка + + + + Security + Сигурност + + + + Private Key Password + Парола за частен ключ + + + + User Certificate + Потребителски сертификат + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + Използване на STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Използване на TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Звук и видео + + + + Enable Video + Активиране на видео + + + + Audio Codecs + + + + + Video Codecs + Видео кодеци + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + От + + + + BannedItemWidget + + + Form + От + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Разговори + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Ring е безплатен софтуер за универсална комуникация, който уважава свободата и личните данни на потребителите си. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + Грешка при създаването на QR-код + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Добавяне към контактите + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Желае да говори с Вас! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Отговор + + + + Ignore + Игнориране + + + + Cancel outgoing call + + + + + Cancel + Отказ + + + + Start video call + Започване на видео разговор + + + + Start audio call + Започване на гласов разговор + + + + Clear conversation + Изчистване на разговора + + + + Remove contact + + + + + Block contact + Блокиране на контакт + + + + Copy number + Копиране на номера + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Входящо повикване от %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Изтриване + + + + Cancel account deletion + + + + + Cancel + Отказ + + + + DeviceItemWidget + + + Form + От + + + + Device Id + + + + + this device + това устройство + + + + InviteButtonsWidget + + + Accept + Приемане + + + + Refuse + Отказ + + + + Block + Блокиране + + + + LinkDeviceDialog + + + Dialog + Прозорец + + + + Enter your account password + + + + + Password + Парола + + + + Ok + Добре + + + + Cancel + Отказ + + + + Exporting account + Изнасяне на акаунта + + + + Your PIN is + + + + + PIN + ПИН + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Затваряне + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Настройки + + + + Exit + Изход + + + + About + Относно + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Парола + + + + Register + Регистриране + + + + Cancel + Отказ + + + + Registering Name + + + + + Something went wrong + + + + + Close + Затваряне + + + + Incorrect password + + + + + Network error + Мрежова грешка + + + + NewWizardWidget + + + Form + От + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Създаване на профил в Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Свързване на това устройство с акаунт + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Свържи това устройство със съществуващ акаунт + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Парола: + + + + + Profile + Профил + + + + + Profile name + + + + + Account + Акаунт + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Регистриране на публично потребителско име + + + + Public username edit + + + + + Choose your username + Изберете потребителско име + + + + Password text input + + + + + Password text entry + + + + + + Password + Парола + + + + + Password confirmation text input + + + + + Confirm password + Потвърдете паролата + + + + SIP Account + Акаунт за SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Прокси + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Потребителско име + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Предишен + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Назад + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Напред + + + + Open File + Отваряне на файл + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + Грешка при създаването на акаунта + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Отказ + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + От + + + + Photobooth display + + + + + Choose File + Изберете файл + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Изберете файл + + + + Files (*) + Всички файлове (*) + + + + QObject + + + No default mail client found + Не е открит клиент за е-поща по подразбиране + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + От + + + + Settings + Настройки + + + + Account + Акаунт + + + + + General + Общи + + + + + Audio / Video + + + + + System + Система + + + + Enable desktop notifications + + + + + Keep minimized on close + Минимизиране при затваряне + + + + Download folder + + + + + Save in + Запазване в + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Парола + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Профил + + + + + Identity + + + + + Id + + + + + Registered name + Регистрирано име + + + + Type here to register a username + + + + + Register + Регистриране + + + + Change Password + Промяна на паролата + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Блокирани контакти + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + Акаунт за SIP + + + + Username + Потребителско име + + + + Hostname + Име на сървъра + + + + Proxy + Прокси + + + + Audio + Звук + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + Изходящо устройство + + + + Choose the output device + + + + + Video + Видео + + + + Device + Устройство + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Задържан разговор + + + + Hold / Unhold + Задържане / подновяване + + + + Chat + Съобщения + + + + Mute Mic + Заглушаване на микрофона + + + + Record call + + + + + Name label + Етикет за името + + + + Time elapsed + Изминало време + + + + 00:00 + 00:00 + + + + Hangup + Затваряне + + + + Mute Video + Заглушаване на видеото + + + + VideoView + + + Share entire screen + Споделяне на целия екран + + + + Share screen area + Споделяне на област от екрана + + + + Share file + Споделяне на файл + + + \ No newline at end of file diff --git a/translations/ring_client_windows_ca.ts b/translations/ring_client_windows_ca.ts new file mode 100644 index 00000000..bf8c6851 --- /dev/null +++ b/translations/ring_client_windows_ca.ts @@ -0,0 +1,1605 @@ + + + AboutDialog + + + + About + Quant a + + + + about button + botó quant a + + + + credits button + botó de crèdits + + + + Credits + Crèdits + + + + Free as in Freedom + Free as in Freedom + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + El client de Microsoft Windows per a Jami. +Ring és un programari de comunicació segur i distribuït. + + + + version + versió + + + + Created by: + Programat per: + + + + Artwork by: + Art fet per: + + + + Based on the SFLPhone project + Basat en el projecte SFLPhone + + + + AccountItemDelegate + + + Add Account + Afegeix compte + + + + AdvancedSIPSettingsWidget + + + Form + Formulari + + + + Call Settings + Preferències de trucada + + + + Auto Answer Calls + Respon trucades automàticament + + + + Enable Custom Ringtone + Habilita to de trucada personalitzat + + + + Connectivity + Connectivitat + + + + STUN Address + Adreça STUN + + + + Use STUN + Utilitza STUN + + + + Use UPnP + Utilitza UPnP + + + + Use TURN + Utilitza TURN + + + + TURN Password + Contrasenya de TURN + + + + TURN Username + Usuari de TURN + + + + TURN Address + Adreça TURN + + + + Media + Mitjans + + + + Enable Video + Habilita el vídeo + + + + Video Codecs + Còdecs de vídeo + + + + Audio Codecs + Còdecs d'àudio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Fitxers d'àudio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Afegeix un to de trucada personalitzat + + + + AdvancedSettingsWidget + + + Form + Formulari + + + + Call Settings + Preferències de trucada + + + + Allow Calls From Untrusted Peers + Permet trucades de contactes desconeguts + + + + Auto Answer Calls + Respon trucades automàticament + + + + Enable Custom Ringtone + Habilita to de trucada personalitzat + + + + Add a custom ringtone + Afegeix un to de trucada personalitzat + + + + Name Server + Servidor de noms + + + + Address + Adreça + + + + OpenDHT Configuration + Configuració de OpenDHT + + + + Enable Proxy + Habilita servidor intermediari + + + + Bootstrap + Bootstrap + + + + Security + Seguretat + + + + Private Key Password + Contrasenya de la clau privada + + + + User Certificate + Certificat d'usuari + + + + Private Key + Clau privada + + + + CA Certificate + Certificat CA + + + + Connectivity + Connectivitat + + + + Use STUN + Utilitza STUN + + + + STUN Address + Adreça STUN + + + + Use UPnP + Utilitza UPnP + + + + Use TURN + Utilitza TURN + + + + TURN Password + Contrasenya de TURN + + + + TURN Username + Usuari de TURN + + + + TURN Address + Adreça TURN + + + + Media + Mitjans + + + + Enable Video + Habilita el vídeo + + + + Audio Codecs + Còdecs d'àudio + + + + Video Codecs + Còdecs de vídeo + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Fitxers d'àudio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formulari + + + + BannedItemWidget + + + Form + Formulari + + + + name + nom + + + + id + id + + + + CallWidget + + + Show conversations + Mostra converses + + + + Conversations + Converses + + + + Search contact text input + Cerca text del contacte + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami és un programari lliure de comunicació universal, que respecta les llibertats i la privacitat dels seus usuaris. + + + + + This is your ID. +Copy and share it with your friends! + + +Aquest és el seu ID. +Copiï'l i comparteixi'l amb els seus amics! + + + + Show ring ID QR code + Mostra codi QR de ring + + + + Share ring ID button + Botó de compartició de ID de ring + + + + Double-click to copy + Doble clic per copiar + + + + Error while generating QR Code + Error al generar el codi QR + + + + + best name + millor nom + + + + best Id + millor Id + + + + Back to homepage button + Botó per tornar a la pàgina principal + + + + Add to contacts + Afegir als contactes + + + + Show invites + Mostra invitacions + + + + Invites + Invitacions + + + + + Find a new or existing contact + Troba un contacte nou o existent + + + + Wants to talk to you! + Vol parlar amb tu! + + + + Answer incoming call button + Botó per respondre trucada entrant + + + + Ignore incoming call button + Botó per ignorar trucada entrant + + + + Answer + Respon + + + + Ignore + Ignora + + + + Cancel outgoing call + Canceŀla trucada sortint + + + + Cancel + Canceŀla + + + + Start video call + Comença una videotrucada + + + + Start audio call + Comença una trucada de veu. + + + + Clear conversation + Neteja la conversa + + + + Remove contact + Elimina el contacte + + + + Block contact + Bloqueja el contacte + + + + Copy number + Copia el número + + + + Search your received invitations + Cerca les invitacions rebudes + + + + Contact me on Jami + Contacta amb mi a Jami + + + + My Id is : + El meu Id és: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Trucada entrant de %1 + + + + DeleteAccountDialog + + + Account deletion + Eliminació de compte + + + + Do you really want to delete the following account? + Esteu segur que voleu esborrar el compte? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Si aquest compte no s'ha exportat o afegit a un altre dispositiu, es perdrà irremeiablement. + + + + Permanently delete account + Elimina el compte de forma permanent + + + + Delete + Suprimeix + + + + Cancel account deletion + Canceŀla l'eliminació del compte + + + + Cancel + Cancel·la + + + + DeviceItemWidget + + + Form + Formulari + + + + Device Id + Id del dispositiu + + + + this device + aquest dispositiu + + + + InviteButtonsWidget + + + Accept + Accepta + + + + Refuse + Rebutja + + + + Block + Bloca + + + + LinkDeviceDialog + + + Dialog + Diàleg + + + + Enter your account password + Inseriu la contrasenya del vostre compte + + + + Password + Contrasenya + + + + Ok + D'acord + + + + Cancel + Cancel·la + + + + Exporting account + Exportant compte + + + + Your PIN is + El vostre PIN és + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Aquest PIN i la contrasenya del compte tindrien que introduir-se en el vostre dispositiu en un màxim de 10 minuts. + + + + Close + Tanca + + + + Link Another Device + Vincula un altre dispositiu + + + + Incorrect password + Contrasenya incorrecta + + + + Something went wrong. +Please try again later. + Alguna cosa ha anat malament. +Si us plau, torni ha intentar-ho més tard. + + + + MainWindow + + + Settings + Opcions + + + + Exit + Sortir + + + + About + Quant a + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Estableix nom registrat + + + + Enter your account password + Inseriu la contrasenya del vostre compte + + + + Password text input + Entrada de text de la contrasenya + + + + Password text entry + Entrada de text de la contrasenya + + + + Password + Contrasenya + + + + Register + Registra + + + + Cancel + Cancel·la + + + + Registering Name + Registrant nom d'usuari + + + + Something went wrong + Alguna cosa ha anat malament + + + + Close + Tanca + + + + Incorrect password + Contrasenya incorrecta + + + + Network error + Error de xarxa + + + + NewWizardWidget + + + Form + Formulari + + + + Welcome Label + Etiqueta de Benvinguda + + + + Welcome to + Benvingut a + + + + Welcome Logo + Logo de Benvinguda + + + + Create Jami account button + Botó de creació de compte Jami + + + + Push button for Jami account creation start trigger + Botó per al disparador d'inici de la creació de compte Jami + + + + Create a Jami account + Crear un compte Jami + + + + Link device button + Botó de vinculació de dispositiu + + + + Push button for device linkage start trigger + Botó per al disparador d'inici de vinculació de dispositiu + + + + Link this device to an account + Enllaça aquest dispositiu a un compte + + + + Create Jami SIP account button + Botó de creació de compte Jami SIP + + + + Push button for Jami SIP account creation start trigger + Botó per al disparador d'inici de creació de compte Jami SIP + + + + Create a SIP account + Crea un compte SIP + + + + Link this device to an existing account + Enllaça aquest dispositiu a un compte ja existent + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Per a vincular aquest dispositiu a un altre compte, primer has </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">d'obtenir un codi PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">. Per a generar un codi PIN:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Vés a </span><span style=" font-size:14px; font-weight:600;">opcions de gestió del compte</span><span style=" font-size:14px;"> d'un dispositiu anterior</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Escull un </span><span style=" font-size:14px; font-weight:600;">compte Jami</span><span style=" font-size:14px;"> que vulguis utilitzar</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Vés a la pestanya</span><span style=" font-size:14px; font-weight:600;">Dispositius</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Selecciona</span><span style=" font-size:14px; font-weight:600;">Afegeix un dispositiu</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Obtindràs el PIN necessari per completar aquest formulari. El PIN només és vàlid durant </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minuts</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Entra el pin: + + + + Or import a file: + O importa un fitxer: + + + + Link from exported account archive file + Vincula des d'un compte exportat a un fitxer d'arxiu + + + + + + (None) + (Cap) + + + + Password: + Contrasenya: + + + + + Profile + Perfil + + + + + Profile name + Nom de perfil + + + + Account + Compte + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Registra el teu nom d'usuari. +Es reservarà el nom d'usuari de forma que només vostè pugui utilitzar-lo. +Els seus amics podran trucar-lo amb el vostre nom d'usuari +en comptes d'utilitzar el vostre ID. + + + + Public username checkbox + Capsa de verificació de nom d'usuari públic + + + + Checkbox selecting if the user wants a public username + Capsa de verificació seleccionant si l'usuari vol un nom d'usuari públic + + + + Register public username + Registra nom d'usuari públic + + + + Public username edit + Edició de nom d'usuari públic + + + + Choose your username + Esculli el vostre nom d'usuari + + + + Password text input + Entrada de text de la contrasenya + + + + Password text entry + Entrada de text de la contrasenya + + + + + Password + Contrasenya + + + + + Password confirmation text input + Entrada de text de confirmació de contrasenya + + + + Confirm password + Confirmi contrasenya + + + + SIP Account + Compte SIP + + + + SIP Server edit + Edició de servidor SIP + + + + Server + Servidor + + + + SIP proxy input + Entrada de servidor intermediari SIP + + + + SIP proxy text entry + Entrada de text de servidor intermediari SIP + + + + Proxy + Intermediari + + + + SIP username input + Entrada de nom d'usuari SIP + + + + SIP Password text entry + Entrada de text de contraseña SIP + + + + Username + Nom d'usuari + + + + + SIP Password text input + Entrada de text de contraseña SIP + + + + Generating your Jami account… + Generant el seu compte Jami… + + + + Previous page button + Botó de pàgina anterior + + + + push button to access previous page of wizard + Botó per accedir a la pàgina anterior de l'assistent + + + + Previous + Previ + + + + Cancel account create/link + Canceŀla creació/vinculació de compte + + + + push button to cancel account creation or linking + Botó per a canceŀlar la creació o vinculació del compte + + + + Back + Enrere + + + + Next page Button + Botó següent pàgina + + + + Push button to access next page of wizard + Botó per accedir a la pàgina següent de l'assistent + + + + Next + Endavant + + + + Open File + Obre fitxer + + + + Jami archive files (*.gz); All files (*) + Arxius Jami (*.gz); Tots els fitxers (*) + + + + Your account needs to be migrated. Enter your password. + El vostre compte s'ha de migrar. Inseriu la vostra contrasenya + + + + Migrating your Jami account... + Migrant el teu compte Jami… + + + + Importing account archive... + Important l'arxiu del compte… + + + + Generating your Jami account... + Generant el seu compte Jami… + + + + Generating your SIP account... + Generant el seu compte SIP… + + + + Error creating account + Error al crear el compte + + + + PasswordDialog + + + Change Account Password + Canvia la contrasenya del compte + + + + Enter Current Password + Inseriu la contrasenya actual + + + + Enter New Password + Inseriu la nova contrasenya + + + + Confirm New Password + Confirmi la nova contrasenya + + + + Confirm + Confirma + + + + Cancel + Cancel·la + + + + Current Password Incorrect + Contrasenya actual incorrecta + + + + PhotoBoothDialog + + + Photobooth + Galeria de fotos + + + + PhotoboothWidget + + + Form + Formulari + + + + Photobooth display + Vista de la galeria de fotos + + + + Choose File + Esculli un fitxer + + + + Image Files (*.jpg *.jpeg *.png) + Fitxers d'imatge (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Esculli un fitxer + + + + Files (*) + Fitxers (*) + + + + QObject + + + No default mail client found + No s'ha detectat cap client de correu electrònic + + + + + Edit Device Name + Editar nom de dispositiu + + + + Unlink Device From Account + Desvincula dispositiu del compte + + + + Save new device name + Desa el nou nom de dispositiu + + + + Add as contact + Afegir com a contacte + + + + RingButton + + + Select folder + Esculli carpeta + + + + SettingsWidget + + + Form + Formulari + + + + Settings + Opcions + + + + Account + Compte + + + + + General + General + + + + + Audio / Video + Àudio / Vídeo + + + + System + Sistema + + + + Enable desktop notifications + Habilitar notificacions d'escriptori + + + + Keep minimized on close + Manté minimitzat al tancar + + + + Download folder + Descarrega carpeta + + + + Save in + Desa a + + + + Always Recording + Sempre enregistrant + + + + Updates + Actualitzacions + + + + Check for updates automatically every + Comprova actualitzacions cada + + + + Interval between update checks in days selector + Selector d'interval entre comprovacions d'actualitzacions + + + + days + dies + + + + Check for updates now button + Botó de comprovació immediata d'actualitzacions + + + + Check for updates now + Comprova actualitzacions immediatament + + + + Password + Contrasenya + + + + + Enable + Habilita + + + + toggle enable notifications + Commuta habilitar notificacions + + + + Toggle keep minimized on close + Commuta mantenir minimitzat al tancar + + + + Call Recordings + Enregistraments de trucades + + + + Toggle automatic updates + Commuta actualitzacions automàtiques + + + + Jami Account + Compte Jami + + + + + Profile + Perfil + + + + + Identity + Identitat + + + + Id + Id + + + + Registered name + Nom registrat + + + + Type here to register a username + Escrigui aquí per registrar un nom d'usuari + + + + Register + Registra + + + + Change Password + Canvia la contrasenya + + + + Export Account + Exporta el compte + + + + + Delete Account + Esborrar compte + + + + Linked Devices + Dispositius vinculats + + + + Link Another Device + Vincula un altre dispositiu + + + + Banned Contacts + Contactes blocats + + + + Format + Formata + + + + Video device framerate selector + Selector de taxa de fotogrames del dispositiu de vídeo + + + + Preview unavailable + Previsualització no disponible + + + + + Advanced Account Settings + Preferències avançades del compte + + + + SIP Account + Compte SIP + + + + Username + Nom d'usuari + + + + Hostname + Nom d'amfitrió + + + + Proxy + Intermediari + + + + Audio + Àudio + + + + Microphone + Micròfon + + + + Audio input device selector + Selector de dispositiu d'entrada d'àudio + + + + Output Device + Dispositiu de sortida + + + + Choose the output device + Esculli dispositiu de sortida + + + + Video + Vídeo + + + + Device + Dispositiu + + + + Video device selector + Selector de dispositiu de vídeo + + + + A registered name should not have any spaces and must be at least three letters long + Un nom registrat no pot tenir espais i ha de tenir una longitud mínima de tres caràcters + + + + This name is already taken + Aquest nom ja està registrat + + + + + Enter an alias + Inseriu un alias + + + + Register this name + Registri aquest nom + + + + Remove Device + Elimini el dispositiu + + + + Enter this account's password to confirm the removal of this device + Insereix la contrasenya d'aquest compte per confirmar l'eliminació d'aquest dispositiu + + + + Are you sure you wish to remove this device? + Esteu segur que voleu eliminar aquest dispositiu? + + + + Export Account Here + Exporta el compte aquí + + + + Select A Folder For Your Downloads + Seleccioni una carpeta per les seves descàrregues + + + + Select A Folder For Your Recordings + Seleccioni una carpeta per als seus enregistraments + + + + VideoOverlay + + + Call on Hold + Trucada en espera + + + + Hold / Unhold + Reté / Allibera + + + + Chat + Xat + + + + Mute Mic + Silencia el micròfon + + + + Record call + Enregistra trucada + + + + Name label + Etiqueta de nom + + + + Time elapsed + Temps transcorregut + + + + 00:00 + 00:00 + + + + Hangup + Penjar + + + + Mute Video + Silencia el vídeo + + + + VideoView + + + Share entire screen + Comparteix tota la pantalla + + + + Share screen area + Compartir pantalla + + + + Share file + Compartir arxiu + + + \ No newline at end of file diff --git a/translations/ring_client_windows_cs_CZ.ts b/translations/ring_client_windows_cs_CZ.ts new file mode 100644 index 00000000..5c0e9d1b --- /dev/null +++ b/translations/ring_client_windows_cs_CZ.ts @@ -0,0 +1,1590 @@ + + + AboutDialog + + + + About + O aplikaci + + + + about button + O + + + + credits button + Zásluhy + + + + Credits + Zásluhy + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Klient Microsoft Windows pro Jami. +Jami je bezpečný a distribuovaný komunikační program. + + + + version + verze + + + + Created by: + Vytvořili: + + + + Artwork by: + Grafika: + + + + Based on the SFLPhone project + Postaveno na projektu SFLPhone + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + Formulář + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Připojení + + + + STUN Address + + + + + Use STUN + Použít STUN + + + + Use UPnP + + + + + Use TURN + Použít TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Média + + + + Enable Video + Povolit video + + + + Video Codecs + Obrazové kodeky + + + + Audio Codecs + Zvukové kodeky + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + Formulář + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + Název serveru + + + + Address + Adresa + + + + OpenDHT Configuration + + + + + Enable Proxy + Povolit proxy + + + + Bootstrap + Bootstrap + + + + Security + Zabezpečení + + + + Private Key Password + Heslo soukromého klíče + + + + User Certificate + Uživatelský certifikát + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Připojení + + + + Use STUN + Použít STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Použít TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Média + + + + Enable Video + Povolit video + + + + Audio Codecs + Zvukové kodeky + + + + Video Codecs + Obrazové kodeky + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Formulář + + + + BannedItemWidget + + + Form + Formulář + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Konverzace + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami je svobodný software určený k univerzální komunikaci, který respektuje svobody a práva uživatelů. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + Dvakrát klepněte pro kopírování + + + + Error while generating QR Code + Chyba při vytváření QR kódu + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Přidat do kontaktů + + + + Show invites + Ukázat pozvání + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Chce s vámi hovořit! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Přijmout + + + + Ignore + Přehlížet + + + + Cancel outgoing call + + + + + Cancel + Zrušit + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + Blokovat kontakt + + + + Copy number + Zkopírovat číslo + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Příchozí hovor od %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Odstranit + + + + Cancel account deletion + + + + + Cancel + Zrušit + + + + DeviceItemWidget + + + Form + Formulář + + + + Device Id + + + + + this device + toto zařízení + + + + InviteButtonsWidget + + + Accept + Přijmout + + + + Refuse + Odmítnout + + + + Block + Blokovat + + + + LinkDeviceDialog + + + Dialog + Dialog + + + + Enter your account password + + + + + Password + Heslo + + + + Ok + OK + + + + Cancel + Zrušit + + + + Exporting account + Exportuji účet + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Zavřít + + + + Link Another Device + Připojit další zařízení + + + + Incorrect password + Nesprávné heslo + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Nastavení + + + + Exit + Odejít + + + + About + O aplikaci + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Nastavit registrované jméno + + + + Enter your account password + + + + + Password text input + Zadání textu hesla + + + + Password text entry + Zadání textu hesla + + + + Password + Heslo + + + + Register + Zaregistrovat + + + + Cancel + Zrušit + + + + Registering Name + + + + + Something went wrong + + + + + Close + Zavřít + + + + Incorrect password + Nesprávné heslo + + + + Network error + Chyba sítě + + + + NewWizardWidget + + + Form + Formulář + + + + Welcome Label + + + + + Welcome to + Vítejte v + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Vytvořit účet Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Připojit toto zařízení k účtu + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Propojit zařízení s již existujícím účtem + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Vložte svůj pin: + + + + Or import a file: + Nebo zavést soubor: + + + + Link from exported account archive file + + + + + + + (None) + (žádný) + + + + Password: + Heslo: + + + + + Profile + Profil + + + + + Profile name + Název profilu + + + + Account + Účet + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + Zaškrtávací okénko pro veřejné uživatelské jméno + + + + Checkbox selecting if the user wants a public username + Zaškrtnutí okénka, pokud chce uživatel veřejné uživatelské jméno + + + + Register public username + Zaregistrovat veřejné uživatelské jméno + + + + Public username edit + Upravení veřejného uživatelského jména + + + + Choose your username + Zvolte si uživatelské jméno + + + + Password text input + Zadání textu hesla + + + + Password text entry + Zadání textu hesla + + + + + Password + Heslo + + + + + Password confirmation text input + Textové zadání potvrzení hesla + + + + Confirm password + Potvrdit heslo + + + + SIP Account + Účet SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + Zadání uživatelského jména pro SIP + + + + SIP Password text entry + + + + + Username + Uživatelské jméno + + + + + SIP Password text input + + + + + Generating your Jami account… + Vytváří se váš účet Jami… + + + + Previous page button + Předchozí strana + + + + push button to access previous page of wizard + Tlačítko pro vrácení se v průvodci o stranu zpět + + + + Previous + Předchozí + + + + Cancel account create/link + Zrušit vytváření/propojování účtu + + + + push button to cancel account creation or linking + Tlačítko pro zrušení vytváření nebo propojování účtu + + + + Back + Zpět + + + + Next page Button + Další strana + + + + Push button to access next page of wizard + Tlačítko pro přechod na další stranu průvodce + + + + Next + Další + + + + Open File + Otevřít soubor + + + + Jami archive files (*.gz); All files (*) + Archivní soubory Jami (*.gz); Všechny soubory (*) + + + + Your account needs to be migrated. Enter your password. + Váš účet musí být převeden. Zadejte své heslo. + + + + Migrating your Jami account... + Převádí se váš účet Jami… + + + + Importing account archive... + Zavádí se archiv s účtem… + + + + Generating your Jami account... + Vytváří se váš účet Jami… + + + + Generating your SIP account... + + + + + Error creating account + Chyba při vytváření účtu + + + + PasswordDialog + + + Change Account Password + Změnit heslo k účtu + + + + Enter Current Password + Zadejte nynější heslo + + + + Enter New Password + Zadejte nové heslo + + + + Confirm New Password + Potvrďte nové heslo + + + + Confirm + Potvrdit + + + + Cancel + Zrušit + + + + Current Password Incorrect + Nynější heslo je nesprávné + + + + PhotoBoothDialog + + + Photobooth + Fotoautomat + + + + PhotoboothWidget + + + Form + Formulář + + + + Photobooth display + + + + + Choose File + Vybrat soubor + + + + Image Files (*.jpg *.jpeg *.png) + Obrázkové soubory (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Vybrat soubor + + + + Files (*) + Soubory (*) + + + + QObject + + + No default mail client found + Nenalezen žádný výchozí e-mailový klient + + + + + Edit Device Name + Upravit název zařízení + + + + Unlink Device From Account + Odpojit zařízení od účtu + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + Vybrat složku + + + + SettingsWidget + + + Form + Formulář + + + + Settings + Nastavení + + + + Account + Účet + + + + + General + Obecné + + + + + Audio / Video + Zvuk/Obraz + + + + System + Systém + + + + Enable desktop notifications + + + + + Keep minimized on close + Neukončovat Ring po uzavření okna + + + + Download folder + Složka pro stahování + + + + Save in + Uložit do + + + + Always Recording + Vždy nahrávat + + + + Updates + Aktualizace + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + Zkontrolovat aktualizace nyní + + + + Password + Heslo + + + + + Enable + Povolit + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + Totožnost + + + + Id + + + + + Registered name + Registrované jméno + + + + Type here to register a username + + + + + Register + Zaregistrovat + + + + Change Password + Změnit heslo + + + + Export Account + Vyvést účet + + + + + Delete Account + Odstranit účet + + + + Linked Devices + Připojená zařízení + + + + Link Another Device + Připojit další zařízení + + + + Banned Contacts + Zakázané kontakty + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + Pokročilá nastavení účtu + + + + SIP Account + Účet SIP + + + + Username + Uživatelské jméno + + + + Hostname + Název serveru + + + + Proxy + Proxy + + + + Audio + Zvuk + + + + Microphone + Mikrofon + + + + Audio input device selector + Výběr vstupního zvukového zařízení + + + + Output Device + Výstupní zařízení + + + + Choose the output device + Vybrat název výstupního zařízení + + + + Video + Video + + + + Device + Zařízení + + + + Video device selector + Výběr obrazového zařízení + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + Toto uživatelské jméno je již zabráno + + + + + Enter an alias + + + + + Register this name + Zaregistrovat toto jméno + + + + Remove Device + Odstranit zařízení + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + Vyvést účet sem + + + + Select A Folder For Your Downloads + Vyberte složku pro vaše stahování + + + + Select A Folder For Your Recordings + Vyberte složku pro vaše nahrávání + + + + VideoOverlay + + + Call on Hold + Hovor pozastaven + + + + Hold / Unhold + Pozastavit/Pokračovat + + + + Chat + Rozhovor + + + + Mute Mic + Ztlumit mikrofon + + + + Record call + Nahrávat vše + + + + Name label + Název štítku + + + + Time elapsed + Uplynulý čas + + + + 00:00 + 00:00 + + + + Hangup + Zavěsit + + + + Mute Video + Vypnout obraz + + + + VideoView + + + Share entire screen + Sdílet celou obrazovku + + + + Share screen area + Sdílet část obrazovky + + + + Share file + Sdílet soubor + + + \ No newline at end of file diff --git a/translations/ring_client_windows_da.ts b/translations/ring_client_windows_da.ts new file mode 100644 index 00000000..62a01b76 --- /dev/null +++ b/translations/ring_client_windows_da.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + Om + + + + about button + om-knap + + + + credits button + krediteringer-knap + + + + Credits + Krediteringer + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + version + + + + Created by: + Skabt af: + + + + Artwork by: + Grafik af: + + + + Based on the SFLPhone project + Baseret på SFLPhone-projektet + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + Form + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Forbindelse + + + + STUN Address + + + + + Use STUN + Brug STUN + + + + Use UPnP + + + + + Use TURN + Brug TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Medier + + + + Enable Video + Aktiver video + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + Form + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adresse + + + + OpenDHT Configuration + + + + + Enable Proxy + Aktivér proxy + + + + Bootstrap + Bootstrap + + + + Security + Sikkerhed + + + + Private Key Password + Adgangskode for Privatnøgle + + + + User Certificate + Brugercertifikat + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Forbindelse + + + + Use STUN + Brug STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Brug TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Medier + + + + Enable Video + Aktiver video + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Form + + + + BannedItemWidget + + + Form + Form + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Samtaler + + + + Search contact text input + Tekstinput for søg efter kontakt + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami is free software for universal communication which respects the freedoms and privacy of its users. (Näcken) + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + Del ring-ID-knap + + + + Double-click to copy + Dobbeltklik for at kopiere + + + + Error while generating QR Code + Fejl ved generering af QR-kode + + + + + best name + + + + + best Id + + + + + Back to homepage button + Tilbage til hjemmeside-knap + + + + Add to contacts + Tilføj til kontakt + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Vil tale med dig! + + + + Answer incoming call button + + + + + Ignore incoming call button + Ignorer indgående opkald-knap + + + + Answer + Besvar + + + + Ignore + Afvis + + + + Cancel outgoing call + + + + + Cancel + Annuller + + + + Start video call + Start et video opkald + + + + Start audio call + Start en audio samtale + + + + Clear conversation + Ryd samtale + + + + Remove contact + Fjern kontakt + + + + Block contact + Bloker kontakt + + + + Copy number + Kopiér nummer + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Indgående kald fra %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Slet + + + + Cancel account deletion + + + + + Cancel + Annuller + + + + DeviceItemWidget + + + Form + Form + + + + Device Id + + + + + this device + denne enhed + + + + InviteButtonsWidget + + + Accept + Accepter + + + + Refuse + Afvis + + + + Block + Blokér + + + + LinkDeviceDialog + + + Dialog + Dialog + + + + Enter your account password + + + + + Password + Adgangskode + + + + Ok + Ok + + + + Cancel + Annuller + + + + Exporting account + + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Luk + + + + Link Another Device + + + + + Incorrect password + Forkert adgangskode + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Indstillinger + + + + Exit + Afslut + + + + About + Om + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + Tekstinput for adgangskode + + + + Password text entry + Tekstindtastning for adgangskode + + + + Password + Adgangskode + + + + Register + Registrér + + + + Cancel + Annuller + + + + Registering Name + + + + + Something went wrong + + + + + Close + Luk + + + + Incorrect password + Forkert adgangskode + + + + Network error + Netværksfejl + + + + NewWizardWidget + + + Form + Form + + + + Welcome Label + + + + + Welcome to + Velkommen til + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + Tilknyt enhed-knap + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Tilknyt denne enhed til en konto + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Tilnyt denne enhed til en eksisterende konto + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Indtast PIN-kode: + + + + Or import a file: + Eller importér en fil: + + + + Link from exported account archive file + Tilknyt fra eksporteret konto-arkivfil + + + + + + (None) + + + + + Password: + Adgangskode: + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Konto + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Registrer offentligt brugernavn + + + + Public username edit + + + + + Choose your username + Vælg dit brugernavn + + + + Password text input + Tekstinput for adgangskode + + + + Password text entry + Tekstindtastning for adgangskode + + + + + Password + Adgangskode + + + + + Password confirmation text input + Tekstinput for bekræftelse af adgangskode + + + + Confirm password + Bekræft adgangskode + + + + SIP Account + SIP-konto + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Stedfortræder + + + + SIP username input + Input af SIP-brugernavn + + + + SIP Password text entry + + + + + Username + Brugernavn + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + Forrige side-knap + + + + push button to access previous page of wizard + + + + + Previous + Tilbage + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Tilbage + + + + Next page Button + Næste side-knap + + + + Push button to access next page of wizard + + + + + Next + Næste + + + + Open File + Åbn fil + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + Din konto kræver migrering. Indtast din adgangskode. + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Annuller + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + Fotobås + + + + PhotoboothWidget + + + Form + Form + + + + Photobooth display + + + + + Choose File + Vælg fil + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Vælg fil + + + + Files (*) + Filer (*) + + + + QObject + + + No default mail client found + Ingen standard mail-klient fundet + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + Vælg mappe + + + + SettingsWidget + + + Form + Form + + + + Settings + Indstillinger + + + + Account + Konto + + + + + General + Generelt + + + + + Audio / Video + + + + + System + System + + + + Enable desktop notifications + + + + + Keep minimized on close + Hold minimeret ved lukning + + + + Download folder + + + + + Save in + Gem i + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + Søg efter for opdateringer nu + + + + Password + Adgangskode + + + + + Enable + Tillad + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + Identitet + + + + Id + + + + + Registered name + Registreret navn + + + + Type here to register a username + + + + + Register + Registrér + + + + Change Password + + + + + Export Account + Eksport konto + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Blokerede kontakter + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + SIP-konto + + + + Username + Brugernavn + + + + Hostname + Værtsnavn + + + + Proxy + Stedfortræder + + + + Audio + Lyd + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + Outputenhed + + + + Choose the output device + + + + + Video + Video + + + + Device + Enhed + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Samtale parkeret + + + + Hold / Unhold + Parkeret / Aktiv + + + + Chat + Chat + + + + Mute Mic + Sluk mikrofon + + + + Record call + Optag opkald + + + + Name label + Navne etiket + + + + Time elapsed + Tid gået + + + + 00:00 + 00:00 + + + + Hangup + Læg på + + + + Mute Video + Sluk mikrofon + + + + VideoView + + + Share entire screen + Del hele skærmen + + + + Share screen area + Del skærmområde + + + + Share file + Del fil + + + \ No newline at end of file diff --git a/translations/ring_client_windows_da_DK.ts b/translations/ring_client_windows_da_DK.ts new file mode 100644 index 00000000..b13f0bb4 --- /dev/null +++ b/translations/ring_client_windows_da_DK.ts @@ -0,0 +1,1591 @@ + + + + + AboutDialog + + + + About + Om + + + + about button + Knap til om + + + + credits button + Knap til krediteringer + + + + Credits + Krediteringer + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + version + + + + Created by: + Skabt af: + + + + Artwork by: + Grafik af: + + + + Based on the SFLPhone project + Baseret på SFLPhone-projektet + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + Form + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + Brug STUN + + + + Use UPnP + + + + + Use TURN + Brug TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + Form + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adresse + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + Sikkerhed + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + Brug STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Brug TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Form + + + + BannedItemWidget + + + Form + Form + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Samtaler + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + Dobbeltklik for at kopiere + + + + Error while generating QR Code + Fejl ved generering af QR code + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Tilføj til kontakt + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Vil tale med dig! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Besvar + + + + Ignore + Afvis + + + + Cancel outgoing call + + + + + Cancel + Annuller + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + Kopiér nummer + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Indgående kald fra %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + Annuller + + + + DeviceItemWidget + + + Form + Form + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Adgangskode + + + + Ok + Ok + + + + Cancel + Annuller + + + + Exporting account + + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Indstillinger + + + + Exit + Afslut + + + + About + Om + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Adgangskode + + + + Register + + + + + Cancel + Annuller + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + Form + + + + Welcome Label + + + + + Welcome to + Velkommen til + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Tilknyt denne enhed til en konto + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + Konto + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + Vælg dit brugernavn + + + + Password text input + + + + + Password text entry + + + + + + Password + Adgangskode + + + + + Password confirmation text input + + + + + Confirm password + Bekræft adgangskode + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Stedfortræder + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Brugernavn + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Tilbage + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Næste + + + + Open File + Åbn fil + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + Din konto kræver migrering. Indtast din adgangskode. + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Annuller + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + Fotobås + + + + PhotoboothWidget + + + Form + Form + + + + Photobooth display + + + + + Choose File + Vælg fil + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Vælg fil + + + + Files (*) + Filer (*) + + + + QObject + + + No default mail client found + Ingen standard mail-klient fundet + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + Form + + + + Settings + Indstillinger + + + + Account + Konto + + + + + General + Generelt + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Adgangskode + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Blokerede kontakter + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Brugernavn + + + + Hostname + Værtsnavn + + + + Proxy + Stedfortræder + + + + Audio + + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Samtale parkeret + + + + Hold / Unhold + Parkeret / Aktiv + + + + Chat + Chat + + + + Mute Mic + Sluk mikrofon + + + + Record call + + + + + Name label + Navne etiket + + + + Time elapsed + Tid gået + + + + 00:00 + 00:00 + + + + Hangup + Læg på + + + + Mute Video + Sluk mikrofon + + + + VideoView + + + Share entire screen + Del hele skærmen + + + + Share screen area + Del skærmområde + + + + Share file + Del fil + + + diff --git a/translations/ring_client_windows_de.ts b/translations/ring_client_windows_de.ts new file mode 100644 index 00000000..7398fa55 --- /dev/null +++ b/translations/ring_client_windows_de.ts @@ -0,0 +1,1606 @@ + + + AboutDialog + + + + About + Über + + + + about button + Über Button + + + + credits button + Credits Button + + + + Credits + Würdigung + + + + Free as in Freedom + Frei wie in Freiheit + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Die Microsoft Windows Anwendung für Jami. +Jami ist eine sichere und verteilte Kommunikationsanwendung. + + + + version + Version + + + + Created by: + Erstellt von: + + + + Artwork by: + Visuelle Gestaltung: + + + + Based on the SFLPhone project + Basierend auf dem SFLPhone-Projekt + + + + AccountItemDelegate + + + Add Account + Konto hinzufügen + + + + AdvancedSIPSettingsWidget + + + Form + Formular + + + + Call Settings + Anrufeinstellungen + + + + Auto Answer Calls + Anrufe automatisch beantworten + + + + Enable Custom Ringtone + Eigenen Klingelton aktivieren + + + + Connectivity + Verbindung + + + + STUN Address + STUN Adresse + + + + Use STUN + STUN benutzen + + + + Use UPnP + UPnP benutzen + + + + Use TURN + TURN benutzen + + + + TURN Password + TURN Passwort + + + + TURN Username + TURN Benutzername + + + + TURN Address + TURN Adresse + + + + Media + Medium + + + + Enable Video + Video aktivieren + + + + Video Codecs + Video Codecs + + + + Audio Codecs + Audio Codecs + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Audiodateien (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Eigenen Klingelton hinzufügen + + + + AdvancedSettingsWidget + + + Form + Formular + + + + Call Settings + Anrufeinstellungen + + + + Allow Calls From Untrusted Peers + Erlaube Anruf von unbekannten Teilnehmern + + + + Auto Answer Calls + Anrufe automatisch beantworten + + + + Enable Custom Ringtone + Eigenen Klingelton aktivieren + + + + Add a custom ringtone + Eigenen Klingelton hinzufügen + + + + Name Server + Server für Benutzernamen + + + + Address + Adresse + + + + OpenDHT Configuration + OpenDHT-Einstellungen + + + + Enable Proxy + Proxy aktivieren + + + + Bootstrap + Aktivierung + + + + Security + Sicherheit + + + + Private Key Password + Passwort für den privaten Schlüssel + + + + User Certificate + Benutzerzertifikat + + + + Private Key + Privater Schlüssel + + + + CA Certificate + CA-Zertifikat + + + + Connectivity + Verbindungsmöglichkeiten + + + + Use STUN + STUN benutzen + + + + STUN Address + STUN Adresse + + + + Use UPnP + UPnP benutzen + + + + Use TURN + TURN benutzen + + + + TURN Password + TURN Passwort + + + + TURN Username + TURN Benutzername + + + + TURN Address + TURN Adresse + + + + Media + Medium + + + + Enable Video + Video aktivieren + + + + Audio Codecs + Audio Codecs + + + + Video Codecs + Video Codecs + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Audiodateien (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formular + + + + BannedItemWidget + + + Form + Formular + + + + name + Name + + + + id + Jami-ID + + + + CallWidget + + + Show conversations + Zeige Unterhaltungen + + + + Conversations + Unterhaltungen + + + + Search contact text input + Nach Kontakte suchen Eingabe + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami ist eine freie Software für universelle Kommunikation und respektiert die Freiheiten und Privatsphäre der Benutzer. + + + + + This is your ID. +Copy and share it with your friends! + + + Das ist Ihre Jami-ID. +Kopieren und teilen Sie diese mit Ihren Freunden! + + + + + Show ring ID QR code + Zeige Ring-ID QR-Code + + + + Share ring ID button + Teile Ring-ID Button + + + + Double-click to copy + Doppelklick zum kopieren + + + + Error while generating QR Code + Fehler bei der Erstellung des QR-Codes + + + + + best name + Bester Name + + + + best Id + Bester ID + + + + Back to homepage button + Zurück zur Übersicht Button + + + + Add to contacts + Zu Kontakten hinzufügen + + + + Show invites + Zeige Einladungen + + + + Invites + Einladungen + + + + + Find a new or existing contact + Suche nach neuen oder existierenden Kontakten + + + + Wants to talk to you! + Möchte mit dir Sprechen + + + + Answer incoming call button + Eingehenden Anruf annehmen Button + + + + Ignore incoming call button + Eingehenden Anruf ignorieren Button + + + + Answer + Antwort + + + + Ignore + Ignorieren + + + + Cancel outgoing call + Ausgehenden Anruf abbrechen + + + + Cancel + Abbrechen + + + + Start video call + Videoanruf starten + + + + Start audio call + Audioanruf starten + + + + Clear conversation + Gesprächsverlauf löschen + + + + Remove contact + Entferne Kontakt + + + + Block contact + Kontakt blockieren + + + + Copy number + Nummer kopieren + + + + Search your received invitations + Suche in empfangenen Einladungen + + + + Contact me on Jami + Kontaktieren Sie mich über Jami + + + + My Id is : + Meine ID ist: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Eingehender Anruf von %1 + + + + DeleteAccountDialog + + + Account deletion + Kontolöschung + + + + Do you really want to delete the following account? + Möchten Sie wirklich das folgende Konto löschen? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Falls dieses Konto nicht exportiert oder auf ein anderes Gerät übertragen wurde, wird es endgültig verloren gehen. + + + + Permanently delete account + Konto permanent löschen + + + + Delete + Löschen + + + + Cancel account deletion + Kontolöschung abbrechen + + + + Cancel + Abbrechen + + + + DeviceItemWidget + + + Form + Formular + + + + Device Id + Gerätekennung + + + + this device + dieses Gerät + + + + InviteButtonsWidget + + + Accept + Akzeptieren + + + + Refuse + Verweigern + + + + Block + Sperren + + + + LinkDeviceDialog + + + Dialog + Dialog + + + + Enter your account password + Geben Sie ihr Kontopasswort ein + + + + Password + Passwort + + + + Ok + Ok + + + + Cancel + Abbrechen + + + + Exporting account + Konto wird exportiert + + + + Your PIN is + Ihre PIN ist + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Diese PIN und das Kontopasswort sollten bei ihrem Gerät innerhalb von 10 Minuten eingegeben werden. + + + + Close + Schließen + + + + Link Another Device + Weiteres Gerät verknüpfen + + + + Incorrect password + Falsches Passwort + + + + Something went wrong. +Please try again later. + Irgendetwas ist schief gelaufen. +Bitte versuchen sie es später nocheinmal. + + + + MainWindow + + + Settings + Einstellungen + + + + Exit + Schliessen + + + + About + Über + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Registrierten Namen setzen + + + + Enter your account password + Geben Sie ihr Kontopasswort ein + + + + Password text input + Passwort-Eingabe + + + + Password text entry + Passwort-Eintrag + + + + Password + Passwort + + + + Register + Registrieren + + + + Cancel + Abbrechen + + + + Registering Name + Name für Registrierung + + + + Something went wrong + Irgendetwas ist schief gelaufen + + + + Close + Schließen + + + + Incorrect password + Falsches Passwort + + + + Network error + Netzwerkfehler + + + + NewWizardWidget + + + Form + Formular + + + + Welcome Label + Begrüßungslabel + + + + Welcome to + Willkommen bei + + + + Welcome Logo + Begrüßungslogo + + + + Create Jami account button + Jami-Konto anlegen Button + + + + Push button for Jami account creation start trigger + Auf Button klicken um Jami-Konto zu erstellen + + + + Create a Jami account + Ein Jami-Konto erstellen + + + + Link device button + Verbinde Gerät Button + + + + Push button for device linkage start trigger + Auf Button klicken um ein Gerät zu verbinden + + + + Link this device to an account + Dieses Gerät mit einem Konto verknüpfen + + + + Create Jami SIP account button + Jami-SIP-Konto erstellen Button + + + + Push button for Jami SIP account creation start trigger + Auf Button klicken um Jami-SIP-Konto zu erstellen + + + + Create a SIP account + SIP-Konto erstellen + + + + Link this device to an existing account + Dieses Gerät mit einem vorhandenen Konto verknüpfen + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Um dieses Gerät mit einem anderen Konto zu verbinden </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">benötigt man einen PIN Code. </span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Um den PIN Code zu generieren muss man folgendes machen: </span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Gehe in die </span><span style=" font-size:14px; font-weight:600;">Kontoeinstellungen</span><span style=" font-size:14px;"> eines anderen bereits verbundenen Gerätes</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Wähle das </span><span style=" font-size:14px; font-weight:600;">Jami-Konto</span><span style=" font-size:14px;">, dass du verwenden möchtest</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Gehe in den </span><span style=" font-size:14px; font-weight:600;">Geräte</span><span style=" font-size:14px;"> Reiter</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Wähle </span><span style=" font-size:14px; font-weight:600;">Gerät hinzufügen</span><span style=" font-size:14px;"> aus</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Dort bekommen Sie den notwendigen PIN Code. Die PIN ist nur gültig für </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 Minuten</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Geben Sie Ihre PIN ein: + + + + Or import a file: + Oder eine Datei importieren: + + + + Link from exported account archive file + Gerät über exportierte Archivdatei verbinden + + + + + + (None) + (Nichts) + + + + Password: + Passwort: + + + + + Profile + Profil + + + + + Profile name + Profilname + + + + Account + Konto + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Registriere deinen Benutzernamen. +Dadurch wird er für dich reserviert, sodass nur du ihn benutzen kannst. +Deine Freunde werden dich dann über deinen Benutzernamen +anstatt deiner Jami-ID anrufen können. + + + + Public username checkbox + Öffentliches Benutzernamen Kästchen + + + + Checkbox selecting if the user wants a public username + Kästchen, das festlegt ob der Benutzer einen öffentlichen Benutzernamen will + + + + Register public username + Öffentlichen Benutzernamen registrieren + + + + Public username edit + Öffentlicher Benutzername Änderung + + + + Choose your username + Nutzername wählen + + + + Password text input + Passwort-Eingabe + + + + Password text entry + Passwort-Eintrag + + + + + Password + Passwort + + + + + Password confirmation text input + Passwortbestätigungs-Eingabe + + + + Confirm password + Passwort bestätigen + + + + SIP Account + SIP-Konto + + + + SIP Server edit + SIP-Server Änderung + + + + Server + Server + + + + SIP proxy input + SIP-Proxy-Eingabe + + + + SIP proxy text entry + SIP-Proxy-Eintrag + + + + Proxy + Proxy + + + + SIP username input + SIP-Benutzername-Eingabe + + + + SIP Password text entry + SIP-Passwort-Eintrag + + + + Username + Benutzername + + + + + SIP Password text input + SIP-Passwort-Eingabe + + + + Generating your Jami account… + Erzeugen Ihres Jami-Kontos... + + + + Previous page button + Vorherige Seite Button + + + + push button to access previous page of wizard + Button um auf die vorherige Seite des Konfigurationsassistenten zu kommen + + + + Previous + Vorherige + + + + Cancel account create/link + Kontoerstellung/-verbindung abbrechen + + + + push button to cancel account creation or linking + Button um Kontoerstellung oder Kontoverbindung abzubrechen + + + + Back + Zurück + + + + Next page Button + Nächste Seite Button + + + + Push button to access next page of wizard + Button um auf die nächste Seite des Konfigurationsassistenten zu kommen + + + + Next + Weiter + + + + Open File + Datei öffnen + + + + Jami archive files (*.gz); All files (*) + Jami Archivdatei (*.gz); Alle Dateien (*) + + + + Your account needs to be migrated. Enter your password. + Ihr Konto muss migriert werden. Bitte geben Sie Ihr Passwort ein. + + + + Migrating your Jami account... + Ihr Jami-Konto wird migriert... + + + + Importing account archive... + Kontoarchiv wird importiert... + + + + Generating your Jami account... + Ihr Jami Konto wird erzeugt... + + + + Generating your SIP account... + Ihr SIP-Konto wird erzeugt... + + + + Error creating account + Fehler beim Erstellen des Kontos + + + + PasswordDialog + + + Change Account Password + Kontopasswort ändern + + + + Enter Current Password + Aktuelles Passwort eingeben + + + + Enter New Password + Neues Passwort eingeben + + + + Confirm New Password + Neues Passwort bestätigen + + + + Confirm + Bestätigen + + + + Cancel + Abbrechen + + + + Current Password Incorrect + Aktuelles Passwort falsch + + + + PhotoBoothDialog + + + Photobooth + Photobooth + + + + PhotoboothWidget + + + Form + Formular + + + + Photobooth display + Fotoautomatenanzeige + + + + Choose File + Datei auswählen + + + + Image Files (*.jpg *.jpeg *.png) + Bilddateien (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Datei auswählen + + + + Files (*) + Dateien (*) + + + + QObject + + + No default mail client found + Kein Standardprogramm für E‑Mail gefunden + + + + + Edit Device Name + Gerätename ändern + + + + Unlink Device From Account + Kontoverknüpfung von Gerät auflösen + + + + Save new device name + Neuen Gerätenamen speichern + + + + Add as contact + Als Kontakt hinzufügen + + + + RingButton + + + Select folder + Ordner auswählen + + + + SettingsWidget + + + Form + Formular + + + + Settings + Einstellungen + + + + Account + Konto + + + + + General + Allgemein + + + + + Audio / Video + Audio / Video + + + + System + System + + + + Enable desktop notifications + Desktop-Benachrichtigungen aktivieren + + + + Keep minimized on close + Beim Schließen minimiert bleiben + + + + Download folder + Ordner herunterladen + + + + Save in + Speichern in + + + + Always Recording + Immer aufzeichnen + + + + Updates + Aktualisierungen + + + + Check for updates automatically every + Automatisch auf Aktualisierungen prüfen. Alle + + + + Interval between update checks in days selector + Intervall zwischen Prüfung auf Aktualisierung in Tagen Wähler + + + + days + Tage + + + + Check for updates now button + Jetzt auf Aktualisierung prüfen Button + + + + Check for updates now + Aktualisieren + + + + Password + Passwort + + + + + Enable + Aktivieren + + + + toggle enable notifications + Benachrichtigungen aktivieren umschalten + + + + Toggle keep minimized on close + Beim Schließen minimiert bleiben umschalten + + + + Call Recordings + Anrufaufzeichnungen + + + + Toggle automatic updates + Automatische Aktualisierungen umschalten + + + + Jami Account + Jami-Konto + + + + + Profile + Profil + + + + + Identity + Identität + + + + Id + Id + + + + Registered name + Registrierter Name + + + + Type here to register a username + Hier zu registrierenden Benutzernamen eingeben + + + + Register + Registrieren + + + + Change Password + Passwort ändern + + + + Export Account + Konten exportieren + + + + + Delete Account + Konto löschen + + + + Linked Devices + Verbundene Geräte + + + + Link Another Device + Weiteres Gerät verknüpfen + + + + Banned Contacts + Gesperrte Kontakte + + + + Format + Format + + + + Video device framerate selector + Videogerät Framerate Wähler + + + + Preview unavailable + Keine Vorschau verfügbar + + + + + Advanced Account Settings + Erweiterte Kontoeinstellungen + + + + SIP Account + SIP-Konto + + + + Username + Benutzername + + + + Hostname + Rechnername + + + + Proxy + Proxy + + + + Audio + Audio + + + + Microphone + Mikrofon + + + + Audio input device selector + Audioeingabegeräte Wähler + + + + Output Device + Ausgabegerät + + + + Choose the output device + Wählen Sie ein Ausgabegerät + + + + Video + Video + + + + Device + Gerät + + + + Video device selector + Videogerät Wähler + + + + A registered name should not have any spaces and must be at least three letters long + Ein registrierter Name sollte keine Leerzeichen beinhalten und muss mindestens drei Zeichen lang sein + + + + This name is already taken + Dieser Benutzername ist bereits vergeben + + + + + Enter an alias + Aliasnamen eingeben + + + + Register this name + Diesen Namen registrieren + + + + Remove Device + Gerät entfernen + + + + Enter this account's password to confirm the removal of this device + Geben Sie das Kontopasswort ein um die Trennung dieses Gerätes zu bestätigen + + + + Are you sure you wish to remove this device? + Sicher, dass Sie das Gerät trennen wollen? + + + + Export Account Here + Konto hier exportieren + + + + Select A Folder For Your Downloads + Wählen Sie einen Ordner für ihre Downloads aus + + + + Select A Folder For Your Recordings + Wählen Sie einen Ordner für ihre Aufnahmen aus + + + + VideoOverlay + + + Call on Hold + Anruf pausiert + + + + Hold / Unhold + Pause / Fortsetzen + + + + Chat + Chat + + + + Mute Mic + Mikrofon stummschalten + + + + Record call + Anruf aufzeichnen + + + + Name label + Namenslabel + + + + Time elapsed + Zeit vergangen + + + + 00:00 + 00:00 + + + + Hangup + Auflegen + + + + Mute Video + Video stummschalten + + + + VideoView + + + Share entire screen + Gesamten Bildschirm teilen + + + + Share screen area + Bildschirmbereich teilen + + + + Share file + Datei teilen + + + \ No newline at end of file diff --git a/translations/ring_client_windows_de_DE.ts b/translations/ring_client_windows_de_DE.ts new file mode 100644 index 00000000..864bd382 --- /dev/null +++ b/translations/ring_client_windows_de_DE.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + Über + + + + about button + + + + + credits button + + + + + Credits + Abspann + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + Version + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Verbindung + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + TURN benutzen + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Video aktivieren + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Bootstrap + + + + Security + Sicherheit + + + + Private Key Password + Kennwort für privaten Schlüssel + + + + User Certificate + Benutzerzertifikat + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Verbindung + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + TURN benutzen + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Video aktivieren + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Gespräche + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami ist Freie Software für universelle Kommunikation, die die Freiheiten und die Privatsphäre ihrer Nutzer respektiert. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + Fehler beim Erzeugen des QR-Codes + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Kontakte hinzufügen + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Antwort + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + Abbrechen + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + Kontakt blockieren + + + + Copy number + Nummer kopieren + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Löschen + + + + Cancel account deletion + + + + + Cancel + Abbrechen + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + dieses Gerät + + + + InviteButtonsWidget + + + Accept + Akzeptieren + + + + Refuse + Ablehnen + + + + Block + Blockieren + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Kennwort + + + + Ok + Ok + + + + Cancel + Abbrechen + + + + Exporting account + Konto wird exportiert + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Schließen + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Einstellungen + + + + Exit + + + + + About + Über + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Kennwort + + + + Register + Registrieren + + + + Cancel + Abbrechen + + + + Registering Name + + + + + Something went wrong + + + + + Close + Schließen + + + + Incorrect password + + + + + Network error + Netzwerkfehler + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Jami-Konto erstellen + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Dieses Gerät mit einem bestehenden Konto verknüpfen + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Dieses Gerät mit einem bestehenden Konto verknüpfen + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + PIN eingeben: + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Kennwort: + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Konto + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Kennwort + + + + + Password confirmation text input + + + + + Confirm password + Konto bestätigen + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Benutzername + + + + + SIP Password text input + + + + + Generating your Jami account… + Ihr Jami-Konto wird erstellt … + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Zurück + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Weiter + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Abbrechen + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Einstellungen + + + + Account + Konto + + + + + General + Allgemein + + + + + Audio / Video + + + + + System + System + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + Download-Ordner + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Kennwort + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Registrieren + + + + Change Password + Ändere das Passwort + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Benutzername + + + + Hostname + Hostname + + + + Proxy + Proxy + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Video + + + + Device + Gerät + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + Chat + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + 00:00 + + + + Hangup + Auflegen + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + Datei teilen + + + \ No newline at end of file diff --git a/translations/ring_client_windows_el.ts b/translations/ring_client_windows_el.ts new file mode 100644 index 00000000..8408e2fd --- /dev/null +++ b/translations/ring_client_windows_el.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + Περί + + + + about button + + + + + credits button + + + + + Credits + Μονάδες + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + έκδοση + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + Χρήση STUN + + + + Use UPnP + + + + + Use TURN + Χρήση TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Μέσα + + + + Enable Video + Ενεργοποίση βίνταο + + + + Video Codecs + Κωδικοποιητές βίντεο + + + + Audio Codecs + Κωδικοποιητές ήχου + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Διεύθυνση + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + Ασφάλεια + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + Χρήση STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Χρήση TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Μέσα + + + + Enable Video + Ενεργοποίση βίνταο + + + + Audio Codecs + Κωδικοποιητές ήχου + + + + Video Codecs + Κωδικοποιητές βίντεο + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Συνομιλίες + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Προσθήκη στις επαφές + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + Ακύρωση + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + Αντιγραφή αριθμού + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Διαγραφή + + + + Cancel account deletion + + + + + Cancel + Ακύρωση + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Αποδοχή + + + + Refuse + Άρνηση + + + + Block + Μπλοκάρισμα + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Συνθηματικό + + + + Ok + Εντάξει + + + + Cancel + Ακύρωση + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Κλείσιμο + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Ρυθμίσεις + + + + Exit + Έξοδος + + + + About + Περί + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Συνθηματικό + + + + Register + Εγγραφή + + + + Cancel + Ακύρωση + + + + Registering Name + + + + + Something went wrong + + + + + Close + Κλείσιμο + + + + Incorrect password + + + + + Network error + Σφάλμα δικτύου + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Σύνδεση αυτής της συσκευής με ένα λογαριασμό + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Σύνδεση αυτής της συσκευής σε έναν λογαριασμό που ήδη υπάρχει + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Εισάγετε το PIN σας: + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Συνθηματικό: + + + + + Profile + Προφίλ + + + + + Profile name + + + + + Account + Λογαριασμός + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Καταχώρηση δημόσιου ονόματος χρήστη + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Συνθηματικό + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Διαμεσολαβητής + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Όνομα χρήστη + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Προηγούμενο + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Πίσω + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Επόμενο + + + + Open File + Άνοιγμα αρχείου + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Ακύρωση + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Ρυθμίσεις + + + + Account + Λογαριασμός + + + + + General + Γενικά + + + + + Audio / Video + + + + + System + Σύστημα + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + Αποθήκευση σε + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Συνθηματικό + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Προφίλ + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Εγγραφή + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Όνομα χρήστη + + + + Hostname + Hostname + + + + Proxy + Διαμεσολαβητής + + + + Audio + Ήχος + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Βίντεο + + + + Device + Συσκευή + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + Συνομιλία + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + Μοίρασμα περιοχής της οθόνης + + + + Share file + Μοίρασμα αρχείου + + + \ No newline at end of file diff --git a/translations/ring_client_windows_en_GB.ts b/translations/ring_client_windows_en_GB.ts new file mode 100644 index 00000000..79dc3933 --- /dev/null +++ b/translations/ring_client_windows_en_GB.ts @@ -0,0 +1,1591 @@ + + + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + + + + + Cancel + + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + + + + + Cancel + + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + + + + + Account + + + + + + General + + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + diff --git a/translations/ring_client_windows_eo.ts b/translations/ring_client_windows_eo.ts new file mode 100644 index 00000000..2a4dcc99 --- /dev/null +++ b/translations/ring_client_windows_eo.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + Pri + + + + about button + + + + + credits button + + + + + Credits + Kredito + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + versio + + + + Created by: + Kreita de: + + + + Artwork by: + Grafiko de: + + + + Based on the SFLPhone project + Surbaze de la SFLPhone projekto + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + Form + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + Uzi STUN + + + + Use UPnP + + + + + Use TURN + Uzi TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Aŭdvidaĵoj + + + + Enable Video + Ŝalti Videon + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + Form + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adreso + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Praŝarĝo + + + + Security + Sekuro + + + + Private Key Password + Pasvorto de Privata Ŝlosilo + + + + User Certificate + Uzanta Atestilo + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + Uzi STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Uzi TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Aŭdvidaĵoj + + + + Enable Video + Ŝalti Videon + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Form + + + + BannedItemWidget + + + Form + Form + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Konversacioj + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + Eraro dum generado de QR Kodo + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Aldoni al kontaktoj + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Volas paroli kun vi! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Respondi + + + + Ignore + Malatenti + + + + Cancel outgoing call + + + + + Cancel + Rezigni + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + Kopii numeron + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Voko eniranta de %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Forigi + + + + Cancel account deletion + + + + + Cancel + Rezigni + + + + DeviceItemWidget + + + Form + Form + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Akcepti + + + + Refuse + Rifuzi + + + + Block + + + + + LinkDeviceDialog + + + Dialog + Dialogo + + + + Enter your account password + + + + + Password + Pasvorto + + + + Ok + Okej + + + + Cancel + Rezigni + + + + Exporting account + Elportanta konton + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Agordoj + + + + Exit + Eliri + + + + About + Pri + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Pasvorto + + + + Register + Registri + + + + Cancel + Rezigni + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + Reta eraro + + + + NewWizardWidget + + + Form + Form + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Ligi tiun aparaton al konto + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Ligi ĉi tiun aparaton al ekzistanta konto + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Entajpu vian PIN-on: + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Pasvorto: + + + + + Profile + Profilo + + + + + Profile name + + + + + Account + Konto + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + Elektu vian uzantnomon + + + + Password text input + + + + + Password text entry + + + + + + Password + Pasvorto + + + + + Password confirmation text input + + + + + Confirm password + Konfirmu pasvorton + + + + SIP Account + SIP Konto + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Prokura Servilo + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Uzantnomo + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Antaŭa + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Sekva + + + + Open File + Malfermi Dosieron + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Rezigni + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + Fotejo + + + + PhotoboothWidget + + + Form + Form + + + + Photobooth display + + + + + Choose File + Elektu dosieron + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Elektu dosieron + + + + Files (*) + Dosieroj (*) + + + + QObject + + + No default mail client found + Neniu defaŭlta retpoŝta kliento trovita + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + Form + + + + Settings + Agordoj + + + + Account + Konto + + + + + General + Ĝeneralaj + + + + + Audio / Video + + + + + System + Sistemo + + + + Enable desktop notifications + + + + + Keep minimized on close + Ne ĉesu pro fermado fenestron + + + + Download folder + + + + + Save in + Konservi en + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Pasvorto + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profilo + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Registri + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + SIP Konto + + + + Username + Uzantnomo + + + + Hostname + Nomo de gastiga komputilo + + + + Proxy + Prokura Servilo + + + + Audio + Aŭdio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + Eliga aparato + + + + Choose the output device + + + + + Video + Video + + + + Device + Aparato + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Voko detenita + + + + Hold / Unhold + Deteni / Maldeteni + + + + Chat + Babilo + + + + Mute Mic + Selentigi Mikrofonon + + + + Record call + + + + + Name label + Noma etikedo + + + + Time elapsed + Pasinta tempo + + + + 00:00 + 00:00 + + + + Hangup + Fini Vokon + + + + Mute Video + Silentigi Videon + + + + VideoView + + + Share entire screen + Kunhavigi tutan ekranon + + + + Share screen area + Kunhavigi areon de ekrano + + + + Share file + Kunhavigi dosieron + + + \ No newline at end of file diff --git a/translations/ring_client_windows_es.ts b/translations/ring_client_windows_es.ts new file mode 100644 index 00000000..8dc04410 --- /dev/null +++ b/translations/ring_client_windows_es.ts @@ -0,0 +1,1605 @@ + + + AboutDialog + + + + About + Acerca de + + + + about button + Botón de Acerca de + + + + credits button + Botón de Créditos + + + + Credits + Créditos + + + + Free as in Freedom + Libre en el sentido de libertad + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + El cliente de Jami para Microsoft Windows. +Jami es un software de comunicacion seguro y distribuído. + + + + version + versión + + + + Created by: + Creado por: + + + + Artwork by: + Diseñado por: + + + + Based on the SFLPhone project + Basado en el proyecto SFLPhone + + + + AccountItemDelegate + + + Add Account + Añadir cuenta + + + + AdvancedSIPSettingsWidget + + + Form + Formulario + + + + Call Settings + Configuración de llamada + + + + Auto Answer Calls + Auto-responder llamadas + + + + Enable Custom Ringtone + Activar configuración de timbre + + + + Connectivity + Conectividad + + + + STUN Address + Dirección STUN + + + + Use STUN + Utilizar STUN + + + + Use UPnP + Usar UPnP (Universal Plug and Play) + + + + Use TURN + Usar TURN + + + + TURN Password + Contraseña TURN + + + + TURN Username + Nombre de usuario TURN + + + + TURN Address + Dirección TURN + + + + Media + Medios + + + + Enable Video + Habilitar vídeo + + + + Video Codecs + Codecs de vídeo + + + + Audio Codecs + Codecs de audio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Ficheros de audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Añadir un timbre a elección + + + + AdvancedSettingsWidget + + + Form + Formulario + + + + Call Settings + Configuración de llamada + + + + Allow Calls From Untrusted Peers + Permitir llamadas de pares desconocidos + + + + Auto Answer Calls + Auto-responder llamadas + + + + Enable Custom Ringtone + Activar configuración de timbre + + + + Add a custom ringtone + Añadir un timbre a elección + + + + Name Server + Nombrar Servidor + + + + Address + Dirección + + + + OpenDHT Configuration + Configuración de OpenDHT + + + + Enable Proxy + Habilitar proxy + + + + Bootstrap + Cargador de inicio + + + + Security + Seguridad + + + + Private Key Password + Contraseña de clave privada + + + + User Certificate + Certificado del usuario + + + + Private Key + Llave Privada + + + + CA Certificate + Certificado CA + + + + Connectivity + Conectividad + + + + Use STUN + Utilizar STUN + + + + STUN Address + Dirección STUN + + + + Use UPnP + Usar UPnP + + + + Use TURN + Usar TURN + + + + TURN Password + Contraseña TURN + + + + TURN Username + Nombre de usuario TURN + + + + TURN Address + Dirección TURN + + + + Media + Medios + + + + Enable Video + Habilitar vídeo + + + + Audio Codecs + Codecs de audio + + + + Video Codecs + Codecs de vídeo + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Ficheros de audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formulario + + + + BannedItemWidget + + + Form + Formulario + + + + name + Nombre + + + + id + ID + + + + CallWidget + + + Show conversations + Mostrar conversaciones + + + + Conversations + Conversaciones + + + + Search contact text input + Entrada de texto para buscar contacto + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami es software libre para la comunicación universal que respeta las libertades y la privacidad de sus usuarios. + + + + + This is your ID. +Copy and share it with your friends! + + +Este es tu ID. +¡Cópiala y compártela con tus amigos! + + + + Show ring ID QR code + Mostrar la Ring ID como código QR + + + + Share ring ID button + Botón de compartir el ID de Ring + + + + Double-click to copy + Doble click para copiar + + + + Error while generating QR Code + Hubo un error al generar el código QR + + + + + best name + mejor nombre + + + + best Id + mejor Id + + + + Back to homepage button + Botón para regresar a la página principal + + + + Add to contacts + Añadir a contactos + + + + Show invites + Mostrar invitaciones + + + + Invites + Invitaciones + + + + + Find a new or existing contact + Encontrar un contacto nuevo o existente + + + + Wants to talk to you! + ¡Quiere charlar contigo! + + + + Answer incoming call button + Botón de respuesta llamada entrante + + + + Ignore incoming call button + Ignorar el botón de llamada entrante + + + + Answer + Contestar + + + + Ignore + Ignorar + + + + Cancel outgoing call + Cancelar llamada saliente + + + + Cancel + Cancelar + + + + Start video call + Iniciar videollamada + + + + Start audio call + Empezar llamada de audio + + + + Clear conversation + Limpiar conversación + + + + Remove contact + Eliminar contacto + + + + Block contact + Bloquear contacto + + + + Copy number + Copiar Número + + + + Search your received invitations + Buscar tus invitaciones recibidas + + + + Contact me on Jami + Contacta conmigo en Jami + + + + My Id is : + Mi Id. es : + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Llamada entrante de %1 + + + + DeleteAccountDialog + + + Account deletion + Eliminar cuenta + + + + Do you really want to delete the following account? + ¿Desea eliminar la siguiente cuenta? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Esta cuenta se perderá definitivamente si no ha sido exportada o añadida a otro dispositivo. + + + + Permanently delete account + Eliminar cuenta definitivamente + + + + Delete + Borrar + + + + Cancel account deletion + Cancelar la eliminación de la cuenta + + + + Cancel + Cancelar + + + + DeviceItemWidget + + + Form + Formulario + + + + Device Id + Identificador de dispositivo + + + + this device + este dispositivo + + + + InviteButtonsWidget + + + Accept + Aceptar + + + + Refuse + No aceptar + + + + Block + Bloquear + + + + LinkDeviceDialog + + + Dialog + Mensaje + + + + Enter your account password + Introduce la contraseña de tu cuenta + + + + Password + Contraseña + + + + Ok + Aceptar + + + + Cancel + Cancelar + + + + Exporting account + Exportando cuenta + + + + Your PIN is + Tu PIN es + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + El pin y la contraseña de la cuenta debe ser introducida en tu dispositivo en menos de 10 minutos + + + + Close + Cerrar + + + + Link Another Device + Conectar otro dispositivo + + + + Incorrect password + Contraseña incorrecta + + + + Something went wrong. +Please try again later. + Algo falló. +Por favor inténtelo de nuevo más tarde. + + + + MainWindow + + + Settings + Configuración + + + + Exit + Salir + + + + About + Acerca de + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Configurar nombre registrado + + + + Enter your account password + Introduzca el password de su cuenta + + + + Password text input + Entrada de constraseña + + + + Password text entry + Entrada de texto para contraseña + + + + Password + Contraseña + + + + Register + Registrar + + + + Cancel + Cancelar + + + + Registering Name + Registrando nombre + + + + Something went wrong + Algo salió mal + + + + Close + Cerrar + + + + Incorrect password + Contraseña incorrecta + + + + Network error + Error en la red + + + + NewWizardWidget + + + Form + Formulario + + + + Welcome Label + Etiqueta de Bienvenida + + + + Welcome to + Bienvenido(a) a + + + + Welcome Logo + Logo de Bienvenida + + + + Create Jami account button + Botón para crear cuenta de Jami + + + + Push button for Jami account creation start trigger + Pulse el boton para activar el inicio de creación de la cuenta Jami + + + + Create a Jami account + Crear una cuenta en Jami + + + + Link device button + Botón de vincular dispositivo + + + + Push button for device linkage start trigger + Pulse el botón para comenzar a enlazar el dispositivo + + + + Link this device to an account + Conectar este dispositivo a una cuenta + + + + Create Jami SIP account button + Botón de crear cuenta Jami SIP + + + + Push button for Jami SIP account creation start trigger + Pulsa el botón de crear cuenta Jami SIP + + + + Create a SIP account + Crear una cuenta SIP + + + + Link this device to an existing account + Vincular este dispositivo con una cuenta existente + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Para vincular este dispositivo con otra cuenta, primero debes </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">obtener un código</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> PIN. Para generar el código PIN:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Ve a las </span><span style=" font-size:14px; font-weight:600;">opciones de administración de la cuenta</span><span style=" font-size:14px;"> de un dispositivo previo</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Elije la </span><span style=" font-size:14px; font-weight:600;">cuenta Jami</span><span style=" font-size:14px;"> que deseas utilizar</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Ve a </span><span style=" font-size:14px; font-weight:600;">Dispositivos</span><span style=" font-size:14px;"> en las pestañas</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Selecciona </span><span style=" font-size:14px; font-weight:600;">Añadir un dispositivo</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Recibirás el PIN necesario para completar este formulario. El PIN solo es válido durante </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutos</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Escribe tu PIN: + + + + Or import a file: + o importar a un fichero: + + + + Link from exported account archive file + Enlace desde archivo de cuenta exportado + + + + + + (None) + (Nada) + + + + Password: + Contraseña: + + + + + Profile + Perfil + + + + + Profile name + Nombre de perfil + + + + Account + Cuenta + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Registre su nombre de usuario. +Esto reservará el nombre de usuario de modo que sólo usted pueda usarlo. +Sus amigos podrán llamarlo por su nombre de usuario +en lugar de usar su ID. + + + + Public username checkbox + Casilla de verificación de nombre de usuario público + + + + Checkbox selecting if the user wants a public username + Marque la casilla de verificación si el usuario desea un nombre de usuario público + + + + Register public username + Registrar nombre de usuario público + + + + Public username edit + Editar nombre de usuario público + + + + Choose your username + Elije un nombre de usuario para ti + + + + Password text input + Entrada de constraseña + + + + Password text entry + Entrada de texto para contraseña + + + + + Password + Contraseña + + + + + Password confirmation text input + Confirmación de contraseña de entrada de texto + + + + Confirm password + Confirmar contraseña + + + + SIP Account + Cuenta SIP + + + + SIP Server edit + Edición de servidor SIP + + + + Server + Servidor + + + + SIP proxy input + Entrada de proxy SIP + + + + SIP proxy text entry + Entreda de texto del proxy SIP + + + + Proxy + Proxy + + + + SIP username input + Entrada de nombre de usuario SIP + + + + SIP Password text entry + Entrada de texto de la Contraseña SIP + + + + Username + Nombre de usuario + + + + + SIP Password text input + Entrada de texto de la Contraseña SIP + + + + Generating your Jami account… + Generando tu cuenta Jami... + + + + Previous page button + Botón de página anterior + + + + push button to access previous page of wizard + pulse el botón para acceder a la página anterior del asistente + + + + Previous + Anterior + + + + Cancel account create/link + Cancelar creación/enlace de cuenta + + + + push button to cancel account creation or linking + presione el botón para cancelar la creación o enlace de cuenta + + + + Back + Volver + + + + Next page Button + Botón de página siguiente + + + + Push button to access next page of wizard + Pulse el botón para acceder a la siguiente página del asistente + + + + Next + Siguiente + + + + Open File + Abrir fichero + + + + Jami archive files (*.gz); All files (*) + Ficheros de archivo de Jami (*.gz); Todos los ficheros (*) + + + + Your account needs to be migrated. Enter your password. + Debe migrar su cuenta. Introduzca su contraseña. + + + + Migrating your Jami account... + Migrando su cuenta de Jami... + + + + Importing account archive... + Importando archivo de cuenta... + + + + Generating your Jami account... + Generando tu cuenta de Jami... + + + + Generating your SIP account... + Creando tu cuenta SIP... + + + + Error creating account + Error al crear la cuenta + + + + PasswordDialog + + + Change Account Password + Cambiar contraseña de cuenta + + + + Enter Current Password + Ingrese la contraseña actual + + + + Enter New Password + Ingrese la nueva contraseña + + + + Confirm New Password + Confirme la nueva contraseña + + + + Confirm + Confirmar + + + + Cancel + Cancelar + + + + Current Password Incorrect + Contraseña actual incorrecta + + + + PhotoBoothDialog + + + Photobooth + Cabina de fotos + + + + PhotoboothWidget + + + Form + Formulario + + + + Photobooth display + Pantalla de Photobooth + + + + Choose File + Examinar + + + + Image Files (*.jpg *.jpeg *.png) + Ficheros de imagen (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Examinar + + + + Files (*) + Ficheros (*) + + + + QObject + + + No default mail client found + No se encuentra el cliente de e-mail predeterminado + + + + + Edit Device Name + Editar nombre del dispositivo + + + + Unlink Device From Account + Desvincular dispositivo de la cuenta + + + + Save new device name + Guardar nuevo nombre de dispositivo + + + + Add as contact + Añadir como contacto + + + + RingButton + + + Select folder + Seleccionar carpeta + + + + SettingsWidget + + + Form + Formulario + + + + Settings + Configuración + + + + Account + Cuenta + + + + + General + General + + + + + Audio / Video + Audio / Vídeo + + + + System + Sistema + + + + Enable desktop notifications + Habilitar notificaciones de escritorio + + + + Keep minimized on close + Mantener minimizado al cerrar + + + + Download folder + Descargar carpeta + + + + Save in + guardar + + + + Always Recording + Siempre grabando + + + + Updates + Actualizaciones + + + + Check for updates automatically every + Buscar actualizaciones automáticamente cada + + + + Interval between update checks in days selector + Selector de intervalo de días entre chequeos de actualizaciones + + + + days + días + + + + Check for updates now button + Botón de búsqueda de inmediata actualizaciones + + + + Check for updates now + Buscar actualizaciones ahora + + + + Password + Contraseña + + + + + Enable + Activar + + + + toggle enable notifications + Cambiar Activar notificaciones + + + + Toggle keep minimized on close + Cambiar Mantener minimizado al cerrar + + + + Call Recordings + Grabación de llamadas + + + + Toggle automatic updates + Activar actualizaciones automáticas + + + + Jami Account + Cuenta Jami + + + + + Profile + Perfil + + + + + Identity + Identidad + + + + Id + ID + + + + Registered name + Nombre registrado + + + + Type here to register a username + Escriba aquí para registrar un nombre de usuario + + + + Register + Registrar + + + + Change Password + Cambiar contraseña + + + + Export Account + Exportar cuenta + + + + + Delete Account + Borrar cuenta + + + + Linked Devices + Dispositivos conectados + + + + Link Another Device + Conectar otro dispositivo + + + + Banned Contacts + Contactos bloqueados + + + + Format + Formato + + + + Video device framerate selector + Selector de velocidad de cuadros del vídeo + + + + Preview unavailable + Previsualización no disponible + + + + + Advanced Account Settings + Configuraciones de cuenta avanzadas + + + + SIP Account + Cuenta SIP + + + + Username + Nombre de usuario + + + + Hostname + Nombre del servidor + + + + Proxy + Proxy + + + + Audio + Audio + + + + Microphone + Micrófono + + + + Audio input device selector + Selector de dispositivo de entrada de audio + + + + Output Device + Dispositivo de salida + + + + Choose the output device + Selecciones el dispositivo de salida + + + + Video + Vídeo + + + + Device + Dispositivo + + + + Video device selector + Selector de dispositivo de video + + + + A registered name should not have any spaces and must be at least three letters long + Un nombre registrado no puede tener espacios y debe tener como mínimo tres letras + + + + This name is already taken + Este nombre ya está tomado + + + + + Enter an alias + Introducir un alias + + + + Register this name + Registrar este nombre + + + + Remove Device + Quitar dispositivo + + + + Enter this account's password to confirm the removal of this device + Ingrese la contraseña de esta cuenta para confirmar la quita de este dispositivo + + + + Are you sure you wish to remove this device? + ¿Está seguro de querer eliminar este dispositivo? + + + + Export Account Here + Exportar cuenta aquí + + + + Select A Folder For Your Downloads + Elija una carpeta para sus descargas + + + + Select A Folder For Your Recordings + Elija una carpeta para sus grabaciones + + + + VideoOverlay + + + Call on Hold + Llamada en espera + + + + Hold / Unhold + En espera / Reanudar + + + + Chat + Chat + + + + Mute Mic + Silenciar micrófono + + + + Record call + Grabar llamada + + + + Name label + Etiqueta de nombre + + + + Time elapsed + Tiempo transcurrido + + + + 00:00 + 00:00 + + + + Hangup + Cortar + + + + Mute Video + Silenciar Video + + + + VideoView + + + Share entire screen + Compartir toda la pantalla + + + + Share screen area + Compartir la ventana + + + + Share file + Compartir archivo + + + \ No newline at end of file diff --git a/translations/ring_client_windows_es_AR.ts b/translations/ring_client_windows_es_AR.ts new file mode 100644 index 00000000..9679572f --- /dev/null +++ b/translations/ring_client_windows_es_AR.ts @@ -0,0 +1,1605 @@ + + + AboutDialog + + + + About + Acerca de + + + + about button + Botón Acerca de + + + + credits button + Botón Créditos + + + + Credits + Créditos + + + + Free as in Freedom + Libre como en libertad + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + El cliente de Jami para Microsoft Windows. +Jami es un software de comunicación seguro y distribuido. + + + + version + versión + + + + Created by: + Creado por: + + + + Artwork by: + Ilustraciones realizadas por: + + + + Based on the SFLPhone project + Basado en el projecto SFLPhone + + + + AccountItemDelegate + + + Add Account + Añadir cuenta + + + + AdvancedSIPSettingsWidget + + + Form + Formulario + + + + Call Settings + Opciones de llamada + + + + Auto Answer Calls + Responder automáticamente + + + + Enable Custom Ringtone + Habilitar tono de llamada personalizado + + + + Connectivity + Conectividad + + + + STUN Address + Dirección STUN + + + + Use STUN + Usar STUN + + + + Use UPnP + Usar UPnP + + + + Use TURN + Usar TURN + + + + TURN Password + Contraseña TURN + + + + TURN Username + Usuario TURN + + + + TURN Address + Dirección TURN + + + + Media + Medios + + + + Enable Video + Permitir video + + + + Video Codecs + Códecs de video + + + + Audio Codecs + Códecs de audio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Archivos de audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Agregar un tono de llamada personalizado + + + + AdvancedSettingsWidget + + + Form + Formulario + + + + Call Settings + Opciones de llamada + + + + Allow Calls From Untrusted Peers + Permitir llamadas de contactos no conocidos + + + + Auto Answer Calls + Responder automáticamente + + + + Enable Custom Ringtone + Habilitar tono de llamada personalizado + + + + Add a custom ringtone + Agregar un tono de llamada personalizado + + + + Name Server + Nombre del servidor + + + + Address + Dirección + + + + OpenDHT Configuration + Configuración de OpenDHT + + + + Enable Proxy + Habilitar Proxy + + + + Bootstrap + Bootstrap + + + + Security + Seguridad + + + + Private Key Password + Contraseña de clave privada + + + + User Certificate + Certificado de Usuario + + + + Private Key + Clave privada + + + + CA Certificate + Certificado CA + + + + Connectivity + Conectividad + + + + Use STUN + Usar STUN + + + + STUN Address + Dirección STUN + + + + Use UPnP + Usar UPnP + + + + Use TURN + Usar TURN + + + + TURN Password + Contraseña TURN + + + + TURN Username + Usuario TURN + + + + TURN Address + Dirección TURN + + + + Media + Medios + + + + Enable Video + Permitir video + + + + Audio Codecs + Códecs de audio + + + + Video Codecs + Códecs de video + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Archivos de audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formulario + + + + BannedItemWidget + + + Form + Formulario + + + + name + nombre + + + + id + ID + + + + CallWidget + + + Show conversations + Mostrar conversaciones + + + + Conversations + Conversaciones + + + + Search contact text input + Entrada de texto para buscar contacto + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami es un software libre para comunicarse universalmente que respeta las libertades y la privacidad de sus usuarios. + + + + + This is your ID. +Copy and share it with your friends! + + +Este es su ID. +¡Cópielo y compártalo con sus amigos! + + + + Show ring ID QR code + Mostrar el ID de Ring como código QR + + + + Share ring ID button + Botón compartir el ID de Ring + + + + Double-click to copy + Doble click para copiar + + + + Error while generating QR Code + Error al generar el Código QR + + + + + best name + mejor nombre + + + + best Id + mejor ID + + + + Back to homepage button + Volver a la página de inicio + + + + Add to contacts + Agregar a contactos + + + + Show invites + Mostrar invitaciones + + + + Invites + Invitaciones + + + + + Find a new or existing contact + Buscar un contacto nuevo o existente + + + + Wants to talk to you! + ¡Quiere hablarte! + + + + Answer incoming call button + Botón responder llamada entrante + + + + Ignore incoming call button + Botón ignorar llamada entrante + + + + Answer + Respuesta + + + + Ignore + Ignorar + + + + Cancel outgoing call + Cancelar llamada saliente + + + + Cancel + Cancelar + + + + Start video call + Iniciar una videollamada + + + + Start audio call + Iniciar una llamada de voz + + + + Clear conversation + Limpiar conversación + + + + Remove contact + Eliminar contacto + + + + Block contact + Bloquear contacto + + + + Copy number + Copiar número + + + + Search your received invitations + Buscar tus invitaciones recibidas + + + + Contact me on Jami + Contactá conmigo en Jami + + + + My Id is : + Mi ID es: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Llamada entrante de %1 + + + + DeleteAccountDialog + + + Account deletion + Eliminación de cuenta + + + + Do you really want to delete the following account? + ¿Realmente quiere eliminar la cuenta? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Si esta cuenta no ha sido exportada, o añadida a otro dispositivo, se perderá definitivamente. + + + + Permanently delete account + Eliminar cuenta permanentemente + + + + Delete + Eliminar + + + + Cancel account deletion + Cancelar la eliminación de cuenta + + + + Cancel + Cancelar + + + + DeviceItemWidget + + + Form + Formulario + + + + Device Id + ID del dispositivo + + + + this device + este dispositivo + + + + InviteButtonsWidget + + + Accept + Aceptar + + + + Refuse + Rechazar + + + + Block + Bloquear + + + + LinkDeviceDialog + + + Dialog + Diálogo + + + + Enter your account password + Ingrese la contraseña de la cuenta + + + + Password + Contraseña + + + + Ok + Ok + + + + Cancel + Cancelar + + + + Exporting account + Exportando cuenta + + + + Your PIN is + Tu PIN es: + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Este PIN y la contraseña de la contraseña deben ser ingresadas en tu dispositivo en los próximos 10 minutos. + + + + Close + Cerrar + + + + Link Another Device + Vincular otro dispositivo + + + + Incorrect password + Contraseña incorrecta + + + + Something went wrong. +Please try again later. + Algo salió mal. +Por favor intente de nuevo más tarde. + + + + MainWindow + + + Settings + Ajustes + + + + Exit + Salir + + + + About + Acerca de + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Establecer nombre registrado + + + + Enter your account password + Ingrese la contraseña de la cuenta + + + + Password text input + Entrada de contraseña + + + + Password text entry + Entrada de texto de contraseña + + + + Password + Contraseña + + + + Register + Registrar + + + + Cancel + Cancelar + + + + Registering Name + Registrando nombre + + + + Something went wrong + Algo salió mal + + + + Close + Cerrar + + + + Incorrect password + Contraseña incorrecta + + + + Network error + Error de red + + + + NewWizardWidget + + + Form + Formulario + + + + Welcome Label + Etiqueta de bienvenida + + + + Welcome to + Bienvenido a + + + + Welcome Logo + Logo de bienvenida + + + + Create Jami account button + Botón crear una cuenta de Jami + + + + Push button for Jami account creation start trigger + Pulse el boton para activar el inicio de creación de la cuenta Jami + + + + Create a Jami account + Crear una cuenta de Jami + + + + Link device button + Botón vincular dispositivo + + + + Push button for device linkage start trigger + Pulse el botón para comenzar a enlazar el dispositivo + + + + Link this device to an account + Vincular este dispositivo a una cuenta + + + + Create Jami SIP account button + Botón crear cuenta Jami SIP + + + + Push button for Jami SIP account creation start trigger + Pulse el botón crear cuenta Jami SIP + + + + Create a SIP account + Crear una cuenta SIP + + + + Link this device to an existing account + Enlazar este dispositivo a una cuenta existente + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:12pt;">Para enlazar este dispositivo a otra cuenta, primero </span><span style=" font-family:'Ubuntu'; font-size:12pt; font-weight:600;">necesita obtener un código</span><span style=" font-family:'Ubuntu'; font-size:12pt;"> PIN. Para generar el código PIN:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:12pt;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Diríjase a los <span style=" font-weight:600;">Ajustes de administración de cuenta</span> de un dispositivo previo</li> +<li style=" font-family:'Ubuntu'; font-size:12pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Elija la <span style=" font-weight:600;">cuenta Jami</span> que quiera usar</li> +<li style=" font-family:'Ubuntu'; font-size:12pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Vaya a la pestaña </li><span style=" font-weight:600;">Dispositivos</span> +<li style=" font-family:'Ubuntu'; font-size:12pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Seleccione <span style=" font-weight:600;">Añadir un dispositivo</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:12pt;">Obtendrá el PIN necesario para completar este formulario. El PIN sólo es valido por </span><span style=" font-family:'Ubuntu'; font-size:12pt; font-weight:600;">10 minutos</span><span style=" font-family:'Ubuntu'; font-size:12pt;">.</span></p></body></html> + + + + Enter your pin: + Ingrese su pin: + + + + Or import a file: + O importar un archivo: + + + + Link from exported account archive file + Enlace desde archivo de cuenta exportado + + + + + + (None) + (Ninguno) + + + + Password: + Contraseña: + + + + + Profile + Perfil + + + + + Profile name + Nombre de perfil + + + + Account + Cuenta + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Registre su nombre de usuario. +Esto reservará el nombre de usuario de modo que sólo usted pueda usarlo. +Sus amigos podrán llamarlo por su nombre de usuario +en lugar de usar su ID. + + + + Public username checkbox + Casilla de verificación de nombre de usuario público + + + + Checkbox selecting if the user wants a public username + Marque la casilla de verificación si el usuario desea un nombre de usuario público + + + + Register public username + Registrar nombre de usuario público + + + + Public username edit + Editar nombre de usuario público + + + + Choose your username + Elija su nombre de usuario + + + + Password text input + Entrada de texto de contraseña + + + + Password text entry + Entrada de texto de contraseña + + + + + Password + Contraseña + + + + + Password confirmation text input + Confirmación de contraseña de entrada de texto + + + + Confirm password + Confirmar contraseña + + + + SIP Account + Cuenta SIP + + + + SIP Server edit + Editar servidor SIP + + + + Server + Servidor + + + + SIP proxy input + Entrada de proxy SIP + + + + SIP proxy text entry + Entrada de texto de SIP proxy + + + + Proxy + Proxy + + + + SIP username input + Entrada de nombre de usuario SIP + + + + SIP Password text entry + Entrada de texto de contraseña SIP + + + + Username + Nombre de usuario + + + + + SIP Password text input + Entrada de texto de contraseña SIP + + + + Generating your Jami account… + Generando su cuenta Jami... + + + + Previous page button + Botón página anterior + + + + push button to access previous page of wizard + pulse el botón para acceder a la página anterior del asistente + + + + Previous + Anterior + + + + Cancel account create/link + Cancelar la creación/vinculación de cuenta + + + + push button to cancel account creation or linking + pulse el botón para cancelar la creación o vinculación de cuenta + + + + Back + Atrás + + + + Next page Button + Botón página siguiente + + + + Push button to access next page of wizard + Pulse el botón para acceder a la página siguiente del asistente + + + + Next + Siguiente + + + + Open File + Abrir Archivo + + + + Jami archive files (*.gz); All files (*) + Archivos de Jami (*.gz); Todos los ficheros (*) + + + + Your account needs to be migrated. Enter your password. + Su cuenta debe ser migrada. Ingrese su contraseña. + + + + Migrating your Jami account... + MIgrando su cuenta Jami... + + + + Importing account archive... + Importando su cuenta archivada... + + + + Generating your Jami account... + Generando su cuenta Jami... + + + + Generating your SIP account... + Generando su cuenta SIP... + + + + Error creating account + Error en la creación de cuenta + + + + PasswordDialog + + + Change Account Password + Cambiar la contraseña de la cuenta + + + + Enter Current Password + Ingresar la contraseña actual + + + + Enter New Password + Ingresar la nueva contraseña + + + + Confirm New Password + Confirmar la nueva contraseña + + + + Confirm + Confirmar + + + + Cancel + Cancelar + + + + Current Password Incorrect + Contraseña actual incorrecta + + + + PhotoBoothDialog + + + Photobooth + Cabina de fotos + + + + PhotoboothWidget + + + Form + Formulario + + + + Photobooth display + Pantalla de cabina de fotos + + + + Choose File + Elegir archivo + + + + Image Files (*.jpg *.jpeg *.png) + Archivos de imágenes (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Elegir archivo + + + + Files (*) + Archivos (*) + + + + QObject + + + No default mail client found + No se encontró el cliente de e-mail predeterminado + + + + + Edit Device Name + Editar nombre de dispositivo + + + + Unlink Device From Account + Desvincular dispositivo de la cuenta + + + + Save new device name + Guardar nmbre de nuevo dispositivo + + + + Add as contact + Agregar como contacto + + + + RingButton + + + Select folder + Seleccionar carpeta + + + + SettingsWidget + + + Form + Formulario + + + + Settings + Ajustes + + + + Account + Cuenta + + + + + General + General + + + + + Audio / Video + Audio / Video + + + + System + Sistema + + + + Enable desktop notifications + Habilitar notificaciones en el escritorio + + + + Keep minimized on close + Mantener minimizado al cerrar + + + + Download folder + Descargar carpeta + + + + Save in + Guardar en + + + + Always Recording + Siempre grabando + + + + Updates + Actualizaciones + + + + Check for updates automatically every + Comprobar actualizaciones automáticamente cada + + + + Interval between update checks in days selector + Selector de intervalo de días entre comprobación de actualizaciones + + + + days + días + + + + Check for updates now button + Botón comprobar actualizaciones en forma inmediata + + + + Check for updates now + Comprobar actualizaciones ahora + + + + Password + Contraseña + + + + + Enable + Habilitar + + + + toggle enable notifications + Cambiar Activar notificaciones + + + + Toggle keep minimized on close + Cambiar Mantener minimizado al cerrar + + + + Call Recordings + Grabaciones de llamadas + + + + Toggle automatic updates + Activar actualizaciones automáticas + + + + Jami Account + Cuenta Jami + + + + + Profile + Perfil + + + + + Identity + Identidad + + + + Id + ID + + + + Registered name + Nombre registrado + + + + Type here to register a username + Escriba aquí para registrar un nombre de usuario + + + + Register + Registrar + + + + Change Password + Cambiar contraseña + + + + Export Account + Exportar cuenta + + + + + Delete Account + Eliminar cuenta + + + + Linked Devices + Dispositvos vinculados + + + + Link Another Device + Vincular otro dispositivo + + + + Banned Contacts + Contactos bloqueados + + + + Format + Formato + + + + Video device framerate selector + Selector de velocidad de cuadros del dispositivo de video + + + + Preview unavailable + Vista previa no disponible + + + + + Advanced Account Settings + Opciones avanzadas de cuenta + + + + SIP Account + Cuenta SIP + + + + Username + Nombre de usuario + + + + Hostname + Nombre del anfitrión + + + + Proxy + Proxy + + + + Audio + Audio + + + + Microphone + Micrófono + + + + Audio input device selector + Selector de dispositivo de entrada de audio + + + + Output Device + Dispositivo de Salida + + + + Choose the output device + Elija el dispositivo de salida + + + + Video + Video + + + + Device + Dispositivo + + + + Video device selector + Selector de dispositivo de video + + + + A registered name should not have any spaces and must be at least three letters long + Un nombre de registro no debe tener espacios y debe tener al menos tres letras como mínimo de extensión + + + + This name is already taken + Este nombre ya está en uso + + + + + Enter an alias + Ingresar un alias + + + + Register this name + Registrar este nombre + + + + Remove Device + Remover dispositivo + + + + Enter this account's password to confirm the removal of this device + Ingrese la contraseña de la cuenta para confirmar la remoción del dispositivo + + + + Are you sure you wish to remove this device? + ¿Está seguro que desea remover este dispositivo? + + + + Export Account Here + Exportar cuenta aquí + + + + Select A Folder For Your Downloads + Seleccione una carpeta para sus descargas + + + + Select A Folder For Your Recordings + Seleccione una carpeta para sus grabaciones + + + + VideoOverlay + + + Call on Hold + Llamada en espera + + + + Hold / Unhold + En espera / Reanudar + + + + Chat + Chat + + + + Mute Mic + Silenciar micrófono + + + + Record call + Grabar llamada + + + + Name label + Etiqueta de nombre + + + + Time elapsed + Tiempo transcurrido + + + + 00:00 + 00:00 + + + + Hangup + Colgar + + + + Mute Video + Deshabilitar video + + + + VideoView + + + Share entire screen + Compartir pantalla completa + + + + Share screen area + Compartir área de pantalla específica + + + + Share file + Compartir archivo + + + \ No newline at end of file diff --git a/translations/ring_client_windows_es_MX.ts b/translations/ring_client_windows_es_MX.ts new file mode 100644 index 00000000..68701a02 --- /dev/null +++ b/translations/ring_client_windows_es_MX.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Conversaciones + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + Cancelar + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + Cancelar + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + Ok + + + + Cancel + Cancelar + + + + Exporting account + + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + + + + + Cancel + Cancelar + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Vincular este dispositivo a una cuenta + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + Cuenta + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Siguiente + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Cancelar + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + + + + + Account + Cuenta + + + + + General + + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Video + + + + Device + Dispositivo + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_et_EE.ts b/translations/ring_client_windows_et_EE.ts new file mode 100644 index 00000000..1b90f250 --- /dev/null +++ b/translations/ring_client_windows_et_EE.ts @@ -0,0 +1,1591 @@ + + + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + vasta + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + + + + + Cancel + + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + + + + + Cancel + + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + + + + + Account + + + + + + General + + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + diff --git a/translations/ring_client_windows_eu.ts b/translations/ring_client_windows_eu.ts new file mode 100644 index 00000000..a50d2c9f --- /dev/null +++ b/translations/ring_client_windows_eu.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + Honi buruz + + + + about button + + + + + credits button + + + + + Credits + Kredituak + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + bertsioa + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + Gehitu kontua + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Multimedia + + + + Enable Video + Gaitu bideoa + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Helbidea + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + Segurtasuna + + + + Private Key Password + Gako pribatuaren pasahitza + + + + User Certificate + + + + + Private Key + Gako pribatua + + + + CA Certificate + CA ziurtagiria + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Multimedia + + + + Enable Video + Gaitu bideoa + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + Erakutsi elkarrizketa + + + + Conversations + Elkarrizketak + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami komunikazio unibertsalerako software librea da, erabiltzaileek askatasunak eta pribatutasuna aintzat hartzen dituena + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Gehitu kontaktuetara + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Zurekin hitz egin nahi du! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Erantzun + + + + Ignore + Ezikusi + + + + Cancel outgoing call + + + + + Cancel + Ezeztatu + + + + Start video call + Hasi bideo-deia + + + + Start audio call + Hasi audio-deia + + + + Clear conversation + + + + + Remove contact + Kendu kontaktua + + + + Block contact + Blokeatu kontaktua + + + + Copy number + Kopiatu zenbakia + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Ezabatu + + + + Cancel account deletion + + + + + Cancel + Ezeztatu + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + gailu hau + + + + InviteButtonsWidget + + + Accept + Onartu + + + + Refuse + Ukatu + + + + Block + Bloketu + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Pasahitza + + + + Ok + Ados + + + + Cancel + Ezeztatu + + + + Exporting account + + + + + Your PIN is + + + + + PIN + PIN-a + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Itxi + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Ezarpenak + + + + Exit + Irten + + + + About + Honi buruz + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Pasahitza + + + + Register + Erregistratu + + + + Cancel + Ezeztatu + + + + Registering Name + + + + + Something went wrong + + + + + Close + Itxi + + + + Incorrect password + + + + + Network error + Sare errorea + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Sortu Jami kontua + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Estekatu gailua kontu batekin + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + (Bat ere ez) + + + + Password: + Pasahitza: + + + + + Profile + Profila + + + + + Profile name + + + + + Account + Kontua + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Erregistratu izen publikoa + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Pasahitza + + + + + Password confirmation text input + + + + + Confirm password + Berretsi pasahitza + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxya + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Erabiltzaile-izena + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Aurrekoa + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Atzera + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Hurrengoa + + + + Open File + Ireki fitxategia + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + Sartu pasahitz berria + + + + Confirm New Password + Baieztatu pasahitz berria + + + + Confirm + + + + + Cancel + Ezeztatu + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + Hautatu fitxategia + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Hautatu fitxategia + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + Hautatu karpeta + + + + SettingsWidget + + + Form + + + + + Settings + Ezarpenak + + + + Account + Kontua + + + + + General + Orokorra + + + + + Audio / Video + + + + + System + Sistema + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + Eguneratzeak + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Pasahitza + + + + + Enable + Gaitu + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profila + + + + + Identity + Identitatea + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Erregistratu + + + + Change Password + Aldatu pasahitza + + + + Export Account + + + + + + Delete Account + Ezabatu kontua + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Erabiltzaile-izena + + + + Hostname + Ostalari-izena + + + + Proxy + Proxya + + + + Audio + Audioa + + + + Microphone + Mikrofonoa + + + + Audio input device selector + + + + + Output Device + Irteera gailua + + + + Choose the output device + + + + + Video + Bideoa + + + + Device + Gailua + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + Txata + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + 00:00 + + + + Hangup + Eseki + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + Partekatu fitxategia + + + \ No newline at end of file diff --git a/translations/ring_client_windows_fa_IR.ts b/translations/ring_client_windows_fa_IR.ts new file mode 100644 index 00000000..a31c68bf --- /dev/null +++ b/translations/ring_client_windows_fa_IR.ts @@ -0,0 +1,1602 @@ + + + AboutDialog + + + + About + درباره + + + + about button + دکمه درباره + + + + credits button + دکمه دست‌اندرکاران + + + + Credits + دست‌اندرکاران + + + + Free as in Freedom + آزاد مانند آزادی + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + سرویس‌گیرنده ویندوز مایکروسافت برای جمی. +جمی یک نرم افزار ارتباطی امن و توزیع شده است. + + + + version + نسخه + + + + Created by: + ساخته‌شده توسط: + + + + Artwork by: + اثر هنری توسط: + + + + Based on the SFLPhone project + برمبنای پروژه SFLPhone + + + + AccountItemDelegate + + + Add Account + افزودن حساب + + + + AdvancedSIPSettingsWidget + + + Form + فرم + + + + Call Settings + تنظیمات تماس + + + + Auto Answer Calls + پاسخگویی خودکار تماس‌ها + + + + Enable Custom Ringtone + فعال‌سازی صدای زنگ سفارشی + + + + Connectivity + اتصال + + + + STUN Address + نشانی STUN + + + + Use STUN + استفاده از STUN + + + + Use UPnP + استفاده از UPnP + + + + Use TURN + استفاده از TURN + + + + TURN Password + گذرواژه TURN + + + + TURN Username + نام کاربری TURN + + + + TURN Address + نشانی TURN + + + + Media + رسانه + + + + Enable Video + فعّال کردن ویدیو + + + + Video Codecs + کدک‌های ویدیویی + + + + Audio Codecs + رمزینه‌های صوتی + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + فایل‌های صوتی (‎*.wav *.ogg *.opus *.mp3 *.aiff *.wma‎) + + + + Add a custom ringtone + افزودن یک صدای زنگ سفارشی + + + + AdvancedSettingsWidget + + + Form + فرم + + + + Call Settings + تنظیمات تماس + + + + Allow Calls From Untrusted Peers + اجازه تماس به همتاهای ناشناخته + + + + Auto Answer Calls + پاسخگویی خودکار تماس‌ها + + + + Enable Custom Ringtone + فعال‌سازی صدای زنگ سفارشی + + + + Add a custom ringtone + افزودن یک صدای زنگ سفارشی + + + + Name Server + سرور نام + + + + Address + نشانی + + + + OpenDHT Configuration + پکربندی OpenDHT + + + + Enable Proxy + فعال سازی پروکسی + + + + Bootstrap + خود راه اندازی + + + + Security + امنیت + + + + Private Key Password + گذرواژه کلید خصوصی + + + + User Certificate + گواهی کاربر + + + + Private Key + کلید خصوصی + + + + CA Certificate + گواهی CA + + + + Connectivity + اتصال + + + + Use STUN + استفاده از STUN + + + + STUN Address + نشانی STUN + + + + Use UPnP + استفاده از UPnP + + + + Use TURN + استفاده از TURN + + + + TURN Password + گذرواژه TURN + + + + TURN Username + نام‌کاربری TURN + + + + TURN Address + نشانی TURN + + + + Media + رسانه + + + + Enable Video + فعّال کردن ویدیو + + + + Audio Codecs + رمزینه‌های صوتی + + + + Video Codecs + کدک‌های ویدیویی + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + فایل‌های صوتی (‎*.wav *.ogg *.opus *.mp3 *.aiff *.wma‎) + + + + AnimatedOverlay + + + Form + فرم + + + + BannedItemWidget + + + Form + فرم + + + + name + نام + + + + id + شناسه + + + + CallWidget + + + Show conversations + نمایش گفتگو‌ها + + + + Conversations + گفتگو‌ها + + + + Search contact text input + جستجوی متن ورودی مخاطب + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + جمی نرم‌افزار آزاد برای ارتباط جهانی است که به آزادی‌ها و محرمانگی کاربرانش احترام می‌گذارد. + + + + + This is your ID. +Copy and share it with your friends! + + +این شناسه شماست. +آن را کپی کنید و با دوستان خود به اشتراک بگذارید! + + + + + Show ring ID QR code + نمایش کد QR شناسه رینگ + + + + Share ring ID button + به اشتراک گذاری دکمه آیدی رینگ + + + + Double-click to copy + برای کپی کردن دو بار کلیک کنید + + + + Error while generating QR Code + خطا در هنگام تولید کد QR + + + + + best name + بهترین نام + + + + best Id + بهترین شناسه + + + + Back to homepage button + دکمه برگشت به صفحه نخست + + + + Add to contacts + افزودن به مخاطبان + + + + Show invites + نمایش دعوت ها + + + + Invites + دعوت ها + + + + + Find a new or existing contact + یافتن مخاطب جدید یا موجود + + + + Wants to talk to you! + میخواهد با شما حرف بزند! + + + + Answer incoming call button + دکمه پاسخ به تماس ورودی + + + + Ignore incoming call button + دکمه نادیده گرفتن تماس ورودی + + + + Answer + پاسخ + + + + Ignore + رد + + + + Cancel outgoing call + لغو تماس خروجی + + + + Cancel + لغو + + + + Start video call + شروع تماس ویدیویی + + + + Start audio call + شروع تماس صوتی + + + + Clear conversation + پاک کردن گفتگو + + + + Remove contact + حذف مخاطب + + + + Block contact + مسدودسازی مخاطب + + + + Copy number + رونوشت از شماره + + + + Search your received invitations + جستجوی دعوت نامه‌های دریافت‌شده شما + + + + Contact me on Jami + با من روی جمی در تماس باشید! + + + + My Id is : + شناسه من: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + تماس ورودی از %1 + + + + DeleteAccountDialog + + + Account deletion + حذف حساب + + + + Do you really want to delete the following account? + آیا واقعا می‌خواهید حساب زیر را پاک کنید؟ + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + اگر این حساب برون‌برد نشده یا به دستگاه دیگری اضافه نشده باشد، برگشت‌ناپذیر خواهد بود. + + + + Permanently delete account + حذف حساب برای همیشه + + + + Delete + حذف + + + + Cancel account deletion + لغو حذف حساب + + + + Cancel + لغو + + + + DeviceItemWidget + + + Form + فرم + + + + Device Id + شناسه دستگاه + + + + this device + این دستگاه + + + + InviteButtonsWidget + + + Accept + پذیرش + + + + Refuse + رد + + + + Block + مسدود + + + + LinkDeviceDialog + + + Dialog + گفتگو + + + + Enter your account password + گذرواژه حساب‌تان را وارد کنید + + + + Password + گذرواژه + + + + Ok + باشه + + + + Cancel + لغو + + + + Exporting account + برون‌برد حساب + + + + Your PIN is + پین شما + + + + PIN + پین + + + + This pin and the account password should be entered in your device within 10 minutes. + این پین و گذرواژه حساب باید ظرف 10 دقیقه در دستگاه شما وارد شود. + + + + Close + بستن + + + + Link Another Device + پیوند یک دستگاه دیگر + + + + Incorrect password + گذرواژه ناصحیح + + + + Something went wrong. +Please try again later. + مشکلی پیش آمد. + لطفا بعدا دوباره امتحان کنید. + + + + MainWindow + + + Settings + تنظیمات + + + + Exit + خروج + + + + About + درباره + + + + Jami + جمی + + + + NameRegistrationDialog + + + Set Registered Name + تنظیم نام ثبت‌شده + + + + Enter your account password + گذرواژه حساب‌تان را وارد کنید + + + + Password text input + ورودی متن گذرواژه + + + + Password text entry + ورودی متن گذرواژه + + + + Password + گذرواژه + + + + Register + ثبت‌نام + + + + Cancel + لغو + + + + Registering Name + ثبت نام + + + + Something went wrong + مشکلی پیش آمد. + + + + Close + بستن + + + + Incorrect password + گذرواژه ناصحیح + + + + Network error + خطای شبکه + + + + NewWizardWidget + + + Form + فرم + + + + Welcome Label + برچست خوش آمد + + + + Welcome to + خوش آمدید به + + + + Welcome Logo + لوگو خوش آمد + + + + Create Jami account button + دکمه ایجاد حساب جمی + + + + Push button for Jami account creation start trigger + راه‌انداز دکمه فشاری برای ایجاد حساب جمی + + + + Create a Jami account + ایجاد یک حساب جمی + + + + Link device button + دکمه پیوند دستگاه + + + + Push button for device linkage start trigger + راه‌انداز دکمه فشاری برای پیوند دستگاه + + + + Link this device to an account + پیوند این دستگاه به یک حساب + + + + Create Jami SIP account button + دکمه ایجاد حساب SIP + + + + Push button for Jami SIP account creation start trigger + راه‌انداز دکمه فشاری برای ایجاد حساب SIP جمی + + + + Create a SIP account + ایجاد یک حساب SIP + + + + Link this device to an existing account + پیوند این دستگاه به یک حساب کاربری موجود + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; }</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">برای پیوند این دستگاه به حسابی دیگر شما در ابتدا </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">نیاز است کد پین را تولید کنید.</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> برای تولید کد پین:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">به </span><span style=" font-size:14px; font-weight:600;">تنظیمات مدیریت حساب</span><span style=" font-size:14px;"> در دستگاه قبلی رفته،</li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">حساب جمی را </span><span style=" font-size:14px; font-weight:600;">که می‌خواهید استفاده کنید،</span><span style=" font-size:14px;"> انتخاب کنید</span></li><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">‌به برگه </span><span style=" font-size:14px; font-weight:600;">دستگاه‌ها</span><span style=" font-size:14px;"> بروید و افزودن یک دستگاه را </span>انتخاب کنید</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">شما پین لازم برای تکمیل این فرم را دریافت خواهید کرد که تنها برای </span>10 دقیقه معتبر خواهد بود<span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + پین خود را وارد کنید: + + + + Or import a file: + یا درون‌ریزی یک پرونده: + + + + Link from exported account archive file + پیوند از فایل بایگانی حساب برون‌برد‌شده + + + + + + (None) + (هیچ) + + + + Password: + گذرواژه: + + + + + Profile + نمایه + + + + + Profile name + نام نمایه + + + + Account + حساب کاربری + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + نام‌کاربری خود را ثبت کنید. +با این کار نام‌کاربری رزرو می‌شود تا فقط شما بتوانید از آن استفاده کنید. + دوستان شما می‌توانند به جای استفاده از شناسه شما با نام‌کاربری‌تان با شما تماس بگیرند. + + + + Public username checkbox + چک‌باکس نام‌کاربری عمومی + + + + Checkbox selecting if the user wants a public username + چک‌باکس انتخاب اینکه آیا کاربر یک نام‌کاربری عمومی می‌خواهد + + + + Register public username + ثبت نام‌کاربری عمومی + + + + Public username edit + ویرایش نام کاربری عمومی + + + + Choose your username + نام کاربری خود را انتخاب کنید + + + + Password text input + ورودی متن گذرواژه + + + + Password text entry + ورودی متن گذرواژه + + + + + Password + گذرواژه + + + + + Password confirmation text input + متن وارد‌شده تایید گذرواژه + + + + Confirm password + تأیید گذرواژه + + + + SIP Account + حساب کاربری سیپ + + + + SIP Server edit + ویرایش سرور SIP + + + + Server + سرور + + + + SIP proxy input + پروکسی SIP وارد‌شده + + + + SIP proxy text entry + ورودی متن پروکسی SIP + + + + Proxy + پیشکار یا پروکسی + + + + SIP username input + ورودی نام کاربری SIP + + + + SIP Password text entry + ورودی متن گذرواژه SIP + + + + Username + نام کاربری + + + + + SIP Password text input + متن گذرواژه SIP وارد‌شده + + + + Generating your Jami account… + تولید حساب جمی شما... + + + + Previous page button + دکمه صفحه قبلی + + + + push button to access previous page of wizard + دکمه فشاری برای دسترسی به صفحه قبلی wizard + + + + Previous + پیشین + + + + Cancel account create/link + لغو ساخت/پیوند حساب + + + + push button to cancel account creation or linking + دکمه فشاری برای لغو ایجاد یا پیوند حساب + + + + Back + بازگشت + + + + Next page Button + دکمه صفحه بعد + + + + Push button to access next page of wizard + دکمه فشاری برای دسترسی به صفحه بعدی wizard + + + + Next + بعدی + + + + Open File + باز کردن فایل + + + + Jami archive files (*.gz); All files (*) + فایل‌های بایگانی جمی (‎*.gz)؛ تمام فایلها (*) + + + + Your account needs to be migrated. Enter your password. + نیاز است که حساب شما انتقال یابد. گذرواژه خود را وارد کنید. + + + + Migrating your Jami account... + در حال انتقال حساب جمی شما... + + + + Importing account archive... + ورود بایگانی حساب... + + + + Generating your Jami account... + تولید حساب جمی شما... + + + + Generating your SIP account... + تولید حساب SIP شما... + + + + Error creating account + خطای ایجاد حساب + + + + PasswordDialog + + + Change Account Password + تغییر گذرواژه حساب + + + + Enter Current Password + ورود گذرواژه فعلی + + + + Enter New Password + ورود گذرواژه جدید + + + + Confirm New Password + تایید گذرواژه جدید + + + + Confirm + تایید + + + + Cancel + لغو + + + + Current Password Incorrect + گذرواژه فعلی نادرست است. + + + + PhotoBoothDialog + + + Photobooth + غرفه عکس + + + + PhotoboothWidget + + + Form + فرم + + + + Photobooth display + نمایش غرفه عکس + + + + Choose File + انتخاب فایل + + + + Image Files (*.jpg *.jpeg *.png) + فایل‌های تصویر (‎*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + انتخاب فایل + + + + Files (*) + فایل‌ها (*) + + + + QObject + + + No default mail client found + هیچ سرویس‌گیرنده ایمیل پیش‌فرضی یافت نشد + + + + + Edit Device Name + ویرایش نام دستگاه + + + + Unlink Device From Account + حذف پیوند دستگاه به حساب + + + + Save new device name + ذخیره نام جدید دستگاه + + + + Add as contact + افزودن به عنوان مخاطب + + + + RingButton + + + Select folder + انتخاب پوشه + + + + SettingsWidget + + + Form + فرم + + + + Settings + تنظیمات + + + + Account + حساب کاربری + + + + + General + عمومی + + + + + Audio / Video + صدا / تصویر + + + + System + سامانه + + + + Enable desktop notifications + فعال‌سازی اعلان‌ها + + + + Keep minimized on close + در هنگام بستن کمینه نگه دار + + + + Download folder + شاخهٔ بارگیری + + + + Save in + ذخیره کن در + + + + Always Recording + همیشه در حال ضبط + + + + Updates + به‌روزرسانی‌ها + + + + Check for updates automatically every + بررسی خودکار به‌روزرسانی‌ها هر + + + + Interval between update checks in days selector + انتخاب‌گر بازه بین بررسی‌های برای به‌روزرسانی در واحد روز + + + + days + روز + + + + Check for updates now button + دکمه همین الان برروزرسانی ها را چک کن + + + + Check for updates now + به‌روزرسانی‌ها را همین الان بررسی کن + + + + Password + گذرواژه + + + + + Enable + فعال‌سازی + + + + toggle enable notifications + تغییر وضعیت فعال‌سازی اعلان‌ها + + + + Toggle keep minimized on close + تغییر وضعیت کمینه نگه داشتن در هنگام بستن + + + + Call Recordings + تماس‌های ضبط‌شده + + + + Toggle automatic updates + تغییر وضعیت به‌روزرسانی‌های خودکار + + + + Jami Account + حساب جمی + + + + + Profile + نمایه + + + + + Identity + هویت + + + + Id + شناسایی ID + + + + Registered name + نام ثبت شده + + + + Type here to register a username + برای ثبت نام‌کاربری اینجا بنویسید + + + + Register + ثبت‌نام + + + + Change Password + تغییر گذرواژه + + + + Export Account + برون‌برد (ذخیره) حساب + + + + + Delete Account + حذف حساب + + + + Linked Devices + دستگاه‌های پیوند‌داده‌شده + + + + Link Another Device + پیوند یک دستگاه دیگر + + + + Banned Contacts + مخاطبان مسدود شده + + + + Format + قالب + + + + Video device framerate selector + انتخاب‌گر نرخ فریم دستگاه ویدیویی + + + + Preview unavailable + پیش‌نمایش در‌دسترس نیست + + + + + Advanced Account Settings + تنظیمات پیش‌رفته حساب + + + + SIP Account + حساب کاربری سیپ + + + + Username + نام کاربری + + + + Hostname + نام میزبان + + + + Proxy + پیشکار یا پروکسی + + + + Audio + صدا + + + + Microphone + میکروفون + + + + Audio input device selector + انتخاب کننده دستگاه ورودی صوتی + + + + Output Device + دستگاه خروجی + + + + Choose the output device + انتخاب دستگاه خروجی + + + + Video + ویدیو + + + + Device + دستگاه + + + + Video device selector + انتخاب کننده دستگاه ویدیو + + + + A registered name should not have any spaces and must be at least three letters long + یک نام ثبت شده نمی‌بایست دارای هیچ فضای خالی بوده و حداقل دارای سه حرف باشد + + + + This name is already taken + این نام پیش از این گرفته شده است + + + + + Enter an alias + یک نام مستعار وارد کنید + + + + Register this name + تبت این نام + + + + Remove Device + حذف دستگاه + + + + Enter this account's password to confirm the removal of this device + گذرواژه این حساب را برای تایید حذف این دستگاه وارد کنید + + + + Are you sure you wish to remove this device? + اطمینان دارید که می‌خواهید این دستگاه را حذف کنید؟ + + + + Export Account Here + حساب را در اینجا برون‌برد کن + + + + Select A Folder For Your Downloads + یک پوشه برای دانلود‌های خود انتخاب کنید + + + + Select A Folder For Your Recordings + یک پوشه برای صدا‌های ضبط‌شده خود انتخاب کنید + + + + VideoOverlay + + + Call on Hold + تماس نگه‌داشته + + + + Hold / Unhold + نگه‌داشتن / رها کردن + + + + Chat + چت + + + + Mute Mic + قطع میکروفون + + + + Record call + ضبط تماس + + + + Name label + نامگذاری برچسب + + + + Time elapsed + زمان سپری شده + + + + 00:00 + 00:00 + + + + Hangup + به صحبت تلفنی خاتمه دادن + + + + Mute Video + بی‌صدا کردن ویدیو + + + + VideoView + + + Share entire screen + به اشتراک گذاری تمام صفحه نمایش + + + + Share screen area + ناحیه اشتراک صفحه + + + + Share file + هم‌رسانی پرونده + + + \ No newline at end of file diff --git a/translations/ring_client_windows_fi.ts b/translations/ring_client_windows_fi.ts new file mode 100644 index 00000000..f202c84f --- /dev/null +++ b/translations/ring_client_windows_fi.ts @@ -0,0 +1,1605 @@ + + + AboutDialog + + + + About + Tietoa + + + + about button + tietoja-painike + + + + credits button + tekijät -painike + + + + Credits + Tekijät + + + + Free as in Freedom + Ilmainen kuin vapaus + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Jami -sovellus Microsoft Windowsille. +Jami on turvallinen ja hajautettu viestintäsovellus. + + + + version + versio + + + + Created by: + Luonut: + + + + Artwork by: + Grafiikka: + + + + Based on the SFLPhone project + Pohjautuu SFLPhone-projektiin + + + + AccountItemDelegate + + + Add Account + Lisää tunnus + + + + AdvancedSIPSettingsWidget + + + Form + Muoto + + + + Call Settings + Puheluasetukset + + + + Auto Answer Calls + Vastaa puheluihin automaattisesti + + + + Enable Custom Ringtone + Oma soittoääni + + + + Connectivity + Yhteydet + + + + STUN Address + STUN osoite + + + + Use STUN + Käytä STUNia + + + + Use UPnP + Käytä UPnP + + + + Use TURN + Käytä TURNia + + + + TURN Password + TURN salasana + + + + TURN Username + TURN käyttäjänimi + + + + TURN Address + TURN osoite + + + + Media + Media + + + + Enable Video + Video päälle + + + + Video Codecs + Videokoodekit + + + + Audio Codecs + Äänikoodekit + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Äänitiedostot (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Lisää oma soittoääni + + + + AdvancedSettingsWidget + + + Form + Muoto + + + + Call Settings + Puheluasetukset + + + + Allow Calls From Untrusted Peers + Salli puhelut tuntemattomilta + + + + Auto Answer Calls + Vastaa puheluihin automaattisesti + + + + Enable Custom Ringtone + Oma soittoääni + + + + Add a custom ringtone + Lisää oma soittoääni + + + + Name Server + Nimipalvelin + + + + Address + Osoite + + + + OpenDHT Configuration + OpenDHT-määritykset + + + + Enable Proxy + Proxy käyttöön + + + + Bootstrap + Bootstrap + + + + Security + Turvallisuus + + + + Private Key Password + Salausavaimen salassana + + + + User Certificate + Käyttäjävarmenne + + + + Private Key + Henkilökohtainen salausavain + + + + CA Certificate + CA-varmenne + + + + Connectivity + Yhteydet + + + + Use STUN + Käytä STUNia + + + + STUN Address + STUN-osoite + + + + Use UPnP + Käytä UPnP + + + + Use TURN + Käytä TURNia + + + + TURN Password + TURN salasana + + + + TURN Username + TURN käyttäjänimi + + + + TURN Address + TURN osoite + + + + Media + Media + + + + Enable Video + Video päälle + + + + Audio Codecs + Äänikoodekit + + + + Video Codecs + Videokoodekit + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Äänitiedostot (*.wav *.ogg *.opus *.mp3 *aiff * wma) + + + + AnimatedOverlay + + + Form + Muoto + + + + BannedItemWidget + + + Form + Muoto + + + + name + nimi + + + + id + id + + + + CallWidget + + + Show conversations + Näytä keskustelu + + + + Conversations + Keskustelu + + + + Search contact text input + Hae yhteystieto tekstihaulla + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami on vapaa ja universaali viestintäalusta, joka kunnioittaa käyttäjiensä vapauksia ja yksityisyyttä. + + + + + This is your ID. +Copy and share it with your friends! + + +Tämä on sinun ID-tunnus. +Kopioi ja jaa se ystäviesi kanssa! + + + + Show ring ID QR code + Näytä ring ID QR-koodi + + + + Share ring ID button + Jaa ring ID -painike + + + + Double-click to copy + Kopioi kaksoisnapsauttamalla + + + + Error while generating QR Code + Virhe generoidessa QR-koodia + + + + + best name + paras nimi + + + + best Id + paras Id + + + + Back to homepage button + Takaisin kotisivulle -painike + + + + Add to contacts + Lisää yhteystietoihin + + + + Show invites + Näytä kutsut + + + + Invites + Kutsut + + + + + Find a new or existing contact + Etsi uusi tai olemassaoleva yhteystieto + + + + Wants to talk to you! + Haluaa puhua kanssasi! + + + + Answer incoming call button + Vastaa saapuvaan puheluun -painike + + + + Ignore incoming call button + Ohita saapuvan puhelun -painike + + + + Answer + Vastaa + + + + Ignore + Sivuuta + + + + Cancel outgoing call + Peruuta lähtevä puhelu + + + + Cancel + Peru + + + + Start video call + Aloita videopuhelu + + + + Start audio call + Aloita äänipuhelu + + + + Clear conversation + Tyhjennä keskustelu + + + + Remove contact + Poista yhteystieto + + + + Block contact + Estä yhteystieto + + + + Copy number + Kopioi numero + + + + Search your received invitations + Hae vastaanotetuista kutsuista + + + + Contact me on Jami + Ota minuun yhteyttä Jamilla + + + + My Id is : + Minun id on : + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Saapuva puhelu %1 + + + + DeleteAccountDialog + + + Account deletion + Tunnuksen poisto + + + + Do you really want to delete the following account? + Haluatko todella poistaa seuraavan tunnuksen? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Jos tätä tunnusta ei ole viety tai lisätty toiseen laitteeseen, se katoaa lopullisesti. + + + + Permanently delete account + Poista tunnus pysyvästi + + + + Delete + Poista + + + + Cancel account deletion + Peruuta tunnuksen poisto + + + + Cancel + Peru + + + + DeviceItemWidget + + + Form + Muoto + + + + Device Id + Laitteen Id + + + + this device + tämä laite + + + + InviteButtonsWidget + + + Accept + Hyväksy + + + + Refuse + Kieltäydy + + + + Block + Estä + + + + LinkDeviceDialog + + + Dialog + Valintaikkuna + + + + Enter your account password + Anna tunnuksen salasana + + + + Password + Salasana + + + + Ok + Vahvista + + + + Cancel + Peruuta + + + + Exporting account + Tunnuksen vienti + + + + Your PIN is + Sinun PIN on + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Tämä pin-koodi ja tunnuksen salasana on syötettävä laitteeseesi 10 minuutin kuluessa. + + + + Close + Sulje + + + + Link Another Device + Linkitä toinen laite + + + + Incorrect password + Väärä salasana + + + + Something went wrong. +Please try again later. + Jokin meni pieleen. +Yritä myöhemmin uudelleen. + + + + MainWindow + + + Settings + Asetukset + + + + Exit + Poistu + + + + About + Tietoa + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Aseta rekisteröity nimi + + + + Enter your account password + Anna tunnuksen salasana + + + + Password text input + Salasana tekstinsyöttö + + + + Password text entry + Salasanan tekstikenttä + + + + Password + Salasana + + + + Register + Rekistöröi + + + + Cancel + Peruuta + + + + Registering Name + Rekisteröidään nimeä + + + + Something went wrong + Jokin meni pieleen. + + + + Close + Sulje + + + + Incorrect password + Väärä salasana + + + + Network error + Verkkovirhe + + + + NewWizardWidget + + + Form + Muoto + + + + Welcome Label + Tervehdysmerkki + + + + Welcome to + Tervetuloa + + + + Welcome Logo + Tervetuloa logo + + + + Create Jami account button + Luo Jami-tunnuksen painike + + + + Push button for Jami account creation start trigger + Paina painiketta Jami-tunnuksen luomiseksi + + + + Create a Jami account + Luo Jami-tunnus + + + + Link device button + Linkitä laite painike + + + + Push button for device linkage start trigger + Paina painiketta laitteen linkittämiseksi + + + + Link this device to an account + Yhdistä tämä laite tunnukseen + + + + Create Jami SIP account button + Luo Jami SIP-tunnuksen painike + + + + Push button for Jami SIP account creation start trigger + Paina painiketta Jami SIP-tunnuksen luomiseksi + + + + Create a SIP account + Luo SIP tunnus + + + + Link this device to an existing account + Yhdistä tämä laite olemassa olevaan tunnukseen + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Jos haluat linkittää tämän laitteen toiseen tunnukseen, sinun on </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">ensin hankittava PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> -koodi. PIN-koodin luominen:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Mene </span><span style=" font-size:14px; font-weight:600;">käyttäjäasetuksiin</span><span style=" font-size:14px;"> aiemmasta laitteesta.</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Valitse </span><span style=" font-size:14px; font-weight:600;">Jami-tunnus</span><span style=" font-size:14px;"> jota haluat käyttää</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Mene </span><span style=" font-size:14px; font-weight:600;">Laitteet</span><span style=" font-size:14px;"> välilehteen</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Valitse </span><span style=" font-size:14px; font-weight:600;">Lisää laite</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Saat tarvittavan PIN-koodin tämän lomakkeen lopussa. PIN-koodi on voimassa vain </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minuuttia</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Syötä pin-koodisi: + + + + Or import a file: + Tai tuo tiedosto: + + + + Link from exported account archive file + Linkki viedystä tunnuksen arkistotiedostosta + + + + + + (None) + (Ei mitään) + + + + Password: + Salasana: + + + + + Profile + Profiili + + + + + Profile name + Profiilin nimi + + + + Account + Tunnus + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Rekisteröi käyttäjänimesi. +Tämä varaa käyttäjätunnuksen, jotta vain sinä voit käyttää sitä. +Kaverisi voivat soittaa sinulle käyttäjänimelläsi sen sijaan, +että käyttäisit tunnustasi. + + + + Public username checkbox + Julkinen käyttäjänimi valintaruutu + + + + Checkbox selecting if the user wants a public username + Merkitse laatikko, jos käyttäjä haluaa julkisen käyttänimen + + + + Register public username + Rekisteröi julkinen käyttäjänimi + + + + Public username edit + Julkisen käyttäjänimen muokkaus + + + + Choose your username + Valitse käyttäjänimesi + + + + Password text input + Salasana tekstinsyöttö + + + + Password text entry + Salasanan tekstikenttä + + + + + Password + Salasana + + + + + Password confirmation text input + Salasanan vahvistuksen tekstikenttä + + + + Confirm password + Varmista salasana + + + + SIP Account + SIP tunnus + + + + SIP Server edit + SIP-palvelimen muokkaus + + + + Server + Palvelin + + + + SIP proxy input + Syötä SIP-välityspalvelin + + + + SIP proxy text entry + SIP-välityspalvelimen tekstikenttä + + + + Proxy + Välityspalvelin + + + + SIP username input + Syötä SIP-käyttäjänimi + + + + SIP Password text entry + SIP-salasanan tekstikenttä + + + + Username + Käyttäjänimi + + + + + SIP Password text input + Syötä SIP-salasana + + + + Generating your Jami account… + Luodaan Jami-tunnusta... + + + + Previous page button + Edellinen sivu -painike + + + + push button to access previous page of wizard + paina painiketta päästäksesi ohjatun toiminnon edelliselle sivulle + + + + Previous + Edellinen + + + + Cancel account create/link + Peruuta tunnuksen luominen/linkitys + + + + push button to cancel account creation or linking + painamalla painiketta voit peruuttaa tunnuksen luomisen tai linkittämisen + + + + Back + Takaisin + + + + Next page Button + Seuraava sivu -painike + + + + Push button to access next page of wizard + Painamalla painiketta pääset ohjatun toiminnon seuraavalle sivulle + + + + Next + Seuraava + + + + Open File + Avaa tiedosto + + + + Jami archive files (*.gz); All files (*) + Jami-arkistoidut tiedostot (*.gz); Kaikki tiedostot (*) + + + + Your account needs to be migrated. Enter your password. + Tunnuksesi on siirrettävä. Syötä salasanasi. + + + + Migrating your Jami account... + Jami-tunnuksesi siirretään… + + + + Importing account archive... + Tuodaan Tunnus-arkistoa... + + + + Generating your Jami account... + Luodaan sinun Jami-tunnusta... + + + + Generating your SIP account... + Luodaan sinun SIP-tunnusta... + + + + Error creating account + Virhe tunnuksen luomisessa + + + + PasswordDialog + + + Change Account Password + Vaihda tunnuksen salasana + + + + Enter Current Password + Anna nykyinen salasana + + + + Enter New Password + Anna uusi salasana + + + + Confirm New Password + Vahvista uusi salasana + + + + Confirm + Vahvista + + + + Cancel + Peruuta + + + + Current Password Incorrect + Nykyinen salasana väärä + + + + PhotoBoothDialog + + + Photobooth + Valokuva-automaatti + + + + PhotoboothWidget + + + Form + Muoto + + + + Photobooth display + Valokuvanäyttö + + + + Choose File + Valitse tiedosto + + + + Image Files (*.jpg *.jpeg *.png) + Kuvatiedostot (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Valitse tiedosto + + + + Files (*) + Tiedostot (*) + + + + QObject + + + No default mail client found + Oletussähköpostiohjelmaa ei löytynyt + + + + + Edit Device Name + Muokkaa laitteen nimeä + + + + Unlink Device From Account + Irrota laite tunnuksesta + + + + Save new device name + Tallenna uuden laitteen nimi + + + + Add as contact + Lisää yhteystietona + + + + RingButton + + + Select folder + Valitse kansio + + + + SettingsWidget + + + Form + Muoto + + + + Settings + Asetukset + + + + Account + Tunnus + + + + + General + Yleiset + + + + + Audio / Video + Ääni / Video + + + + System + Järjestelmä + + + + Enable desktop notifications + Työpöytäilmoitukset käyttöön + + + + Keep minimized on close + Pidä pienennettynä suljettaessa + + + + Download folder + Ladatut kansio + + + + Save in + Tallenna sisään + + + + Always Recording + Tallentaa aina + + + + Updates + Päivitykset + + + + Check for updates automatically every + Tarkista päivitykset automaattisesti joka + + + + Interval between update checks in days selector + Päivitystarkistusten välinen aika + + + + days + päivää + + + + Check for updates now button + Tarkista päivitykset nyt -painike + + + + Check for updates now + Tarkista päivitykset nyt + + + + Password + Salasana + + + + + Enable + Päällä + + + + toggle enable notifications + Vaihda ilmoitukset käyttöön + + + + Toggle keep minimized on close + Valinta pidä pinennettynä suljettaessa + + + + Call Recordings + Puhelun tallenteet + + + + Toggle automatic updates + Vaihda automaattiset päivitykset + + + + Jami Account + Jami tunnus + + + + + Profile + Profiili + + + + + Identity + Tunnistetiedot + + + + Id + Id + + + + Registered name + Rekisteröity nimi + + + + Type here to register a username + Kirjoita tähän rekisteröidäksesi käyttäjänimen + + + + Register + Rekistöröi + + + + Change Password + Vaihda salasana + + + + Export Account + Vie tunnus + + + + + Delete Account + Poista tunnus + + + + Linked Devices + Linkitetyt laitteet + + + + Link Another Device + Linkitä toinen laite + + + + Banned Contacts + Estetyt yhteystiedot + + + + Format + Muoto + + + + Video device framerate selector + Videolaitteen kuvanopeuden valitsin + + + + Preview unavailable + Esikatselua ei käytettävissä + + + + + Advanced Account Settings + Tilin lisäasetukset + + + + SIP Account + SIP tunnus + + + + Username + Käyttäjänimi + + + + Hostname + Yhteysosoite + + + + Proxy + Välityspalvelin + + + + Audio + Ääni + + + + Microphone + Mikrofoni + + + + Audio input device selector + Äänen sisääntulon valitsin + + + + Output Device + Ulostulon laite + + + + Choose the output device + Valitse laitteen ulostulo + + + + Video + Video + + + + Device + Laite + + + + Video device selector + Videolaitteen valitsin + + + + A registered name should not have any spaces and must be at least three letters long + Rekisteröitävässä nimessä ei saa olla välilyöntejä ja sen on oltava vähintään kolme kirjainta pitkä + + + + This name is already taken + Tämä nimi on jo varattu + + + + + Enter an alias + Anna lempinimi + + + + Register this name + Rekisteröi tämä nimi + + + + Remove Device + Poista laite + + + + Enter this account's password to confirm the removal of this device + Anna tämän tunnuksen salasana vahvistaaksesi laitteen poistamisen + + + + Are you sure you wish to remove this device? + Haluatko varmasti poistaa tämän laitteen? + + + + Export Account Here + Vie tunnus tähän + + + + Select A Folder For Your Downloads + Valitse kansio latauksia varten + + + + Select A Folder For Your Recordings + Valitse kansio tallennuksia varten + + + + VideoOverlay + + + Call on Hold + Puhelu pidossa + + + + Hold / Unhold + Pitoon / poista pidosta + + + + Chat + keskustelu + + + + Mute Mic + Mykistä mikki + + + + Record call + Tallenna puhelu + + + + Name label + Nimikenttä + + + + Time elapsed + Kulunut aika + + + + 00:00 + 00:00 + + + + Hangup + Katkaisu + + + + Mute Video + Mykistä video + + + + VideoView + + + Share entire screen + Jaa näyttö + + + + Share screen area + Jaa osa näytöstä + + + + Share file + Jaa tiedosto + + + \ No newline at end of file diff --git a/translations/ring_client_windows_fr.ts b/translations/ring_client_windows_fr.ts new file mode 100644 index 00000000..a3fd61b0 --- /dev/null +++ b/translations/ring_client_windows_fr.ts @@ -0,0 +1,1600 @@ + + + AboutDialog + + + + About + À Propos + + + + about button + Le bouton à-propos + + + + credits button + Bouton de crédits + + + + Credits + Crédits + + + + Free as in Freedom + Free as in Freedom + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Le client Windows de Jami +Jami est un logiciel libre de communication sécurisé et distribué. + + + + version + version + + + + Created by: + Créé par : + + + + Artwork by: + Graphismes par : + + + + Based on the SFLPhone project + Basé sur le projet SFLphone + + + + AccountItemDelegate + + + Add Account + Ajouter un compte + + + + AdvancedSIPSettingsWidget + + + Form + Formulaire + + + + Call Settings + Paramètres des Appels + + + + Auto Answer Calls + Répondre automatiquement les appels + + + + Enable Custom Ringtone + Activer les sonneries personnalisables + + + + Connectivity + Connectivité + + + + STUN Address + Adresse STUN + + + + Use STUN + Utiliser STUN + + + + Use UPnP + Utiliser UPnP + + + + Use TURN + Utiliser TURN + + + + TURN Password + Mot de passe TURN + + + + TURN Username + Nom d'utilisateur TURN + + + + TURN Address + Adresse du serveur TURN + + + + Media + Médias + + + + Enable Video + Activer la vidéo + + + + Video Codecs + Codecs vidéo + + + + Audio Codecs + Codecs audio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Fichiers audio ( wav, ogg, opus, mp3, aiff, wma) + + + + Add a custom ringtone + Ajouter une sonnerie externe + + + + AdvancedSettingsWidget + + + Form + Formulaire + + + + Call Settings + Paramètres des Appels + + + + Allow Calls From Untrusted Peers + Accepter les appels provenant de peers non fiables + + + + Auto Answer Calls + Répondre automatiquement les appels + + + + Enable Custom Ringtone + Activer les sonneries personnalisables + + + + Add a custom ringtone + Ajouter une sonnerie externe + + + + Name Server + Nom du Serveur + + + + Address + Adresse + + + + OpenDHT Configuration + Configuration OpenDHT + + + + Enable Proxy + Utiliser un nœud délégataire + + + + Bootstrap + Adresse du nœud d'amorçage + + + + Security + Sécurité + + + + Private Key Password + Mot de passe de la clé + + + + User Certificate + Certificat de l'utilisateur + + + + Private Key + Clé privée + + + + CA Certificate + Certificat CA + + + + Connectivity + Connectivité + + + + Use STUN + Utiliser STUN + + + + STUN Address + Adresse STUN + + + + Use UPnP + Utiliser UPnP + + + + Use TURN + Utiliser TURN + + + + TURN Password + Mot de passe TURN + + + + TURN Username + Nom d'utilisateur TURN + + + + TURN Address + Adresse du serveur TURN + + + + Media + Médias + + + + Enable Video + Activer la vidéo + + + + Audio Codecs + Codecs audio + + + + Video Codecs + Codecs vidéo + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Fichiers audio ( wav, ogg, opus, mp3, aiff, wma) + + + + AnimatedOverlay + + + Form + Formulaire + + + + BannedItemWidget + + + Form + Formulaire + + + + name + nom + + + + id + id + + + + CallWidget + + + Show conversations + Montrer les conversations + + + + Conversations + Conversations + + + + Search contact text input + Rechercher le texte du contact + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami est un logiciel libre de communication universel qui respecte la vie privée et l'intimité de ses utilisateurs. + + + + + This is your ID. +Copy and share it with your friends! + + Ceci est votre identifiant Jami. Conservez et partagez-le avec vos amis ! + + + + Show ring ID QR code + Montrer le code QR d'identification ring + + + + Share ring ID button + Partager le bouton d'identification ring + + + + Double-click to copy + Double-cliquez pour copier + + + + Error while generating QR Code + Erreur pendant la génération du QR code + + + + + best name + meilleur nom + + + + best Id + Meilleur ID + + + + Back to homepage button + Bouton de retour a la page d'accueil + + + + Add to contacts + Ajouter aux contacts + + + + Show invites + Montrer les invitations + + + + Invites + Invitations + + + + + Find a new or existing contact + Recherche de contacts nouveaux ou existants + + + + Wants to talk to you! + souhaite entrer en communication ! + + + + Answer incoming call button + Bouton de réponse d'appel + + + + Ignore incoming call button + Bouton de refus d'appel + + + + Answer + Répondre + + + + Ignore + Ignorer + + + + Cancel outgoing call + Annuler l'appel sortant + + + + Cancel + Annuler + + + + Start video call + Démarrer un appel vidéo + + + + Start audio call + Démarrer un appel audio + + + + Clear conversation + Effacer la conversation + + + + Remove contact + Retirer le contact + + + + Block contact + Bloquer le contact + + + + Copy number + Copier le numéro ou identifiant Ring + + + + Search your received invitations + Rechercher dans les invitations reçues + + + + Contact me on Jami + Contactez moi sur Jami + + + + My Id is : + Mon ID est : + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Appel entrant de %1 + + + + DeleteAccountDialog + + + Account deletion + Suppression de compte + + + + Do you really want to delete the following account? + Souhaitez-vous vraiment supprimer ce compte ? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Si ce compte n'a pas été exporté, ou ajouté vers un autre appareil, il sera perdu irrémédiablement. + + + + Permanently delete account + Supprimer de manière définitive le compte + + + + Delete + Supprimer + + + + Cancel account deletion + Annuler la suppression du compte + + + + Cancel + Annuler + + + + DeviceItemWidget + + + Form + Formulaire + + + + Device Id + Identifiant d'appareil + + + + this device + cet appareil + + + + InviteButtonsWidget + + + Accept + Accepter + + + + Refuse + Refuser + + + + Block + Bloquer + + + + LinkDeviceDialog + + + Dialog + Dialogue + + + + Enter your account password + Entrer le mot de passe de votre compte + + + + Password + Mot de passe + + + + Ok + OK + + + + Cancel + Annuler + + + + Exporting account + Compte exporté + + + + Your PIN is + Votre NIP est + + + + PIN + NIP + + + + This pin and the account password should be entered in your device within 10 minutes. + Ce NIP ainsi que le mot de passe du compte doivent être saisis dans votre nouvel appareil dans les 10 prochaines minutes. + + + + Close + Fermer + + + + Link Another Device + Associer un autre appareil + + + + Incorrect password + Mot de passe incorrecte + + + + Something went wrong. +Please try again later. + Quelque chose s'est mal passée. +Veuillez réessayer plus tard. + + + + MainWindow + + + Settings + Paramètres + + + + Exit + Sortir + + + + About + À Propos + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Enregistrer un nom d'utilisateur + + + + Enter your account password + Entrer le mot de passe de votre compte + + + + Password text input + Saisie du mot de passe + + + + Password text entry + Saisie de mot de passe + + + + Password + Mot de passe + + + + Register + Enregistrer + + + + Cancel + Annuler + + + + Registering Name + En cours d’enregistrement du nom + + + + Something went wrong + Quelque chose s'est mal passée. + + + + Close + Fermer + + + + Incorrect password + Mot de passe incorrecte + + + + Network error + Erreur de réseau + + + + NewWizardWidget + + + Form + Formulaire + + + + Welcome Label + étiquette de bienvenue + + + + Welcome to + Bienvenue sur + + + + Welcome Logo + Logo d'accueil + + + + Create Jami account button + Bouton de création de compte Jami + + + + Push button for Jami account creation start trigger + Bouton pour déclencher la création d'un compte Ring + + + + Create a Jami account + Créer un compte Jami + + + + Link device button + Bouton pour lier le compte de l'appareil + + + + Push button for device linkage start trigger + Appuyez sur le bouton pour déclencher la liaison de l'appareil + + + + Link this device to an account + Lier cet appareil à un compte + + + + Create Jami SIP account button + Bouton de création de compte SIP Jami + + + + Push button for Jami SIP account creation start trigger + Bouton pour déclencher la création d'un compte Jami + + + + Create a SIP account + Créer un compte SIP + + + + Link this device to an existing account + Lier cet appareil à un compte existant + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:12pt;">Pour associer cet appareil à un autre compte, vous devez d'abord obtenir un NIP</span>Pour cela, allez dans la gestion des comptes de l'appareil contenant le compte voulu et sélectionnez le compte que vous souhaitez utiliser. +<li style=" font-family:'Ubuntu'; font-size:12pt;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Allez à la tabulation<span style=" font-weight:600;">Appareils</span></li> +<li style=" font-family:'Ubuntu'; font-size:12pt;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Choisissez<span style=" font-weight:600;">Ajouter un appareil</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:12pt;">Vous obtiendrez alors un NIP qui vous permettra de terminer l'opération. Ce NIP est seulement valide pour une durée de </span><span style=" font-family:'Ubuntu'; font-size:12pt; font-weight:600;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:12pt;">.</span></p></body></html> + + + + Enter your pin: + Entrez votre NIP : + + + + Or import a file: + Ou importer une archive : + + + + Link from exported account archive file + Lier depuis une archive du compte exportée + + + + + + (None) + (None) + + + + Password: + Mot de passe : + + + + + Profile + Profil + + + + + Profile name + Nom du profile + + + + Account + Compte + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Enregistre votre nom d'utilisateur Jami. +Ceci réserve ce nom de façon définitive et uniquement pour votre usage. +Vos amis pourront vous appeler avec ce nom. + + + + Public username checkbox + case à cocher nom d'utilisateur public + + + + Checkbox selecting if the user wants a public username + Case à cocher si l'utilisateur veut un identifiant public + + + + Register public username + Enregistrer un nom d'utilisateur public + + + + Public username edit + Modification de l'identifiant public + + + + Choose your username + Choisissez un nom d'utilisateur + + + + Password text input + Saisie du mot de passe + + + + Password text entry + Saisie de mot de passe + + + + + Password + Mot de passe + + + + + Password confirmation text input + Texte de confirmation de l'entrée du mot de passe + + + + Confirm password + Confirmer le mot de passe + + + + SIP Account + Compte SIP + + + + SIP Server edit + Adresse du serveur SIP + + + + Server + Serveur + + + + SIP proxy input + Adresse du proxy SIP + + + + SIP proxy text entry + Texte du proxy SIP + + + + Proxy + Serveur mandataire + + + + SIP username input + SIP du nom d'usager en entré + + + + SIP Password text entry + Texte de mot de passe du compte SIP + + + + Username + Nom d'utilisateur + + + + + SIP Password text input + Texte de mot de passe du compte SIP + + + + Generating your Jami account… + Génération de votre compte Jami... + + + + Previous page button + Bouton de la page précédente + + + + push button to access previous page of wizard + Appuyez sur le bouton pour accéder à la page précédente + + + + Previous + Précédent + + + + Cancel account create/link + Annuler la création/lien de compte + + + + push button to cancel account creation or linking + Bouton pour déclencher la création d'un compte Jami ou association + + + + Back + Précédent + + + + Next page Button + bouton prochaine page + + + + Push button to access next page of wizard + Appuyez sur le bouton pour accéder à la page suivante de l'assistant + + + + Next + Suivant + + + + Open File + Ouvrir un fichier + + + + Jami archive files (*.gz); All files (*) + Fichier d'archive de compte Jami (*.gz); Tout fichier (*) + + + + Your account needs to be migrated. Enter your password. + Votre compte doit être migré. Entrez votre mot de passe + + + + Migrating your Jami account... + Migration de votre compte Jami... + + + + Importing account archive... + Importation de l'archive du compte + + + + Generating your Jami account... + Génération de votre compte Jami... + + + + Generating your SIP account... + Création de votre compte SIP ... + + + + Error creating account + Erreur pendant la création du compte + + + + PasswordDialog + + + Change Account Password + Changer le mot de passe du compte + + + + Enter Current Password + Entrez le mot de passe actuel + + + + Enter New Password + Entrez le nouveau mot de passe + + + + Confirm New Password + Confirmer le nouveau mot de passe + + + + Confirm + Confirmer + + + + Cancel + Annuler + + + + Current Password Incorrect + Mot de passe actuel incorrect + + + + PhotoBoothDialog + + + Photobooth + Kiosque de photo + + + + PhotoboothWidget + + + Form + Formulaire + + + + Photobooth display + Affichage du photomaton + + + + Choose File + Choisir un fichier + + + + Image Files (*.jpg *.jpeg *.png) + Fichiers images (JPG, JPEG, PNG) + + + + PrivateBridging + + + Choose File + Choisir un fichier + + + + Files (*) + Fichiers (*) + + + + QObject + + + No default mail client found + Aucun client email trouvé + + + + + Edit Device Name + Modifier le nom de l'appareil + + + + Unlink Device From Account + Dissocier cet appareil d'un compte + + + + Save new device name + Sauvegarder le nouveau nom d'appareil + + + + Add as contact + Rajouter comme contact + + + + RingButton + + + Select folder + Selectionner le fichier + + + + SettingsWidget + + + Form + Formulaire + + + + Settings + Paramètres + + + + Account + Compte + + + + + General + Général + + + + + Audio / Video + Audio / Vidéo + + + + System + Système + + + + Enable desktop notifications + Activer les notifications du bureau + + + + Keep minimized on close + Laisser minimisé en fermant + + + + Download folder + Répertoire de téléchargement + + + + Save in + Enregistrer dans + + + + Always Recording + Enregistrer tout le temps + + + + Updates + Mises à jour + + + + Check for updates automatically every + Vérifiez automatiquement les mises à jours tous les + + + + Interval between update checks in days selector + Sélectionneur d'intervalle de jour pour la vérification des mises à jour + + + + days + jours + + + + Check for updates now button + Bouton vérifier les mises à jour actuelles + + + + Check for updates now + Vérifier les mises-à-jour + + + + Password + Mot de passe + + + + + Enable + Activer + + + + toggle enable notifications + Case à cocher Activer les notifications + + + + Toggle keep minimized on close + Case à cocher laisser minimisé à la fermeture + + + + Call Recordings + Appel enregistrement + + + + Toggle automatic updates + Case à cocher des mises à jour automatiques + + + + Jami Account + Compte Jami + + + + + Profile + Profil + + + + + Identity + Identité + + + + Id + Identifiant + + + + Registered name + Nom enregistré + + + + Type here to register a username + Saisissez ici le nom d'enregistrement + + + + Register + Enregistrer + + + + Change Password + Changer le mot de passe + + + + Export Account + Exporter le compte + + + + + Delete Account + Supprimer le compte + + + + Linked Devices + Appareils associés + + + + Link Another Device + Associer un autre appareil + + + + Banned Contacts + Contacts bannis + + + + Format + Format + + + + Video device framerate selector + Sélecteur du débit de trame vidéo + + + + Preview unavailable + Prévisualisation indisponible + + + + + Advanced Account Settings + Paramètres avancés du compte + + + + SIP Account + Compte SIP + + + + Username + Nom d'utilisateur + + + + Hostname + Nom d'hôte + + + + Proxy + Serveur mandataire + + + + Audio + Audio + + + + Microphone + Microphone + + + + Audio input device selector + Sélecteur d'entrée audio + + + + Output Device + Périphérique de sortie + + + + Choose the output device + Choisissez le périphérique de sortie + + + + Video + Vidéo + + + + Device + Appareil + + + + Video device selector + Sélecteur de périphérique vidéeo + + + + A registered name should not have any spaces and must be at least three letters long + Un nom d’enregistrement est composé d'un minimum de 3 caractères et ne doit contenir aucun espace + + + + This name is already taken + Ce nom a déjà été pris + + + + + Enter an alias + Entrer un alias + + + + Register this name + Enregistrer ce nom + + + + Remove Device + Supprimer cet appareil + + + + Enter this account's password to confirm the removal of this device + Entrer le mot de passe du compte pour confirmer la suppression de cet appareil + + + + Are you sure you wish to remove this device? + Êtes-vous certain que vous souhaitez supprimer cet appareil ? + + + + Export Account Here + Exporter le compte ici + + + + Select A Folder For Your Downloads + Choisissez un répertoire pour vos téléchargements + + + + Select A Folder For Your Recordings + Choisissez un répertoire pour vos enregistrements + + + + VideoOverlay + + + Call on Hold + Appel en attente + + + + Hold / Unhold + Pause / Reprendre + + + + Chat + Clavardage + + + + Mute Mic + Couper le micro + + + + Record call + Enregistrer l'appel + + + + Name label + Étiquette de nom + + + + Time elapsed + Temps écoulé + + + + 00:00 + 00:00 + + + + Hangup + Raccrocher + + + + Mute Video + Désactiver la vidéo + + + + VideoView + + + Share entire screen + Partager tout l'écran + + + + Share screen area + Partager une partie de l'écran + + + + Share file + Partager un fichier + + + \ No newline at end of file diff --git a/translations/ring_client_windows_fr_BE.ts b/translations/ring_client_windows_fr_BE.ts new file mode 100644 index 00000000..f94c3862 --- /dev/null +++ b/translations/ring_client_windows_fr_BE.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Connectivité + + + + STUN Address + + + + + Use STUN + Utiliser STUN + + + + Use UPnP + + + + + Use TURN + Utiliser le TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adresse + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Amorcer + + + + Security + Sécurité + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Connectivité + + + + Use STUN + Utiliser STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Utiliser le TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Conversations + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami est un logiciel libre de communication universel qui respecte la vie privée et l'intimité de ses utilisateurs. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Ajouter aux contacts + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + Annuler + + + + Start video call + Démarrer appel video + + + + Start audio call + Démarrer appel audio + + + + Clear conversation + Effacer la conversation + + + + Remove contact + + + + + Block contact + Bloquer le contact + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + Annuler + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Accepter + + + + Refuse + Refuser + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + OK + + + + Cancel + Annuler + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + + + + + Exit + + + + + About + + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + Enregistrer + + + + Cancel + Annuler + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Créer un compte Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Lier cet appareil à un compte + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Mot de passe: + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Compte + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Enregistrer un nom d'utilisateur public + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Suivant + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Annuler + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + + + + + Account + Compte + + + + + General + Général + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + Activé + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Enregistrer + + + + Change Password + + + + + Export Account + Sauvegarder Compte + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Contacts bannis + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Vidéo + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_fr_CA.ts b/translations/ring_client_windows_fr_CA.ts new file mode 100644 index 00000000..10e8ad74 --- /dev/null +++ b/translations/ring_client_windows_fr_CA.ts @@ -0,0 +1,1591 @@ + + + AboutDialog + + + + About + À propos + + + + about button + bouton à propos + + + + credits button + bouton crédits + + + + Credits + Crédits + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Le client Microsoft Windows pour Jami. +Jami est un logiciel de communication sécurisé et distribué. + + + + version + version + + + + Created by: + Créé par : + + + + Artwork by: + Artwork de: + + + + Based on the SFLPhone project + Basé sur le projet SFLPhone + + + + AccountItemDelegate + + + Add Account + Ajouter un compte + + + + AdvancedSIPSettingsWidget + + + Form + Formulaire + + + + Call Settings + Paramètres d'appel + + + + Auto Answer Calls + Réponse automatique aux appels + + + + Enable Custom Ringtone + Activer une sonnerie personnalisée + + + + Connectivity + Connectivité + + + + STUN Address + + + + + Use STUN + Utiliser STUN + + + + Use UPnP + + + + + Use TURN + Utiliser le TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Média + + + + Enable Video + Activer la vidéo + + + + Video Codecs + Codecs Vidéo + + + + Audio Codecs + Codecs Audio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + Ajouter une sonnerie personnalisée + + + + AdvancedSettingsWidget + + + Form + Formulaire + + + + Call Settings + Paramètres d'appel + + + + Allow Calls From Untrusted Peers + Autoriser les appels d'une source inconnue + + + + Auto Answer Calls + Réponse automatique aux appels + + + + Enable Custom Ringtone + Activer une sonnerie personnalisée + + + + Add a custom ringtone + Ajouter une sonnerie personnalisée + + + + Name Server + Nom du serveur + + + + Address + Adresse + + + + OpenDHT Configuration + Configuration de l'OpenDHT + + + + Enable Proxy + Activer le Proxy + + + + Bootstrap + Amorcer + + + + Security + Sécurité + + + + Private Key Password + Mot de passe de la clé privée + + + + User Certificate + certificat d'utilisateur + + + + Private Key + Clé privée + + + + CA Certificate + Certificat CA + + + + Connectivity + Connectivité + + + + Use STUN + Utiliser STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Utiliser le TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Média + + + + Enable Video + Activer la vidéo + + + + Audio Codecs + Codecs Audio + + + + Video Codecs + Codecs Vidéo + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Formulaire + + + + BannedItemWidget + + + Form + Formulaire + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Conversations + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami est un logiciel libre de communication universel qui respecte la vie privée et l'intimité de ses utilisateurs. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + Bouton partager l'identificateur Ring + + + + Double-click to copy + Double-cliquez pour copier + + + + Error while generating QR Code + Une erreur s'est produite lors de la génération du code QR + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Ajouter aux contacts + + + + Show invites + Montrer les invitations + + + + Invites + Invitations + + + + + Find a new or existing contact + Trouver un nouveau contact ou un contact existant + + + + Wants to talk to you! + veut vous parler! + + + + Answer incoming call button + Répondre à l'appel entrant + + + + Ignore incoming call button + Ignorer l'appel entrant + + + + Answer + Répondre + + + + Ignore + Ignorer + + + + Cancel outgoing call + Annuler l'appel sortant + + + + Cancel + Annuler + + + + Start video call + Démarrer appel video + + + + Start audio call + Démarrer appel audio + + + + Clear conversation + Effacer la conversation + + + + Remove contact + Supprimer contact + + + + Block contact + Bloquer le contact + + + + Copy number + Copier le numéro + + + + Search your received invitations + Recherchez vos invitations reçues + + + + Contact me on Jami + Contactez-moi sur Jami + + + + My Id is : + Mon identifiant est : + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Appel entrant de %1 + + + + DeleteAccountDialog + + + Account deletion + Suppression de compte + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + Supprimer définitivement le compte + + + + Delete + Supprimer + + + + Cancel account deletion + + + + + Cancel + Annuler + + + + DeviceItemWidget + + + Form + Formulaire + + + + Device Id + + + + + this device + cet appareil + + + + InviteButtonsWidget + + + Accept + Accepter + + + + Refuse + Refuser + + + + Block + Bloquer + + + + LinkDeviceDialog + + + Dialog + Dialogue + + + + Enter your account password + + + + + Password + Mot de passe + + + + Ok + Ok + + + + Cancel + Annuler + + + + Exporting account + + + + + Your PIN is + + + + + PIN + NIP + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Fermer + + + + Link Another Device + Connecter un autre appareil + + + + Incorrect password + Mot de passe incorrect + + + + Something went wrong. +Please try again later. + Une erreur s'est produite. +Veuillez réessayer plus tard. + + + + MainWindow + + + Settings + Réglages + + + + Exit + Quitter + + + + About + À propos + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + Entrée du mot de passe + + + + Password text entry + + + + + Password + Mot de passe + + + + Register + Inscription + + + + Cancel + Annuler + + + + Registering Name + + + + + Something went wrong + Une erreur s'est produite + + + + Close + Fermer + + + + Incorrect password + Mot de passe incorrect + + + + Network error + Erreur de réseau + + + + NewWizardWidget + + + Form + Formulaire + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Créer un compte Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Associer cet appareil à un compte + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + Créer un compte SIP + + + + Link this device to an existing account + Associer cet appareil avec un compte existant + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Entrer votre NIP : + + + + Or import a file: + Ou importer une archive de compte Ring : + + + + Link from exported account archive file + + + + + + + (None) + (aucun) + + + + Password: + Mot de passe: + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Compte + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Enregistrer un nom d'utilisateur public + + + + Public username edit + Modification du nom d'utilisateur public + + + + Choose your username + Choisissez votre nom d'utilisateur + + + + Password text input + Entrée du mot de passe + + + + Password text entry + + + + + + Password + Mot de passe + + + + + Password confirmation text input + Mot de confirmation de saisie de texte + + + + Confirm password + Confirmer le mot de passe + + + + SIP Account + Compte SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + Entrée du username SIP + + + + SIP Password text entry + + + + + Username + Nom d'utilisateur + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + Bouton page précédente + + + + push button to access previous page of wizard + + + + + Previous + Précédent + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Précédent + + + + Next page Button + Bouton page suivante + + + + Push button to access next page of wizard + Appuyer sur le bouton pour accéder à la prochaine page de l'assistant + + + + Next + Suivant + + + + Open File + Ouvrir un fichier + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + Entrer un nouveau mot de passe + + + + Confirm New Password + Confirmer votre mot de passe + + + + Confirm + + + + + Cancel + Annuler + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + Formulaire + + + + Photobooth display + + + + + Choose File + Choisir le fichier + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Choisir le fichier + + + + Files (*) + Fichiers (*) + + + + QObject + + + No default mail client found + Aucun client de courriel par défaut n'a été trouvé + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + Formulaire + + + + Settings + Réglages + + + + Account + Compte + + + + + General + Général + + + + + Audio / Video + + + + + System + Système + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + Vérifier les mises à nouveaux maintenant + + + + Password + Mot de passe + + + + + Enable + Activé + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + Identité + + + + Id + + + + + Registered name + Nom enregistré + + + + Type here to register a username + + + + + Register + Inscription + + + + Change Password + + + + + Export Account + Sauvegarder Compte + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + Connecter un autre appareil + + + + Banned Contacts + Contacts bloqués + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + Compte SIP + + + + Username + Nom d'utilisateur + + + + Hostname + Nom de domaine + + + + Proxy + Proxy + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + Périphérique de sortie + + + + Choose the output device + + + + + Video + Vidéo + + + + Device + Appareil + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Appel en attente + + + + Hold / Unhold + Mettre en attente / Reprendre l'appel + + + + Chat + Clavardage + + + + Mute Mic + Couper le microphone + + + + Record call + + + + + Name label + Nom de l'étiquette + + + + Time elapsed + + + + + 00:00 + 00:00 + + + + Hangup + Raccrocher + + + + Mute Video + Couper le son de la vidéo + + + + VideoView + + + Share entire screen + Partager la totalité de l'écran + + + + Share screen area + Partager une partie de l'écran + + + + Share file + Partager le fichier + + + \ No newline at end of file diff --git a/translations/ring_client_windows_fr_CH.ts b/translations/ring_client_windows_fr_CH.ts new file mode 100644 index 00000000..b556cef8 --- /dev/null +++ b/translations/ring_client_windows_fr_CH.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Connectivité + + + + STUN Address + + + + + Use STUN + Utiliser STUN + + + + Use UPnP + + + + + Use TURN + Utiliser le TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adresse + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Amorcer + + + + Security + Sécurité + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Connectivité + + + + Use STUN + Utiliser STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Utiliser le TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Conversations + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami est un logiciel libre de communication universel qui respecte la vie privée et l'intimité de ses utilisateurs. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Ajouter aux contacts + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + Annuler + + + + Start video call + Démarrer appel video + + + + Start audio call + Démarrer appel audio + + + + Clear conversation + Effacer la conversation + + + + Remove contact + + + + + Block contact + Bloquer le contact + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + Annuler + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Accepter + + + + Refuse + Refuser + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + OK + + + + Cancel + Annuler + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + + + + + Exit + + + + + About + + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + Enregistrer + + + + Cancel + Annuler + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Créer un compte Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Lier cet appareil à un compte + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Mot de passe : + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Compte + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Enregistrer un nom d'utilisateur public + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Suivant + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Annuler + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + + + + + Account + Compte + + + + + General + Général + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + Activé + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Enregistrer + + + + Change Password + + + + + Export Account + Sauvegarder Compte + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Contacts bloqués + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Vidéo + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_fr_FR.ts b/translations/ring_client_windows_fr_FR.ts new file mode 100644 index 00000000..c8aa9539 --- /dev/null +++ b/translations/ring_client_windows_fr_FR.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + A propos + + + + about button + à propos du bouton + + + + credits button + + + + + Credits + Crédits + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + version + + + + Created by: + créer par + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Connectivité + + + + STUN Address + + + + + Use STUN + Utiliser STUN + + + + Use UPnP + + + + + Use TURN + Utiliser le TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adresse + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Amorcer + + + + Security + securité + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Connectivité + + + + Use STUN + Utiliser STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Utiliser le TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Conversation + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami est un logiciel libre de communication universel qui respecte la vie privée et l'intimité de ses utilisateurs. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Ajouter dans les contacts + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Répondre + + + + Ignore + Ignorer + + + + Cancel outgoing call + + + + + Cancel + Annuler + + + + Start video call + Démarrer appel video + + + + Start audio call + Démarrer appel audio + + + + Clear conversation + Effacer la conversation + + + + Remove contact + + + + + Block contact + Bloquer le contact + + + + Copy number + Copier le numéro de téléphone + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Supprimer + + + + Cancel account deletion + + + + + Cancel + Annuler + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Accepter + + + + Refuse + Refuser + + + + Block + Bloquer + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + mot de passe + + + + Ok + d'accord + + + + Cancel + Annuler + + + + Exporting account + + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Réglages + + + + Exit + + + + + About + A propos + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + mot de passe + + + + Register + Enregistrer + + + + Cancel + Annuler + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Créer un compte Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Lier cet appareil à un compte + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Mot de passe : + + + + + Profile + Profil + + + + + Profile name + + + + + Account + compte + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Enregistrer un nom d'utilisateur public + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + mot de passe + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + Compte SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + nom d'utilisateur + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Retour + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Suivant + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Annuler + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + choisir un fichier + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + choisir un fichier + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Réglages + + + + Account + compte + + + + + General + Général + + + + + Audio / Video + + + + + System + Système + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + mot de passe + + + + + Enable + Activé + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Enregistrer + + + + Change Password + + + + + Export Account + Sauvegarder Compte + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Contacts bloqués + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + Compte SIP + + + + Username + nom d'utilisateur + + + + Hostname + nom d'hôte + + + + Proxy + + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Vidéo + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + Vidéo muette + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + Partager le fichier + + + \ No newline at end of file diff --git a/translations/ring_client_windows_gl.ts b/translations/ring_client_windows_gl.ts new file mode 100644 index 00000000..12a7bf8f --- /dev/null +++ b/translations/ring_client_windows_gl.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + Cŕedito + + + + Free as in Freedom + Libre como en liberdade + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Conectividade + + + + STUN Address + + + + + Use STUN + Usar STUN + + + + Use UPnP + + + + + Use TURN + Usar TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Multimedia + + + + Enable Video + Activar vídeo + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Enderezo + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Bootstrap + + + + Security + Seguridade + + + + Private Key Password + Contrasinal da chave privada + + + + User Certificate + Certificado de usuaria + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Conectividade + + + + Use STUN + Usar STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Usar TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Multimedia + + + + Enable Video + Activar vídeo + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + Mostrar conversas + + + + Conversations + Conversas + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami é software libre para comunicacións universais e que respecta as liberdades e privacidade das súas usuarias. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Engadir a contactos + + + + Show invites + Mostrar convites + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + Cancelar + + + + Start video call + Iniciar chamada de vídeo + + + + Start audio call + Iniciar chamada de audio + + + + Clear conversation + Limpar conversa + + + + Remove contact + + + + + Block contact + Bloquear contacto + + + + Copy number + Copiar número + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Eliminar + + + + Cancel account deletion + + + + + Cancel + Cancelar + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + este dispositivo + + + + InviteButtonsWidget + + + Accept + Aceptar + + + + Refuse + Rexeitar + + + + Block + Bloquar + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Contrasinal + + + + Ok + Ok + + + + Cancel + Cancelar + + + + Exporting account + + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Axuste + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Contrasinal + + + + Register + Rexistro + + + + Cancel + Cancelar + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + Fallo na rede + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Crear conta Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Ligar este dispositivo a unha conta + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Contrasinal: + + + + + Profile + Perfil + + + + + Profile name + + + + + Account + Conta + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Rexistrar o nome público de usuaria + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Contrasinal + + + + + Password confirmation text input + + + + + Confirm password + Confirmar contrasinal + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Nome de usuaria + + + + + SIP Password text input + + + + + Generating your Jami account… + Estase a xerar a súa conta Jami... + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Anterior + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Atrás + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Seguinte + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Cancelar + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Axuste + + + + Account + Conta + + + + + General + Xeral + + + + + Audio / Video + + + + + System + Sistema + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + Cartafol para descargas + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Contrasinal + + + + + Enable + Activar + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Perfil + + + + + Identity + Identidade + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Rexistro + + + + Change Password + Cambiar contrasinal + + + + Export Account + Exportar conta + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Contactos excluídos + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Nome de usuaria + + + + Hostname + Servidor + + + + Proxy + Proxy + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Vídeo + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + Conversa + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + 00:00 + + + + Hangup + + + + + Mute Video + Acalar vídeo + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + Compartir ficheiro + + + \ No newline at end of file diff --git a/translations/ring_client_windows_he.ts b/translations/ring_client_windows_he.ts new file mode 100644 index 00000000..b4596283 --- /dev/null +++ b/translations/ring_client_windows_he.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + אודות + + + + about button + + + + + credits button + + + + + Credits + תודות + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + גירסה + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + מבוסס על הפרויקט SFLPhone + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + מדיה + + + + Enable Video + אפשר וידאו + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + כתובת + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + אבטחה + + + + Private Key Password + סיסמת מפתח פרטי + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + מדיה + + + + Enable Video + אפשר וידאו + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + דיונים + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + ביטול + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + העתק מספר + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + מחק + + + + Cancel account deletion + + + + + Cancel + ביטול + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + קבל + + + + Refuse + סרב + + + + Block + חסום + + + + LinkDeviceDialog + + + Dialog + דו שיח + + + + Enter your account password + + + + + Password + סיסמה + + + + Ok + + + + + Cancel + ביטול + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + הגדרות + + + + Exit + + + + + About + אודות + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + סיסמה + + + + Register + הירשם + + + + Cancel + ביטול + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + פרופיל + + + + + Profile name + + + + + Account + חשבון + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + סיסמה + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + חשבון SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + פרוקסי + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + שם משתמש + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + קדימה + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + ביטול + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + בחר קובץ + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + בחר קובץ + + + + Files (*) + קבצים (*) + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + הגדרות + + + + Account + חשבון + + + + + General + כלליות + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + סיסמה + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + פרופיל + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + הירשם + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + חשבון SIP + + + + Username + שם משתמש + + + + Hostname + שם מארח + + + + Proxy + פרוקסי + + + + Audio + אודיו + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + וידאו + + + + Device + מכשיר + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + שיחה + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + נתק + + + + Mute Video + כבה וידאו + + + + VideoView + + + Share entire screen + + + + + Share screen area + שתף אזור מסך + + + + Share file + שתף קובץ + + + \ No newline at end of file diff --git a/translations/ring_client_windows_hi_IN.ts b/translations/ring_client_windows_hi_IN.ts new file mode 100644 index 00000000..dab0d6f4 --- /dev/null +++ b/translations/ring_client_windows_hi_IN.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + + + + + Cancel + + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + + + + + Cancel + + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + खाता + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + + + + + Account + खाता + + + + + General + + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + आॅडियो + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + वीडियो + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_hr.ts b/translations/ring_client_windows_hr.ts new file mode 100644 index 00000000..8aa78ba0 --- /dev/null +++ b/translations/ring_client_windows_hr.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + O programu + + + + about button + + + + + credits button + + + + + Credits + Zasluge + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + verzija + + + + Created by: + Napravio: + + + + Artwork by: + + + + + Based on the SFLPhone project + Bazirano na SFLPhone projektu + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Spojivost + + + + STUN Address + + + + + Use STUN + Koristi STUN + + + + Use UPnP + + + + + Use TURN + Koristi TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Medij + + + + Enable Video + Omogući video + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adresa + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + Sigurnost + + + + Private Key Password + Lozinka privatnog ključa + + + + User Certificate + Korisnički certifikat + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Spojivost + + + + Use STUN + Koristi STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Koristi TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Medij + + + + Enable Video + Omogući video + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Razgovori + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami je slobodni software za univerzalnu komunikaciju koji poštuje slobode i privatnost svojih korisnika. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + Greška prilikom generiranja QR koda + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Dodaj u kontakte + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Želi govoriti sa vama! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Javi se + + + + Ignore + Ignoriraj + + + + Cancel outgoing call + + + + + Cancel + Poništi + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + Blokiraj kontakt + + + + Copy number + Kopiraj broj + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Dolazni poziv od %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Izbriši + + + + Cancel account deletion + + + + + Cancel + Poništi + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Prihvati + + + + Refuse + Odbij + + + + Block + Blokiraj + + + + LinkDeviceDialog + + + Dialog + Dijalog + + + + Enter your account password + + + + + Password + Lozinka + + + + Ok + U redu + + + + Cancel + Poništi + + + + Exporting account + izvozim račun + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Postavke + + + + Exit + Izađi + + + + About + O programu + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Lozinka + + + + Register + Registriraj + + + + Cancel + Poništi + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + Greška mreže + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Napravite Jami račun + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Povežite ovaj uređaj s računom + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Lozinka: + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Račun + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + Izaberite vaše korisničko ime + + + + Password text input + + + + + Password text entry + + + + + + Password + Lozinka + + + + + Password confirmation text input + + + + + Confirm password + Potvrdi lozinku + + + + SIP Account + SIP račun + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Korisničko ime + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Natrag + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Slijedeće + + + + Open File + Otvori datoteku + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Poništi + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + Izaberite datoteku + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Izaberite datoteku + + + + Files (*) + Datoteke (*) + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Postavke + + + + Account + Račun + + + + + General + Opće + + + + + Audio / Video + + + + + System + Sustav + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + Spremi u + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Lozinka + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Registriraj + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + SIP račun + + + + Username + Korisničko ime + + + + Hostname + Ime poslužitelja + + + + Proxy + Proxy + + + + Audio + Zvuk + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + Izlazni uređaj + + + + Choose the output device + + + + + Video + Video + + + + Device + Uređaj + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + Čavrljanje + + + + Mute Mic + Isključi mikrofon + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + 00:00 + + + + Hangup + + + + + Mute Video + Ugasi video + + + + VideoView + + + Share entire screen + + + + + Share screen area + Dijelite područje zaslona + + + + Share file + Dijeli datoteku + + + \ No newline at end of file diff --git a/translations/ring_client_windows_hu.ts b/translations/ring_client_windows_hu.ts new file mode 100644 index 00000000..6774d349 --- /dev/null +++ b/translations/ring_client_windows_hu.ts @@ -0,0 +1,1604 @@ + + + AboutDialog + + + + About + Névjegy + + + + about button + Névjegye gombja + + + + credits button + Közreműködők gombja + + + + Credits + Készítők + + + + Free as in Freedom + Szabad, mint a szabadság + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + A Microsoft Windows ügyfél a Jamihoz. +A Jami egy biztonságos és osztott csevegőprogram. + + + + version + verzió + + + + Created by: + Szerző: + + + + Artwork by: + Művészi elemek készítője: + + + + Based on the SFLPhone project + Az SFLPhone termék alapján + + + + AccountItemDelegate + + + Add Account + Fiók hozzáadása + + + + AdvancedSIPSettingsWidget + + + Form + Feladó + + + + Call Settings + Hívás beállítások + + + + Auto Answer Calls + Hívások önműködő fogadása + + + + Enable Custom Ringtone + Egyéni csengőhang engedélyezése + + + + Connectivity + Kapcsolódás + + + + STUN Address + STUN cím + + + + Use STUN + STUN használata + + + + Use UPnP + UPnP használata + + + + Use TURN + TURN használata + + + + TURN Password + TURN jelszó + + + + TURN Username + TURN felhasználónév + + + + TURN Address + TURN cím + + + + Media + Média + + + + Enable Video + Videó engedélyezése + + + + Video Codecs + Videókodekek + + + + Audio Codecs + Hangkodekek + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Hangfájlok (*aiff *.mp3 *.ogg *.opus *.wav *wma) + + + + Add a custom ringtone + Egyéni csengőhang hozzáadása + + + + AdvancedSettingsWidget + + + Form + Feladó + + + + Call Settings + Hívás beállítások + + + + Allow Calls From Untrusted Peers + Hívások engedélyezése ismeretlen kapcsolatoktól + + + + Auto Answer Calls + Hívások önműködő fogadása + + + + Enable Custom Ringtone + Egyéni csengőhang engedélyezése + + + + Add a custom ringtone + Egyéni csengőhang hozzáadása + + + + Name Server + Kiszolgálói néve + + + + Address + Cím + + + + OpenDHT Configuration + OpenDHT beállítások + + + + Enable Proxy + Meghatalmazott engedélyezése + + + + Bootstrap + Bootstrap + + + + Security + Biztonság + + + + Private Key Password + Személyes kulcs jelszava + + + + User Certificate + Felhasználói tanúsítvány + + + + Private Key + Személyes kulcs + + + + CA Certificate + CA tanúsítvány + + + + Connectivity + Kapcsolódás + + + + Use STUN + STUN használata + + + + STUN Address + STUN cím + + + + Use UPnP + UPnP használata + + + + Use TURN + TURN használata + + + + TURN Password + TURN jelszó + + + + TURN Username + TURN felhasználónév + + + + TURN Address + TURN cím + + + + Media + Média + + + + Enable Video + Videó engedélyezése + + + + Audio Codecs + Hangkodekek + + + + Video Codecs + Videókodekek + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Hangfájlok (*aiff *.mp3 *.ogg *.opus *.wav *wma) + + + + AnimatedOverlay + + + Form + Feladó + + + + BannedItemWidget + + + Form + Feladó + + + + name + név + + + + id + Azonosító + + + + CallWidget + + + Show conversations + Beszélgetések megjelenítése + + + + Conversations + Beszélgetések + + + + Search contact text input + Keresés kapcsolati szövegbevitel + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + A Jami (GNU csomag, korábban Ring) egy univerzális és elosztott közlés emelvény, amely tiszteletben tartja a felhasználók szabadságait és magánéletét. + + + + + This is your ID. +Copy and share it with your friends! + + +Ez az Ön azonosítója. +Másolja és ossza meg a barátaival! + + + + Show ring ID QR code + Jami azonosító QR-kód mutatása + + + + Share ring ID button + Ring azonosító megosztó gomb + + + + Double-click to copy + Kattintson duplán a másolásra + + + + Error while generating QR Code + Hiba a QR-kód generálása közben + + + + + best name + legjobb név + + + + best Id + legjobb azonosító + + + + Back to homepage button + Vissza a főoldalra gomb + + + + Add to contacts + Hozzáadás a kapcsolatokhoz + + + + Show invites + Meghívók megjelenítése + + + + Invites + Meghívók + + + + + Find a new or existing contact + Keressen új vagy meglévő kapcsolatot + + + + Wants to talk to you! + Beszélni akar önnel! + + + + Answer incoming call button + Bejövő hívás fogadása gomb + + + + Ignore incoming call button + Bejövő hívás figyelmen kívül hagyása gomb + + + + Answer + Válaszol + + + + Ignore + Figyelmen kívül hagy + + + + Cancel outgoing call + Kimenő hívás visszavonása + + + + Cancel + Mégse + + + + Start video call + Videóhívás indítása + + + + Start audio call + Hanghívás indítása + + + + Clear conversation + Beszélgetés törlése + + + + Remove contact + Kapcsolat eltávolítása + + + + Block contact + Kapcsolat letiltása + + + + Copy number + Szám másolása + + + + Search your received invitations + Megérkezett meghívások keresése + + + + Contact me on Jami + Lépjen kapcsolatba velem a Jami-n + + + + My Id is : + Az azonosítóm: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Bejövő hívás tőle: %1 + + + + DeleteAccountDialog + + + Account deletion + Fiók eltávolítása + + + + Do you really want to delete the following account? + Tényleg törli ezt az fiókot? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Ha ezt a fiókot nem exportálták vagy hozzáadták egy másik eszközhöz, akkor ez el fog veszni. + + + + Permanently delete account + Fiók végleges törlése + + + + Delete + Törlés + + + + Cancel account deletion + Fiók eltávolítás megszakítása + + + + Cancel + Mé_gse + + + + DeviceItemWidget + + + Form + Feladó + + + + Device Id + Eszközazonosító + + + + this device + ez az eszköz + + + + InviteButtonsWidget + + + Accept + Elfogadás + + + + Refuse + Elutasítás + + + + Block + Letiltás + + + + LinkDeviceDialog + + + Dialog + Párbeszédpanel + + + + Enter your account password + Adja meg fiókjának jelszavát + + + + Password + Jelszó + + + + Ok + Rendben + + + + Cancel + Mégse + + + + Exporting account + Fiók exportálása + + + + Your PIN is + A PIN-kódja + + + + PIN + PIN-kód + + + + This pin and the account password should be entered in your device within 10 minutes. + Ezt a PIN-kódot és a fiók jelszót 10 percen belül be kell írni a eszközbe. + + + + Close + Bezárás + + + + Link Another Device + Csatlakoztasson másik eszközt + + + + Incorrect password + Hibás jelszó + + + + Something went wrong. +Please try again later. + Valami hiba történt. +Próbálja meg újra később. + + + + MainWindow + + + Settings + Beállítások + + + + Exit + Kilépés + + + + About + Névjegy + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Bejegyzett név beállítása + + + + Enter your account password + Adja meg fiókjának jelszavát + + + + Password text input + Jelszó szövegbevitel + + + + Password text entry + Jelszó beírása + + + + Password + Jelszó + + + + Register + Bejegyez + + + + Cancel + Mégse + + + + Registering Name + Név bejegyzése + + + + Something went wrong + Valami hiba történt + + + + Close + Bezárás + + + + Incorrect password + Hibás jelszó + + + + Network error + Hálózati hiba + + + + NewWizardWidget + + + Form + Feladó + + + + Welcome Label + Üdvözlő címke + + + + Welcome to + Isten hozott + + + + Welcome Logo + Üdvözlő jelkép + + + + Create Jami account button + Jami fiók létrehozása gomb + + + + Push button for Jami account creation start trigger + Nyomógomb a Jami fiók létrehozás indításának aktivizálásához + + + + Create a Jami account + Jami-fiók létrehozása + + + + Link device button + Összekapcsoló eszköz gombja + + + + Push button for device linkage start trigger + Nyomógomb a készülék összekapcsolásának indítási triggerjére + + + + Link this device to an account + Ezen eszköz összekapcsolása egy fiókhoz + + + + Create Jami SIP account button + Jami SIP-fiók létrehozása gomb + + + + Push button for Jami SIP account creation start trigger + Nyomógomb a Jami-/SIP-fiók létrehozás indításának aktivizálásához + + + + Create a SIP account + SIP fiók létrehozása + + + + Link this device to an existing account + Ezen eszköz egy létező fiókhoz kapcsolása + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Először az eszköz összekapcsolása egy másik fiókkal </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">meg kell szereznie a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">-kódot. A PIN kód létrehozása:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Menjen a </span><span style=" font-size:14px; font-weight:600;">Fiókkezelés beállítása</span><span style=" font-size:14px;"> egy korábbi eszközről</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Válassza a </span><span style=" font-size:14px; font-weight:600;">Jami fiók</span><span style=" font-size:14px;"> szeretné használni</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Menjen a </span><span style=" font-size:14px; font-weight:600;">Eszközök</span><span style=" font-size:14px;"> lap</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Válassza </span><span style=" font-size:14px; font-weight:600;">Eszköz hozzáadása</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Kapja meg a szükséges PIN-kódot az űrlap kitöltéséhez. A PIN-kód csak érvényes </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 percig</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Adja meg a PIN-kódját: + + + + Or import a file: + Vagy egy fájl importálása: + + + + Link from exported account archive file + Exportált fiók összekapcsolása archív fájlból + + + + + + (None) + (Nincs) + + + + Password: + Jelszó: + + + + + Profile + Profil + + + + + Profile name + Profilnév + + + + Account + Fiók + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Bejegyeztetés a felhasználónevét. +Ez fenntartja a felhasználónevet, hogy csak Ön tudja használni. +Ismerősei képesek lesznek felhívni a felhasználónevét az azonosító használata helyett. + + + + Public username checkbox + Nyilvános felhasználónév jelölőnégyzet + + + + Checkbox selecting if the user wants a public username + Válassza ki a jelölőnégyzetet, ha a felhasználó nyilvános felhasználónevet szeretne + + + + Register public username + Nyilvános felhasználónév bejegyeztetése + + + + Public username edit + Nyilvános felhasználónév szerkesztés + + + + Choose your username + Válasszon egy felhasználónevet + + + + Password text input + Jelszó szövegbevitel + + + + Password text entry + Jelszó beírása + + + + + Password + Jelszó + + + + + Password confirmation text input + Jelszó megerősítési szövegbevitel + + + + Confirm password + Jelszó megerősítése + + + + SIP Account + SIP fiók + + + + SIP Server edit + SIP-kiszolgáló szerkesztése + + + + Server + Kiszolgáló + + + + SIP proxy input + SIP-meghatalmazott bevitel + + + + SIP proxy text entry + SIP-meghatalmazott szövegbevitel + + + + Proxy + Meghatalmazott + + + + SIP username input + SIP felhasználónév bevitel + + + + SIP Password text entry + SIP jelszó szövegbevitel + + + + Username + Felhasználónév + + + + + SIP Password text input + SIP jelszó szöveg bemenet + + + + Generating your Jami account… + Jami fiók létrehozása… + + + + Previous page button + Előző oldal gombja + + + + push button to access previous page of wizard + nyomógomb az előző oldal hozzáférésének varázslója + + + + Previous + Vissza + + + + Cancel account create/link + Fiók létrehozása-/összekapcsolása megszakítása + + + + push button to cancel account creation or linking + nyomja meg a gombot a fiók létrehozása-/összekapcsolása megszakításához + + + + Back + Vissza + + + + Next page Button + Következő oldal gombja + + + + Push button to access next page of wizard + Nyomógomb a következő oldal hozzáférésének varázslója + + + + Next + Tovább + + + + Open File + Fájl megnyitása + + + + Jami archive files (*.gz); All files (*) + Jami archív fájlok (*.gz); Minden fájl (*) + + + + Your account needs to be migrated. Enter your password. + Fiók áttelepítés szükséges. Adja meg a jelszavát. + + + + Migrating your Jami account... + Jami-fiók áttelepítése… + + + + Importing account archive... + Fiók archívum importálása… + + + + Generating your Jami account... + Jami-fiók létrehozása… + + + + Generating your SIP account... + A SIP-fiók létrehozása… + + + + Error creating account + Hiba a fiók létrehozása során + + + + PasswordDialog + + + Change Account Password + Fiók jelszóváltoztatása + + + + Enter Current Password + Adja meg a jelenlegi jelszót + + + + Enter New Password + Új jelszó megadása + + + + Confirm New Password + Új jelszó megerősítése + + + + Confirm + Megerősítés + + + + Cancel + Mégse + + + + Current Password Incorrect + Hibás jelenlegi jelszót + + + + PhotoBoothDialog + + + Photobooth + Fénykép automata + + + + PhotoboothWidget + + + Form + Feladó + + + + Photobooth display + Fénykép automata kijelző + + + + Choose File + Fájl kiválasztása + + + + Image Files (*.jpg *.jpeg *.png) + Képfájlok (*.jpeg *.jpg *.png) + + + + PrivateBridging + + + Choose File + Fájl kiválasztása + + + + Files (*) + Fájlok (*) + + + + QObject + + + No default mail client found + Nem találtam alapértelmezett levelezőügyfélt + + + + + Edit Device Name + Eszköznév szerkesztése + + + + Unlink Device From Account + Ezen eszköz fiókhoz kikapcsolása + + + + Save new device name + Új eszköz nevének mentése + + + + Add as contact + Hozzáadás a partnerekhez + + + + RingButton + + + Select folder + Mappa kijelölése + + + + SettingsWidget + + + Form + Feladó + + + + Settings + Beállítások + + + + Account + Fiók + + + + + General + Általános + + + + + Audio / Video + Hang/Videó + + + + System + Rendszer + + + + Enable desktop notifications + Az asztali értesítések engedélyezése + + + + Keep minimized on close + Bezáráskor legyen minimalizálva + + + + Download folder + Letöltés mappa + + + + Save in + Mentés helye + + + + Always Recording + Mindig legyen felvétel + + + + Updates + Frissítések + + + + Check for updates automatically every + Frissítések keresése minden + + + + Interval between update checks in days selector + Időközönkénti frissítések ellenőrzése napi választó + + + + days + nap + + + + Check for updates now button + Frissítések ellenőrzése most gomb + + + + Check for updates now + Frissítések ellenőrzése most + + + + Password + Jelszó + + + + + Enable + Engedélyezés + + + + toggle enable notifications + Értesítések engedélyezése be-/kikapcsolása + + + + Toggle keep minimized on close + Bezáráskor minimalizálás be-/kikapcsolása + + + + Call Recordings + Hívás felvételek + + + + Toggle automatic updates + Önműködő frissítések engedélyezése be-/kikapcsolása + + + + Jami Account + Jami fiók + + + + + Profile + Profil + + + + + Identity + Személyazonosság + + + + Id + Azonosító + + + + Registered name + Bejegyzett név + + + + Type here to register a username + Adja meg a felhasználónév bejegyeztetése ide + + + + Register + Bejegyez + + + + Change Password + Jelszó megváltoztatása + + + + Export Account + Fiók exportálása + + + + + Delete Account + Fiók törlése + + + + Linked Devices + Összekapcsolt eszközök + + + + Link Another Device + Csatlakoztasson másik eszközt + + + + Banned Contacts + Letiltott kapcsolatok + + + + Format + Formátum + + + + Video device framerate selector + Videó eszköz képkocka sebességválasztó + + + + Preview unavailable + Előnézet nem érhető el + + + + + Advanced Account Settings + Haladó fiókbeállítások + + + + SIP Account + SIP fiók + + + + Username + Felhasználónév + + + + Hostname + Kiszolgálónév + + + + Proxy + Meghatalmazott + + + + Audio + Hang + + + + Microphone + Mikrofon + + + + Audio input device selector + Audió bemeneti eszköz választó + + + + Output Device + Kimeneti eszköz + + + + Choose the output device + Válasszon ki egy kimeneti eszközt + + + + Video + Videó + + + + Device + Eszköz + + + + Video device selector + Videó eszköz választó + + + + A registered name should not have any spaces and must be at least three letters long + A bejegyzett névnek nem szabad szóközt lennie, és legalább három betűnek kell lennie + + + + This name is already taken + A név már foglalt + + + + + Enter an alias + Adjon meg egy álnevet + + + + Register this name + Bejegyeztetés ezt a nevet + + + + Remove Device + Eszköz eltávolítása + + + + Enter this account's password to confirm the removal of this device + Adja meg a fiók jelszót ehhez eszköz eltávolításának megerősítéséhez + + + + Are you sure you wish to remove this device? + Biztosan eltávolítja ezt az eszközt? + + + + Export Account Here + Fiók exportálása ide + + + + Select A Folder For Your Downloads + Letöltés célmappájának választása + + + + Select A Folder For Your Recordings + Felvétel célmappájának választása + + + + VideoOverlay + + + Call on Hold + Hívástartás + + + + Hold / Unhold + Tartás/Tartás megszüntetése + + + + Chat + Csevegés + + + + Mute Mic + Mikrofon elnémítása + + + + Record call + Hívás felvétele + + + + Name label + Névcímke + + + + Time elapsed + Eltelt idő + + + + 00:00 + 00:00 + + + + Hangup + Hívás befejezése + + + + Mute Video + Videó némítása + + + + VideoView + + + Share entire screen + Az egész képernyő megosztása + + + + Share screen area + Képernyő részének megosztása + + + + Share file + Fájl megosztása + + + \ No newline at end of file diff --git a/translations/ring_client_windows_id.ts b/translations/ring_client_windows_id.ts new file mode 100644 index 00000000..ef7932f0 --- /dev/null +++ b/translations/ring_client_windows_id.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + Partisipan + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami adalah perangkat lunak gratis untuk komunikasi universal yang menghormati kebebasan dan privasi penggunanya + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Tambah sebagai kontak + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + Salin nomor + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Terima + + + + Refuse + Tolak + + + + Block + Blokir + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + + + + + Cancel + + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Setelan + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + + + + + Cancel + + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Akun + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Kembali + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Selanjutnya + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Setelan + + + + Account + Akun + + + + + General + + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + Suara + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Video + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_it.ts b/translations/ring_client_windows_it.ts new file mode 100644 index 00000000..493adcc8 --- /dev/null +++ b/translations/ring_client_windows_it.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + Crediti + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + versione + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + Aggiungi Account + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Connettività + + + + STUN Address + + + + + Use STUN + Usare STUN + + + + Use UPnP + + + + + Use TURN + Usare TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Attivare Video + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + Consenti chiamate da peer non attendibili + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Indirizzo + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Innesco + + + + Security + Sicurezza + + + + Private Key Password + Password della chiave privata + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Connettività + + + + Use STUN + Usare STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Usare TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Attivare Video + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Converzazioni + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami è un software gratuito per la comunicazione universale che rispetta le libertà e la privacy dei suoi utenti. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Aggiungi contatti + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Rispondi + + + + Ignore + Ignora + + + + Cancel outgoing call + + + + + Cancel + Cancella + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + Rimuovi contatto + + + + Block contact + Blocca contatto + + + + Copy number + Copia il numerp + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Cancella + + + + Cancel account deletion + + + + + Cancel + Cancella + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + questo dispositivo + + + + InviteButtonsWidget + + + Accept + Accettare + + + + Refuse + Rifiuta + + + + Block + Blocca + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Password + + + + Ok + Ok + + + + Cancel + Cancella + + + + Exporting account + Esportazione account in corso + + + + Your PIN is + Il tuo PIN è + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Chiudi + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Impostazioni + + + + Exit + + + + + About + + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Password + + + + Register + Register + + + + Cancel + Cancella + + + + Registering Name + + + + + Something went wrong + + + + + Close + Chiudi + + + + Incorrect password + + + + + Network error + Errore di rete + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + Benvenuto in + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Crea un account Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Collega questo dispositivo a un account + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Collega questo dispositivo a un account esistente + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Inserisci il tuo pin: + + + + Or import a file: + O importa un file: + + + + Link from exported account archive file + + + + + + + (None) + (Nessuno) + + + + Password: + Password: + + + + + Profile + Profilo + + + + + Profile name + + + + + Account + Profilo + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Password + + + + + Password confirmation text input + + + + + Confirm password + Confermare password + + + + SIP Account + Account SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Nome utente + + + + + SIP Password text input + + + + + Generating your Jami account… + Generare il tuo account Jami ... + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Precedente + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Indietro + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Seguente + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Cancella + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Impostazioni + + + + Account + Profilo + + + + + General + Generale + + + + + Audio / Video + + + + + System + Sistema + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + Cartella download + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + giorni + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Password + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + Account Jami + + + + + Profile + Profilo + + + + + Identity + Identità + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Register + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + Account SIP + + + + Username + Nome utente + + + + Hostname + Nome Host + + + + Proxy + Proxy + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Video + + + + Device + Dispositivo + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + Chat + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + Riattacare + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + Condividere una porzione dello schermo + + + + Share file + Condividere file + + + \ No newline at end of file diff --git a/translations/ring_client_windows_it_IT.ts b/translations/ring_client_windows_it_IT.ts new file mode 100644 index 00000000..e0b0dcd9 --- /dev/null +++ b/translations/ring_client_windows_it_IT.ts @@ -0,0 +1,1606 @@ + + + AboutDialog + + + + About + Informazioni su + + + + about button + pulsante informazioni su + + + + credits button + pulsante crediti + + + + Credits + Crediti + + + + Free as in Freedom + Free as in Freedom + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Il client Microsoft Windows per Jami. +Jami è un software di comunicazione sicuro e distribuito. + + + + version + versione + + + + Created by: + Creato da: + + + + Artwork by: + Tema di: + + + + Based on the SFLPhone project + Basato sul progetto SFLPhone + + + + AccountItemDelegate + + + Add Account + Aggiungi account + + + + AdvancedSIPSettingsWidget + + + Form + Modulo + + + + Call Settings + Impostazioni di chiamata + + + + Auto Answer Calls + Risposta automatica alle chiamate + + + + Enable Custom Ringtone + Abilita suoneria personalizzata + + + + Connectivity + Connettività + + + + STUN Address + Indirizzo STUN + + + + Use STUN + Utilizza STUN + + + + Use UPnP + Usa UPnP + + + + Use TURN + Utilizza TURN + + + + TURN Password + TURN Password + + + + TURN Username + TURN Nome Utente + + + + TURN Address + Indirizzo TURN + + + + Media + Media + + + + Enable Video + Attiva Video + + + + Video Codecs + Codec Video + + + + Audio Codecs + Codec audio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + File audio (* .wav * .ogg * .opus * .mp3 * aiff * wma) + + + + Add a custom ringtone + Aggiungi una suoneria personalizzata + + + + AdvancedSettingsWidget + + + Form + Modulo + + + + Call Settings + Impostazioni di chiamata + + + + Allow Calls From Untrusted Peers + Consenti chiamate da peer non attendibili + + + + Auto Answer Calls + Risposta automatica alle chiamate + + + + Enable Custom Ringtone + Abilita suoneria personalizzata + + + + Add a custom ringtone + Aggiungi una suoneria personalizzata + + + + Name Server + Nome Server + + + + Address + Indirizzo + + + + OpenDHT Configuration + Configurazione OpenDHT + + + + Enable Proxy + Abilita proxy + + + + Bootstrap + Avvio + + + + Security + Sicurezza + + + + Private Key Password + Password della Chiave Privata + + + + User Certificate + Certificato Utente + + + + Private Key + Chiave privata + + + + CA Certificate + Certificato CA. + + + + Connectivity + Connettività + + + + Use STUN + Utilizza STUN + + + + STUN Address + Indirizzo STUN + + + + Use UPnP + Usa UPnP + + + + Use TURN + Utilizza TURN + + + + TURN Password + TURN Password + + + + TURN Username + TURN Nome Utente + + + + TURN Address + Indirizzo TURN + + + + Media + Media + + + + Enable Video + Attiva Video + + + + Audio Codecs + Codec audio + + + + Video Codecs + Codec Video + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + File audio (* .wav * .ogg * .opus * .mp3 * aiff * wma) + + + + AnimatedOverlay + + + Form + Modulo + + + + BannedItemWidget + + + Form + Modulo + + + + name + nome + + + + id + id + + + + CallWidget + + + Show conversations + Mostra conversazioni + + + + Conversations + Conversazioni + + + + Search contact text input + Cerca l'inserimento del testo del contatto + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami è un software gratuito per la comunicazione universale che rispetta le libertà e la privacy dei suoi utenti. + + + + + This is your ID. +Copy and share it with your friends! + + +Questo è il tuo ID. +Copia e condividi con i tuoi amici! + + + + + Show ring ID QR code + Mostra codice ID ring e QR code + + + + Share ring ID button + Condividi il pulsante ID ring + + + + Double-click to copy + Fare doppio clic per copiare + + + + Error while generating QR Code + Errore durante la generazione del Codice QR + + + + + best name + miglior nome + + + + best Id + miglior ID + + + + Back to homepage button + Torna al pulsante homepage + + + + Add to contacts + Aggiungi alla rubrica + + + + Show invites + Mostra inviti + + + + Invites + Inviti + + + + + Find a new or existing contact + Trova un contatto nuovo o esistente + + + + Wants to talk to you! + Vuole parlare con te! + + + + Answer incoming call button + Rispondi col pulsante di chiamata in arrivo + + + + Ignore incoming call button + Ignora il pulsante di chiamata in arrivo + + + + Answer + Rispondi + + + + Ignore + Ignora + + + + Cancel outgoing call + Annulla chiamata in uscita + + + + Cancel + Annulla + + + + Start video call + Inizia la video chiamata + + + + Start audio call + Avvia chiamata audio + + + + Clear conversation + Pulisci conversazione + + + + Remove contact + Rimuovi contatto + + + + Block contact + Blocca contatto + + + + Copy number + Copia numero + + + + Search your received invitations + Cerca i tuoi inviti ricevuti + + + + Contact me on Jami + Contattami su Jami + + + + My Id is : + Il mio ID è: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Chiamata in arrivo da %1 + + + + DeleteAccountDialog + + + Account deletion + Cancellazione dell'account + + + + Do you really want to delete the following account? + Vuoi veramente cancellare il seguente account? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Se questo account non è stato esportato o aggiunto a un altro dispositivo, verrà perso in modo irrevocabile. + + + + Permanently delete account + Elimina definitivamente l'account + + + + Delete + Elimina + + + + Cancel account deletion + Annulla la cancellazione dell'account + + + + Cancel + Annulla + + + + DeviceItemWidget + + + Form + Modulo + + + + Device Id + ID del dispositivo + + + + this device + questo dispositivo + + + + InviteButtonsWidget + + + Accept + Accetta + + + + Refuse + Rifiuto + + + + Block + Blocca + + + + LinkDeviceDialog + + + Dialog + Dialog + + + + Enter your account password + Inserisci la password del tuo account + + + + Password + Password + + + + Ok + Ok + + + + Cancel + Annulla + + + + Exporting account + Esportazione dell'account + + + + Your PIN is + Il tuo PIN è + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Questo pin e la password dell'account devono essere inseriti nel dispositivo entro 10 minuti. + + + + Close + Chiudi + + + + Link Another Device + Collega un altro dispositivo + + + + Incorrect password + password errata + + + + Something went wrong. +Please try again later. + Qualcosa è andato storto. +Riprova più tardi. + + + + MainWindow + + + Settings + Opzioni + + + + Exit + Esci + + + + About + Info + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Imposta il nome registrato + + + + Enter your account password + Inserisci la password del tuo account + + + + Password text input + Inserimento testo password + + + + Password text entry + Inserimento testo password + + + + Password + Password + + + + Register + Salva + + + + Cancel + Annulla + + + + Registering Name + Nome di Registrazione + + + + Something went wrong + Qualcosa è andato storto + + + + Close + Chiudi + + + + Incorrect password + password errata + + + + Network error + Errore di rete + + + + NewWizardWidget + + + Form + Modulo + + + + Welcome Label + Etichetta di Benvenuto + + + + Welcome to + Benvenuto a + + + + Welcome Logo + Logo di benvenuto + + + + Create Jami account button + Pulsante Crea account Jami + + + + Push button for Jami account creation start trigger + Pulsante per l'attivazione della creazione dell'account Jami + + + + Create a Jami account + Crea un account Jami + + + + Link device button + Pulsante Collega dispositivo + + + + Push button for device linkage start trigger + Pulsante per l'attivazione dell'avvio del collegamento del dispositivo + + + + Link this device to an account + Collega questo dispositivo ad un account + + + + Create Jami SIP account button + Crea pulsante account SIP Jami + + + + Push button for Jami SIP account creation start trigger + Pulsante per l'attivazione della creazione dell'account SIP Jami + + + + Create a SIP account + Crea un account SIP + + + + Link this device to an existing account + Collega questo dispositivo ad un account esistente + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Per collegare questo dispositivo a un altro account, per prima cosa</span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">è necessario ottenere un codice PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">. Per generare il codice PIN:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Vai su </span><span style=" font-size:14px; font-weight:600;">Impostazioni di gestione dell'account</span><span style=" font-size:14px;"> di un dispositivo precedente</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Scegli l' </span><span style=" font-size:14px; font-weight:600;">account Jami</span><span style=" font-size:14px;"> che vuoi usare</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Vai alla </span><span style=" font-size:14px; font-weight:600;">scheda</span><span style=" font-size:14px;">dispositivi</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Seleziona </span><span style=" font-size:14px; font-weight:600;">Aggiungi dispositivo</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Riceverai il PIN necessario per completare questo modulo. Il PIN è valido solo per </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minuti</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Inserire il pin: + + + + Or import a file: + O importa un file: + + + + Link from exported account archive file + Collegamento dal file di archivio dell'account esportato + + + + + + (None) + (Nessuno) + + + + Password: + Password: + + + + + Profile + Profilo + + + + + Profile name + Nome del profilo + + + + Account + Account + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Registra il tuo nome utente. +Questo riserverà il nome utente in modo che solo tu possa usarlo. +I tuoi amici potranno chiamarti con il tuo nome utente +invece di usare il tuo ID. + + + + Public username checkbox + Casella di controllo nome utente pubblico + + + + Checkbox selecting if the user wants a public username + Selezionare la casella se l'utente desidera un nome utente pubblico + + + + Register public username + Registra nome utente pubblico + + + + Public username edit + Modifica nome utente pubblico + + + + Choose your username + scegli il tuo nome utente + + + + Password text input + Inserimento testo password + + + + Password text entry + Inserimento testo password + + + + + Password + Password + + + + + Password confirmation text input + Inserimento di testo di conferma della password + + + + Confirm password + Conferma password + + + + SIP Account + Account SIP + + + + SIP Server edit + Modifica server SIP + + + + Server + Server + + + + SIP proxy input + Ingresso proxy SIP + + + + SIP proxy text entry + Immissione di testo proxy SIP + + + + Proxy + Proxy + + + + SIP username input + Input del nome utente SIP + + + + SIP Password text entry + Immissione di testo password SIP + + + + Username + Utente + + + + + SIP Password text input + Immissione di testo password SIP + + + + Generating your Jami account… + Generare il tuo account Jami ... + + + + Previous page button + Precedente pulsante della pagina + + + + push button to access previous page of wizard + pulsante per accedere alla pagina precedente della procedura guidata + + + + Previous + Precedente + + + + Cancel account create/link + Annulla creazione/collegamento account + + + + push button to cancel account creation or linking + premere il pulsante per annullare la creazione o il collegamento dell'account + + + + Back + Indietro + + + + Next page Button + Pulsante della pagina successiva + + + + Push button to access next page of wizard + Premere il pulsante per accedere alla pagina successiva della procedura guidata + + + + Next + Prossimo + + + + Open File + Apri File + + + + Jami archive files (*.gz); All files (*) + File di archivio Jami (* gz); Tutti i files (*) + + + + Your account needs to be migrated. Enter your password. + Il tuo account deve essere migrato. Inserisci la tua password. + + + + Migrating your Jami account... + Migrazione del tuo account Jami ... + + + + Importing account archive... + Importazione dell'archivio dell'account ... + + + + Generating your Jami account... + Sto' generando il tuo account Jami ... + + + + Generating your SIP account... + Generazione del tuo account SIP... + + + + Error creating account + Errore nella creazione dell'account + + + + PasswordDialog + + + Change Account Password + Cambia password account + + + + Enter Current Password + Immetti la password corrente + + + + Enter New Password + Inserire una nuova password + + + + Confirm New Password + Conferma la nuova password + + + + Confirm + Conferma + + + + Cancel + Annulla + + + + Current Password Incorrect + Password attuale errata + + + + PhotoBoothDialog + + + Photobooth + Photobooth + + + + PhotoboothWidget + + + Form + Modulo + + + + Photobooth display + Visualizzazione photobooth + + + + Choose File + Scegli un file + + + + Image Files (*.jpg *.jpeg *.png) + File immagine (* .jpg * .jpeg * .png) + + + + PrivateBridging + + + Choose File + Scegli un file + + + + Files (*) + Files (*) + + + + QObject + + + No default mail client found + Nessun client E-Mail di Default + + + + + Edit Device Name + Modifica nome dispositivo + + + + Unlink Device From Account + Scollega il dispositivo dall'account + + + + Save new device name + Salva il nuovo nome del dispositivo + + + + Add as contact + Aggiungi come contatto + + + + RingButton + + + Select folder + Seleziona cartella + + + + SettingsWidget + + + Form + Modulo + + + + Settings + Opzioni + + + + Account + Account + + + + + General + Generale + + + + + Audio / Video + Audio / Video + + + + System + Sistema + + + + Enable desktop notifications + Abilita notifiche desktop + + + + Keep minimized on close + Minimizza alla chiusura + + + + Download folder + Scarica la cartella + + + + Save in + Salva in + + + + Always Recording + Sempre in registrazione + + + + Updates + Aggiornamenti + + + + Check for updates automatically every + Controlla gli aggiornamenti automaticamente ogni + + + + Interval between update checks in days selector + Intervallo tra i controlli di aggiornamento nel selettore giorni + + + + days + giorni + + + + Check for updates now button + Controlla gli aggiornamenti ora col pulsante + + + + Check for updates now + Verifica subito gli aggiornamenti + + + + Password + Password + + + + + Enable + Abilitato + + + + toggle enable notifications + attiva/disattiva le notifiche + + + + Toggle keep minimized on close + Attiva/disattiva mantenimento minimizzato alla chiusura + + + + Call Recordings + Registrazioni delle chiamate + + + + Toggle automatic updates + Attiva/disattiva gli aggiornamenti automatici + + + + Jami Account + Jami account + + + + + Profile + Profilo + + + + + Identity + Indentità + + + + Id + Id + + + + Registered name + Nome registrato + + + + Type here to register a username + Digita qui per registrare un nome utente + + + + Register + Salva + + + + Change Password + Cambia password + + + + Export Account + Esporta account + + + + + Delete Account + Elimina account + + + + Linked Devices + Dispositivi collegati + + + + Link Another Device + Collega un altro dispositivo + + + + Banned Contacts + Contatti vietati + + + + Format + Formato + + + + Video device framerate selector + Selettore framerate dispositivo video + + + + Preview unavailable + Anteprima non disponibile + + + + + Advanced Account Settings + Impostazioni avanzate account + + + + SIP Account + Account SIP + + + + Username + Utente + + + + Hostname + Hostname + + + + Proxy + Proxy + + + + Audio + Audio + + + + Microphone + Microfono + + + + Audio input device selector + Selettore del dispositivo di ingresso audio + + + + Output Device + Dispositivo di Uscita + + + + Choose the output device + Scegli il dispositivo di output + + + + Video + Video + + + + Device + Dispositivo + + + + Video device selector + Selettore dispositivo video + + + + A registered name should not have any spaces and must be at least three letters long + Un nome registrato non deve contenere spazi e deve contenere almeno tre lettere + + + + This name is already taken + Questo nome è già stato scelto + + + + + Enter an alias + Inserisci un alias + + + + Register this name + Registra questo nome + + + + Remove Device + Rimuovi dispositivo + + + + Enter this account's password to confirm the removal of this device + Inserisci la password di questo account per confermare la rimozione di questo dispositivo + + + + Are you sure you wish to remove this device? + Sei sicuro di voler rimuovere questo dispositivo? + + + + Export Account Here + Esporta account qui + + + + Select A Folder For Your Downloads + Seleziona una cartella per i tuoi download + + + + Select A Folder For Your Recordings + Seleziona una cartella per le tue registrazioni + + + + VideoOverlay + + + Call on Hold + Chiamata in attesa... + + + + Hold / Unhold + Pausa / Riprendi + + + + Chat + Chat + + + + Mute Mic + Mutati + + + + Record call + Registra chiamata + + + + Name label + Etichetta Nome + + + + Time elapsed + Tempo trascorso + + + + 00:00 + 00:00 + + + + Hangup + Aggancia + + + + Mute Video + Muta Video + + + + VideoView + + + Share entire screen + Condividi l'intero schermo + + + + Share screen area + Condividi l'area dello schermo + + + + Share file + Condividi file + + + \ No newline at end of file diff --git a/translations/ring_client_windows_ja.ts b/translations/ring_client_windows_ja.ts new file mode 100644 index 00000000..93715fcd --- /dev/null +++ b/translations/ring_client_windows_ja.ts @@ -0,0 +1,1592 @@ + + + AboutDialog + + + + About + アプリについて + + + + about button + + + + + credits button + + + + + Credits + クレジット + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + バージョン + + + + Created by: + 作成者: + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + アカウントを追加 + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + 着信設定 + + + + Auto Answer Calls + 自動通話応答 + + + + Enable Custom Ringtone + カスタム着信音を有効にする + + + + Connectivity + 接続性 + + + + STUN Address + STUNサーバーアドレス + + + + Use STUN + STUNを使用 + + + + Use UPnP + UPnPを使用 + + + + Use TURN + TURNを使用 + + + + TURN Password + TURNのパスワード + + + + TURN Username + TURNのユーザー名 + + + + TURN Address + TURNサーバーアドレス + + + + Media + メディア + + + + Enable Video + ビデオを有効化 + + + + Video Codecs + ビデオコーデック + + + + Audio Codecs + 音声コーデック + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + 音声ファイル(*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + カスタム着信音を追加 + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + 通話設定 + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + 自動通話応答 + + + + Enable Custom Ringtone + カスタム着信音を有効にする + + + + Add a custom ringtone + カスタム着信音を追加 + + + + Name Server + ネームサーバー + + + + Address + アドレス + + + + OpenDHT Configuration + OpenDHT設定 + + + + Enable Proxy + プロキシを有効にする + + + + Bootstrap + ブートストラップ + + + + Security + セキュリティ + + + + Private Key Password + 秘密鍵のパスワード + + + + User Certificate + ユーザー証明書 + + + + Private Key + 秘密鍵 + + + + CA Certificate + CA証明書 + + + + Connectivity + 接続性 + + + + Use STUN + STUNを使用 + + + + STUN Address + STUNサーバーアドレス + + + + Use UPnP + UPnPを使用 + + + + Use TURN + TURNを使用 + + + + TURN Password + TURNのパスワード + + + + TURN Username + TURNのユーザー名 + + + + TURN Address + TURNサーバーアドレス + + + + Media + メディア + + + + Enable Video + ビデオを有効化 + + + + Audio Codecs + 音声コーデック + + + + Video Codecs + ビデオコーデック + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + 音声ファイル(*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + 名前 + + + + id + + + + + CallWidget + + + Show conversations + 会話を表示 + + + + Conversations + 会話 + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jamiは、世界中のユーザーと繋がることができるユーザーの自由とプライバシーを尊重したフリーソフトウェアです。 + + + + + This is your ID. +Copy and share it with your friends! + + +これがあなたのIDです。 +コピーして友達とシェアしましょう! + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + 連絡先に追加 + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + 新規・既存の連絡先を検索 + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + 応答 + + + + Ignore + 無視 + + + + Cancel outgoing call + 発信をキャンセル + + + + Cancel + キャンセル + + + + Start video call + ビデオ通話を開始 + + + + Start audio call + 音声通話を開始 + + + + Clear conversation + 会話履歴を削除 + + + + Remove contact + 連絡先を削除 + + + + Block contact + 連絡先をブロック + + + + Copy number + 番号をコピー + + + + Search your received invitations + 受信した招待を検索 + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + %1 から着信 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + アカウントを完全に削除 + + + + Delete + 削除 + + + + Cancel account deletion + + + + + Cancel + キャンセル + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + このデバイス + + + + InviteButtonsWidget + + + Accept + 受け入れ + + + + Refuse + 拒否 + + + + Block + ブロック + + + + LinkDeviceDialog + + + Dialog + ダイアログ + + + + Enter your account password + アカウントのパスワードを入力してください + + + + Password + パスワード + + + + Ok + OK + + + + Cancel + キャンセル + + + + Exporting account + アカウントをエクスポート中 + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + 閉じる + + + + Link Another Device + 別のデバイスをリンク + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + 設定 + + + + Exit + 終了 + + + + About + アプリについて + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + アカウントのパスワードを入力してください + + + + Password text input + + + + + Password text entry + + + + + Password + パスワード + + + + Register + 登録 + + + + Cancel + キャンセル + + + + Registering Name + + + + + Something went wrong + + + + + Close + 閉じる + + + + Incorrect password + + + + + Network error + ネットワークエラー + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Jamiアカウントを作成 + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + このデバイスをアカウントへリンク + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + SIPアカウントを作成 + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + PINを入力: + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + (なし) + + + + Password: + パスワード: + + + + + Profile + プロフィール + + + + + Profile name + プロフィール名 + + + + Account + アカウント + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + 公開ユーザー名を登録 + + + + Public username edit + + + + + Choose your username + ユーザー名を選択 + + + + Password text input + + + + + Password text entry + + + + + + Password + パスワード + + + + + Password confirmation text input + + + + + Confirm password + パスワードの再入力 + + + + SIP Account + SIPアカウント + + + + SIP Server edit + SIPサーバーを編集 + + + + Server + サーバー + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + プロキシ + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + ユーザー名 + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + 戻る + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + 戻る + + + + Next page Button + + + + + Push button to access next page of wizard + 次のページへ進むにはボタンを押してください + + + + Next + 次へ + + + + Open File + ファイルを開く + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + アカウントのアーカイブをインポート中... + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + アカウントの作成中にエラーが発生しました + + + + PasswordDialog + + + Change Account Password + パスワードの変更 + + + + Enter Current Password + 現在のパスワードを入力 + + + + Enter New Password + 新しいパスワードを入力 + + + + Confirm New Password + 新しいパスワードを再入力 + + + + Confirm + 確認 + + + + Cancel + キャンセル + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + ファイルの選択 + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + ファイルの選択 + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + 連絡先を追加 + + + + RingButton + + + Select folder + フォルダを選択 + + + + SettingsWidget + + + Form + + + + + Settings + 設定 + + + + Account + アカウント + + + + + General + 全般 + + + + + Audio / Video + 音声/ビデオ + + + + System + システム + + + + Enable desktop notifications + デスクトップ通知を有効化 + + + + Keep minimized on close + ウィンドウを閉じたら最小化して待機する + + + + Download folder + ダウンロードフォルダー + + + + Save in + 保存先 + + + + Always Recording + + + + + Updates + アップデート + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + アップデートの確認ボタン + + + + Check for updates now + 今すぐアップデートを確認 + + + + Password + パスワード + + + + + Enable + 有効化 + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + 通話の録音 + + + + Toggle automatic updates + 自動アップデートの切り替え + + + + Jami Account + Jamiアカウント + + + + + Profile + プロフィール + + + + + Identity + ID + + + + Id + ID + + + + Registered name + + + + + Type here to register a username + + + + + Register + 登録 + + + + Change Password + パスワードの変更 + + + + Export Account + アカウントのエクスポート + + + + + Delete Account + アカウントの削除 + + + + Linked Devices + リンクされたデバイス + + + + Link Another Device + 別のデバイスをリンク + + + + Banned Contacts + 禁止された連絡先 + + + + Format + フォーマット + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + アカウントの拡張設定 + + + + SIP Account + SIPアカウント + + + + Username + ユーザー名 + + + + Hostname + ホスト名 + + + + Proxy + プロキシ + + + + Audio + 音声 + + + + Microphone + マイク + + + + Audio input device selector + + + + + Output Device + 出力デバイス + + + + Choose the output device + 出力デバイスの選択 + + + + Video + 動画 + + + + Device + デバイス + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + この名前はすでに使用されています + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + デバイスを取り除いてもよろしいですか? + + + + Export Account Here + + + + + Select A Folder For Your Downloads + ダウンロードフォルダーの選択 + + + + Select A Folder For Your Recordings + 記録フォルダーの選択 + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + チャット + + + + Mute Mic + マイクをミュート + + + + Record call + 通話を録画 + + + + Name label + + + + + Time elapsed + 経過時間 + + + + 00:00 + 00:00 + + + + Hangup + 受話 + + + + Mute Video + 動画オフ + + + + VideoView + + + Share entire screen + + + + + Share screen area + 画面を共有 + + + + Share file + ファイルを共有 + + + \ No newline at end of file diff --git a/translations/ring_client_windows_ko_KR.ts b/translations/ring_client_windows_ko_KR.ts new file mode 100644 index 00000000..48195e9a --- /dev/null +++ b/translations/ring_client_windows_ko_KR.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + 버전 + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + SFLPhone 프로젝트 기반 + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + 서식 + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + STUN 사용 + + + + Use UPnP + + + + + Use TURN + TURN 사용 + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + 서식 + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + 주소 + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + 보안 + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + STUN 사용 + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + TURN 사용 + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + 서식 + + + + BannedItemWidget + + + Form + 서식 + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + 대화 + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + 더블클릭으로 복사하기 + + + + Error while generating QR Code + QR 생성 중 오류 + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + 연락처에 추가하기 + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + 당신과 이야기하고 싶어합니다! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + 응답 + + + + Ignore + 무시 + + + + Cancel outgoing call + + + + + Cancel + 취소 + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + 취소 + + + + DeviceItemWidget + + + Form + 서식 + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + 수락 + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + 암호 + + + + Ok + 확인 + + + + Cancel + 취소 + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + 설정 + + + + Exit + 종료 + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + 암호 + + + + Register + + + + + Cancel + 취소 + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + 서식 + + + + Welcome Label + + + + + Welcome to + 환영합니다 + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + 이 장치를 계정에 연결하기 + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + 프로필 + + + + + Profile name + + + + + Account + 계정 + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + 사용자명 선택 + + + + Password text input + + + + + Password text entry + + + + + + Password + 암호 + + + + + Password confirmation text input + + + + + Confirm password + 암호 확인 + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + 프록시 + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + 사용자명 + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + 이전 + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + 다음 + + + + Open File + 파일 열기 + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + 당신의 계정은 이전이 필요합니다. 암호를 입력하세요. + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + 취소 + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + 포토부스 + + + + PhotoboothWidget + + + Form + 서식 + + + + Photobooth display + + + + + Choose File + 파일 선택하기 + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + 파일 선택하기 + + + + Files (*) + 파일들 (*) + + + + QObject + + + No default mail client found + 기본 메일 클라이언트를 찾을 수 없습니다. + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + 서식 + + + + Settings + 설정 + + + + Account + 계정 + + + + + General + 일반 + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + 닫을 때 최소화 유지하기 + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + 지금 업데이트 확인하기 + + + + Password + 암호 + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + 프로필 + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + 차단한 연락처 + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + 사용자명 + + + + Hostname + 호스트명 + + + + Proxy + 프록시 + + + + Audio + + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + 출력 장치 + + + + Choose the output device + + + + + Video + 비디오 + + + + Device + 장치 + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + 대기 / 대기해제 + + + + Chat + 대화하기 + + + + Mute Mic + 마이크 끄기 + + + + Record call + + + + + Name label + + + + + Time elapsed + 경과한 시간 + + + + 00:00 + + + + + Hangup + 통화종료 + + + + Mute Video + 비디오 숨기기 + + + + VideoView + + + Share entire screen + 전체화면 공유하기 + + + + Share screen area + 화면 영역 공유하기 + + + + Share file + 파일 공유하기 + + + \ No newline at end of file diff --git a/translations/ring_client_windows_lt.ts b/translations/ring_client_windows_lt.ts new file mode 100644 index 00000000..b5eeca88 --- /dev/null +++ b/translations/ring_client_windows_lt.ts @@ -0,0 +1,1606 @@ + + + AboutDialog + + + + About + Apie + + + + about button + + + + + credits button + + + + + Credits + Padėkos + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Microsoft Windows kliento programa skirta Jami. +Jami yra apsaugota ir paskirstytoji bendravimo programinė įranga. + + + + version + versija + + + + Created by: + Sukūrė: + + + + Artwork by: + Apipavidalino: + + + + Based on the SFLPhone project + Pagrįsta SFLPhone projektu + + + + AccountItemDelegate + + + Add Account + Pridėti paskyrą + + + + AdvancedSIPSettingsWidget + + + Form + Forma + + + + Call Settings + Skambučio nustatymai + + + + Auto Answer Calls + Automatiškai atsiliepti į skambučius + + + + Enable Custom Ringtone + Įjungti tinkintą skambučio melodiją + + + + Connectivity + Jungiamumas + + + + STUN Address + STUN adresas + + + + Use STUN + Naudoti STUN + + + + Use UPnP + Naudoti UPnP + + + + Use TURN + Naudoti TURN + + + + TURN Password + TURN slaptažodis + + + + TURN Username + TURN naudotojo vardas + + + + TURN Address + TURN adresas + + + + Media + Medija + + + + Enable Video + Įjungti vaizdą + + + + Video Codecs + Vaizdo kodekai + + + + Audio Codecs + Garso kodekai + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Garso failai (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Pridėti tinkintą skambučio melodiją + + + + AdvancedSettingsWidget + + + Form + Forma + + + + Call Settings + Skambučio nustatymai + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + Automatiškai atsiliepti į skambučius + + + + Enable Custom Ringtone + Įjungti tinkintą skambučio melodiją + + + + Add a custom ringtone + Pridėti tinkintą skambučio melodiją + + + + Name Server + Vardų serveris + + + + Address + Adresas + + + + OpenDHT Configuration + OpenDHT konfigūracija + + + + Enable Proxy + Įjungti įgaliotąjį serverį + + + + Bootstrap + Pradinė įkeltis + + + + Security + Saugumas + + + + Private Key Password + Privačiojo rakto slaptažodis + + + + User Certificate + Naudotojo liudijimas + + + + Private Key + Privatusis raktas + + + + CA Certificate + LĮ liudijimas + + + + Connectivity + Jungiamumas + + + + Use STUN + Naudoti STUN + + + + STUN Address + STUN adresas + + + + Use UPnP + Naudoti UPnP + + + + Use TURN + Naudoti TURN + + + + TURN Password + TURN slaptažodis + + + + TURN Username + TURN naudotojo vardas + + + + TURN Address + TURN adresas + + + + Media + Medija + + + + Enable Video + Įjungti vaizdą + + + + Audio Codecs + Garso kodekai + + + + Video Codecs + Vaizdo kodekai + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Garso failai (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Forma + + + + BannedItemWidget + + + Form + Forma + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + Rodyti pokalbius + + + + Conversations + Pokalbiai + + + + Search contact text input + Ieškoti adresato + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami yra visuotiniam bendravimui skirta laisva programinė įranga, kuri gerbia savo naudotojų laisves ir privatumą. + + + + + This is your ID. +Copy and share it with your friends! + + + Tai yra jūsų ID. +Nukopijuokite jį ir dalinkitės juo su savo draugais! + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + Spustelėkite du kartus, norėdami nukopijuoti + + + + Error while generating QR Code + Klaida, kuriant QR kodą + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Pridėti prie adresatų + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + Rasti naują ar esamą adresatą + + + + Wants to talk to you! + Nori su jumis pakalbėti! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Atsiliepti + + + + Ignore + Nepaisyti + + + + Cancel outgoing call + + + + + Cancel + Atsisakyti + + + + Start video call + Pradėti vaizdo skambutį + + + + Start audio call + Pradėti garso skambutį + + + + Clear conversation + Išvalyti pokalbį + + + + Remove contact + Šalinti adresatą + + + + Block contact + Užblokuoti adresatą + + + + Copy number + Kopijuoti numerį + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Gaunamasis skambutis nuo %1 + + + + DeleteAccountDialog + + + Account deletion + Paskyros ištrynimas + + + + Do you really want to delete the following account? + Ar tikrai norite ištrinti šią paskyrą? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Jeigu ši paskyra nebuvo eksportuota ar pridėta į kitą įrenginį, ji bus negrįžtamai prarasta. + + + + Permanently delete account + Ištrinti paskyrą visiems laikams + + + + Delete + Ištrinti + + + + Cancel account deletion + Atsisakyti paskyros ištrynimo + + + + Cancel + Atsisakyti + + + + DeviceItemWidget + + + Form + Forma + + + + Device Id + Įrenginio ID + + + + this device + šis įrenginys + + + + InviteButtonsWidget + + + Accept + Priimti + + + + Refuse + Atmesti + + + + Block + Užblokuoti + + + + LinkDeviceDialog + + + Dialog + Dialogas + + + + Enter your account password + Įveskite savo paskyros slaptažodį + + + + Password + Slaptažodis + + + + Ok + Gerai + + + + Cancel + Atsisakyti + + + + Exporting account + Eksportuojama paskyra + + + + Your PIN is + Jūsų PIN yra + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Užverti + + + + Link Another Device + Susieti kitą įrenginį + + + + Incorrect password + Neteisingas slaptažodis + + + + Something went wrong. +Please try again later. + Kažkas nutiko. +Vėliau bandykite dar kartą. + + + + MainWindow + + + Settings + Nustatymai + + + + Exit + Išeiti + + + + About + Apie + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Nustatyti registruotą vardą + + + + Enter your account password + Įveskite savo paskyros slaptažodį + + + + Password text input + + + + + Password text entry + + + + + Password + Slaptažodis + + + + Register + Registruoti + + + + Cancel + Atsisakyti + + + + Registering Name + + + + + Something went wrong + Kažkas nutiko + + + + Close + Užverti + + + + Incorrect password + Neteisingas slaptažodis + + + + Network error + Tinklo klaida + + + + NewWizardWidget + + + Form + Forma + + + + Welcome Label + + + + + Welcome to + Sveiki atvykę į + + + + Welcome Logo + + + + + Create Jami account button + Susikurkite Jami paskyrą + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Susikurti Jami paskyrą + + + + Link device button + Susiekite įrenginį + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Susieti šį įrenginį su paskyra + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Susieti šį įrenginį su esama paskyra + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Norėdami susieti šį įrenginį su kita paskyra, iš pradžių, </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">turite gauti PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> kodą. Norėdami generuoti PIN kodą:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Ankstesniajame įrenginyje pereikite į </span><span style=" font-size:14px; font-weight:600;">paskyros tvarkymo nustatymus</span><span style=" font-size:14px;"></span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Pasirinkite norimą naudoti </span><span style=" font-size:14px; font-weight:600;">Jami paskyrą</span><span style=" font-size:14px;"></span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Pereikite į </span><span style=" font-size:14px; font-weight:600;">Įrenginių</span><span style=" font-size:14px;"> kortelę</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Pasirinkite </span><span style=" font-size:14px; font-weight:600;">Pridėti įrenginį</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Taip gausite šiai formai reikalingą PIN kodą. PIN galios tik </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minučių</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Įveskite savo pin: + + + + Or import a file: + Arba importuokite failą: + + + + Link from exported account archive file + Susieti iš eksportuotos paskyros archyvo failo + + + + + + (None) + (Nėra) + + + + Password: + Slaptažodis: + + + + + Profile + Profilis + + + + + Profile name + Profilio pavadinimas + + + + Account + Paskyra + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Užregistruokite savo naudotojo vardą. +Tai rezervuos naudotojo vardą ir tokiu būdu tik jūs +galėsite jį naudoti. Jūsų draugai galės jums skambinti, +vietoj jūsų ID naudodami jūsų naudotojo vardą. + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Registruoti viešąjį naudotojo vardą + + + + Public username edit + + + + + Choose your username + Pasirinkite savo naudotojo vardą + + + + Password text input + + + + + Password text entry + + + + + + Password + Slaptažodis + + + + + Password confirmation text input + + + + + Confirm password + Patvirtinkite slaptažodį + + + + SIP Account + SIP paskyra + + + + SIP Server edit + + + + + Server + Serveris + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Įgaliotasis serveris + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Naudotojo vardas + + + + + SIP Password text input + + + + + Generating your Jami account… + Kuriama jūsų Jami paskyra… + + + + Previous page button + Ankstesnis puslapis + + + + push button to access previous page of wizard + + + + + Previous + Ankstesnis + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Atgal + + + + Next page Button + Kitas puslapis + + + + Push button to access next page of wizard + + + + + Next + Kitas + + + + Open File + Atverti failą + + + + Jami archive files (*.gz); All files (*) + Jami archyvų failai (*.gz); Visi failai (*) + + + + Your account needs to be migrated. Enter your password. + Jūsų paskyra turi būti perkelta. Įveskite savo slaptažodį. + + + + Migrating your Jami account... + Perkeliama jūsų Jami paskyra... + + + + Importing account archive... + Importuojamas paskyros archyvas... + + + + Generating your Jami account... + Kuriama jūsų Jami paskyra... + + + + Generating your SIP account... + + + + + Error creating account + Klaida, kuriant paskyrą + + + + PasswordDialog + + + Change Account Password + Pakeisti paskyros slaptažodį + + + + Enter Current Password + Įveskite dabartinį slaptažodį + + + + Enter New Password + Įveskite naują slaptažodį + + + + Confirm New Password + Pakartokite naują slaptažodį + + + + Confirm + Patvirtinti + + + + Cancel + Atsisakyti + + + + Current Password Incorrect + Dabartinis slaptažodis neteisingas + + + + PhotoBoothDialog + + + Photobooth + Nuotraukų budelė + + + + PhotoboothWidget + + + Form + Forma + + + + Photobooth display + Nuotraukų budelės rodinys + + + + Choose File + Pasirinkti failą + + + + Image Files (*.jpg *.jpeg *.png) + Paveikslų failai (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Pasirinkti failą + + + + Files (*) + Failai (*) + + + + QObject + + + No default mail client found + Numatytoji pašto kliento programa nerasta + + + + + Edit Device Name + Taisyti įrenginio pavadinimą + + + + Unlink Device From Account + Atsieti įrenginį nuo paskyros + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + Pasirinkti aplanką + + + + SettingsWidget + + + Form + Forma + + + + Settings + Nustatymai + + + + Account + Paskyra + + + + + General + Bendra + + + + + Audio / Video + Garsas / Vaizdas + + + + System + Sistema + + + + Enable desktop notifications + Įjungti darbalaukio pranešimus + + + + Keep minimized on close + Užvėrus, palikti suskleistą + + + + Download folder + Atsiuntimų aplankas + + + + Save in + Įrašyti į + + + + Always Recording + Visada įrašinėti + + + + Updates + Atnaujinimai + + + + Check for updates automatically every + Automatiškai tikrinti ar yra atnaujinimų kas + + + + Interval between update checks in days selector + + + + + days + d. + + + + Check for updates now button + + + + + Check for updates now + Tikrinti dabar ar yra atnaujinimų + + + + Password + Slaptažodis + + + + + Enable + Įjungti + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + Jami paskyra + + + + + Profile + Profilis + + + + + Identity + Tapatybė + + + + Id + Id + + + + Registered name + + + + + Type here to register a username + Rašykite, norėdami registruoti vardą + + + + Register + Registruoti + + + + Change Password + Pakeisti slaptažodį + + + + Export Account + Eksportuoti paskyrą + + + + + Delete Account + Ištrinti paskyrą + + + + Linked Devices + Susieti įrenginiai + + + + Link Another Device + Susieti kitą įrenginį + + + + Banned Contacts + Užblokuoti adresatai + + + + Format + Formatas + + + + Video device framerate selector + + + + + Preview unavailable + Peržiūra neprieinama + + + + + Advanced Account Settings + Išplėstiniai paskyros nustatymai + + + + SIP Account + SIP paskyra + + + + Username + Naudotojo vardas + + + + Hostname + Kompiuterio vardas + + + + Proxy + Įgaliotasis serveris + + + + Audio + Garsas + + + + Microphone + Mikrofonas + + + + Audio input device selector + + + + + Output Device + Išvesties įrenginys + + + + Choose the output device + + + + + Video + Vaizdas + + + + Device + Įrenginys + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + Registruotame varde negali būti tarpų ir jis privalo būti bent 3 raidžių ilgio + + + + This name is already taken + Šis vardas jau yra užimtas + + + + + Enter an alias + + + + + Register this name + Registruoti šį vardą + + + + Remove Device + Šalinti įrenginį + + + + Enter this account's password to confirm the removal of this device + Įveskite šios paskyros slaptažodį, kad patvirtintumėte šio įrenginio šalinimą + + + + Are you sure you wish to remove this device? + Ar tikrai norite pašalinti šį įrenginį? + + + + Export Account Here + Eksportuoti paskyrą čia + + + + Select A Folder For Your Downloads + Pasirinkite atsiuntimų aplanką + + + + Select A Folder For Your Recordings + Pasirinkite įrašų aplanką + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + Pokalbis + + + + Mute Mic + Išjungti mikrofoną + + + + Record call + Įrašyti skambutį + + + + Name label + Vardo etiketė + + + + Time elapsed + Praėjęs laikas + + + + 00:00 + 00:00 + + + + Hangup + Padėti ragelį + + + + Mute Video + Išjungti vaizdą + + + + VideoView + + + Share entire screen + Bendrinti visą ekraną + + + + Share screen area + Bendrinti ekrano sritį + + + + Share file + Bendrinti failą + + + \ No newline at end of file diff --git a/translations/ring_client_windows_ms.ts b/translations/ring_client_windows_ms.ts new file mode 100644 index 00000000..38a3f477 --- /dev/null +++ b/translations/ring_client_windows_ms.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + Tentang + + + + about button + + + + + credits button + + + + + Credits + Kredit + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + versi + + + + Created by: + Dicipta oleh: + + + + Artwork by: + Kerja seni oleh: + + + + Based on the SFLPhone project + Berdasarkan projek + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + Guna TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Aktifkan panggilan video + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Bootstrap + + + + Security + Penyulitan + + + + Private Key Password + Kata laluan untuk fail Private Key + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Guna TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Aktifkan panggilan video + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Tambahkan sebagai kenalan + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + Batal + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + Salin nombor + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Hapus + + + + Cancel account deletion + + + + + Cancel + Batal + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Terima + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Kara laluan + + + + Ok + + + + + Cancel + Batal + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Tetapan + + + + Exit + + + + + About + Tentang + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Kara laluan + + + + Register + Daftar + + + + Cancel + Batal + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + Rangkaian gagal + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Sambungkan perangkat ini dengan akaun + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + Akaun + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Kara laluan + + + + + Password confirmation text input + + + + + Confirm password + Sahkan kata laluan + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proksi + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Nama pengguna + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + Gagal mencipta akaun + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Batal + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Tetapan + + + + Account + Akaun + + + + + General + Utama + + + + + Audio / Video + + + + + System + Sistem + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Kara laluan + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Daftar + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Nama pengguna + + + + Hostname + Alamat server + + + + Proxy + Proksi + + + + Audio + Bunyi + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Video + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_nb.ts b/translations/ring_client_windows_nb.ts new file mode 100644 index 00000000..8a151969 --- /dev/null +++ b/translations/ring_client_windows_nb.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + Om + + + + about button + om-knapp + + + + credits button + bidragsytere-knapp + + + + Credits + Bidragsytere + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + versjon + + + + Created by: + Opprettet av: + + + + Artwork by: + Omslagskunst av: + + + + Based on the SFLPhone project + Basert på SFLPhone-prosjektet + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + Skjema + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Forbindelsesstatus + + + + STUN Address + + + + + Use STUN + Bruk STUN + + + + Use UPnP + + + + + Use TURN + Bruk TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Aktiver video + + + + Video Codecs + Video-kodeker + + + + Audio Codecs + Lydkodeker + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + Skjema + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adresse + + + + OpenDHT Configuration + + + + + Enable Proxy + Skru på mellomtjener + + + + Bootstrap + Bootstrap + + + + Security + Sikkerhet + + + + Private Key Password + Privat nøkkel-passord + + + + User Certificate + Brukersertifikat + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Forbindelsesstatus + + + + Use STUN + Bruk STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Bruk TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Aktiver video + + + + Audio Codecs + Lydkodeker + + + + Video Codecs + Video-kodeker + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Skjema + + + + BannedItemWidget + + + Form + Skjema + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Samtaler + + + + Search contact text input + Søk etter kontakttekst -inndata + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami er fri programvare for allsidig kommunikasjon som respekterer brukernes frihet og retten til privatliv. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + Vis QR-kode for ring ID + + + + Share ring ID button + Del ring ID -knapp + + + + Double-click to copy + Dobbeltklikk for å kopiere + + + + Error while generating QR Code + Feil ved generering av QR-kode + + + + + best name + + + + + best Id + + + + + Back to homepage button + Tilbake til hjemmeside -knapp + + + + Add to contacts + Legg til kontakter + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Ønsker å snakke med deg! + + + + Answer incoming call button + Svar innkommende anrop -knapp + + + + Ignore incoming call button + Ignorer innkommende anrop -knapp + + + + Answer + Svar + + + + Ignore + Ignorer + + + + Cancel outgoing call + Avbryt utgående anrop + + + + Cancel + Avbryt + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + Fjern kontakt + + + + Block contact + Blokker kontakt + + + + Copy number + Kopier nummer + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Innkommende anrop fra %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Slett + + + + Cancel account deletion + + + + + Cancel + Avbryt + + + + DeviceItemWidget + + + Form + Skjema + + + + Device Id + + + + + this device + denne enheten + + + + InviteButtonsWidget + + + Accept + Aksepter + + + + Refuse + Avslå + + + + Block + Blokker + + + + LinkDeviceDialog + + + Dialog + Dialogvindu + + + + Enter your account password + + + + + Password + Passord + + + + Ok + OK + + + + Cancel + Avbryt + + + + Exporting account + Eksporterer konto + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Lukk + + + + Link Another Device + + + + + Incorrect password + Feil passord + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Innstillinger + + + + Exit + Avslutt + + + + About + Om + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + Passordstekstinndata + + + + Password text entry + Passordteksinnskriving + + + + Password + Passord + + + + Register + Registrer + + + + Cancel + Avbryt + + + + Registering Name + + + + + Something went wrong + + + + + Close + Lukk + + + + Incorrect password + Feil passord + + + + Network error + Nettverksfeil + + + + NewWizardWidget + + + Form + Skjema + + + + Welcome Label + Velkomstetikett + + + + Welcome to + Velkommen til + + + + Welcome Logo + Velkomstlogo + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Opprett en Jami-konto + + + + Link device button + Lenk enhet -knapp + + + + Push button for device linkage start trigger + Trykknapp for start av enhetslinking -utløser + + + + Link this device to an account + Lenk denne enheten til en konto + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Lenk denne enheten til en eksisterende konto + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Skriv inn din PIN: + + + + Or import a file: + Eller importer fil: + + + + Link from exported account archive file + Lenk fra eksportert kontoarkivfil + + + + + + (None) + + + + + Password: + Passord: + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Konto + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + Offentlig brukernavn -avkryssningsboks + + + + Checkbox selecting if the user wants a public username + Avkryssningsboks som velger hvorvidt en bruker ønsker et offentlig brukernavn + + + + Register public username + Registrer offentlig brukernavn + + + + Public username edit + Redigering av offentlig brukernavn + + + + Choose your username + Velg ditt brukernavn + + + + Password text input + Passordstekstinndata + + + + Password text entry + Passordteksinnskriving + + + + + Password + Passord + + + + + Password confirmation text input + Passordsbekreftelsestekst-inndata + + + + Confirm password + Bekreft passord + + + + SIP Account + SIP-konto + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Mellomtjener + + + + SIP username input + SIP-brukernavn -inndata + + + + SIP Password text entry + + + + + Username + Brukernavn + + + + + SIP Password text input + + + + + Generating your Jami account… + Oppretter Jami-kontoen din + + + + Previous page button + Forrige side -knapp + + + + push button to access previous page of wizard + trykkbar knapp for å nå forrige side av veivisning + + + + Previous + Forrige + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Tilbake + + + + Next page Button + Neste side -knapp + + + + Push button to access next page of wizard + Trykk på knappen for å komme til neste side i veivisningen + + + + Next + Neste + + + + Open File + Åpne fil + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + Kontoen din må flyttes. Skriv inn passordet ditt. + + + + Migrating your Jami account... + + + + + Importing account archive... + Importerer kontoarkiv… + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Avbryt + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + Fotoboks + + + + PhotoboothWidget + + + Form + Skjema + + + + Photobooth display + Fotoboksvisning + + + + Choose File + Velg fil + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Velg fil + + + + Files (*) + Filer (*) + + + + QObject + + + No default mail client found + Fant ingen forvalgt e-postklient + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + Velg mappe + + + + SettingsWidget + + + Form + Skjema + + + + Settings + Innstillinger + + + + Account + Konto + + + + + General + Generell + + + + + Audio / Video + + + + + System + System + + + + Enable desktop notifications + + + + + Keep minimized on close + Behold minimert ved lukking + + + + Download folder + Nedlastingsmappe + + + + Save in + Lagre i + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + Intervall mellom oppdateringssjekker i dager -velger + + + + days + + + + + Check for updates now button + Se etter oppdateringer nå -knapp + + + + Check for updates now + Se etter oppdateringer nå + + + + Password + Passord + + + + + Enable + Aktivèr + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + Identitet + + + + Id + ID + + + + Registered name + Registrert navn + + + + Type here to register a username + + + + + Register + Registrer + + + + Change Password + + + + + Export Account + Eksporter konto + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Bannlyste kontakter + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + SIP-konto + + + + Username + Brukernavn + + + + Hostname + Vertsnavn + + + + Proxy + Mellomtjener + + + + Audio + Lyd + + + + Microphone + + + + + Audio input device selector + Lydinndataenhets-velger + + + + Output Device + Utgangsenhet + + + + Choose the output device + + + + + Video + Video + + + + Device + Enhet + + + + Video device selector + Videoenhetsvelger + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Samtale på vent + + + + Hold / Unhold + Parker / gjenoppta parkering + + + + Chat + Sludre + + + + Mute Mic + Demp mikrofon + + + + Record call + Ta opp samtale + + + + Name label + Navneetikett + + + + Time elapsed + Forløpt tid + + + + 00:00 + 00:00 + + + + Hangup + Legg på + + + + Mute Video + Demp video + + + + VideoView + + + Share entire screen + Del hele skjermen + + + + Share screen area + Del skjermområde + + + + Share file + Del fil + + + \ No newline at end of file diff --git a/translations/ring_client_windows_nb_NO.ts b/translations/ring_client_windows_nb_NO.ts new file mode 100644 index 00000000..d491fec8 --- /dev/null +++ b/translations/ring_client_windows_nb_NO.ts @@ -0,0 +1,2073 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Live Free or Die + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountDetails + + + Settings + Innstillinger + + + + General + + + + + Account + + + + + Type + + + + + Parameters + + + + + Hostname + + + + + Public registered username + + + + + + Username + + + + + + Password + Passord… + + + + Proxy + + + + + Voicemail number + + + + + Auto-answer calls + + + + + UPnP enabled + + + + + Account Settings + + + + + Allow Call From Unknown + + + + + Allow Call From Contact + + + + + Allow Call From History + + + + + General account info configuration + + + + + Displayed name + + + + + Displayed alias input + + + + + Hostname URL for DHT bootstrap or SIP + + + + + Public username + + + + + Register username on blockchain button + + + + + Register on blockchain + + + + + Name service URL + + + + + Nameservice URL input + + + + + SIP username input + + + + + SIP password input + + + + + SIP proxy URL input + + + + + SIP voicemail number + + + + + Autoanswer call checkbox + + + + + UPnP enabled checkbox + + + + + Old password input line + + + + + Input old password + + + + + Old Password + + + + + New password text input + + + + + Input new password + + + + + New password + + + + + Password confirmation text input + + + + + Input confirmation + + + + + New pass confirmation + + + + + Change password + + + + + Ok + + + + + Cancel password change button + + + + + Changing password ... + + + + + Allow call from unknown checkbox + + + + + Allow Call From History checkbox + + + + + Allow Call From Contact checkbox + + + + + Ringtone Selection + + + + + Play ringtone button + + + + + Play + + + + + ringtone file selector + + + + + Audio/Video + + + + + Audio/video codecs preferences configuration + + + + + Audio codecs + + + + + Video codecs + + + + + Advanced + + + + + Account advanced configuration + + + + + Registration + + + + + Registration expire timeout (seconds): + + + + + Registration timeout selector spinbox + + + + + Network Interface + + + + + Local Port: + + + + + local port selector spinbox + + + + + same as local radio button + + + + + Same as local parameters + + + + + personalise address and ports radio button + + + + + Set published address and port: + + + + + Address + + + + + ip address text input + + + + + Port + + + + + port selector spinbox + + + + + use STUN checkbox + + + + + Use STUN + + + + + + Server URL + + + + + use TURN checkbox + + + + + Use TURN + + + + + TURN server URL input + + + + + TURN server username input + + + + + TURN server password input + + + + + TURN server Realm input + + + + + Realm + + + + + SDP Session Negotiation (ICE Fallback) + + + + + These settings are only used during SDP session negotiation in case ICE is not supported by the server or peer. + + + + + Audio RTP Port Range + + + + + + Min + + + + + + Lower boundary for audio RTP port range selector + + + + + + Max + + + + + + higher boundary for audio RTP port range selector + + + + + Video RTP Port Range + + + + + Security + + + + + Account security configuration + + + + + Certificate authaurity certificate selector + + + + + User certificate selector + + + + + private key password input + + + + + User private key selector + + + + + Encrypt media stream (SRTP) checkbox + + + + + Media Stream Encryption (SRTP) + + + + + use default ciphers checkbox + + + + + Devices + + + + + Account devices management + + + + + Add device button + + + + + Add device + + + + + Export on the network + + + + + Password text input + + + + + Password (required) + + + + + Cancel account export for linking button + + + + + + Cancel + Avbryt + + + + To add a new device to your Jami account, you export your account on the network. This will generate a pin that must be entered on your new device within 5 minutes of its generation. + + + + + Validate export on DHT for device linking button + + + + + Your generated pin: + + + + + Generated PIN for linking + + + + + PIN + + + + + This pin should be entered on your new device within 5 minutes. You may generate a new one at any moment. + + + + + Accept PIN generation and quit button + + + + + OK + OK + + + + Banned Contacts + + + + + Account banned contacts management tab + + + + + Published Address + + + + + CA certificate + + + + + User certificate + + + + + Private key + + + + + Private key password + + + + + Use default ciphers + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + no password + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + TURN Address + + + + + Use STUN + + + + + STUN Address + + + + + TURN Username + + + + + TURN Password + + + + + Use UPnP + + + + + Use TURN + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedContactsWidget + + + Form + + + + + Banned contacts list + + + + + Banned ID + + + + + Deban contact button + + + + + Deban contact, and add to contact list + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami er fri programvare til universell kommunikasjon som respekterer brukernes frihet og rett til privatliv. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + Avbryt + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + Message incoming from %1 + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + ContactDialog + + + Dialog + + + + + New Contact + + + + + Enter a name... + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + Avbryt + + + + Form + + + Form + + + + + PushButton + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDevWidget + + + Form + + + + + Add A New Device + + + + + To add a new device, enter your account password: + + + + + Cancel + Avbryt + + + + Enter + + + + + Exporting account on the network + + + + + Your generated pin: + + + + + This pin and the account password should be entered in your device within 5 minutes. + + + + + Close + + + + + Your account password was incorrect + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Innstillinger + + + + Exit + + + + + About + + + + + Jami + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + Profile + + + + + Profile name + + + + + Account + + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + Password + Passord… + + + + + Password confirmation text input + + + + + Confirm password + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Avbryt + + + + Password Changed Successfully + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Unblock Contact + + + + + Jami account + Default alias for new Jami account + + + + + RegNameDialog + + + Set Registered Name + + + + + Are you sure you would like to register this name? + + + + + Once associated to this account, it cannot be changed. + + + + + Your new registered name would be: + + + + + Confirm Registration + + + + + Cancel + Avbryt + + + + The name has been successfully registered! + + + + + Something went wrong. + + + + + Please try again later + + + + + Your account does not have a registered name + + + + + RingButton + + + Select folder + + + + + SetAvatarDialog + + + Change Your Avatar Image + + + + + Save + + + + + Take Picture + + + + + Camera + + + + + Select A File + + + + + Cancel + Avbryt + + + + Open Image + + + + + Image Files (*.jpg *.jpeg *.png *.bmp) + + + + + SettingsWidget + + + Form + + + + + Settings + Innstillinger + + + + + Account + + + + + General + + + + + Audio / Video + + + + + System + + + + + enable notifications checkbox + + + + + Enable desktop notifications + + + + + keep minimized on close checkbox + + + + + Keep minimized on close + + + + + Download folder + + + + + Recordings + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Autamatic update checks checkbox + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Registered Name + + + + + ID + ID + + + + Password + Passord… + + + + Enable + + + + + Type here to register a username + + + + + Change Password + + + + + Export Account + + + + + Delete Account + + + + + + + Linked Devices + + + + + Link Another Device + + + + + + + Blocked Contacts + + + + + Advanced Account Settings + + + + + Audio + + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + + + + + Device + + + + + Video device selector + + + + + Resolution + + + + + Video device resolution selector + + + + + Preview unavailable during call + + + + + Enter the displayed name + + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + This registered name is already taken + + + + + This name is available + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Please confirm that you wish to remove this device + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_nl.ts b/translations/ring_client_windows_nl.ts new file mode 100644 index 00000000..147db2a7 --- /dev/null +++ b/translations/ring_client_windows_nl.ts @@ -0,0 +1,1605 @@ + + + AboutDialog + + + + About + Over + + + + about button + over-knop + + + + credits button + knop met dank aan + + + + Credits + Met dank aan + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + De Microsoft Windows-cliënt voor Jami. +Jami is software voor beveiligde en gedistribueerde communicatie. + + + + version + versie + + + + Created by: + Geschreven door: + + + + Artwork by: + Illustraties door: + + + + Based on the SFLPhone project + Gebaseerd op het SFLPhone-project + + + + AccountItemDelegate + + + Add Account + Account toevoegen + + + + AdvancedSIPSettingsWidget + + + Form + Formulier + + + + Call Settings + Oproepinstellingen + + + + Auto Answer Calls + Oproepen automatisch beantwoorden + + + + Enable Custom Ringtone + Aangepaste beltoon inschakelen + + + + Connectivity + Connectiviteit + + + + STUN Address + STUN-adres + + + + Use STUN + STUN gebruiken + + + + Use UPnP + UPnP gebruiken + + + + Use TURN + TURN gebruiken + + + + TURN Password + TURN-wachtwoord + + + + TURN Username + TURN-gebruikersnaam + + + + TURN Address + TURN-adres + + + + Media + Media + + + + Enable Video + Video inschakelen + + + + Video Codecs + Videocodecs + + + + Audio Codecs + Audiocodecs + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Audiobestanden (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Voeg een aangepaste beltoon toe + + + + AdvancedSettingsWidget + + + Form + Formulier + + + + Call Settings + Oproepinstellingen + + + + Allow Calls From Untrusted Peers + Oproepen van onvertrouwde gebruikers toestaan + + + + Auto Answer Calls + Oproepen automatisch beantwoorden + + + + Enable Custom Ringtone + Aangepaste beltoon inschakelen + + + + Add a custom ringtone + Voeg een aangepaste beltoon toe + + + + Name Server + Naamserver + + + + Address + Adres + + + + OpenDHT Configuration + OpenDHT-configuratie + + + + Enable Proxy + Proxy inschakelen + + + + Bootstrap + Bootstrap + + + + Security + Beveiliging + + + + Private Key Password + Wachtwoord persoonlijke sleutel + + + + User Certificate + Gebruikerscertificaat + + + + Private Key + Privésleutel + + + + CA Certificate + CA-certificaat + + + + Connectivity + Connectiviteit + + + + Use STUN + STUN gebruiken + + + + STUN Address + STUN-adres + + + + Use UPnP + UPnP gebruiken + + + + Use TURN + TURN gebruiken + + + + TURN Password + TURN-wachtwoord + + + + TURN Username + TURN-gebruikersnaam + + + + TURN Address + TURN-adres + + + + Media + Media + + + + Enable Video + Video inschakelen + + + + Audio Codecs + Audiocodecs + + + + Video Codecs + Videocodecs + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Audiobestanden (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formulier + + + + BannedItemWidget + + + Form + Formulier + + + + name + naam + + + + id + ID + + + + CallWidget + + + Show conversations + Gesprekken tonen + + + + Conversations + Gesprekken + + + + Search contact text input + Tekstinvoer contact zoeken + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami is vrije software voor universele communicatie, die de vrijheden en privacy van haar gebruikers eerbiedigt. + + + + + This is your ID. +Copy and share it with your friends! + + + Dit is uw ID. +Kopieer en deel het met uw vrienden! + + + + Show ring ID QR code + Toon QR-code voor Ring-ID + + + + Share ring ID button + Knop Ring-ID delen + + + + Double-click to copy + Dubbelklik om te kopiëren + + + + Error while generating QR Code + Fout bij het aanmaken van QR-code + + + + + best name + beste naam + + + + best Id + beste ID + + + + Back to homepage button + Knop terug naar startpagina + + + + Add to contacts + Toevoegen aan contacten + + + + Show invites + Uitnodigingen tonen + + + + Invites + Uitnodigingen + + + + + Find a new or existing contact + Zoek een nieuw of bestaand contact + + + + Wants to talk to you! + Wil met u praten! + + + + Answer incoming call button + Knop inkomende oproep beantwoorden + + + + Ignore incoming call button + Knop inkomende oproep negeren + + + + Answer + Beantwoorden + + + + Ignore + Negeren + + + + Cancel outgoing call + Uitgaande oproep annuleren + + + + Cancel + Annuleren + + + + Start video call + Video-oproep starten + + + + Start audio call + Audio-oproep starten + + + + Clear conversation + Gesprek wissen + + + + Remove contact + Contact verwijderen + + + + Block contact + Contact blokkeren + + + + Copy number + Nummer kopiëren + + + + Search your received invitations + Zoek naar uw ontvangen uitnodigingen + + + + Contact me on Jami + Contacteer me op Jami + + + + My Id is : + Mijn ID is: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Inkomende oproep van %1 + + + + DeleteAccountDialog + + + Account deletion + Accountverwijdering + + + + Do you really want to delete the following account? + Weet u zeker dat u het volgende account wilt verwijderen? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Als dit account nog niet geëxporteerd is of nog niet toegevoegd is aan een ander apparaat, zal het onherroepelijk verloren gaan + + + + Permanently delete account + Account voorgoed verwijderen + + + + Delete + Verwijderen + + + + Cancel account deletion + Accountverwijdering annuleren + + + + Cancel + Annuleren + + + + DeviceItemWidget + + + Form + Formulier + + + + Device Id + Apparaat-ID + + + + this device + dit apparaat + + + + InviteButtonsWidget + + + Accept + Accepteren + + + + Refuse + Afwijzen + + + + Block + Blokkeren + + + + LinkDeviceDialog + + + Dialog + Dialoog + + + + Enter your account password + Voer uw accountwachtwoord in + + + + Password + Wachtwoord + + + + Ok + Oké + + + + Cancel + Annuleren + + + + Exporting account + Account wordt geëxporteerd + + + + Your PIN is + Uw pincode is + + + + PIN + Pincode + + + + This pin and the account password should be entered in your device within 10 minutes. + Deze pincode en het accountwachtwoord moeten binnen 10 minuten op uw apparaat worden ingevoerd + + + + Close + Sluiten + + + + Link Another Device + Een ander apparaat koppelen + + + + Incorrect password + Verkeerd wachtwoord + + + + Something went wrong. +Please try again later. + Er ging iets mis. +Probeer het later opnieuw. + + + + MainWindow + + + Settings + Instellingen + + + + Exit + Afsluiten + + + + About + Over + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Stel geregistreerde naam in + + + + Enter your account password + Voer uw accountwachtwoord in + + + + Password text input + Wachtwoordtekstinvoer + + + + Password text entry + Wachtwoordtekstinvoer + + + + Password + Wachtwoord + + + + Register + Registreren + + + + Cancel + Annuleren + + + + Registering Name + Naam wordt geregistreerd + + + + Something went wrong + Er ging iets mis + + + + Close + Sluiten + + + + Incorrect password + Verkeerd wachtwoord + + + + Network error + Netwerkfout + + + + NewWizardWidget + + + Form + Formulier + + + + Welcome Label + Welkomstlabel + + + + Welcome to + Welkom bij + + + + Welcome Logo + Welkomstlogo + + + + Create Jami account button + Knop Jami-account aanmaken + + + + Push button for Jami account creation start trigger + Trekker voor starten van aanmaken van Jami-account na druk op knop + + + + Create a Jami account + Een Jami-account aanmaken + + + + Link device button + Knop apparaat koppelen + + + + Push button for device linkage start trigger + Trekker voor koppelen van apparaat na druk op knop + + + + Link this device to an account + Dit apparaat aan een account koppelen + + + + Create Jami SIP account button + Knop Jami-SIP-account aanmaken + + + + Push button for Jami SIP account creation start trigger + Trekker voor starten van aanmaken van Jami-SIP-account na druk op knop + + + + Create a SIP account + Een SIP-account aanmaken + + + + Link this device to an existing account + Dit apparaat aan een bestaand account koppelen + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Om dit apparaat aan een ander account te koppelen, dient u eerst </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">een pincode te verkrijgen</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">. Om de pincode te genereren:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Ga naar de </span><span style=" font-size:14px; font-weight:600;">Accountbeheerinstellingen</span><span style=" font-size:14px;"> van een vorig apparaat</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Kies het </span><span style=" font-size:14px; font-weight:600;">Jami-account</span><span style=" font-size:14px;"> dat u wilt gebruiken</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Ga naar het tabblad </span><span style=" font-size:14px; font-weight:600;">Apparaten</span><span style=" font-size:14px;"></span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Selecteer </span><span style=" font-size:14px; font-weight:600;">Een apparaat toevoegen</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">U zult de pincode krijgen die u nodig heeft om dit proces te voltooien. De pincode is slechts </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minuten</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;"> geldig.</span></p></body></html> + + + + Enter your pin: + Voer uw pincode in: + + + + Or import a file: + Of importeer een bestand: + + + + Link from exported account archive file + Koppelen van geëxporteerd accountarchiefbestand + + + + + + (None) + (geen) + + + + Password: + Wachtwoord: + + + + + Profile + Profiel + + + + + Profile name + Profielnaam + + + + Account + Account + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Registreer uw gebruikersnaam. +Dit zal de gebruikersnaam registreren, zodat enkel u ze kunt gebruiken. +Uw vrienden zullen u kunnen bellen met uw gebruikersnaam +in plaats van uw Ring-ID. + + + + Public username checkbox + Selectievakje openbare gebruikersnaam + + + + Checkbox selecting if the user wants a public username + Selectievakje om te kiezen of de gebruiker een openbare gebruikersnaam wil + + + + Register public username + Registreer een openbare gebruikersnaam + + + + Public username edit + Bewerken openbare gebruikersnaam + + + + Choose your username + Kies uw gebruikersnaam + + + + Password text input + Wachtwoordtekstinvoer + + + + Password text entry + Wachtwoordtekstinvoer + + + + + Password + Wachtwoord + + + + + Password confirmation text input + Wachtwoordbevestigingstekstinvoer + + + + Confirm password + Bevestig wachtwoord + + + + SIP Account + SIP-account + + + + SIP Server edit + SIP-server bewerken + + + + Server + Server + + + + SIP proxy input + SIP-proxy-invoer + + + + SIP proxy text entry + SIP-proxy-tekstinvoer + + + + Proxy + Proxy + + + + SIP username input + SIP-gebruikersnaaminvoer + + + + SIP Password text entry + SIP-wachtwoord-tekstinvoer + + + + Username + Gebruikersnaam + + + + + SIP Password text input + SIP-wachtwoord-tekstinvoer + + + + Generating your Jami account… + Uw Jami-account wordt aangemaakt… + + + + Previous page button + Knop vorige pagina + + + + push button to access previous page of wizard + druk op de knop om naar de vorige pagina van de assistent te gaan + + + + Previous + Vorige + + + + Cancel account create/link + Aanmaken/koppelen van account annuleren + + + + push button to cancel account creation or linking + trekker voor annuleren van aanmaken of verwijderen van account + + + + Back + Terug + + + + Next page Button + Knop volgende pagina + + + + Push button to access next page of wizard + Druk op de knop om naar de volgende pagina van de assistent te gaan + + + + Next + Volgende + + + + Open File + Bestand openen + + + + Jami archive files (*.gz); All files (*) + Jami-archiefbestanden (*.gz); Alle bestanden (*) + + + + Your account needs to be migrated. Enter your password. + Uw account moet gemigreerd worden. Voer uw wachtwoord in. + + + + Migrating your Jami account... + Uw Jami-account wordt gemigreerd… + + + + Importing account archive... + Accountarchief importeren… + + + + Generating your Jami account... + Uw Jami-account wordt aangemaakt… + + + + Generating your SIP account... + Uw SIP-account wordt gegenereerd… + + + + Error creating account + Fout bij aanmaken van account + + + + PasswordDialog + + + Change Account Password + Accountwachtwoord wijzigen + + + + Enter Current Password + Voer huidig wachtwoord in + + + + Enter New Password + Voer nieuw wachtwoord in + + + + Confirm New Password + Bevestig nieuw wachtwoord + + + + Confirm + Bevestigen + + + + Cancel + Annuleren + + + + Current Password Incorrect + Huidige wachtwoord is verkeerd + + + + PhotoBoothDialog + + + Photobooth + Fotoautomaat + + + + PhotoboothWidget + + + Form + Formulier + + + + Photobooth display + Fotoautomaatweergave + + + + Choose File + Bestand kiezen + + + + Image Files (*.jpg *.jpeg *.png) + Afbeeldingsbestanden (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Bestand kiezen + + + + Files (*) + Bestanden (*) + + + + QObject + + + No default mail client found + Geen standaard-e-mailprogramma gevonden + + + + + Edit Device Name + Apparaatnaam bewerken + + + + Unlink Device From Account + Apparaat ontkoppelen van account + + + + Save new device name + Nieuwe apparaatnaam opslaan + + + + Add as contact + Toevoegen aan contacten + + + + RingButton + + + Select folder + Kies een map + + + + SettingsWidget + + + Form + Formulier + + + + Settings + Instellingen + + + + Account + Account + + + + + General + Algemeen + + + + + Audio / Video + Audio/video + + + + System + Systeem + + + + Enable desktop notifications + Bureaubladmeldingen inschakelen + + + + Keep minimized on close + Minimaliseren bij sluiten + + + + Download folder + Downloadmap + + + + Save in + Opslaan in + + + + Always Recording + Altijd opnemen + + + + Updates + Updates + + + + Check for updates automatically every + Automatisch controleren op updates elke + + + + Interval between update checks in days selector + Selectie interval in dagen tussen controles op updates + + + + days + dagen + + + + Check for updates now button + Knop nu controleren op updates + + + + Check for updates now + Nu controleren op updates + + + + Password + Wachtwoord + + + + + Enable + Inschakelen + + + + toggle enable notifications + schakelaar meldingen inschakelen + + + + Toggle keep minimized on close + Schakelaar minimaliseren bij sluiten + + + + Call Recordings + Oproepopnamen + + + + Toggle automatic updates + Schakelaar automatische updates + + + + Jami Account + Jami-account + + + + + Profile + Profiel + + + + + Identity + Identiteit + + + + Id + ID + + + + Registered name + Geregistreerde naam + + + + Type here to register a username + Typ hier om een gebruikersnaam te registreren + + + + Register + Registreren + + + + Change Password + Wachtwoord wijzigen + + + + Export Account + Account exporteren + + + + + Delete Account + Account verwijderen + + + + Linked Devices + Gekoppelde apparaten + + + + Link Another Device + Een ander apparaat koppelen + + + + Banned Contacts + Geblokkeerde contacten + + + + Format + Formaat + + + + Video device framerate selector + Framerateselectie voor video-apparaat + + + + Preview unavailable + Voorbeeld niet beschikbaar + + + + + Advanced Account Settings + Geavanceerde accountinstellingen + + + + SIP Account + SIP-account + + + + Username + Gebruikersnaam + + + + Hostname + Hostnaam + + + + Proxy + Proxy + + + + Audio + Geluid + + + + Microphone + Microfoon + + + + Audio input device selector + Selectie audio-invoerapparaat + + + + Output Device + Uitvoerapparaat + + + + Choose the output device + Kies het uitvoerapparaat + + + + Video + Video + + + + Device + Apparaat + + + + Video device selector + Selectie video-apparaat + + + + A registered name should not have any spaces and must be at least three letters long + Een geregistreerde naam mag geen spaties bevatten, en moet minstens drie tekens lang zijn + + + + This name is already taken + Deze naam is reeds in gebruik + + + + + Enter an alias + Voer een alias in + + + + Register this name + Deze naam registreren + + + + Remove Device + Apparaat verwijderen + + + + Enter this account's password to confirm the removal of this device + Voer het wachtwoord van dit account in om het verwijderen van dit apparaat te bevestigen + + + + Are you sure you wish to remove this device? + Weet u zeker dat u dit apparaat wilt verwijderen? + + + + Export Account Here + Account hier exporteren + + + + Select A Folder For Your Downloads + Selecteer een map voor uw downloads + + + + Select A Folder For Your Recordings + Kies een map voor uw opnamen + + + + VideoOverlay + + + Call on Hold + Telefoongesprek in wacht + + + + Hold / Unhold + In wacht zetten / uit wacht halen + + + + Chat + Chat + + + + Mute Mic + Microfoon dempen + + + + Record call + Gesprek opnemen + + + + Name label + Naamlabel + + + + Time elapsed + Tijd verstreken + + + + 00:00 + 00:00 + + + + Hangup + Ophangen + + + + Mute Video + Video uitschakelen + + + + VideoView + + + Share entire screen + Scherm delen + + + + Share screen area + Schermgebied delen + + + + Share file + Bestand delen + + + \ No newline at end of file diff --git a/translations/ring_client_windows_nl_BE.ts b/translations/ring_client_windows_nl_BE.ts new file mode 100644 index 00000000..b78da348 --- /dev/null +++ b/translations/ring_client_windows_nl_BE.ts @@ -0,0 +1,1605 @@ + + + AboutDialog + + + + About + Over + + + + about button + over-knop + + + + credits button + knop met dank aan + + + + Credits + Met dank aan + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + De Microsoft Windows-cliënt voor Jami. +Jami is software voor beveiligde en gedistribueerde communicatie. + + + + version + versie + + + + Created by: + Geschreven door: + + + + Artwork by: + Illustraties door: + + + + Based on the SFLPhone project + Gebaseerd op het SFLPhone-project + + + + AccountItemDelegate + + + Add Account + Account toevoegen + + + + AdvancedSIPSettingsWidget + + + Form + Formulier + + + + Call Settings + Oproepinstellingen + + + + Auto Answer Calls + Oproepen automatisch beantwoorden + + + + Enable Custom Ringtone + Aangepaste beltoon inschakelen + + + + Connectivity + Connectiviteit + + + + STUN Address + STUN-adres + + + + Use STUN + STUN gebruiken + + + + Use UPnP + UPnP gebruiken + + + + Use TURN + TURN gebruiken + + + + TURN Password + TURN-paswoord + + + + TURN Username + TURN-gebruikersnaam + + + + TURN Address + TURN-adres + + + + Media + Media + + + + Enable Video + Video inschakelen + + + + Video Codecs + Videocodecs + + + + Audio Codecs + Audiocodecs + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Audiobestanden (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Voeg een aangepaste beltoon toe + + + + AdvancedSettingsWidget + + + Form + Formulier + + + + Call Settings + Oproepinstellingen + + + + Allow Calls From Untrusted Peers + Oproepen van onvertrouwde gebruikers toestaan + + + + Auto Answer Calls + Oproepen automatisch beantwoorden + + + + Enable Custom Ringtone + Aangepaste beltoon inschakelen + + + + Add a custom ringtone + Voeg een aangepaste beltoon toe + + + + Name Server + Naamserver + + + + Address + Adres + + + + OpenDHT Configuration + OpenDHT-configuratie + + + + Enable Proxy + Proxy inschakelen + + + + Bootstrap + Bootstrap + + + + Security + Beveiliging + + + + Private Key Password + Paswoord persoonlijke sleutel + + + + User Certificate + Gebruikerscertificaat + + + + Private Key + Privésleutel + + + + CA Certificate + CA-certificaat + + + + Connectivity + Connectiviteit + + + + Use STUN + STUN gebruiken + + + + STUN Address + STUN-adres + + + + Use UPnP + UPnP gebruiken + + + + Use TURN + TURN gebruiken + + + + TURN Password + TURN-paswoord + + + + TURN Username + TURN-gebruikersnaam + + + + TURN Address + TURN-adres + + + + Media + Media + + + + Enable Video + Video inschakelen + + + + Audio Codecs + Audiocodecs + + + + Video Codecs + Videocodecs + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Audiobestanden (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formulier + + + + BannedItemWidget + + + Form + Formulier + + + + name + naam + + + + id + ID + + + + CallWidget + + + Show conversations + Gesprekken tonen + + + + Conversations + Gesprekken + + + + Search contact text input + Tekstingave contact zoeken + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami is vrije software voor universele communicatie, die de vrijheden en privacy van zijn gebruikers eerbiedigt. + + + + + This is your ID. +Copy and share it with your friends! + + + Dit is uw ID. +Kopieer en deel hem met uw vrienden! + + + + Show ring ID QR code + Toon QR-code voor Ring-ID + + + + Share ring ID button + Knop Ring-ID delen + + + + Double-click to copy + Dubbelklik om te kopiëren + + + + Error while generating QR Code + Fout bij het aanmaken van QR-code + + + + + best name + beste naam + + + + best Id + beste ID + + + + Back to homepage button + Knop terug naar startblad + + + + Add to contacts + Toevoegen aan contacten + + + + Show invites + Uitnodigingen tonen + + + + Invites + Uitnodigingen + + + + + Find a new or existing contact + Zoek een nieuw of bestaand contact + + + + Wants to talk to you! + Wilt met u praten! + + + + Answer incoming call button + Knop inkomende oproep beantwoorden + + + + Ignore incoming call button + Knop inkomende oproep negeren + + + + Answer + Beantwoorden + + + + Ignore + Negeren + + + + Cancel outgoing call + Uitgaande oproep annuleren + + + + Cancel + Annuleren + + + + Start video call + Video-oproep starten + + + + Start audio call + Audio-oproep starten + + + + Clear conversation + Gesprek wissen + + + + Remove contact + Contact verwijderen + + + + Block contact + Contact blokkeren + + + + Copy number + Nummer kopiëren + + + + Search your received invitations + Zoek naar uw ontvangen uitnodigingen + + + + Contact me on Jami + Contacteer mij op Jami + + + + My Id is : + Mijn ID is: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Inkomende oproep van %1 + + + + DeleteAccountDialog + + + Account deletion + Accountverwijdering + + + + Do you really want to delete the following account? + Weet u zeker dat u de volgende account wilt verwijderen? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Als deze account nog niet geëxporteerd is of nog niet toegevoegd is aan een ander apparaat, zal hij onherroepelijk verloren gaan + + + + Permanently delete account + Account voorgoed verwijderen + + + + Delete + Verwijderen + + + + Cancel account deletion + Accountverwijdering annuleren + + + + Cancel + Annuleren + + + + DeviceItemWidget + + + Form + Formulier + + + + Device Id + Apparaats-ID + + + + this device + dit apparaat + + + + InviteButtonsWidget + + + Accept + Aanvaarden + + + + Refuse + Afwijzen + + + + Block + Blokkeren + + + + LinkDeviceDialog + + + Dialog + Dialoog + + + + Enter your account password + Geef uw accountpaswoord in + + + + Password + Paswoord + + + + Ok + Oké + + + + Cancel + Annuleren + + + + Exporting account + Account wordt geëxporteerd + + + + Your PIN is + Uw pincode is + + + + PIN + Pincode + + + + This pin and the account password should be entered in your device within 10 minutes. + U moet deze pincode en het accountpaswoord binnen de 10 minuten op uw nieuwe apparaat ingeven. + + + + Close + Sluiten + + + + Link Another Device + Een ander apparaat koppelen + + + + Incorrect password + Verkeerd paswoord + + + + Something went wrong. +Please try again later. + Er is iets misgegaan. +Herprobeer het later. + + + + MainWindow + + + Settings + Instellingen + + + + Exit + Afsluiten + + + + About + Over + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Stel geregistreerde naam in + + + + Enter your account password + Geef uw accountpaswoord in + + + + Password text input + Paswoordtekstingave + + + + Password text entry + Paswoordtekstingave + + + + Password + Paswoord + + + + Register + Registreren + + + + Cancel + Annuleren + + + + Registering Name + Naam wordt geregistreerd + + + + Something went wrong + Er is iets misgegaan + + + + Close + Sluiten + + + + Incorrect password + Verkeerd paswoord + + + + Network error + Netwerkfout + + + + NewWizardWidget + + + Form + Formulier + + + + Welcome Label + Welkomstlabel + + + + Welcome to + Welkom bij + + + + Welcome Logo + Welkomstlogo + + + + Create Jami account button + Knop Jami-account aanmaken + + + + Push button for Jami account creation start trigger + Trekker voor starten van aanmaken van Jami-account na druk op knop + + + + Create a Jami account + Een Jami-account aanmaken + + + + Link device button + Knop apparaat koppelen + + + + Push button for device linkage start trigger + Trekker voor koppelen van apparaat na druk op knop + + + + Link this device to an account + Dit apparaat aan een account koppelen + + + + Create Jami SIP account button + Knop Jami-SIP-account aanmaken + + + + Push button for Jami SIP account creation start trigger + Trekker voor starten van aanmaken van Jami-SIP-account na druk op knop + + + + Create a SIP account + Maak een SIP-account aan + + + + Link this device to an existing account + Dit apparaat aan een bestaande account koppelen + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Om dit apparaat aan een andere account te koppelen, moet u eerst </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">een pincode verkrijgen</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">. Om de pincode te genereren:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Ga naar de </span><span style=" font-size:14px; font-weight:600;">Accountbeheerinstellingen</span><span style=" font-size:14px;"> van een vorig apparaat</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Kies de </span><span style=" font-size:14px; font-weight:600;">Jami-account</span><span style=" font-size:14px;"> die u wilt gebruiken</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Ga naar het tabblad </span><span style=" font-size:14px; font-weight:600;">Apparaten</span><span style=" font-size:14px;"></span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Selecteer </span><span style=" font-size:14px; font-weight:600;">Een apparaat toevoegen</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">U zult de pincode krijgen die u nodig heeft om dit proces te voltooien. De pincode is slechts </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minuten</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;"> geldig.</span></p></body></html> + + + + Enter your pin: + Geef uw pincode in: + + + + Or import a file: + Of importeer een bestand: + + + + Link from exported account archive file + Koppelen van geëxporteerd accountarchiefbestand + + + + + + (None) + (geen) + + + + Password: + Paswoord: + + + + + Profile + Profiel + + + + + Profile name + Profielnaam + + + + Account + Account + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Registreer uw gebruikersnaam. +Dit zal de gebruikersnaam registreren, zodat enkel u ze kunt gebruiken. +Uw vrienden zullen u kunnen bellen met uw gebruikersnaam +in plaats van uw Ring-ID. + + + + Public username checkbox + Selectievakje openbare gebruikersnaam + + + + Checkbox selecting if the user wants a public username + Selectievakje om te kiezen of de gebruiker een openbare gebruikersnaam wil + + + + Register public username + Registreer een openbare gebruikersnaam + + + + Public username edit + Bewerken openbare gebruikersnaam + + + + Choose your username + Kies uw gebruikersnaam + + + + Password text input + Paswoordtekstingave + + + + Password text entry + Paswoordtekstingave + + + + + Password + Paswoord + + + + + Password confirmation text input + Paswoordbevestigingstekstingave + + + + Confirm password + Bevestig paswoord + + + + SIP Account + SIP-account + + + + SIP Server edit + SIP-server bewerken + + + + Server + Server + + + + SIP proxy input + SIP-proxy-ingave + + + + SIP proxy text entry + SIP-proxy-tekstingave + + + + Proxy + Proxy + + + + SIP username input + SIP-gebruikersnaamingave + + + + SIP Password text entry + SIP-paswoord-tekstingave + + + + Username + Gebruikersnaam + + + + + SIP Password text input + SIP-paswoord-tekstingave + + + + Generating your Jami account… + Uw Jami-account wordt aangemaakt… + + + + Previous page button + Knop vorig blad + + + + push button to access previous page of wizard + druk op de knop om naar het vorige blad van de assistent te gaan + + + + Previous + Vorige + + + + Cancel account create/link + Aanmaken/koppelen van account annuleren + + + + push button to cancel account creation or linking + trekker voor annuleren van aanmaken of verwijderen van account + + + + Back + Terug + + + + Next page Button + Knop volgend blad + + + + Push button to access next page of wizard + Druk op de knop om naar het volgende blad van de assistent te gaan + + + + Next + Volgende + + + + Open File + Bestand openen + + + + Jami archive files (*.gz); All files (*) + Jami-archiefbestanden (*.gz); Alle bestanden (*) + + + + Your account needs to be migrated. Enter your password. + Uw account moet gemigreerd worden. Geef uw paswoord in. + + + + Migrating your Jami account... + Uw Jami-account wordt gemigreerd… + + + + Importing account archive... + Accountarchief importeren… + + + + Generating your Jami account... + Uw Jami-account wordt aangemaakt… + + + + Generating your SIP account... + Uw SIP-account wordt gegenereerd… + + + + Error creating account + Fout bij aanmaken van account + + + + PasswordDialog + + + Change Account Password + Accountpaswoord wijzigen + + + + Enter Current Password + Geef huidig paswoord in + + + + Enter New Password + Geef nieuw paswoord in + + + + Confirm New Password + Bevestig nieuw paswoord + + + + Confirm + Bevestigen + + + + Cancel + Annuleren + + + + Current Password Incorrect + Huidig paswoord is verkeerd + + + + PhotoBoothDialog + + + Photobooth + Fotoautomaat + + + + PhotoboothWidget + + + Form + Formulier + + + + Photobooth display + Fotoautomaatweergave + + + + Choose File + Bestand kiezen + + + + Image Files (*.jpg *.jpeg *.png) + Afbeeldingsbestanden (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Bestand kiezen + + + + Files (*) + Bestanden (*) + + + + QObject + + + No default mail client found + Geen standaard-e-mailprogramma gevonden + + + + + Edit Device Name + Apparaatnaam bewerken + + + + Unlink Device From Account + Apparaat ontkoppelen van account + + + + Save new device name + Nieuwe apparaatsnaam opslaan + + + + Add as contact + Toevoegen aan contacten + + + + RingButton + + + Select folder + Kies een map + + + + SettingsWidget + + + Form + Formulier + + + + Settings + Instellingen + + + + Account + Account + + + + + General + Algemeen + + + + + Audio / Video + Audio/video + + + + System + Systeem + + + + Enable desktop notifications + Bureaubladmeldingen inschakelen + + + + Keep minimized on close + Minimaliseren bij sluiten + + + + Download folder + Downloadmap + + + + Save in + Opslaan in + + + + Always Recording + Altijd opnemen + + + + Updates + Updates + + + + Check for updates automatically every + Automatisch controleren op updates elke + + + + Interval between update checks in days selector + Selectie interval in dagen tussen controles op updates + + + + days + dagen + + + + Check for updates now button + Knop nu controleren op updates + + + + Check for updates now + Nu controleren op updates + + + + Password + Paswoord + + + + + Enable + Inschakelen + + + + toggle enable notifications + schakelaar meldingen inschakelen + + + + Toggle keep minimized on close + Schakelaar minimaliseren bij sluiten + + + + Call Recordings + Gespreksopnames + + + + Toggle automatic updates + Schakelaar automatische updates + + + + Jami Account + Jami-account + + + + + Profile + Profiel + + + + + Identity + Identiteit + + + + Id + ID + + + + Registered name + Geregistreerde naam + + + + Type here to register a username + Typ hier om een gebruikersnaam te registreren + + + + Register + Registreren + + + + Change Password + Paswoord wijzigen + + + + Export Account + Account exporteren + + + + + Delete Account + Account verwijderen + + + + Linked Devices + Gekoppelde apparaten + + + + Link Another Device + Een ander apparaat koppelen + + + + Banned Contacts + Geblokkeerde contacten + + + + Format + Formaat + + + + Video device framerate selector + Framerateselectie video-apparaat + + + + Preview unavailable + Voorbeeld onbeschikbaar + + + + + Advanced Account Settings + Geavanceerde accountinstellingen + + + + SIP Account + SIP-account + + + + Username + Gebruikersnaam + + + + Hostname + Hostnaam + + + + Proxy + Proxy + + + + Audio + Geluid + + + + Microphone + Microfoon + + + + Audio input device selector + Selectie audio-invoerapparaat + + + + Output Device + Uitvoerapparaat + + + + Choose the output device + Kies het uitvoerapparaat + + + + Video + Video + + + + Device + Apparaat + + + + Video device selector + Selectie video-apparaat + + + + A registered name should not have any spaces and must be at least three letters long + Een geregistreerde naam mag geen spaties bevatten, en moet minstens drie tekens lang zijn + + + + This name is already taken + Deze naam is reeds in gebruik + + + + + Enter an alias + Geef een alias in + + + + Register this name + Deze naam registreren + + + + Remove Device + Apparaat verwijderen + + + + Enter this account's password to confirm the removal of this device + Geef het paswoord van deze account in om het verwijderen van dit apparaat te bevestigen + + + + Are you sure you wish to remove this device? + Weet u zeker dat u dit apparaat wilt verwijderen? + + + + Export Account Here + Account hier exporteren + + + + Select A Folder For Your Downloads + Selecteer een map voor uw downloads + + + + Select A Folder For Your Recordings + Kies een map voor uw opnamen + + + + VideoOverlay + + + Call on Hold + Telefoongesprek in wacht + + + + Hold / Unhold + In wacht zetten / uit wacht halen + + + + Chat + Chat + + + + Mute Mic + Microfoon dempen + + + + Record call + Gesprek opnemen + + + + Name label + Naamlabel + + + + Time elapsed + Tijd verstreken + + + + 00:00 + 00:00 + + + + Hangup + Ophangen + + + + Mute Video + Video uitschakelen + + + + VideoView + + + Share entire screen + Scherm delen + + + + Share screen area + Schermgebied delen + + + + Share file + Bestand delen + + + \ No newline at end of file diff --git a/translations/ring_client_windows_nl_NL.ts b/translations/ring_client_windows_nl_NL.ts new file mode 100644 index 00000000..d9457e98 --- /dev/null +++ b/translations/ring_client_windows_nl_NL.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + Beveiliging + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Voeg toe aan contacten + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + Kopieër nummer + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Verwijderen + + + + Cancel account deletion + + + + + Cancel + + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Wachtwoord + + + + Ok + + + + + Cancel + + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Instellingen + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Wachtwoord + + + + Register + Registreren + + + + Cancel + + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Wachtwoord + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Gebruikersnaam + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Instellingen + + + + Account + + + + + + General + Algemeen + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Wachtwoord + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Registreren + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Gebruikersnaam + + + + Hostname + + + + + Proxy + + + + + Audio + Geluid + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Beeld + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_oc.ts b/translations/ring_client_windows_oc.ts new file mode 100644 index 00000000..0a5d2e50 --- /dev/null +++ b/translations/ring_client_windows_oc.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + A prepaus + + + + about button + + + + + credits button + + + + + Credits + Crèdit + + + + Free as in Freedom + Liure coma la libertat + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Connectivitat + + + + STUN Address + + + + + Use STUN + Utilizar STUN + + + + Use UPnP + + + + + Use TURN + Utilizar TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Mèdia + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adreça + + + + OpenDHT Configuration + + + + + Enable Proxy + Activar lo servidor mandatari + + + + Bootstrap + + + + + Security + Seguretat + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Connectivitat + + + + Use STUN + Utilizar STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Utilizar TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Mèdia + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Discutidas + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami es un logicial liure per comunicacions universalas que respèctan la libertat e la vida privada de sos utilizaires. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + Ignorar + + + + Cancel outgoing call + + + + + Cancel + anullar + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + Blocar contacte + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Suprimir + + + + Cancel account deletion + + + + + Cancel + anullar + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Acceptar + + + + Refuse + Refusar + + + + Block + Blocar + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Senha + + + + Ok + D’acòrdi + + + + Cancel + anullar + + + + Exporting account + + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + Marrit senhal + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Paramètres + + + + Exit + + + + + About + A prepaus + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Senha + + + + Register + + + + + Cancel + anullar + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + Marrit senhal + + + + Network error + Error ret + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Crear un compte Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Associar aqueste aparelh a un compte + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + (Cap) + + + + Password: + + + + + + Profile + Perfil + + + + + Profile name + + + + + Account + Compte + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Senha + + + + + Password confirmation text input + + + + + Confirm password + Confirmar lo senhal + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Servidor mandatari + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Nom d’utilizaire + + + + + SIP Password text input + + + + + Generating your Jami account… + Generacion del compte Jami… + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Precedent + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Tornar + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Seguent + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + anullar + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Paramètres + + + + Account + Compte + + + + + General + General + + + + + Audio / Video + + + + + System + Sistèma + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + Dossièr de telecargament + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Senha + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Perfil + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Nom d’utilizaire + + + + Hostname + + + + + Proxy + Servidor mandatari + + + + Audio + Àudio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Vidèo + + + + Device + Periferic + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + Conversacion + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_pa.ts b/translations/ring_client_windows_pa.ts new file mode 100644 index 00000000..1ab8496f --- /dev/null +++ b/translations/ring_client_windows_pa.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + ਸ਼ਲਾਘਾਯੋਗ ਵਿਅਕਤੀ + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + TURN ਵਰਤੋ + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + ਮੀਡੀਆ + + + + Enable Video + ਵੀਡੀਓ ਚਾਲੂ ਕਰੋ + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + ਬੂਟਸਟ੍ਰੈਪ + + + + Security + ਸੁਰੱਖਿਆ + + + + Private Key Password + ਨਿੱਜੀ ਕੁੰਜੀ ਪਾਸਵਰਡ + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + TURN ਵਰਤੋ + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + ਮੀਡੀਆ + + + + Enable Video + ਵੀਡੀਓ ਚਾਲੂ ਕਰੋ + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + ਗੱਲਾਂਬਾਤਾਂ + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami ਦੁਨੀਆ ਭਰ ਵਿੱਚ ਸੰਚਾਰ ਕਰਨ ਲਈ ਮੁਫ਼ਤ ਸਾਫ਼ਟਵੇਅਰ ਹੈ ਜੋ ਆਪਣੇ ਵਰਤੋਂਕਾਰਾਂ ਦੀ ਅਜ਼ਾਦੀ ਅਤੇ ਪਰਦੇਦਾਰੀ ਦਾ ਖਿਆਲ ਰੱਖਦਾ ਹੈ। + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + ਸੰਪਰਕਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + ਰੱਦ ਕਰੋ + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + ਸੰਪਰਕ ਬਲਾਕ ਕਰੋ + + + + Copy number + ਨੰਬਰ ਕਾਪੀ ਕਰੋ + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + ਮਿਟਾਓ + + + + Cancel account deletion + + + + + Cancel + ਰੱਦ ਕਰੋ + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + ਇਹ ਡੀਵਾਈਸ + + + + InviteButtonsWidget + + + Accept + ਸਵੀਕਾਰ ਕਰੋ + + + + Refuse + ਅਸਵੀਕਾਰ ਕਰੋ + + + + Block + ਬਲਾਕ ਕਰੋ + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + ਪਾਸਵਰਡ + + + + Ok + ਠੀਕ ਹੈ + + + + Cancel + ਰੱਦ ਕਰੋ + + + + Exporting account + + + + + Your PIN is + + + + + PIN + ਪਿੰਨ + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + ਸੈਟਿੰਗ + + + + Exit + + + + + About + + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + ਪਾਸਵਰਡ + + + + Register + ਪੰਜੀਕਰਨ ਕਰੋ + + + + Cancel + ਰੱਦ ਕਰੋ + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + ਨੈੱਟਵਰਕ ਗੜਬੜ + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Jami ਖਾਤਾ ਬਣਾਓ + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + ਇਹ ਡੀਵਾਈਸ ਕਿਸੇ ਖਾਤੇ ਨਾਲ ਲਿੰਕ ਕਰੋ + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + ਖਾਤਾ + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + ਪਾਸਵਰਡ + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + ਪ੍ਰੌਕਸੀ + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + ਵਰਤੋਂਕਾਰ ਨਾਂ + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + ਪਿੱਛੇ + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + ਅੱਗੇ + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + ਰੱਦ ਕਰੋ + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + ਸੈਟਿੰਗ + + + + Account + ਖਾਤਾ + + + + + General + ਸਧਾਰਨ + + + + + Audio / Video + + + + + System + ਸਿਸਟਮ + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + ਪਾਸਵਰਡ + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + ਪਛਾਣ + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + ਪੰਜੀਕਰਨ ਕਰੋ + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + ਵਰਤੋਂਕਾਰ ਨਾਂ + + + + Hostname + ਹੋਸਟਨਾਮ + + + + Proxy + ਪ੍ਰੌਕਸੀ + + + + Audio + ਆਡੀਓ + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + ਵੀਡੀਓ + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + ਫ਼ਾਈਲ ਸਾਂਝੀ ਕਰੋ + + + \ No newline at end of file diff --git a/translations/ring_client_windows_pl.ts b/translations/ring_client_windows_pl.ts new file mode 100644 index 00000000..794aee8c --- /dev/null +++ b/translations/ring_client_windows_pl.ts @@ -0,0 +1,1604 @@ + + + AboutDialog + + + + About + O programie + + + + about button + przycisk o programie + + + + credits button + przycisk zasługi + + + + Credits + Podziękowania + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Klient Jami dla Microsoft Windows. +Jami jest wolnym oprogramowaniem do uniwersalnej komunikacji, respektującym wolność i prywatność swoich użytkowników. + + + + version + Wersja + + + + Created by: + Stworzone przez: + + + + Artwork by: + Obrazki przez: + + + + Based on the SFLPhone project + Na podstawie projektu SFLPhone + + + + AccountItemDelegate + + + Add Account + Dodaj Konto + + + + AdvancedSIPSettingsWidget + + + Form + Forma + + + + Call Settings + Ustawienia Połączeń + + + + Auto Answer Calls + Automatycznie Odbieraj Połączenia + + + + Enable Custom Ringtone + Użyj Własnego Dzwonka + + + + Connectivity + Łączność + + + + STUN Address + Adres STUN + + + + Use STUN + Użyj STUN + + + + Use UPnP + Użyj UPnP + + + + Use TURN + Użyj TURN + + + + TURN Password + Hasło TURN + + + + TURN Username + Nazwa użytkownika TURN + + + + TURN Address + Adres TURN + + + + Media + Multimedia + + + + Enable Video + Włącz video + + + + Video Codecs + Kodeki Wideo + + + + Audio Codecs + Kodeki Audio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Pliki Audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Dodaj własny dzwonek + + + + AdvancedSettingsWidget + + + Form + Forma + + + + Call Settings + Ustawienia Połączeń + + + + Allow Calls From Untrusted Peers + Zezwól Na Połączenia Od Niezaufanych Użytkowników + + + + Auto Answer Calls + Automatycznie Odbieraj Połączenia + + + + Enable Custom Ringtone + Użyj Własnego Dzwonka + + + + Add a custom ringtone + Dodaj własny dzwonek + + + + Name Server + Serwer Nazw + + + + Address + Adres + + + + OpenDHT Configuration + Konfiguracja OpenDHT + + + + Enable Proxy + Włącz proxy + + + + Bootstrap + Bootstrap + + + + Security + Zabezpieczenia + + + + Private Key Password + Hasło Klucza Prywatnego + + + + User Certificate + Certyfikat Użytkownika + + + + Private Key + Klucz Prywatny + + + + CA Certificate + Certyfikat CA + + + + Connectivity + Łączność + + + + Use STUN + Użyj STUN + + + + STUN Address + Adres STUN + + + + Use UPnP + Użyj UPnP + + + + Use TURN + Użyj TURN + + + + TURN Password + Hasło TURN + + + + TURN Username + Nazwa użytkownika TURN + + + + TURN Address + Adres TURN + + + + Media + Multimedia + + + + Enable Video + Włącz video + + + + Audio Codecs + Kodeki Audio + + + + Video Codecs + Kodeki Wideo + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Pliki Audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Forma + + + + BannedItemWidget + + + Form + Forma + + + + name + nazwa + + + + id + ID + + + + CallWidget + + + Show conversations + Pokaż rozmowy + + + + Conversations + Rozmowy + + + + Search contact text input + Pole szukania kontaktów + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami jest wolnym oprogramowaniem do uniwersalnej komunikacji, respektującym wolności i prywatność swoich użytkowników. + + + + + This is your ID. +Copy and share it with your friends! + + +To jest twoje ID. +Skopiuj i podziel się ze swoimi znajomymi! + + + + Show ring ID QR code + Pokaż kod QR Jami ID + + + + Share ring ID button + Guzik dzielenia się ID Jami + + + + Double-click to copy + Kliknij dwukrotnie, aby skopiować + + + + Error while generating QR Code + Wystąpił błąd podczas generowania kodu QR. + + + + + best name + najlepsza nazwa + + + + best Id + Najlepsze ID + + + + Back to homepage button + Guzik powrotu do strony głównej + + + + Add to contacts + Dodaj do kontaków + + + + Show invites + Pokaż zaproszenia + + + + Invites + Zaprasza + + + + + Find a new or existing contact + Szukaj nowego lub istniejącego kontaktu + + + + Wants to talk to you! + Chce z tobą rozmawiać! + + + + Answer incoming call button + Guzik odbierania przychodzących rozmów + + + + Ignore incoming call button + Guzik ignorowania nadchodzących połączeń + + + + Answer + Odbierz + + + + Ignore + Zignoruj + + + + Cancel outgoing call + Anuluj połączenie wychodzące + + + + Cancel + Anuluj + + + + Start video call + Rozpocznij rozmowę wideo + + + + Start audio call + Rozpocznij połączenie głosowe + + + + Clear conversation + Wyczyść rozmowę + + + + Remove contact + Usuń kontakt + + + + Block contact + Blokuj kontakt + + + + Copy number + Skopiuj numer + + + + Search your received invitations + Szukaj odebranych zaproszeń + + + + Contact me on Jami + Skontaktuj się ze mną na Jami + + + + My Id is : + Mój identyfikator to: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Połączenie przychodzące od %1 + + + + DeleteAccountDialog + + + Account deletion + Kasowanie konta + + + + Do you really want to delete the following account? + Czy na pewno chcesz usunąć następujące konto? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Jeżeli to konto nie zostało wyeksportowane, lub dodane do innego urządzenia, zostanie bezpowrotnie utracone. + + + + Permanently delete account + Usuń konto na zawsze + + + + Delete + Usuń + + + + Cancel account deletion + Anuluj usuwanie konta + + + + Cancel + Anuluj + + + + DeviceItemWidget + + + Form + Forma + + + + Device Id + ID Urządzenia + + + + this device + to urządzenie + + + + InviteButtonsWidget + + + Accept + Akceptuj + + + + Refuse + Odmów + + + + Block + Blokuj + + + + LinkDeviceDialog + + + Dialog + Dialog + + + + Enter your account password + Wprowadź hasło do twojego konta + + + + Password + Hasło + + + + Ok + Ok + + + + Cancel + Anuluj + + + + Exporting account + Eksportuję konto + + + + Your PIN is + Twój PIN to + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Ten pin i hasło do konta powinny zostać wprowadzone na twoim urządzeniu w ciągu 10 minut. + + + + Close + Zamknij + + + + Link Another Device + Połącz Inne Urządzenie + + + + Incorrect password + Niepoprawne hasło + + + + Something went wrong. +Please try again later. + Coś poszło nie tak. +Proszę, spróbuj ponownie. + + + + MainWindow + + + Settings + Ustawienia + + + + Exit + Wyjdź + + + + About + O + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Ustaw Zarejestrowaną Nazwę + + + + Enter your account password + Wprowadź hasło do twojego konta + + + + Password text input + Pole tekstowe hasła + + + + Password text entry + Wpis tekstu hasła + + + + Password + Hasło + + + + Register + Zarejestruj + + + + Cancel + Anuluj + + + + Registering Name + Rejestrowanie Nazwy + + + + Something went wrong + Coś poszło nie tak + + + + Close + Zamknij + + + + Incorrect password + Niepoprawne hasło + + + + Network error + Błąd sieci + + + + NewWizardWidget + + + Form + Forma + + + + Welcome Label + Naklejka "Witaj" + + + + Welcome to + Witaj! + + + + Welcome Logo + Logo Powitania + + + + Create Jami account button + Guzik tworzenia konta Jami + + + + Push button for Jami account creation start trigger + Guzik do rozpoczęcia tworzenia konta Jami + + + + Create a Jami account + Stwórz konto Jami + + + + Link device button + Guzik łączenia urządzenia + + + + Push button for device linkage start trigger + Guzik do rozpoczęcia połączenia urządzenia + + + + Link this device to an account + Połącz to urządzenie z kontem + + + + Create Jami SIP account button + Guzik tworzenia konta SIP Jami + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + Stwórz konto SIP + + + + Link this device to an existing account + Połącz to urządzenie z istniejącym kontem + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Aby połączyć to urządzenie z innym kątem, najpierw</span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">musisz pozyskać kod PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> Aby wygenerować kod PIN:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Przejdź do </span><span style=" font-size:14px; font-weight:600;">Ustawień zarządzenia kontem</span><span style=" font-size:14px;"> na poprzednim urządzeniu</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Wybierz </span><span style=" font-size:14px; font-weight:600;">Konto Jami,</span><span style=" font-size:14px;"> z którego chcesz korzystać</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Przejdź do</span><span style=" font-size:14px; font-weight:600;">Karty urządzeń</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Zaznacz </span><span style=" font-size:14px; font-weight:600;">Dodaj urządzenie</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Będziesz potrzebować kodu PIN, aby uzupełnić ten formularz. Kod PIN jest ważny przez</span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minut</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Wpisz swój PIN: + + + + Or import a file: + Lub zaimportuj plik: + + + + Link from exported account archive file + Połącz z wyeksportowanego pliku archiwum + + + + + + (None) + (Brak) + + + + Password: + Hasło: + + + + + Profile + Profil + + + + + Profile name + Nazwa profilu + + + + Account + Konto + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Zarejestruj swoją nazwę użytkownika. +Ta akcja zarezerwuje nazwę użytkownika, tak, że tylko ty będziesz w stanie z niej korzystać. +Twoi znajomi będą w stanie kontaktować się za pomocą tej nazwy użytkownika zamiast używać twojego ID. + + + + Public username checkbox + Checkbox publicznej nazwy użytkownika + + + + Checkbox selecting if the user wants a public username + Checkbox zaznaczający, jeśli użytkownik chce mieć publiczną nazwę użytkownika + + + + Register public username + Zarejestruj publiczną nazwę użytkownika + + + + Public username edit + Edycja publicznej nazwy użytkownika + + + + Choose your username + Wybierz swoją nazwę użytkownika + + + + Password text input + Pole tekstowe hasła + + + + Password text entry + Wpis tekstu hasła + + + + + Password + Hasło + + + + + Password confirmation text input + Pole potwierdzenia hasła + + + + Confirm password + Potwierdź hasło + + + + SIP Account + Konto SIP + + + + SIP Server edit + Edycja Serwera SIP + + + + Server + Serwer + + + + SIP proxy input + Pole wprowadzenia proxy dla SIP + + + + SIP proxy text entry + + + + + Proxy + Pełnomocnik + + + + SIP username input + Wprowadź nazwę użytkownika SIP + + + + SIP Password text entry + + + + + Username + Nazwa użytkownika + + + + + SIP Password text input + + + + + Generating your Jami account… + Tworzenie konta Jami... + + + + Previous page button + Guzik poprzedniej strony + + + + push button to access previous page of wizard + guzik dostępu do poprzedniej strony instalatora + + + + Previous + Poprzednie + + + + Cancel account create/link + Anuluj konto stwórz/połącz + + + + push button to cancel account creation or linking + guzik do anulowania tworzenia lub łączenia konta + + + + Back + Wstecz + + + + Next page Button + Guzik następnej strony + + + + Push button to access next page of wizard + Guzik dostępu do następnej strony instalatora + + + + Next + Następne + + + + Open File + Otwórz plik + + + + Jami archive files (*.gz); All files (*) + Pliki archiwum Jami (*.gz); Wszystkie pliki (*) + + + + Your account needs to be migrated. Enter your password. + Twoje konto musi zostać zaktualizowane. Wprowadź swoje hasło. + + + + Migrating your Jami account... + Migrowanie twojego kąta Jami... + + + + Importing account archive... + Importowanie archiwum konta... + + + + Generating your Jami account... + Generowanie twojego konta Jami... + + + + Generating your SIP account... + + + + + Error creating account + Błąd podczas tworzenia konta + + + + PasswordDialog + + + Change Account Password + Zmień Hasło Konta + + + + Enter Current Password + Wprowadź Obecne Hasło + + + + Enter New Password + Wprowadź Nowe Hasło + + + + Confirm New Password + Potwierdź Nowe Hasło + + + + Confirm + Potwierdź + + + + Cancel + Anuluj + + + + Current Password Incorrect + Bieżące Hasło Jest Niepoprawne + + + + PhotoBoothDialog + + + Photobooth + Budka fotograficzna + + + + PhotoboothWidget + + + Form + Forma + + + + Photobooth display + Widok z przodu + + + + Choose File + Wybierz Plik + + + + Image Files (*.jpg *.jpeg *.png) + Pliki Obrazków (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Wybierz Plik + + + + Files (*) + Pliki (*) + + + + QObject + + + No default mail client found + Nie znaleziono domyślnego klienta poczty + + + + + Edit Device Name + Edytuj Nazwę Urządzenia + + + + Unlink Device From Account + Odłącz Urządzenie Od Konta + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + Wybierz folder + + + + SettingsWidget + + + Form + Forma + + + + Settings + Ustawienia + + + + Account + Konto + + + + + General + Generalne + + + + + Audio / Video + Audio / Wideo + + + + System + System + + + + Enable desktop notifications + Zezwól na powiadomienia na pulpicie + + + + Keep minimized on close + Przechowaj zminimalizowane przy zamknięciu + + + + Download folder + Folder pobrane + + + + Save in + Zapisz w + + + + Always Recording + Zawsze Nagrywaj + + + + Updates + Aktualizacje + + + + Check for updates automatically every + Sprawdzaj automatycznie aktualizacje co + + + + Interval between update checks in days selector + Pole wyboru interwału pomiędzy sprawdzaniem aktualizacji w dniach + + + + days + dni + + + + Check for updates now button + Guzik sprawdzenia aktualizacji teraz + + + + Check for updates now + Sprawdź aktualizacje teraz + + + + Password + Hasło + + + + + Enable + Włącz + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + Tożsamość + + + + Id + Id + + + + Registered name + Zarejestrowana nazwa + + + + Type here to register a username + Wpisz tutaj, by zarejestrować nazwę użytkownika + + + + Register + Zarejestruj + + + + Change Password + Zmień Hasło + + + + Export Account + Wyeksportuj konto + + + + + Delete Account + Usuń Konto + + + + Linked Devices + Połączone Urządzenia + + + + Link Another Device + Połącz Inne Urządzenie + + + + Banned Contacts + Zablokowane Kontakty + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + Zaawansowane Ustawienia Konta + + + + SIP Account + Konto SIP + + + + Username + Nazwa użytkownika + + + + Hostname + Nazwa hosta + + + + Proxy + Pełnomocnik + + + + Audio + Audio + + + + Microphone + Mikrofon + + + + Audio input device selector + Pole wyboru urządzenia wejściowego audio + + + + Output Device + Urządzenie Wyjściowe + + + + Choose the output device + Wybierz urządzenie wyjściowe + + + + Video + Wideo + + + + Device + Urządzenie + + + + Video device selector + Pole wyboru urządzenia wideo + + + + A registered name should not have any spaces and must be at least three letters long + Zarejestrowana nazwa powinna nie zawierać żadnych spacji i musi mieć przynajmniej 3 litery długości + + + + This name is already taken + Ta nazwa jest już zajęta + + + + + Enter an alias + + + + + Register this name + Zarejestruj tą nazwę + + + + Remove Device + Usuń Urządzenie + + + + Enter this account's password to confirm the removal of this device + Wprowadź hasło do tego konta, aby potwierdzić usunięcie tego urządzenia + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + Wyeksportuj Konto Tutaj + + + + Select A Folder For Your Downloads + Wybierz Folder Dla Twoich Pobranych + + + + Select A Folder For Your Recordings + Wybierz Folder Dla Twoich Nagrań + + + + VideoOverlay + + + Call on Hold + Rozmowa jest Zawieszona + + + + Hold / Unhold + Zawiesić / Wznowić + + + + Chat + Czat + + + + Mute Mic + Wyłącz mikrofon + + + + Record call + Nagraj Rozmowę + + + + Name label + Etykieta Nazwiska + + + + Time elapsed + Czas się skończył + + + + 00:00 + 00:00 + + + + Hangup + Odłożyć słuchawkę + + + + Mute Video + Wyłącz video + + + + VideoView + + + Share entire screen + Podziel się obszarem ekranu + + + + Share screen area + Podziel się obszar ekranu + + + + Share file + Podziel się plikiem + + + \ No newline at end of file diff --git a/translations/ring_client_windows_pl_PL.ts b/translations/ring_client_windows_pl_PL.ts new file mode 100644 index 00000000..9bcf83e4 --- /dev/null +++ b/translations/ring_client_windows_pl_PL.ts @@ -0,0 +1,1590 @@ + + + AboutDialog + + + + About + O programie + + + + about button + przycisk o programie + + + + credits button + przycisk zasługi + + + + Credits + Podziękowania + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Klient Jami dla Microsoft Windows. +Jami jest wolnym oprogramowaniem do uniwersalnej komunikacji, respektującym wolność i prywatność swoich użytkowników. + + + + version + Wersja + + + + Created by: + Stworzone przez: + + + + Artwork by: + Obrazki przez: + + + + Based on the SFLPhone project + Na podstawie projektu SFLPhone + + + + AccountItemDelegate + + + Add Account + Dodaj konto + + + + AdvancedSIPSettingsWidget + + + Form + Forma + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Łączność + + + + STUN Address + + + + + Use STUN + Użyj STUN + + + + Use UPnP + + + + + Use TURN + Użyj TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Multimedia + + + + Enable Video + Włącz video + + + + Video Codecs + + + + + Audio Codecs + Kodeki audio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + Forma + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + Serwer nazw + + + + Address + Adres + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Bootstrap + + + + Security + Zabezpieczenia + + + + Private Key Password + Hasło Klucza Prywatnego + + + + User Certificate + Certyfikat Użytkownika + + + + Private Key + Klucz Prywatny + + + + CA Certificate + + + + + Connectivity + Łączność + + + + Use STUN + Użyj STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Użyj TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Multimedia + + + + Enable Video + Włącz video + + + + Audio Codecs + Kodeki audio + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Forma + + + + BannedItemWidget + + + Form + Forma + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + Pokaż rozmowy + + + + Conversations + Rozmowy + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami jest wolnym oprogramowaniem do uniwersalnej komunikacji, respektującym wolności i prywatność swoich użytkowników. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + Rozpocznij połączenie wideo + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + Wystąpił błąd podczas generowania kodu QR. + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Dodaj do kontaków + + + + Show invites + Pokaż zaproszenia + + + + Invites + Zaproszenia + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Chce z tobą rozmawiać! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Odbierz + + + + Ignore + Zignoruj + + + + Cancel outgoing call + Anuluj wychodzące połączenie + + + + Cancel + Anuluj + + + + Start video call + Rozpocznij rozmowę wideo + + + + Start audio call + Rozpocznij połączenie głosowe + + + + Clear conversation + Wyczyść rozmowę + + + + Remove contact + Usuń kontakt + + + + Block contact + Blokuj kontakt + + + + Copy number + Skopiuj numer + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Połączenie przychodzące od %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Usuń + + + + Cancel account deletion + + + + + Cancel + Anuluj + + + + DeviceItemWidget + + + Form + Forma + + + + Device Id + + + + + this device + to urządzenie + + + + InviteButtonsWidget + + + Accept + Akceptuj + + + + Refuse + Odmów + + + + Block + Blokuj + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Hasło + + + + Ok + Ok + + + + Cancel + Anuluj + + + + Exporting account + + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Zamknij + + + + Link Another Device + + + + + Incorrect password + Nieprawidłowe hasło + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Ustawienia + + + + Exit + Wyjdź + + + + About + O + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Hasło + + + + Register + Zarejestruj + + + + Cancel + Anuluj + + + + Registering Name + + + + + Something went wrong + + + + + Close + Zamknij + + + + Incorrect password + Nieprawidłowe hasło + + + + Network error + Błąd sieci + + + + NewWizardWidget + + + Form + Forma + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Utwórz konto Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Połącz to urządzenie z kontem + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Połącz to urządzenie z istniejącym kontem + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Wpisz swój PIN: + + + + Or import a file: + Lub zaimportuj plik: + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Hasło: + + + + + Profile + Profil + + + + + Profile name + Nazwa profilu + + + + Account + Konto + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Zarejestruj publiczną nazwę użytkownika + + + + Public username edit + + + + + Choose your username + Wybierz swoją nazwę użytkownika + + + + Password text input + + + + + Password text entry + + + + + + Password + Hasło + + + + + Password confirmation text input + Pole potwierdzenia hasła + + + + Confirm password + Potwierdź hasło + + + + SIP Account + Konto SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Pełnomocnik + + + + SIP username input + Wprowadź nazwę użytkownika SIP + + + + SIP Password text entry + + + + + Username + Nazwa użytkownika + + + + + SIP Password text input + + + + + Generating your Jami account… + Tworzenie konta Jami... + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Poprzednie + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Wstecz + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Następne + + + + Open File + Otwórz plik + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Anuluj + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + Budka fotograficzna + + + + PhotoboothWidget + + + Form + Forma + + + + Photobooth display + + + + + Choose File + Wybierz Plik + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Wybierz Plik + + + + Files (*) + Pliki (*) + + + + QObject + + + No default mail client found + Nie znaleziono domyślnego klienta poczty + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + Wybierz folder + + + + SettingsWidget + + + Form + Forma + + + + Settings + Ustawienia + + + + Account + Konto + + + + + General + Generalne + + + + + Audio / Video + + + + + System + System + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + Zapisz w + + + + Always Recording + + + + + Updates + Aktualizacje + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Hasło + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + Tożsamość + + + + Id + + + + + Registered name + Zarejestrowana nazwa + + + + Type here to register a username + + + + + Register + Zarejestruj + + + + Change Password + + + + + Export Account + + + + + + Delete Account + Usuń konto + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + Konto SIP + + + + Username + Nazwa użytkownika + + + + Hostname + Nazwa hosta + + + + Proxy + Pełnomocnik + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + Urządzenie wyjściowe + + + + Choose the output device + + + + + Video + Wideo + + + + Device + Urządzenie + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Rozmowa jest Zawieszona + + + + Hold / Unhold + Zawiesić / Wznowić + + + + Chat + Czat + + + + Mute Mic + Wyłącz mikrofon + + + + Record call + Nagraj rozmowę + + + + Name label + Etykieta Nazwiska + + + + Time elapsed + Czas się skończył + + + + 00:00 + 00:00 + + + + Hangup + Odłożyć słuchawkę + + + + Mute Video + Wyłącz video + + + + VideoView + + + Share entire screen + Podziel się obszarem ekranu + + + + Share screen area + Podziel się obszar ekranu + + + + Share file + Podziel się plikiem + + + \ No newline at end of file diff --git a/translations/ring_client_windows_pt.ts b/translations/ring_client_windows_pt.ts new file mode 100644 index 00000000..bd851d17 --- /dev/null +++ b/translations/ring_client_windows_pt.ts @@ -0,0 +1,1606 @@ + + + AboutDialog + + + + About + Sobre + + + + about button + botão sobre + + + + credits button + botão créditos + + + + Credits + Créditos + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + O cliente Microsoft Windows para o Jami +O Jami é um programa de comunicação distribuído e seguro. + + + + version + versão + + + + Created by: + Criado por: + + + + Artwork by: + Arte por: + + + + Based on the SFLPhone project + Baseado no projeto SFLPhone + + + + AccountItemDelegate + + + Add Account + Adicionar Conta + + + + AdvancedSIPSettingsWidget + + + Form + Formulário + + + + Call Settings + Configurações de chamadas + + + + Auto Answer Calls + Atender chamadas automaticamente + + + + Enable Custom Ringtone + Ativar toque de chamada personalizado + + + + Connectivity + Conectividade + + + + STUN Address + Endereço STUN + + + + Use STUN + Usar STUN + + + + Use UPnP + Usar UPnP + + + + Use TURN + Usar TURN + + + + TURN Password + Palavra-chave TURN + + + + TURN Username + Nome de utilizador TURN + + + + TURN Address + Endereço TURN + + + + Media + Média + + + + Enable Video + Ativar Vídeo + + + + Video Codecs + Codecs de vídeo + + + + Audio Codecs + Codecs de áudio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Ficheiros de áudio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Adicionar um toque de chamada personalizado + + + + AdvancedSettingsWidget + + + Form + Formulário + + + + Call Settings + Configurações de chamadas + + + + Allow Calls From Untrusted Peers + Permitir chamadas de utilizadores desconhecidos + + + + Auto Answer Calls + Atender chamadas automaticamente + + + + Enable Custom Ringtone + Ativar toque de chamada personalizado + + + + Add a custom ringtone + Adicionar um toque de chamada personalizado + + + + Name Server + Nome do servidor + + + + Address + Endereço + + + + OpenDHT Configuration + Configuração OpenDHT + + + + Enable Proxy + Ativar Proxy + + + + Bootstrap + Inicialização + + + + Security + Segurança + + + + Private Key Password + Palavra-chave da Chave Privada + + + + User Certificate + Certificado do Utilizador + + + + Private Key + Chave privada + + + + CA Certificate + Certificado CA + + + + Connectivity + Conectividade + + + + Use STUN + Usar STUN + + + + STUN Address + Endereço STUN + + + + Use UPnP + Usar UPnP + + + + Use TURN + Usar TURN + + + + TURN Password + Palavra-chave TURN + + + + TURN Username + Nome de utilizador TURN + + + + TURN Address + Endereço TURN + + + + Media + Média + + + + Enable Video + Ativar Vídeo + + + + Audio Codecs + Codecs de áudio + + + + Video Codecs + Codecs de vídeo + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Ficheiros de áudio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formulário + + + + BannedItemWidget + + + Form + Formulário + + + + name + nome + + + + id + id + + + + CallWidget + + + Show conversations + Ver conversações + + + + Conversations + Conversas + + + + Search contact text input + Entrada de texto para pesquisa de contacto + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + O Jami é um programa livre para comunicação universal que respeita as liberdades e privacidade dos seus utilizadores. + + + + + This is your ID. +Copy and share it with your friends! + + + Este é o seu ID. +Copie-o e partilhe-o com os seus amigos! + + + + + Show ring ID QR code + Mostrar código QR do seu ID do Ring + + + + Share ring ID button + Botão para partilhar o seu ID do Ring + + + + Double-click to copy + Duplo-clique para copiar + + + + Error while generating QR Code + Erro ao gerar o Código QR + + + + + best name + melhor nome + + + + best Id + melhor Id + + + + Back to homepage button + Botão para voltar à página inicial + + + + Add to contacts + Adicionar aos contactos + + + + Show invites + Mostrar convites + + + + Invites + Convites + + + + + Find a new or existing contact + Procurar um contacto novo ou existente + + + + Wants to talk to you! + Quer conversar consigo! + + + + Answer incoming call button + Botão para atender chamada + + + + Ignore incoming call button + Botão para ignorar chamada + + + + Answer + Resposta + + + + Ignore + Ignorar + + + + Cancel outgoing call + Cancelar chamada efetuada + + + + Cancel + Cancelar + + + + Start video call + Começar chamada de vídeo + + + + Start audio call + Começar chamada de áudio + + + + Clear conversation + Limpar conversação + + + + Remove contact + Remover contacto + + + + Block contact + Bloquear contacto + + + + Copy number + Copiar número + + + + Search your received invitations + Pesquisar os seus convites recebidos + + + + Contact me on Jami + Contacte-me no Jami + + + + My Id is : + O meu ID é: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + A receber chamada de %1 + + + + DeleteAccountDialog + + + Account deletion + Eliminação de conta + + + + Do you really want to delete the following account? + Tem certeza que quer eliminar a seguinte conta? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Se esta conta não foi exportada ou adicionada a outro dispositivo, ela será perdida. + + + + Permanently delete account + Eliminar conta permanentemente + + + + Delete + Eliminar + + + + Cancel account deletion + Cancelar eliminação da conta + + + + Cancel + Cancelar + + + + DeviceItemWidget + + + Form + Formulário + + + + Device Id + ID do dispositivo + + + + this device + este dispositivo + + + + InviteButtonsWidget + + + Accept + Aceitar + + + + Refuse + Recusar + + + + Block + Bloquear + + + + LinkDeviceDialog + + + Dialog + Diálogo + + + + Enter your account password + Introduza a palavra-chave da sua conta + + + + Password + Palavra-chave + + + + Ok + OK + + + + Cancel + Cancelar + + + + Exporting account + Exportar a conta + + + + Your PIN is + O seu PIN é + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Este PIN e a palavra-chave devem ser inseridos no seu novo dispositivo nos próximos 10 minutos. + + + + Close + Fechar + + + + Link Another Device + Associar outro dispositivo + + + + Incorrect password + Palavra-chave incorreta + + + + Something went wrong. +Please try again later. + Algo correu mal. +Por favor tente de novo mais tarde. + + + + MainWindow + + + Settings + Configurações + + + + Exit + Sair + + + + About + Sobre + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Definir o nome registado + + + + Enter your account password + Introduza a palavra-chave da sua conta + + + + Password text input + Entrada de texto da palavra-chave + + + + Password text entry + Campo de texto da palavra-chave + + + + Password + Palavra-chave + + + + Register + Registar + + + + Cancel + Cancelar + + + + Registering Name + Registar o nome + + + + Something went wrong + Algo correu mal + + + + Close + Fechar + + + + Incorrect password + Palavra-chave incorreta + + + + Network error + Erro de rede + + + + NewWizardWidget + + + Form + Formulário + + + + Welcome Label + Rótulo de Boas-Vindas + + + + Welcome to + Bem-vindo(a) ao + + + + Welcome Logo + Logótipo de Boas-Vindas + + + + Create Jami account button + Botão para criar conta Jami + + + + Push button for Jami account creation start trigger + Botão para acionar a criação da conta do Jami + + + + Create a Jami account + Criar uma conta Jami + + + + Link device button + Botão para associar dispositivo + + + + Push button for device linkage start trigger + Botão para acionar a associação de dispositivo + + + + Link this device to an account + Associar este dispositivo a uma conta + + + + Create Jami SIP account button + Botão para criar a conta SIP Jami + + + + Push button for Jami SIP account creation start trigger + Botão para acionar a criação da conta SIP Jami + + + + Create a SIP account + Criar uma conta SIP + + + + Link this device to an existing account + Associar este dispositivo a uma conta existente + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Para associar este dispositivo a outra conta, primeiro </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">tem de obter um código PIN.</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Para gerar o código PIN:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Aceda à </span><span style=" font-size:14px; font-weight:600;">configuração de gestão de conta</span><span style=" font-size:14px;"> de um dispositivo anterior</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Escolha a </span><span style=" font-size:14px; font-weight:600;">conta Jami</span><span style=" font-size:14px;"> quer quer usar</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Aceda à aba </span><span style=" font-size:14px; font-weight:600;">Dispositivos</span><span style=" font-size:14px;"> </span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Selecione </span><span style=" font-size:14px; font-weight:600;">Adicionar dispositivo</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Irá obter o PIN necessário para completar este formulário. O código PIN é válido apenas durante </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutos</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Introduza o seu PIN: + + + + Or import a file: + Ou importe de um ficheiro: + + + + Link from exported account archive file + Link do ficheiro da conta exportada + + + + + + (None) + (nenhum) + + + + Password: + Palavra-chave: + + + + + Profile + Perfil + + + + + Profile name + Nome do perfil + + + + Account + Conta + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Registe o seu nome de utilizador. +Isto irá reservar o nome de utilizador para que apenas você o possa utilizar. +Os seus amigos serão capazes de contactá-lo com seu nome de +utilizador em vez do seu ID. + + + + Public username checkbox + Caixa de visto para o nome público de utilizador + + + + Checkbox selecting if the user wants a public username + Caixa de visto para indicar se o utilizador quer um nome público de utilizador + + + + Register public username + Registar nome de utilizador público + + + + Public username edit + Editar nome público de utilizador + + + + Choose your username + Escolha o seu nome de utilizador + + + + Password text input + Entrada de texto da palavra-chave + + + + Password text entry + Campo de texto da palavra-chave + + + + + Password + Palavra-chave + + + + + Password confirmation text input + Campo de texto para confirmação da palavra-chave + + + + Confirm password + Confirmar palavra-chave + + + + SIP Account + Conta SIP + + + + SIP Server edit + Editar servidor SIP + + + + Server + Servidor + + + + SIP proxy input + Entrada do proxy SIP + + + + SIP proxy text entry + Campo de texto do proxy SIP + + + + Proxy + Proxy + + + + SIP username input + Entrada de nome de utilizador SIP + + + + SIP Password text entry + Campo de texto da palavra-chave SIP + + + + Username + Nome de utilizador + + + + + SIP Password text input + Entrada de texto da palavra-chave SIP + + + + Generating your Jami account… + A gerar a sua conta Jami... + + + + Previous page button + Botão de página anterior + + + + push button to access previous page of wizard + Botão de pressionar para aceder à página anterior do assistente + + + + Previous + Anterior + + + + Cancel account create/link + Cancelar a criação de conta / associação + + + + push button to cancel account creation or linking + botão para cancelar criação de conta ou associação + + + + Back + Voltar + + + + Next page Button + Botão de página seguinte + + + + Push button to access next page of wizard + Botão de pressionar para aceder à página seguinte do assistente + + + + Next + Próximo + + + + Open File + Abrir Ficheiro + + + + Jami archive files (*.gz); All files (*) + Ficheiros de arquivo Jami (*.gz); Todos os ficheiros (*) + + + + Your account needs to be migrated. Enter your password. + A sua conta tem de ser migrada. Introduza a sua palavra-chave. + + + + Migrating your Jami account... + A migrar a sua conta Jami... + + + + Importing account archive... + A importar ficheiro da conta... + + + + Generating your Jami account... + A gerar a sua conta Jami... + + + + Generating your SIP account... + A gerar a sua conta SIP... + + + + Error creating account + Erro ao criar a conta + + + + PasswordDialog + + + Change Account Password + Alterar a palavra-chave da conta + + + + Enter Current Password + Introduzir a palavra-chave atual + + + + Enter New Password + Introduzir a palavra-chave nova + + + + Confirm New Password + Confirmar a palavra-chave nova + + + + Confirm + Confirmar + + + + Cancel + Cancelar + + + + Current Password Incorrect + A palavra-chave atual está incorreta + + + + PhotoBoothDialog + + + Photobooth + Foto instantânea + + + + PhotoboothWidget + + + Form + Formulário + + + + Photobooth display + Mostrar foto instantânea + + + + Choose File + Escolher Ficheiro + + + + Image Files (*.jpg *.jpeg *.png) + Ficheiros de imagem (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Escolher Ficheiro + + + + Files (*) + Ficheiros (*) + + + + QObject + + + No default mail client found + Cliente de e-mail padrão não encontrado + + + + + Edit Device Name + Editar Nome do Dispositivo + + + + Unlink Device From Account + Desassociar Dispositivo da Conta + + + + Save new device name + Guardar o novo nome do dispositivo + + + + Add as contact + Adicionar como contacto + + + + RingButton + + + Select folder + Select folder + + + + SettingsWidget + + + Form + Formulário + + + + Settings + Configurações + + + + Account + Conta + + + + + General + Geral + + + + + Audio / Video + Áudio / vídeo + + + + System + Sistema + + + + Enable desktop notifications + Ativar notificações no ambiente de trabalho + + + + Keep minimized on close + Manter minimizado ao fechar + + + + Download folder + Pasta de descarregamentos + + + + Save in + Gravar em + + + + Always Recording + Gravar sempre + + + + Updates + Atualizações + + + + Check for updates automatically every + Verificar se existem atualizações a cada + + + + Interval between update checks in days selector + Seletor do intervalo em dias entre verificação de atualizações + + + + days + dias + + + + Check for updates now button + Botão para verificar atualizações agora + + + + Check for updates now + Verificar se existem atualizações + + + + Password + Palavra-chave + + + + + Enable + Ativar + + + + toggle enable notifications + alternar ativar notificações + + + + Toggle keep minimized on close + Alternar manter minimizado ao fechar + + + + Call Recordings + Gravações de chamadas + + + + Toggle automatic updates + Alternar atualizações automáticas + + + + Jami Account + Conta Jami + + + + + Profile + Perfil + + + + + Identity + Identificação + + + + Id + ID + + + + Registered name + Utilizador registado + + + + Type here to register a username + Escreva aqui para registar um nome + + + + Register + Registar + + + + Change Password + Alterar palavra-chave + + + + Export Account + Exportar conta + + + + + Delete Account + Eliminar conta + + + + Linked Devices + Dispositivos associados + + + + Link Another Device + Associar outro dispositivo + + + + Banned Contacts + Contactos Banidos + + + + Format + Formato + + + + Video device framerate selector + Seletor de taxa de fotogramas do vídeo do dispositivo + + + + Preview unavailable + Pré-visualização não disponível + + + + + Advanced Account Settings + Configurações avançadas de conta + + + + SIP Account + Conta SIP + + + + Username + Nome de utilizador + + + + Hostname + Servidor + + + + Proxy + Proxy + + + + Audio + Áudio + + + + Microphone + Microfone + + + + Audio input device selector + Seletor de dispositivo de entrada de áudio + + + + Output Device + Dispositivo de Saída + + + + Choose the output device + Escolha o dispositivo de saída + + + + Video + Vídeo + + + + Device + Dispositivo + + + + Video device selector + Seletor de dispositivo de vídeo + + + + A registered name should not have any spaces and must be at least three letters long + Um nome registado não deve ter espaços e deve ter no mínimo 3 caracteres + + + + This name is already taken + Este nome já foi escolhido por outra pessoa + + + + + Enter an alias + Introduza um apelido + + + + Register this name + Registar este nome + + + + Remove Device + Remover dispositivo + + + + Enter this account's password to confirm the removal of this device + Introduza a palavra-chave desta conta para confirmar a remoção deste dispositivo + + + + Are you sure you wish to remove this device? + Tem a certeza que quer remover este dispositivo? + + + + Export Account Here + Exportar conta aqui + + + + Select A Folder For Your Downloads + Selecionar uma pasta para os seus descarregamentos + + + + Select A Folder For Your Recordings + Selecionar uma pasta para as suas gravações + + + + VideoOverlay + + + Call on Hold + Chamada em Espera + + + + Hold / Unhold + Esperar / Retomar + + + + Chat + Conversa + + + + Mute Mic + Desativar Microfone + + + + Record call + Gravar chamada + + + + Name label + Rótulo do nome + + + + Time elapsed + Tempo decorrido + + + + 00:00 + 00:00 + + + + Hangup + Desligar + + + + Mute Video + Tirar Som ao Vídeo + + + + VideoView + + + Share entire screen + Partilhar ecrã inteiro + + + + Share screen area + Partilhar área do ecrã + + + + Share file + Partilhar ficheiro + + + \ No newline at end of file diff --git a/translations/ring_client_windows_pt_BR.ts b/translations/ring_client_windows_pt_BR.ts new file mode 100644 index 00000000..4020c5b8 --- /dev/null +++ b/translations/ring_client_windows_pt_BR.ts @@ -0,0 +1,1592 @@ + + + AboutDialog + + + + About + Sobre + + + + about button + botão sobre + + + + credits button + botão créditos + + + + Credits + Créditos + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + O cliente para Microsoft Windows do Jami. +Jami é um programa de comunicação seguro e distribuído. + + + + version + versão + + + + Created by: + Criado por: + + + + Artwork by: + Artwork por: + + + + Based on the SFLPhone project + Baseado no projeto SFLPhone + + + + AccountItemDelegate + + + Add Account + Adicionar Conta + + + + AdvancedSIPSettingsWidget + + + Form + Formulário + + + + Call Settings + Configurações de Chamada + + + + Auto Answer Calls + Atender Chamadas Automaticamente + + + + Enable Custom Ringtone + Ativar Toque Personalizado + + + + Connectivity + Conectividade + + + + STUN Address + Endereço STUN + + + + Use STUN + Usar STUN + + + + Use UPnP + Usar UPnP + + + + Use TURN + Usar TURN + + + + TURN Password + Senha TURN + + + + TURN Username + Usuário TURN + + + + TURN Address + Endereço TURN + + + + Media + Mídia + + + + Enable Video + Habilitar Vídeo + + + + Video Codecs + Codecs de Vídeo + + + + Audio Codecs + Codecs de Áudio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Arquivos de áudio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Adicionar um toque personalizado + + + + AdvancedSettingsWidget + + + Form + Formulário + + + + Call Settings + Configurações de Chamada + + + + Allow Calls From Untrusted Peers + Permitir Chamadas de Usuários Desconhecidos + + + + Auto Answer Calls + Atender Chamadas Automaticamente + + + + Enable Custom Ringtone + Ativar Toque Personalizado + + + + Add a custom ringtone + Adicionar um toque personalizado + + + + Name Server + Servidor de Nome + + + + Address + Endereço + + + + OpenDHT Configuration + Configuração OpenDHT + + + + Enable Proxy + Habilitar Proxy + + + + Bootstrap + Inicialização + + + + Security + Segurança + + + + Private Key Password + Senha chave privada + + + + User Certificate + Certificado do Usuário + + + + Private Key + Chave Privada + + + + CA Certificate + Certificado de CA + + + + Connectivity + Conectividade + + + + Use STUN + Usar STUN + + + + STUN Address + Endereço STUN + + + + Use UPnP + Usar UPnP + + + + Use TURN + Usar TURN + + + + TURN Password + Senha TURN + + + + TURN Username + Usuário TURN + + + + TURN Address + Endereço TURN + + + + Media + Mídia + + + + Enable Video + Habilitar Vídeo + + + + Audio Codecs + Codecs de Áudio + + + + Video Codecs + Codecs de Vídeo + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Arquivos de áudio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formulário + + + + BannedItemWidget + + + Form + Formulário + + + + name + nome + + + + id + id + + + + CallWidget + + + Show conversations + Mostrar conversas + + + + Conversations + Conversas + + + + Search contact text input + Entrada de texto para busca de contato + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami é um software livre para comunicação universal que respeita as liberdades e privacidade de seus usuários. + + + + + This is your ID. +Copy and share it with your friends! + + + Esse é seu ID +Copiei e compartilhe com seus amigos! + + + + Show ring ID QR code + Mostrar código QR do seu ID do Ring + + + + Share ring ID button + Botão para compartilhar o seu Id do Ring + + + + Double-click to copy + Duplo-clique para copiar + + + + Error while generating QR Code + Erro ao gerar o Código QR + + + + + best name + melhor nome + + + + best Id + melhor Id + + + + Back to homepage button + Botão para voltar à página inicial + + + + Add to contacts + Adicionar aos contatos + + + + Show invites + Mostrar convites + + + + Invites + Convida + + + + + Find a new or existing contact + Encontrar um contato novo ou preexistente + + + + Wants to talk to you! + Quer conversar com você! + + + + Answer incoming call button + Botão para atender chamada + + + + Ignore incoming call button + Botão para ignorar chamada + + + + Answer + Resposta + + + + Ignore + Ignorar + + + + Cancel outgoing call + Cancelar chamada efetuada + + + + Cancel + Cancelar + + + + Start video call + Iniciar videochamada + + + + Start audio call + Iniciar chamada de áudio + + + + Clear conversation + Eliminar a conversa + + + + Remove contact + Remover contato + + + + Block contact + Bloquear contato + + + + Copy number + Copiar número + + + + Search your received invitations + Pesquisar seus convites recebidos + + + + Contact me on Jami + Encontre-me no Jami ! + + + + My Id is : + Meu Id é : + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Recebendo chamada de %1 + + + + DeleteAccountDialog + + + Account deletion + Remoção de conta + + + + Do you really want to delete the following account? + Tem certeza que deseja deletar a seguinte conta? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Se esta conta não foi exportada, ou adicionada a outro dispositivo, ela será completamente perdida. + + + + Permanently delete account + Deletar conta permanentemente + + + + Delete + Deletar + + + + Cancel account deletion + Cancelar deleção da conta + + + + Cancel + Cancelar + + + + DeviceItemWidget + + + Form + Formulário + + + + Device Id + Id do Dispositivo + + + + this device + este dispositivo + + + + InviteButtonsWidget + + + Accept + Aceitar + + + + Refuse + Recusar + + + + Block + Bloquear + + + + LinkDeviceDialog + + + Dialog + Diálogo + + + + Enter your account password + Entre com a senha de sua conta + + + + Password + Senha + + + + Ok + Ok + + + + Cancel + Cancelar + + + + Exporting account + Exportando conta + + + + Your PIN is + Seu PIN é + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Fechar + + + + Link Another Device + + + + + Incorrect password + Senha incorreta + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Configurações + + + + Exit + Sair + + + + About + Sobre + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + Entre com a senha de sua conta + + + + Password text input + Entrada de texto de senha + + + + Password text entry + Campo de texto da Senha + + + + Password + Senha + + + + Register + Registrar + + + + Cancel + Cancelar + + + + Registering Name + + + + + Something went wrong + + + + + Close + Fechar + + + + Incorrect password + Senha incorreta + + + + Network error + Erro de rede + + + + NewWizardWidget + + + Form + Formulário + + + + Welcome Label + Legenda de boas-vindas + + + + Welcome to + Bem-vindo ao + + + + Welcome Logo + Logotipo de boas-vindas + + + + Create Jami account button + Botão para criar conta do Jami + + + + Push button for Jami account creation start trigger + Botão para acionar a criação da conta do Jami + + + + Create a Jami account + Criar uma conta no Jami + + + + Link device button + Botão para associar dispositivo + + + + Push button for device linkage start trigger + Botão para acionar a associação de dispositivo + + + + Link this device to an account + Associar este dispositivo a uma conta + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Associar este dispositivo à uma conta existente + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Entre o seu PIN: + + + + Or import a file: + Ou importe de arquivo: + + + + Link from exported account archive file + Link do arquivo da conta exportada + + + + + + (None) + + + + + Password: + Senha: + + + + + Profile + Perfil + + + + + Profile name + Nome do perfil + + + + Account + Conta + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + Caixa de seleção para nome de usuário público + + + + Checkbox selecting if the user wants a public username + Caixa de seleção para indicar se o usuário deseja um nome de usuário público + + + + Register public username + Registrar nome de usuário público + + + + Public username edit + Editar nome de usuário público + + + + Choose your username + Escolha seu nome de usuário + + + + Password text input + Entrada de texto de senha + + + + Password text entry + Campo de texto da Senha + + + + + Password + Senha + + + + + Password confirmation text input + Campo de texto para confirmação da Senha + + + + Confirm password + Confirmar senha + + + + SIP Account + Conta SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + Entrada de usuário SIP + + + + SIP Password text entry + + + + + Username + Nome de usuário + + + + + SIP Password text input + + + + + Generating your Jami account… + Gerando sua conta do Jami... + + + + Previous page button + Botão da página anterior + + + + push button to access previous page of wizard + Pressione botão para acessar a página anterior do assistente + + + + Previous + Anterior + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Voltar + + + + Next page Button + Botão da próxima página + + + + Push button to access next page of wizard + Pressione botão para acessar próxima página do assistente + + + + Next + Próximo + + + + Open File + Abrir arquivo + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + Sua conta precisa ser migrada. Entre com sua senha. + + + + Migrating your Jami account... + Migrando sua conta do Jami... + + + + Importing account archive... + Importando arquivo da conta... + + + + Generating your Jami account... + Gerando sua conta do Jami... + + + + Generating your SIP account... + + + + + Error creating account + Erro ao criar conta + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Cancelar + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + Foto instantânea + + + + PhotoboothWidget + + + Form + Formulário + + + + Photobooth display + Exibir foto instantânea + + + + Choose File + Escolher Arquivo + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Escolher Arquivo + + + + Files (*) + Arquivos (*) + + + + QObject + + + No default mail client found + Cliente de e-mail não encontrado + + + + + Edit Device Name + Editar Nome do Dispositivo + + + + Unlink Device From Account + Desassociar Este Dispositivo Da Conta + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + Selecionar pasta + + + + SettingsWidget + + + Form + Formulário + + + + Settings + Configurações + + + + Account + Conta + + + + + General + Geral + + + + + Audio / Video + Áudio / vídeo + + + + System + Sistema + + + + Enable desktop notifications + + + + + Keep minimized on close + Manter minimizado ao fechar + + + + Download folder + Pasta de downloads + + + + Save in + Salvar em + + + + Always Recording + + + + + Updates + Atualizações + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + Seletor do intervalo da verificação de atualizaçôes em dias + + + + days + dias + + + + Check for updates now button + Botão para verificar atualizações agora + + + + Check for updates now + Checar por atualizações agora + + + + Password + Senha + + + + + Enable + Habilitar + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Perfil + + + + + Identity + Identificação + + + + Id + ID + + + + Registered name + Usuário registrado + + + + Type here to register a username + + + + + Register + Registrar + + + + Change Password + Alterar senha + + + + Export Account + Exportar Conta + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Contatos Banidos + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + Conta SIP + + + + Username + Nome de usuário + + + + Hostname + Servidor + + + + Proxy + Proxy + + + + Audio + Áudio + + + + Microphone + Microfone + + + + Audio input device selector + Seletor de dispositivo de entrada de áudio + + + + Output Device + Dispositivo de saída + + + + Choose the output device + + + + + Video + Vídeo + + + + Device + Dispositivo + + + + Video device selector + Seletor de dispositivo de vídeo + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Chamada em espera + + + + Hold / Unhold + Esperar / Retomar + + + + Chat + Conversa + + + + Mute Mic + Desativar microfone + + + + Record call + Gravar chamada + + + + Name label + Nome do rótulo + + + + Time elapsed + Tempo decorrido + + + + 00:00 + 00:00 + + + + Hangup + Desligar + + + + Mute Video + Desativar vídeo + + + + VideoView + + + Share entire screen + Compartilhar tela inteira + + + + Share screen area + Compartilhar área da tela + + + + Share file + Compartilhar arquivo + + + \ No newline at end of file diff --git a/translations/ring_client_windows_pt_PT.ts b/translations/ring_client_windows_pt_PT.ts new file mode 100644 index 00000000..dd4f0f57 --- /dev/null +++ b/translations/ring_client_windows_pt_PT.ts @@ -0,0 +1,1606 @@ + + + AboutDialog + + + + About + Sobre + + + + about button + botão sobre + + + + credits button + botão créditos + + + + Credits + Créditos + + + + Free as in Freedom + Livre como em Liberdade + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + O cliente Microsoft Windows para o Jami +O Jami é um programa de comunicação distribuído e seguro. + + + + version + versão + + + + Created by: + Criado por: + + + + Artwork by: + Arte por: + + + + Based on the SFLPhone project + Baseado no projeto SFLPhone + + + + AccountItemDelegate + + + Add Account + Adicionar Conta + + + + AdvancedSIPSettingsWidget + + + Form + Formulário + + + + Call Settings + Configurações de chamadas + + + + Auto Answer Calls + Atender chamadas automaticamente + + + + Enable Custom Ringtone + Ativar toque de chamada personalizado + + + + Connectivity + Conectividade + + + + STUN Address + Endereço STUN + + + + Use STUN + Usar STUN + + + + Use UPnP + Usar UPnP + + + + Use TURN + Usar TURN + + + + TURN Password + Palavra-chave TURN + + + + TURN Username + Nome de utilizador TURN + + + + TURN Address + Endereço TURN + + + + Media + Média + + + + Enable Video + Ativar Vídeo + + + + Video Codecs + Codecs de vídeo + + + + Audio Codecs + Codecs de áudio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Ficheiros de áudio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Adicionar um toque de chamada personalizado + + + + AdvancedSettingsWidget + + + Form + Formulário + + + + Call Settings + Configurações de chamadas + + + + Allow Calls From Untrusted Peers + Permitir chamadas de utilizadores desconhecidos + + + + Auto Answer Calls + Atender chamadas automaticamente + + + + Enable Custom Ringtone + Ativar toque de chamada personalizado + + + + Add a custom ringtone + Adicionar um toque de chamada personalizado + + + + Name Server + Nome do servidor + + + + Address + Endereço + + + + OpenDHT Configuration + Configuração OpenDHT + + + + Enable Proxy + Ativar Proxy + + + + Bootstrap + Inicialização + + + + Security + Segurança + + + + Private Key Password + Palavra-chave da Chave Privada + + + + User Certificate + Certificado do Utilizador + + + + Private Key + Chave privada + + + + CA Certificate + Certificado CA + + + + Connectivity + Conectividade + + + + Use STUN + Usar STUN + + + + STUN Address + Endereço STUN + + + + Use UPnP + Usar UPnP + + + + Use TURN + Usar TURN + + + + TURN Password + Palavra-chave TURN + + + + TURN Username + Nome de utilizador TURN + + + + TURN Address + Endereço TURN + + + + Media + Média + + + + Enable Video + Ativar Vídeo + + + + Audio Codecs + Codecs de áudio + + + + Video Codecs + Codecs de vídeo + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Ficheiros de áudio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formulário + + + + BannedItemWidget + + + Form + Formulário + + + + name + nome + + + + id + id + + + + CallWidget + + + Show conversations + Ver conversações + + + + Conversations + Conversas + + + + Search contact text input + Entrada de texto para pesquisa de contacto + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + O Jami é um programa livre para comunicação universal que respeita as liberdades e privacidade dos seus utilizadores. + + + + + This is your ID. +Copy and share it with your friends! + + + Este é o seu ID. +Copie-o e partilhe-o com os seus amigos! + + + + + Show ring ID QR code + Mostrar código QR do seu ID do Ring + + + + Share ring ID button + Botão para partilhar o seu ID do Ring + + + + Double-click to copy + Duplo-clique para copiar + + + + Error while generating QR Code + Erro ao gerar o Código QR + + + + + best name + melhor nome + + + + best Id + melhor Id + + + + Back to homepage button + Botão para voltar à página inicial + + + + Add to contacts + Adicionar aos contactos + + + + Show invites + Mostrar convites + + + + Invites + Convites + + + + + Find a new or existing contact + Procurar um contacto novo ou existente + + + + Wants to talk to you! + Quer conversar consigo! + + + + Answer incoming call button + Botão para atender chamada + + + + Ignore incoming call button + Botão para ignorar chamada + + + + Answer + Resposta + + + + Ignore + Ignorar + + + + Cancel outgoing call + Cancelar chamada efetuada + + + + Cancel + Cancelar + + + + Start video call + Começar chamada de vídeo + + + + Start audio call + Começar chamada de áudio + + + + Clear conversation + Limpar conversação + + + + Remove contact + Remover contacto + + + + Block contact + Bloquear contacto + + + + Copy number + Copiar número + + + + Search your received invitations + Pesquisar os seus convites recebidos + + + + Contact me on Jami + Contacte-me no Jami + + + + My Id is : + O meu ID é: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + A receber chamada de %1 + + + + DeleteAccountDialog + + + Account deletion + Eliminação de conta + + + + Do you really want to delete the following account? + Tem certeza que quer eliminar a seguinte conta? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Se esta conta não foi exportada ou adicionada a outro dispositivo, ela será perdida. + + + + Permanently delete account + Eliminar conta permanentemente + + + + Delete + Eliminar + + + + Cancel account deletion + Cancelar eliminação da conta + + + + Cancel + Cancelar + + + + DeviceItemWidget + + + Form + Formulário + + + + Device Id + ID do dispositivo + + + + this device + este dispositivo + + + + InviteButtonsWidget + + + Accept + Aceitar + + + + Refuse + Recusar + + + + Block + Bloquear + + + + LinkDeviceDialog + + + Dialog + Diálogo + + + + Enter your account password + Introduza a palavra-chave da sua conta + + + + Password + Palavra-chave + + + + Ok + OK + + + + Cancel + Cancelar + + + + Exporting account + Exportar a conta + + + + Your PIN is + O seu PIN é + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Este PIN e a palavra-chave devem ser inseridos no seu novo dispositivo nos próximos 10 minutos. + + + + Close + Fechar + + + + Link Another Device + Associar outro dispositivo + + + + Incorrect password + Palavra-chave incorreta + + + + Something went wrong. +Please try again later. + Algo correu mal. +Por favor tente de novo mais tarde. + + + + MainWindow + + + Settings + Configurações + + + + Exit + Sair + + + + About + Sobre + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Definir o nome registado + + + + Enter your account password + Introduza a palavra-chave da sua conta + + + + Password text input + Entrada de texto da palavra-chave + + + + Password text entry + Campo de texto da palavra-chave + + + + Password + Palavra-chave + + + + Register + Registar + + + + Cancel + Cancelar + + + + Registering Name + Registar o nome + + + + Something went wrong + Algo correu mal + + + + Close + Fechar + + + + Incorrect password + Palavra-chave incorreta + + + + Network error + Erro de rede + + + + NewWizardWidget + + + Form + Formulário + + + + Welcome Label + Rótulo de Boas-Vindas + + + + Welcome to + Bem-vindo(a) ao + + + + Welcome Logo + Logótipo de Boas-Vindas + + + + Create Jami account button + Botão para criar conta Jami + + + + Push button for Jami account creation start trigger + Botão para acionar a criação da conta do Jami + + + + Create a Jami account + Criar uma conta Jami + + + + Link device button + Botão para associar dispositivo + + + + Push button for device linkage start trigger + Botão para acionar a associação de dispositivo + + + + Link this device to an account + Associar este dispositivo a uma conta + + + + Create Jami SIP account button + Botão para criar a conta SIP Jami + + + + Push button for Jami SIP account creation start trigger + Botão para acionar a criação da conta SIP Jami + + + + Create a SIP account + Criar uma conta SIP + + + + Link this device to an existing account + Associar este dispositivo a uma conta existente + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Para associar este dispositivo a outra conta, primeiro </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">tem de obter um código PIN.</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Para gerar o código PIN:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Aceda à </span><span style=" font-size:14px; font-weight:600;">configuração de gestão de conta</span><span style=" font-size:14px;"> de um dispositivo anterior</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Escolha a </span><span style=" font-size:14px; font-weight:600;">conta Jami</span><span style=" font-size:14px;"> quer quer usar</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Aceda à aba </span><span style=" font-size:14px; font-weight:600;">Dispositivos</span><span style=" font-size:14px;"> </span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Selecione </span><span style=" font-size:14px; font-weight:600;">Adicionar dispositivo</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Irá obter o PIN necessário para completar este formulário. O código PIN é válido apenas durante </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutos</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Introduza o seu PIN: + + + + Or import a file: + Ou importe de um ficheiro: + + + + Link from exported account archive file + Link do ficheiro da conta exportada + + + + + + (None) + (nenhum) + + + + Password: + Palavra-chave: + + + + + Profile + Perfil + + + + + Profile name + Nome do perfil + + + + Account + Conta + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Registe o seu nome de utilizador. +Isto irá reservar o nome de utilizador para que apenas você o possa utilizar. +Os seus amigos serão capazes de contactá-lo com seu nome de +utilizador em vez do seu ID. + + + + Public username checkbox + Caixa de visto para o nome público de utilizador + + + + Checkbox selecting if the user wants a public username + Caixa de visto para indicar se o utilizador quer um nome público de utilizador + + + + Register public username + Registar nome de utilizador público + + + + Public username edit + Editar nome público de utilizador + + + + Choose your username + Escolha o seu nome de utilizador + + + + Password text input + Entrada de texto da palavra-chave + + + + Password text entry + Campo de texto da palavra-chave + + + + + Password + Palavra-chave + + + + + Password confirmation text input + Campo de texto para confirmação da palavra-chave + + + + Confirm password + Confirmar palavra-chave + + + + SIP Account + Conta SIP + + + + SIP Server edit + Editar servidor SIP + + + + Server + Servidor + + + + SIP proxy input + Entrada do proxy SIP + + + + SIP proxy text entry + Campo de texto do proxy SIP + + + + Proxy + Proxy + + + + SIP username input + Entrada de nome de utilizador SIP + + + + SIP Password text entry + Campo de texto da palavra-chave SIP + + + + Username + Nome de utilizador + + + + + SIP Password text input + Entrada de texto da palavra-chave SIP + + + + Generating your Jami account… + A gerar a sua conta Jami... + + + + Previous page button + Botão de página anterior + + + + push button to access previous page of wizard + Botão de pressionar para aceder à página anterior do assistente + + + + Previous + Anterior + + + + Cancel account create/link + Cancelar a criação de conta / associação + + + + push button to cancel account creation or linking + botão para cancelar criação de conta ou associação + + + + Back + Voltar + + + + Next page Button + Botão de página seguinte + + + + Push button to access next page of wizard + Botão de pressionar para aceder à página seguinte do assistente + + + + Next + Próximo + + + + Open File + Abrir Ficheiro + + + + Jami archive files (*.gz); All files (*) + Ficheiros de arquivo Jami (*.gz); Todos os ficheiros (*) + + + + Your account needs to be migrated. Enter your password. + A sua conta tem de ser migrada. Introduza a sua palavra-chave. + + + + Migrating your Jami account... + A migrar a sua conta Jami... + + + + Importing account archive... + A importar ficheiro da conta... + + + + Generating your Jami account... + A gerar a sua conta Jami... + + + + Generating your SIP account... + A gerar a sua conta SIP... + + + + Error creating account + Erro ao criar a conta + + + + PasswordDialog + + + Change Account Password + Alterar a palavra-chave da conta + + + + Enter Current Password + Introduzir a palavra-chave atual + + + + Enter New Password + Introduzir a palavra-chave nova + + + + Confirm New Password + Confirmar a palavra-chave nova + + + + Confirm + Confirmar + + + + Cancel + Cancelar + + + + Current Password Incorrect + A palavra-chave atual está incorreta + + + + PhotoBoothDialog + + + Photobooth + Foto instantânea + + + + PhotoboothWidget + + + Form + Formulário + + + + Photobooth display + Mostrar foto instantânea + + + + Choose File + Escolher Ficheiro + + + + Image Files (*.jpg *.jpeg *.png) + Ficheiros de imagem (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Escolher Ficheiro + + + + Files (*) + Ficheiros (*) + + + + QObject + + + No default mail client found + Cliente de e-mail padrão não encontrado + + + + + Edit Device Name + Editar Nome do Dispositivo + + + + Unlink Device From Account + Desassociar Dispositivo da Conta + + + + Save new device name + Guardar o novo nome do dispositivo + + + + Add as contact + Adicionar como contacto + + + + RingButton + + + Select folder + Select folder + + + + SettingsWidget + + + Form + Formulário + + + + Settings + Configurações + + + + Account + Conta + + + + + General + Geral + + + + + Audio / Video + Áudio / vídeo + + + + System + Sistema + + + + Enable desktop notifications + Ativar notificações no ambiente de trabalho + + + + Keep minimized on close + Manter minimizado ao fechar + + + + Download folder + Pasta de descarregamentos + + + + Save in + Gravar em + + + + Always Recording + Gravar sempre + + + + Updates + Atualizações + + + + Check for updates automatically every + Verificar se existem atualizações a cada + + + + Interval between update checks in days selector + Seletor do intervalo em dias entre verificação de atualizações + + + + days + dias + + + + Check for updates now button + Botão para verificar atualizações agora + + + + Check for updates now + Verificar se existem atualizações + + + + Password + Palavra-chave + + + + + Enable + Ativar + + + + toggle enable notifications + alternar ativar notificações + + + + Toggle keep minimized on close + Alternar manter minimizado ao fechar + + + + Call Recordings + Gravações de chamadas + + + + Toggle automatic updates + Alternar atualizações automáticas + + + + Jami Account + Conta Jami + + + + + Profile + Perfil + + + + + Identity + Identificação + + + + Id + ID + + + + Registered name + Utilizador registado + + + + Type here to register a username + Escreva aqui para registar um nome + + + + Register + Registar + + + + Change Password + Alterar palavra-chave + + + + Export Account + Exportar conta + + + + + Delete Account + Eliminar conta + + + + Linked Devices + Dispositivos associados + + + + Link Another Device + Associar outro dispositivo + + + + Banned Contacts + Contactos Banidos + + + + Format + Formato + + + + Video device framerate selector + Seletor de taxa de fotogramas do vídeo do dispositivo + + + + Preview unavailable + Pré-visualização não disponível + + + + + Advanced Account Settings + Configurações avançadas de conta + + + + SIP Account + Conta SIP + + + + Username + Nome de utilizador + + + + Hostname + Servidor + + + + Proxy + Proxy + + + + Audio + Áudio + + + + Microphone + Microfone + + + + Audio input device selector + Seletor de dispositivo de entrada de áudio + + + + Output Device + Dispositivo de Saída + + + + Choose the output device + Escolha o dispositivo de saída + + + + Video + Vídeo + + + + Device + Dispositivo + + + + Video device selector + Seletor de dispositivo de vídeo + + + + A registered name should not have any spaces and must be at least three letters long + Um nome registado não deve ter espaços e deve ter no mínimo 3 caracteres + + + + This name is already taken + Este nome já foi escolhido por outra pessoa + + + + + Enter an alias + Introduza um apelido + + + + Register this name + Registar este nome + + + + Remove Device + Remover dispositivo + + + + Enter this account's password to confirm the removal of this device + Introduza a palavra-chave desta conta para confirmar a remoção deste dispositivo + + + + Are you sure you wish to remove this device? + Tem a certeza que quer remover este dispositivo? + + + + Export Account Here + Exportar conta aqui + + + + Select A Folder For Your Downloads + Selecionar uma pasta para os seus descarregamentos + + + + Select A Folder For Your Recordings + Selecionar uma pasta para as suas gravações + + + + VideoOverlay + + + Call on Hold + Chamada em Espera + + + + Hold / Unhold + Esperar / Retomar + + + + Chat + Conversa + + + + Mute Mic + Desativar Microfone + + + + Record call + Gravar chamada + + + + Name label + Rótulo do nome + + + + Time elapsed + Tempo decorrido + + + + 00:00 + 00:00 + + + + Hangup + Desligar + + + + Mute Video + Tirar Som ao Vídeo + + + + VideoView + + + Share entire screen + Partilhar ecrã inteiro + + + + Share screen area + Partilhar área do ecrã + + + + Share file + Partilhar ficheiro + + + \ No newline at end of file diff --git a/translations/ring_client_windows_ro.ts b/translations/ring_client_windows_ro.ts new file mode 100644 index 00000000..5271bdfb --- /dev/null +++ b/translations/ring_client_windows_ro.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + Credite + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Activează video + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + Seuritate + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Activează video + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Adaugă la contacte + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + Blocați contactul + + + + Copy number + Copiați numărul + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Șterge + + + + Cancel account deletion + + + + + Cancel + + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + Acceptă + + + + Refuse + Refuzați + + + + Block + Blochează + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Parola + + + + Ok + + + + + Cancel + + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Setari + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Parola + + + + Register + Registrare + + + + Cancel + + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Creați un cont Jami + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Parola + + + + + Password confirmation text input + + + + + Confirm password + Confirmă parola + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Nume de utilizator + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Înapoi + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Înainte + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Setari + + + + Account + + + + + + General + General + + + + + Audio / Video + + + + + System + Sistem + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Parola + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Registrare + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Nume de utilizator + + + + Hostname + Hostname + + + + Proxy + + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Video + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + Distribuie fișierul + + + \ No newline at end of file diff --git a/translations/ring_client_windows_ro_RO.ts b/translations/ring_client_windows_ro_RO.ts new file mode 100644 index 00000000..6a83867a --- /dev/null +++ b/translations/ring_client_windows_ro_RO.ts @@ -0,0 +1,1598 @@ + + + AboutDialog + + + + About + Despre + + + + about button + butonul Informații + + + + credits button + butonul Merite + + + + Credits + Merite + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Conectare + + + + STUN Address + + + + + Use STUN + Folosește STUN + + + + Use UPnP + + + + + Use TURN + Folosește TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Activează Video + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Fișiere Audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adresă + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Pornire + + + + Security + Protecție + + + + Private Key Password + Parola cheii private + + + + User Certificate + Certificat de utilizator + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Conectare + + + + Use STUN + Folosește STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Folosește TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Activează Video + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Fișiere Audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Conversații + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami este un program liber pentru comunicare universală care respectă libertățile și intimitatea utilizatorilor săi. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + Arată Jami ID și codul QR + + + + Share ring ID button + butonul Distribuie Jami ID + + + + Double-click to copy + + + + + Error while generating QR Code + Eroare la generarea codului QR + + + + + best name + + + + + best Id + + + + + Back to homepage button + butonul Înapoi la pagina Acasă + + + + Add to contacts + Adaugă la contacte + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + butonul Răspunde la apel + + + + Ignore incoming call button + butonul Ignoră apelul + + + + Answer + + + + + Ignore + Ignoră + + + + Cancel outgoing call + Anulează apelul efectuat + + + + Cancel + Renunță + + + + Start video call + Începe un apel video + + + + Start audio call + Începe un apel audio + + + + Clear conversation + Șterge conversația + + + + Remove contact + Șterge contactul + + + + Block contact + Blochează contactul + + + + Copy number + Copiază numărul + + + + Search your received invitations + + + + + Contact me on Jami + Contactează-mă pe Jami + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + Apel primit de la %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Șterge + + + + Cancel account deletion + + + + + Cancel + Renunță + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + acest dispozitiv + + + + InviteButtonsWidget + + + Accept + Acceptă + + + + Refuse + Refuză + + + + Block + Blochează + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Parolă + + + + Ok + Ok + + + + Cancel + Renunță + + + + Exporting account + + + + + Your PIN is + Codul tău PIN e + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Acest cod PIN și parola contului trebuie introduse în dispozitiv în 10 minute. + + + + Close + Închide + + + + Link Another Device + Asociază alt dispozitiv + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Reglări + + + + Exit + + + + + About + Despre + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Parolă + + + + Register + Înscrie-te + + + + Cancel + Renunță + + + + Registering Name + + + + + Something went wrong + + + + + Close + Închide + + + + Incorrect password + + + + + Network error + Eroare de rețea + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + butonul Creează un cont Jami + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Creează un cont Jami + + + + Link device button + butonul Asociază dispozitivul + + + + Push button for device linkage start trigger + Apasă butonul pentru a începe asocierea dispozitivului + + + + Link this device to an account + Asociază acest dispozitiv unui cont + + + + Create Jami SIP account button + butonul Creează un cont Jami SIP + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Asociază acest dispozitiv unui cont existent + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Pentru a asocia acest dispozitiv unui alt cont, mai întîi </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">trebuie să obții un cod</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> PIN. Pentru a genera codul PIN:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Du-te la </span><span style=" font-size:14px; font-weight:600;">reglările contului</span><span style=" font-size:14px;"> în dispozitivul care conține contul</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Alege </span><span style=" font-size:14px; font-weight:600;">contul Jami</span><span style=" font-size:14px;"> pe care vrei să-l folosești</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Du-te la </span><span style=" font-size:14px; font-weight:600;">pagina</span><span style=" font-size:14px;"> Dispozitive</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Alege </span><span style=" font-size:14px; font-weight:600;">Adaugă un dispozitiv</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Vei obține codul PIN necesar. Acest PIN e valabil doar </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minute</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Introdu codul PIN: + + + + Or import a file: + Sau importă un fișier: + + + + Link from exported account archive file + Asociază din fișierul arhivei contului exportat + + + + + + (None) + (Niciunul) + + + + Password: + Parolă: + + + + + Profile + Profil + + + + + Profile name + Numele profilului + + + + Account + Cont + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Înregistrează numele de utilizator public + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + Parolă + + + + + Password confirmation text input + + + + + Confirm password + Confirmă parola + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Nume de Utilizator + + + + + SIP Password text input + + + + + Generating your Jami account… + Se creează contul tău Jami... + + + + Previous page button + butonul Pagina precedentă + + + + push button to access previous page of wizard + + + + + Previous + Înapoi + + + + Cancel account create/link + Renunță la crearea/asocierea contului + + + + push button to cancel account creation or linking + apasă butonul pentru a renunța la crearea sau asocierea contului + + + + Back + Înapoi + + + + Next page Button + butonul Pagina următoare + + + + Push button to access next page of wizard + + + + + Next + Următorul + + + + Open File + Deschide fișierul + + + + Jami archive files (*.gz); All files (*) + Fișierul arhivei Jami (*.gz); Toate fișierele (*) + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + Se importă arhiva contului... + + + + Generating your Jami account... + Se creează contul tău Jami... + + + + Generating your SIP account... + Se creează contul tău SIP... + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Renunță + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + Alege fișierul + + + + Image Files (*.jpg *.jpeg *.png) + Fișiere Imagine (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Alege fișierul + + + + Files (*) + Fișiere (*) + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + Disociază dispozitivul de cont + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Reglări + + + + Account + Cont + + + + + General + General + + + + + Audio / Video + + + + + System + Sistem + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + Dosarul descărcării + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + butonul Caută actualizări acum + + + + Check for updates now + + + + + Password + Parolă + + + + + Enable + Activează + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + Identitate + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + Înscrie-te + + + + Change Password + Schimbă Parola + + + + Export Account + Exportă Contul + + + + + Delete Account + + + + + Linked Devices + Dispozitive asociate + + + + Link Another Device + Asociază alt dispozitiv + + + + Banned Contacts + Contacte Blocate + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + Previzualizare indisponibilă + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + Nume de Utilizator + + + + Hostname + Numele serverului + + + + Proxy + Proxy + + + + Audio + Audio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + Video + + + + Device + Dispozitiv + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + Discuție + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + 00:00 + + + + Hangup + + + + + Mute Video + Video fără sonor + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + Distribuie fișierul + + + \ No newline at end of file diff --git a/translations/ring_client_windows_ru.ts b/translations/ring_client_windows_ru.ts new file mode 100644 index 00000000..1c18290c --- /dev/null +++ b/translations/ring_client_windows_ru.ts @@ -0,0 +1,1602 @@ + + + AboutDialog + + + + About + О программе + + + + about button + о кнопке  + + + + credits button + кнопка кредитов + + + + Credits + Благодарности + + + + Free as in Freedom + Свобода + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Клиент Jami для Microsoft Windows. +Jami – это приложение с распределённой архитектурой для безопасного общения. + + + + version + версия + + + + Created by: + Авторы проекта: + + + + Artwork by: + Дизайн: + + + + Based on the SFLPhone project + Основано на проекте SFLPhone + + + + AccountItemDelegate + + + Add Account + Добавить аккаунт + + + + AdvancedSIPSettingsWidget + + + Form + Форма + + + + Call Settings + Настройки вызовов + + + + Auto Answer Calls + Автоматически принимать входящие вызовы + + + + Enable Custom Ringtone + Использовать свою мелодию звонка + + + + Connectivity + Связь + + + + STUN Address + Адрес STUN + + + + Use STUN + Использовать STUN + + + + Use UPnP + Использовать UPnP + + + + Use TURN + Использовать TURN + + + + TURN Password + Пароль TURN + + + + TURN Username + Имя пользователя TURN + + + + TURN Address + Адрес TURN + + + + Media + Медиа + + + + Enable Video + Включить видео + + + + Video Codecs + Видео кодеки + + + + Audio Codecs + Аудио кодеки + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Аудиофайлы (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Добавить свою мелодию звонка + + + + AdvancedSettingsWidget + + + Form + Форма + + + + Call Settings + Настройки вызовов + + + + Allow Calls From Untrusted Peers + Разрешить вызовы от недоверенных участников + + + + Auto Answer Calls + Автоматически принимать входящие вызовы + + + + Enable Custom Ringtone + Использовать свою мелодию звонка + + + + Add a custom ringtone + Добавить свою мелодию звонка + + + + Name Server + Сервер имён + + + + Address + Адрес + + + + OpenDHT Configuration + Конфигурация OpenDHT + + + + Enable Proxy + Включить прокси + + + + Bootstrap + Инициализация + + + + Security + Безопасность + + + + Private Key Password + Пароль закрытого ключа + + + + User Certificate + Пользовательский Cертификат + + + + Private Key + Закрытый ключ + + + + CA Certificate + Сертификат удостоверяющего центра + + + + Connectivity + Связь + + + + Use STUN + Использовать STUN + + + + STUN Address + Адрес STUN + + + + Use UPnP + Использовать UPnP + + + + Use TURN + Использовать TURN + + + + TURN Password + Пароль TURN + + + + TURN Username + Имя пользователя TURN + + + + TURN Address + Адрес TURN + + + + Media + Медиа + + + + Enable Video + Включить видео + + + + Audio Codecs + Аудио кодеки + + + + Video Codecs + Видео кодеки + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Аудиофайлы (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Форма + + + + BannedItemWidget + + + Form + Форма + + + + name + имя + + + + id + id + + + + CallWidget + + + Show conversations + Показать беседы + + + + Conversations + Разговоры + + + + Search contact text input + Найдите контакты текстовый ввод + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami — свободная программа для связи, уважающая свободу и конфиденциальность пользователей. + + + + + This is your ID. +Copy and share it with your friends! + + +Это ваш ID. +Скопируйте и поделитесь им с друзьями! + + + + + Show ring ID QR code + Показать Ring ID QR код + + + + Share ring ID button + кнопка Поделиться ID + + + + Double-click to copy + Двойной клик для копирования + + + + Error while generating QR Code + Ошибка при создании QR кода + + + + + best name + наилучшее имя + + + + best Id + наилучший ID + + + + Back to homepage button + Кнопка возврата на главную страницу + + + + Add to contacts + Добавить в контакты + + + + Show invites + Показать приглашения + + + + Invites + Приглашения + + + + + Find a new or existing contact + Поиск нового или существующего контакта + + + + Wants to talk to you! + Хочет поговорить с вами! + + + + Answer incoming call button + Кнопка ответа на входящий вызов + + + + Ignore incoming call button + Кнопка игнорирования входящего вызова + + + + Answer + Ответить + + + + Ignore + Игнорировать + + + + Cancel outgoing call + Отменить исходящий вызов + + + + Cancel + Отмена + + + + Start video call + Начать видеовызов + + + + Start audio call + Начать голосовой вызов + + + + Clear conversation + Очистить разговор + + + + Remove contact + Удалить контакт + + + + Block contact + Заблокировать контакт + + + + Copy number + Копировать номер + + + + Search your received invitations + Поиск по полученным приглашениям + + + + Contact me on Jami + Свяжитесь со мной в Jami + + + + My Id is : + Мой ID: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Звонок от %1 + + + + DeleteAccountDialog + + + Account deletion + Удаление аккаунта + + + + Do you really want to delete the following account? + Вы действительно хотите удалить указанный аккаунт? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Если этот аккаунт не был экспортирован или привязан к другому устройству, он будет безвозвратно потерян + + + + Permanently delete account + Удалить аккаунт безвозвратно + + + + Delete + Удалить + + + + Cancel account deletion + Отменить удаление аккаунта + + + + Cancel + Отмена + + + + DeviceItemWidget + + + Form + Форма + + + + Device Id + Устройство ID + + + + this device + это устройство + + + + InviteButtonsWidget + + + Accept + Принять + + + + Refuse + Отказать + + + + Block + Заблокировать + + + + LinkDeviceDialog + + + Dialog + Диалог + + + + Enter your account password + Введите пароль вашего аккаунта + + + + Password + Пароль + + + + Ok + OK + + + + Cancel + Отмена + + + + Exporting account + Экспортировать аккаунт + + + + Your PIN is + Ваш PIN: + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Этот пин-код и пароль учетной записи должны быть введены на вашем устройстве в течение 10 минут. + + + + Close + Закрыть + + + + Link Another Device + Привязать другое устройство + + + + Incorrect password + Неверный пароль + + + + Something went wrong. +Please try again later. + Что-то пошло не так, повторите попытку позднее. + + + + MainWindow + + + Settings + Настройки + + + + Exit + Выход + + + + About + О программе + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Установить зарегистрированное имя + + + + Enter your account password + Введите пароль вашего аккаунта + + + + Password text input + Ввод текста пароля + + + + Password text entry + Ввод текста пароля + + + + Password + Пароль + + + + Register + Зарегистрировать + + + + Cancel + Отмена + + + + Registering Name + Зарегистрированное Имя + + + + Something went wrong + Что-то пошло не так. + + + + Close + Закрыть + + + + Incorrect password + Неверный пароль + + + + Network error + Ошибка сети + + + + NewWizardWidget + + + Form + Форма + + + + Welcome Label + Ярлык добро пожаловать + + + + Welcome to + Добро пожаловать в + + + + Welcome Logo + Логотип добро пожаловать + + + + Create Jami account button + Кнопка создать учётную запись Jami + + + + Push button for Jami account creation start trigger + Нажмите кнопку для запуска создания учетной записи Jami + + + + Create a Jami account + Создать учётную запись Jami + + + + Link device button + Кнопка привязки устройства + + + + Push button for device linkage start trigger + Нажмите кнопку для запуска устройства связи + + + + Link this device to an account + Привязать это устройство к аккаунту + + + + Create Jami SIP account button + Кнопка создания учётной запись Jami SIP + + + + Push button for Jami SIP account creation start trigger + Кнопка для создания начала Jami SIP триггера + + + + Create a SIP account + Создать SIP Аккаунт + + + + Link this device to an existing account + Привязать это устройство к существующему аккаунту + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Чтобы соединить это устройство с другой учетной записью, сначала</span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">нужно получить ПИН-код</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> Чтобы сгенерировать ПИН-код:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Перейдите к </span><span style=" font-size:14px; font-weight:600;">настройкам управления аккаунтом</span><span style=" font-size:14px;"> предыдущего устройства </span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Выберите </span><span style=" font-size:14px; font-weight:600;">акаунт Jami </span><span style=" font-size:14px;"> который хотите использовать</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Перейдите на</span><span style=" font-size:14px; font-weight:600;">вкладку устройства</span><span style=" font-size:14px;"> </span></li>выберите</span><span style=" font-size:14px; font-weight:600;">Добавить устройство</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Вы получите необходимый ПИН-код для заполнения этой формы. ПИН действителен только </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 минут</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Введите ваш PIN: + + + + Or import a file: + Или импортируйте из файла: + + + + Link from exported account archive file + Привязать из архивного файла аккаунта + + + + + + (None) + (нет) + + + + Password: + Пароль: + + + + + Profile + Профиль + + + + + Profile name + Имя профиля + + + + Account + Аккаунт + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Зарегистрируйте свое имя пользователя. +После регистрации только вы сможете его использовать. Ваши друзья смогут связаться с вами по вашему имени вместо ID. + + + + Public username checkbox + Флажок публичного имени пользователя + + + + Checkbox selecting if the user wants a public username + Флажок, если пользователь хочет публичное имя пользователя + + + + Register public username + Зарегистрировать публичное имя + + + + Public username edit + Изменить публичное имя пользователя + + + + Choose your username + Выберите ваше имя пользователя + + + + Password text input + Ввод текста пароля + + + + Password text entry + Ввод текста пароля + + + + + Password + Пароль + + + + + Password confirmation text input + Ввод текста подтверждения пароля + + + + Confirm password + Подтверждение пароля + + + + SIP Account + Аккаунт SIP + + + + SIP Server edit + Изменить сервер SIP + + + + Server + Сервер + + + + SIP proxy input + Ввод SIP Прокси + + + + SIP proxy text entry + SIP прокси текстовый ввод + + + + Proxy + Прокси + + + + SIP username input + SIP ввод имени пользователя + + + + SIP Password text entry + Ввод SIP пароля + + + + Username + Имя пользователя + + + + + SIP Password text input + текстовое поле SIP + + + + Generating your Jami account… + Идёт создание вашего аккаунта Jami… + + + + Previous page button + кнопка Предыдущая страница + + + + push button to access previous page of wizard + нажмите кнопку для перехода к предыдущей странице Мастера + + + + Previous + Назад + + + + Cancel account create/link + Отмена создание/ссылки аккаунта + + + + push button to cancel account creation or linking + нажмите кнопку, чтобы отменить аккаунта или привязки + + + + Back + Назад + + + + Next page Button + Кнопка Следующая страница + + + + Push button to access next page of wizard + Нажмите кнопку для перехода к следующей странице Мастера + + + + Next + Далее + + + + Open File + Открыть файл + + + + Jami archive files (*.gz); All files (*) + Архивы Jami (*.gz); Все файлы (*) + + + + Your account needs to be migrated. Enter your password. + Ваша учетная запись должна быть перенесена. Введите ваш пароль. + + + + Migrating your Jami account... + Переносим ваш аккаунт Jami… + + + + Importing account archive... + Импорт архива с аккаунтом… + + + + Generating your Jami account... + Идёт создание вашего аккаунта Jami… + + + + Generating your SIP account... + Сгенерировать свой SIP аккаунт... + + + + Error creating account + Ошибка при создании аккаунта + + + + PasswordDialog + + + Change Account Password + Изменить пароль аккаунта + + + + Enter Current Password + Введите текущий пароль + + + + Enter New Password + Введите новый пароль + + + + Confirm New Password + Подтвердите новый пароль + + + + Confirm + Подтвердить + + + + Cancel + Отмена + + + + Current Password Incorrect + Текущий пароль неправилен + + + + PhotoBoothDialog + + + Photobooth + Фотобудка + + + + PhotoboothWidget + + + Form + Форма + + + + Photobooth display + Дисплей фото + + + + Choose File + Выберите файл + + + + Image Files (*.jpg *.jpeg *.png) + Файлы изображений (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Выберите файл + + + + Files (*) + Файлы (*) + + + + QObject + + + No default mail client found + Не найден почтовый клиент по умолчанию + + + + + Edit Device Name + Изменить имя устройства + + + + Unlink Device From Account + Отвязать устройство от аккаунта + + + + Save new device name + Сохранить новое имя устройства + + + + Add as contact + Добавить как контакт + + + + RingButton + + + Select folder + Выбрать папку + + + + SettingsWidget + + + Form + Форма + + + + Settings + Настройки + + + + Account + Аккаунт + + + + + General + Общие + + + + + Audio / Video + Аудио/Видео + + + + System + Система + + + + Enable desktop notifications + Включить уведомления + + + + Keep minimized on close + Сворачивать вместо закрытия + + + + Download folder + Папка для загрузок + + + + Save in + Сохранить в + + + + Always Recording + Всегда записывать + + + + Updates + Обновления + + + + Check for updates automatically every + Проверять обновления автоматически каждые + + + + Interval between update checks in days selector + Интервал проверки обновлений (дней) + + + + days + дней + + + + Check for updates now button + Кнопка Проверить обновления + + + + Check for updates now + Проверить обновления + + + + Password + Пароль + + + + + Enable + Задействовать + + + + toggle enable notifications + переключатель активации уведомлений + + + + Toggle keep minimized on close + Пеоекючатель Сворачивать при закрытии + + + + Call Recordings + Записи звонков + + + + Toggle automatic updates + Переключение автоматических обновлений + + + + Jami Account + Аккаунт Jami + + + + + Profile + Профиль + + + + + Identity + Идентификатор + + + + Id + ID + + + + Registered name + Зарегистрированное имя + + + + Type here to register a username + Введите имя пользователя для регистрации + + + + Register + Зарегистрировать + + + + Change Password + Изменить пароль + + + + Export Account + Экспортировать учетную запись + + + + + Delete Account + Удалить аккаунт + + + + Linked Devices + Привязанные устройства + + + + Link Another Device + Привязать другое устройство + + + + Banned Contacts + Заблокированные контакты + + + + Format + Формат + + + + Video device framerate selector + Селектор частоты кадров видео + + + + Preview unavailable + Предпросмотр недоступен + + + + + Advanced Account Settings + Дополнительные настройки аккаунта + + + + SIP Account + Аккаунт SIP + + + + Username + Имя пользователя + + + + Hostname + Имя узла + + + + Proxy + Прокси + + + + Audio + Звук + + + + Microphone + Микрофон + + + + Audio input device selector + Выбор входного аудиоустройства + + + + Output Device + Устройство вывода + + + + Choose the output device + Выберите устройство вывода + + + + Video + Видео + + + + Device + Устройство + + + + Video device selector + Селектор видеоустройств + + + + A registered name should not have any spaces and must be at least three letters long + Зарегистрированное имя пользователя не должно содержать пробелов и быть как минимум три символа длиной + + + + This name is already taken + Это имя пользователя уже занято + + + + + Enter an alias + Введите псевдоним + + + + Register this name + Зарегистрировать новое имя + + + + Remove Device + Удалить устройство + + + + Enter this account's password to confirm the removal of this device + Введите пароль аккаунта для удаления этого устройства + + + + Are you sure you wish to remove this device? + Вы действительно хотите удалить это устройство? + + + + Export Account Here + Экспортировать аккаунт сюда + + + + Select A Folder For Your Downloads + Выберите папку для ваших загрузок + + + + Select A Folder For Your Recordings + Выберите папку для ваших записей + + + + VideoOverlay + + + Call on Hold + Звонок приостановлен + + + + Hold / Unhold + Приостановить / Возобновить + + + + Chat + Чат + + + + Mute Mic + Выключить микрофон + + + + Record call + Записать разговор + + + + Name label + Название метки + + + + Time elapsed + Прошло времени + + + + 00:00 + 00:00 + + + + Hangup + Повесить трубку + + + + Mute Video + Выключить видео + + + + VideoView + + + Share entire screen + Транслировать весь экран + + + + Share screen area + Транслировать область экрана + + + + Share file + Поделиться файлом + + + \ No newline at end of file diff --git a/translations/ring_client_windows_ru_RU.ts b/translations/ring_client_windows_ru_RU.ts new file mode 100644 index 00000000..a67fb577 --- /dev/null +++ b/translations/ring_client_windows_ru_RU.ts @@ -0,0 +1,1606 @@ + + + AboutDialog + + + + About + О программе + + + + about button + кнопка "О нас" + + + + credits button + Кнопка "создатели" + + + + Credits + Благодарности + + + + Free as in Freedom + Свобода + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Клиент Jami для Microsoft Windows. +Jami это защищённое и распределённое ПО для коммуникации. + + + + version + Версия + + + + Created by: + Создано: + + + + Artwork by: + Художник: + + + + Based on the SFLPhone project + Основано на SFLPhone проекте + + + + AccountItemDelegate + + + Add Account + Добавить аккаунт + + + + AdvancedSIPSettingsWidget + + + Form + Форма + + + + Call Settings + Настройки вызова + + + + Auto Answer Calls + Автоматический ответ на вызов + + + + Enable Custom Ringtone + Включить свой рингтон + + + + Connectivity + Связь + + + + STUN Address + STUN Адрес + + + + Use STUN + Использовать STUN + + + + Use UPnP + Использовать UPnP + + + + Use TURN + Использовать TURN + + + + TURN Password + TURN Пароль + + + + TURN Username + TURN Имя пользователя + + + + TURN Address + TURN Адрес + + + + Media + Медиа + + + + Enable Video + Включить видео + + + + Video Codecs + Видеокодеки + + + + Audio Codecs + Аудиокодеки + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Аудиофайлы (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Добавить свой рингтон + + + + AdvancedSettingsWidget + + + Form + Форма + + + + Call Settings + Настройки вызова + + + + Allow Calls From Untrusted Peers + Разрешить звонки из ненадёжных пиров + + + + Auto Answer Calls + Автоматический ответ на вызов + + + + Enable Custom Ringtone + Включить свой рингтон + + + + Add a custom ringtone + Добавить свой рингтон + + + + Name Server + Имя Сервера + + + + Address + Адрес + + + + OpenDHT Configuration + Конфигурация OpenDHT + + + + Enable Proxy + Включить Прокси + + + + Bootstrap + Начальная загрузка + + + + Security + Безопасность + + + + Private Key Password + Пароль закрытого ключа + + + + User Certificate + Сертификат пользователя + + + + Private Key + Приватный Ключ + + + + CA Certificate + Сертификат удостоверяющего центра + + + + Connectivity + Связь + + + + Use STUN + Использовать STUN + + + + STUN Address + STUN Адрес + + + + Use UPnP + Использовать UPnP + + + + Use TURN + Использовать TURN + + + + TURN Password + TURN Пароль + + + + TURN Username + TURN Имя пользователя + + + + TURN Address + TURN адрес + + + + Media + Медиа + + + + Enable Video + Включить видео + + + + Audio Codecs + Аудиокодеки + + + + Video Codecs + Видеокодеки + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Аудиофайлы (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Форма + + + + BannedItemWidget + + + Form + Форма + + + + name + имя + + + + id + идентификатор + + + + CallWidget + + + Show conversations + Показать переписку + + + + Conversations + Беседы + + + + Search contact text input + Поле ввода для поиска контактов + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami — свободная программа для связи, уважающая свободу и конфиденциальность пользователей. + + + + + This is your ID. +Copy and share it with your friends! + + +Это ваш ID. +Скопируйте и поделитесь им со своими друзьями! + + + + + Show ring ID QR code + Показать ring ID QR код + + + + Share ring ID button + Кнопка Поделиться ring ID + + + + Double-click to copy + Double-click to copy + + + + Error while generating QR Code + Ошибка при генерации QR кода + + + + + best name + лучшее имя + + + + best Id + лучший Id + + + + Back to homepage button + Кнопка вернуться на главную + + + + Add to contacts + Добавить контакт + + + + Show invites + Показать приглашения + + + + Invites + Приглашения + + + + + Find a new or existing contact + Найти новый или уже существующий контакт + + + + Wants to talk to you! + Хочет поговорить с вами! + + + + Answer incoming call button + Кнопка ответа на звонок + + + + Ignore incoming call button + Кнопка сброса входящего вызова + + + + Answer + Ответ + + + + Ignore + Игнорировать + + + + Cancel outgoing call + Отменить исходящий вызов + + + + Cancel + Отмена + + + + Start video call + Начать видеозвонок + + + + Start audio call + Начать аудиозвонок + + + + Clear conversation + Очистить переписку + + + + Remove contact + Удалить контакт + + + + Block contact + Заблокировать контакт + + + + Copy number + Скопировать номер + + + + Search your received invitations + Поиск полученных приглашений + + + + Contact me on Jami + Свяжитесь со мной через Jami + + + + My Id is : + Мой Id : + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Входящий вызов от %1 + + + + DeleteAccountDialog + + + Account deletion + Удаление аккаунта + + + + Do you really want to delete the following account? + Вы действительно хотите удалить привязанный аккаунт? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Если этот аккаунт не был экспортирован или добавлен на другом устройстве, он будет безвозвратно потерян. + + + + Permanently delete account + Удалить аккаунт навсегда + + + + Delete + Удалить + + + + Cancel account deletion + Отменить удаление аккаунта + + + + Cancel + Отмена + + + + DeviceItemWidget + + + Form + Форма + + + + Device Id + Id Устройства + + + + this device + это устройство + + + + InviteButtonsWidget + + + Accept + Принять + + + + Refuse + Отказать + + + + Block + Заблокировать + + + + LinkDeviceDialog + + + Dialog + Диалог + + + + Enter your account password + Введите пароль вашего аккаунта + + + + Password + Пароль + + + + Ok + Да + + + + Cancel + Отмена + + + + Exporting account + Экспорт аккаунта + + + + Your PIN is + Ваш PIN + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Этот pin-код и пароль вашего аккаунта должны быть введены в течении 10 минут. + + + + Close + Закрыть + + + + Link Another Device + Привязать Другое Устройство + + + + Incorrect password + Неверный пароль + + + + Something went wrong. +Please try again later. + Что-то пошло не так. +Пожалуйста, попробуйте позже. + + + + MainWindow + + + Settings + Настройки + + + + Exit + Выход + + + + About + О продукте + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Задать Зарегистрированное Имя + + + + Enter your account password + Введите пароль вашего аккаунта + + + + Password text input + Поле ввода пароля + + + + Password text entry + Поле ввода пароля + + + + Password + Пароль + + + + Register + Зарегистрировать + + + + Cancel + Отмена + + + + Registering Name + Зарегистрировать Имя + + + + Something went wrong + Что-то пошло не так + + + + Close + Закрыть + + + + Incorrect password + Неверный пароль + + + + Network error + Ошибка сети + + + + NewWizardWidget + + + Form + Форма + + + + Welcome Label + Заголовок приветствия + + + + Welcome to + Добро пожаловать в + + + + Welcome Logo + Приветствующий логотип + + + + Create Jami account button + Создать Jami аккаунт кнопка + + + + Push button for Jami account creation start trigger + Кнопка для начала создания аккаунта Jami + + + + Create a Jami account + Создать учётную запись Jami + + + + Link device button + Кнопка привязки устройства + + + + Push button for device linkage start trigger + Кнопка для запуска триггера привязки устройства + + + + Link this device to an account + Привязать это устройство к аккаунту + + + + Create Jami SIP account button + Кнопка Создать Jami SIP аккаунт + + + + Push button for Jami SIP account creation start trigger + Кнопка для запуска запуска создания аккаунта JAMI SIP + + + + Create a SIP account + Создать SIP аккаунт + + + + Link this device to an existing account + Привязать это устройство к существующему аккаунту + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Чтобы привязать это устройство к другому аккаунту, вам нужно</span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">получить PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">код. Чтобы сгенерировать PIN код нужно:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Перейдите </span><span style=" font-size:14px; font-weight:600;">Управление аккаунтом</span><span style=" font-size:14px;"> предыдущего устройства</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Выберите </span><span style=" font-size:14px; font-weight:600;">Jami аккаунт</span><span style=" font-size:14px;"> который вы хотите использовать</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Перейдите во </span><span style=" font-size:14px; font-weight:600;">вкладку</span><span style=" font-size:14px;">Устройства </span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Выберите </span><span style=" font-size:14px; font-weight:600;">Добавить устройство</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Вы получите необходимый PIN-код для заполнения этой формы. PIN-код действителен </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 минут</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Введите ваш PIN: + + + + Or import a file: + Или импортируйте из файла: + + + + Link from exported account archive file + Ссылка из экспортированного файла архива аккаунта + + + + + + (None) + (Никто) + + + + Password: + Пароль: + + + + + Profile + Профиль + + + + + Profile name + Имя профиля + + + + Account + Учётная запись + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Зарегистрируйте свое имя пользователя. +Это позволит зарезервировать имя пользователя чтобы в дальнейшем его могли использовать только вы +Ваши друзьям станет доступна возможность совершать звонки по имени пользователя +вместо использования вашего ID. + + + + Public username checkbox + Чекбокс Публичного Имя пользователя + + + + Checkbox selecting if the user wants a public username + Установите флажок, если пользователь хочет использовать общедоступное имя пользователя + + + + Register public username + Зарегистрировать общедоступное имя пользователя + + + + Public username edit + Редактирование общедоступного имени пользователя + + + + Choose your username + Выберите ваше имя пользователя + + + + Password text input + Поле ввода пароля + + + + Password text entry + Поле ввода пароля + + + + + Password + Пароль + + + + + Password confirmation text input + Поле ввода подтверждения пароля + + + + Confirm password + Подтверждение пароля + + + + SIP Account + Учетная запись SIP + + + + SIP Server edit + Изменение SIP Сервер + + + + Server + Сервер + + + + SIP proxy input + SIP прокси ввод + + + + SIP proxy text entry + SIP ввод текста прокси-сервера + + + + Proxy + Прокси + + + + SIP username input + SIP ввод имя пользователя + + + + SIP Password text entry + SIP ввод пароля + + + + Username + Имя пользователя + + + + + SIP Password text input + SIP поле ввода пароля + + + + Generating your Jami account… + Идёт создание вашего аккаунта Jami… + + + + Previous page button + Кнопка предыдущая страница + + + + push button to access previous page of wizard + нажмите кнопку, чтобы вернуться к предыдущей страницы мастера + + + + Previous + Назад + + + + Cancel account create/link + Отменить создание/ссылки аккаунта + + + + push button to cancel account creation or linking + нажмите кнопку, чтобы отменить создание аккаунта или привязки + + + + Back + Назад + + + + Next page Button + Кнопка следующая страница + + + + Push button to access next page of wizard + Нажмите кнопку, чтобы перейти к следующей страницы мастера + + + + Next + Далее + + + + Open File + Открыть файл + + + + Jami archive files (*.gz); All files (*) + Архивные файлы Jami (*.gz); Все файлы (*) + + + + Your account needs to be migrated. Enter your password. + Ваша учетная запись должна быть перенесена. Введите ваш пароль. + + + + Migrating your Jami account... + Миграция вашего Jami аккаунта... + + + + Importing account archive... + Импорт архива аккаунта... + + + + Generating your Jami account... + Генерация вашего Jami аккаунта... + + + + Generating your SIP account... + Генерация вашего SIP аккаунта... + + + + Error creating account + Ошибка создания аккаунта + + + + PasswordDialog + + + Change Account Password + Поменять Пароль Аккаунта + + + + Enter Current Password + Введите Текущий Пароль + + + + Enter New Password + Введите Новый Пароль + + + + Confirm New Password + Подтвердите Новый Пароль + + + + Confirm + Подтвердить + + + + Cancel + Отмена + + + + Current Password Incorrect + Текущий Пароль Неверный + + + + PhotoBoothDialog + + + Photobooth + Фото + + + + PhotoboothWidget + + + Form + Форма + + + + Photobooth display + Фото дисплей + + + + Choose File + Выбрать файл + + + + Image Files (*.jpg *.jpeg *.png) + Изображения (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Выбрать файл + + + + Files (*) + Файлы (*) + + + + QObject + + + No default mail client found + Не найден почтовый клиент используемый по умолчанию + + + + + Edit Device Name + Изменить Имя Устройства + + + + Unlink Device From Account + Отвязать Устройство От Аккаунта + + + + Save new device name + Сохранить новое имя устройства + + + + Add as contact + Добавить как контакт + + + + RingButton + + + Select folder + Выбрать папку + + + + SettingsWidget + + + Form + Форма + + + + Settings + Настройки + + + + Account + Учётная запись + + + + + General + Основные + + + + + Audio / Video + Аудио / Видео + + + + System + Система + + + + Enable desktop notifications + Включить уведомления на рабочем столе + + + + Keep minimized on close + Оставлять свернутым при закрытии + + + + Download folder + Папка для загрузок + + + + Save in + Сохранить в + + + + Always Recording + Всегда записывать + + + + Updates + Обновления + + + + Check for updates automatically every + Проверять обновления каждые + + + + Interval between update checks in days selector + Селектор Интервал между проверками обновлений в днях + + + + days + дней + + + + Check for updates now button + Кнопка для проверки обновлений + + + + Check for updates now + Проверить наличие обновлений + + + + Password + Пароль + + + + + Enable + Включить + + + + toggle enable notifications + Переключатель включить уведомлений + + + + Toggle keep minimized on close + Переключатель свернуть или закрыть + + + + Call Recordings + Записи вызовов + + + + Toggle automatic updates + Переключение автоматического обновления + + + + Jami Account + Jami Аккаунт + + + + + Profile + Профиль + + + + + Identity + Идентичность + + + + Id + Идентификатор + + + + Registered name + Зарегистрированное имя + + + + Type here to register a username + Напишите здесь чтобы зарегистрировать имя пользователя + + + + Register + Зарегистрировать + + + + Change Password + Изменить Пароль + + + + Export Account + Экспортировать Аккаунт + + + + + Delete Account + Удалить Аккаунт + + + + Linked Devices + Привязанные Устройства + + + + Link Another Device + Привязать Другое Устройство + + + + Banned Contacts + Заблокированные Контакты + + + + Format + Формат + + + + Video device framerate selector + Селектор частоты кадров видеоустройства + + + + Preview unavailable + Предварительный просмотр недоступен + + + + + Advanced Account Settings + Дополнительные Настройки Аккаунта + + + + SIP Account + Учетная запись SIP + + + + Username + Имя пользователя + + + + Hostname + Имя компьютера + + + + Proxy + Прокси + + + + Audio + Звук + + + + Microphone + Микрофон + + + + Audio input device selector + Селектор устройства аудиовхода + + + + Output Device + Устройства Вывода + + + + Choose the output device + Выбрать устройство вывода + + + + Video + Видео + + + + Device + Устройство + + + + Video device selector + Селектор видеоустройств + + + + A registered name should not have any spaces and must be at least three letters long + Зарегистрированное имя не должно содержать пробелов и должно иметь не менее трех букв + + + + This name is already taken + Это имя уже занято + + + + + Enter an alias + Введите псевдоним + + + + Register this name + Зарегистрировать это имя + + + + Remove Device + Удалить устройство + + + + Enter this account's password to confirm the removal of this device + Введите пароль вашего аккаунта чтобы подтвердить его удаление из устройства. + + + + Are you sure you wish to remove this device? + Вы уверены, что хотите удалить это устройство? + + + + Export Account Here + Экспортировать Аккаунт + + + + Select A Folder For Your Downloads + Выберите папку для загрузок + + + + Select A Folder For Your Recordings + Выберите Папку Для Ваших Записей + + + + VideoOverlay + + + Call on Hold + Вызов на удержании + + + + Hold / Unhold + Удерживание / Возврат + + + + Chat + Чат + + + + Mute Mic + Отключить микрофон + + + + Record call + Записывать звонок + + + + Name label + Имя метки + + + + Time elapsed + Прошедшее время + + + + 00:00 + 00:00 + + + + Hangup + Разъединить + + + + Mute Video + Громкость видео + + + + VideoView + + + Share entire screen + Транслировать весь экран + + + + Share screen area + Общий доступ к экрану + + + + Share file + Поделится файлом + + + \ No newline at end of file diff --git a/translations/ring_client_windows_sk_SK.ts b/translations/ring_client_windows_sk_SK.ts new file mode 100644 index 00000000..c7f2a9b9 --- /dev/null +++ b/translations/ring_client_windows_sk_SK.ts @@ -0,0 +1,1606 @@ + + + AboutDialog + + + + About + O programe + + + + about button + tlačidlo o programe + + + + credits button + tlačidlo autori + + + + Credits + Autori + + + + Free as in Freedom + Free as in Freedom + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Microsoft Windows klient pre Jami. +Jami je zabezpečený a distribuovaný komunikačný softvér. + + + + version + verzia + + + + Created by: + Vytvorili: + + + + Artwork by: + Dizajn od: + + + + Based on the SFLPhone project + Založené na projekte SFLPhone + + + + AccountItemDelegate + + + Add Account + Pridať účet + + + + AdvancedSIPSettingsWidget + + + Form + Form + + + + Call Settings + Nastavenia hovoru + + + + Auto Answer Calls + Automaticky zdvihnúť hovory + + + + Enable Custom Ringtone + Povoliť vlastné vyzváňacie tóny + + + + Connectivity + Pripojenia + + + + STUN Address + STUN adresa + + + + Use STUN + Použiť STUN + + + + Use UPnP + Používať UPnP + + + + Use TURN + Použiť TURN + + + + TURN Password + TURN heslo + + + + TURN Username + TURN používateľské meno + + + + TURN Address + TURN adresa + + + + Media + Médiá + + + + Enable Video + Povoliť video + + + + Video Codecs + Video kodeky + + + + Audio Codecs + Zvukové kodeky + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Audio súbory (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Pridať vlastný vyzváňací tón + + + + AdvancedSettingsWidget + + + Form + Formulár + + + + Call Settings + Nastavenia hovoru + + + + Allow Calls From Untrusted Peers + Povoliť hovory od nedôveryhodných peerov + + + + Auto Answer Calls + Automaticky zdvihnúť hovory + + + + Enable Custom Ringtone + Povoliť vlastné vyzváňacie tóny + + + + Add a custom ringtone + Pridať vlastný vyzváňací tón + + + + Name Server + Name server + + + + Address + Adresa + + + + OpenDHT Configuration + Konfigurácia OpenDHT + + + + Enable Proxy + Povoliť proxy + + + + Bootstrap + Bootstrap + + + + Security + Bezpečnosť + + + + Private Key Password + Heslo privátneho kľúča + + + + User Certificate + Používateľský certifikát + + + + Private Key + Privátny kľúč + + + + CA Certificate + CA certifikát + + + + Connectivity + Pripojenie + + + + Use STUN + Použiť STUN + + + + STUN Address + STUN adresa + + + + Use UPnP + Používať UPnP + + + + Use TURN + Použiť TURN + + + + TURN Password + TURN heslo + + + + TURN Username + TURN používateľské meno + + + + TURN Address + TURN adresa + + + + Media + Médiá + + + + Enable Video + Zapnúť video + + + + Audio Codecs + Zvukové kodeky + + + + Video Codecs + Video kodeky + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Audio súbory (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formulár + + + + BannedItemWidget + + + Form + Form + + + + name + meno + + + + id + id + + + + CallWidget + + + Show conversations + Zobraziť konverzácie + + + + Conversations + Konverzácie + + + + Search contact text input + Vstupné pole Hľadať v kontaktoch + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami je slobodný softvér na univerzálnu komunikáciu, ktorý rešpektuje slobodu a súkromie používateľov. + + + + + This is your ID. +Copy and share it with your friends! + + + Toto je vaše ID. +Skopírujte a zdieľajte ho s vašimi priateľmi! + + + + + Show ring ID QR code + Zobraziť RingID QR kód + + + + Share ring ID button + zdieľať RingID tlačidlo + + + + Double-click to copy + Dvojklik na kopírovanie + + + + Error while generating QR Code + Chyba pri generovaní QR kódu + + + + + best name + najlepšie meno + + + + best Id + najlepšie id + + + + Back to homepage button + Tlačidlo Späť domov + + + + Add to contacts + Pridať do kontaktov + + + + Show invites + Zobraziť pozvánky + + + + Invites + Pozvánky + + + + + Find a new or existing contact + Vyhľadať nový alebo existujúci kontakt + + + + Wants to talk to you! + Chce s vami hovoriť! + + + + Answer incoming call button + Tlačidlo Zdvihnúť prichádzajúci hovor + + + + Ignore incoming call button + Tlačidlo Ignorovať prichádzajúci hovor + + + + Answer + Zdvihnúť + + + + Ignore + Ignorovať + + + + Cancel outgoing call + Zrušiť odchádzajúci hovor + + + + Cancel + Zrušiť + + + + Start video call + Začať video hovor + + + + Start audio call + Začať audio hovor + + + + Clear conversation + Vymazať konverzáciu + + + + Remove contact + Odstrániť kontakt + + + + Block contact + Zablokovať kontakt + + + + Copy number + Skopírovať číslo + + + + Search your received invitations + Prehľadávať prijaté pozvánky + + + + Contact me on Jami + Kontaktujte ma na Jami + + + + My Id is : + Moje ID je: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Prichádzajúci hovor od %1 + + + + DeleteAccountDialog + + + Account deletion + Odstraňovanie účtu + + + + Do you really want to delete the following account? + Určite chcete odstrániť nasledujúci účet? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Ak tento účet nebol exportovaný alebo pridaný k inému zariadeniu, bude nenávratne stratený. + + + + Permanently delete account + Permanentne odstrániť účet + + + + Delete + Vymazať + + + + Cancel account deletion + Zrušiť odstraňovanie účtu + + + + Cancel + Zrušiť + + + + DeviceItemWidget + + + Form + Form + + + + Device Id + Id zariadenia + + + + this device + toto zariadenie + + + + InviteButtonsWidget + + + Accept + Prijať + + + + Refuse + Odmietnuť + + + + Block + Blokovať + + + + LinkDeviceDialog + + + Dialog + Dialog + + + + Enter your account password + Zadajte heslo k účtu + + + + Password + Heslo + + + + Ok + OK + + + + Cancel + Zrušiť + + + + Exporting account + Exportovanie účtu + + + + Your PIN is + Váš PIN je + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Tento PIN a heslo k účtu musia byť zadané na vašom zariadení v priebehu 10 minút. + + + + Close + Zavrieť + + + + Link Another Device + Prepojiť ďalšie zariadenie + + + + Incorrect password + Nesprávne heslo + + + + Something went wrong. +Please try again later. + Došlo k chybe. +Skúste prosím neskôr. + + + + MainWindow + + + Settings + Nastavenia + + + + Exit + Odísť + + + + About + O programe + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Nastaviť registrované meno + + + + Enter your account password + Zadajte heslo k účtu + + + + Password text input + Textové pole na zadanie hesla + + + + Password text entry + Heslo + + + + Password + Heslo + + + + Register + Registrovať + + + + Cancel + Zrušiť + + + + Registering Name + Meno sa registruje + + + + Something went wrong + Došlo k chybe + + + + Close + Zavrieť + + + + Incorrect password + Nesprávne heslo + + + + Network error + Chyba siete + + + + NewWizardWidget + + + Form + Formulár + + + + Welcome Label + Popis Vitajte + + + + Welcome to + Vitajte v + + + + Welcome Logo + Vitajte Logo + + + + Create Jami account button + Tlačidlo Vytvoriť Jami účet + + + + Push button for Jami account creation start trigger + Push button for Jami account creation start trigger + + + + Create a Jami account + Vytvoriť Jami účet + + + + Link device button + Tlačidlo Prepojiť zariadenie + + + + Push button for device linkage start trigger + Push button for device linkage start trigger + + + + Link this device to an account + Prepojiť toto zariadenie s účtom + + + + Create Jami SIP account button + Tlačidlo Vytvoriť Jami SIP účet + + + + Push button for Jami SIP account creation start trigger + Stlačte tlačidlo na spustenie vytvorenia Jami SIP účtu + + + + Create a SIP account + Vytvoriť SIP účet + + + + Link this device to an existing account + Prepojiť toto zariadenie s existujúcim účtom + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Na prepojenie zariadenia s účtom musíte najprv </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">získať PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> kód. Na jeho vygenerovanie:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choďte do </span><span style=" font-size:14px; font-weight:600;">Nastavení účtu</span><span style=" font-size:14px;"> na predchádzajúcom zariadení</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Zvoľte </span><span style=" font-size:14px; font-weight:600;">Jami účet,</span><span style=" font-size:14px;"> ktorý chcete použiť</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choďte do </span><span style=" font-size:14px; font-weight:600;">Zariadenia</span><span style=" font-size:14px;"> záložky</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Zvoľte </span><span style=" font-size:14px; font-weight:600;">Pridať zariadenie</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Tak získate PIN kód na dokončenie vyplnenie formuláru. Bude platný </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minút</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Zadajte váš PIN: + + + + Or import a file: + Alebo importujte súbor: + + + + Link from exported account archive file + Prepojiť pomocou exportovaného archívu účtu + + + + + + (None) + (Žiadny) + + + + Password: + Heslo: + + + + + Profile + Profil + + + + + Profile name + Profilové meno + + + + Account + Účet + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Zaregistrujte si svoje používateľské meno. +Tým bude toto meno rezervované a iba vy ho budete môcť používať. +Vaši priatelia ho budú môcť použiť namiesto vášho ID, +keď vám budú chcieť zavolať. + + + + Public username checkbox + Zaškrtávacie políčko pre verejné používateľské meno + + + + Checkbox selecting if the user wants a public username + Zaškrtávacie políčko na výber toho, či používateľ chce verejné používateľské meno + + + + Register public username + Registrovať verejné používateľské meno + + + + Public username edit + Upravenie verejného používateľského mena + + + + Choose your username + Zvoľte si používateľské meno + + + + Password text input + Textové pole na zadanie hesla + + + + Password text entry + Heslo + + + + + Password + Heslo + + + + + Password confirmation text input + Textové pole na potvrdenie hesla + + + + Confirm password + Potvrďte heslo + + + + SIP Account + SIP Účet + + + + SIP Server edit + SIP server úprava + + + + Server + Server + + + + SIP proxy input + Textové pole SIP proxy + + + + SIP proxy text entry + SIP proxy + + + + Proxy + Proxy + + + + SIP username input + Vstup pre SIP používateľské meno + + + + SIP Password text entry + SIP heslo + + + + Username + Používateľské meno + + + + + SIP Password text input + Textové pole na zadanie SIP hesla + + + + Generating your Jami account… + Generovanie vášho Jami účtu... + + + + Previous page button + Tlačidlo Predchádzajúca strana + + + + push button to access previous page of wizard + stlačiť tlačidlo na návrat na predchádzajúcu stranu + + + + Previous + Predchádzajúci + + + + Cancel account create/link + Zrušiť vytváranie/prepájanie účtu + + + + push button to cancel account creation or linking + push button to cancel account creation or linking + + + + Back + Späť + + + + Next page Button + Tlačidlo Ďalšia strana + + + + Push button to access next page of wizard + Stlačte tlačidlo na prechod na ďalšiu stranu nastavenia + + + + Next + Ďalej + + + + Open File + Otvoriť súbor + + + + Jami archive files (*.gz); All files (*) + Súbory s Jami archívmi (*.gz); Všetky súbory (*) + + + + Your account needs to be migrated. Enter your password. + Váš účet musí byť migrovaný. Zadajte vaše heslo. + + + + Migrating your Jami account... + Migrovanie vášho Jami účtu... + + + + Importing account archive... + Importuje sa archív s účtom + + + + Generating your Jami account... + Generovanie vášho Jami účtu... + + + + Generating your SIP account... + Váš SIP účet sa generuje... + + + + Error creating account + Chyba pri vytváraní účtu + + + + PasswordDialog + + + Change Account Password + Zmeniť heslo účtu + + + + Enter Current Password + Zadajte súčasné heslo + + + + Enter New Password + Zadajte nové heslo + + + + Confirm New Password + Potvrďte nové heslo + + + + Confirm + Potvrdiť + + + + Cancel + Zrušiť + + + + Current Password Incorrect + Nesprávne súčasné heslo + + + + PhotoBoothDialog + + + Photobooth + Fotobúdka + + + + PhotoboothWidget + + + Form + Form + + + + Photobooth display + Displej fotobúdky + + + + Choose File + Vyberte súbor + + + + Image Files (*.jpg *.jpeg *.png) + Obrazové súbory (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Vyberte súbor + + + + Files (*) + Súbory (*) + + + + QObject + + + No default mail client found + Nebol nájdený predvolený emailový klient + + + + + Edit Device Name + Upraviť názov zariadenia + + + + Unlink Device From Account + Zrušiť prepojenie zariadenia s účtom + + + + Save new device name + Uložiť nový názov zariadenia + + + + Add as contact + Pridať ako kontakt + + + + RingButton + + + Select folder + Vybrať priečinok + + + + SettingsWidget + + + Form + Formulár + + + + Settings + Nastavenia + + + + Account + Účet + + + + + General + Všeobecné + + + + + Audio / Video + Audio / Video + + + + System + Systém + + + + Enable desktop notifications + Povoliť desktopové notifikácie + + + + Keep minimized on close + Ponechať minimalizované namiesto vypnutia + + + + Download folder + Priečinok na stiahnutie + + + + Save in + Uložiť v + + + + Always Recording + Neustále zaznamenávanie + + + + Updates + Aktualizácie + + + + Check for updates automatically every + Skontrolovať aktualizácie automaticky s frekvenciou + + + + Interval between update checks in days selector + Prepínač pre Interval na kontrolu aktualizácií v dňoch + + + + days + dni + + + + Check for updates now button + Tlačidlo Skontrolovať aktualizácie + + + + Check for updates now + Skontrolovať aktualizácie + + + + Password + Heslo + + + + + Enable + Povoliť + + + + toggle enable notifications + prepnúť povolenie upozornení + + + + Toggle keep minimized on close + Prepnúť ponechanie minimalizovania pri zatvorení + + + + Call Recordings + Nahrávky hovorov + + + + Toggle automatic updates + Prepnúť automatické aktualizácie + + + + Jami Account + Jami účet + + + + + Profile + Profil + + + + + Identity + Identita + + + + Id + Id + + + + Registered name + Registrované meno + + + + Type here to register a username + Pre zaregistrovanie používateľského mena píšte + + + + Register + Registrovať + + + + Change Password + Zmeniť heslo + + + + Export Account + Exportovať účet + + + + + Delete Account + Odstrániť účet + + + + Linked Devices + Prepojené zariadenia + + + + Link Another Device + Prepojiť ďalšie zariadenie + + + + Banned Contacts + Blokované kontakty + + + + Format + Formát + + + + Video device framerate selector + Výber frameratu video zariadenia + + + + Preview unavailable + Náhľad nedostupný + + + + + Advanced Account Settings + Pokročilé nastavenia účtu + + + + SIP Account + SIP Účet + + + + Username + Používateľské meno + + + + Hostname + Názov hostiteľa + + + + Proxy + Proxy + + + + Audio + Zvuk + + + + Microphone + Mikrofón + + + + Audio input device selector + Volič Zvukového vstupného zariadenia + + + + Output Device + Výstupné zariadenie + + + + Choose the output device + Zvoliť výstupné zariadenie + + + + Video + Video + + + + Device + Zariadenie + + + + Video device selector + Prepínač video zariadenia + + + + A registered name should not have any spaces and must be at least three letters long + Registrované meno by nemalo obsahovať medzery a malo by byť aspoň tri znaky dlhé + + + + This name is already taken + Toto meno už je použité + + + + + Enter an alias + Zadajte alias + + + + Register this name + Registrovať toto meno + + + + Remove Device + Odstrániť zariadenie + + + + Enter this account's password to confirm the removal of this device + Potvrďte odobranie tohto zariadenia zadaním hesla zariadenia + + + + Are you sure you wish to remove this device? + Naozaj chcete odstrániť toto zariadenie? + + + + Export Account Here + Exportovať účet sem + + + + Select A Folder For Your Downloads + Zvoľte priečinok pre sťahovanie súborov + + + + Select A Folder For Your Recordings + Zvoľte priečinok pre vaše nahrávky + + + + VideoOverlay + + + Call on Hold + Volať počas podržania + + + + Hold / Unhold + Podržať / Zrušiť podržanie + + + + Chat + Chat + + + + Mute Mic + Stlmiť mikrofón + + + + Record call + Zaznamenať hovor + + + + Name label + Menovka + + + + Time elapsed + Uplynutý čas + + + + 00:00 + 00:00 + + + + Hangup + Zložiť + + + + Mute Video + Stlmiť video + + + + VideoView + + + Share entire screen + Zdieľať celú obrazovku + + + + Share screen area + Zdieľať oblasť obrazovky + + + + Share file + Zdieľať súbor + + + \ No newline at end of file diff --git a/translations/ring_client_windows_sl.ts b/translations/ring_client_windows_sl.ts new file mode 100644 index 00000000..06f61c24 --- /dev/null +++ b/translations/ring_client_windows_sl.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + O Ring + + + + about button + + + + + credits button + + + + + Credits + Zasluge + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + različica + + + + Created by: + Ustvarjalci: + + + + Artwork by: + Grafična podoba: + + + + Based on the SFLPhone project + Osnovano na projeku SFLPhone + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + Obrazec + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Povezljivost + + + + STUN Address + + + + + Use STUN + Uporabi STUN + + + + Use UPnP + + + + + Use TURN + Uporabi TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Vsebine + + + + Enable Video + Omogoči video + + + + Video Codecs + Video kodeki + + + + Audio Codecs + Avdio kodeki + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + Obrazec + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Naslov + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + Varnost + + + + Private Key Password + Geslo osebnega ključa + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Povezljivost + + + + Use STUN + Uporabi STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Uporabi TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Vsebine + + + + Enable Video + Omogoči video + + + + Audio Codecs + Avdio kodeki + + + + Video Codecs + Video kodeki + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Obrazec + + + + BannedItemWidget + + + Form + Obrazec + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Pogovori + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + Kopiraj z dvojnim klikom + + + + Error while generating QR Code + Napaka med pripravo QR kode + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Dodaj v stike + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Želi govoriti s tabo! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Odgovori + + + + Ignore + Ignoriraj + + + + Cancel outgoing call + + + + + Cancel + Prekliči + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + Odstrani stik + + + + Block contact + + + + + Copy number + Kopiraj številko + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Dohodni klic od %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Pobriši + + + + Cancel account deletion + + + + + Cancel + Prekliči + + + + DeviceItemWidget + + + Form + Obrazec + + + + Device Id + + + + + this device + ta naprava + + + + InviteButtonsWidget + + + Accept + Sprejmi + + + + Refuse + Zavrni + + + + Block + Blokiraj + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + Geslo + + + + Ok + V redu + + + + Cancel + Prekliči + + + + Exporting account + + + + + Your PIN is + + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Zapri + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Nastavitve + + + + Exit + Izhod + + + + About + O Ring + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Geslo + + + + Register + Prijavi se + + + + Cancel + Prekliči + + + + Registering Name + + + + + Something went wrong + + + + + Close + Zapri + + + + Incorrect password + + + + + Network error + Napaka mreže + + + + NewWizardWidget + + + Form + Obrazec + + + + Welcome Label + + + + + Welcome to + Dobrodošli v + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Poveži to napravo k računu + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Poveži to napravo k obstoječemu računu + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Vnesi svoj PIN: + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Račun + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Prijavi javno ime + + + + Public username edit + + + + + Choose your username + Izberi svoje uporabniško ime: + + + + Password text input + + + + + Password text entry + + + + + + Password + Geslo + + + + + Password confirmation text input + + + + + Confirm password + Potrdi geslo + + + + SIP Account + SIP račun + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Uporabnik + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Prejšni + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Nazaj + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Naprej + + + + Open File + Odpri datoteko + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Prekliči + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + Foto stojnica + + + + PhotoboothWidget + + + Form + Obrazec + + + + Photobooth display + + + + + Choose File + Izberi datoteko + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Izberi datoteko + + + + Files (*) + Datoteke (*) + + + + QObject + + + No default mail client found + Ne najdem privzetega programa za e-pošto + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + Obrazec + + + + Settings + Nastavitve + + + + Account + Račun + + + + + General + Splošno + + + + + Audio / Video + + + + + System + Sistem + + + + Enable desktop notifications + + + + + Keep minimized on close + Minimiziraj ob zaprtju + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + Preveri posodobitve zdaj + + + + Password + Geslo + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + Identiteta + + + + Id + ID + + + + Registered name + Prijavljeno ime + + + + Type here to register a username + + + + + Register + Prijavi se + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Zavrnjeni stiki + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + SIP račun + + + + Username + Uporabnik + + + + Hostname + Strežnik + + + + Proxy + Proxy + + + + Audio + Avdio + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + Izhodna naprava + + + + Choose the output device + + + + + Video + Video + + + + Device + Naprava + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Klic na čakanju + + + + Hold / Unhold + Čakanje / Nadaljevanje + + + + Chat + Pogovor + + + + Mute Mic + Izključi mikrofon + + + + Record call + + + + + Name label + Oznaka imena + + + + Time elapsed + Pretečeni čas + + + + 00:00 + 00:00 + + + + Hangup + Prekini + + + + Mute Video + Izključi video + + + + VideoView + + + Share entire screen + Deli celoten zaslon + + + + Share screen area + Deli območje zaslona + + + + Share file + Deli datoteko + + + \ No newline at end of file diff --git a/translations/ring_client_windows_sq_AL.ts b/translations/ring_client_windows_sq_AL.ts new file mode 100644 index 00000000..3a02cfd5 --- /dev/null +++ b/translations/ring_client_windows_sq_AL.ts @@ -0,0 +1,1605 @@ + + + AboutDialog + + + + About + Mbi + + + + about button + buton Mbi + + + + credits button + buton Kreditesh + + + + Credits + Kredite + + + + Free as in Freedom + I lirë si në Liri + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Klienti Jami për Microsoft Windows. +Jami është një software komunikimi të siguruar dhe të shpërndarë. + + + + version + version + + + + Created by: + Krijuar nga: + + + + Artwork by: + Përkujdesja grafike: + + + + Based on the SFLPhone project + Bazuar në projektin SFLPhone + + + + AccountItemDelegate + + + Add Account + Shtoni Llogari + + + + AdvancedSIPSettingsWidget + + + Form + Formular + + + + Call Settings + Rregullime për Thirrjet + + + + Auto Answer Calls + Vetëpërgjigjiu Thirrjeve + + + + Enable Custom Ringtone + Aktivizo Zile Vetjake + + + + Connectivity + Aftësi lidhjeje + + + + STUN Address + Adresë STUN + + + + Use STUN + Përdor STUN + + + + Use UPnP + Përdor UPnP + + + + Use TURN + Përdor TURN + + + + TURN Password + Fjalëkalim TURN + + + + TURN Username + Emër përdoruesi TURN + + + + TURN Address + Adresë TURN + + + + Media + Media + + + + Enable Video + Aktivizo Video + + + + Video Codecs + Kodekë Video + + + + Audio Codecs + Kodekë Audio + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Kartela Audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Shtoni një zile vetjake + + + + AdvancedSettingsWidget + + + Form + Formular + + + + Call Settings + Rregullime për Thirrjet + + + + Allow Calls From Untrusted Peers + Lejo Thirrje Prej Ortakësh Jo të Besuar + + + + Auto Answer Calls + Vetëpërgjigjiu Thirrjeve + + + + Enable Custom Ringtone + Aktivizo Zile Vetjake + + + + Add a custom ringtone + Shtoni një zile vetjake + + + + Name Server + Shërbyes Emrash + + + + Address + Adresë + + + + OpenDHT Configuration + Formësim OpenDHT-je + + + + Enable Proxy + Aktivizo Ndërmjetës + + + + Bootstrap + Bootstrap + + + + Security + Siguri + + + + Private Key Password + Fjalëkalim Kyçi Privat + + + + User Certificate + Dëshmi Përdoruesi + + + + Private Key + Kyç Privat + + + + CA Certificate + Dëshmi AD + + + + Connectivity + Aftësi lidhjeje + + + + Use STUN + Përdor STUN + + + + STUN Address + Adresë STUN + + + + Use UPnP + Përdor UPnP + + + + Use TURN + Përdor TURN + + + + TURN Password + Fjalëkalim TURN + + + + TURN Username + Emër përdoruesi TURN + + + + TURN Address + Adresë TURN + + + + Media + Media + + + + Enable Video + Aktivizo Video + + + + Audio Codecs + Kodekë Audio + + + + Video Codecs + Kodekë Video + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Kartela Audio (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Formular + + + + BannedItemWidget + + + Form + Formular + + + + name + emër + + + + id + id + + + + CallWidget + + + Show conversations + Shfaq biseda + + + + Conversations + Biseda + + + + Search contact text input + Dhënie teksti kërkimi kontakti + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami është software i lirë për komunikim universal që respekton privatësinë e përdoruesve të tij. + + + + + This is your ID. +Copy and share it with your friends! + + +Kjo është ID-ja juaj. +Kopjojeni dhe jepuani shokëve! + + + + Show ring ID QR code + Shfaq kod QR ID-je Ring-u + + + + Share ring ID button + Buton Ndani ID Ringu Me të Tjerët + + + + Double-click to copy + Dyklikojeni që të kopjohet + + + + Error while generating QR Code + Gabim teksa prodhohej kodi QR + + + + + best name + Përzgjedhës shpejtësie kuadrosh për pajisje video + + + + best Id + + + + + Back to homepage button + Buton Mbrapsht te faqja hyrëse + + + + Add to contacts + Shtoje te kontaktet + + + + Show invites + Shfaq ftesa + + + + Invites + Ftesa + + + + + Find a new or existing contact + Gjeni një kontakt të ri ose ekzistues + + + + Wants to talk to you! + Dëshiron të bisedojë me ju! + + + + Answer incoming call button + Buton Përgjigjuni thirrjes ardhëse + + + + Ignore incoming call button + Buton Shpërfille thirrjen ardhëse + + + + Answer + Përgjigjuni + + + + Ignore + Shpërfille + + + + Cancel outgoing call + Anuloje dërgimin e thirrjes + + + + Cancel + Anuloje + + + + Start video call + Filloni thirrje video + + + + Start audio call + Filloni thirrje audio + + + + Clear conversation + Spastroje bisedën + + + + Remove contact + Hiqe kontaktin + + + + Block contact + Bllokojeni kontaktin + + + + Copy number + Kopjoje numrin + + + + Search your received invitations + Kërkoni te ftesat tuaja të marra + + + + Contact me on Jami + Lidhuni me mua në Jami + + + + My Id is : + ID-ja ime është: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Thirrje ardhur nga %1 + + + + DeleteAccountDialog + + + Account deletion + Fshirje llogarie + + + + Do you really want to delete the following account? + Doni vërtet të fshihet llogaria vijuese? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Nëse kjo llogari s’është eksportuar, ose shtuar te një tjetër pajisje, do të humbë në mënyrë të pakthyeshme. + + + + Permanently delete account + Fshije llogarinë përgjithmonë + + + + Delete + Fshije + + + + Cancel account deletion + Anulo fshirje llogarie + + + + Cancel + Anuloje + + + + DeviceItemWidget + + + Form + Formular + + + + Device Id + ID pajisjeje + + + + this device + këtë pajisje + + + + InviteButtonsWidget + + + Accept + Pranoje + + + + Refuse + Hidhe tej + + + + Block + Bllokoje + + + + LinkDeviceDialog + + + Dialog + Dialog + + + + Enter your account password + Jepni fjalëkalimin e llogarisë tuaj + + + + Password + Fjalëkalim + + + + Ok + OK + + + + Cancel + Anuloje + + + + Exporting account + Po eksportohet llogari + + + + Your PIN is + PIN-i juaj është + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Ky PIN dhe fjalëkalimi i llogarisë duhen dhënë te pajisja juaj brenda 10 minutash + + + + Close + Mbylle + + + + Link Another Device + Lidhni Një Pajisje Tjetër + + + + Incorrect password + Fjalëkalim i pasaktë + + + + Something went wrong. +Please try again later. + Diç shkoi ters. +Ju lutemi, riprovoni më vonë. + + + + MainWindow + + + Settings + Rregullime + + + + Exit + Dalje + + + + About + Mbi + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Caktoni Emër të Regjistruar + + + + Enter your account password + Jepni fjalëkalimin e llogarisë tuaj + + + + Password text input + Dhënie teksti fjalëkalimi + + + + Password text entry + Dhënie teksti fjalëkalimi + + + + Password + Fjalëkalim + + + + Register + Regjistrohuni + + + + Cancel + Anuloje + + + + Registering Name + Po Regjistrohet Emër + + + + Something went wrong + Diç shkoi ters + + + + Close + Mbylle + + + + Incorrect password + Fjalëkalim i pasaktë + + + + Network error + Gabim rrjeti + + + + NewWizardWidget + + + Form + Formular + + + + Welcome Label + Etiketë Mirëseardhjeje + + + + Welcome to + Mirë se vini te + + + + Welcome Logo + Logo Mirëseardhjeje + + + + Create Jami account button + Buton Krijoni llogari Jami + + + + Push button for Jami account creation start trigger + Buton për të shkaktuar fillimin e krijimit të llogarisë Jami + + + + Create a Jami account + Krijoni një llogari Jami + + + + Link device button + Buton Lidhni pajisje + + + + Push button for device linkage start trigger + Buton për të shkaktuar fillimin e lidhjes së një llogarie + + + + Link this device to an account + Lidheni këtë pajisje me një llogari + + + + Create Jami SIP account button + Krijoni buton llogarie SIP Jami + + + + Push button for Jami SIP account creation start trigger + Buton për të shkaktuar fillimin e krijimit të llogarisë SIP Jami + + + + Create a SIP account + Krijoni llogari SIP + + + + Link this device to an existing account + Lidheni këtë pajisje me një llogari ekzistuese + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Që të lidhni këtë pajisje me një tjetër llogari, së pari </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">lypset të merrni një kod PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">. Që të prodhoni kodin PIN:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Shkoni te </span><span style=" font-size:14px; font-weight:600;">rregullimi Administrim llogarie</span><span style=" font-size:14px;"> i një pajisje të mëparshme</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Zgjidhni </span><span style=" font-size:14px; font-weight:600;">llogarinë Jami</span><span style=" font-size:14px;"> që doni të përdoret</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Shkoni te skeda </span><span style=" font-size:14px; font-weight:600;">Pajisje</span><span style=" font-size:14px;"></span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">përzgjidhni </span><span style=" font-size:14px; font-weight:600;">Shtoni një pajisje</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Do të merrni PIN-in e nevojshëm për plotësimin e këtij formulari. PIN-i është i vlefshëm vetëm për </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minuta</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Jepni PIN-in tuaj: + + + + Or import a file: + Ose importoni një kartelë: + + + + Link from exported account archive file + Lidheni prej kartele arkivi të eksportuar llogarie + + + + + + (None) + (Asnjë) + + + + Password: + Fjalëkalim: + + + + + Profile + Profil + + + + + Profile name + Emër profili + + + + Account + Llogari + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Regjistroni emrin tuaj të përdoruesit. +Kjo do të bëjë rezervimin e emrit të përdoruesit, që kështu vetëm ju mund ta përdorni. +Shokët tuaj do të jenë në gjendje t’ju bëjnë thirrje me emrin tuaj të përdoruesit +në vend se të përdorin ID-në tuaj. + + + + Public username checkbox + Kutizë emri publik përdoruesi + + + + Checkbox selecting if the user wants a public username + Kutizë përzgjedhjeje nëse përdoruesi dëshiron një emër publik përdoruesi + + + + Register public username + Regjistroni emër publik përdoruesi + + + + Public username edit + Përpunim emri publik përdoruesi + + + + Choose your username + Zgjidhni mrin tuaj të përdoruesit + + + + Password text input + Dhënie teksti fjalëkalimi + + + + Password text entry + Dhënie teksti fjalëkalimi + + + + + Password + Fjalëkalim + + + + + Password confirmation text input + Dhënie teksti ripohimi fjalëkalimi + + + + Confirm password + Ripohoni fjalëkalimin + + + + SIP Account + Llogari SIP + + + + SIP Server edit + Përpunim Shërbyesi SIP + + + + Server + Shërbyes + + + + SIP proxy input + Dhënie ndërmjetësi SIP + + + + SIP proxy text entry + Zë teksti ndërmjetësi SIP + + + + Proxy + Ndërmjetës + + + + SIP username input + Dhënie emri përdoruesi SIP + + + + SIP Password text entry + Zë teksti Fjalëkalimi SIP + + + + Username + Emër përdoruesi + + + + + SIP Password text input + Dhënie teksti fjalëkalimi SIP + + + + Generating your Jami account… + Po prodhohet llogaria juaj Jami… + + + + Previous page button + Buton Faqja e mëparshme + + + + push button to access previous page of wizard + buton për të kaluar te faqja e mëparshme e ndihmësit + + + + Previous + I mëparshmi + + + + Cancel account create/link + Anuloni krijim/lidhje llogarie + + + + push button to cancel account creation or linking + shtypni butoni që të anulohet krijimi ose lidhja e llogarisë + + + + Back + Mbrapsht + + + + Next page Button + Buton Faqja pasuese + + + + Push button to access next page of wizard + buton për të kaluar te faqja pasuese e ndihmësit + + + + Next + Pasuesi + + + + Open File + Hapni Kartelë + + + + Jami archive files (*.gz); All files (*) + Kartela arkivi Jami (*.gz); Krejt kartelat (*) + + + + Your account needs to be migrated. Enter your password. + Lypset të migrohet llogaria juaj. Jepni fjalëkalimin tuaj. + + + + Migrating your Jami account... + Po migrohet llogaria juaj Jami… + + + + Importing account archive... + Po importohet arkiv llogarie… + + + + Generating your Jami account... + Po prodhohet llogaria juaj Jami… + + + + Generating your SIP account... + Po prodhohet llogaria juaj SIP… + + + + Error creating account + Gabim në krijimin e llogarisë + + + + PasswordDialog + + + Change Account Password + Ndryshoni Fjalëkalim Llogarie + + + + Enter Current Password + Jepni Fjalëkalimin e Tanishëm + + + + Enter New Password + Jepni Fjalëkalimin e Ri + + + + Confirm New Password + Ripohoni Fjalëkalim të Ri + + + + Confirm + Ripohoje + + + + Cancel + Anuloje + + + + Current Password Incorrect + Fjalëkalim i Tanishëm i Pasaktë + + + + PhotoBoothDialog + + + Photobooth + Kioskë fotosh + + + + PhotoboothWidget + + + Form + Formular + + + + Photobooth display + Shfaqje kioske fotosh + + + + Choose File + Zgjidhni Kartelë + + + + Image Files (*.jpg *.jpeg *.png) + Kartela Figurash (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Zgjidhni Kartelë + + + + Files (*) + Kartela (*) + + + + QObject + + + No default mail client found + S’u gjet klient parazgjedhje poste + + + + + Edit Device Name + Përpunoni Emër Pajisjeje + + + + Unlink Device From Account + Hiqini Lidhjen Prej Llogarie Kësaj Pajisjeje + + + + Save new device name + Ruani emrin e pajisjes së re + + + + Add as contact + Shtojeni si kontakt + + + + RingButton + + + Select folder + Përzgjidhni dosje + + + + SettingsWidget + + + Form + Formular + + + + Settings + Rregullime + + + + Account + Llogari + + + + + General + Të përgjithshme + + + + + Audio / Video + Audio / Video + + + + System + Sistem + + + + Enable desktop notifications + Aktivizoni njoftime nga desktopi + + + + Keep minimized on close + Mbaje të minimizuar kur dilet + + + + Download folder + Dosje Shkarkimi + + + + Save in + Ruaje te + + + + Always Recording + Përherë Në Regjistrim + + + + Updates + Përditësime + + + + Check for updates automatically every + Kontrollo vetvetiu për përditësime çdo + + + + Interval between update checks in days selector + Përzgjedhës intervali mes kontrollesh për përditësime, në ditë + + + + days + ditë + + + + Check for updates now button + Buton Kontrollo tani për përditësime + + + + Check for updates now + Kontrollo tani për përditësime + + + + Password + Fjalëkalim + + + + + Enable + Aktivizoje + + + + toggle enable notifications + aktivizo/çaktivizo njoftime + + + + Toggle keep minimized on close + Këmbe zgjedhjen për “Mbaje të minimizuar kur dilet” + + + + Call Recordings + Incizim Thirrjesh + + + + Toggle automatic updates + Aktivizo/çaktivizo përditësime të automatizuara + + + + Jami Account + Llogari Jami + + + + + Profile + Profil + + + + + Identity + Identitet + + + + Id + ID + + + + Registered name + Emër i regjistruar + + + + Type here to register a username + Shtypni këtu që të regjistroni një emër përdoruesi + + + + Register + Regjistrohuni + + + + Change Password + Ndryshoni Fjalëkalimin + + + + Export Account + Eksportoje Llogarinë + + + + + Delete Account + Fshije Llogarinë + + + + Linked Devices + Pajisje të Lidhura + + + + Link Another Device + Lidhni Një Pajisje Tjetër + + + + Banned Contacts + Kontakte të Dëbuar + + + + Format + Format + + + + Video device framerate selector + Përzgjedhës shpejtësie kuadrosh pajisjeje video + + + + Preview unavailable + S’ka paraparje + + + + + Advanced Account Settings + Rregullime të Mëtejshme Llogarie + + + + SIP Account + Llogari SIP + + + + Username + Emër përdoruesi + + + + Hostname + Strehëemër + + + + Proxy + Ndërmjetës + + + + Audio + Audio + + + + Microphone + Mikrofon + + + + Audio input device selector + Përzgjedhës pajisje audio në hyrje + + + + Output Device + Pajisje Në Dalje + + + + Choose the output device + Zgjidhni pajisje output-i + + + + Video + Video + + + + Device + Pajisje + + + + Video device selector + Përzgjedhës pajisje video + + + + A registered name should not have any spaces and must be at least three letters long + Emri i regjistruar s’duhet të përmbajë hapësira dhe duhet të jetë të paktën tre shkronja i gjatë + + + + This name is already taken + Ky emër është zënë tashmë + + + + + Enter an alias + Jepni një alias + + + + Register this name + Regjistro këtë emër + + + + Remove Device + Hiqe Pajisjen + + + + Enter this account's password to confirm the removal of this device + Që të ripohoni heqjen e kësaj pajisje, jepni fjalëkalimin e kësaj llogarie + + + + Are you sure you wish to remove this device? + Jeni i sigurt se doni të hiqet kjo pajisje? + + + + Export Account Here + Eksportoje Llogarinë Këtu + + + + Select A Folder For Your Downloads + Përzgjidhni Një Dosje Për Shkarkimet Tuaja + + + + Select A Folder For Your Recordings + Përzgjidhni Një Dosje Për Regjistrimet Tuaja + + + + VideoOverlay + + + Call on Hold + Thirrje Pezull + + + + Hold / Unhold + Mbaje Pezull/Rimerre + + + + Chat + Fjalosje + + + + Mute Mic + Heshtoje Mikrofonin + + + + Record call + Incizo thirrjen + + + + Name label + Etiketë emri + + + + Time elapsed + Kohë e rrjedhur + + + + 00:00 + 00:00 + + + + Hangup + Mbylle + + + + Mute Video + Hiqi Zërin Videos + + + + VideoView + + + Share entire screen + Nda krejt ekranin + + + + Share screen area + Ndani me të tjerët zonën e ekranit + + + + Share file + Ndani kartelë me të tjerë + + + \ No newline at end of file diff --git a/translations/ring_client_windows_sr@latin.ts b/translations/ring_client_windows_sr@latin.ts new file mode 100644 index 00000000..bd133c6f --- /dev/null +++ b/translations/ring_client_windows_sr@latin.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + Zasluge + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami je besplatan i slobodan softver za univerzalnu komunikaciju, koji poštuje slobode i privatnost svojih korisnika + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + Otkaži + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + Otkaži + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + + + + + Cancel + Otkaži + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + + + + + Cancel + Otkaži + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Napravi Jami profil + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Poveži uređaj sa profilom + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Nazad + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Otkaži + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + + + + + Account + + + + + + General + + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_sv.ts b/translations/ring_client_windows_sv.ts new file mode 100644 index 00000000..ce5f9265 --- /dev/null +++ b/translations/ring_client_windows_sv.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + Om + + + + about button + + + + + credits button + + + + + Credits + Tack till + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + version + + + + Created by: + Skapad av: + + + + Artwork by: + Bildkonst av: + + + + Based on the SFLPhone project + Baserad på SFLPhone-projektet + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + Formulär + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + Anslutningsbarhet + + + + STUN Address + + + + + Use STUN + Använd STUN + + + + Use UPnP + + + + + Use TURN + Använd TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Aktivera video + + + + Video Codecs + Videokodekar + + + + Audio Codecs + Ljudkodekar + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + Formulär + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Adress + + + + OpenDHT Configuration + + + + + Enable Proxy + Aktivera proxy + + + + Bootstrap + Bootstrap + + + + Security + Säkerhet + + + + Private Key Password + Privat nyckellösenord + + + + User Certificate + Användarcertifikat + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + Anslutningsbarhet + + + + Use STUN + Använd STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Använd TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Media + + + + Enable Video + Aktivera video + + + + Audio Codecs + Ljudkodekar + + + + Video Codecs + Videokodekar + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Formulär + + + + BannedItemWidget + + + Form + Formulär + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Konversationer + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami är fri mjukvara för universell kommunikation, med respekt för användarens frihet och integritet. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + Dubbelklicka för att kopiera + + + + Error while generating QR Code + Ett fel inträffade vid generering av QR-kod + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Lägg till i kontakter + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Vill prata med dig! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + Svara + + + + Ignore + Ignorera + + + + Cancel outgoing call + + + + + Cancel + Avbryt + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + Rensa konversation + + + + Remove contact + Ta bort kontakt + + + + Block contact + Blockera kontakt + + + + Copy number + Kopiera nummer + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Inkommande samtal från %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Ta bort + + + + Cancel account deletion + + + + + Cancel + Avbryt + + + + DeviceItemWidget + + + Form + Formulär + + + + Device Id + + + + + this device + denna enhet + + + + InviteButtonsWidget + + + Accept + Acceptera + + + + Refuse + Neka + + + + Block + Blockera + + + + LinkDeviceDialog + + + Dialog + Dialog + + + + Enter your account password + + + + + Password + Lösenord + + + + Ok + OK + + + + Cancel + Avbryt + + + + Exporting account + Exporterar konto + + + + Your PIN is + + + + + PIN + PIN-kod + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Stäng + + + + Link Another Device + + + + + Incorrect password + Felaktigt lösenord + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Inställningar + + + + Exit + Avsluta + + + + About + Om + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Lösenord + + + + Register + Registrera + + + + Cancel + Avbryt + + + + Registering Name + + + + + Something went wrong + + + + + Close + Stäng + + + + Incorrect password + Felaktigt lösenord + + + + Network error + Nätverksfel + + + + NewWizardWidget + + + Form + Formulär + + + + Welcome Label + + + + + Welcome to + Välkommen till + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + Skapa ett Jami-konto + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Koppla denna enhet till ett konto + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Koppla den här enheten till ett befintligt konto + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + Ange din PIN-kod: + + + + Or import a file: + Eller importera en fil: + + + + Link from exported account archive file + + + + + + + (None) + (Ingen) + + + + Password: + Lösenord: + + + + + Profile + Profil + + + + + Profile name + + + + + Account + Konto + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Registrera offentligt användarnamn + + + + Public username edit + + + + + Choose your username + Välj användarnamn + + + + Password text input + + + + + Password text entry + + + + + + Password + Lösenord + + + + + Password confirmation text input + + + + + Confirm password + Bekräfta lösenord + + + + SIP Account + SIP-konto + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Proxy + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Användarnamn + + + + + SIP Password text input + + + + + Generating your Jami account… + Genererar ditt Jami-konto... + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Föregående + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Tillbaka + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Nästa + + + + Open File + Öppna fil + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + Ditt konto måste migreras. Ange ditt lösenord. + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + Fel vid kontoskapande + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Avbryt + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + Photobooth + + + + PhotoboothWidget + + + Form + Formulär + + + + Photobooth display + + + + + Choose File + Välj fil + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Välj fil + + + + Files (*) + Filer (*) + + + + QObject + + + No default mail client found + Ingen ordinarie e-postklient hittades + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + Formulär + + + + Settings + Inställningar + + + + Account + Konto + + + + + General + Allmänt + + + + + Audio / Video + + + + + System + System + + + + Enable desktop notifications + + + + + Keep minimized on close + Minimera istället för att avsluta + + + + Download folder + Nerladdningsmapp + + + + Save in + Spara i + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + Sök efter uppdateringar nu + + + + Password + Lösenord + + + + + Enable + Aktivera + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Profil + + + + + Identity + Identitet + + + + Id + ID + + + + Registered name + Registrerat namn + + + + Type here to register a username + + + + + Register + Registrera + + + + Change Password + + + + + Export Account + Exportera konto + + + + + Delete Account + Ta bort konto + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Bannlysta kontakter + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + SIP-konto + + + + Username + Användarnamn + + + + Hostname + Värdnamn + + + + Proxy + Proxy + + + + Audio + Ljud + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + Utdataenhet + + + + Choose the output device + + + + + Video + Video + + + + Device + Enhet + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Parkerat samtal + + + + Hold / Unhold + Parkera / Återuppta + + + + Chat + Chatt + + + + Mute Mic + Stäng av mikrofon + + + + Record call + + + + + Name label + Namnetikett + + + + Time elapsed + Förfluten tid + + + + 00:00 + 00:00 + + + + Hangup + Lägg på + + + + Mute Video + Pausa video + + + + VideoView + + + Share entire screen + Dela hela skärmen + + + + Share screen area + Dela skärmområde + + + + Share file + Dela fil + + + \ No newline at end of file diff --git a/translations/ring_client_windows_ta.ts b/translations/ring_client_windows_ta.ts new file mode 100644 index 00000000..3eb3c948 --- /dev/null +++ b/translations/ring_client_windows_ta.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + + + + + Cancel + + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + + + + + Cancel + + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + + + + + Account + + + + + + General + பொது + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + கேள்விமாற்றி + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + காணொளி + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_tr.ts b/translations/ring_client_windows_tr.ts new file mode 100644 index 00000000..65d7db15 --- /dev/null +++ b/translations/ring_client_windows_tr.ts @@ -0,0 +1,1605 @@ + + + AboutDialog + + + + About + Hakkında + + + + about button + hakkında butonu + + + + credits button + kredi butonu + + + + Credits + Krediler + + + + Free as in Freedom + Özgürlükteki gibi ücretsiz + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Jami için Microsoft Windows istemcisi. +Jami güvenli ve dağıtık bir iletişim yazılımıdır. + + + + version + sürüm + + + + Created by: + Oluşturanlar: + + + + Artwork by: + Çizenler: + + + + Based on the SFLPhone project + SFLPhone tasarısı temel alınmıştır. + + + + AccountItemDelegate + + + Add Account + Hesap Ekle + + + + AdvancedSIPSettingsWidget + + + Form + Form + + + + Call Settings + Arama Ayarları + + + + Auto Answer Calls + Aramaları otomatik yanıtla + + + + Enable Custom Ringtone + Özel zil sesini etkinleştir + + + + Connectivity + Bağlanabilirlik + + + + STUN Address + STUN Adresi + + + + Use STUN + STUN kullan + + + + Use UPnP + UpnP kullan + + + + Use TURN + TURN kullan + + + + TURN Password + TURN Parolası + + + + TURN Username + TURN Kullanıcı Adı + + + + TURN Address + TURN Adresi + + + + Media + Ortam + + + + Enable Video + Görüntüyü Etkinleştir + + + + Video Codecs + Görüntü çözücüler + + + + Audio Codecs + Ses çözücüleri + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Ses Dosyaları (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + Özel bir zil sesi ekle + + + + AdvancedSettingsWidget + + + Form + Form + + + + Call Settings + Arama Ayarları + + + + Allow Calls From Untrusted Peers + Güvenilmeyen Eşlerden Gelen Aramalara İzin Ver + + + + Auto Answer Calls + Aramaları otomatik yanıtla + + + + Enable Custom Ringtone + Özel zil sesini etkinleştir + + + + Add a custom ringtone + Özel bir zil sesi ekle + + + + Name Server + Ad Sunucusu + + + + Address + Yer + + + + OpenDHT Configuration + OpenDHT Yapılandırması + + + + Enable Proxy + Vekil sunucuyu etkinleştir + + + + Bootstrap + Önyükleme + + + + Security + Güvenlik + + + + Private Key Password + Özel Anahtar Parolası + + + + User Certificate + Kullanıcı Sertifikası + + + + Private Key + Özel Anahtar + + + + CA Certificate + CA sertifikası + + + + Connectivity + Bağlanabilirlik + + + + Use STUN + STUN kullan + + + + STUN Address + STUN Adresi + + + + Use UPnP + UpnP kullan + + + + Use TURN + TURN kullan + + + + TURN Password + TURN Parolası + + + + TURN Username + TURN Kullanıcı Adı + + + + TURN Address + TURN Adresi + + + + Media + Ortam + + + + Enable Video + Görüntüyü Etkinleştir + + + + Audio Codecs + Ses çözücüleri + + + + Video Codecs + Görüntü çözücüler + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + Ses Dosyaları (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + Form + + + + BannedItemWidget + + + Form + Form + + + + name + isim + + + + id + kimlik + + + + CallWidget + + + Show conversations + Konuşmaları göster + + + + Conversations + Konuşmalar + + + + Search contact text input + Kişi ara + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami, kullanıcılarının özgürlüklerine ve gizliliğine önem veren, evrensel iletişim için özgür yazılımdır. + + + + + This is your ID. +Copy and share it with your friends! + + + Bu sizin kimliğiniz. +Kopyalayın ve arkadaşlarınızla paylaşın! + + + + Show ring ID QR code + Ring kimliği QR kodunu göster + + + + Share ring ID button + Ring kimliği düğmesini paylaş + + + + Double-click to copy + Kopyalamak için çift tıkla + + + + Error while generating QR Code + QR Kodu oluşturulurken hata + + + + + best name + en iyi ad + + + + best Id + en iyi kimlik + + + + Back to homepage button + Anasayfaya dön düğmesi + + + + Add to contacts + Kişilere ekle + + + + Show invites + Davetleri göster + + + + Invites + Davetler + + + + + Find a new or existing contact + Yeni veya mevcut bir kişiyi bulun + + + + Wants to talk to you! + Sizinle konuşmak istiyor! + + + + Answer incoming call button + Gelen aramayı yanıtla düğmesi + + + + Ignore incoming call button + Gelen arama düğmesini yoksay + + + + Answer + Yanıtla + + + + Ignore + Göz ardı et + + + + Cancel outgoing call + Giden aramayı iptal et + + + + Cancel + İptal + + + + Start video call + Görüntülü arama başlat + + + + Start audio call + Sesli arama başlat + + + + Clear conversation + Konuşmayı temizle + + + + Remove contact + Kişiyi kaldır + + + + Block contact + Kişiyi engelle + + + + Copy number + Numarayı kopyala + + + + Search your received invitations + Aldığınız davetiyeleri arayın + + + + Contact me on Jami + Jami'de bana ulaşın + + + + My Id is : + Kimliğim : + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + %1$s'dan gelen arama + + + + DeleteAccountDialog + + + Account deletion + Hesap silme + + + + Do you really want to delete the following account? + Aşağıdaki hesabı gerçekten silmek istiyor musunuz? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + Bu hesap dışa aktarılmadıysa veya başka bir cihaza eklenmediyse, geri döndürülemez şekilde kaybolacaktır. + + + + Permanently delete account + Hesabı kalıcı olarak sil + + + + Delete + Sil + + + + Cancel account deletion + Hesap silmeyi iptal et + + + + Cancel + İptal + + + + DeviceItemWidget + + + Form + Form + + + + Device Id + Aygıt Kimliği + + + + this device + bu aygıt + + + + InviteButtonsWidget + + + Accept + Kabul et + + + + Refuse + Geri Çevir + + + + Block + Engelleme + + + + LinkDeviceDialog + + + Dialog + Karşılıklı konuşma + + + + Enter your account password + Hesap şifrenizi girin + + + + Password + Parola + + + + Ok + Tamam + + + + Cancel + İptal + + + + Exporting account + Hesap dışa aktarılıyor + + + + Your PIN is + PIN kodunuz + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + Bu pin ve hesap şifresi 10 dakika içinde cihazınıza girilmelidir. + + + + Close + Kapat + + + + Link Another Device + Diğer aygıtı bağla + + + + Incorrect password + Geçersiz parola + + + + Something went wrong. +Please try again later. + Bir şeyler yanlış gitt. +Lütfen daha sonra yeniden deneyin. + + + + MainWindow + + + Settings + Ayarlar + + + + Exit + Çıkış + + + + About + Hakkında + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + Kayıtlı Adı Ayarla + + + + Enter your account password + Hesap şifrenizi girin + + + + Password text input + Şifre metin girişi + + + + Password text entry + Şifre metin girişi + + + + Password + Parola + + + + Register + Kaydol + + + + Cancel + İptal + + + + Registering Name + Kayıt Adı + + + + Something went wrong + Bir şeyler yanlış gitti + + + + Close + Kapat + + + + Incorrect password + Geçersiz parola + + + + Network error + Ağ hatası + + + + NewWizardWidget + + + Form + Form + + + + Welcome Label + Hoşgeldin Etiketi + + + + Welcome to + Hoş Geldiniz + + + + Welcome Logo + Hoşgeldin Logosu + + + + Create Jami account button + Jami hesabı düğmesi oluştur + + + + Push button for Jami account creation start trigger + Jami hesabı oluşturma başlatma tetikleyicisi için basma düğmesi + + + + Create a Jami account + Bir Jami hesabı oluştur + + + + Link device button + Aygıt bağla düğmesi + + + + Push button for device linkage start trigger + Jami hesabı oluşturma başlatma tetikleyicisi için basınız + + + + Link this device to an account + Bu aygıtı bir hesaba bağla + + + + Create Jami SIP account button + Jami SIP hesabı oluştur düğmesi + + + + Push button for Jami SIP account creation start trigger + Jami SIP hesabı oluşturma başlatma tetikleyicisi için basınız + + + + Create a SIP account + Bir SIP hesabı oluşturun + + + + Link this device to an existing account + Bu aygıtı var olan bir hesaba bağla + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Bu cihazı başka bir hesapla eşlemek için, önce </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;"> PIN edinmeniz gerekir</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> PIN kodu edinmek için:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Bir önceki </span><span style=" font-size:14px; font-weight:600;">cihazınızdaki hesap ayarları</span><span style=" font-size:14px;"> yönetimine gidin</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">İstediğiniz </span><span style=" font-size:14px; font-weight:600;">Jami hesabını</span><span style=" font-size:14px;"> seçin</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Cihazlar </span><span style=" font-size:14px; font-weight:600;">sekmesine </span><span style=" font-size:14px;"> gidin </li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Cihaz </span><span style=" font-size:14px; font-weight:600;"> Ekleyi seçin</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Bu formu doldurmak için gerekli PIN kodunu alacaksınız. Bu PIN sadece </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 dakika geçerlidir.</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Pin'inizi girin: + + + + Or import a file: + veya bir dosya içe aktar + + + + Link from exported account archive file + Dışa aktarılan hesap arşiv dosyasından bağlantı + + + + + + (None) + (Hiçbiri) + + + + Password: + Parola: + + + + + Profile + Profil + + + + + Profile name + Profil adı + + + + Account + Hesap + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + Kullanıcı adınızı kaydedin. +Bu, yalnızca siz kullanabilmeniz için kullanıcı adını ayıracaktır. +Arkadaşlarınız kimliğinizi kullanmak yerine +kullanıcı adınızla sizi arayabilecek. + + + + Public username checkbox + Herkese açık kullanıcı adı onay kutusu + + + + Checkbox selecting if the user wants a public username + Kullanıcının herkese açık bir kullanıcı adı isteyip istemediğini seçme onay kutusu + + + + Register public username + Halka açık kullanıcı adı kaydet + + + + Public username edit + Herkese açık kullanıcı adı düzenleme + + + + Choose your username + Kullanıcı adınızı seçin + + + + Password text input + Şifre metin girişi + + + + Password text entry + Şifre metin girişi + + + + + Password + Parola + + + + + Password confirmation text input + Şifre onay metni girişi + + + + Confirm password + Parolayı onayla + + + + SIP Account + SIP Hesabı + + + + SIP Server edit + SIP Sunucusu düzenleme + + + + Server + Sunucu + + + + SIP proxy input + SIP proxy girişi + + + + SIP proxy text entry + SIP proxy metin girişi + + + + Proxy + Vekil + + + + SIP username input + SIP kullanıcı adı girişi + + + + SIP Password text entry + SIP Parolası metin girişi + + + + Username + Kullanıcı adı + + + + + SIP Password text input + SIP Şifre metin girişi + + + + Generating your Jami account… + Jami hesabınız oluşturuluyor... + + + + Previous page button + Önceki sayfa düğmesi + + + + push button to access previous page of wizard + sihirbazın önceki sayfasına erişmek için düğmeye basın + + + + Previous + Önceki + + + + Cancel account create/link + Hesap oluşturmayı / bağlantıyı iptal et + + + + push button to cancel account creation or linking + hesap oluşturmayı veya bağlamayı iptal etmek için düğmeye basın + + + + Back + Geri + + + + Next page Button + Sonraki sayfa düğmesi + + + + Push button to access next page of wizard + Sihirbazın sonraki sayfasına erişmek için düğmeye basın + + + + Next + İleri + + + + Open File + Dosyayı Aç + + + + Jami archive files (*.gz); All files (*) + Jami arşiv dosyaları (*.gz); Tüm Dosyalar (*) + + + + Your account needs to be migrated. Enter your password. + Hesabınızın taşınması gerekiyor. Parolanızı girin. + + + + Migrating your Jami account... + Jami hesabınız göç ettiriliyor... + + + + Importing account archive... + Hesap arşivi içe aktarılıyor... + + + + Generating your Jami account... + Jami hesabınız oluşturuluyor... + + + + Generating your SIP account... + SIP hesabınız oluşturuluyor ... + + + + Error creating account + Hesap oluşturma hatası + + + + PasswordDialog + + + Change Account Password + Hesap Parolasını Değiştir + + + + Enter Current Password + Şimdiki Parolayı Girin + + + + Enter New Password + Yeni Parolayı Girin + + + + Confirm New Password + Yeni Parolayı Doğrulayın + + + + Confirm + Onayla + + + + Cancel + İptal + + + + Current Password Incorrect + Kullanılan Parola Hatalı + + + + PhotoBoothDialog + + + Photobooth + Fotoğraf kabini + + + + PhotoboothWidget + + + Form + Form + + + + Photobooth display + Fotoğraf kabini görüntüsü + + + + Choose File + Dosya Seç + + + + Image Files (*.jpg *.jpeg *.png) + Resim Dosyaları (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + Dosya Seç + + + + Files (*) + Dosyalar (*) + + + + QObject + + + No default mail client found + Varsayılan posta istemcisi bulunmadı + + + + + Edit Device Name + Aygıt Adını Düzenle + + + + Unlink Device From Account + Aygıtın Hesaptan Bağlantısını Kaldır + + + + Save new device name + Yeni cihaz adını kaydet + + + + Add as contact + Kişi listesine ekle + + + + RingButton + + + Select folder + Klasör seç + + + + SettingsWidget + + + Form + Form + + + + Settings + Ayarlar + + + + Account + Hesap + + + + + General + Genel + + + + + Audio / Video + Ses / Görüntü + + + + System + Düzen + + + + Enable desktop notifications + Masaüstü bildirimlerini etkinleştir + + + + Keep minimized on close + Kapatırken küçültülmüş olarak tut + + + + Download folder + İndirme klasörü + + + + Save in + Kaydet + + + + Always Recording + Devamlı kaydediliyor + + + + Updates + Güncellemeler + + + + Check for updates automatically every + Güncellemeleri kendiliğinden denetle + + + + Interval between update checks in days selector + Gün seçicideki güncelleme denetimleri aralığı + + + + days + gün + + + + Check for updates now button + Şimdi güncellemeleri denetle düğmesi + + + + Check for updates now + Şimdi güncellemeleri denetle + + + + Password + Parola + + + + + Enable + Etkin + + + + toggle enable notifications + bildirimleri etkinleştirmeyi aç / kapat + + + + Toggle keep minimized on close + Kapatırken simge durumuna küçültmeyi aç / kapat + + + + Call Recordings + Çağrı Kayıtları + + + + Toggle automatic updates + Otomatik güncellemeleri aç / kapat + + + + Jami Account + Jami Hesabı + + + + + Profile + Profil + + + + + Identity + Kimlik + + + + Id + Kimlik + + + + Registered name + Kaydedilen ad + + + + Type here to register a username + Bir kullanıcı adı kaydetmek için buraya yazın + + + + Register + Kaydol + + + + Change Password + Parolayı Değiştir + + + + Export Account + Hesabı Dışa Aktar + + + + + Delete Account + Hesabı Sil + + + + Linked Devices + Bağlı Aygıtlar + + + + Link Another Device + Diğer aygıtı bağla + + + + Banned Contacts + Engellenmiş Kişiler + + + + Format + Biçim + + + + Video device framerate selector + Video cihazı kare hızı seçici + + + + Preview unavailable + Önizleme kullanılamıyor + + + + + Advanced Account Settings + Gelişmiş Hesap ayarları + + + + SIP Account + SIP Hesabı + + + + Username + Kullanıcı adı + + + + Hostname + Sunucu adı + + + + Proxy + Vekil + + + + Audio + Ses + + + + Microphone + Mikrofon + + + + Audio input device selector + Ses giriş aygıtı seçici + + + + Output Device + Çıkış Aygıtı + + + + Choose the output device + Çıkış aygıtını seç + + + + Video + Görüntü + + + + Device + Aygıt + + + + Video device selector + Görüntü aygıtı seçici + + + + A registered name should not have any spaces and must be at least three letters long + Kayıtlı bir isim hiç boşluk bırakmamalı ve en az üç harf uzunluğunda olmalıdır. + + + + This name is already taken + Bu ad önceden alınmış + + + + + Enter an alias + Bir takma ad girin + + + + Register this name + Bu adı kaydet + + + + Remove Device + Aygıtı Kaldır + + + + Enter this account's password to confirm the removal of this device + Bu aygıtın kaldırılmasını onaylamak için bu hesabın şifresini girin. + + + + Are you sure you wish to remove this device? + Bu cihazı kaldırmak istediğinizden emin misiniz? + + + + Export Account Here + Hesabı Buraya Dışa Aktar + + + + Select A Folder For Your Downloads + İndirmeleriniz İçin Bir Klasör Seçin + + + + Select A Folder For Your Recordings + Kayıtlarınız İçin Bir Klasör Seçin + + + + VideoOverlay + + + Call on Hold + Çağrı Beklemede + + + + Hold / Unhold + Beklet / Bekletmeyi kaldır + + + + Chat + Sohbet + + + + Mute Mic + Mikrofonu kapat + + + + Record call + Çağrıyı kaydet + + + + Name label + İsim etiketi + + + + Time elapsed + Geçen zaman + + + + 00:00 + 00:00 + + + + Hangup + Kapat + + + + Mute Video + Görüntüyü kapat + + + + VideoView + + + Share entire screen + Tüm ekranı paylaş + + + + Share screen area + Ekran alanı paylaş + + + + Share file + Dosya paylaş + + + \ No newline at end of file diff --git a/translations/ring_client_windows_tr_TR.ts b/translations/ring_client_windows_tr_TR.ts new file mode 100644 index 00000000..06e7fb97 --- /dev/null +++ b/translations/ring_client_windows_tr_TR.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + + + + + about button + + + + + credits button + + + + + Credits + Emek verenler + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + + + + + Security + + + + + Private Key Password + + + + + User Certificate + + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + + + + + Enable Video + + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + + + + + Ignore + + + + + Cancel outgoing call + + + + + Cancel + + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + + + + + Cancel account deletion + + + + + Cancel + + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + + + + + Ok + + + + + Cancel + + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Ayarlar + + + + Exit + + + + + About + + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + + + + + Register + + + + + Cancel + + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + + + + + + Profile name + + + + + Account + + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + + + + + Password text input + + + + + Password text entry + + + + + + Password + + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + + + + + Open File + + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + + + + + Files (*) + + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + Ayarlar + + + + Account + + + + + + General + + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + + + + + Hostname + + + + + Proxy + + + + + Audio + + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + + + + + Choose the output device + + + + + Video + + + + + Device + + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + + + + + Mute Mic + + + + + Record call + + + + + Name label + + + + + Time elapsed + + + + + 00:00 + + + + + Hangup + + + + + Mute Video + + + + + VideoView + + + Share entire screen + + + + + Share screen area + + + + + Share file + + + + \ No newline at end of file diff --git a/translations/ring_client_windows_uk.ts b/translations/ring_client_windows_uk.ts new file mode 100644 index 00000000..c083d3cc --- /dev/null +++ b/translations/ring_client_windows_uk.ts @@ -0,0 +1,1599 @@ + + + AboutDialog + + + + About + Про программу + + + + about button + кнопка про програму + + + + credits button + + + + + Credits + Подяки + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Клієнт Джамі для Microsoft Windows. +Джамі — це безпечне розподілене програмне забезпечення для зв'язку. + + + + version + версія + + + + Created by: + Стоворено: + + + + Artwork by: + Намальовано: + + + + Based on the SFLPhone project + Базується на проекті SFLPhone + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + Форма + + + + Call Settings + + + + + Auto Answer Calls + Приймати дзвінки автоматично + + + + Enable Custom Ringtone + + + + + Connectivity + З'єднання + + + + STUN Address + + + + + Use STUN + Використовувати STUN + + + + Use UPnP + + + + + Use TURN + Використовувати TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Медіа + + + + Enable Video + Увімкнути відео + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + Форма + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + Приймати дзвінки автоматично + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + Адреса + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + Початкове завантаження + + + + Security + Безпека + + + + Private Key Password + Пароль від приватного ключа + + + + User Certificate + Сертифікат користувача + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + З'єднання + + + + Use STUN + Використовувати STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + Використовувати TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + Медіа + + + + Enable Video + Увімкнути відео + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + Форма + + + + BannedItemWidget + + + Form + Форма + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + Групові чати + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Джамі — це вільне програмне забезпечення для будь-якого зв'язку з повагою до свобод і приватності користувачів. + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + Клацніть двічі, щоб скопіювати + + + + Error while generating QR Code + Помилка при генерації QR-коду + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + Додати контакти + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + Хоче поговорити з вами! + + + + Answer incoming call button + + + + + Ignore incoming call button + Кнопка ігнорування вхідного дзвінка + + + + Answer + Відповідь + + + + Ignore + Ігнорувати + + + + Cancel outgoing call + + + + + Cancel + Відміна + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + Видалити контакт + + + + Block contact + Заблокувати контакт + + + + Copy number + Копіювати номер + + + + Search your received invitations + + + + + Contact me on Jami + Зв'яжіться зі мною в Джамі + + + + My Id is : + + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + Вхідний виклик від %1 + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + Видалити + + + + Cancel account deletion + + + + + Cancel + Відміна + + + + DeviceItemWidget + + + Form + Форма + + + + Device Id + + + + + this device + цей пристрій + + + + InviteButtonsWidget + + + Accept + Прийняти + + + + Refuse + Скидання + + + + Block + Заблокувати + + + + LinkDeviceDialog + + + Dialog + Діалог + + + + Enter your account password + + + + + Password + Пароль + + + + Ok + Ок + + + + Cancel + Відміна + + + + Exporting account + Експортувати акаунт + + + + Your PIN is + + + + + PIN + PIN-код + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + Закрити + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + Налаштування + + + + Exit + Вийти + + + + About + Про программу + + + + Jami + Джамі + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + Пароль + + + + Register + Зареєструватися + + + + Cancel + Відміна + + + + Registering Name + + + + + Something went wrong + + + + + Close + Закрити + + + + Incorrect password + + + + + Network error + Збій мережі + + + + NewWizardWidget + + + Form + Форма + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + Кнопка створення облікового запису Джамі + + + + Push button for Jami account creation start trigger + Натисніть кнопку, щоб розпочати створення облікового запису Джамі + + + + Create a Jami account + Створити обліковий запис Джамі + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + Прив'язати цей пристрій до облікового запису + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + Зв'язати цей пристрій з наявним обліковим записом + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Щоб прив'язати цей пристрій до іншого облікового запису, вам, спершу, </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">потрібно отримати PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">-код. Щоб отримати PIN-код:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Перейдіть до </span><span style=" font-size:14px; font-weight:600;">Налаштування управління обліковим записом</span><span style=" font-size:14px;"> на попередньому пристрої</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Виберіть </span><span style=" font-size:14px; font-weight:600;">обліковий запис Джамі</span><span style=" font-size:14px;"> який ви бажаєте</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Перейдіть до </span><span style=" font-size:14px; font-weight:600;">Пристрої</span><span style=" font-size:14px;"> вкладки</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Виберіть </span><span style=" font-size:14px; font-weight:600;">Додатки пристрій</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">Ви отримаєте PIN-код, який потрібно ввести тут. PIN-код дійсний лише протягом </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 хвилин</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + Enter your pin: + Введіть ваш PIN-код: + + + + Or import a file: + Або завантажте з файлу: + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + Пароль: + + + + + Profile + Профіль + + + + + Profile name + + + + + Account + Аккаунт + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + Зареєструвати публічне ум'я користувача + + + + Public username edit + + + + + Choose your username + Оберіть ваше ім'я користувача + + + + Password text input + + + + + Password text entry + + + + + + Password + Пароль + + + + + Password confirmation text input + + + + + Confirm password + Підтвердити пароль + + + + SIP Account + Аккаунт SIP + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + Проксі + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + Ім’я + + + + + SIP Password text input + + + + + Generating your Jami account… + Створення вашого облікового запису Джамі… + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + Попереднє + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + Назад + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + Далі + + + + Open File + Відкрити файл + + + + Jami archive files (*.gz); All files (*) + Файли архівів Джамі (*.gz); Усі файли (*) + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + Переведення вашого облікового запису Джамі… + + + + Importing account archive... + + + + + Generating your Jami account... + Створення вашого облікового запису Джамі… + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + Відміна + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + Стенд + + + + PhotoboothWidget + + + Form + Форма + + + + Photobooth display + Показ віконця світлини + + + + Choose File + Вибрати файл + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + Вибрати файл + + + + Files (*) + Файли (*) + + + + QObject + + + No default mail client found + Не знайдено жодного поштового клієнту + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + Форма + + + + Settings + Налаштування + + + + Account + Аккаунт + + + + + General + Головні + + + + + Audio / Video + + + + + System + Система + + + + Enable desktop notifications + + + + + Keep minimized on close + Згортати вікно при закриванні + + + + Download folder + + + + + Save in + Зберігти в + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + Пароль + + + + + Enable + Увімкнути + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + Профіль + + + + + Identity + Особа + + + + Id + + + + + Registered name + Зареєстроване ім'я + + + + Type here to register a username + + + + + Register + Зареєструватися + + + + Change Password + + + + + Export Account + Вивантажити обліковий запис + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + Заблоковані контакти + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + Аккаунт SIP + + + + Username + Ім’я + + + + Hostname + Ім'я хоста + + + + Proxy + Проксі + + + + Audio + Звук + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + Пристрій Виведення + + + + Choose the output device + + + + + Video + Відео + + + + Device + Пристрій + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + Виклик на утриманні + + + + Hold / Unhold + Утримати / Заняти з утримування + + + + Chat + Чат + + + + Mute Mic + Відключити мікрофон + + + + Record call + Запис дзвінка + + + + Name label + Назва етикетки + + + + Time elapsed + Витрачено часу + + + + 00:00 + 00:00 + + + + Hangup + Відновлення виклику + + + + Mute Video + Відключити відео + + + + VideoView + + + Share entire screen + Поділитися частиною екрану + + + + Share screen area + Поділитися екраном + + + + Share file + Відправити файл + + + \ No newline at end of file diff --git a/translations/ring_client_windows_zh.ts b/translations/ring_client_windows_zh.ts new file mode 100644 index 00000000..74332296 --- /dev/null +++ b/translations/ring_client_windows_zh.ts @@ -0,0 +1,1589 @@ + + + AboutDialog + + + + About + 關於 + + + + about button + + + + + credits button + + + + + Credits + + + + + Free as in Freedom + + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + + + + + version + 版本 + + + + Created by: + + + + + Artwork by: + + + + + Based on the SFLPhone project + + + + + AccountItemDelegate + + + Add Account + + + + + AdvancedSIPSettingsWidget + + + Form + + + + + Call Settings + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Connectivity + + + + + STUN Address + + + + + Use STUN + 使用STUN + + + + Use UPnP + + + + + Use TURN + 使用TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + 媒體 + + + + Enable Video + 啟用視訊 + + + + Video Codecs + + + + + Audio Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + Add a custom ringtone + + + + + AdvancedSettingsWidget + + + Form + + + + + Call Settings + + + + + Allow Calls From Untrusted Peers + + + + + Auto Answer Calls + + + + + Enable Custom Ringtone + + + + + Add a custom ringtone + + + + + Name Server + + + + + Address + 地址 + + + + OpenDHT Configuration + + + + + Enable Proxy + + + + + Bootstrap + 引導 + + + + Security + 安全 + + + + Private Key Password + + + + + User Certificate + 使用者認證 + + + + Private Key + + + + + CA Certificate + + + + + Connectivity + + + + + Use STUN + 使用STUN + + + + STUN Address + + + + + Use UPnP + + + + + Use TURN + 使用TURN + + + + TURN Password + + + + + TURN Username + + + + + TURN Address + + + + + Media + 媒體 + + + + Enable Video + 啟用視訊 + + + + Audio Codecs + + + + + Video Codecs + + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + + AnimatedOverlay + + + Form + + + + + BannedItemWidget + + + Form + + + + + name + + + + + id + + + + + CallWidget + + + Show conversations + + + + + Conversations + 對話 + + + + Search contact text input + + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + + + + + + This is your ID. +Copy and share it with your friends! + + + + + + Show ring ID QR code + + + + + Share ring ID button + + + + + Double-click to copy + + + + + Error while generating QR Code + + + + + + best name + + + + + best Id + + + + + Back to homepage button + + + + + Add to contacts + 加到聯絡人 + + + + Show invites + + + + + Invites + + + + + + Find a new or existing contact + + + + + Wants to talk to you! + 想要與你談天! + + + + Answer incoming call button + + + + + Ignore incoming call button + + + + + Answer + 接通 + + + + Ignore + 忽略 + + + + Cancel outgoing call + + + + + Cancel + 取消 + + + + Start video call + + + + + Start audio call + + + + + Clear conversation + + + + + Remove contact + + + + + Block contact + + + + + Copy number + 複製號碼 + + + + Search your received invitations + + + + + Contact me on Jami + + + + + My Id is : + + + + + %1 + %1 is the contact username + + + + + %1 + %1 is the contact unique identifier + + + + + Call incoming from %1 + + + + + DeleteAccountDialog + + + Account deletion + + + + + Do you really want to delete the following account? + + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + + + + + Permanently delete account + + + + + Delete + 刪除 + + + + Cancel account deletion + + + + + Cancel + 取消 + + + + DeviceItemWidget + + + Form + + + + + Device Id + + + + + this device + + + + + InviteButtonsWidget + + + Accept + + + + + Refuse + 拒絕 + + + + Block + + + + + LinkDeviceDialog + + + Dialog + + + + + Enter your account password + + + + + Password + 密码 + + + + Ok + 確定 + + + + Cancel + 取消 + + + + Exporting account + + + + + Your PIN is + + + + + PIN + + + + + This pin and the account password should be entered in your device within 10 minutes. + + + + + Close + + + + + Link Another Device + + + + + Incorrect password + + + + + Something went wrong. +Please try again later. + + + + + MainWindow + + + Settings + 設定 + + + + Exit + 離開 + + + + About + 關於 + + + + Jami + + + + + NameRegistrationDialog + + + Set Registered Name + + + + + Enter your account password + + + + + Password text input + + + + + Password text entry + + + + + Password + 密码 + + + + Register + + + + + Cancel + 取消 + + + + Registering Name + + + + + Something went wrong + + + + + Close + + + + + Incorrect password + + + + + Network error + + + + + NewWizardWidget + + + Form + + + + + Welcome Label + + + + + Welcome to + + + + + Welcome Logo + + + + + Create Jami account button + + + + + Push button for Jami account creation start trigger + + + + + Create a Jami account + + + + + Link device button + + + + + Push button for device linkage start trigger + + + + + Link this device to an account + + + + + Create Jami SIP account button + + + + + Push button for Jami SIP account creation start trigger + + + + + Create a SIP account + + + + + Link this device to an existing account + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + + + + + Enter your pin: + + + + + Or import a file: + + + + + Link from exported account archive file + + + + + + + (None) + + + + + Password: + + + + + + Profile + 個人檔案 + + + + + Profile name + + + + + Account + 帳號 + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + + + + + Public username checkbox + + + + + Checkbox selecting if the user wants a public username + + + + + Register public username + + + + + Public username edit + + + + + Choose your username + 選擇你的使用者名稱 + + + + Password text input + + + + + Password text entry + + + + + + Password + 密码 + + + + + Password confirmation text input + + + + + Confirm password + + + + + SIP Account + + + + + SIP Server edit + + + + + Server + + + + + SIP proxy input + + + + + SIP proxy text entry + + + + + Proxy + 代理 + + + + SIP username input + + + + + SIP Password text entry + + + + + Username + 使用者名 + + + + + SIP Password text input + + + + + Generating your Jami account… + + + + + Previous page button + + + + + push button to access previous page of wizard + + + + + Previous + + + + + Cancel account create/link + + + + + push button to cancel account creation or linking + + + + + Back + + + + + Next page Button + + + + + Push button to access next page of wizard + + + + + Next + 下一步 + + + + Open File + 開啟檔案 + + + + Jami archive files (*.gz); All files (*) + + + + + Your account needs to be migrated. Enter your password. + + + + + Migrating your Jami account... + + + + + Importing account archive... + + + + + Generating your Jami account... + + + + + Generating your SIP account... + + + + + Error creating account + + + + + PasswordDialog + + + Change Account Password + + + + + Enter Current Password + + + + + Enter New Password + + + + + Confirm New Password + + + + + Confirm + + + + + Cancel + 取消 + + + + Current Password Incorrect + + + + + PhotoBoothDialog + + + Photobooth + + + + + PhotoboothWidget + + + Form + + + + + Photobooth display + + + + + Choose File + 選擇檔案 + + + + Image Files (*.jpg *.jpeg *.png) + + + + + PrivateBridging + + + Choose File + 選擇檔案 + + + + Files (*) + 檔案(*) + + + + QObject + + + No default mail client found + + + + + + Edit Device Name + + + + + Unlink Device From Account + + + + + Save new device name + + + + + Add as contact + + + + + RingButton + + + Select folder + + + + + SettingsWidget + + + Form + + + + + Settings + 設定 + + + + Account + 帳號 + + + + + General + 通用 + + + + + Audio / Video + + + + + System + + + + + Enable desktop notifications + + + + + Keep minimized on close + + + + + Download folder + + + + + Save in + + + + + Always Recording + + + + + Updates + + + + + Check for updates automatically every + + + + + Interval between update checks in days selector + + + + + days + + + + + Check for updates now button + + + + + Check for updates now + + + + + Password + 密码 + + + + + Enable + + + + + toggle enable notifications + + + + + Toggle keep minimized on close + + + + + Call Recordings + + + + + Toggle automatic updates + + + + + Jami Account + + + + + + Profile + 個人檔案 + + + + + Identity + + + + + Id + + + + + Registered name + + + + + Type here to register a username + + + + + Register + + + + + Change Password + + + + + Export Account + + + + + + Delete Account + + + + + Linked Devices + + + + + Link Another Device + + + + + Banned Contacts + + + + + Format + + + + + Video device framerate selector + + + + + Preview unavailable + + + + + + Advanced Account Settings + + + + + SIP Account + + + + + Username + 使用者名 + + + + Hostname + 主機名稱 + + + + Proxy + 代理 + + + + Audio + 語音 + + + + Microphone + + + + + Audio input device selector + + + + + Output Device + 輸出裝置 + + + + Choose the output device + + + + + Video + 影像 + + + + Device + 裝置 + + + + Video device selector + + + + + A registered name should not have any spaces and must be at least three letters long + + + + + This name is already taken + + + + + + Enter an alias + + + + + Register this name + + + + + Remove Device + + + + + Enter this account's password to confirm the removal of this device + + + + + Are you sure you wish to remove this device? + + + + + Export Account Here + + + + + Select A Folder For Your Downloads + + + + + Select A Folder For Your Recordings + + + + + VideoOverlay + + + Call on Hold + + + + + Hold / Unhold + + + + + Chat + 聊天 + + + + Mute Mic + 麥克風靜音 + + + + Record call + + + + + Name label + + + + + Time elapsed + 經過時間 + + + + 00:00 + 00:00 + + + + Hangup + + + + + Mute Video + 視訊靜音 + + + + VideoView + + + Share entire screen + 分享整個螢幕 + + + + Share screen area + 分享部分螢幕 + + + + Share file + 分享檔案 + + + \ No newline at end of file diff --git a/translations/ring_client_windows_zh_CN.ts b/translations/ring_client_windows_zh_CN.ts new file mode 100644 index 00000000..de17cad0 --- /dev/null +++ b/translations/ring_client_windows_zh_CN.ts @@ -0,0 +1,1606 @@ + + + AboutDialog + + + + About + 关于 + + + + about button + 关于按钮 + + + + credits button + 致谢按钮 + + + + Credits + 致谢 + + + + Free as in Freedom + "Free as in Freedom" + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Jami 的 Microsoft Windows 客户端。 +Jami 是一款加密、分布式的通讯软件。 + + + + version + 版本 + + + + Created by: + 开发者: + + + + Artwork by: + 美工设计: + + + + Based on the SFLPhone project + 基于 SFLPhone 项目 + + + + AccountItemDelegate + + + Add Account + 添加账户 + + + + AdvancedSIPSettingsWidget + + + Form + 表单 + + + + Call Settings + 呼叫设置 + + + + Auto Answer Calls + 自动接听来电 + + + + Enable Custom Ringtone + 启用自定义铃声 + + + + Connectivity + 连接性 + + + + STUN Address + STUN地址 + + + + Use STUN + 使用STUN + + + + Use UPnP + 使用UPnP + + + + Use TURN + 使用TURN + + + + TURN Password + TURN密码 + + + + TURN Username + TURN用户名 + + + + TURN Address + TURN地址 + + + + Media + 媒体 + + + + Enable Video + 启用视频 + + + + Video Codecs + 视频编码 + + + + Audio Codecs + 音频编码 + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + 音频文件(*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + 添加自定义铃声 + + + + AdvancedSettingsWidget + + + Form + 表单 + + + + Call Settings + 呼叫设置 + + + + Allow Calls From Untrusted Peers + 允许未被信任的用户来电 + + + + Auto Answer Calls + 自动接听来电 + + + + Enable Custom Ringtone + 启用自定义铃声 + + + + Add a custom ringtone + 添加自定义铃声 + + + + Name Server + 名称服务器 + + + + Address + 地址: + + + + OpenDHT Configuration + OpenDHT配置 + + + + Enable Proxy + 启用代理 + + + + Bootstrap + 初始服务器 + + + + Security + 安全 + + + + Private Key Password + 私钥密码 + + + + User Certificate + 用户证书 + + + + Private Key + 私钥 + + + + CA Certificate + 颁发机构证书 + + + + Connectivity + 连接性 + + + + Use STUN + 使用STUN + + + + STUN Address + STUN地址 + + + + Use UPnP + 使用UPnP + + + + Use TURN + 使用TURN + + + + TURN Password + TURN密码 + + + + TURN Username + TURN用户名 + + + + TURN Address + TURN地址 + + + + Media + 媒体 + + + + Enable Video + 启用视频 + + + + Audio Codecs + 音频编码 + + + + Video Codecs + 视频编码 + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + 音频文件(*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + 表单 + + + + BannedItemWidget + + + Form + 表单 + + + + name + 姓名 + + + + id + id + + + + CallWidget + + + Show conversations + 显示会话 + + + + Conversations + 会话 + + + + Search contact text input + 搜索联系人文本输入 + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami是一种自由通信软件,它尊重用户的自由和隐私。 + + + + + This is your ID. +Copy and share it with your friends! + + +这是您的ID。 +复制并发送给您的朋友! + + + + + Show ring ID QR code + 显示RingID二维码 + + + + Share ring ID button + 分享ring ID按钮 + + + + Double-click to copy + 双击复制 + + + + Error while generating QR Code + 创建二维码时产生错误 + + + + + best name + 最佳名字 + + + + best Id + 最佳Id + + + + Back to homepage button + 返回主页按钮 + + + + Add to contacts + 加入通讯录 + + + + Show invites + 显示邀请 + + + + Invites + 邀请 + + + + + Find a new or existing contact + 查找新的或现有的联系人 + + + + Wants to talk to you! + 想要与您通信! + + + + Answer incoming call button + 接听来电按钮 + + + + Ignore incoming call button + 忽略来电按钮 + + + + Answer + 应答 + + + + Ignore + 忽略 + + + + Cancel outgoing call + 取消拨出的电话 + + + + Cancel + 取消 + + + + Start video call + 发起视频通话 + + + + Start audio call + 发起语音通话 + + + + Clear conversation + 清除会话 + + + + Remove contact + 删除联系人 + + + + Block contact + 屏蔽联系人 + + + + Copy number + 复制号码 + + + + Search your received invitations + 搜索收到的邀请 + + + + Contact me on Jami + 在Jami上联系我 + + + + My Id is : + 我的ID是: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + 来电:%1 + + + + DeleteAccountDialog + + + Account deletion + 删除账户 + + + + Do you really want to delete the following account? + 确定删除以下账户吗? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + 如果此账户尚未导出或添加到其他设备,它将不可挽回地丢失。 + + + + Permanently delete account + 永久删除账户 + + + + Delete + 删除 + + + + Cancel account deletion + 取消账户删除 + + + + Cancel + 取消 + + + + DeviceItemWidget + + + Form + 表单 + + + + Device Id + 设备ID + + + + this device + 该设备 + + + + InviteButtonsWidget + + + Accept + 接听 + + + + Refuse + 拒接 + + + + Block + 屏蔽 + + + + LinkDeviceDialog + + + Dialog + 对话框 + + + + Enter your account password + 输入您的账户密码 + + + + Password + 密码 + + + + Ok + 确定 + + + + Cancel + 取消 + + + + Exporting account + 正导出账户 + + + + Your PIN is + 您的PIN码是 + + + + PIN + PIN + + + + This pin and the account password should be entered in your device within 10 minutes. + 您需在 10 分钟内在您的设备上输入此 PIN 码和账户密码。 + + + + Close + 关闭 + + + + Link Another Device + 连结其他设备 + + + + Incorrect password + 密码错误 + + + + Something went wrong. +Please try again later. + 有些不对劲。 +请稍后再试 + + + + MainWindow + + + Settings + 设置 + + + + Exit + 退出 + + + + About + 关于 + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + 设置注册名称 + + + + Enter your account password + 输入您的账户密码 + + + + Password text input + 密码文本输入 + + + + Password text entry + 密码文本条目 + + + + Password + 密码 + + + + Register + 注册 + + + + Cancel + 取消 + + + + Registering Name + 正在注册名称 + + + + Something went wrong + 有些不对劲 + + + + Close + 关闭 + + + + Incorrect password + 密码错误 + + + + Network error + 网络错误 + + + + NewWizardWidget + + + Form + 表单 + + + + Welcome Label + 欢迎标签 + + + + Welcome to + 欢迎来到 + + + + Welcome Logo + 欢迎图标 + + + + Create Jami account button + 创建Jami账户按钮 + + + + Push button for Jami account creation start trigger + 按下Jami账户创建按钮开始触发 + + + + Create a Jami account + 创建一个Jami账户 + + + + Link device button + 连结设备按钮 + + + + Push button for device linkage start trigger + 按下设备连结按钮开始触发 + + + + Link this device to an account + 将此设备与一个账户进行连接 + + + + Create Jami SIP account button + 创建 Jami SIP 账户按钮 + + + + Push button for Jami SIP account creation start trigger + 按下 Jami SIP 账户创建按钮开始触发 + + + + Create a SIP account + 创建 SIP 账户 + + + + Link this device to an existing account + 将此设备与已有账户进行连接 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">要将此设备关联到账户,首先您 </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;"> 需要获取 PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> 码。 要生成 PIN 码:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">需前往 </span><span style=" font-size:14px; font-weight:600;">先前设备的</span><span style=" font-size:14px;"> 账户管理设置中</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">选择 </span><span style=" font-size:14px; font-weight:600;">您要使用的</span><span style=" font-size:14px;"> Jami 账户</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">到 </span><span style=" font-size:14px; font-weight:600;">设备</span><span style=" font-size:14px;"> 选项卡下</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">选择 </span><span style=" font-size:14px; font-weight:600;">添加设备</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">您会得到填写本栏所需的 PIN 码。PIN 码有效期为 </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 分钟</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">。</span></p></body></html> + + + + Enter your pin: + 输入您的PIN: + + + + Or import a file: + 或导入一个文件: + + + + Link from exported account archive file + 用导出账户的存档文件进行设备连结 + + + + + + (None) + (无) + + + + Password: + 密码: + + + + + Profile + 账户资料 + + + + + Profile name + 个人资料名称 + + + + Account + 账户 + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + 用你的用户名注册 +这将保留用户名,以便只有您可以使用它 +您的朋友可以使用您的用户名给您打电话 +而非用你的ID + + + + Public username checkbox + 公开用户名的复选框 + + + + Checkbox selecting if the user wants a public username + 如果用户想要公开的用户名就勾选这个复选框 + + + + Register public username + 注册公共用户名 + + + + Public username edit + 编辑公开用户名 + + + + Choose your username + 设置您的用户名 + + + + Password text input + 密码文本输入 + + + + Password text entry + 密码文本条目 + + + + + Password + 密码 + + + + + Password confirmation text input + 确认密码文本输入 + + + + Confirm password + 确认密码 + + + + SIP Account + SIP 账户 + + + + SIP Server edit + 编辑SIP服务器 + + + + Server + 服务器 + + + + SIP proxy input + 输入SIP代理 + + + + SIP proxy text entry + SIP代理文本条目 + + + + Proxy + 代理 + + + + SIP username input + SIP用户名输入 + + + + SIP Password text entry + SIP密码文本条目 + + + + Username + 用户名 + + + + + SIP Password text input + SIP密码文本输入 + + + + Generating your Jami account… + 正在生成您的Jami账户… + + + + Previous page button + 上一页按钮 + + + + push button to access previous page of wizard + 按按钮访问向导的上一页 + + + + Previous + 上一步 + + + + Cancel account create/link + 取消账户创建/连结 + + + + push button to cancel account creation or linking + 按按钮取消账户创建或连结 + + + + Back + 上一步 + + + + Next page Button + 下一页按钮 + + + + Push button to access next page of wizard + 按按钮来访问向导的下一页 + + + + Next + 下一步 + + + + Open File + 打开文件 + + + + Jami archive files (*.gz); All files (*) + Jami档案文件 (*.gz); 所有文件 (*) + + + + Your account needs to be migrated. Enter your password. + 您的Ring账户可以被更新。\n请输入您的密码。 + + + + Migrating your Jami account... + 正在迁移您的Jami账户… + + + + Importing account archive... + 正导入账户存档... + + + + Generating your Jami account... + 正在生成您的Jami账户… + + + + Generating your SIP account... + 正在生成您的SIP账户… + + + + Error creating account + 创建账户时发生错误 + + + + PasswordDialog + + + Change Account Password + 更改账户密码 + + + + Enter Current Password + 输入当前密码 + + + + Enter New Password + 输入新密码 + + + + Confirm New Password + 确认新密码 + + + + Confirm + 确认 + + + + Cancel + 取消 + + + + Current Password Incorrect + 当前密码错误 + + + + PhotoBoothDialog + + + Photobooth + Photobooth + + + + PhotoboothWidget + + + Form + 表单 + + + + Photobooth display + 照片展台展示 + + + + Choose File + 选择文件 + + + + Image Files (*.jpg *.jpeg *.png) + 图像文件(* .jpg * .jpeg * .png) + + + + PrivateBridging + + + Choose File + 选择文件 + + + + Files (*) + 文件 (*) + + + + QObject + + + No default mail client found + 未找到默认邮件客户端 + + + + + Edit Device Name + 编辑设备名 + + + + Unlink Device From Account + 取消设备与账户的链接 + + + + Save new device name + 保存新设备名称 + + + + Add as contact + 添加为联系人 + + + + RingButton + + + Select folder + 选择文件夹 + + + + SettingsWidget + + + Form + 表单 + + + + Settings + 设置 + + + + Account + 账户 + + + + + General + 通用 + + + + + Audio / Video + 音频/视频 + + + + System + 系统 + + + + Enable desktop notifications + 启用桌面通知 + + + + Keep minimized on close + 退出时保持窗口最小化 + + + + Download folder + 下载文件夹 + + + + Save in + 保存至 + + + + Always Recording + 总是录音 + + + + Updates + 更新 + + + + Check for updates automatically every + 自动检查更新:每 + + + + Interval between update checks in days selector + 每隔几天检查更新 + + + + days + + + + + Check for updates now button + 现在检查更新按钮 + + + + Check for updates now + 立即检查更新 + + + + Password + 密码 + + + + + Enable + 启用 + + + + toggle enable notifications + 是否启用通知 + + + + Toggle keep minimized on close + 是否在关闭时保持最小化 + + + + Call Recordings + 通话录音 + + + + Toggle automatic updates + 是否自动更新应用 + + + + Jami Account + Jami账户 + + + + + Profile + 资料 + + + + + Identity + 身份 + + + + Id + ID + + + + Registered name + 已注册名称 + + + + Type here to register a username + 在此处输入注册用户名 + + + + Register + 注册 + + + + Change Password + 更改用户名 + + + + Export Account + 导出账户 + + + + + Delete Account + 删除账户 + + + + Linked Devices + 连结的设备 + + + + Link Another Device + 连结其他设备 + + + + Banned Contacts + 已屏蔽的联系人 + + + + Format + 格式 + + + + Video device framerate selector + 选择视频设备帧率 + + + + Preview unavailable + 预览不可用 + + + + + Advanced Account Settings + 高级账户设置 + + + + SIP Account + SIP 账户 + + + + Username + 用户名 + + + + Hostname + 主机名 + + + + Proxy + 代理 + + + + Audio + 音频 + + + + Microphone + 麦克风 + + + + Audio input device selector + 选择音频输入设备 + + + + Output Device + 输出设备 + + + + Choose the output device + 选择输出设备 + + + + Video + 视频 + + + + Device + 设备 + + + + Video device selector + 选择视频设备 + + + + A registered name should not have any spaces and must be at least three letters long + 注册名称不得有空格,并且必须至少包含三个字母 + + + + This name is already taken + 这个名字已被占用 + + + + + Enter an alias + 输入别名 + + + + Register this name + 注册这个名字 + + + + Remove Device + 删除设备 + + + + Enter this account's password to confirm the removal of this device + 输入此账户的密码,以确认删除该设备 + + + + Are you sure you wish to remove this device? + 您确定要删除此设备吗? + + + + Export Account Here + 此处导出账户 + + + + Select A Folder For Your Downloads + 为您下载的文件选择一个文件夹 + + + + Select A Folder For Your Recordings + 为你的录音挑选一个文件夹 + + + + VideoOverlay + + + Call on Hold + 通话等待中 + + + + Hold / Unhold + 等待/取消等待 + + + + Chat + 聊天 + + + + Mute Mic + 将麦克风静音 + + + + Record call + 录制通话 + + + + Name label + 名称标签 + + + + Time elapsed + 用时 + + + + 00:00 + 00:00 + + + + Hangup + 挂断 + + + + Mute Video + 关闭视频 + + + + VideoView + + + Share entire screen + 共享整个屏幕 + + + + Share screen area + 共享屏幕区域 + + + + Share file + 共享文件 + + + \ No newline at end of file diff --git a/translations/ring_client_windows_zh_TW.ts b/translations/ring_client_windows_zh_TW.ts new file mode 100644 index 00000000..bc7cd8e1 --- /dev/null +++ b/translations/ring_client_windows_zh_TW.ts @@ -0,0 +1,1606 @@ + + + AboutDialog + + + + About + 關於 + + + + about button + 關於按鈕 + + + + credits button + 感謝按鈕 + + + + Credits + 榮譽 + + + + Free as in Freedom + Free as in Freedom + + + + The Microsoft Windows client for Jami. +Jami is a secured and distributed communication software. + Jami 的 Microsoft Windows 客戶端。 +Jami 是一個安全且分散式的通訊軟體。 + + + + version + 版本 + + + + Created by: + 建立由: + + + + Artwork by: + 美術由: + + + + Based on the SFLPhone project + 基於 SFLPhone 專案 + + + + AccountItemDelegate + + + Add Account + 新增帳號 + + + + AdvancedSIPSettingsWidget + + + Form + 表單 + + + + Call Settings + 通話設定 + + + + Auto Answer Calls + 自動回應通話 + + + + Enable Custom Ringtone + 啟用自訂鈴聲 + + + + Connectivity + 連線 + + + + STUN Address + STUN 地址 + + + + Use STUN + 使用 STUN + + + + Use UPnP + 使用 UPnP + + + + Use TURN + 使用 TURN + + + + TURN Password + TURN 密碼 + + + + TURN Username + TURN 使用者名稱 + + + + TURN Address + TURN 地址 + + + + Media + 媒體 + + + + Enable Video + 啟用視訊 + + + + Video Codecs + 視訊編解碼器 + + + + Audio Codecs + 音訊編解碼器 + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + 音訊檔 (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + Add a custom ringtone + 新增自訂鈴聲 + + + + AdvancedSettingsWidget + + + Form + 表單 + + + + Call Settings + 通話設定 + + + + Allow Calls From Untrusted Peers + 允許來自未受信任使用者的通話 + + + + Auto Answer Calls + 自動回應通話 + + + + Enable Custom Ringtone + 啟用自訂鈴聲 + + + + Add a custom ringtone + 新增自訂鈴聲 + + + + Name Server + 名稱伺服器 + + + + Address + 地址 + + + + OpenDHT Configuration + OpenDHT 設定 + + + + Enable Proxy + 啟用代理伺服器 + + + + Bootstrap + Bootstrap + + + + Security + 安全性 + + + + Private Key Password + 私密金鑰密碼 + + + + User Certificate + 使用者憑證 + + + + Private Key + 私鑰 + + + + CA Certificate + CA 憑證 + + + + Connectivity + 連線 + + + + Use STUN + 使用 STUN + + + + STUN Address + STUN 地址 + + + + Use UPnP + 使用 UPnP + + + + Use TURN + 使用 TURN + + + + TURN Password + TURN 密碼 + + + + TURN Username + TURN 使用者名稱 + + + + TURN Address + TURN 地址 + + + + Media + 媒體 + + + + Enable Video + 啟用視訊 + + + + Audio Codecs + 音訊編解碼器 + + + + Video Codecs + 視訊編解碼器 + + + + Audio Files (*.wav *.ogg *.opus *.mp3 *aiff *wma) + 音訊檔 (*.wav *.ogg *.opus *.mp3 *aiff *wma) + + + + AnimatedOverlay + + + Form + 表單 + + + + BannedItemWidget + + + Form + 表單 + + + + name + 名稱 + + + + id + id + + + + CallWidget + + + Show conversations + 顯示對話 + + + + Conversations + 對話 + + + + Search contact text input + 搜尋聯絡人文字輸入 + + + + Jami is free software for universal communication which respects the freedoms and privacy of its users. + Jami 是一套通用通訊的自由軟體,其尊重使用者的自由與隱私。 + + + + + This is your ID. +Copy and share it with your friends! + + + 這是您的 ID。 +複製並分享它給您的朋友! + + + + + Show ring ID QR code + 顯示 Ring ID 二維條碼 + + + + Share ring ID button + 分享 Ring ID 按鈕 + + + + Double-click to copy + 雙擊以複製 + + + + Error while generating QR Code + 產生 QR Code 時發生錯誤 + + + + + best name + 最佳名稱 + + + + best Id + 最佳 ID + + + + Back to homepage button + 回到首頁按鈕 + + + + Add to contacts + 加到聯絡人 + + + + Show invites + 顯示邀請 + + + + Invites + 邀請 + + + + + Find a new or existing contact + 尋找新的或既有的聯絡人 + + + + Wants to talk to you! + 想要與您說說話! + + + + Answer incoming call button + 答覆來電按鈕 + + + + Ignore incoming call button + 忽略來電按鈕 + + + + Answer + 回覆 + + + + Ignore + 忽略 + + + + Cancel outgoing call + 取消去電 + + + + Cancel + 取消 + + + + Start video call + 開始視訊通話 + + + + Start audio call + 開始音訊通話 + + + + Clear conversation + 清除對話 + + + + Remove contact + 移除聯絡人 + + + + Block contact + 阻擋連絡人 + + + + Copy number + 複製號碼 + + + + Search your received invitations + 搜尋您收到的邀請 + + + + Contact me on Jami + 在 Jami 上聯絡我 + + + + My Id is : + 我的 Id 是: + + + + %1 + %1 is the contact username + %1 + + + + %1 + %1 is the contact unique identifier + %1 + + + + Call incoming from %1 + 從 %1 的來電 + + + + DeleteAccountDialog + + + Account deletion + 帳號刪除 + + + + Do you really want to delete the following account? + 您真的想要刪除以下的帳號嗎? + + + + If this account hasn't been exported, or added to another device, it will be irrevocably lost. + 若此帳號尚未匯出,或是新增到其他裝置,它將會不可逆地遺失。 + + + + Permanently delete account + 永久刪除帳號 + + + + Delete + 刪除 + + + + Cancel account deletion + 取消帳號刪除 + + + + Cancel + 取消 + + + + DeviceItemWidget + + + Form + 表單 + + + + Device Id + 裝置 Id + + + + this device + 此裝置 + + + + InviteButtonsWidget + + + Accept + 接聽 + + + + Refuse + 拒絕 + + + + Block + 阻擋 + + + + LinkDeviceDialog + + + Dialog + 對話框 + + + + Enter your account password + 輸入您的帳號密碼 + + + + Password + 密碼 + + + + Ok + 確定 + + + + Cancel + 取消 + + + + Exporting account + 正在匯出帳號 + + + + Your PIN is + 您的 PIN 碼為 + + + + PIN + PIN 碼 + + + + This pin and the account password should be entered in your device within 10 minutes. + 這個 PIN 碼與帳號密碼應該要在 10 分鐘之內在您的裝置上輸入。 + + + + Close + 關閉 + + + + Link Another Device + 聯結其他裝置 + + + + Incorrect password + 不正確的密碼 + + + + Something went wrong. +Please try again later. + 有東西出問題了。 +請再試一次。 + + + + MainWindow + + + Settings + 設定 + + + + Exit + 離開 + + + + About + 關於 + + + + Jami + Jami + + + + NameRegistrationDialog + + + Set Registered Name + 設定已註冊的名稱 + + + + Enter your account password + 輸入您的帳號密碼 + + + + Password text input + 密碼文字輸入 + + + + Password text entry + 密碼文字項目 + + + + Password + 密碼 + + + + Register + 註冊 + + + + Cancel + 取消 + + + + Registering Name + 註冊名稱 + + + + Something went wrong + 出了點問題 + + + + Close + 關閉 + + + + Incorrect password + 不正確的密碼 + + + + Network error + 網路錯誤 + + + + NewWizardWidget + + + Form + 表單 + + + + Welcome Label + 歡迎標籤 + + + + Welcome to + 歡迎來到 + + + + Welcome Logo + 歡迎圖示 + + + + Create Jami account button + 建立 Jami 帳號按鈕 + + + + Push button for Jami account creation start trigger + Jami 帳號建立開始觸發請按下按鈕 + + + + Create a Jami account + 建立 Jami 帳號 + + + + Link device button + 連結裝置按鈕 + + + + Push button for device linkage start trigger + 裝置連結開始觸發請按下按鈕 + + + + Link this device to an account + 連結這個裝置到一個帳號 + + + + Create Jami SIP account button + 建立 Jami SIP 帳號按鈕 + + + + Push button for Jami SIP account creation start trigger + 按下按鈕以啟動 Jami SIP 帳號建立程序 + + + + Create a SIP account + 建立 SIP 帳號 + + + + Link this device to an existing account + 連結此裝置到既有的帳號 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">To link this device to another account, you first </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">need to obtain a PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;"> code. To generate the PIN code:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Account management setting</span><span style=" font-size:14px;"> of a previous device</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Choose the </span><span style=" font-size:14px; font-weight:600;">Jami account</span><span style=" font-size:14px;"> you want to use</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Go to the </span><span style=" font-size:14px; font-weight:600;">Devices</span><span style=" font-size:14px;"> tab</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">Select </span><span style=" font-size:14px; font-weight:600;">Add a device</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">You will get the necessary PIN to complete this form. The PIN is only valid for </span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 minutes</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">.</span></p></body></html> + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">要連結此裝置到其他帳號,您必須先</span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">取得 PIN</span><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">碼。要生成 PIN 碼:</span></p> +<ol style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">請到</span><span style=" font-size:14px; font-weight:600;">帳號管理設定</span><span style=" font-size:14px;"> 在前一個裝置上</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">選擇</span><span style=" font-size:14px; font-weight:600;">Jami 帳號</span><span style=" font-size:14px;">,當然是您想要用的</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">到</span><span style=" font-size:14px; font-weight:600;">裝置</span><span style=" font-size:14px;"> 分頁</span></li> +<li style=" font-family:'Ubuntu'; font-size:14px; color:#555555;" style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:14px;">選取</span><span style=" font-size:14px; font-weight:600;">新增裝置</span></li></ol> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:14px; color:#555555;">您將會取得所需的 PIN 碼來完成此表單。PIN 只在</span><span style=" font-family:'Ubuntu'; font-size:14px; font-weight:600; color:#555555;">10 分鐘</span><span style=" font-family:'Ubuntu'; font-size:10px; color:#555555;">內有效。</span></p></body></html> + + + + Enter your pin: + 輸入您的 PIN 碼: + + + + Or import a file: + 或是匯入檔案: + + + + Link from exported account archive file + 從已匯出的帳號封存檔連結 + + + + + + (None) + (無) + + + + Password: + 密碼: + + + + + Profile + 介紹 + + + + + Profile name + 個人檔案名稱 + + + + Account + 帳號 + + + + Register your username. +This will reserve the username so that only you can use it. +Your friends will be able to call you with your usename +instead of using your ID. + 註冊您的使用者名稱。 +這將會保留使用者名稱,只有您可以使用它。 +您的朋友將可以使用您的使用者名稱來與您通話, +而不用使用您的 ID。 + + + + Public username checkbox + 公開使用者名稱勾選框 + + + + Checkbox selecting if the user wants a public username + 若使用者想要一個公開的使用者名稱就在勾選框打勾 + + + + Register public username + 註冊公開使用者名稱 + + + + Public username edit + 公開使用者名稱編輯 + + + + Choose your username + 選擇您的使用者名稱 + + + + Password text input + 密碼文字輸入 + + + + Password text entry + 密碼文字項目 + + + + + Password + 密碼 + + + + + Password confirmation text input + 確認密碼文字輸入 + + + + Confirm password + 確認密碼 + + + + SIP Account + SIP 帳號 + + + + SIP Server edit + SIP 伺服器編輯 + + + + Server + 伺服器 + + + + SIP proxy input + SIP 代理伺服器輸入 + + + + SIP proxy text entry + SIP 代理伺服器文字項目 + + + + Proxy + 代理服務器 + + + + SIP username input + SIP 使用者名稱輸入 + + + + SIP Password text entry + SIP 密碼文字項目 + + + + Username + 使用者名稱 + + + + + SIP Password text input + SIP 密碼文字輸入 + + + + Generating your Jami account… + 正在生成您的 Jami 帳號…… + + + + Previous page button + 先前頁面按鈕 + + + + push button to access previous page of wizard + 存取前一頁精靈請按下按鈕 + + + + Previous + 前一個 + + + + Cancel account create/link + 取消帳號建立/連結 + + + + push button to cancel account creation or linking + 按下按鈕以取消帳號建立或連結 + + + + Back + 前一個 + + + + Next page Button + 下一頁按鈕 + + + + Push button to access next page of wizard + 存取下一頁精靈請按下按鈕 + + + + Next + 下一個 + + + + Open File + 開啟檔案 + + + + Jami archive files (*.gz); All files (*) + Jami 封存檔 (*.gz); 所有檔案 (*) + + + + Your account needs to be migrated. Enter your password. + 您的帳號需要遷移。輸入您的密碼。 + + + + Migrating your Jami account... + 正在遷移您的 Jami 帳號…… + + + + Importing account archive... + 正在匯入帳號封存檔…… + + + + Generating your Jami account... + 正在生成您的 Jami 帳號…… + + + + Generating your SIP account... + 正在生成您的 SIP 帳號…… + + + + Error creating account + 無法建立帳號 + + + + PasswordDialog + + + Change Account Password + 變更帳號密碼 + + + + Enter Current Password + 輸入目前的密碼 + + + + Enter New Password + 輸入新密碼 + + + + Confirm New Password + 確認新密碼 + + + + Confirm + 確認 + + + + Cancel + 取消 + + + + Current Password Incorrect + 目前的密碼不正確 + + + + PhotoBoothDialog + + + Photobooth + 照片展台 + + + + PhotoboothWidget + + + Form + 表單 + + + + Photobooth display + 大頭貼相機顯示 + + + + Choose File + 選擇檔案 + + + + Image Files (*.jpg *.jpeg *.png) + 圖片檔 (*.jpg *.jpeg *.png) + + + + PrivateBridging + + + Choose File + 選擇檔案 + + + + Files (*) + 檔案 (*) + + + + QObject + + + No default mail client found + 找不到預設的電子郵件客戶端 + + + + + Edit Device Name + 編輯裝置名稱 + + + + Unlink Device From Account + 從帳號取消連結裝置 + + + + Save new device name + 儲存新裝置名稱 + + + + Add as contact + 新增為聯絡人 + + + + RingButton + + + Select folder + 選擇資料夾 + + + + SettingsWidget + + + Form + 表單 + + + + Settings + 設定 + + + + Account + 帳號 + + + + + General + 一般 + + + + + Audio / Video + 音訊/視訊 + + + + System + 系統 + + + + Enable desktop notifications + 啟用桌面通知 + + + + Keep minimized on close + 在關閉時保持最小化 + + + + Download folder + 下載資料夾 + + + + Save in + 儲存在 + + + + Always Recording + 總是錄製 + + + + Updates + 更新 + + + + Check for updates automatically every + 自動檢查更新每 + + + + Interval between update checks in days selector + 檢查更新間隔(以天計)選擇器 + + + + days + + + + + Check for updates now button + 立刻檢查更新按鈕 + + + + Check for updates now + 現在檢查更新 + + + + Password + 密碼 + + + + + Enable + 啟用 + + + + toggle enable notifications + 啟用通知開關 + + + + Toggle keep minimized on close + 切換時保持最小化開關 + + + + Call Recordings + 通話紀錄 + + + + Toggle automatic updates + 自動更新開關 + + + + Jami Account + Jami 帳號 + + + + + Profile + 介紹 + + + + + Identity + 身份 + + + + Id + Id + + + + Registered name + 已註冊的名稱 + + + + Type here to register a username + 在此輸入以註冊使用者名稱 + + + + Register + 註冊 + + + + Change Password + 變更密碼 + + + + Export Account + 匯出帳號 + + + + + Delete Account + 刪除帳號 + + + + Linked Devices + 已連結的裝置 + + + + Link Another Device + 聯結其他裝置 + + + + Banned Contacts + 黑名單聯絡人 + + + + Format + 格式 + + + + Video device framerate selector + 視訊裝置幀率選擇器 + + + + Preview unavailable + 預覽不可用 + + + + + Advanced Account Settings + 進階帳號設定 + + + + SIP Account + SIP 帳號 + + + + Username + 使用者名稱 + + + + Hostname + 主機名稱 + + + + Proxy + 代理服務器 + + + + Audio + 音訊 + + + + Microphone + 麥克風 + + + + Audio input device selector + 音訊輸入裝置選擇器 + + + + Output Device + 輸出裝置 + + + + Choose the output device + 選擇輸出裝置 + + + + Video + 視訊 + + + + Device + 裝置 + + + + Video device selector + 視訊裝置選擇器 + + + + A registered name should not have any spaces and must be at least three letters long + 註冊的使用者名稱不應該有任何空格,且必須至少有三個字母 + + + + This name is already taken + 這個名字已經有人用了 + + + + + Enter an alias + 輸入別名 + + + + Register this name + 註冊這個名字 + + + + Remove Device + 移除裝置 + + + + Enter this account's password to confirm the removal of this device + 輸入這個帳號的密碼以確認移除此裝置 + + + + Are you sure you wish to remove this device? + 您確定您想要移除此裝置嗎? + + + + Export Account Here + 在此匯出帳號 + + + + Select A Folder For Your Downloads + 選取您的下載資料夾 + + + + Select A Folder For Your Recordings + 選取您的錄音資料夾 + + + + VideoOverlay + + + Call on Hold + 保留中的通話 + + + + Hold / Unhold + 保留/繼續通話 + + + + Chat + 聊天 + + + + Mute Mic + 麥克風靜音 + + + + Record call + 記錄通話 + + + + Name label + 名稱標籤 + + + + Time elapsed + 時間流逝 + + + + 00:00 + 00:00 + + + + Hangup + 掛斷 + + + + Mute Video + 視訊靜音 + + + + VideoView + + + Share entire screen + 分享整個螢幕 + + + + Share screen area + 分享螢幕區域 + + + + Share file + 分享檔案 + + + \ No newline at end of file diff --git a/update-translations.py b/update-translations.py new file mode 100755 index 00000000..0cbc9d22 --- /dev/null +++ b/update-translations.py @@ -0,0 +1,62 @@ +#!/usr/bin/python + +## +## Copyright (C) 2016-2017 Savoir-faire Linux Inc. +## +## Author: Edric Milaret +## Author: Guillaume Roguez +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU 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. +## + +import os +import shutil + +print("== Updating from sources") +if os.system("lupdate jami-qt.pro -no-obsolete"): + print("trying with 'lupdate-qt5'") + if os.system("lupdate-qt5 jami-qt.pro -no-obsolete"): + raise RuntimeError("unable to find any suitable lupdate Qt tool on this system. Stopping") + +print("== Pushing sources") +os.system("tx push -s") + +print("== Pulling translations") +os.system("tx pull -af --minimum-perc=1") + +print("Updating .pro file") + +translationFiles = [] + +for filename in os.listdir('./translations'): + translationFiles.append("translations/{0}".format(filename)) + +proFile = "jami-qt.pro" +shutil.move(proFile, proFile + "~") + +destination = open(proFile, "w") +source = open(proFile + "~", "r") +for line in source: + if not ".ts" in line: + destination.write(line) + if "TRANSLATIONS = " in line: + for filename in translationFiles: + destination.write(" {0} \\\n".format(filename)) + +source.close() +destination.close() +os.remove(proFile + "~") + +print("== All done you can commit now")