From 6f4b12c61e76dcd25111896495996cc8677190be Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Fri, 23 Jul 2021 18:55:21 +0200 Subject: [PATCH] Backup --- CMakeLists.txt | 11 +- linphone-app/CMakeLists.txt | 22 +- .../assets/assistant/use-app-sip-account.rc | 1 + .../assets/images/add_participant_hovered.svg | 56 + .../assets/images/add_participant_normal.svg | 56 + .../assets/images/add_participant_pressed.svg | 56 + linphone-app/assets/images/chat_room.svg | 55 + linphone-app/assets/images/close.svg | 50 + linphone-app/assets/images/collapsed.svg | 70 + .../assets/images/conferences_normal.svg | 54 + .../assets/images/conferences_selected.svg | 54 + .../current_account_status_available.svg | 70 + linphone-app/assets/images/expanded.svg | 71 + linphone-app/assets/images/home_disabled.svg | 76 + linphone-app/assets/images/home_hovered.svg | 83 + linphone-app/assets/images/home_normal.svg | 91 +- linphone-app/assets/images/home_pressed.svg | 82 + linphone-app/assets/images/menu_devices.svg | 53 + linphone-app/assets/images/menu_ephemeral.svg | 53 + linphone-app/assets/images/menu_infos.png | Bin 0 -> 3299 bytes linphone-app/assets/images/menu_infos.svg | 106 ++ linphone-app/assets/images/menu_infos2.svg | 59 + .../assets/images/menu_vdots_hovered.svg | 13 + .../assets/images/menu_vdots_normal.svg | 13 + .../assets/images/menu_vdots_pressed.svg | 13 + .../assets/images/new_chat_group_disabled.svg | 116 ++ .../assets/images/new_chat_group_hovered.svg | 112 ++ .../assets/images/new_chat_group_normal.svg | 105 ++ .../assets/images/new_chat_group_pressed.svg | 112 ++ .../assets/images/new_chat_hovered.svg | 97 ++ .../assets/images/new_chat_normal.svg | 97 ++ .../assets/images/new_chat_pressed.svg | 97 ++ .../assets/images/new_conference_disabled.svg | 92 + .../assets/images/new_conference_hovered.svg | 102 +- .../assets/images/new_conference_normal.svg | 99 +- .../assets/images/new_conference_pressed.svg | 104 +- .../assets/images/panel_hidden_hovered.svg | 57 + .../assets/images/panel_hidden_normal.svg | 57 + .../assets/images/panel_hidden_pressed.svg | 57 + .../assets/images/panel_shown_hovered.svg | 57 + .../assets/images/panel_shown_normal.svg | 57 + .../assets/images/panel_shown_pressed.svg | 57 + linphone-app/assets/images/plus_disabled.svg | 65 + linphone-app/assets/images/plus_hovered.svg | 65 + linphone-app/assets/images/plus_normal.svg | 65 + linphone-app/assets/images/plus_pressed.svg | 65 + linphone-app/assets/images/remove_normal.svg | 50 + .../images/remove_participant_hovered.svg | 56 + .../images/remove_participant_normal.svg | 56 + .../images/remove_participant_pressed.svg | 56 + linphone-app/assets/images/secure_level_1.svg | 64 + linphone-app/assets/images/secure_level_2.svg | 198 +++ .../assets/images/secure_level_unsafe.svg | 154 ++ linphone-app/assets/images/secure_off.svg | 69 + linphone-app/assets/images/secure_on.svg | 56 + linphone-app/assets/images/send.svg | 75 + linphone-app/assets/images/timeline_close.svg | 50 + .../assets/images/timeline_filter.svg | 50 + .../assets/images/timeline_search (copy).svg | 65 + .../assets/images/timeline_search.svg | 66 + linphone-app/assets/images/timer.svg | 124 ++ linphone-app/resources.qrc | 57 +- linphone-app/src/app/App.cpp | 27 +- linphone-app/src/components/Components.hpp | 8 + .../components/assistant/AssistantModel.cpp | 3 +- .../src/components/call/CallModel.cpp | 1 - .../src/components/calls/CallsListModel.cpp | 142 +- .../src/components/calls/CallsListModel.hpp | 13 +- .../chat-message/ChatMessageModel.cpp | 43 + .../chat-message/ChatMessageModel.hpp | 54 + .../chat-room/ChatRoomListModel.cpp | 58 +- .../components/chat-room/ChatRoomModel.cpp | 1494 ++++++++++------- .../components/chat-room/ChatRoomModel.hpp | 461 ++--- .../chat-room/ChatRoomProxyModel.cpp | 38 +- .../chat-room/ChatRoomProxyModel.hpp | 31 +- .../components/conference/ConferenceModel.cpp | 1 - .../src/components/contact/ContactModel.cpp | 5 + .../src/components/contact/ContactModel.hpp | 5 +- .../src/components/contact/VcardModel.hpp | 1 + .../src/components/core/CoreHandlers.cpp | 15 +- .../src/components/core/CoreHandlers.hpp | 8 + .../src/components/core/CoreManager.cpp | 75 +- .../src/components/core/CoreManager.hpp | 9 +- .../EventCountNotifierSystemTrayIcon.cpp | 3 +- .../src/components/ldap/LdapListModel.cpp | 1 - .../src/components/ldap/LdapModel.cpp | 1 - .../src/components/other/colors/Colors.hpp | 7 +- .../ParticipantDeviceListModel.cpp | 85 + .../ParticipantDeviceListModel.hpp | 61 + .../participant/ParticipantDeviceModel.cpp | 64 + .../participant/ParticipantDeviceModel.hpp | 67 + .../ParticipantDeviceProxyModel.cpp | 57 + .../ParticipantDeviceProxyModel.hpp | 51 + .../participant/ParticipantListModel.cpp | 276 +++ .../participant/ParticipantListModel.hpp | 84 + .../participant/ParticipantModel.cpp | 77 +- .../participant/ParticipantModel.hpp | 60 +- .../participant/ParticipantProxyModel.cpp | 306 +--- .../participant/ParticipantProxyModel.hpp | 49 +- .../src/components/presence/Presence.cpp | 33 +- .../src/components/presence/Presence.hpp | 5 +- .../settings/AccountSettingsModel.cpp | 56 +- .../settings/AccountSettingsModel.hpp | 1 + .../src/components/settings/SettingsModel.hpp | 6 +- .../sip-addresses/SearchSipAddressesModel.cpp | 1 - .../sip-addresses/SearchSipAddressesModel.hpp | 2 +- .../SearchSipAddressesProxyModel.cpp | 87 + .../SearchSipAddressesProxyModel.hpp | 61 + .../sip-addresses/SipAddressesModel.cpp | 13 +- .../sip-addresses/SipAddressesModel.hpp | 2 + .../sip-addresses/SipAddressesSorter.cpp | 112 ++ .../sip-addresses/SipAddressesSorter.hpp | 45 + .../components/timeline/TimelineListModel.cpp | 115 +- .../components/timeline/TimelineListModel.hpp | 9 +- .../src/components/timeline/TimelineModel.cpp | 8 +- .../src/components/timeline/TimelineModel.hpp | 1 + .../timeline/TimelineProxyModel.cpp | 3 +- linphone-app/src/utils/LinphoneEnums.cpp | 53 + linphone-app/src/utils/LinphoneEnums.hpp | 87 + linphone-app/src/utils/LinphoneUtils.cpp | 19 + linphone-app/src/utils/LinphoneUtils.hpp | 8 +- linphone-app/src/utils/Utils.cpp | 43 + linphone-app/src/utils/Utils.hpp | 152 +- .../modules/Common/Dialog/ConfirmDialog.qml | 2 +- .../ui/modules/Common/Dialog/DialogPlus.qml | 32 +- .../ui/modules/Common/Dialog/DialogTitle.qml | 53 + .../ui/modules/Common/Form/ActionButton.qml | 80 +- .../Form/Buttons/AbstractTextButton.qml | 8 +- .../Common/Form/Buttons/FileChooserButton.qml | 2 +- .../modules/Common/Form/DroppableTextArea.qml | 390 +++-- .../modules/Common/Form/Fields/HexField.qml | 2 +- .../Common/Form/Fields/NumericField.qml | 2 +- .../modules/Common/Form/Fields/PortField.qml | 2 +- .../Form/Fields/ScrollableListViewField.qml | 12 +- .../modules/Common/Form/Fields/TextField.qml | 49 +- .../ui/modules/Common/Form/Switch.qml | 41 +- linphone-app/ui/modules/Common/Image/Icon.qml | 2 +- .../Common/Menus/ApplicationMenuEntry.qml | 2 + .../ui/modules/Common/Menus/MenuItem.qml | 88 +- .../ui/modules/Common/Misc/ForceScrollBar.qml | 3 +- .../Common/Styles/Dialog/DialogStyle.qml | 1 + .../Styles/Form/Buttons/TextButtonAStyle.qml | 2 +- .../Styles/Form/Fields/TextFieldStyle.qml | 86 +- .../Common/Styles/Form/SwitchStyle.qml | 130 +- .../Styles/Misc/ForceScrollBarStyle.qml | 29 +- .../ui/modules/Common/Tooltip/TooltipArea.qml | 2 +- .../Common/View/ScrollableListView.qml | 3 +- .../Linphone/Account/AccountStatus (copy).qml | 118 ++ .../Linphone/Account/AccountStatus.qml | 184 +- .../modules/Linphone/Calls/CallControls.qml | 2 +- linphone-app/ui/modules/Linphone/Chat/Chat.js | 11 +- .../ui/modules/Linphone/Chat/Chat.qml | 39 +- .../ui/modules/Linphone/Chat/Message.qml | 14 +- .../ui/modules/Linphone/Chat/Notice.qml | 52 + .../ui/modules/Linphone/Contact/Avatar.qml | 6 +- .../ui/modules/Linphone/Contact/Contact.qml | 78 +- .../Linphone/Contact/ContactDescription.qml | 73 +- .../Linphone/Dialog/OnlineInstallerDialog.qml | 2 +- .../Linphone/Presence/PresenceLevel.qml | 5 +- .../SmartSearchBar/SmartSearchBar.qml | 35 +- .../Styles/Account/AccountStatusStyle.qml | 9 +- .../Styles/Misc/MessageCounterStyle.qml | 4 +- .../Styles/Timeline/TimelineStyle.qml | 2 +- .../ui/modules/Linphone/Timeline/Timeline.qml | 386 +++-- .../Linphone/View/ParticipantsView.qml | 313 ++++ .../Linphone/View/SipAddressesView.qml | 55 +- linphone-app/ui/modules/Linphone/qmldir | 2 + .../App/Calls/Dialogs/CallSipAddress.qml | 2 +- .../views/App/Calls/Dialogs/CallTransfer.qml | 2 +- .../App/Calls/Dialogs/ConferenceManager.qml | 2 +- .../Calls/Dialogs/MultimediaParameters.qml | 2 +- linphone-app/ui/views/App/Main/Contacts.qml | 618 +++---- .../ui/views/App/Main/Conversation.qml | 460 ++--- .../ui/views/App/Main/Dialogs/About.qml | 2 +- .../Main/Dialogs/AuthenticationRequest.qml | 2 +- .../App/Main/Dialogs/EphemeralChatRoom.qml | 97 ++ .../views/App/Main/Dialogs/InfoChatRoom.qml | 136 ++ .../views/App/Main/Dialogs/ManageAccounts.qml | 2 +- .../views/App/Main/Dialogs/ManageChatRoom.qml | 2 +- .../ui/views/App/Main/Dialogs/NewChatRoom.qml | 486 ++++++ .../App/Main/Dialogs/ParticipantsDevices.qml | 159 ++ linphone-app/ui/views/App/Main/MainWindow.js | 5 +- linphone-app/ui/views/App/Main/MainWindow.qml | 71 +- .../App/Settings/Dialogs/SettingsLdapEdit.qml | 2 +- .../Dialogs/SettingsSipAccountsEdit.qml | 2 +- .../Settings/Dialogs/SettingsVideoPreview.qml | 2 +- .../views/App/Styles/Main/MainWindowStyle.qml | 8 +- 187 files changed, 10798 insertions(+), 2589 deletions(-) create mode 100644 linphone-app/assets/images/add_participant_hovered.svg create mode 100644 linphone-app/assets/images/add_participant_normal.svg create mode 100644 linphone-app/assets/images/add_participant_pressed.svg create mode 100644 linphone-app/assets/images/chat_room.svg create mode 100644 linphone-app/assets/images/close.svg create mode 100644 linphone-app/assets/images/collapsed.svg create mode 100644 linphone-app/assets/images/conferences_normal.svg create mode 100644 linphone-app/assets/images/conferences_selected.svg create mode 100644 linphone-app/assets/images/current_account_status_available.svg create mode 100644 linphone-app/assets/images/expanded.svg create mode 100644 linphone-app/assets/images/home_disabled.svg create mode 100644 linphone-app/assets/images/home_hovered.svg create mode 100644 linphone-app/assets/images/home_pressed.svg create mode 100644 linphone-app/assets/images/menu_devices.svg create mode 100644 linphone-app/assets/images/menu_ephemeral.svg create mode 100644 linphone-app/assets/images/menu_infos.png create mode 100644 linphone-app/assets/images/menu_infos.svg create mode 100644 linphone-app/assets/images/menu_infos2.svg create mode 100644 linphone-app/assets/images/menu_vdots_hovered.svg create mode 100644 linphone-app/assets/images/menu_vdots_normal.svg create mode 100644 linphone-app/assets/images/menu_vdots_pressed.svg create mode 100644 linphone-app/assets/images/new_chat_group_disabled.svg create mode 100644 linphone-app/assets/images/new_chat_group_hovered.svg create mode 100644 linphone-app/assets/images/new_chat_group_normal.svg create mode 100644 linphone-app/assets/images/new_chat_group_pressed.svg create mode 100644 linphone-app/assets/images/new_chat_hovered.svg create mode 100644 linphone-app/assets/images/new_chat_normal.svg create mode 100644 linphone-app/assets/images/new_chat_pressed.svg create mode 100644 linphone-app/assets/images/new_conference_disabled.svg create mode 100644 linphone-app/assets/images/panel_hidden_hovered.svg create mode 100644 linphone-app/assets/images/panel_hidden_normal.svg create mode 100644 linphone-app/assets/images/panel_hidden_pressed.svg create mode 100644 linphone-app/assets/images/panel_shown_hovered.svg create mode 100644 linphone-app/assets/images/panel_shown_normal.svg create mode 100644 linphone-app/assets/images/panel_shown_pressed.svg create mode 100644 linphone-app/assets/images/plus_disabled.svg create mode 100644 linphone-app/assets/images/plus_hovered.svg create mode 100644 linphone-app/assets/images/plus_normal.svg create mode 100644 linphone-app/assets/images/plus_pressed.svg create mode 100644 linphone-app/assets/images/remove_normal.svg create mode 100644 linphone-app/assets/images/remove_participant_hovered.svg create mode 100644 linphone-app/assets/images/remove_participant_normal.svg create mode 100644 linphone-app/assets/images/remove_participant_pressed.svg create mode 100644 linphone-app/assets/images/secure_level_1.svg create mode 100644 linphone-app/assets/images/secure_level_2.svg create mode 100644 linphone-app/assets/images/secure_level_unsafe.svg create mode 100644 linphone-app/assets/images/secure_off.svg create mode 100644 linphone-app/assets/images/secure_on.svg create mode 100644 linphone-app/assets/images/send.svg create mode 100644 linphone-app/assets/images/timeline_close.svg create mode 100644 linphone-app/assets/images/timeline_filter.svg create mode 100644 linphone-app/assets/images/timeline_search (copy).svg create mode 100644 linphone-app/assets/images/timeline_search.svg create mode 100644 linphone-app/assets/images/timer.svg create mode 100644 linphone-app/src/components/chat-message/ChatMessageModel.cpp create mode 100644 linphone-app/src/components/chat-message/ChatMessageModel.hpp create mode 100644 linphone-app/src/components/participant/ParticipantDeviceListModel.cpp create mode 100644 linphone-app/src/components/participant/ParticipantDeviceListModel.hpp create mode 100644 linphone-app/src/components/participant/ParticipantDeviceModel.cpp create mode 100644 linphone-app/src/components/participant/ParticipantDeviceModel.hpp create mode 100644 linphone-app/src/components/participant/ParticipantDeviceProxyModel.cpp create mode 100644 linphone-app/src/components/participant/ParticipantDeviceProxyModel.hpp create mode 100644 linphone-app/src/components/participant/ParticipantListModel.cpp create mode 100644 linphone-app/src/components/participant/ParticipantListModel.hpp create mode 100644 linphone-app/src/components/sip-addresses/SearchSipAddressesProxyModel.cpp create mode 100644 linphone-app/src/components/sip-addresses/SearchSipAddressesProxyModel.hpp create mode 100644 linphone-app/src/components/sip-addresses/SipAddressesSorter.cpp create mode 100644 linphone-app/src/components/sip-addresses/SipAddressesSorter.hpp create mode 100644 linphone-app/src/utils/LinphoneEnums.cpp create mode 100644 linphone-app/src/utils/LinphoneEnums.hpp create mode 100644 linphone-app/ui/modules/Common/Dialog/DialogTitle.qml create mode 100644 linphone-app/ui/modules/Linphone/Account/AccountStatus (copy).qml create mode 100644 linphone-app/ui/modules/Linphone/Chat/Notice.qml create mode 100644 linphone-app/ui/modules/Linphone/View/ParticipantsView.qml create mode 100644 linphone-app/ui/views/App/Main/Dialogs/EphemeralChatRoom.qml create mode 100644 linphone-app/ui/views/App/Main/Dialogs/InfoChatRoom.qml create mode 100644 linphone-app/ui/views/App/Main/Dialogs/NewChatRoom.qml create mode 100644 linphone-app/ui/views/App/Main/Dialogs/ParticipantsDevices.qml diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c92453ff..64badf6f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,7 +181,7 @@ if(ENABLE_BUILD_APP_PLUGINS) endif() - +if(NOT LINPHONE_QT_ONLY) ExternalProject_Add(sdk PREFIX "${CMAKE_BINARY_DIR}/sdk" SOURCE_DIR "${CMAKE_SOURCE_DIR}/linphone-sdk" INSTALL_DIR "${LINPHONE_OUTPUT_DIR}" @@ -192,7 +192,7 @@ ExternalProject_Add(sdk PREFIX "${CMAKE_BINARY_DIR}/sdk" INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Install step is already done at build time." LIST_SEPARATOR | # Use the alternate list separator CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${PREFIX_PATH} - #BUILD_ALWAYS NO #${DO_BUILD} + BUILD_ALWAYS NO #${DO_BUILD} ) ExternalProject_Add_Step(sdk force_build COMMENT "Forcing build for 'desktop'" @@ -200,6 +200,7 @@ ExternalProject_Add_Step(sdk force_build DEPENDERS build ALWAYS 1 ) +endif() include(FindPkgConfig) set(APP_DEPENDS sdk) @@ -257,7 +258,9 @@ else() message("Adding Linphone Desktop in an IDE-friendly state") set(CMAKE_INSTALL_PREFIX "${APPLICATION_OUTPUT_DIR}") add_subdirectory(${CMAKE_SOURCE_DIR}/linphone-app) - add_dependencies(app-library ${APP_DEPENDS}) + if(NOT LINPHONE_QT_ONLY) + add_dependencies(app-library ${APP_DEPENDS}) + endif() if( ENABLE_BUILD_APP_PLUGINS) add_subdirectory(${CMAKE_SOURCE_DIR}/plugins "plugins-app") endif() @@ -271,5 +274,5 @@ ExternalProject_Add(linphone-qt-only PREFIX "${CMAKE_BINARY_DIR}/linphone-app" LIST_SEPARATOR | # Use the alternate list separator CMAKE_ARGS ${APP_OPTIONS} ${USER_ARGS} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_PREFIX_PATH=${PREFIX_PATH} EXCLUDE_FROM_ALL ON - BUILD_ALWAYS ON + #BUILD_ALWAYS ON ) diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt index 322749cf3..1e13d963f 100644 --- a/linphone-app/CMakeLists.txt +++ b/linphone-app/CMakeLists.txt @@ -122,6 +122,7 @@ set(SOURCES src/components/calls/CallsListProxyModel.cpp src/components/camera/Camera.cpp src/components/camera/CameraPreview.cpp + src/components/chat-message/ChatMessageModel.cpp src/components/chat-room/ChatRoomModel.cpp src/components/chat-room/ChatRoomListModel.cpp src/components/chat-room/ChatRoomProxyModel.cpp @@ -155,6 +156,11 @@ set(SOURCES src/components/other/text-to-speech/TextToSpeech.cpp src/components/other/units/Units.cpp src/components/participant/ParticipantModel.cpp + src/components/participant/ParticipantListModel.cpp + src/components/participant/ParticipantProxyModel.cpp + src/components/participant/ParticipantDeviceModel.cpp + src/components/participant/ParticipantDeviceListModel.cpp + src/components/participant/ParticipantDeviceProxyModel.cpp src/components/presence/OwnPresenceModel.cpp src/components/presence/Presence.cpp src/components/search/SearchHandler.cpp @@ -162,18 +168,21 @@ set(SOURCES src/components/settings/SettingsModel.cpp src/components/sip-addresses/SipAddressesModel.cpp src/components/sip-addresses/SipAddressesProxyModel.cpp + src/components/sip-addresses/SipAddressesSorter.cpp src/components/sip-addresses/SipAddressObserver.cpp src/components/sip-addresses/SearchSipAddressesModel.cpp + src/components/sip-addresses/SearchSipAddressesProxyModel.cpp src/components/sound-player/SoundPlayer.cpp src/components/telephone-numbers/TelephoneNumbersModel.cpp src/components/timeline/TimelineModel.cpp src/components/timeline/TimelineListModel.cpp src/components/timeline/TimelineProxyModel.cpp src/components/url-handlers/UrlHandlers.cpp - src/utils/LinphoneUtils.cpp + src/utils/LinphoneEnums.cpp src/utils/MediastreamerUtils.cpp src/utils/QExifImageHeader.cpp src/utils/Utils.cpp + src/utils/Tools.cpp src/utils/plugins/PluginsManager.cpp ) set(PLUGIN_SOURCES src/utils/plugins/PluginDataAPI.cpp @@ -200,6 +209,7 @@ set(HEADERS src/components/calls/CallsListProxyModel.hpp src/components/camera/Camera.hpp src/components/camera/CameraPreview.hpp + src/components/chat-message/ChatMessageModel.hpp src/components/chat-room/ChatRoomModel.hpp src/components/chat-room/ChatRoomListModel.hpp src/components/chat-room/ChatRoomProxyModel.hpp @@ -235,6 +245,11 @@ set(HEADERS src/components/other/text-to-speech/TextToSpeech.hpp src/components/other/units/Units.hpp src/components/participant/ParticipantModel.hpp + src/components/participant/ParticipantListModel.hpp + src/components/participant/ParticipantProxyModel.hpp + src/components/participant/ParticipantDeviceModel.hpp + src/components/participant/ParticipantDeviceListModel.hpp + src/components/participant/ParticipantDeviceProxyModel.hpp src/components/presence/OwnPresenceModel.hpp src/components/presence/Presence.hpp src/components/search/SearchHandler.hpp @@ -242,18 +257,21 @@ set(HEADERS src/components/settings/SettingsModel.hpp src/components/sip-addresses/SipAddressesModel.hpp src/components/sip-addresses/SipAddressesProxyModel.hpp + src/components/sip-addresses/SipAddressesSorter.hpp src/components/sip-addresses/SipAddressObserver.hpp src/components/sip-addresses/SearchSipAddressesModel.hpp + src/components/sip-addresses/SearchSipAddressesProxyModel.hpp src/components/sound-player/SoundPlayer.hpp src/components/telephone-numbers/TelephoneNumbersModel.hpp src/components/timeline/TimelineModel.hpp src/components/timeline/TimelineListModel.hpp src/components/timeline/TimelineProxyModel.hpp src/components/url-handlers/UrlHandlers.hpp - src/utils/LinphoneUtils.hpp + src/utils/LinphoneEnums.hpp src/utils/MediastreamerUtils.hpp src/utils/QExifImageHeader.hpp src/utils/Utils.hpp + src/utils/Tools.hpp src/utils/plugins/PluginsManager.hpp ) set(PLUGIN_HEADERS diff --git a/linphone-app/assets/assistant/use-app-sip-account.rc b/linphone-app/assets/assistant/use-app-sip-account.rc index 489d881de..f7ec2d67b 100644 --- a/linphone-app/assets/assistant/use-app-sip-account.rc +++ b/linphone-app/assets/assistant/use-app-sip-account.rc @@ -14,6 +14,7 @@ nat_policy_default_values sip.linphone.org message-expires=604800 + sip:conference-factory@sip.linphone.org
stun.linphone.org diff --git a/linphone-app/assets/images/add_participant_hovered.svg b/linphone-app/assets/images/add_participant_hovered.svg new file mode 100644 index 000000000..fc0aceb21 --- /dev/null +++ b/linphone-app/assets/images/add_participant_hovered.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/add_participant_normal.svg b/linphone-app/assets/images/add_participant_normal.svg new file mode 100644 index 000000000..fa20f9912 --- /dev/null +++ b/linphone-app/assets/images/add_participant_normal.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/add_participant_pressed.svg b/linphone-app/assets/images/add_participant_pressed.svg new file mode 100644 index 000000000..01e569aee --- /dev/null +++ b/linphone-app/assets/images/add_participant_pressed.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/chat_room.svg b/linphone-app/assets/images/chat_room.svg new file mode 100644 index 000000000..4dae42a49 --- /dev/null +++ b/linphone-app/assets/images/chat_room.svg @@ -0,0 +1,55 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/close.svg b/linphone-app/assets/images/close.svg new file mode 100644 index 000000000..97cc19996 --- /dev/null +++ b/linphone-app/assets/images/close.svg @@ -0,0 +1,50 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/collapsed.svg b/linphone-app/assets/images/collapsed.svg new file mode 100644 index 000000000..5f1260870 --- /dev/null +++ b/linphone-app/assets/images/collapsed.svg @@ -0,0 +1,70 @@ + + + + + + image/svg+xml + + drop_down_list + + + + + + drop_down_list + Created with Sketch. + + + + + + + diff --git a/linphone-app/assets/images/conferences_normal.svg b/linphone-app/assets/images/conferences_normal.svg new file mode 100644 index 000000000..c0b066fb6 --- /dev/null +++ b/linphone-app/assets/images/conferences_normal.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + diff --git a/linphone-app/assets/images/conferences_selected.svg b/linphone-app/assets/images/conferences_selected.svg new file mode 100644 index 000000000..c0b066fb6 --- /dev/null +++ b/linphone-app/assets/images/conferences_selected.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + diff --git a/linphone-app/assets/images/current_account_status_available.svg b/linphone-app/assets/images/current_account_status_available.svg new file mode 100644 index 000000000..76a4a685b --- /dev/null +++ b/linphone-app/assets/images/current_account_status_available.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/expanded.svg b/linphone-app/assets/images/expanded.svg new file mode 100644 index 000000000..b18f30c06 --- /dev/null +++ b/linphone-app/assets/images/expanded.svg @@ -0,0 +1,71 @@ + + + + + + image/svg+xml + + drop_down_list + + + + + + drop_down_list + Created with Sketch. + + + + + + + diff --git a/linphone-app/assets/images/home_disabled.svg b/linphone-app/assets/images/home_disabled.svg new file mode 100644 index 000000000..f9cb2f7c3 --- /dev/null +++ b/linphone-app/assets/images/home_disabled.svg @@ -0,0 +1,76 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/home_hovered.svg b/linphone-app/assets/images/home_hovered.svg new file mode 100644 index 000000000..3293b35dc --- /dev/null +++ b/linphone-app/assets/images/home_hovered.svg @@ -0,0 +1,83 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/home_normal.svg b/linphone-app/assets/images/home_normal.svg index 926fd686f..d3755d91a 100644 --- a/linphone-app/assets/images/home_normal.svg +++ b/linphone-app/assets/images/home_normal.svg @@ -1,12 +1,79 @@ - - - - home_icon_default - Created with Sketch. - - - - - - - \ No newline at end of file + + + + + + + + + + diff --git a/linphone-app/assets/images/home_pressed.svg b/linphone-app/assets/images/home_pressed.svg new file mode 100644 index 000000000..1bbda16a5 --- /dev/null +++ b/linphone-app/assets/images/home_pressed.svg @@ -0,0 +1,82 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/menu_devices.svg b/linphone-app/assets/images/menu_devices.svg new file mode 100644 index 000000000..b2d65a4af --- /dev/null +++ b/linphone-app/assets/images/menu_devices.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + diff --git a/linphone-app/assets/images/menu_ephemeral.svg b/linphone-app/assets/images/menu_ephemeral.svg new file mode 100644 index 000000000..35b03b166 --- /dev/null +++ b/linphone-app/assets/images/menu_ephemeral.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + diff --git a/linphone-app/assets/images/menu_infos.png b/linphone-app/assets/images/menu_infos.png new file mode 100644 index 0000000000000000000000000000000000000000..1d08cf24e990d3a1fd79b056847e58b19045fc99 GIT binary patch literal 3299 zcmV<93>@=`P)pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H13~)(A zK~#90?VW#YQ`Z&8zvudyKnOn!&9qe$$PNRhscT30)n@99{cIGWtZO$zHI=raqdx$( z)mDjiqa7V>*HzQHwiG&At#qJ?7Ip0k3>rU&{4omsk!})FS7>WPD+O&poIpqj@w;b# z_yx#&*N&aoNgVQ#EIH@?^vQki-Fxo2=Mjbhe!suM<#Js^MBfH5lbL4{(KG;)0F1Xj z69Jq7aL)R)GV@nNbQHi5X8zPP&BKk2jqOAAJ9?m@XtcJrw#+om8Y$%>A__3`d;nSj zs_q8x0TDGwDff>VGv>X$d-rw~px(R~Or=sN6uJSxa%Nr%z%vMKCIP%IrQFug(D2?M zvi2CxdijAFN!h0g(O5<+aLudhE>%!Z1l)S8-_3FYPGE13CV0ADXw zJN@7kfEUZk%AVV^XHT*i4HR6dkx0bV($cb;h<=0ggC;} zyAa~s-o1Oz)YjHclv0kDQdXFzSt*2=!OXLS5Hp#%8h|&C_L2Y|^LRX)ckbM2=Jg}{ zprA_Cb$ua#O#r@^L-w-(b^$OnP20b3-@ao5Z9g0id!>|%0O$bf0952qtVv3_s-dCb zgMqf+LqU{UTU%R}N~NA*<~0CBmaHZKgPFILmzTe_YuB!hLbR_U91f3X=9`&$Inqkb z)-MU*IgiKl@Xnn(FATKp0V_2S2uv43>;SMZOSWzRJA@EV)z{a5I?y)rMb~v75k0`n z_W&r%lC?=nxvZg~;fsN^Hz1{kLZO?P`Bh}TcS-=S0C>_cj4ub$Rv`!k0@I0T9TBZS z=8@Y9;7-FZ{@&kq`mNMZD72cHpGW4-{R_Y<|5^-_@T&x~^{k@HEu_lrAEA$m8*>+`W7ESN*BK*d!8(6V=t#uXc8Jwh_@HIQ2k8 zwAkzQibNvOm~Z|0j#ScheIwEnc@>{q_oR>W?Z{9O3Weq}^XmY7s&r<4F&2xh$*0b| zMyfSJe~}^iZMWN98;wSf zuHT3B9Ml0bZ#7MGWn*JwD%bil;rIK;YMOR5*ZjTetj&LH7{=cU)I3>NFQeJ+KwDz&=0+AW0G z4)r{AOEemNET^uP7}h{}S&=kZO8I&u5*d@Nd{(7SnKI?~P_LBU_INy*W&k4r_INz^ z0eDA|HZPe>{w`bLj7kj#gY&Glyqzxq+*xSL&Tzx}eYXRME0XWGj7Lh8N{vJ!E+YDq zBE3sWxzsR>wtn}vgfI-Fjfj>3uy4r-02?Ebh;mA*RBB60%W5dhcSN+dlqU9tAR3K+ z1mF=xQgus9%RQeo2xFYXIlP6E!IKV!ZBp#3Fd8BO_y&$3uwY9Yq?Q+Ks zrlx6Y08F-tr4VAxVBAN?jvd?8+1YtluKA^uO~vdFkiNeLgTZ@<=n#4j#3|j~-K&tc zgIuK7*^XFGyF0kKI@3mJ%MB9dgfq6KvZ{NOSMD$m?+?sk`yy`FBQ$wLp z4KvTQ#~b_ONtu z=!J-G?Xd|607#`$--pw;sogZqes6w_WP}iJ0PK{y;9FN6@^ z1>m+NCJN!(G?KBrSdQDoW2BVV2qA=O2eIhu>5%|4t1Nai1ptRqnfXZZyHxrRkwZ_+ zJWBwWolE9XoT&6fM6(5eZ`u=>c~DnTC5%c>(*)8-X?j6Kr;Fd8(kFwSCI|o%>{21b z=%hafQmNEgg?NI1!iBr5t4o!66dPS#U1t>H2?D@4yR@>hva|SIDt)fL`f8PXeYpVO zP~gdvCmo6&#YuB>bGbs?A&@>L(u=aPGRJ*(6ene6WsZkDfU^Ql6BeYDIa}VZWHe1v z4ZgDi>66L5kW$(=|3*>3%u^NOGXj9aFQ16MRs1fMK8ig(0Qgb>P%TMSnMZM=w3{fE zs`7mwMTyG7cSJC=;{={{cy^S&nWK;$6{cw(hSTf9n~NLvmCWMBi)R3sW)oi!LL3&2 zjg9R@^r5|wbsBk;45U)2pk2nyhwAI=&-Lu4sy4CoqW7$XAtHxTiD*Ay?WPJLRGWN1 z%*XxaN+KK%j|Wg|mkS{p086PZm+Jt47F(Y2ZnyhKMekt=Vw&bp;I!_Ynlx$BC0{Bl z%+MS5!k7C8jdBShQu&?j=n24Z(Ls%w|DwpK2ZO=Ug}`Nm!{ONg91n5pyjl8t&zD#% zb`XI5OsI>99xhrBOAsmLBXIilepX*!e=!D&J#n+5p4$R}!1O|Qt`yOA-3Q>Ob~!U| z1i(Fi_fo15;$;9QZDrg3v+~so3dH3CS?JKYmhu`m?tZCZEaK;dnLWmpc>+9cFmCJZE-8$&1iWxFJl}c?L z>GxN^->+$!wjGL~jLiIOCWQih1W`yPlWPGSw9AR8E}2ZeiXn;=JP456?S2Ws5=GjF zQ>IM$WwyduN85=LC#28kivqXqF|KA-OhfTeJb%o}|^pF5wJO~V|r6skJkZ!+^T!!Y^`37YfeYaPvgNm&We zYu(-5KPyCBs-eP)OSKum$_!cVHw?o*%~X=}!zPhPG%KdP##TmV!f)&t-Uuh-i#ckbK|KKbMm zR^%BL`2Buu#*7(1C!*~{q;lT@(jzo4g#rVZ5(R_7RYddxGEd9?Eu~yNnDO;<#ah^G z0x&N_7L$n9M59sVGo&vDG|$v^{U!ig;f%S`g9NZu)3go4;jmjc9QK-~`4|!1fy}=> z$;`LMVzJ%*ZD+ufcU{+~0oVZ`n`ux2coV>{4Z|q90mP1FTwV)c1+u?R(1_(g+7-t%%>aNP(o4+oITZVl zh*nv)j)G8>H&V2=w%*Ik8<5{iXO07C0&tXwj+mx-l$lQxQM(YL-Lmsel2TSMbA_g9 zS6Tj{J)gO7`Zi_bG!d<x?z@{a_A2i0K5;Nfth2i zt*wU&@ifnuVPRypVhmnGM6;28$KNcZA376}b_A=yrFZ + + + + + + + + + diff --git a/linphone-app/assets/images/menu_infos2.svg b/linphone-app/assets/images/menu_infos2.svg new file mode 100644 index 000000000..fbc563352 --- /dev/null +++ b/linphone-app/assets/images/menu_infos2.svg @@ -0,0 +1,59 @@ + + + + + + + + i + + + diff --git a/linphone-app/assets/images/menu_vdots_hovered.svg b/linphone-app/assets/images/menu_vdots_hovered.svg new file mode 100644 index 000000000..f9cb5f9c0 --- /dev/null +++ b/linphone-app/assets/images/menu_vdots_hovered.svg @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/linphone-app/assets/images/menu_vdots_normal.svg b/linphone-app/assets/images/menu_vdots_normal.svg new file mode 100644 index 000000000..f9cb5f9c0 --- /dev/null +++ b/linphone-app/assets/images/menu_vdots_normal.svg @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/linphone-app/assets/images/menu_vdots_pressed.svg b/linphone-app/assets/images/menu_vdots_pressed.svg new file mode 100644 index 000000000..f9cb5f9c0 --- /dev/null +++ b/linphone-app/assets/images/menu_vdots_pressed.svg @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/linphone-app/assets/images/new_chat_group_disabled.svg b/linphone-app/assets/images/new_chat_group_disabled.svg new file mode 100644 index 000000000..336a8a101 --- /dev/null +++ b/linphone-app/assets/images/new_chat_group_disabled.svg @@ -0,0 +1,116 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/new_chat_group_hovered.svg b/linphone-app/assets/images/new_chat_group_hovered.svg new file mode 100644 index 000000000..6c2a25926 --- /dev/null +++ b/linphone-app/assets/images/new_chat_group_hovered.svg @@ -0,0 +1,112 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/new_chat_group_normal.svg b/linphone-app/assets/images/new_chat_group_normal.svg new file mode 100644 index 000000000..1ec88f1b0 --- /dev/null +++ b/linphone-app/assets/images/new_chat_group_normal.svg @@ -0,0 +1,105 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/new_chat_group_pressed.svg b/linphone-app/assets/images/new_chat_group_pressed.svg new file mode 100644 index 000000000..85680b043 --- /dev/null +++ b/linphone-app/assets/images/new_chat_group_pressed.svg @@ -0,0 +1,112 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/new_chat_hovered.svg b/linphone-app/assets/images/new_chat_hovered.svg new file mode 100644 index 000000000..db2f52b86 --- /dev/null +++ b/linphone-app/assets/images/new_chat_hovered.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/new_chat_normal.svg b/linphone-app/assets/images/new_chat_normal.svg new file mode 100644 index 000000000..db2f52b86 --- /dev/null +++ b/linphone-app/assets/images/new_chat_normal.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/new_chat_pressed.svg b/linphone-app/assets/images/new_chat_pressed.svg new file mode 100644 index 000000000..db2f52b86 --- /dev/null +++ b/linphone-app/assets/images/new_chat_pressed.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/new_conference_disabled.svg b/linphone-app/assets/images/new_conference_disabled.svg new file mode 100644 index 000000000..9ad2da828 --- /dev/null +++ b/linphone-app/assets/images/new_conference_disabled.svg @@ -0,0 +1,92 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/new_conference_hovered.svg b/linphone-app/assets/images/new_conference_hovered.svg index 5156b6192..83b653b07 100644 --- a/linphone-app/assets/images/new_conference_hovered.svg +++ b/linphone-app/assets/images/new_conference_hovered.svg @@ -1,13 +1,91 @@ - - - - new_conference_clic - Created with Sketch. - - - - - - - + + + + + + + + + diff --git a/linphone-app/assets/images/new_conference_normal.svg b/linphone-app/assets/images/new_conference_normal.svg index 0d4f27b4b..757af6e8f 100644 --- a/linphone-app/assets/images/new_conference_normal.svg +++ b/linphone-app/assets/images/new_conference_normal.svg @@ -1,13 +1,88 @@ - - - - new_conference_default - Created with Sketch. - - - - - - - + + + + + + + + + diff --git a/linphone-app/assets/images/new_conference_pressed.svg b/linphone-app/assets/images/new_conference_pressed.svg index af5e8e8ca..dc9f2cf75 100644 --- a/linphone-app/assets/images/new_conference_pressed.svg +++ b/linphone-app/assets/images/new_conference_pressed.svg @@ -1,13 +1,91 @@ - - - - new_conference_default - Created with Sketch. - - - - - - - - \ No newline at end of file + + + + + + + + + + diff --git a/linphone-app/assets/images/panel_hidden_hovered.svg b/linphone-app/assets/images/panel_hidden_hovered.svg new file mode 100644 index 000000000..99dd8189d --- /dev/null +++ b/linphone-app/assets/images/panel_hidden_hovered.svg @@ -0,0 +1,57 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/panel_hidden_normal.svg b/linphone-app/assets/images/panel_hidden_normal.svg new file mode 100644 index 000000000..99dd8189d --- /dev/null +++ b/linphone-app/assets/images/panel_hidden_normal.svg @@ -0,0 +1,57 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/panel_hidden_pressed.svg b/linphone-app/assets/images/panel_hidden_pressed.svg new file mode 100644 index 000000000..99dd8189d --- /dev/null +++ b/linphone-app/assets/images/panel_hidden_pressed.svg @@ -0,0 +1,57 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/panel_shown_hovered.svg b/linphone-app/assets/images/panel_shown_hovered.svg new file mode 100644 index 000000000..a9d328703 --- /dev/null +++ b/linphone-app/assets/images/panel_shown_hovered.svg @@ -0,0 +1,57 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/panel_shown_normal.svg b/linphone-app/assets/images/panel_shown_normal.svg new file mode 100644 index 000000000..a9d328703 --- /dev/null +++ b/linphone-app/assets/images/panel_shown_normal.svg @@ -0,0 +1,57 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/panel_shown_pressed.svg b/linphone-app/assets/images/panel_shown_pressed.svg new file mode 100644 index 000000000..a9d328703 --- /dev/null +++ b/linphone-app/assets/images/panel_shown_pressed.svg @@ -0,0 +1,57 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/plus_disabled.svg b/linphone-app/assets/images/plus_disabled.svg new file mode 100644 index 000000000..e1ac823f8 --- /dev/null +++ b/linphone-app/assets/images/plus_disabled.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/plus_hovered.svg b/linphone-app/assets/images/plus_hovered.svg new file mode 100644 index 000000000..8067cda62 --- /dev/null +++ b/linphone-app/assets/images/plus_hovered.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/plus_normal.svg b/linphone-app/assets/images/plus_normal.svg new file mode 100644 index 000000000..558867507 --- /dev/null +++ b/linphone-app/assets/images/plus_normal.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/plus_pressed.svg b/linphone-app/assets/images/plus_pressed.svg new file mode 100644 index 000000000..84b7f71da --- /dev/null +++ b/linphone-app/assets/images/plus_pressed.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/remove_normal.svg b/linphone-app/assets/images/remove_normal.svg new file mode 100644 index 000000000..97cc19996 --- /dev/null +++ b/linphone-app/assets/images/remove_normal.svg @@ -0,0 +1,50 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/remove_participant_hovered.svg b/linphone-app/assets/images/remove_participant_hovered.svg new file mode 100644 index 000000000..c0c2126c7 --- /dev/null +++ b/linphone-app/assets/images/remove_participant_hovered.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/remove_participant_normal.svg b/linphone-app/assets/images/remove_participant_normal.svg new file mode 100644 index 000000000..f53108df9 --- /dev/null +++ b/linphone-app/assets/images/remove_participant_normal.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/remove_participant_pressed.svg b/linphone-app/assets/images/remove_participant_pressed.svg new file mode 100644 index 000000000..f8ba7a4bb --- /dev/null +++ b/linphone-app/assets/images/remove_participant_pressed.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/secure_level_1.svg b/linphone-app/assets/images/secure_level_1.svg new file mode 100644 index 000000000..7e9adcd94 --- /dev/null +++ b/linphone-app/assets/images/secure_level_1.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/secure_level_2.svg b/linphone-app/assets/images/secure_level_2.svg new file mode 100644 index 000000000..3518579a5 --- /dev/null +++ b/linphone-app/assets/images/secure_level_2.svg @@ -0,0 +1,198 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/secure_level_unsafe.svg b/linphone-app/assets/images/secure_level_unsafe.svg new file mode 100644 index 000000000..4b6767918 --- /dev/null +++ b/linphone-app/assets/images/secure_level_unsafe.svg @@ -0,0 +1,154 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/secure_off.svg b/linphone-app/assets/images/secure_off.svg new file mode 100644 index 000000000..326ea4b46 --- /dev/null +++ b/linphone-app/assets/images/secure_off.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/secure_on.svg b/linphone-app/assets/images/secure_on.svg new file mode 100644 index 000000000..a38470fff --- /dev/null +++ b/linphone-app/assets/images/secure_on.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/send.svg b/linphone-app/assets/images/send.svg new file mode 100644 index 000000000..64e4bef27 --- /dev/null +++ b/linphone-app/assets/images/send.svg @@ -0,0 +1,75 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/timeline_close.svg b/linphone-app/assets/images/timeline_close.svg new file mode 100644 index 000000000..97cc19996 --- /dev/null +++ b/linphone-app/assets/images/timeline_close.svg @@ -0,0 +1,50 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/timeline_filter.svg b/linphone-app/assets/images/timeline_filter.svg new file mode 100644 index 000000000..d2cd7a860 --- /dev/null +++ b/linphone-app/assets/images/timeline_filter.svg @@ -0,0 +1,50 @@ + + + + + + + + + + diff --git a/linphone-app/assets/images/timeline_search (copy).svg b/linphone-app/assets/images/timeline_search (copy).svg new file mode 100644 index 000000000..db2c58d33 --- /dev/null +++ b/linphone-app/assets/images/timeline_search (copy).svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/timeline_search.svg b/linphone-app/assets/images/timeline_search.svg new file mode 100644 index 000000000..61b2cf396 --- /dev/null +++ b/linphone-app/assets/images/timeline_search.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + diff --git a/linphone-app/assets/images/timer.svg b/linphone-app/assets/images/timer.svg new file mode 100644 index 000000000..6c7f18da1 --- /dev/null +++ b/linphone-app/assets/images/timer.svg @@ -0,0 +1,124 @@ + + + + + + + + + + diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc index aca2f24d0..2fd5c0420 100644 --- a/linphone-app/resources.qrc +++ b/linphone-app/resources.qrc @@ -12,6 +12,9 @@ assets/images/add_hovered.svg assets/images/add_normal.svg assets/images/add_pressed.svg + assets/images/add_participant_hovered.svg + assets/images/add_participant_normal.svg + assets/images/add_participant_pressed.svg assets/images/attachment_disabled.svg assets/images/attachment_hovered.svg assets/images/attachment_normal.svg @@ -68,7 +71,12 @@ assets/images/chat_normal.svg assets/images/chat_pressed.svg assets/images/chat_read.svg + assets/images/chat_room.svg + assets/images/close.svg assets/images/collapse.svg + assets/images/collapsed.svg + assets/images/conferences_normal.svg + assets/images/conferences_selected.svg assets/images/contact_add_hovered.svg assets/images/contact_add_normal.svg assets/images/contact_add_pressed.svg @@ -82,6 +90,7 @@ assets/images/contact_edit_pressed.svg assets/images/contact_normal.svg assets/images/contact_selected.svg + assets/images/current_account_status_available.svg assets/images/declined_incoming_call.svg assets/images/declined_outgoing_call.svg assets/images/delete_hovered.svg @@ -93,6 +102,7 @@ assets/images/edit_normal.svg assets/images/edit_pressed.svg assets/images/ended_call.svg + assets/images/expanded.svg assets/images/file_hovered.svg assets/images/file_normal.svg assets/images/file_pressed.svg @@ -118,13 +128,21 @@ assets/images/home_account_assistant.svg assets/images/home_invite_friends.svg assets/images/home_normal.svg - assets/images/home_selected.svg + assets/images/home_pressed.svg + assets/images/home_hovered.svg + assets/images/home_disabled.svg assets/images/home_use_linphone.svg assets/images/incoming_call.svg assets/images/led_green.svg assets/images/led_orange.svg assets/images/led_red.svg assets/images/led_white.svg + assets/images/menu_vdots_normal.svg + assets/images/menu_vdots_hovered.svg + assets/images/menu_vdots_pressed.svg + assets/images/menu_infos.svg + assets/images/menu_devices.svg + assets/images/menu_ephemeral.svg assets/images/message_sign.svg assets/images/micro_off_hovered.svg assets/images/micro_off_normal.svg @@ -138,6 +156,12 @@ assets/images/new_call_hovered.svg assets/images/new_call_normal.svg assets/images/new_call_pressed.svg + assets/images/new_chat_hovered.svg + assets/images/new_chat_normal.svg + assets/images/new_chat_pressed.svg + assets/images/new_chat_group_hovered.svg + assets/images/new_chat_group_normal.svg + assets/images/new_chat_group_pressed.svg assets/images/new_conference_hovered.svg assets/images/new_conference_normal.svg assets/images/new_conference_pressed.svg @@ -145,6 +169,12 @@ assets/images/options_normal.svg assets/images/options_pressed.svg assets/images/outgoing_call.svg + assets/images/panel_hidden_normal.svg + assets/images/panel_hidden_hovered.svg + assets/images/panel_hidden_pressed.svg + assets/images/panel_shown_normal.svg + assets/images/panel_shown_hovered.svg + assets/images/panel_shown_pressed.svg assets/images/pause_off_hovered.svg assets/images/pause_off_normal.svg assets/images/pause_off_pressed.svg @@ -153,13 +183,26 @@ assets/images/pause_on_normal.svg assets/images/pause_on_pressed.svg assets/images/pause_on_updating.svg + assets/images/plus_disabled.svg + assets/images/plus_hovered.svg + assets/images/plus_normal.svg + assets/images/plus_pressed.svg assets/images/recording_sign.svg assets/images/record_off.svg assets/images/record_on.svg + assets/images/remove_participant_hovered.svg + assets/images/remove_participant_normal.svg + assets/images/remove_participant_pressed.svg assets/images/screenshot_hovered.svg assets/images/screenshot_normal.svg assets/images/screenshot_pressed.svg assets/images/search.svg + assets/images/secure_level_unsafe.svg + assets/images/secure_level_1.svg + assets/images/secure_level_2.svg + assets/images/secure_off.svg + assets/images/secure_on.svg + assets/images/send.svg assets/images/settings_advanced_normal.svg assets/images/settings_advanced_selected.svg assets/images/settings_audio_normal.svg @@ -180,10 +223,14 @@ assets/images/speaker_on_normal.svg assets/images/speaker_on_pressed.svg assets/images/speaker.svg + assets/images/timer.svg assets/images/tel_keypad_hovered.svg assets/images/tel_keypad_normal.svg assets/images/tel_keypad_pressed.svg assets/images/timeline_history.svg + assets/images/timeline_filter.svg + assets/images/timeline_search.svg + assets/images/timeline_close.svg assets/images/tooltip_arrow_bottom.svg assets/images/tooltip_arrow_left.svg assets/images/tooltip_arrow_right.svg @@ -204,6 +251,7 @@ ui/modules/Common/Dialog/ConfirmDialog.qml ui/modules/Common/Dialog/DialogDescription.qml ui/modules/Common/Dialog/DialogPlus.qml + ui/modules/Common/Dialog/DialogTitle.qml ui/modules/Common/Form/ActionBar.qml ui/modules/Common/Form/ActionButton.qml ui/modules/Common/Form/ActionSwitch.qml @@ -330,6 +378,7 @@ ui/modules/Linphone/Chat/IncomingMessage.qml ui/modules/Linphone/Chat/Message.js ui/modules/Linphone/Chat/Message.qml + ui/modules/Linphone/Chat/Notice.qml ui/modules/Linphone/Chat/OutgoingMessage.qml ui/modules/Linphone/Codecs/CodecAttribute.qml ui/modules/Linphone/Codecs/CodecLegend.qml @@ -386,6 +435,7 @@ ui/modules/Linphone/TelKeypad/TelKeypad.qml ui/modules/Linphone/Timeline/Timeline.js ui/modules/Linphone/Timeline/Timeline.qml + ui/modules/Linphone/View/ParticipantsView.qml ui/modules/Linphone/View/SipAddressesView.qml ui/scripts/LinphoneUtils/linphone-utils.js ui/scripts/LinphoneUtils/qmldir @@ -430,9 +480,13 @@ ui/views/App/Main/Dialogs/About.qml ui/views/App/Main/Dialogs/AuthenticationRequest.js ui/views/App/Main/Dialogs/AuthenticationRequest.qml + ui/views/App/Main/Dialogs/EphemeralChatRoom.qml + ui/views/App/Main/Dialogs/InfoChatRoom.qml ui/views/App/Main/Dialogs/ManageAccount.js ui/views/App/Main/Dialogs/ManageAccounts.qml ui/views/App/Main/Dialogs/ManageChatRoom.qml + ui/views/App/Main/Dialogs/NewChatRoom.qml + ui/views/App/Main/Dialogs/ParticipantsDevices.qml ui/views/App/Main/Home.qml ui/views/App/Main/HistoryView.qml ui/views/App/Main/HistoryView.js @@ -491,6 +545,7 @@ assets/images/linphone_logo.svg ui/dev-modules/Colors/Colors.qml ui/dev-modules/Units/Units.qml + ui/dev-modules/Tools/Tools.qml assets/icon.ico diff --git a/linphone-app/src/app/App.cpp b/linphone-app/src/app/App.cpp index 57c589499..71a52fc22 100644 --- a/linphone-app/src/app/App.cpp +++ b/linphone-app/src/app/App.cpp @@ -44,7 +44,6 @@ #include "providers/ExternalImageProvider.hpp" #include "providers/ThumbnailProvider.hpp" #include "translator/DefaultTranslator.hpp" -#include "utils/LinphoneUtils.hpp" #include "utils/Utils.hpp" #include "components/other/desktop-tools/DesktopTools.hpp" @@ -52,6 +51,10 @@ #include "components/timeline/TimelineListModel.hpp" #include "components/timeline/TimelineProxyModel.hpp" +#include "components/participant/ParticipantModel.hpp" +#include "components/participant/ParticipantListModel.hpp" +#include "components/participant/ParticipantProxyModel.hpp" + // ============================================================================= using namespace std; @@ -216,7 +219,7 @@ App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::U connect(this, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(stateChanged(Qt::ApplicationState))); - setWindowIcon(QIcon(LinphoneUtils::WindowIconPath)); + setWindowIcon(QIcon(Utils::WindowIconPath)); createParser(); mParser->process(*this); @@ -585,7 +588,11 @@ void App::registerTypes () { qRegisterMetaType(); qRegisterMetaType>(); qRegisterMetaType > >(); + qRegisterMetaType>(); qRegisterMetaType>(); + qRegisterMetaType>(); + qRegisterMetaType>(); + LinphoneEnums::registerMetaTypes(); registerType("AssistantModel"); registerType("AuthenticationNotifier"); @@ -603,8 +610,11 @@ void App::registerTypes () { registerType("LdapProxyModel"); registerType("SipAddressesProxyModel"); registerType("SearchSipAddressesModel"); + registerType("SearchSipAddressesProxyModel"); + registerType("TimelineProxyModel"); + registerType("ParticipantProxyModel"); registerType("SoundPlayer"); registerType("TelephoneNumbersModel"); @@ -616,6 +626,7 @@ void App::registerTypes () { registerSingletonType("VideoCodecsModel"); registerUncreatableType("CallModel"); + registerUncreatableType("ChatMessageModel"); registerUncreatableType("ChatRoomModel"); registerUncreatableType("ConferenceAddModel"); registerUncreatableType("ContactModel"); @@ -625,6 +636,13 @@ void App::registerTypes () { registerUncreatableType("SipAddressObserver"); registerUncreatableType("VcardModel"); registerUncreatableType("TimelineModel"); + registerUncreatableType("ParticipantModel"); + registerUncreatableType("ParticipantListModel"); + registerUncreatableType("ParticipantDeviceModel"); + registerUncreatableType("ParticipantDeviceListModel"); + registerUncreatableType("ParticipantDeviceProxyModel"); + + qmlRegisterUncreatableMetaObject(LinphoneEnums::staticMetaObject, "LinphoneEnums", 1, 0, "LinphoneEnums", "Only enums"); } void App::registerSharedTypes () { @@ -634,7 +652,7 @@ void App::registerSharedTypes () { registerSharedSingletonType("CoreManager"); registerSharedSingletonType("SettingsModel"); registerSharedSingletonType("AccountSettingsModel"); - registerSharedSingletonType("SipAddressesModel"); + registerSharedSingletonType("SipAddressesModel"); registerSharedSingletonType("CallsListModel"); registerSharedSingletonType("ContactsListModel"); registerSharedSingletonType("ContactsImporterListModel"); @@ -650,6 +668,7 @@ void App::registerToolTypes () { registerToolType("TextToSpeech"); registerToolType("Units"); registerToolType("ContactsImporterPluginsManager"); + registerToolType("Utils"); } void App::registerSharedToolTypes () { @@ -711,7 +730,7 @@ void App::setTrayIcon () { systemTrayIcon->setContextMenu(menu); - systemTrayIcon->setIcon(QIcon(LinphoneUtils::WindowIconPath)); + systemTrayIcon->setIcon(QIcon(Utils::WindowIconPath)); systemTrayIcon->setToolTip(APPLICATION_NAME); systemTrayIcon->show(); mSystemTrayIcon = systemTrayIcon; diff --git a/linphone-app/src/components/Components.hpp b/linphone-app/src/components/Components.hpp index f17758b70..f4ce82e37 100644 --- a/linphone-app/src/components/Components.hpp +++ b/linphone-app/src/components/Components.hpp @@ -28,6 +28,7 @@ #include "calls/CallsListProxyModel.hpp" #include "camera/Camera.hpp" #include "camera/CameraPreview.hpp" +#include "components/chat-message/ChatMessageModel.hpp" #include "chat-room/ChatRoomProxyModel.hpp" #include "codecs/AudioCodecsModel.hpp" #include "codecs/VideoCodecsModel.hpp" @@ -50,12 +51,19 @@ #include "ldap/LdapListModel.hpp" #include "ldap/LdapProxyModel.hpp" #include "notifier/Notifier.hpp" +#include "participant/ParticipantListModel.hpp" +#include "participant/ParticipantModel.hpp" +#include "participant/ParticipantProxyModel.hpp" +#include "participant/ParticipantDeviceListModel.hpp" +#include "participant/ParticipantDeviceModel.hpp" +#include "participant/ParticipantDeviceProxyModel.hpp" #include "presence/OwnPresenceModel.hpp" #include "settings/AccountSettingsModel.hpp" #include "settings/SettingsModel.hpp" #include "sip-addresses/SipAddressesModel.hpp" #include "sip-addresses/SipAddressesProxyModel.hpp" #include "sip-addresses/SearchSipAddressesModel.hpp" +#include "sip-addresses/SearchSipAddressesProxyModel.hpp" #include "sound-player/SoundPlayer.hpp" #include "telephone-numbers/TelephoneNumbersModel.hpp" #include "timeline/TimelineModel.hpp" diff --git a/linphone-app/src/components/assistant/AssistantModel.cpp b/linphone-app/src/components/assistant/AssistantModel.cpp index b8023806c..03918eed1 100644 --- a/linphone-app/src/components/assistant/AssistantModel.cpp +++ b/linphone-app/src/components/assistant/AssistantModel.cpp @@ -23,7 +23,6 @@ #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" -#include "utils/LinphoneUtils.hpp" #include "utils/Utils.hpp" #include "AssistantModel.hpp" @@ -222,7 +221,7 @@ bool AssistantModel::addOtherSipAccount (const QVariantMap &map) { const QString &transport(map["transport"].toString()); if (!transport.isEmpty()) - address->setTransport(LinphoneUtils::stringToTransportType(transport)); + address->setTransport(Utils::stringToTransportType(transport)); if (proxyConfig->setServerAddr(address->asString())) { qWarning() << QStringLiteral("Unable to add server address: `%1`.") diff --git a/linphone-app/src/components/call/CallModel.cpp b/linphone-app/src/components/call/CallModel.cpp index 9daf94b5b..daf0bd23e 100644 --- a/linphone-app/src/components/call/CallModel.cpp +++ b/linphone-app/src/components/call/CallModel.cpp @@ -33,7 +33,6 @@ #include "components/notifier/Notifier.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" -#include "utils/LinphoneUtils.hpp" #include "utils/MediastreamerUtils.hpp" #include "utils/Utils.hpp" diff --git a/linphone-app/src/components/calls/CallsListModel.cpp b/linphone-app/src/components/calls/CallsListModel.cpp index 91da689da..51ddae494 100644 --- a/linphone-app/src/components/calls/CallsListModel.cpp +++ b/linphone-app/src/components/calls/CallsListModel.cpp @@ -28,7 +28,10 @@ #include "components/conference/ConferenceHelperModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" +#include "components/participant/ParticipantModel.hpp" #include "components/settings/SettingsModel.hpp" +#include "components/timeline/TimelineListModel.hpp" +#include "components/timeline/TimelineModel.hpp" #include "utils/Utils.hpp" #include "CallsListModel.hpp" @@ -132,6 +135,43 @@ void CallsListModel::launchAudioCall (const QString &sipAddress, const QHashinviteAddressWithParams(address, params); } +void CallsListModel::launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers) const { + shared_ptr core = CoreManager::getInstance()->getCore(); + + shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress)); + if (!address) + return; + + shared_ptr params = core->createCallParams(nullptr); + params->enableVideo(false); + + QHashIterator iterator(headers); + while (iterator.hasNext()) { + iterator.next(); + params->addCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value())); + } + params->setProxyConfig(core->getDefaultProxyConfig()); + CallModel::setRecordFile(params, QString::fromStdString(address->getUsername())); + shared_ptr currentProxyConfig = core->getDefaultProxyConfig(); + params->setMediaEncryption(LinphoneEnums::toLinphone(encryption)); + if(currentProxyConfig){ + if(currentProxyConfig->getState() == linphone::RegistrationState::Ok) + core->inviteAddressWithParams(address, params); + else{ + QObject * context = new QObject(); + QObject::connect(CoreManager::getInstance()->getHandlers().get(), &CoreHandlers::registrationStateChanged,context, + [address,core,params,currentProxyConfig, context](const std::shared_ptr &proxyConfig, linphone::RegistrationState state) mutable { + if(context && proxyConfig==currentProxyConfig && state==linphone::RegistrationState::Ok){ + delete context; + context = nullptr; + core->inviteAddressWithParams(address, params); + } + }); + } + }else + core->inviteAddressWithParams(address, params); +} + void CallsListModel::launchVideoCall (const QString &sipAddress) const { shared_ptr core = CoreManager::getInstance()->getCore(); if (!core->videoSupported()) { @@ -151,11 +191,11 @@ void CallsListModel::launchVideoCall (const QString &sipAddress) const { core->inviteAddressWithParams(address, params); } -bool CallsListModel::launchSecureChat (const QString &sipAddress) const { +ChatRoomModel* CallsListModel::launchSecureChat (const QString &sipAddress) const { shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(sipAddress)); if (!address) - return false; + return nullptr; std::shared_ptr params = core->createDefaultChatRoomParams(); std::list > participants; @@ -164,7 +204,7 @@ bool CallsListModel::launchSecureChat (const QString &sipAddress) const { auto proxy = core->getDefaultProxyConfig(); params->enableEncryption(true); - params->setSubject("This is Desktop test"); + params->setSubject("Dummy Subject"); params->setBackend(linphone::ChatRoomBackend::FlexisipChat); params->setEncryptionBackend(linphone::ChatRoomEncryptionBackend::Lime); @@ -181,7 +221,52 @@ bool CallsListModel::launchSecureChat (const QString &sipAddress) const { return chatRoom!=nullptr; */ - return false; + if( chatRoom != nullptr){ + auto timelineList = CoreManager::getInstance()->getTimelineListModel(); + timelineList->update(); + auto timeline = timelineList->getTimeline(chatRoom, false); + if(!timeline){ + timeline = timelineList->getTimeline(chatRoom, true); + timelineList->add(timeline); + } + return timeline->getChatRoomModel(); + } + return nullptr; +} + +ChatRoomModel* CallsListModel::createChat (const QString &participantAddress) const{ + shared_ptr core = CoreManager::getInstance()->getCore(); + shared_ptr address = core->interpretUrl(Utils::appStringToCoreString(participantAddress)); + if (!address) + return nullptr; + + std::shared_ptr params = core->createDefaultChatRoomParams(); + std::list > participants; + std::shared_ptr localAddress; + participants.push_back(address); + auto proxy = core->getDefaultProxyConfig(); + + params->setBackend(linphone::ChatRoomBackend::Basic); + + std::shared_ptr chatRoom = core->createChatRoom(params, localAddress, participants); + /* + if( chatRoom!=nullptr){ + auto search = core->searchChatRoom(params, localAddress + , address + , participants); + if(search != chatRoom) + qWarning("toto"); + } + + + return chatRoom!=nullptr; + */ + if( chatRoom != nullptr){ + auto timelineList = CoreManager::getInstance()->getTimelineListModel(); + auto timeline = timelineList->getTimeline(chatRoom, true); + return timeline->getChatRoomModel(); + } + return nullptr; } bool CallsListModel::createSecureChat (const QString& subject, const QString &participantAddress) const{ @@ -206,6 +291,55 @@ bool CallsListModel::createSecureChat (const QString& subject, const QString &pa return chatRoom != nullptr; } +bool CallsListModel::createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants) const{ + shared_ptr core = CoreManager::getInstance()->getCore(); + std::shared_ptr chatRoom; + qWarning() << "Creation of " << subject << " " << securityLevel << " " << participants; + for(auto p : participants){ + ParticipantModel* pp = p.value(); + qWarning() << pp->getSipAddress() << "=>" << pp->getAdminStatus(); + } + + + + std::shared_ptr params = core->createDefaultChatRoomParams(); + std::list > chatRoomParticipants; + std::shared_ptr localAddress; + for(auto p : participants){ + ParticipantModel* participant = p.value(); + auto address = Utils::interpretUrl(participant->getSipAddress()); + if( address) + chatRoomParticipants.push_back( address ); + } + auto proxy = core->getDefaultProxyConfig(); + params->enableEncryption(securityLevel>0); + + if( securityLevel>0){ + params->setBackend(linphone::ChatRoomBackend::FlexisipChat); + params->setEncryptionBackend(linphone::ChatRoomEncryptionBackend::Lime); + }else + params->setBackend(linphone::ChatRoomBackend::Basic); + params->enableGroup(subject != ""); + + + if(chatRoomParticipants.size() > 0) { + if(!params->groupEnabled()) {// Chat room is one-one : check if it is already exist with empty or dummy subject + chatRoom = core->searchChatRoom(params, localAddress + , localAddress + , chatRoomParticipants); + params->setSubject(subject != ""?subject.toStdString():"Dummy Subject"); + if(!chatRoom) + chatRoom = core->searchChatRoom(params, localAddress + , localAddress + , chatRoomParticipants); + }else + params->setSubject(subject != ""?subject.toStdString():"Dummy Subject"); + if( !chatRoom) + chatRoom = core->createChatRoom(params, localAddress, chatRoomParticipants); + } + return chatRoom != nullptr; +} + // ----------------------------------------------------------------------------- int CallsListModel::getRunningCallsNumber () const { diff --git a/linphone-app/src/components/calls/CallsListModel.hpp b/linphone-app/src/components/calls/CallsListModel.hpp index 7a978dfcf..441f7c1bd 100644 --- a/linphone-app/src/components/calls/CallsListModel.hpp +++ b/linphone-app/src/components/calls/CallsListModel.hpp @@ -24,13 +24,16 @@ #include #include +#include "components/call/CallModel.hpp" +#include "utils/LinphoneEnums.hpp" + // ============================================================================= -class CallModel; +class ChatRoomModel; class CoreHandlers; class CallsListModel : public QAbstractListModel { - Q_OBJECT; + Q_OBJECT public: CallsListModel (QObject *parent = Q_NULLPTR); @@ -43,9 +46,13 @@ public: void askForTransfer (CallModel *callModel); Q_INVOKABLE void launchAudioCall (const QString &sipAddress, const QHash &headers = {}) const; + Q_INVOKABLE void launchSecureAudioCall (const QString &sipAddress, LinphoneEnums::MediaEncryption encryption, const QHash &headers = {}) const; Q_INVOKABLE void launchVideoCall (const QString &sipAddress) const; - Q_INVOKABLE bool launchSecureChat (const QString &sipAddress) const; + Q_INVOKABLE ChatRoomModel* launchSecureChat (const QString &sipAddress) const; + Q_INVOKABLE ChatRoomModel* createChat (const QString &participantAddress) const; Q_INVOKABLE bool createSecureChat (const QString& subject, const QString &participantAddress) const; + + Q_INVOKABLE bool createChatRoom(const QString& subject, const int& securityLevel, const QVariantList& participants) const; Q_INVOKABLE int getRunningCallsNumber () const; diff --git a/linphone-app/src/components/chat-message/ChatMessageModel.cpp b/linphone-app/src/components/chat-message/ChatMessageModel.cpp new file mode 100644 index 000000000..7a40395e2 --- /dev/null +++ b/linphone-app/src/components/chat-message/ChatMessageModel.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "app/App.hpp" + +#include "ChatMessageModel.hpp" + +// ============================================================================= + +ChatMessageModel::ChatMessageModel ( std::shared_ptr chatMessage, QObject * parent) : QObject(parent) { + mChatMessage = chatMessage; +} + +std::shared_ptr ChatMessageModel::getChatMessage(){ + return mChatMessage; +} + +bool ChatMessageModel::isEphemeral() const{ + return mChatMessage->isEphemeral(); +} + +qint64 ChatMessageModel::getEphemeralExpireTime() const{ + return mChatMessage->getEphemeralExpireTime(); +} \ No newline at end of file diff --git a/linphone-app/src/components/chat-message/ChatMessageModel.hpp b/linphone-app/src/components/chat-message/ChatMessageModel.hpp new file mode 100644 index 000000000..e2eca1b5a --- /dev/null +++ b/linphone-app/src/components/chat-message/ChatMessageModel.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CHAT_MESSAGE_MODEL_H +#define CHAT_MESSAGE_MODEL_H + +#include "utils/LinphoneEnums.hpp" + +// ============================================================================= + + +class ChatMessageModel : public QObject { + Q_OBJECT + +public: + ChatMessageModel (std::shared_ptr chatMessage, QObject * parent = nullptr); + + Q_PROPERTY(bool isEphemeral READ isEphemeral NOTIFY isEphemeralChanged) + Q_PROPERTY(qint64 ephemeralExpireTime READ getEphemeralExpireTime NOTIFY ephemeralExpireTimeChanged) + + std::shared_ptr getChatMessage(); + + bool isEphemeral() const; + qint64 getEphemeralExpireTime() const; + +signals: + void isEphemeralChanged(); + void ephemeralExpireTimeChanged(); + + +private: + std::shared_ptr mChatMessage; +}; + +Q_DECLARE_METATYPE(std::shared_ptr) + +#endif diff --git a/linphone-app/src/components/chat-room/ChatRoomListModel.cpp b/linphone-app/src/components/chat-room/ChatRoomListModel.cpp index ee74d4ba0..f61ce6cc9 100644 --- a/linphone-app/src/components/chat-room/ChatRoomListModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomListModel.cpp @@ -38,8 +38,8 @@ using namespace std; namespace { - // Delay before removing call in ms. - constexpr int DelayBeforeRemoveCall = 3000; +// Delay before removing call in ms. +constexpr int DelayBeforeRemoveCall = 3000; } @@ -49,48 +49,48 @@ ChatRoomListModel::ChatRoomListModel (QObject *parent) : QAbstractListModel(pare } int ChatRoomListModel::rowCount (const QModelIndex &) const { - return mList.count(); + return mList.count(); } QHash ChatRoomListModel::roleNames () const { - QHash roles; - roles[Qt::DisplayRole] = "$chatRoom"; - return roles; + QHash roles; + roles[Qt::DisplayRole] = "$chatRoom"; + return roles; } QVariant ChatRoomListModel::data (const QModelIndex &index, int role) const { - int row = index.row(); - - if (!index.isValid() || row < 0 || row >= mList.count()) - return QVariant(); - - if (role == Qt::DisplayRole) - return QVariant::fromValue(mList[row]); - - return QVariant(); + int row = index.row(); + + if (!index.isValid() || row < 0 || row >= mList.count()) + return QVariant(); + + if (role == Qt::DisplayRole) + return QVariant::fromValue(mList[row]); + + return QVariant(); } // ----------------------------------------------------------------------------- bool ChatRoomListModel::removeRow (int row, const QModelIndex &parent) { - return removeRows(row, 1, parent); + return removeRows(row, 1, parent); } bool ChatRoomListModel::removeRows (int row, int count, const QModelIndex &parent) { - int limit = row + count - 1; - - if (row < 0 || count < 0 || limit >= mList.count()) - return false; - - beginRemoveRows(parent, row, limit); - - for (int i = 0; i < count; ++i) - mList.takeAt(row)->deleteLater(); - - endRemoveRows(); - - return true; + int limit = row + count - 1; + + if (row < 0 || count < 0 || limit >= mList.count()) + return false; + + beginRemoveRows(parent, row, limit); + + for (int i = 0; i < count; ++i) + mList.takeAt(row)->deleteLater(); + + endRemoveRows(); + + return true; } // ----------------------------------------------------------------------------- diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.cpp b/linphone-app/src/components/chat-room/ChatRoomModel.cpp index 99879af52..434bc61bc 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.cpp @@ -18,6 +18,8 @@ * along with this program. If not, see . */ +#include "ChatRoomModel.hpp" + #include #include @@ -34,6 +36,7 @@ #include "app/App.hpp" #include "app/paths/Paths.hpp" #include "app/providers/ThumbnailProvider.hpp" +#include "components/chat-message/ChatMessageModel.hpp" #include "components/contact/ContactModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" @@ -42,71 +45,73 @@ #include "components/notifier/Notifier.hpp" #include "components/settings/SettingsModel.hpp" #include "components/participant/ParticipantModel.hpp" +#include "components/participant/ParticipantListModel.hpp" #include "components/presence/Presence.hpp" #include "utils/QExifImageHeader.hpp" #include "utils/Utils.hpp" +#include "utils/LinphoneEnums.hpp" + -#include "ChatRoomModel.hpp" // ============================================================================= using namespace std; namespace { - constexpr int ThumbnailImageFileWidth = 100; - constexpr int ThumbnailImageFileHeight = 100; +constexpr int ThumbnailImageFileWidth = 100; +constexpr int ThumbnailImageFileHeight = 100; - // In Bytes. - constexpr qint64 FileSizeLimit = 524288000; +// In Bytes. +constexpr qint64 FileSizeLimit = 524288000; } // MessageAppData is using to parse what's it in Appdata field of a message class MessageAppData { public: - MessageAppData(){} - MessageAppData(const QString&); - QString m_id; - QString m_path; - QString toString()const; - void fromString(const QString& ); - static QString toString(const QVector& ); - static QVector fromListString(const QString& ); + MessageAppData(){} + MessageAppData(const QString&); + QString m_id; + QString m_path; + QString toString()const; + void fromString(const QString& ); + static QString toString(const QVector& ); + static QVector fromListString(const QString& ); }; MessageAppData::MessageAppData(const QString& p_data) { - fromString(p_data); + fromString(p_data); } QString MessageAppData::toString()const { - return m_id+':'+m_path; + return m_id+':'+m_path; } void MessageAppData::fromString(const QString& p_data) { - QStringList fields = p_data.split(':'); - if( fields.size() > 1) - { - m_id = fields[0]; - m_path = fields[1]; - } + QStringList fields = p_data.split(':'); + if( fields.size() > 1) + { + m_id = fields[0]; + m_path = fields[1]; + } } QString MessageAppData::toString(const QVector& p_data) { - QString serialization; - if( p_data.size() > 0) - { - serialization = p_data[0].toString(); - for(int i = 1 ; i < p_data.size() ; ++i) - serialization += ';'+p_data[i].toString(); - } - return serialization; + QString serialization; + if( p_data.size() > 0) + { + serialization = p_data[0].toString(); + for(int i = 1 ; i < p_data.size() ; ++i) + serialization += ';'+p_data[i].toString(); + } + return serialization; } QVector MessageAppData::fromListString(const QString& p_data) { - QVector data; - QStringList files = p_data.split(";"); - for(int i = 0 ; i < files.size() ; ++i) - data.push_back(MessageAppData(files[i])); - return data; + QVector data; + QStringList files = p_data.split(";"); + for(int i = 0 ; i < files.size() ; ++i) + data.push_back(MessageAppData(files[i])); + return data; } @@ -116,258 +121,309 @@ static inline MessageAppData getMessageAppData (const shared_ptr &message) { - const MessageAppData appData = getMessageAppData(message); - return !appData.m_path.isEmpty() && QFileInfo(appData.m_path).isFile(); + const MessageAppData appData = getMessageAppData(message); + return !appData.m_path.isEmpty() && QFileInfo(appData.m_path).isFile(); } // Set the thumbnail as the first content static inline void fillThumbnailProperty (QVariantMap &dest, const shared_ptr &message) { - if( !dest.contains("thumbnail")) - { - MessageAppData thumbnailData = getMessageAppData(message); - if( thumbnailData.m_id != "") - dest["thumbnail"] = QStringLiteral("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(thumbnailData.m_id); - } + if( !dest.contains("thumbnail")) + { + MessageAppData thumbnailData = getMessageAppData(message); + if( thumbnailData.m_id != "") + dest["thumbnail"] = QStringLiteral("image://%1/%2").arg(ThumbnailProvider::ProviderId).arg(thumbnailData.m_id); + } } // Create a thumbnail from the first content that have a file and store it in Appdata static inline void createThumbnail (const shared_ptr &message) { - if (!message->getAppdata().empty()) - return;// Already exist : no need to create one - std::list > contents = message->getContents(); - if( contents.size() > 0) - { - MessageAppData thumbnailData; - thumbnailData.m_path = Utils::coreStringToAppString(contents.front()->getFilePath()); - QImage image(thumbnailData.m_path); - if( image.isNull()){// Try to determine format from headers - QImageReader reader(thumbnailData.m_path); - reader.setDecideFormatFromContent(true); - QByteArray format = reader.format(); - if(!format.isEmpty()) - image = QImage(thumbnailData.m_path, format); - } - if (!image.isNull()){ - int rotation = 0; - QExifImageHeader exifImageHeader; - if (exifImageHeader.loadFromJpeg(thumbnailData.m_path)) - rotation = int(exifImageHeader.value(QExifImageHeader::ImageTag::Orientation).toShort()); - QImage thumbnail = image.scaled( - ThumbnailImageFileWidth, ThumbnailImageFileHeight, - Qt::KeepAspectRatio, Qt::SmoothTransformation - ); - - if (rotation != 0) { - QTransform transform; - if (rotation == 3 || rotation == 4) - transform.rotate(180); - else if (rotation == 5 || rotation == 6) - transform.rotate(90); - else if (rotation == 7 || rotation == 8) - transform.rotate(-90); - thumbnail = thumbnail.transformed(transform); - if (rotation == 2 || rotation == 4 || rotation == 5 || rotation == 7) - thumbnail = thumbnail.mirrored(true, false); + if (!message->getAppdata().empty()) + return;// Already exist : no need to create one + std::list > contents = message->getContents(); + if( contents.size() > 0) + { + MessageAppData thumbnailData; + thumbnailData.m_path = Utils::coreStringToAppString(contents.front()->getFilePath()); + QImage image(thumbnailData.m_path); + if( image.isNull()){// Try to determine format from headers + QImageReader reader(thumbnailData.m_path); + reader.setDecideFormatFromContent(true); + QByteArray format = reader.format(); + if(!format.isEmpty()) + image = QImage(thumbnailData.m_path, format); } - QString uuid = QUuid::createUuid().toString(); - thumbnailData.m_id = QStringLiteral("%1.jpg").arg(uuid.mid(1, uuid.length() - 2)); - - if (!thumbnail.save(Utils::coreStringToAppString(Paths::getThumbnailsDirPath()) + thumbnailData.m_id , "jpg", 100)) { - qWarning() << QStringLiteral("Unable to create thumbnail of: `%1`.").arg(thumbnailData.m_path); + if (!image.isNull()){ + int rotation = 0; + QExifImageHeader exifImageHeader; + if (exifImageHeader.loadFromJpeg(thumbnailData.m_path)) + rotation = int(exifImageHeader.value(QExifImageHeader::ImageTag::Orientation).toShort()); + QImage thumbnail = image.scaled( + ThumbnailImageFileWidth, ThumbnailImageFileHeight, + Qt::KeepAspectRatio, Qt::SmoothTransformation + ); + + if (rotation != 0) { + QTransform transform; + if (rotation == 3 || rotation == 4) + transform.rotate(180); + else if (rotation == 5 || rotation == 6) + transform.rotate(90); + else if (rotation == 7 || rotation == 8) + transform.rotate(-90); + thumbnail = thumbnail.transformed(transform); + if (rotation == 2 || rotation == 4 || rotation == 5 || rotation == 7) + thumbnail = thumbnail.mirrored(true, false); + } + QString uuid = QUuid::createUuid().toString(); + thumbnailData.m_id = QStringLiteral("%1.jpg").arg(uuid.mid(1, uuid.length() - 2)); + + if (!thumbnail.save(Utils::coreStringToAppString(Paths::getThumbnailsDirPath()) + thumbnailData.m_id , "jpg", 100)) { + qWarning() << QStringLiteral("Unable to create thumbnail of: `%1`.").arg(thumbnailData.m_path); + } } + message->setAppdata(Utils::appStringToCoreString(thumbnailData.toString())); } - message->setAppdata(Utils::appStringToCoreString(thumbnailData.toString())); - } } static inline void removeFileMessageThumbnail (const shared_ptr &message) { - if (message && message->getFileTransferInformation()) { - message->cancelFileTransfer(); - MessageAppData thumbnailFile = getMessageAppData(message); - if(thumbnailFile.m_id.size() > 0) - { - QString thumbnailPath = Utils::coreStringToAppString(Paths::getThumbnailsDirPath()) + thumbnailFile.m_id; - if (!QFile::remove(thumbnailPath)) - qWarning() << QStringLiteral("Unable to remove `%1`.").arg(thumbnailPath); - } - message->setAppdata("");// Remove completely Thumbnail from the message - } + if (message && message->getFileTransferInformation()) { + message->cancelFileTransfer(); + MessageAppData thumbnailFile = getMessageAppData(message); + if(thumbnailFile.m_id.size() > 0) + { + QString thumbnailPath = Utils::coreStringToAppString(Paths::getThumbnailsDirPath()) + thumbnailFile.m_id; + if (!QFile::remove(thumbnailPath)) + qWarning() << QStringLiteral("Unable to remove `%1`.").arg(thumbnailPath); + } + message->setAppdata("");// Remove completely Thumbnail from the message + } } // ----------------------------------------------------------------------------- -static inline void fillMessageEntry (QVariantMap &dest, const shared_ptr &message) { - std::list> contents = message->getContents(); - QString txt; - foreach(auto content, contents){ - if(content->isText()) - txt += content->getStringBuffer().c_str(); - } - dest["content"] = txt; - dest["isOutgoing"] = message->isOutgoing() || message->getState() == linphone::ChatMessage::State::Idle; - - // Old workaround. - // It can exist messages with a not delivered status. It's a linphone core bug. - linphone::ChatMessage::State state = message->getState(); - if (state == linphone::ChatMessage::State::InProgress) - dest["status"] = ChatRoomModel::MessageStatusNotDelivered; - else - dest["status"] = static_cast(message->getState()); - - shared_ptr content = message->getFileTransferInformation(); - if (content) { - dest["fileSize"] = quint64(content->getFileSize()); - dest["fileName"] =Utils::coreStringToAppString(content->getName()); - if (state==linphone::ChatMessage::State::Displayed) - createThumbnail(message); - fillThumbnailProperty(dest, message); - dest["wasDownloaded"] = ::fileWasDownloaded(message); - } +static inline void fillMessageEntry (QVariantMap &dest, const shared_ptr &message) { + std::list> contents = message->getChatMessage()->getContents(); + QString txt; + foreach(auto content, contents){ + if(content->isText()) + txt += content->getStringBuffer().c_str(); + } + dest["content"] = txt; + dest["isOutgoing"] = message->getChatMessage()->isOutgoing() || message->getChatMessage()->getState() == linphone::ChatMessage::State::Idle; + + // Old workaround. + // It can exist messages with a not delivered status. It's a linphone core bug. + linphone::ChatMessage::State state = message->getChatMessage()->getState(); + if (state == linphone::ChatMessage::State::InProgress) + dest["status"] = ChatRoomModel::MessageStatusNotDelivered; + else + dest["status"] = static_cast(message->getChatMessage()->getState()); + + shared_ptr content = message->getChatMessage()->getFileTransferInformation(); + if (content) { + dest["fileSize"] = quint64(content->getFileSize()); + dest["fileName"] =Utils::coreStringToAppString(content->getName()); + if (state==linphone::ChatMessage::State::Displayed) + createThumbnail(message->getChatMessage()); + fillThumbnailProperty(dest, message->getChatMessage()); + dest["wasDownloaded"] = ::fileWasDownloaded(message->getChatMessage()); + } + dest["chatMessageModel"] = QVariant::fromValue(message.get()); } static inline void fillCallStartEntry (QVariantMap &dest, const shared_ptr &callLog) { - dest["type"] = ChatRoomModel::CallEntry; - dest["timestamp"] = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000); - dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; - dest["status"] = static_cast(callLog->getStatus()); - dest["isStart"] = true; + dest["type"] = ChatRoomModel::CallEntry; + dest["timestamp"] = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000); + dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; + dest["status"] = static_cast(callLog->getStatus()); + dest["isStart"] = true; } static inline void fillCallEndEntry (QVariantMap &dest, const shared_ptr &callLog) { - dest["type"] = ChatRoomModel::CallEntry; - dest["timestamp"] = QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000); - dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; - dest["status"] = static_cast(callLog->getStatus()); - dest["isStart"] = false; + dest["type"] = ChatRoomModel::CallEntry; + dest["timestamp"] = QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000); + dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; + dest["status"] = static_cast(callLog->getStatus()); + dest["isStart"] = false; +} + +static inline bool fillNoticeEntry (QVariantMap &dest, const shared_ptr &eventLog) { + bool handledEvent = true; + dest["type"] = ChatRoomModel::NoticeEntry; + dest["timestamp"] = QDateTime::fromMSecsSinceEpoch((eventLog->getCreationTime() ) * 1000); + auto participantAddress = eventLog->getParticipantAddress(); + + switch(eventLog->getType()){ + case linphone::EventLog::Type::ConferenceCreated: + dest["name"] = ""; + dest["message"] = "You have joined the group"; + dest["status"] = ChatRoomModel::NoticeMessage; + break; + case linphone::EventLog::Type::ConferenceTerminated: + dest["name"] = ""; + dest["status"] = ChatRoomModel::NoticeMessage; + dest["message"] = "You have left the group"; + break; + case linphone::EventLog::Type::ConferenceParticipantAdded: + dest["name"] = Utils::getDisplayName(participantAddress); + dest["status"] = ChatRoomModel::NoticeMessage; + dest["message"] = "%1 has joined"; + break; + case linphone::EventLog::Type::ConferenceParticipantRemoved: + dest["name"] = Utils::getDisplayName(participantAddress); + dest["status"] = ChatRoomModel::NoticeMessage; + dest["message"] = "%1 has left"; + break; + case linphone::EventLog::Type::ConferenceSecurityEvent: { + if(eventLog->getSecurityEventType() == linphone::EventLog::SecurityEventType::SecurityLevelDowngraded ){ + auto faultyParticipant = eventLog->getSecurityEventFaultyDeviceAddress(); + if(faultyParticipant) + dest["name"] = Utils::getDisplayName(faultyParticipant); + else if(participantAddress) + dest["name"] = Utils::getDisplayName(participantAddress); + dest["status"] = ChatRoomModel::NoticeError; + dest["message"] = "Security level degraded by %1"; + }else// No callback from SDK on upgraded security event yet + handledEvent = false; + } + break; + + default:{ + handledEvent = false; + qWarning() << "Unhandled Notice event : " << (int)eventLog->getType(); + } + } + dest["eventType"] = LinphoneEnums::fromLinphone(eventLog->getType()); + return handledEvent; } // ----------------------------------------------------------------------------- class ChatRoomModel::MessageHandlers : public linphone::ChatMessageListener { - friend class ChatRoomModel; - + friend class ChatRoomModel; + public: - MessageHandlers (ChatRoomModel *ChatRoomModel) : mChatRoomModel(ChatRoomModel) {} - + MessageHandlers (ChatRoomModel *ChatRoomModel) : mChatRoomModel(ChatRoomModel) {} + private: - QList::iterator findMessageEntry (const shared_ptr &message) { - return find_if(mChatRoomModel->mEntries.begin(), mChatRoomModel->mEntries.end(), [&message](const ChatEntryData &entry) { - return entry.second == message; - }); - } - - void signalDataChanged (const QList::iterator &it) { - int row = int(distance(mChatRoomModel->mEntries.begin(), it)); - emit mChatRoomModel->dataChanged(mChatRoomModel->index(row, 0), mChatRoomModel->index(row, 0)); - } - - shared_ptr onFileTransferSend ( - const shared_ptr &, - const shared_ptr &, - size_t, - size_t - ) override { - qWarning() << "`onFileTransferSend` called."; - return nullptr; - } - - void onFileTransferProgressIndication ( - const shared_ptr &message, - const shared_ptr &, - size_t offset, - size_t - ) override { - if (!mChatRoomModel) - return; - - auto it = findMessageEntry(message); - if (it == mChatRoomModel->mEntries.end()) - return; - - (*it).first["fileOffset"] = quint64(offset); - - signalDataChanged(it); - } - - void onMsgStateChanged (const shared_ptr &message, linphone::ChatMessage::State state) override { - if (!mChatRoomModel) - return; - - auto it = findMessageEntry(message); - if (it == mChatRoomModel->mEntries.end()) - return; - - // File message downloaded. - if (state == linphone::ChatMessage::State::FileTransferDone && !message->isOutgoing()) { - createThumbnail(message); - fillThumbnailProperty((*it).first, message); - (*it).first["wasDownloaded"] = true; - App::getInstance()->getNotifier()->notifyReceivedFileMessage(message); - } - - (*it).first["status"] = static_cast(state); - - signalDataChanged(it); - } - - ChatRoomModel *mChatRoomModel; + QList::iterator findMessageEntry (const shared_ptr &message) { + return find_if(mChatRoomModel->mEntries.begin(), mChatRoomModel->mEntries.end(), [&message](const ChatEntryData &entry) { + return entry.second == message; + }); + } + + void signalDataChanged (const QList::iterator &it) { + int row = int(distance(mChatRoomModel->mEntries.begin(), it)); + emit mChatRoomModel->dataChanged(mChatRoomModel->index(row, 0), mChatRoomModel->index(row, 0)); + } + + shared_ptr onFileTransferSend ( + const shared_ptr &, + const shared_ptr &, + size_t, + size_t + ) override { + qWarning() << "`onFileTransferSend` called."; + return nullptr; + } + + void onFileTransferProgressIndication ( + const shared_ptr &message, + const shared_ptr &, + size_t offset, + size_t + ) override { + if (!mChatRoomModel) + return; + + auto it = findMessageEntry(message); + if (it == mChatRoomModel->mEntries.end()) + return; + + (*it).first["fileOffset"] = quint64(offset); + + signalDataChanged(it); + } + + void onMsgStateChanged (const shared_ptr &message, linphone::ChatMessage::State state) override { + if (!mChatRoomModel) + return; + + auto it = findMessageEntry(message); + if (it == mChatRoomModel->mEntries.end()) + return; + + // File message downloaded. + if (state == linphone::ChatMessage::State::FileTransferDone && !message->isOutgoing()) { + createThumbnail(message); + fillThumbnailProperty((*it).first, message); + (*it).first["wasDownloaded"] = true; + App::getInstance()->getNotifier()->notifyReceivedFileMessage(message); + } + + (*it).first["status"] = static_cast(state); + + signalDataChanged(it); + } + + ChatRoomModel *mChatRoomModel; }; // ----------------------------------------------------------------------------- /* ChatRoomModel::ChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure) { CoreManager *coreManager = CoreManager::getInstance(); - + mCoreHandlers = coreManager->getHandlers(); mMessageHandlers = make_shared(this); - + shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr factory(linphone::Factory::get()); std::shared_ptr params = core->createDefaultChatRoomParams(); std::list> participants; - + mChatRoom = core->searchChatRoom(params, factory->createAddress(localAddress.toStdString()) , factory->createAddress(peerAddress.toStdString()) , participants); Q_ASSERT(mChatRoom); - + handleIsComposingChanged(mChatRoom); - + // Get messages. mEntries.clear(); - + QElapsedTimer timer; timer.start(); - + for (auto &message : mChatRoom->getHistory(0)) - mEntries << qMakePair( - QVariantMap{ - { "type", EntryType::MessageEntry }, - { "timestamp", QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000) } - }, - static_pointer_cast(message) - ); - + mEntries << qMakePair( + QVariantMap{ + { "type", EntryType::MessageEntry }, + { "timestamp", QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000) } + }, + static_pointer_cast(message) + ); + // Get calls. if(!getIsSecure() ) for (auto &callLog : core->getCallHistory(mChatRoom->getPeerAddress(), mChatRoom->getLocalAddress())) - insertCall(callLog); - + insertCall(callLog); + qInfo() << QStringLiteral("ChatRoomModel (%1, %2) loaded in %3 milliseconds.") - .arg(peerAddress).arg(localAddress).arg(timer.elapsed()); - + .arg(peerAddress).arg(localAddress).arg(timer.elapsed()); + // Rebind lost handlers for(auto i = mEntries.begin() ; i != mEntries.end() ; ++i){ - if(i->first["type"] == EntryType::MessageEntry){ - shared_ptr message = static_pointer_cast(i->second); - message->removeListener(mMessageHandlers);// Remove old listener if already exists - message->addListener(mMessageHandlers); - } + if(i->first["type"] == EntryType::MessageEntry){ + shared_ptr message = static_pointer_cast(i->second); + message->removeListener(mMessageHandlers);// Remove old listener if already exists + message->addListener(mMessageHandlers); + } } { - CoreHandlers *coreHandlers = mCoreHandlers.get(); - QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatRoomModel::handleMessageReceived); - QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatRoomModel::handleCallStateChanged); - QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatRoomModel::handleIsComposingChanged); + CoreHandlers *coreHandlers = mCoreHandlers.get(); + QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatRoomModel::handleMessageReceived); + QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatRoomModel::handleCallStateChanged); + QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatRoomModel::handleIsComposingChanged); } if(!mChatRoom) qWarning("TOTO A"); @@ -376,10 +432,9 @@ ChatRoomModel::ChatRoomModel (const QString &peerAddress, const QString &localAd ChatRoomModel::ChatRoomModel (std::shared_ptr chatRoom){ CoreManager *coreManager = CoreManager::getInstance(); - mCoreHandlers = coreManager->getHandlers(); mMessageHandlers = make_shared(this); - + shared_ptr core = CoreManager::getInstance()->getCore(); shared_ptr factory(linphone::Factory::get()); std::shared_ptr params = core->createDefaultChatRoomParams(); @@ -390,147 +445,181 @@ ChatRoomModel::ChatRoomModel (std::shared_ptr chatRoom){ Q_ASSERT(mChatRoom); - setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(chatRoom->getLastUpdateTime())); - setUnreadMessagesCount(chatRoom->getUnreadMessagesCount()); - mMissedCallsCount = 0; - + setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(mChatRoom->getLastUpdateTime())); + setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); + + qWarning() << "Creation ChatRoom with unreadmessages: " << mChatRoom->getUnreadMessagesCount(); + //handleIsComposingChanged(mChatRoom); - + // Get messages. mEntries.clear(); - + QElapsedTimer timer; timer.start(); - - for (auto &message : mChatRoom->getHistory(0)) - mEntries << qMakePair( - QVariantMap{ - { "type", EntryType::MessageEntry }, - { "timestamp", QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000) } - }, - static_pointer_cast(message) - ); - - // Get calls. - if(!getIsSecure() ) - for (auto &callLog : core->getCallHistory(mChatRoom->getPeerAddress(), mChatRoom->getLocalAddress())) - insertCall(callLog); - - - - // Rebind lost handlers + for (auto &message : mChatRoom->getHistory(0)) + mEntries << qMakePair( + QVariantMap{ + { "type", EntryType::MessageEntry }, + { "timestamp", QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000) } + }, + static_pointer_cast(std::make_shared(message)) + ); + + // Get calls. + if(!isSecure() ) + for (auto &callLog : core->getCallHistory(mChatRoom->getPeerAddress(), mChatRoom->getLocalAddress())) + insertCall(callLog); + + // Get events + for(auto &eventLog : mChatRoom->getHistoryEvents(0)){ + auto entry = qMakePair( + QVariantMap{ + { "type", EntryType::NoticeEntry }, + { "timestamp", QDateTime::fromMSecsSinceEpoch(eventLog->getCreationTime() * 1000) } + }, + static_pointer_cast(eventLog) + ); + if(fillNoticeEntry(entry.first, eventLog)) + mEntries << entry; + } + + + // Rebind lost handlers for(auto i = mEntries.begin() ; i != mEntries.end() ; ++i){ - if(i->first["type"] == EntryType::MessageEntry){ - shared_ptr message = static_pointer_cast(i->second); - message->removeListener(mMessageHandlers);// Remove old listener if already exists - message->addListener(mMessageHandlers); - } + if(i->first["type"] == EntryType::MessageEntry){ + shared_ptr message = static_pointer_cast(i->second); + message->getChatMessage()->removeListener(mMessageHandlers);// Remove old listener if already exists + message->getChatMessage()->addListener(mMessageHandlers); + } } { - CoreHandlers *coreHandlers = mCoreHandlers.get(); - //QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatRoomModel::handleMessageReceived); - QObject::connect(coreHandlers, &CoreHandlers::callCreated, this, &ChatRoomModel::handleCallCreated); - QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatRoomModel::handleCallStateChanged); - //QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatRoomModel::handleIsComposingChanged); + CoreHandlers *coreHandlers = mCoreHandlers.get(); + //QObject::connect(coreHandlers, &CoreHandlers::messageReceived, this, &ChatRoomModel::handleMessageReceived); + QObject::connect(coreHandlers, &CoreHandlers::callCreated, this, &ChatRoomModel::handleCallCreated); + QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &ChatRoomModel::handleCallStateChanged); + QObject::connect(coreHandlers, &CoreHandlers::presenceStatusReceived, this, &ChatRoomModel::handlePresenceStatusReceived); + //QObject::connect(coreHandlers, &CoreHandlers::isComposingChanged, this, &ChatRoomModel::handleIsComposingChanged); } if(mChatRoom){ + mParticipantListModel = std::make_shared(this);/* std::list> participants = mChatRoom->getParticipants(); for(auto it = participants.begin() ; it != participants.end() ; ++it){ mParticipants << new ParticipantModel(*it, this); - } - } + }*/ + }else + mParticipantListModel = nullptr; } ChatRoomModel::~ChatRoomModel () { - mMessageHandlers->mChatRoomModel = nullptr; + mMessageHandlers->mChatRoomModel = nullptr; + mParticipantListModel = nullptr; } QHash ChatRoomModel::roleNames () const { - QHash roles; - roles[Roles::ChatEntry] = "$chatEntry"; - roles[Roles::SectionDate] = "$sectionDate"; - return roles; + QHash roles; + roles[Roles::ChatEntry] = "$chatEntry"; + roles[Roles::SectionDate] = "$sectionDate"; + return roles; } int ChatRoomModel::rowCount (const QModelIndex &) const { - return mEntries.count(); + return mEntries.count(); } QVariant ChatRoomModel::data (const QModelIndex &index, int role) const { - int row = index.row(); - - if (!index.isValid() || row < 0 || row >= mEntries.count()) - return QVariant(); - - switch (role) { - case Roles::ChatEntry: { - auto &data = mEntries[row].first; - if (data.contains("type") && data["type"]==EntryType::MessageEntry && !data.contains("content")) - fillMessageEntry(data, static_pointer_cast(mEntries[row].second)); - return QVariant::fromValue(data); - } - case Roles::SectionDate: - return QVariant::fromValue(mEntries[row].first["timestamp"].toDate()); - } - - return QVariant(); + int row = index.row(); + + if (!index.isValid() || row < 0 || row >= mEntries.count()) + return QVariant(); + + switch (role) { + case Roles::ChatEntry: { + auto &data = mEntries[row].first; + if (data.contains("type")) { + if(data["type"]==EntryType::MessageEntry && !data.contains("content")) + fillMessageEntry(data, static_pointer_cast(mEntries[row].second)); + else if(data["type"]==EntryType::NoticeEntry){ + fillNoticeEntry(data, static_pointer_cast(mEntries[row].second)); + } + } + return QVariant::fromValue(data); + } + case Roles::SectionDate: + return QVariant::fromValue(mEntries[row].first["timestamp"].toDate()); + } + + return QVariant(); } bool ChatRoomModel::removeRow (int row, const QModelIndex &) { - return removeRows(row, 1); + return removeRows(row, 1); } bool ChatRoomModel::removeRows (int row, int count, const QModelIndex &parent) { - int limit = row + count - 1; - - if (row < 0 || count < 0 || limit >= mEntries.count()) - return false; - - beginRemoveRows(parent, row, limit); - - for (int i = 0; i < count; ++i) { - removeEntry(mEntries[row]); - mEntries.removeAt(row); - } - - endRemoveRows(); - - if (mEntries.count() == 0) - emit allEntriesRemoved(); - else if (limit == mEntries.count()) - emit lastEntryRemoved(); - emit focused();// Removing rows is like having focus. Don't wait asynchronous events. - return true; + int limit = row + count - 1; + + if (row < 0 || count < 0 || limit >= mEntries.count()) + return false; + + beginRemoveRows(parent, row, limit); + + for (int i = 0; i < count; ++i) { + removeEntry(mEntries[row]); + mEntries.removeAt(row); + } + + endRemoveRows(); + + if (mEntries.count() == 0) + emit allEntriesRemoved(); + else if (limit == mEntries.count()) + emit lastEntryRemoved(); + emit focused();// Removing rows is like having focus. Don't wait asynchronous events. + return true; } QString ChatRoomModel::getPeerAddress () const { - return Utils::coreStringToAppString( - mChatRoom->getPeerAddress()->asStringUriOnly() - ); + if(haveEncryption() || isGroupEnabled()){ + return getParticipants()->addressesToString(); + }else + return Utils::coreStringToAppString(mChatRoom->getPeerAddress()->asStringUriOnly()); } QString ChatRoomModel::getLocalAddress () const { - return Utils::coreStringToAppString( - mChatRoom->getLocalAddress()->asStringUriOnly() - ); + auto localAddress = mChatRoom->getLocalAddress()->clone(); + localAddress->clean(); + return Utils::coreStringToAppString( + localAddress->asStringUriOnly() + ); } + QString ChatRoomModel::getFullPeerAddress () const { - if(!mChatRoom) - qWarning("TOTO Z"); - return QString::fromStdString(mChatRoom->getPeerAddress()->asString()); + if(haveEncryption() || isGroupEnabled()){ + return getParticipants()->addressesToString(); + }else + return Utils::coreStringToAppString(mChatRoom->getPeerAddress()->asString()); } QString ChatRoomModel::getFullLocalAddress () const { - return QString::fromStdString(mChatRoom->getLocalAddress()->asString()); + return QString::fromStdString(mChatRoom->getLocalAddress()->asString()); +} + +QString ChatRoomModel::getConferenceAddress () const { + auto address = mChatRoom->getConferenceAddress(); + return address?QString::fromStdString(address->asString()):""; } QString ChatRoomModel::getSubject () const { - return QString::fromStdString(mChatRoom->getSubject()); + return QString::fromStdString(mChatRoom->getSubject()); } QString ChatRoomModel::getUsername () const { - std::string username = mChatRoom->getSubject(); + std::string username; + if( isGroupEnabled()) + username = mChatRoom->getSubject(); + if(username != ""){ return QString::fromStdString(username); } @@ -560,11 +649,13 @@ QString ChatRoomModel::getAvatar () const { } int ChatRoomModel::getPresenceStatus() const { - if( mChatRoom->getNbParticipants() == 1){ + if( mChatRoom->getNbParticipants() == 1 && !isGroupEnabled()){ auto participants = mChatRoom->getParticipants(); auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(QString::fromStdString((*participants.begin())->getAddress()->asString())); - if(contact) - return contact->getPresenceLevel(); + if(contact) { + int p = contact->getPresenceLevel(); + return p; + } else return 0; }else @@ -573,6 +664,29 @@ int ChatRoomModel::getPresenceStatus() const { //return mChatRoom->getConsolidatedPresence(); } +//std::shared_ptr ChatRoomModel::getParticipants(){ +ParticipantListModel* ChatRoomModel::getParticipants() const{ + return mParticipantListModel.get(); +} + +int ChatRoomModel::getState() const { + return (int)mChatRoom->getState(); +} + +bool ChatRoomModel::hasBeenLeft() const{ + return mChatRoom->hasBeenLeft(); +} + +bool ChatRoomModel::getEphemeralEnabled() const{ + return mChatRoom->ephemeralEnabled(); +} + +long ChatRoomModel::getEphemeralLifetime() const{ + return mChatRoom->getEphemeralLifetime(); +} + +//------------------------------------------------------------------------------------------------ + void ChatRoomModel::setLastUpdateTime(const QDateTime& lastUpdateDate) { if(mLastUpdateTime != lastUpdateDate ) { mLastUpdateTime = lastUpdateDate; @@ -594,10 +708,23 @@ void ChatRoomModel::setMissedCallsCount(const int& count){ } } +void ChatRoomModel::setEphemeralEnabled(bool enabled){ + if(getEphemeralEnabled() != enabled){ + mChatRoom->enableEphemeral(enabled); + emit ephemeralEnabledChanged(); + } +} + +void ChatRoomModel::setEphemeralLifetime(long lifetime){ + if(getEphemeralLifetime() != lifetime){ + mChatRoom->setEphemeralLifetime(lifetime); + emit ephemeralLifetimeChanged(); + } +} void ChatRoomModel::leaveChatRoom (){ mChatRoom->leave(); - mChatRoom->getCore()->deleteChatRoom(mChatRoom); + //mChatRoom->getCore()->deleteChatRoom(mChatRoom); } /* void ChatRoomModel::setSipAddresses (const QString &peerAddress, const QString &localAddress, const bool& isSecure) { @@ -605,398 +732,457 @@ void ChatRoomModel::setSipAddresses (const QString &peerAddress, const QString & shared_ptr factory(linphone::Factory::get()); std::shared_ptr params = core->createDefaultChatRoomParams(); std::list> participants; - + mChatRoom = core->searchChatRoom(params, factory->createAddress(localAddress.toStdString()) , factory->createAddress(peerAddress.toStdString()) , participants); Q_ASSERT(mChatRoom); - + handleIsComposingChanged(mChatRoom); - + // Get messages. mEntries.clear(); - + QElapsedTimer timer; timer.start(); - + for (auto &message : mChatRoom->getHistory(0)) - mEntries << qMakePair( - QVariantMap{ - { "type", EntryType::MessageEntry }, - { "timestamp", QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000) } - }, - static_pointer_cast(message) - ); - + mEntries << qMakePair( + QVariantMap{ + { "type", EntryType::MessageEntry }, + { "timestamp", QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000) } + }, + static_pointer_cast(message) + ); + // Get calls. if(!getIsSecure() ) for (auto &callLog : core->getCallHistory(mChatRoom->getPeerAddress(), mChatRoom->getLocalAddress())) - insertCall(callLog); - + insertCall(callLog); + qInfo() << QStringLiteral("ChatRoomModel (%1, %2) loaded in %3 milliseconds.") - .arg(peerAddress).arg(localAddress).arg(timer.elapsed()); - + .arg(peerAddress).arg(localAddress).arg(timer.elapsed()); + if(!mChatRoom) qWarning("TOTO C"); - + } */ -bool ChatRoomModel::getIsSecure() const{ +bool ChatRoomModel::haveEncryption() const{ + return mChatRoom->getCurrentParams()->getEncryptionBackend() != linphone::ChatRoomEncryptionBackend::None; +} + +bool ChatRoomModel::isSecure() const{ return mChatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Encrypted || mChatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Safe; } -bool ChatRoomModel::getIsRemoteComposing () const { - return mIsRemoteComposing; +int ChatRoomModel::getSecurityLevel() const{ + return (int)mChatRoom->getSecurityLevel() ; } +bool ChatRoomModel::isGroupEnabled() const{ + return mChatRoom->getCurrentParams()->groupEnabled(); +} + +bool ChatRoomModel::getIsRemoteComposing () const { + return mIsRemoteComposing; +} + +/* //QList ChatRoomModel::getParticipants() const{ QString ChatRoomModel::getParticipants() const{ QStringList participants; for(auto it = mParticipants.begin() ; it != mParticipants.end() ; ++it) participants << (*it)->getAddress(); - + return participants.join(","); } - +*/ // ----------------------------------------------------------------------------- void ChatRoomModel::removeEntry (int id) { - qInfo() << QStringLiteral("Removing chat entry: %1 of (%2, %3).") - .arg(id).arg(getPeerAddress()).arg(getLocalAddress()); - - if (!removeRow(id)) - qWarning() << QStringLiteral("Unable to remove chat entry: %1").arg(id); + qInfo() << QStringLiteral("Removing chat entry: %1 of (%2, %3).") + .arg(id).arg(getPeerAddress()).arg(getLocalAddress()); + + if (!removeRow(id)) + qWarning() << QStringLiteral("Unable to remove chat entry: %1").arg(id); } void ChatRoomModel::removeAllEntries () { - qInfo() << QStringLiteral("Removing all chat entries of: (%1, %2).") - .arg(getPeerAddress()).arg(getLocalAddress()); - - beginResetModel(); - - for (auto &entry : mEntries) - removeEntry(entry); - - mEntries.clear(); - - endResetModel(); - - emit allEntriesRemoved(); - emit focused();// Removing all entries is like having focus. Don't wait asynchronous events. + qInfo() << QStringLiteral("Removing all chat entries of: (%1, %2).") + .arg(getPeerAddress()).arg(getLocalAddress()); + + beginResetModel(); + + for (auto &entry : mEntries) + removeEntry(entry); + + mEntries.clear(); + + endResetModel(); + + emit allEntriesRemoved(); + emit focused();// Removing all entries is like having focus. Don't wait asynchronous events. } // ----------------------------------------------------------------------------- void ChatRoomModel::sendMessage (const QString &message) { - shared_ptr _message = mChatRoom->createMessageFromUtf8(""); - _message->getContents().begin()->get()->setUtf8Text(message.toUtf8().toStdString()); - _message->removeListener(mMessageHandlers);// Remove old listener if already exists - _message->addListener(mMessageHandlers); - - _message->send(); - - emit messageSent(_message); + shared_ptr _message = mChatRoom->createMessageFromUtf8(""); + _message->getContents().begin()->get()->setUtf8Text(message.toUtf8().toStdString()); + _message->removeListener(mMessageHandlers);// Remove old listener if already exists + _message->addListener(mMessageHandlers); + + _message->send(); + + emit messageSent(_message); } void ChatRoomModel::resendMessage (int id) { - if (id < 0 || id > mEntries.count()) { - qWarning() << QStringLiteral("Entry %1 not exists.").arg(id); - return; - } - - const ChatEntryData entry = mEntries[id]; - const QVariantMap map = entry.first; - - if (map["type"] != EntryType::MessageEntry) { - qWarning() << QStringLiteral("Unable to resend entry %1. It's not a message.").arg(id); - return; - } - - switch (map["status"].toInt()) { - case MessageStatusFileTransferError: - case MessageStatusNotDelivered: { - shared_ptr message = static_pointer_cast(entry.second); - message->removeListener(mMessageHandlers);// Remove old listener if already exists - message->addListener(mMessageHandlers); - message->send(); - - break; - } - - default: - qWarning() << QStringLiteral("Unable to resend message: %1. Bad state.").arg(id); - } + if (id < 0 || id > mEntries.count()) { + qWarning() << QStringLiteral("Entry %1 not exists.").arg(id); + return; + } + + const ChatEntryData entry = mEntries[id]; + const QVariantMap map = entry.first; + + if (map["type"] != EntryType::MessageEntry) { + qWarning() << QStringLiteral("Unable to resend entry %1. It's not a message.").arg(id); + return; + } + + switch (map["status"].toInt()) { + case MessageStatusFileTransferError: + case MessageStatusNotDelivered: { + shared_ptr message = static_pointer_cast(entry.second); + message->removeListener(mMessageHandlers);// Remove old listener if already exists + message->addListener(mMessageHandlers); + message->send(); + + break; + } + + default: + qWarning() << QStringLiteral("Unable to resend message: %1. Bad state.").arg(id); + } } void ChatRoomModel::sendFileMessage (const QString &path) { - QFile file(path); - if (!file.exists()) - return; - - qint64 fileSize = file.size(); - if (fileSize > FileSizeLimit) { - qWarning() << QStringLiteral("Unable to send file. (Size limit=%1)").arg(FileSizeLimit); - return; - } - - shared_ptr content = CoreManager::getInstance()->getCore()->createContent(); - { - QStringList mimeType = QMimeDatabase().mimeTypeForFile(path).name().split('/'); - if (mimeType.length() != 2) { - qWarning() << QStringLiteral("Unable to get supported mime type for: `%1`.").arg(path); - return; - } - content->setType(Utils::appStringToCoreString(mimeType[0])); - content->setSubtype(Utils::appStringToCoreString(mimeType[1])); - } - content->setSize(size_t(fileSize)); - content->setName(Utils::appStringToCoreString( QFileInfo(file).fileName())); - shared_ptr message = mChatRoom->createFileTransferMessage(content); - message->getContents().front()->setFilePath(Utils::appStringToCoreString(path)); - message->removeListener(mMessageHandlers);// Remove old listener if already exists - message->addListener(mMessageHandlers); - - createThumbnail(message); - - message->send(); - - emit messageSent(message); + QFile file(path); + if (!file.exists()) + return; + + qint64 fileSize = file.size(); + if (fileSize > FileSizeLimit) { + qWarning() << QStringLiteral("Unable to send file. (Size limit=%1)").arg(FileSizeLimit); + return; + } + + shared_ptr content = CoreManager::getInstance()->getCore()->createContent(); + { + QStringList mimeType = QMimeDatabase().mimeTypeForFile(path).name().split('/'); + if (mimeType.length() != 2) { + qWarning() << QStringLiteral("Unable to get supported mime type for: `%1`.").arg(path); + return; + } + content->setType(Utils::appStringToCoreString(mimeType[0])); + content->setSubtype(Utils::appStringToCoreString(mimeType[1])); + } + content->setSize(size_t(fileSize)); + content->setName(Utils::appStringToCoreString( QFileInfo(file).fileName())); + shared_ptr message = mChatRoom->createFileTransferMessage(content); + message->getContents().front()->setFilePath(Utils::appStringToCoreString(path)); + message->removeListener(mMessageHandlers);// Remove old listener if already exists + message->addListener(mMessageHandlers); + + createThumbnail(message); + + message->send(); + + emit messageSent(message); } // ----------------------------------------------------------------------------- void ChatRoomModel::downloadFile (int id) { - const ChatEntryData entry = getFileMessageEntry(id); - if (!entry.second) - return; - - shared_ptr message = static_pointer_cast(entry.second); - - switch (static_cast(message->getState())) { - case MessageStatusDelivered: - case MessageStatusDeliveredToUser: - case MessageStatusDisplayed: - case MessageStatusFileTransferDone: - break; - - default: - qWarning() << QStringLiteral("Unable to download file of entry %1. It was not uploaded.").arg(id); - return; - } - bool soFarSoGood; - const QString safeFilePath = Utils::getSafeFilePath( - QStringLiteral("%1%2") - .arg(CoreManager::getInstance()->getSettingsModel()->getDownloadFolder()) - .arg(entry.first["fileName"].toString()), - &soFarSoGood - ); - - if (!soFarSoGood) { - qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(id); - return; - } - message->removeListener(mMessageHandlers);// Remove old listener if already exists - message->addListener(mMessageHandlers); - - message->getContents().front()->setFilePath(Utils::appStringToCoreString(safeFilePath)); - - if( !message->isFileTransfer()){ - QMessageBox::warning(nullptr, "Download File", "This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it"); - }else - { - if (!message->downloadContent(message->getFileTransferInformation())) - qWarning() << QStringLiteral("Unable to download file of entry %1.").arg(id); - } + const ChatEntryData entry = getFileMessageEntry(id); + if (!entry.second) + return; + + shared_ptr message = static_pointer_cast(entry.second); + + switch (static_cast(message->getState())) { + case MessageStatusDelivered: + case MessageStatusDeliveredToUser: + case MessageStatusDisplayed: + case MessageStatusFileTransferDone: + break; + + default: + qWarning() << QStringLiteral("Unable to download file of entry %1. It was not uploaded.").arg(id); + return; + } + bool soFarSoGood; + const QString safeFilePath = Utils::getSafeFilePath( + QStringLiteral("%1%2") + .arg(CoreManager::getInstance()->getSettingsModel()->getDownloadFolder()) + .arg(entry.first["fileName"].toString()), + &soFarSoGood + ); + + if (!soFarSoGood) { + qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(id); + return; + } + message->removeListener(mMessageHandlers);// Remove old listener if already exists + message->addListener(mMessageHandlers); + + message->getContents().front()->setFilePath(Utils::appStringToCoreString(safeFilePath)); + + if( !message->isFileTransfer()){ + QMessageBox::warning(nullptr, "Download File", "This file was already downloaded and is no more on the server. Your peer have to resend it if you want to get it"); + }else + { + if (!message->downloadContent(message->getFileTransferInformation())) + qWarning() << QStringLiteral("Unable to download file of entry %1.").arg(id); + } } void ChatRoomModel::openFile (int id, bool showDirectory) { - const ChatEntryData entry = getFileMessageEntry(id); - if (!entry.second) - return; - - shared_ptr message = static_pointer_cast(entry.second); - if (!entry.first["wasDownloaded"].toBool()) { - downloadFile(id); - }else{ - QFileInfo info(getMessageAppData(message).m_path); - QDesktopServices::openUrl( - QUrl(QStringLiteral("file:///%1").arg(showDirectory ? info.absolutePath() : info.absoluteFilePath())) - ); - } + const ChatEntryData entry = getFileMessageEntry(id); + if (!entry.second) + return; + + shared_ptr message = static_pointer_cast(entry.second); + if (!entry.first["wasDownloaded"].toBool()) { + downloadFile(id); + }else{ + QFileInfo info(getMessageAppData(message).m_path); + QDesktopServices::openUrl( + QUrl(QStringLiteral("file:///%1").arg(showDirectory ? info.absolutePath() : info.absoluteFilePath())) + ); + } } bool ChatRoomModel::fileWasDownloaded (int id) { - const ChatEntryData entry = getFileMessageEntry(id); - return entry.second && ::fileWasDownloaded(static_pointer_cast(entry.second)); + const ChatEntryData entry = getFileMessageEntry(id); + return entry.second && ::fileWasDownloaded(static_pointer_cast(entry.second)); } void ChatRoomModel::compose () { - mChatRoom->compose(); + mChatRoom->compose(); } void ChatRoomModel::resetMessageCount () { - if (mChatRoom->getUnreadMessagesCount() > 0){ - mChatRoom->markAsRead();// Marking as read is only for messages. Not for calls. - setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); - } - mMissedCallsCount = 0; - emit messageCountReset(); + if (mChatRoom->getUnreadMessagesCount() > 0){ + mChatRoom->markAsRead();// Marking as read is only for messages. Not for calls. + setUnreadMessagesCount(mChatRoom->getUnreadMessagesCount()); + } + mMissedCallsCount = 0; + emit messageCountReset(); } + std::shared_ptr ChatRoomModel::getChatRoom(){ return mChatRoom; } // ----------------------------------------------------------------------------- const ChatRoomModel::ChatEntryData ChatRoomModel::getFileMessageEntry (int id) { - if (id < 0 || id > mEntries.count()) { - qWarning() << QStringLiteral("Entry %1 not exists.").arg(id); - return ChatEntryData(); - } - - const ChatEntryData entry = mEntries[id]; - if (entry.first["type"] != EntryType::MessageEntry) { - qWarning() << QStringLiteral("Unable to download entry %1. It's not a message.").arg(id); - return ChatEntryData(); - } - - shared_ptr message = static_pointer_cast(entry.second); - if (!message->getFileTransferInformation()) { - qWarning() << QStringLiteral("Entry %1 is not a file message.").arg(id); - return ChatEntryData(); - } - - return entry; + if (id < 0 || id > mEntries.count()) { + qWarning() << QStringLiteral("Entry %1 not exists.").arg(id); + return ChatEntryData(); + } + + const ChatEntryData entry = mEntries[id]; + if (entry.first["type"] != EntryType::MessageEntry) { + qWarning() << QStringLiteral("Unable to download entry %1. It's not a message.").arg(id); + return ChatEntryData(); + } + + shared_ptr message = static_pointer_cast(entry.second); + if (!message->getFileTransferInformation()) { + qWarning() << QStringLiteral("Entry %1 is not a file message.").arg(id); + return ChatEntryData(); + } + + return entry; } // ----------------------------------------------------------------------------- void ChatRoomModel::removeEntry (ChatEntryData &entry) { - int type = entry.first["type"].toInt(); - - switch (type) { - case ChatRoomModel::MessageEntry: { - shared_ptr message = static_pointer_cast(entry.second); - removeFileMessageThumbnail(message); - mChatRoom->deleteMessage(message); - break; - } - - case ChatRoomModel::CallEntry: { - if (entry.first["status"].toInt() == CallStatusSuccess) { - // WARNING: Unable to remove symmetric call here. (start/end) - // We are between `beginRemoveRows` and `endRemoveRows`. - // A solution is to schedule a `removeEntry` call in the Qt main loop. - shared_ptr linphonePtr = entry.second; - QTimer::singleShot(0, this, [this, linphonePtr]() { - auto it = find_if(mEntries.begin(), mEntries.end(), [linphonePtr](const ChatEntryData &entry) { - return entry.second == linphonePtr; - }); - - if (it != mEntries.end()) - removeEntry(int(distance(mEntries.begin(), it))); - }); - } - - CoreManager::getInstance()->getCore()->removeCallLog(static_pointer_cast(entry.second)); - break; - } - - default: - qWarning() << QStringLiteral("Unknown chat entry type: %1.").arg(type); - } + int type = entry.first["type"].toInt(); + + switch (type) { + case ChatRoomModel::MessageEntry: { + shared_ptr message = static_pointer_cast(entry.second); + removeFileMessageThumbnail(message); + mChatRoom->deleteMessage(message); + break; + } + + case ChatRoomModel::CallEntry: { + if (entry.first["status"].toInt() == CallStatusSuccess) { + // WARNING: Unable to remove symmetric call here. (start/end) + // We are between `beginRemoveRows` and `endRemoveRows`. + // A solution is to schedule a `removeEntry` call in the Qt main loop. + shared_ptr linphonePtr = entry.second; + QTimer::singleShot(0, this, [this, linphonePtr]() { + auto it = find_if(mEntries.begin(), mEntries.end(), [linphonePtr](const ChatEntryData &entry) { + return entry.second == linphonePtr; + }); + + if (it != mEntries.end()) + removeEntry(int(distance(mEntries.begin(), it))); + }); + } + + CoreManager::getInstance()->getCore()->removeCallLog(static_pointer_cast(entry.second)); + break; + } + + default: + qWarning() << QStringLiteral("Unknown chat entry type: %1.").arg(type); + } } void ChatRoomModel::insertCall (const shared_ptr &callLog) { - linphone::Call::Status status = callLog->getStatus(); - - auto insertEntry = [this]( - const ChatEntryData &entry, - const QList::iterator *start = nullptr - ) { - auto it = lower_bound(start ? *start : mEntries.begin(), mEntries.end(), entry, [](const ChatEntryData &a, const ChatEntryData &b) { - return a.first["timestamp"] < b.first["timestamp"]; - }); - - int row = int(distance(mEntries.begin(), it)); - - beginInsertRows(QModelIndex(), row, row); - it = mEntries.insert(it, entry); - endInsertRows(); - - return it; - }; - - // Add start call. - QVariantMap start; - fillCallStartEntry(start, callLog); - auto it = insertEntry(qMakePair(start, static_pointer_cast(callLog))); - - // Add end call. (if necessary) - if (status == linphone::Call::Status::Success) { - QVariantMap end; - fillCallEndEntry(end, callLog); - insertEntry(qMakePair(end, static_pointer_cast(callLog)), &it); - } + linphone::Call::Status status = callLog->getStatus(); + + auto insertEntry = [this]( + const ChatEntryData &entry, + const QList::iterator *start = nullptr + ) { + auto it = lower_bound(start ? *start : mEntries.begin(), mEntries.end(), entry, [](const ChatEntryData &a, const ChatEntryData &b) { + return a.first["timestamp"] < b.first["timestamp"]; + }); + + int row = int(distance(mEntries.begin(), it)); + + beginInsertRows(QModelIndex(), row, row); + it = mEntries.insert(it, entry); + endInsertRows(); + + return it; + }; + + // Add start call. + QVariantMap start; + fillCallStartEntry(start, callLog); + auto it = insertEntry(qMakePair(start, static_pointer_cast(callLog))); + + // Add end call. (if necessary) + if (status == linphone::Call::Status::Success) { + QVariantMap end; + fillCallEndEntry(end, callLog); + insertEntry(qMakePair(end, static_pointer_cast(callLog)), &it); + } } void ChatRoomModel::insertMessageAtEnd (const shared_ptr &message) { - int row = mEntries.count(); - - beginInsertRows(QModelIndex(), row, row); - - QVariantMap map{ - { "type", EntryType::MessageEntry }, - { "timestamp", QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000) } - }; - fillMessageEntry(map, message); - mEntries << qMakePair(map, static_pointer_cast(message)); - - endInsertRows(); + std::shared_ptr model = std::make_shared(message); + int row = mEntries.count(); + + beginInsertRows(QModelIndex(), row, row); + + QVariantMap map{ + { "type", EntryType::MessageEntry }, + { "timestamp", QDateTime::fromMSecsSinceEpoch(message->getTime() * 1000) } + }; + fillMessageEntry(map, model); + mEntries << qMakePair(map, static_pointer_cast(model)); + + endInsertRows(); } +void ChatRoomModel::insertNotice (const std::shared_ptr &enventLog) { + int row = mEntries.count(); + + beginInsertRows(QModelIndex(), row, row); + + QVariantMap map{ + { "type", EntryType::NoticeEntry }, + { "timestamp", QDateTime::fromMSecsSinceEpoch(enventLog->getCreationTime() * 1000) } + }; + if(fillNoticeEntry(map, enventLog)) + mEntries << qMakePair(map, static_pointer_cast(enventLog)); + + endInsertRows(); +} // ----------------------------------------------------------------------------- void ChatRoomModel::handleCallStateChanged (const shared_ptr &call, linphone::Call::State state) { - if (state == linphone::Call::State::End || state == linphone::Call::State::Error){ - shared_ptr core = CoreManager::getInstance()->getCore(); - std::shared_ptr params = core->createDefaultChatRoomParams(); - std::list> participants; + if (state == linphone::Call::State::End || state == linphone::Call::State::Error){ + shared_ptr core = CoreManager::getInstance()->getCore(); + std::shared_ptr params = core->createDefaultChatRoomParams(); + std::list> participants; - auto chatRoom = core->searchChatRoom(params, mChatRoom->getLocalAddress() - , call->getRemoteAddress() - , participants); - if( mChatRoom == chatRoom){ - insertCall(call->getCallLog()); - setMissedCallsCount(mMissedCallsCount+1); - } - //mChatRoom == CoreManager::getInstance()->getCore()->findChatRoom(call->getRemoteAddress(), mChatRoom->getLocalAddress()) - } + auto chatRoom = core->searchChatRoom(params, mChatRoom->getLocalAddress() + , call->getRemoteAddress() + , participants); + if( mChatRoom == chatRoom){ + insertCall(call->getCallLog()); + setMissedCallsCount(mMissedCallsCount+1); + } + //mChatRoom == CoreManager::getInstance()->getCore()->findChatRoom(call->getRemoteAddress(), mChatRoom->getLocalAddress()) + } } void ChatRoomModel::handleCallCreated(const shared_ptr &call){ } + +void ChatRoomModel::handlePresenceStatusReceived(std::shared_ptr contact){ + + bool canUpdatePresence = false; + auto contactAddresses = contact->getAddresses(); + for( auto itContactAddress = contactAddresses.begin() ; !canUpdatePresence && itContactAddress != contactAddresses.end() ; ++itContactAddress){ + //auto cleanContactAddress = (*itContactAddress)->clone(); + //cleanContactAddress->clean(); + canUpdatePresence = mChatRoom->getLocalAddress()->weakEqual(*itContactAddress); + if(!canUpdatePresence && !isGroupEnabled() && mChatRoom->getNbParticipants() == 1){ + auto participants = mChatRoom->getParticipants(); + auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(QString::fromStdString((*participants.begin())->getAddress()->asString())); + auto friendsAddresses = contact->getVcardModel()->getSipAddresses(); + for(auto friendAddress = friendsAddresses.begin() ; !canUpdatePresence && friendAddress != friendsAddresses.end() ; ++friendAddress){ + shared_ptr lAddress = CoreManager::getInstance()->getCore()->interpretUrl( + Utils::appStringToCoreString(friendAddress->toString()) + ); + canUpdatePresence = lAddress->weakEqual(*itContactAddress); + } + } + } + if(canUpdatePresence) { + //emit presenceStatusChanged((int)contact->getPresenceModel()->getConsolidatedPresence()); + emit presenceStatusChanged(); + } + +} + //---------------------------------------------------------- //------ CHAT ROOM HANDLERS //---------------------------------------------------------- void ChatRoomModel::onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing){ if (isComposing != mIsRemoteComposing) { - mIsRemoteComposing = isComposing; - emit isRemoteComposingChanged(mIsRemoteComposing); - } + mIsRemoteComposing = isComposing; + emit isRemoteComposingChanged(mIsRemoteComposing); + } } void ChatRoomModel::onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){ setUnreadMessagesCount(chatRoom->getUnreadMessagesCount()); /* insertMessageAtEnd(message); - emit messageReceived(message);*/ + emit messageReceived(message);*/ } void ChatRoomModel::onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ qWarning() << "New Event" <<(int) eventLog->getType(); if( eventLog->getType() == linphone::EventLog::Type::ConferenceCallEnd ){ setMissedCallsCount(mMissedCallsCount+1); + }else if( eventLog->getType() == linphone::EventLog::Type::ConferenceCreated ){ + emit fullPeerAddressChanged(); } /*auto message = eventLog->getChatMessage(); if(message){ @@ -1017,7 +1203,7 @@ void ChatRoomModel::onChatMessageSending(const std::shared_ptrgetChatMessage(); if(message){ insertMessageAtEnd(message); - emit messageReceived(message); + emit messageReceived(message); setLastUpdateTime(QDateTime::fromMSecsSinceEpoch(chatRoom->getLastUpdateTime())); } } @@ -1025,28 +1211,88 @@ void ChatRoomModel::onChatMessageSent(const std::shared_ptr /*auto message = eventLog->getChatMessage(); if(message){ insertMessageAtEnd(message); - emit messageReceived(message); + emit messageReceived(message); }*/ } -void ChatRoomModel::onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState){} -void ChatRoomModel::onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void ChatRoomModel::onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + auto events = chatRoom->getHistoryEvents(0); + auto e = std::find(events.begin(), events.end(), eventLog); + if( e != events.end() ) + insertNotice(*e); + emit participantAdded(chatRoom, eventLog); + emit fullPeerAddressChanged(); +} +void ChatRoomModel::onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + auto events = chatRoom->getHistoryEvents(0); + auto e = std::find(events.begin(), events.end(), eventLog); + if( e != events.end() ) + insertNotice(*e); + emit participantRemoved(chatRoom, eventLog); + emit fullPeerAddressChanged(); +} +void ChatRoomModel::onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + emit participantAdminStatusChanged(chatRoom, eventLog); +} +void ChatRoomModel::onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState){ + emit stateChanged(getState()); +} +void ChatRoomModel::onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + auto events = chatRoom->getHistoryEvents(0); + auto e = std::find(events.begin(), events.end(), eventLog); + if( e != events.end() ) + insertNotice(*e); + emit securityLevelChanged((int)chatRoom->getSecurityLevel()); + //emit securityEvent(chatRoom, eventLog); +} void ChatRoomModel::onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { emit subjectChanged(getSubject()); emit usernameChanged(getUsername()); } void ChatRoomModel::onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} -void ChatRoomModel::onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void ChatRoomModel::onConferenceAddressGeneration(const std::shared_ptr & chatRoom){} -void ChatRoomModel::onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){} -void ChatRoomModel::onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){} -void ChatRoomModel::onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} +void ChatRoomModel::onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + emit participantDeviceAdded(chatRoom, eventLog); +} +void ChatRoomModel::onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + emit participantDeviceRemoved(chatRoom, eventLog); +} +void ChatRoomModel::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + qWarning() << "onConferenceJoined"; + auto events = chatRoom->getHistoryEvents(0); + auto e = std::find(events.begin(), events.end(), eventLog); + if(e != events.end() ) + insertNotice(*e); + emit hasBeenLeftChanged(); +} +void ChatRoomModel::onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + qWarning() << "onConferenceLeft"; + auto events = chatRoom->getHistoryEvents(0); + auto e = std::find(events.begin(), events.end(), eventLog); + if( e != events.end()) + insertNotice(*e); + emit conferenceLeft(chatRoom, eventLog); + emit hasBeenLeftChanged(); + if(mChatRoom->isEmpty()) + mChatRoom->getCore()->deleteChatRoom(mChatRoom); +} +void ChatRoomModel::onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + qWarning() << "onEphemeralEvent"; +} +void ChatRoomModel::onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + qWarning() << "onEphemeralMessageTimerStarted"; +} +void ChatRoomModel::onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + qWarning() << "onEphemeralMessageDeleted"; +} +void ChatRoomModel::onConferenceAddressGeneration(const std::shared_ptr & chatRoom){ + qWarning() << "onConferenceAddressGeneration"; +} +void ChatRoomModel::onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){ + emit participantRegistrationSubscriptionRequested(chatRoom, participantAddress); +} +void ChatRoomModel::onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){ + emit participantRegistrationUnsubscriptionRequested(chatRoom, participantAddress); +} +void ChatRoomModel::onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message){ + qWarning() << "onChatMessageShouldBeStored"; +} void ChatRoomModel::onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state){} diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.hpp b/linphone-app/src/components/chat-room/ChatRoomModel.hpp index 565e142a5..601569cb3 100644 --- a/linphone-app/src/components/chat-room/ChatRoomModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomModel.hpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop @@ -31,210 +31,271 @@ class CoreHandlers; class ParticipantModel; +class ParticipantListModel; class ChatRoomModel : public QAbstractListModel, public linphone::ChatRoomListener { - class MessageHandlers; - - Q_OBJECT; - + class MessageHandlers; + + Q_OBJECT + public: - enum Roles { - ChatEntry = Qt::DisplayRole, - SectionDate - }; - - enum EntryType { - GenericEntry, - MessageEntry, - CallEntry - }; - Q_ENUM(EntryType); - - enum CallStatus { - CallStatusDeclined = int(linphone::Call::Status::Declined), - CallStatusMissed = int(linphone::Call::Status::Missed), - CallStatusSuccess = int(linphone::Call::Status::Success), - CallStatusAborted = int(linphone::Call::Status::Aborted), - CallStatusEarlyAborted = int(linphone::Call::Status::EarlyAborted), - CallStatusAcceptedElsewhere = int(linphone::Call::Status::AcceptedElsewhere), - CallStatusDeclinedElsewhere = int(linphone::Call::Status::DeclinedElsewhere) - }; - Q_ENUM(CallStatus); - - enum MessageStatus { - MessageStatusDelivered = int(linphone::ChatMessage::State::Delivered), - MessageStatusDeliveredToUser = int(linphone::ChatMessage::State::DeliveredToUser), - MessageStatusDisplayed = int(linphone::ChatMessage::State::Displayed), - MessageStatusFileTransferDone = int(linphone::ChatMessage::State::FileTransferDone), - MessageStatusFileTransferError = int(linphone::ChatMessage::State::FileTransferError), - MessageStatusFileTransferInProgress = int(linphone::ChatMessage::State::FileTransferInProgress), - MessageStatusIdle = int(linphone::ChatMessage::State::Idle), - MessageStatusInProgress = int(linphone::ChatMessage::State::InProgress), - MessageStatusNotDelivered = int(linphone::ChatMessage::State::NotDelivered) - - }; - Q_ENUM(MessageStatus); - - Q_PROPERTY(QString participants READ getParticipants NOTIFY participantsChanged); - Q_PROPERTY(QString subject READ getSubject NOTIFY subjectChanged) - Q_PROPERTY(QDateTime lastUpdateTime MEMBER mLastUpdateTime WRITE setLastUpdateTime NOTIFY lastUpdateTimeChanged) - Q_PROPERTY(int unreadMessagesCount MEMBER mUnreadMessagesCount WRITE setUnreadMessagesCount NOTIFY unreadMessagesCountChanged) - Q_PROPERTY(int missedCallsCount MEMBER mMissedCallsCount WRITE setMissedCallsCount NOTIFY missedCallsCountChanged) - - Q_PROPERTY(bool isComposing MEMBER mIsRemoteComposing NOTIFY isRemoteComposingChanged) - - - - Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) - Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) - Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged) - Q_PROPERTY(int presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged) - - - - //ChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure); - ChatRoomModel (std::shared_ptr chatRoom); - ~ChatRoomModel (); - - int rowCount (const QModelIndex &index = QModelIndex()) const override; - - QHash roleNames () const override; - QVariant data (const QModelIndex &index, int role) const override; - - bool removeRow (int row, const QModelIndex &parent = QModelIndex()); - bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; - - Q_INVOKABLE QString getPeerAddress () const; - Q_INVOKABLE QString getLocalAddress () const; - Q_INVOKABLE QString getFullPeerAddress () const; - Q_INVOKABLE QString getFullLocalAddress () const; - - QString getSubject () const; - QString getUsername () const; - QString getAvatar () const; - int getPresenceStatus() const; - void setLastUpdateTime(const QDateTime& lastUpdateDate); - - void setUnreadMessagesCount(const int& count); - void setMissedCallsCount(const int& count); - - Q_INVOKABLE void leaveChatRoom (); - - bool getIsSecure() const; - - bool getIsRemoteComposing () const; - - - //Q_INVOKABLE QList getParticipants()const - Q_INVOKABLE QString getParticipants()const; - - - void removeEntry (int id); - void removeAllEntries (); - - void sendMessage (const QString &message); - - void resendMessage (int id); - - void sendFileMessage (const QString &path); - - void downloadFile (int id); - void openFile (int id, bool showDirectory = false); - void openFileDirectory (int id) { - openFile(id, true); - } - - bool fileWasDownloaded (int id); - - void compose (); - - void resetMessageCount (); - - std::shared_ptr getChatRoom(); - QDateTime mLastUpdateTime; - int mUnreadMessagesCount; - int mMissedCallsCount; - - -//-------------------- CHAT ROOM HANDLER - - - virtual void onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing) override; - virtual void onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; - virtual void onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState) override; - virtual void onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; - virtual void onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; - virtual void onConferenceAddressGeneration(const std::shared_ptr & chatRoom) override; - virtual void onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; - virtual void onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; - virtual void onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; - virtual void onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state) override; - - + enum Roles { + ChatEntry = Qt::DisplayRole, + SectionDate + }; + + enum EntryType { + GenericEntry, + MessageEntry, + CallEntry, + NoticeEntry + }; + Q_ENUM(EntryType); + + enum NoticeType { + NoticeMessage, + NoticeError + }; + Q_ENUM(NoticeType); + + + enum CallStatus { + CallStatusDeclined = int(linphone::Call::Status::Declined), + CallStatusMissed = int(linphone::Call::Status::Missed), + CallStatusSuccess = int(linphone::Call::Status::Success), + CallStatusAborted = int(linphone::Call::Status::Aborted), + CallStatusEarlyAborted = int(linphone::Call::Status::EarlyAborted), + CallStatusAcceptedElsewhere = int(linphone::Call::Status::AcceptedElsewhere), + CallStatusDeclinedElsewhere = int(linphone::Call::Status::DeclinedElsewhere) + }; + Q_ENUM(CallStatus); + + enum MessageStatus { + MessageStatusDelivered = int(linphone::ChatMessage::State::Delivered), + MessageStatusDeliveredToUser = int(linphone::ChatMessage::State::DeliveredToUser), + MessageStatusDisplayed = int(linphone::ChatMessage::State::Displayed), + MessageStatusFileTransferDone = int(linphone::ChatMessage::State::FileTransferDone), + MessageStatusFileTransferError = int(linphone::ChatMessage::State::FileTransferError), + MessageStatusFileTransferInProgress = int(linphone::ChatMessage::State::FileTransferInProgress), + MessageStatusIdle = int(linphone::ChatMessage::State::Idle), + MessageStatusInProgress = int(linphone::ChatMessage::State::InProgress), + MessageStatusNotDelivered = int(linphone::ChatMessage::State::NotDelivered) + + }; + Q_ENUM(MessageStatus); + + //Q_PROPERTY(QString participants READ getParticipants NOTIFY participantsChanged); + //Q_PROPERTY(ParticipantProxyModel participants READ getParticipants NOTIFY participantsChanged); + Q_PROPERTY(QString subject READ getSubject NOTIFY subjectChanged) + Q_PROPERTY(QDateTime lastUpdateTime MEMBER mLastUpdateTime WRITE setLastUpdateTime NOTIFY lastUpdateTimeChanged) + Q_PROPERTY(int unreadMessagesCount MEMBER mUnreadMessagesCount WRITE setUnreadMessagesCount NOTIFY unreadMessagesCountChanged) + Q_PROPERTY(int missedCallsCount MEMBER mMissedCallsCount WRITE setMissedCallsCount NOTIFY missedCallsCountChanged) + + Q_PROPERTY(int securityLevel READ getSecurityLevel NOTIFY securityLevelChanged) + Q_PROPERTY(bool groupEnabled READ isGroupEnabled NOTIFY groupEnabledChanged) + Q_PROPERTY(bool haveEncryption READ haveEncryption CONSTANT) + + Q_PROPERTY(bool isComposing MEMBER mIsRemoteComposing NOTIFY isRemoteComposingChanged) + Q_PROPERTY(bool hasBeenLeft READ hasBeenLeft NOTIFY hasBeenLeftChanged) + + + + Q_PROPERTY(QString sipAddress READ getFullPeerAddress NOTIFY fullPeerAddressChanged) + Q_PROPERTY(QString sipAddressUriOnly READ getPeerAddress NOTIFY fullPeerAddressChanged) + Q_PROPERTY(QString username READ getUsername NOTIFY usernameChanged) + Q_PROPERTY(QString avatar READ getAvatar NOTIFY avatarChanged) + Q_PROPERTY(int presenceStatus READ getPresenceStatus NOTIFY presenceStatusChanged) + Q_PROPERTY(int state READ getState NOTIFY stateChanged) + + Q_PROPERTY(long ephemeralLifetime READ getEphemeralLifetime WRITE setEphemeralLifetime NOTIFY ephemeralLifetimeChanged) + Q_PROPERTY(bool ephemeralEnabled READ getEphemeralEnabled WRITE setEphemeralEnabled NOTIFY ephemeralEnabledChanged) + + + + //ChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool& isSecure); + ChatRoomModel (std::shared_ptr chatRoom); + ~ChatRoomModel (); + + int rowCount (const QModelIndex &index = QModelIndex()) const override; + + QHash roleNames () const override; + QVariant data (const QModelIndex &index, int role) const override; + + bool removeRow (int row, const QModelIndex &parent = QModelIndex()); + bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; + + Q_INVOKABLE QString getPeerAddress () const; + Q_INVOKABLE QString getLocalAddress () const; + Q_INVOKABLE QString getFullPeerAddress () const; + Q_INVOKABLE QString getFullLocalAddress () const; + Q_INVOKABLE QString getConferenceAddress () const; + + QString getSubject () const; + QString getUsername () const; + QString getAvatar () const; + int getPresenceStatus() const; + int getState() const; + bool hasBeenLeft() const; + bool getEphemeralEnabled() const; + long getEphemeralLifetime() const; + + + void setLastUpdateTime(const QDateTime& lastUpdateDate); + + void setUnreadMessagesCount(const int& count); + void setMissedCallsCount(const int& count); + void setEphemeralEnabled(bool enabled); + void setEphemeralLifetime(long lifetime); + + + Q_INVOKABLE void leaveChatRoom (); + + Q_INVOKABLE bool haveEncryption() const; + Q_INVOKABLE bool isSecure() const; + int getSecurityLevel() const; + bool isGroupEnabled() const; + + bool getIsRemoteComposing () const; + + + //Q_INVOKABLE QList getParticipants()const + //Q_INVOKABLE QString getParticipants()const; + //QList > getParticipants(); + //Q_INVOKABLE std::shared_ptr getParticipants(); + Q_PROPERTY(ParticipantListModel* participants READ getParticipants CONSTANT) + + ParticipantListModel* getParticipants() const; + + + void removeEntry (int id); + void removeAllEntries (); + + void sendMessage (const QString &message); + + void resendMessage (int id); + + void sendFileMessage (const QString &path); + + void downloadFile (int id); + void openFile (int id, bool showDirectory = false); + void openFileDirectory (int id) { + openFile(id, true); + } + + bool fileWasDownloaded (int id); + + void compose (); + + void resetMessageCount (); + + std::shared_ptr getChatRoom(); + QDateTime mLastUpdateTime; + int mUnreadMessagesCount = 0; + int mMissedCallsCount = 0; + + + //-------------------- CHAT ROOM HANDLER + + + virtual void onIsComposingReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & remoteAddress, bool isComposing) override; + virtual void onMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; + virtual void onNewEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onChatMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onChatMessageSending(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onChatMessageSent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onStateChanged(const std::shared_ptr & chatRoom, linphone::ChatRoom::State newState) override; + virtual void onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onSubjectChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; + virtual void onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) override; + virtual void onConferenceAddressGeneration(const std::shared_ptr & chatRoom) override; + virtual void onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; + virtual void onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress) override; + virtual void onChatMessageShouldBeStored(const std::shared_ptr & chatRoom, const std::shared_ptr & message) override; + virtual void onChatMessageParticipantImdnStateChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & state) override; + + signals: - bool isRemoteComposingChanged (bool status); - - void allEntriesRemoved (); - void lastEntryRemoved (); - - void messageSent (const std::shared_ptr &message); - void messageReceived (const std::shared_ptr &message); - - void messageCountReset (); - - void focused (); - - void fullPeerAddressChanged(); - void participantsChanged(); - void subjectChanged(QString subject); - void usernameChanged(QString username); - void avatarChanged(QString avatar); - void presenceStatusChanged(int presenceStatus); - void lastUpdateTimeChanged(); - void unreadMessagesCountChanged(); - void missedCallsCountChanged(); - + bool isRemoteComposingChanged (bool status); + + void allEntriesRemoved (); + void lastEntryRemoved (); + + void messageSent (const std::shared_ptr &message); + void messageReceived (const std::shared_ptr &message); + + void messageCountReset (); + + void focused (); + + void fullPeerAddressChanged(); + void participantsChanged(); + void subjectChanged(QString subject); + void usernameChanged(QString username); + void avatarChanged(QString avatar); + void presenceStatusChanged(); + void lastUpdateTimeChanged(); + void unreadMessagesCountChanged(); + void missedCallsCountChanged(); + + void securityLevelChanged(int securityLevel); + void groupEnabledChanged(bool groupEnabled); + void stateChanged(int state); + void hasBeenLeftChanged(); + void ephemeralEnabledChanged(); + void ephemeralLifetimeChanged(); + + +// Chat Room listener callbacks + + void securityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void participantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void participantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void participantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void participantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void participantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void participantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); + void participantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); + void conferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + private: - typedef QPair> ChatEntryData; - - //void setSipAddresses (const QString &peerAddress, const QString &localAddress, const bool& isSecure); - - const ChatEntryData getFileMessageEntry (int id); - - void removeEntry (ChatEntryData &entry); - - void insertCall (const std::shared_ptr &callLog); - void insertMessageAtEnd (const std::shared_ptr &message); - - void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); - void handleCallCreated(const std::shared_ptr &call);// Count an event call - //void handleIsComposingChanged (const std::shared_ptr &chatRoom); - //void handleMessageReceived (const std::shared_ptr &message); - - bool mIsRemoteComposing = false; - - mutable QList mEntries; - QList mParticipants; - - std::shared_ptr mCoreHandlers; - std::shared_ptr mMessageHandlers; - - std::shared_ptr mChatRoom; + typedef QPair> ChatEntryData; + + //void setSipAddresses (const QString &peerAddress, const QString &localAddress, const bool& isSecure); + + const ChatEntryData getFileMessageEntry (int id); + + void removeEntry (ChatEntryData &entry); + + void insertCall (const std::shared_ptr &callLog); + void insertMessageAtEnd (const std::shared_ptr &message); + void insertNotice (const std::shared_ptr &enventLog); + + void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); + void handleCallCreated(const std::shared_ptr &call);// Count an event call + void handlePresenceStatusReceived(std::shared_ptr contact); + //void handleIsComposingChanged (const std::shared_ptr &chatRoom); + //void handleMessageReceived (const std::shared_ptr &message); + + bool mIsRemoteComposing = false; + + mutable QList mEntries; + //QList mParticipants; + std::shared_ptr mParticipantListModel; + + std::shared_ptr mCoreHandlers; + std::shared_ptr mMessageHandlers; + + std::shared_ptr mChatRoom; }; Q_DECLARE_METATYPE(std::shared_ptr); diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp index 0eadff228..2a8244a2d 100644 --- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp @@ -64,7 +64,7 @@ private: ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { setSourceModel(new ChatRoomModelFilter(this)); - mIsSecure = false; + //mIsSecure = false; App *app = App::getInstance(); QObject::connect(app->getMainWindow(), &QWindow::activeChanged, this, [this]() { @@ -76,6 +76,7 @@ ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel QObject::connect(callsWindow, &QWindow::activeChanged, this, [this, callsWindow]() { handleIsActiveChanged(callsWindow); }); + sort(0); } // ----------------------------------------------------------------------------- @@ -160,53 +161,63 @@ void ChatRoomProxyModel::setEntryTypeFilter (ChatRoomModel::EntryType type) { bool ChatRoomProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &) const { return sourceModel()->rowCount() - sourceRow <= mMaxDisplayedEntries; } - +bool ChatRoomProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { + const QVariantMap l = sourceModel()->data(left).value(); + const QVariantMap r = sourceModel()->data(right).value(); + + return l["timestamp"].toDateTime() < r["timestamp"].toDateTime(); +} // ----------------------------------------------------------------------------- QString ChatRoomProxyModel::getPeerAddress () const { - return mChatRoomModel ? mChatRoomModel->getPeerAddress() : QString(""); + return mChatRoomModel ? mChatRoomModel->getPeerAddress() : mPeerAddress;//QString(""); } void ChatRoomProxyModel::setPeerAddress (const QString &peerAddress) { mPeerAddress = peerAddress; + emit peerAddressChanged(mPeerAddress); //reload(); } QString ChatRoomProxyModel::getLocalAddress () const { - return mChatRoomModel ? mChatRoomModel->getLocalAddress() : QString(""); + return mChatRoomModel ? mChatRoomModel->getLocalAddress() : mLocalAddress;//QString(""); } void ChatRoomProxyModel::setLocalAddress (const QString &localAddress) { mLocalAddress = localAddress; + emit localAddressChanged(mLocalAddress); //reload(); } QString ChatRoomProxyModel::getFullPeerAddress () const { - return mChatRoomModel ? mChatRoomModel->getFullPeerAddress() : QString(""); + return mChatRoomModel ? mChatRoomModel->getFullPeerAddress() : mFullPeerAddress;//QString(""); } void ChatRoomProxyModel::setFullPeerAddress (const QString &peerAddress) { mFullPeerAddress = peerAddress; + emit fullPeerAddressChanged(mFullPeerAddress); //reload(); } QString ChatRoomProxyModel::getFullLocalAddress () const { - return mChatRoomModel ? mChatRoomModel->getFullLocalAddress() : QString(""); + return mChatRoomModel ? mChatRoomModel->getFullLocalAddress() : mFullLocalAddress;//QString(""); } void ChatRoomProxyModel::setFullLocalAddress (const QString &localAddress) { mFullLocalAddress = localAddress; + emit fullLocalAddressChanged(mFullLocalAddress); //reload(); } - -int ChatRoomProxyModel::getIsSecure () const { - return mChatRoomModel ? mChatRoomModel->getIsSecure() : -1; +/* +bool ChatRoomProxyModel::isSecure () const { + return mChatRoomModel ? mChatRoomModel->isSecure() : false; } void ChatRoomProxyModel::setIsSecure (const int &secure) { mIsSecure = secure; + emit isSecureChanged(mIsSecure); } - +*/ bool ChatRoomProxyModel::getIsRemoteComposing () const { return mChatRoomModel ? mChatRoomModel->getIsRemoteComposing() : false; } @@ -252,8 +263,11 @@ ChatRoomModel *ChatRoomProxyModel::getChatRoomModel () const{ return mChatRoomModel.get(); } -void ChatRoomProxyModel::setChatRoomModel (ChatRoomModel *ChatRoomModel){ - mChatRoom = ChatRoomModel->getChatRoom(); +void ChatRoomProxyModel::setChatRoomModel (ChatRoomModel *chatRoomModel){ + if(chatRoomModel) + mChatRoom = chatRoomModel->getChatRoom(); + else + mChatRoom = nullptr; reload(); emit chatRoomModelChanged(); } diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp index f0e604bb8..265155588 100644 --- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp +++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp @@ -32,18 +32,18 @@ class QWindow; class ChatRoomProxyModel : public QSortFilterProxyModel { class ChatRoomModelFilter; - Q_OBJECT; + Q_OBJECT - Q_PROPERTY(QString peerAddress READ getPeerAddress WRITE setPeerAddress NOTIFY peerAddressChanged); - Q_PROPERTY(QString localAddress READ getLocalAddress WRITE setLocalAddress NOTIFY localAddressChanged); - Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress WRITE setFullPeerAddress NOTIFY fullPeerAddressChanged); - Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress WRITE setFullLocalAddress NOTIFY fullLocalAddressChanged); - Q_PROPERTY(int isSecure READ getIsSecure WRITE setIsSecure NOTIFY isSecureChanged); - Q_PROPERTY(ChatRoomModel *chatRoomModel READ getChatRoomModel WRITE setChatRoomModel NOTIFY chatRoomModelChanged); - //Q_PROPERTY(bool isSecure MEMBER mIsSecure NOTIFY isSecureChanged); - Q_PROPERTY(bool isRemoteComposing READ getIsRemoteComposing NOTIFY isRemoteComposingChanged); - //Q_PROPERTY(bool isSecure READ getIsSecure NOTIFY isSecureChanged); - Q_PROPERTY(QString cachedText READ getCachedText); + Q_PROPERTY(QString peerAddress READ getPeerAddress WRITE setPeerAddress NOTIFY peerAddressChanged) + Q_PROPERTY(QString localAddress READ getLocalAddress WRITE setLocalAddress NOTIFY localAddressChanged) + Q_PROPERTY(QString fullPeerAddress READ getFullPeerAddress WRITE setFullPeerAddress NOTIFY fullPeerAddressChanged) + Q_PROPERTY(QString fullLocalAddress READ getFullLocalAddress WRITE setFullLocalAddress NOTIFY fullLocalAddressChanged) + //Q_PROPERTY(int isSecure READ isSecure WRITE setIsSecure NOTIFY isSecureChanged) + Q_PROPERTY(ChatRoomModel *chatRoomModel READ getChatRoomModel WRITE setChatRoomModel NOTIFY chatRoomModelChanged) + //Q_PROPERTY(bool isSecure MEMBER mIsSecure NOTIFY isSecureChanged) + Q_PROPERTY(bool isRemoteComposing READ getIsRemoteComposing NOTIFY isRemoteComposingChanged) + //Q_PROPERTY(bool isSecure READ getIsSecure NOTIFY isSecureChanged) + Q_PROPERTY(QString cachedText READ getCachedText) public: ChatRoomProxyModel (QObject *parent = Q_NULLPTR); @@ -73,7 +73,7 @@ signals: void fullPeerAddressChanged (const QString &fullPeerAddress); void fullLocalAddressChanged (const QString &fullLocalAddress); bool isRemoteComposingChanged (bool status); - bool isSecureChanged(bool secure); + //bool isSecureChanged(bool secure); void chatRoomModelChanged(); @@ -83,6 +83,7 @@ signals: protected: bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; + bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; private: QString getPeerAddress () const; @@ -97,8 +98,8 @@ private: QString getFullLocalAddress () const; void setFullLocalAddress (const QString &localAddress); - int getIsSecure () const; - void setIsSecure (const int &secure); + //bool isSecure () const; + //void setIsSecure (const int &secure); ChatRoomModel *getChatRoomModel() const; void setChatRoomModel (ChatRoomModel *chatRoomModel); @@ -121,7 +122,7 @@ private: QString mLocalAddress; QString mFullPeerAddress; QString mFullLocalAddress; - int mIsSecure; + //int mIsSecure; static QString gCachedText; std::shared_ptr mChatRoom; diff --git a/linphone-app/src/components/conference/ConferenceModel.cpp b/linphone-app/src/components/conference/ConferenceModel.cpp index 324d027ae..36c5d8c02 100644 --- a/linphone-app/src/components/conference/ConferenceModel.cpp +++ b/linphone-app/src/components/conference/ConferenceModel.cpp @@ -26,7 +26,6 @@ #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" #include "components/settings/SettingsModel.hpp" -#include "utils/LinphoneUtils.hpp" #include "utils/MediastreamerUtils.hpp" #include "utils/Utils.hpp" diff --git a/linphone-app/src/components/contact/ContactModel.cpp b/linphone-app/src/components/contact/ContactModel.cpp index 04d9a30f5..9bd12e4d9 100644 --- a/linphone-app/src/components/contact/ContactModel.cpp +++ b/linphone-app/src/components/contact/ContactModel.cpp @@ -67,6 +67,7 @@ VcardModel *ContactModel::getVcardModel () const { return mVcardModel; } +// ----------------------------------------------------------------------------- void ContactModel::setVcardModel (VcardModel *vcardModel) { VcardModel *oldVcardModel = mVcardModel; @@ -206,3 +207,7 @@ Presence::PresenceStatus ContactModel::getPresenceStatus () const { Presence::PresenceLevel ContactModel::getPresenceLevel () const { return Presence::getPresenceLevel(getPresenceStatus()); } + +bool ContactModel::hasCapability(const LinphoneEnums::FriendCapability& capability){ + return mLinphoneFriend->hasCapability(LinphoneEnums::toLinphone(capability)); +} diff --git a/linphone-app/src/components/contact/ContactModel.hpp b/linphone-app/src/components/contact/ContactModel.hpp index a93834d63..ca446f356 100644 --- a/linphone-app/src/components/contact/ContactModel.hpp +++ b/linphone-app/src/components/contact/ContactModel.hpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2010-2020 Belledonne Communications SARL. * * This file is part of linphone-desktop @@ -22,6 +22,7 @@ #define CONTACT_MODEL_H_ #include "components/presence/Presence.hpp" +#include "utils/LinphoneEnums.hpp" // ============================================================================= @@ -32,6 +33,7 @@ class ContactModel : public QObject { friend class ContactsListModel; friend class ContactsListProxyModel; friend class SipAddressesProxyModel; + friend class SipAddressesSorter; Q_OBJECT; @@ -52,6 +54,7 @@ public: Q_INVOKABLE VcardModel *cloneVcardModel () const; Presence::PresenceLevel getPresenceLevel () const; + Q_INVOKABLE bool hasCapability(const LinphoneEnums::FriendCapability& capability); signals: void contactUpdated (); diff --git a/linphone-app/src/components/contact/VcardModel.hpp b/linphone-app/src/components/contact/VcardModel.hpp index f1a39beaa..d6be16d22 100644 --- a/linphone-app/src/components/contact/VcardModel.hpp +++ b/linphone-app/src/components/contact/VcardModel.hpp @@ -39,6 +39,7 @@ class VcardModel : public QObject { Q_PROPERTY(QString username READ getUsername WRITE setUsername NOTIFY vcardUpdated); Q_PROPERTY(QString avatar READ getAvatar WRITE setAvatar NOTIFY vcardUpdated); Q_PROPERTY(QVariantMap address READ getAddress NOTIFY vcardUpdated); + //Q_PROPERTY(QString sipAddress Q_PROPERTY(QVariantList sipAddresses READ getSipAddresses NOTIFY vcardUpdated); Q_PROPERTY(QVariantList companies READ getCompanies NOTIFY vcardUpdated); Q_PROPERTY(QVariantList emails READ getEmails NOTIFY vcardUpdated); diff --git a/linphone-app/src/components/core/CoreHandlers.cpp b/linphone-app/src/components/core/CoreHandlers.cpp index 74575f75f..cb3d52fab 100644 --- a/linphone-app/src/components/core/CoreHandlers.cpp +++ b/linphone-app/src/components/core/CoreHandlers.cpp @@ -100,6 +100,15 @@ void CoreHandlers::onCallCreated(const shared_ptr &, emit callCreated(call); } +void CoreHandlers::onChatRoomStateChanged( + const std::shared_ptr & core, + const std::shared_ptr & chatRoom, + linphone::ChatRoom::State state +) { + qWarning() << "ChatRoomState : " << (int)state; + emit chatRoomStateChanged(chatRoom, state); +} + void CoreHandlers::onConfiguringStatus( const std::shared_ptr & core, linphone::ConfiguringState status, @@ -194,12 +203,15 @@ void CoreHandlers::onMessageReceived ( if ( !app->hasFocus() || + !CoreManager::getInstance()->getChatRoomModel(chatRoom, false) + /* !CoreManager::getInstance()->chatRoomModelExists( Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly()), Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly()), chatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Encrypted || chatRoom->getSecurityLevel() == linphone::ChatRoomSecurityLevel::Safe - ) + + )*/ ) core->playLocal(Utils::appStringToCoreString(settingsModel->getChatNotificationSoundPath())); } @@ -221,6 +233,7 @@ void CoreHandlers::onNotifyPresenceReceived ( // Ignore friend without vcard because the `contact-model` data doesn't exist. if (linphoneFriend->getVcard() && linphoneFriend->dataExists("contact-model")) linphoneFriend->getData("contact-model").refreshPresence(); + emit presenceStatusReceived(linphoneFriend); } void CoreHandlers::onRegistrationStateChanged ( diff --git a/linphone-app/src/components/core/CoreHandlers.hpp b/linphone-app/src/components/core/CoreHandlers.hpp index d09e3d203..298af4df0 100644 --- a/linphone-app/src/components/core/CoreHandlers.hpp +++ b/linphone-app/src/components/core/CoreHandlers.hpp @@ -45,6 +45,7 @@ signals: void callTransferFailed (const std::shared_ptr &call); void callTransferSucceeded (const std::shared_ptr &call); void callCreated(const std::shared_ptr & call); + void chatRoomStateChanged(const std::shared_ptr &chatRoom,linphone::ChatRoom::State state); void coreStarting(); void coreStarted (); void coreStopped (); @@ -52,6 +53,7 @@ signals: void logsUploadStateChanged (linphone::Core::LogCollectionUploadState state, const std::string &info); void messageReceived (const std::shared_ptr &message); void presenceReceived (const QString &sipAddress, const std::shared_ptr &presenceModel); + void presenceStatusReceived(std::shared_ptr contact); void registrationStateChanged (const std::shared_ptr &proxyConfig, linphone::RegistrationState state); void ecCalibrationResult(linphone::EcCalibratorStatus status, int delayMs); void setLastRemoteProvisioningState(const linphone::ConfiguringState &state); @@ -93,6 +95,12 @@ private: const std::shared_ptr & call ) override; + void onChatRoomStateChanged( + const std::shared_ptr & core, + const std::shared_ptr & chatRoom, + linphone::ChatRoom::State state + ) override; + void onConfiguringStatus( const std::shared_ptr & core, linphone::ConfiguringState status, diff --git a/linphone-app/src/components/core/CoreManager.cpp b/linphone-app/src/components/core/CoreManager.cpp index 5ed3e8e60..c997068fd 100644 --- a/linphone-app/src/components/core/CoreManager.cpp +++ b/linphone-app/src/components/core/CoreManager.cpp @@ -163,7 +163,7 @@ shared_ptr CoreManager::getChatRoomModel (const QString &peerAddr shared_ptr CoreManager::getChatRoomModel (ChatRoomModel * data) { if(data){ for(auto it = mChatRoomModels.begin() ; it != mChatRoomModels.end() ; ++it){ - auto a = it->lock(); + auto a = it->second.lock(); if(a.get() == data) return a; } @@ -171,17 +171,16 @@ shared_ptr CoreManager::getChatRoomModel (ChatRoomModel * data) { return nullptr; } -shared_ptr CoreManager::getChatRoomModel (std::shared_ptr chatRoom) { +shared_ptr CoreManager::getChatRoomModel (std::shared_ptr chatRoom, const bool& create) { if (!chatRoom) return nullptr; auto pc = chatRoom->getCurrentParams(); for(auto it = mChatRoomModels.begin() ; it != mChatRoomModels.end() ; ++it) { - auto a = it->lock(); + auto a = it->second.lock(); auto pa = a->getChatRoom()->getCurrentParams(); if( a->getChatRoom()->getConferenceAddress() == chatRoom->getConferenceAddress() && a->getChatRoom()->getLocalAddress() == chatRoom->getLocalAddress() && a->getChatRoom()->getPeerAddress() == chatRoom->getPeerAddress() - && a->getChatRoom()->getPeerAddress() == chatRoom->getPeerAddress() && pa->encryptionEnabled() == pc->encryptionEnabled() ){ // Returns an existing chat model. @@ -190,30 +189,52 @@ shared_ptr CoreManager::getChatRoomModel (std::shared_ptr> chatRoomModelId{pc->encryptionEnabled(), - { QString::fromStdString(chatRoom->getPeerAddress()->asString()) - , QString::fromStdString(chatRoom->getLocalAddress()->asString()) }}; - - - auto deleter = [this, chatRoomModelId](ChatRoomModel *chatRoomModel) { - bool removed = mChatRoomModels.remove(chatRoomModelId); - Q_ASSERT(removed); - chatRoomModel->deleteLater(); - }; - - shared_ptr chatRoomModel(new ChatRoomModel(chatRoom), deleter); - chatRoom->addListener(chatRoomModel); - mChatRoomModels[chatRoomModelId] = chatRoomModel; - - emit chatRoomModelCreated(chatRoomModel); - - return chatRoomModel; -} - -bool CoreManager::chatRoomModelExists (const QString &peerAddress, const QString &localAddress, const bool &isSecure) { - return mChatRoomModels.contains({isSecure, { peerAddress, localAddress}}); + if(!create){ + return nullptr; + }else{ + /* + bool isEncrypted = pc->encryptionEnabled(); + auto peerAddress = chatRoom->getPeerAddress(); + auto localAddress = chatRoom->getLocalAddress(); + auto conferenceAddress = chatRoom->getConferenceAddress(); + if(!peerAddress) + peerAddress = conferenceAddress; + + QPair> chatRoomModelId{isEncrypted, + { QString::fromStdString(peerAddress->asString()) + , QString::fromStdString(localAddress->asString()) }}; + + */ + //auto deleter = [this, chatRoomModelId](ChatRoomModel *chatRoomModel) { + auto deleter = [this, chatRoom](ChatRoomModel *chatRoomModel) { + //bool removed = mChatRoomModels.remove(chatRoomModelId); + auto iterator = mChatRoomModels.begin(); + while(iterator != mChatRoomModels.end()) { + if(iterator->first != chatRoom) + ++iterator; + else{ + mChatRoomModels.erase(iterator); + iterator = mChatRoomModels.end(); + } + } + chatRoomModel->deleteLater(); + }; + + shared_ptr chatRoomModel(new ChatRoomModel(chatRoom), deleter); + chatRoom->addListener(chatRoomModel); + mChatRoomModels.append({chatRoom, chatRoomModel}); + + emit chatRoomModelCreated(chatRoomModel); + + return chatRoomModel; + } } +/* +//bool CoreManager::chatRoomModelExists (const QString &peerAddress, const QString &localAddress, const bool &isSecure) { +bool CoreManager::chatRoomModelExists (std::shared_ptr chatRoom) { + //return mChatRoomModels.contains({isSecure, { peerAddress, localAddress}}); + return mChatRoomModels.contains(chatRoom); +}*/ HistoryModel* CoreManager::getHistoryModel(){ if(!mHistoryModel){ diff --git a/linphone-app/src/components/core/CoreManager.hpp b/linphone-app/src/components/core/CoreManager.hpp index 03ffe73da..812730a0d 100644 --- a/linphone-app/src/components/core/CoreManager.hpp +++ b/linphone-app/src/components/core/CoreManager.hpp @@ -70,8 +70,9 @@ public: //std::shared_ptr getChatRoomModel (const QString &peerAddress, const QString &localAddress, const bool &isSecure); std::shared_ptr getChatRoomModel (ChatRoomModel * data);// Get the shared pointer. This can be done becuase of unicity of ChatRoomModel - std::shared_ptr getChatRoomModel (std::shared_ptr chatRoom); - bool chatRoomModelExists (const QString &sipAddress, const QString &localAddress, const bool &isSecure); + std::shared_ptr getChatRoomModel (std::shared_ptr chatRoom, const bool& create = true); + //bool chatRoomModelExists (const QString &sipAddress, const QString &localAddress, const bool &isSecure); + //bool chatRoomModelExists (std::shared_ptr chatRoom); HistoryModel* getHistoryModel(); @@ -216,7 +217,9 @@ private: EventCountNotifier *mEventCountNotifier = nullptr; - QHash >, std::weak_ptr> mChatRoomModels; + //QHash >, std::weak_ptr> mChatRoomModels; + //QHash, std::weak_ptr> mChatRoomModels; + QList, std::weak_ptr>> mChatRoomModels; HistoryModel * mHistoryModel = nullptr; LdapListModel *mLdapListModel = nullptr; diff --git a/linphone-app/src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp b/linphone-app/src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp index 372b057a4..2a4398f03 100644 --- a/linphone-app/src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp +++ b/linphone-app/src/components/core/event-count-notifier/EventCountNotifierSystemTrayIcon.cpp @@ -26,7 +26,6 @@ #include #include "app/App.hpp" -#include "utils/LinphoneUtils.hpp" #include "utils/Utils.hpp" #include "EventCountNotifierSystemTrayIcon.hpp" @@ -45,7 +44,7 @@ namespace { } EventCountNotifier::EventCountNotifier (QObject *parent) : AbstractEventCountNotifier(parent) { - QSvgRenderer renderer((QString(LinphoneUtils::WindowIconPath))); + QSvgRenderer renderer((QString(Utils::WindowIconPath))); if (!renderer.isValid()) qFatal("Invalid SVG Image."); diff --git a/linphone-app/src/components/ldap/LdapListModel.cpp b/linphone-app/src/components/ldap/LdapListModel.cpp index 3cf67d9b4..9e7bac551 100644 --- a/linphone-app/src/components/ldap/LdapListModel.cpp +++ b/linphone-app/src/components/ldap/LdapListModel.cpp @@ -25,7 +25,6 @@ #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" -#include "utils/LinphoneUtils.hpp" #include "utils/Utils.hpp" #include "LdapListModel.hpp" diff --git a/linphone-app/src/components/ldap/LdapModel.cpp b/linphone-app/src/components/ldap/LdapModel.cpp index b70fec32c..fe0f60228 100644 --- a/linphone-app/src/components/ldap/LdapModel.cpp +++ b/linphone-app/src/components/ldap/LdapModel.cpp @@ -25,7 +25,6 @@ #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" -#include "utils/LinphoneUtils.hpp" #include "utils/Utils.hpp" #include "LdapModel.hpp" diff --git a/linphone-app/src/components/other/colors/Colors.hpp b/linphone-app/src/components/other/colors/Colors.hpp index 8e32493b1..973fdc69c 100644 --- a/linphone-app/src/components/other/colors/Colors.hpp +++ b/linphone-app/src/components/other/colors/Colors.hpp @@ -65,13 +65,13 @@ class Colors : public QObject { ADD_COLOR(d, "#5A585B") ADD_COLOR(e, "#F3F3F3") ADD_COLOR(f, "#E8E8E8") - ADD_COLOR(g, "#6B7A86") + ADD_COLOR(g, "#6B7A86")// SipAddress ADD_COLOR(h, "#687680") // Primary color. ADD_COLOR(i, "#FE5E00") - ADD_COLOR(j, "#4B5964") + ADD_COLOR(j, "#4B5964")// Username // Popups, home, call, assistant and settings background. ADD_COLOR(k, "#FFFFFF") @@ -89,6 +89,8 @@ class Colors : public QObject { // Fields, backgrounds and text color on some items. ADD_COLOR(q, "#FFFFFF") + + ADD_COLOR(r, "#909fab")//Background button // Field error. ADD_COLOR(error, "#FF0000") @@ -124,6 +126,7 @@ signals: void colorToChanged (const QColor &color); void colorTpChanged (const QColor &color); void colorTqChanged (const QColor &color); + void colorTrChanged (const QColor &color); void colorTerrorChanged (const QColor &color); diff --git a/linphone-app/src/components/participant/ParticipantDeviceListModel.cpp b/linphone-app/src/components/participant/ParticipantDeviceListModel.cpp new file mode 100644 index 000000000..90b5e5eb4 --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantDeviceListModel.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "app/App.hpp" + +#include "ParticipantDeviceListModel.hpp" +#include "utils/Utils.hpp" + +#include "components/Components.hpp" + +// ============================================================================= + +ParticipantDeviceListModel::ParticipantDeviceListModel (std::shared_ptr participant, QObject *parent) : QAbstractListModel(parent) { + std::list> devices = participant->getDevices() ; + for(auto device : devices){ + auto deviceModel = std::make_shared(device); + connect(this, &ParticipantDeviceListModel::securityLevelChanged, deviceModel.get(), &ParticipantDeviceModel::onSecurityLevelChanged); + mList << deviceModel; + } +} + +int ParticipantDeviceListModel::rowCount (const QModelIndex &index) const{ + return mList.count(); +} + +QHash ParticipantDeviceListModel::roleNames () const { + QHash roles; + roles[Qt::DisplayRole] = "$participantDevice"; + return roles; +} + +QVariant ParticipantDeviceListModel::data (const QModelIndex &index, int role) const { + int row = index.row(); + + if (!index.isValid() || row < 0 || row >= mList.count()) + return QVariant(); + + if (role == Qt::DisplayRole) + return QVariant::fromValue(mList[row].get()); + + return QVariant(); +} + +bool ParticipantDeviceListModel::removeRow (int row, const QModelIndex &parent){ + return removeRows(row, 1, parent); +} + +bool ParticipantDeviceListModel::removeRows (int row, int count, const QModelIndex &parent) { + int limit = row + count - 1; + + if (row < 0 || count < 0 || limit >= mList.count()) + return false; + + beginRemoveRows(parent, row, limit); + + for (int i = 0; i < count; ++i) + mList.takeAt(row)->deleteLater(); + + endRemoveRows(); + + return true; +} + +void ParticipantDeviceListModel::onSecurityLevelChanged(std::shared_ptr device){ + emit securityLevelChanged(device); +} \ No newline at end of file diff --git a/linphone-app/src/components/participant/ParticipantDeviceListModel.hpp b/linphone-app/src/components/participant/ParticipantDeviceListModel.hpp new file mode 100644 index 000000000..2e4cc410c --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantDeviceListModel.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PARTICIPANT_DEVICE_LIST_MODEL_H_ +#define PARTICIPANT_DEVICE_LIST_MODEL_H_ + + +#include +// ============================================================================= +#include +#include +#include +#include + +class ParticipantDeviceModel; + +class ParticipantDeviceListModel : public QAbstractListModel { + Q_OBJECT + +public: + ParticipantDeviceListModel (std::shared_ptr participant, QObject *parent = nullptr); + + int rowCount (const QModelIndex &index = QModelIndex()) const override; + + virtual QHash roleNames () const override; + virtual QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + +public slots: + void onSecurityLevelChanged(std::shared_ptr device); + +signals: + void securityLevelChanged(std::shared_ptr device); + +private: + bool removeRow (int row, const QModelIndex &parent = QModelIndex()); + virtual bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; + + QList> mList; + +}; + +Q_DECLARE_METATYPE(std::shared_ptr) + +#endif // PARTICIPANT_MODEL_H_ diff --git a/linphone-app/src/components/participant/ParticipantDeviceModel.cpp b/linphone-app/src/components/participant/ParticipantDeviceModel.cpp new file mode 100644 index 000000000..ddeeacdfb --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantDeviceModel.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "app/App.hpp" + +#include "ParticipantDeviceModel.hpp" +#include "utils/Utils.hpp" + +#include "components/Components.hpp" + +// ============================================================================= + +using namespace std; + +ParticipantDeviceModel::ParticipantDeviceModel (shared_ptr device, QObject *parent) : QObject(parent) { + mParticipantDevice = device; +} + +// ----------------------------------------------------------------------------- + +QString ParticipantDeviceModel::getName() const{ + return Utils::coreStringToAppString(mParticipantDevice->getName()); +} + +int ParticipantDeviceModel::getSecurityLevel() const{ + int security = (int)mParticipantDevice->getSecurityLevel(); + return security; +} + +time_t ParticipantDeviceModel::getTimeOfJoining() const{ + return mParticipantDevice->getTimeOfJoining(); +} + +QString ParticipantDeviceModel::getAddress() const{ + return Utils::coreStringToAppString(mParticipantDevice->getAddress()->asStringUriOnly()); +} + +std::shared_ptr ParticipantDeviceModel::getDevice(){ + return mParticipantDevice; +} + +void ParticipantDeviceModel::onSecurityLevelChanged(std::shared_ptr device){ + if(!device || mParticipantDevice->getAddress()->weakEqual(device)) + emit securityLevelChanged(); +} \ No newline at end of file diff --git a/linphone-app/src/components/participant/ParticipantDeviceModel.hpp b/linphone-app/src/components/participant/ParticipantDeviceModel.hpp new file mode 100644 index 000000000..b9f4d2ae3 --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantDeviceModel.hpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PARTICIPANT_DEVICE_MODEL_H_ +#define PARTICIPANT_DEVICE_MODEL_H_ + + +#include +// ============================================================================= +#include +#include +#include + +class ParticipantDeviceModel : public QObject { + Q_OBJECT + +public: + ParticipantDeviceModel (std::shared_ptr device, QObject *parent = nullptr); + + Q_PROPERTY(QString name READ getName CONSTANT) + Q_PROPERTY(QString address READ getAddress CONSTANT) + Q_PROPERTY(int securityLevel READ getSecurityLevel NOTIFY securityLevelChanged) + Q_PROPERTY(time_t timeOfJoining READ getTimeOfJoining CONSTANT) + + QString getName() const; + QString getAddress() const; + int getSecurityLevel() const; + time_t getTimeOfJoining() const; + + + + std::shared_ptr getDevice(); + + //void deviceSecurityLevelChanged(std::shared_ptr device); + +public slots: + void onSecurityLevelChanged(std::shared_ptr device); +signals: + void securityLevelChanged(); + +private: + + std::shared_ptr mParticipantDevice; + +}; + +//Q_DECLARE_METATYPE(ParticipantModel *); +Q_DECLARE_METATYPE(std::shared_ptr) + +#endif // PARTICIPANT_MODEL_H_ diff --git a/linphone-app/src/components/participant/ParticipantDeviceProxyModel.cpp b/linphone-app/src/components/participant/ParticipantDeviceProxyModel.cpp new file mode 100644 index 000000000..fc4877d11 --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantDeviceProxyModel.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "app/App.hpp" + +#include "ParticipantDeviceProxyModel.hpp" +#include "utils/Utils.hpp" + +#include "components/Components.hpp" +#include "ParticipantDeviceListModel.hpp" + +// ============================================================================= + +ParticipantDeviceProxyModel::ParticipantDeviceProxyModel (QObject *parent) : QSortFilterProxyModel(parent){ +} + +bool ParticipantDeviceProxyModel::filterAcceptsRow ( + int sourceRow, + const QModelIndex &sourceParent +) const { + Q_UNUSED(sourceRow) + Q_UNUSED(sourceParent) + //const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + //const ParticipantDeviceModel *device = index.data().value(); + return true; +} + +bool ParticipantDeviceProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { + const ParticipantDeviceModel *deviceA = sourceModel()->data(left).value(); + const ParticipantDeviceModel *deviceB = sourceModel()->data(right).value(); + + return deviceA->getTimeOfJoining() > deviceB->getTimeOfJoining(); +} +//--------------------------------------------------------------------------------- + +void ParticipantDeviceProxyModel::setParticipant(ParticipantModel * participant){ + setSourceModel(participant->getParticipantDevices().get()); +} \ No newline at end of file diff --git a/linphone-app/src/components/participant/ParticipantDeviceProxyModel.hpp b/linphone-app/src/components/participant/ParticipantDeviceProxyModel.hpp new file mode 100644 index 000000000..404a673fb --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantDeviceProxyModel.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PARTICIPANT_DEVICE_PROXY_MODEL_H_ +#define PARTICIPANT_DEVICE_PROXY_MODEL_H_ + + +#include +// ============================================================================= +#include +#include +#include +#include + +class ParticipantDeviceListModel; +class ParticipantModel; + +class ParticipantDeviceProxyModel : public QSortFilterProxyModel { + Q_OBJECT + +public: + ParticipantDeviceProxyModel (QObject *parent = nullptr); + + void setParticipant(ParticipantModel * participant); + +protected: + virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; + virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; + + std::shared_ptr mDevices; + +}; + +#endif diff --git a/linphone-app/src/components/participant/ParticipantListModel.cpp b/linphone-app/src/components/participant/ParticipantListModel.cpp new file mode 100644 index 000000000..9762066ef --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantListModel.cpp @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "components/core/CoreManager.hpp" +#include "components/settings/AccountSettingsModel.hpp" +#include "components/sip-addresses/SipAddressesModel.hpp" +#include "utils/Utils.hpp" + +#include "ParticipantListModel.hpp" +#include "ParticipantModel.hpp" + +#include + + +// ============================================================================= + +ParticipantListModel::ParticipantListModel (ChatRoomModel * chatRoomModel, QObject *parent) : QAbstractListModel(parent) { + if( chatRoomModel) { + mChatRoomModel = chatRoomModel;//CoreManager::getInstance()->getChatRoomModel(chatRoomModel); + + connect(mChatRoomModel, &ChatRoomModel::securityEvent, this, &ParticipantListModel::onSecurityEvent); + + connect(mChatRoomModel, &ChatRoomModel::participantAdded, this, &ParticipantListModel::onParticipantAdded); + connect(mChatRoomModel, &ChatRoomModel::participantRemoved, this, &ParticipantListModel::onParticipantRemoved); + connect(mChatRoomModel, &ChatRoomModel::participantDeviceAdded, this, &ParticipantListModel::onParticipantDeviceAdded); + connect(mChatRoomModel, &ChatRoomModel::participantDeviceRemoved, this, &ParticipantListModel::onParticipantDeviceRemoved); + + connect(mChatRoomModel, &ChatRoomModel::participantAdminStatusChanged, this, &ParticipantListModel::onParticipantAdminStatusChanged); + connect(mChatRoomModel, &ChatRoomModel::participantRegistrationSubscriptionRequested, this, &ParticipantListModel::onParticipantRegistrationSubscriptionRequested); + connect(mChatRoomModel, &ChatRoomModel::participantRegistrationUnsubscriptionRequested, this, &ParticipantListModel::onParticipantRegistrationUnsubscriptionRequested); + + updateParticipants(); + } +} +ParticipantListModel::~ParticipantListModel(){ + mParticipants.clear(); + mChatRoomModel = nullptr; +} + +// ----------------------------------------------------------------------------- + +ParticipantModel * ParticipantListModel::getAt(const int& index){ + return mParticipants[index].get(); +} + +ChatRoomModel *ParticipantListModel::getChatRoomModel() const{ + return mChatRoomModel; +} + +QString ParticipantListModel::addressesToString()const{ + QStringList txt; + for(auto participant : mParticipants){ + txt << Utils::coreStringToAppString(participant->getParticipant()->getAddress()->asStringUriOnly()); + } + txt.removeFirst();// Remove me + return txt.join(", "); +} + +QString ParticipantListModel::usernamesToString()const{ + QStringList txt; + for(auto participant : mParticipants){ + std::string username = participant->getParticipant()->getAddress()->getDisplayName(); + if(username == "") + username = participant->getParticipant()->getAddress()->getUsername(); + txt << Utils::coreStringToAppString(username); + } + txt.removeFirst();// Remove me + return txt.join(", "); +} + +bool ParticipantListModel::contains(const QString& address) const{ + auto testAddress = Utils::interpretUrl(address); + bool exists = false; + for(auto itParticipant = mParticipants.begin() ; !exists && itParticipant != mParticipants.end() ; ++itParticipant) + exists = testAddress->weakEqual(Utils::interpretUrl((*itParticipant)->getSipAddress() )); + return exists; +} + +//---------------------------------------------------------------------------- +int ParticipantListModel::rowCount (const QModelIndex &) const { + return mParticipants.count(); +} + +QHash ParticipantListModel::roleNames () const { + QHash roles; + roles[Qt::DisplayRole] = "$participant"; + return roles; +} + +QVariant ParticipantListModel::data (const QModelIndex &index, int role) const { + int row = index.row(); + + if (!index.isValid() || row < 0 || row >= mParticipants.count()) + return QVariant(); + + if (role == Qt::DisplayRole) + return QVariant::fromValue(mParticipants[row].get()); + + return QVariant(); +} + +// ----------------------------------------------------------------------------- + + + +// ----------------------------------------------------------------------------- + +bool ParticipantListModel::removeRow (int row, const QModelIndex &parent) { + return removeRows(row, 1, parent); +} + +bool ParticipantListModel::removeRows (int row, int count, const QModelIndex &parent) { + int limit = row + count - 1; + + if (row < 0 || count < 0 || limit >= mParticipants.count()) + return false; + + beginRemoveRows(parent, row, limit); + + for (int i = 0; i < count; ++i){ + mParticipants.takeAt(row); + } + + endRemoveRows(); + + return true; +} + + +// ----------------------------------------------------------------------------- + +void ParticipantListModel::updateParticipants () { + if( mChatRoomModel) { + CoreManager *coreManager = CoreManager::getInstance(); + auto dbParticipants = mChatRoomModel->getChatRoom()->getParticipants(); + auto me = mChatRoomModel->getChatRoom()->getMe(); + dbParticipants.push_front(me); + + //Remove left participants + //for(auto participant : mParticipants){ + auto itParticipant = mParticipants.begin(); + while(itParticipant != mParticipants.end()) { + auto itDbParticipant = dbParticipants.begin(); + while(itDbParticipant != dbParticipants.end() + && !(*itDbParticipant)->getAddress()->weakEqual((*itParticipant)->getParticipant()->getAddress())){ + ++itDbParticipant; + } + if( itDbParticipant == dbParticipants.end()){ + + itParticipant = mParticipants.erase(itParticipant); + }else + ++itParticipant; + } + // Add new + for(auto dbParticipant : dbParticipants){ + auto itParticipant = mParticipants.begin(); + while(itParticipant != mParticipants.end() && !dbParticipant->getAddress()->weakEqual((*itParticipant)->getParticipant()->getAddress())){ + ++itParticipant; + } + if( itParticipant == mParticipants.end()){ + auto participant = std::make_shared(dbParticipant); + connect(this, &ParticipantListModel::deviceSecurityLevelChanged, participant.get(), &ParticipantModel::onDeviceSecurityLevelChanged); + connect(this, &ParticipantListModel::securityLevelChanged, participant.get(), &ParticipantModel::onSecurityLevelChanged); + mParticipants << participant; + } + } + } +} + +void ParticipantListModel::add (std::shared_ptr participant){ + int row = mParticipants.count(); + beginInsertRows(QModelIndex(), row, row); + mParticipants << participant; + endInsertRows(); + resetInternalData(); +} + +void ParticipantListModel::remove (ParticipantModel *model) { + QString address = model->getSipAddress(); + int index = 0; + bool found = false; + auto itParticipant = mParticipants.begin() ; + while(!found && itParticipant != mParticipants.end()){ + if( (*itParticipant)->getSipAddress() == address) + found = true; + else{ + ++itParticipant; + ++index; + } + } + if(found) { + beginRemoveRows(QModelIndex(), index, index); + mParticipants.erase(itParticipant); + endRemoveRows(); + } +} + +const std::shared_ptr ParticipantListModel::getParticipant(const std::shared_ptr& address) const{ + if(address){ + auto itParticipant = std::find_if(mParticipants.begin(), mParticipants.end(), [address] (const std::shared_ptr& participant){ + return participant->getParticipant()->getAddress()->weakEqual(address); + }); + if( itParticipant == mParticipants.end()) + return nullptr; + else + return *itParticipant; + }else + return nullptr; +} + +//------------------------------------------------------------- +void ParticipantListModel::onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog) { + auto address = eventLog->getParticipantAddress(); + if(address) { + auto participant = getParticipant(address); + if( participant){ + emit participant->securityLevelChanged(); + } + }else{ + address = eventLog->getDeviceAddress(); +// Looping on all participant ensure to get all devices. Can be optimized if Device address is unique : Gain 2n operations. + if(address) + emit deviceSecurityLevelChanged(address); + } +} + +void ParticipantListModel::onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + updateParticipants(); +} + +void ParticipantListModel::onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + updateParticipants(); +} + +void ParticipantListModel::onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + + auto participant = getParticipant(eventLog->getParticipantAddress()); + if( participant){ + emit participant->adminStatusChanged();// Request to participant to update its status from its data + } +} +void ParticipantListModel::onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + auto participant = getParticipant(eventLog->getParticipantAddress()); + if( participant){ + emit participant->deviceCountChanged(); + } +} +void ParticipantListModel::onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + auto participant = getParticipant(eventLog->getParticipantAddress()); + if( participant){ + emit participant->deviceCountChanged(); + } +} +void ParticipantListModel::onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){ + qWarning() << "Toto"; +} +void ParticipantListModel::onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress){ + qWarning() << "Toto"; +} diff --git a/linphone-app/src/components/participant/ParticipantListModel.hpp b/linphone-app/src/components/participant/ParticipantListModel.hpp new file mode 100644 index 000000000..1e7d42865 --- /dev/null +++ b/linphone-app/src/components/participant/ParticipantListModel.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PARTICIPANT_LIST_MODEL_H_ +#define PARTICIPANT_LIST_MODEL_H_ + +#include +#include "components/participant/ParticipantModel.hpp" +#include "components/chat-room/ChatRoomModel.hpp" + +// ============================================================================= + +class ParticipantListModel : public QAbstractListModel { + Q_OBJECT +public: + ParticipantListModel (ChatRoomModel * chatRoomModel, QObject *parent = Q_NULLPTR); + virtual ~ParticipantListModel(); + + Q_PROPERTY(ChatRoomModel* chatRoomModel READ getChatRoomModel CONSTANT) + + void reset(); + void update(); + void selectAll(const bool& selected); + ParticipantModel * getAt(const int& index); + const std::shared_ptr getParticipant(const std::shared_ptr& address) const; + + int rowCount (const QModelIndex &index = QModelIndex()) const override; + + QHash roleNames () const override; + QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + + void add (std::shared_ptr participant); + void updateParticipants(); // Update list from Chat Room +// Remove a chatroom + Q_INVOKABLE void remove (ParticipantModel *importer); + Q_INVOKABLE ChatRoomModel* getChatRoomModel() const; + + Q_INVOKABLE QString addressesToString()const; + Q_INVOKABLE QString usernamesToString()const; + + bool contains(const QString& address) const; + + + +public slots: + void onSecurityEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void onParticipantAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void onParticipantRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void onParticipantAdminStatusChanged(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog); + void onParticipantRegistrationSubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); + void onParticipantRegistrationUnsubscriptionRequested(const std::shared_ptr & chatRoom, const std::shared_ptr & participantAddress); + +signals: + void securityLevelChanged(); + void deviceSecurityLevelChanged(std::shared_ptr device); + +private: + bool removeRow (int row, const QModelIndex &parent = QModelIndex()); + bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; + + QList> mParticipants; + ChatRoomModel* mChatRoomModel; +}; +Q_DECLARE_METATYPE(std::shared_ptr); +#endif // PARTICIPANT_LIST_MODEL_H_ diff --git a/linphone-app/src/components/participant/ParticipantModel.cpp b/linphone-app/src/components/participant/ParticipantModel.cpp index 4f8b6a499..d706220b3 100644 --- a/linphone-app/src/components/participant/ParticipantModel.cpp +++ b/linphone-app/src/components/participant/ParticipantModel.cpp @@ -25,30 +25,89 @@ #include "ParticipantModel.hpp" #include "utils/Utils.hpp" +#include "components/Components.hpp" + // ============================================================================= using namespace std; ParticipantModel::ParticipantModel (shared_ptr linphoneParticipant, QObject *parent) : QObject(parent) { - mLinphoneParticipant = linphoneParticipant; + mAdminStatus = false; + mParticipant = linphoneParticipant; + if(mParticipant){ + mParticipantDevices = std::make_shared(mParticipant); + connect(this, &ParticipantModel::deviceSecurityLevelChanged, mParticipantDevices.get(), &ParticipantDeviceListModel::securityLevelChanged); + } } // ----------------------------------------------------------------------------- -QString ParticipantModel::getAddress() const{ - return Utils::coreStringToAppString(mLinphoneParticipant->getAddress()->asStringUriOnly()); +ContactModel *ParticipantModel::getContactModel() const{ + return CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(getSipAddress()); +} + +int ParticipantModel::getSecurityLevel() const{ + return (mParticipant ? (int)mParticipant->getSecurityLevel() : 0); +} + +int ParticipantModel::getDeviceCount() const{ + return (mParticipant ? mParticipant->getDevices().size() : 0); +} + +QString ParticipantModel::getSipAddress() const{ + return (mParticipant ? Utils::coreStringToAppString(mParticipant->getAddress()->asString()) : mSipAddress); } QDateTime ParticipantModel::getCreationTime() const{ - return QDateTime::fromSecsSinceEpoch(mLinphoneParticipant->getCreationTime()); + return (mParticipant ? QDateTime::fromSecsSinceEpoch(mParticipant->getCreationTime()) : QDateTime::currentDateTime()); } //std::list> ParticipantModel::getDevices() const; -bool ParticipantModel::isAdmin() const{ - return mLinphoneParticipant->isAdmin(); +bool ParticipantModel::getAdminStatus() const{ + return (mParticipant ? mParticipant->isAdmin() : mAdminStatus); } + bool ParticipantModel::isFocus() const{ - return mLinphoneParticipant->isFocus(); + return (mParticipant ? mParticipant->isFocus() : false); } -//linphone::ChatRoomSecurityLevel ParticipantModel::getSecurityLevel() const; -//std::shared_ptr ParticipantModel::findDevice(const std::shared_ptr & address) const; + + +//------------------------------------------------------------------------ + +void ParticipantModel::setSipAddress(const QString& address){ + if(mSipAddress != address){ + mSipAddress = address; + emit sipAddressChanged(); + } +} + +void ParticipantModel::setAdminStatus(const bool& status){ + if(status != mAdminStatus){ + mAdminStatus = status; + emit adminStatusChanged(); + } +} + +//------------------------------------------------------------------------ + +void ParticipantModel::onSecurityLevelChanged(){ + emit securityLevelChanged(); +} +void ParticipantModel::onDeviceSecurityLevelChanged(std::shared_ptr device){ + emit deviceSecurityLevelChanged(device); +} + +std::shared_ptr ParticipantModel::getParticipant(){ + return mParticipant; +} + +ParticipantDeviceProxyModel * ParticipantModel::getProxyDevices(){ + ParticipantDeviceProxyModel * devices = new ParticipantDeviceProxyModel(); + devices->setParticipant(this); + return devices; +} + +std::shared_ptr ParticipantModel::getParticipantDevices(){ + return mParticipantDevices; +} + diff --git a/linphone-app/src/components/participant/ParticipantModel.hpp b/linphone-app/src/components/participant/ParticipantModel.hpp index 6ebb7f2d0..7d47758f5 100644 --- a/linphone-app/src/components/participant/ParticipantModel.hpp +++ b/linphone-app/src/components/participant/ParticipantModel.hpp @@ -28,35 +28,69 @@ #include #include +class ContactModel; +class ParticipantDeviceProxyModel; +class ParticipantDeviceListModel; + class ParticipantModel : public QObject { - - Q_OBJECT; - - Q_PROPERTY(QString address READ getAddress CONSTANT); - Q_PROPERTY(QDateTime creationTime READ getCreationTime CONSTANT); - Q_PROPERTY(bool admin READ isAdmin CONSTANT); - Q_PROPERTY(bool focus READ isFocus CONSTANT); + Q_OBJECT public: ParticipantModel (std::shared_ptr linphoneParticipant, QObject *parent = nullptr); + + Q_PROPERTY(ContactModel *contactModel READ getContactModel CONSTANT) + Q_PROPERTY(QString sipAddress MEMBER mSipAddress READ getSipAddress WRITE setSipAddress NOTIFY sipAddressChanged) + Q_PROPERTY(bool adminStatus MEMBER mAdminStatus READ getAdminStatus WRITE setAdminStatus NOTIFY adminStatusChanged) + Q_PROPERTY(QDateTime creationTime READ getCreationTime CONSTANT) + Q_PROPERTY(bool focus READ isFocus CONSTANT) + Q_PROPERTY(int securityLevel READ getSecurityLevel NOTIFY securityLevelChanged) + Q_PROPERTY(int deviceCount READ getDeviceCount NOTIFY deviceCountChanged) - QString getAddress() const; + ContactModel *getContactModel() const; + QString getSipAddress() const; QDateTime getCreationTime() const; //std::list> getDevices() const; - bool isAdmin() const; + bool getAdminStatus() const; bool isFocus() const; + int getSecurityLevel() const; + int getDeviceCount() const; + + void setSipAddress(const QString& address); + void setAdminStatus(const bool& status); + + std::shared_ptr getParticipant(); + Q_INVOKABLE ParticipantDeviceProxyModel * getProxyDevices(); // Reminder : Q_INVOKABLE change the ownership of the proxy to QML + std::shared_ptr getParticipantDevices(); //linphone::ChatRoomSecurityLevel getSecurityLevel() const; //std::shared_ptr findDevice(const std::shared_ptr & address) const; - -//signals: + + + +public slots: + void onSecurityLevelChanged(); + void onDeviceSecurityLevelChanged(std::shared_ptr device); + +signals: + void securityLevelChanged(); + void deviceSecurityLevelChanged(std::shared_ptr device); + void sipAddressChanged(); + void adminStatusChanged(); + void deviceCountChanged(); + // void contactUpdated (); private: - std::shared_ptr mLinphoneParticipant; + std::shared_ptr mParticipant; + std::shared_ptr mParticipantDevices; + +// Variables when Linphone Participant has not been created + QString mSipAddress; + bool mAdminStatus; }; -Q_DECLARE_METATYPE(ParticipantModel *); +//Q_DECLARE_METATYPE(ParticipantModel *); +Q_DECLARE_METATYPE(std::shared_ptr); #endif // PARTICIPANT_MODEL_H_ diff --git a/linphone-app/src/components/participant/ParticipantProxyModel.cpp b/linphone-app/src/components/participant/ParticipantProxyModel.cpp index 3edc783f7..72d056b85 100644 --- a/linphone-app/src/components/participant/ParticipantProxyModel.cpp +++ b/linphone-app/src/components/participant/ParticipantProxyModel.cpp @@ -18,276 +18,92 @@ * along with this program. If not, see . */ -#include +#include "ParticipantProxyModel.hpp" -#include "app/App.hpp" #include "components/core/CoreManager.hpp" +#include "components/settings/AccountSettingsModel.hpp" +#include "components/sip-addresses/SipAddressesModel.hpp" +#include "utils/Utils.hpp" + +#include "ParticipantListModel.hpp" +#include "ParticipantModel.hpp" + +#include -#include "ChatRoomProxyModel.hpp" // ============================================================================= -using namespace std; +// ----------------------------------------------------------------------------- -QString ChatRoomProxyModel::gCachedText; - -// Fetch the L last filtered chat entries. -class ChatRoomProxyModel::ChatRoomModelFilter : public QSortFilterProxyModel { -public: - ChatRoomModelFilter (QObject *parent) : QSortFilterProxyModel(parent) {} - - ChatRoomModel::EntryType getEntryTypeFilter () { - return mEntryTypeFilter; - } - - void setEntryTypeFilter (ChatRoomModel::EntryType type) { - mEntryTypeFilter = type; - invalidate(); - } - -protected: - bool filterAcceptsRow (int sourceRow, const QModelIndex &) const override { - if (mEntryTypeFilter == ChatRoomModel::EntryType::GenericEntry) - return true; - - QModelIndex index = sourceModel()->index(sourceRow, 0, QModelIndex()); - const QVariantMap data = index.data().toMap(); - - return data["type"].toInt() == mEntryTypeFilter; - } - -private: - ChatRoomModel::EntryType mEntryTypeFilter = ChatRoomModel::EntryType::GenericEntry; -}; - -// ============================================================================= - -ChatRoomProxyModel::ChatRoomProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { - setSourceModel(new ChatRoomModelFilter(this)); - mIsSecure = false; - - App *app = App::getInstance(); - QObject::connect(app->getMainWindow(), &QWindow::activeChanged, this, [this]() { - handleIsActiveChanged(App::getInstance()->getMainWindow()); - }); - - QQuickWindow *callsWindow = app->getCallsWindow(); - if (callsWindow) - QObject::connect(callsWindow, &QWindow::activeChanged, this, [this, callsWindow]() { - handleIsActiveChanged(callsWindow); - }); +ParticipantProxyModel::ParticipantProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { + mChatRoomModel = nullptr; } // ----------------------------------------------------------------------------- -#define GET_CHAT_MODEL() \ - if (!mChatRoomModel) \ - return; \ - mChatRoomModel +ChatRoomModel *ParticipantProxyModel::getChatRoomModel() const{ + return mChatRoomModel; +} -#define CREATE_PARENT_MODEL_FUNCTION(METHOD) \ - void ChatRoomProxyModel::METHOD () { \ - GET_CHAT_MODEL()->METHOD(); \ - } +QStringList ParticipantProxyModel::getSipAddresses() const{ + QStringList participants; + ParticipantListModel * list = dynamic_cast(sourceModel()); + for(int i = 0 ; i < list->rowCount() ; ++i) + participants << list->getAt(i)->getSipAddress(); + return participants; +} -#define CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(METHOD, ARG_TYPE) \ - void ChatRoomProxyModel::METHOD (ARG_TYPE value) { \ - GET_CHAT_MODEL()->METHOD(value); \ - } +QVariantList ParticipantProxyModel::getParticipants() const{ + QVariantList participants; + ParticipantListModel * list = dynamic_cast(sourceModel()); + for(int i = 0 ; i < list->rowCount() ; ++i) + participants << QVariant::fromValue(list->getAt(i)); + return participants; +} -#define CREATE_PARENT_MODEL_FUNCTION_WITH_ID(METHOD) \ - void ChatRoomProxyModel::METHOD (int id) { \ - QModelIndex sourceIndex = mapToSource(index(id, 0)); \ - GET_CHAT_MODEL()->METHOD( \ - static_cast(sourceModel())->mapToSource(sourceIndex).row() \ - ); \ - } - -CREATE_PARENT_MODEL_FUNCTION(removeAllEntries); - -CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(sendFileMessage, const QString &); -CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM(sendMessage, const QString &); - -CREATE_PARENT_MODEL_FUNCTION_WITH_ID(downloadFile); -CREATE_PARENT_MODEL_FUNCTION_WITH_ID(openFile); -CREATE_PARENT_MODEL_FUNCTION_WITH_ID(openFileDirectory); -CREATE_PARENT_MODEL_FUNCTION_WITH_ID(removeEntry); -CREATE_PARENT_MODEL_FUNCTION_WITH_ID(resendMessage); - -#undef GET_CHAT_MODEL -#undef CREATE_PARENT_MODEL_FUNCTION -#undef CREATE_PARENT_MODEL_FUNCTION_WITH_PARAM -#undef CREATE_PARENT_MODEL_FUNCTION_WITH_ID - - -void ChatRoomProxyModel::compose (const QString& text) { - if (mChatRoomModel) - mChatRoomModel->compose(); - gCachedText = text; +int ParticipantProxyModel::count(){ + return dynamic_cast(sourceModel())->rowCount(); } // ----------------------------------------------------------------------------- -void ChatRoomProxyModel::loadMoreEntries () { - int count = rowCount(); - int parentCount = sourceModel()->rowCount(); - - if (count < parentCount) { - // Do not increase `mMaxDisplayedEntries` if it's not necessary... - // Limit qml calls. - if (count == mMaxDisplayedEntries) - mMaxDisplayedEntries += EntriesChunkSize; - - invalidateFilter(); - - count = rowCount() - count; - if (count > 0) - emit moreEntriesLoaded(count); - } -} - -void ChatRoomProxyModel::setEntryTypeFilter (ChatRoomModel::EntryType type) { - ChatRoomModelFilter *chatRoomModelFilter = static_cast(sourceModel()); - - if (chatRoomModelFilter->getEntryTypeFilter() != type) { - chatRoomModelFilter->setEntryTypeFilter(type); - emit entryTypeFilterChanged(type); - } -} - -// ----------------------------------------------------------------------------- - -bool ChatRoomProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &) const { - return sourceModel()->rowCount() - sourceRow <= mMaxDisplayedEntries; -} - -// ----------------------------------------------------------------------------- - -QString ChatRoomProxyModel::getPeerAddress () const { - return mChatRoomModel ? mChatRoomModel->getPeerAddress() : QString(""); -} - -void ChatRoomProxyModel::setPeerAddress (const QString &peerAddress) { - mPeerAddress = peerAddress; - //reload(); -} - -QString ChatRoomProxyModel::getLocalAddress () const { - return mChatRoomModel ? mChatRoomModel->getLocalAddress() : QString(""); -} - -void ChatRoomProxyModel::setLocalAddress (const QString &localAddress) { - mLocalAddress = localAddress; - //reload(); -} - -QString ChatRoomProxyModel::getFullPeerAddress () const { - return mChatRoomModel ? mChatRoomModel->getFullPeerAddress() : QString(""); -} - -void ChatRoomProxyModel::setFullPeerAddress (const QString &peerAddress) { - mFullPeerAddress = peerAddress; - //reload(); -} - -QString ChatRoomProxyModel::getFullLocalAddress () const { - return mChatRoomModel ? mChatRoomModel->getFullLocalAddress() : QString(""); -} - -void ChatRoomProxyModel::setFullLocalAddress (const QString &localAddress) { - mFullLocalAddress = localAddress; - //reload(); -} - -int ChatRoomProxyModel::getIsSecure () const { - return mChatRoomModel ? mChatRoomModel->getIsSecure() : -1; -} - -void ChatRoomProxyModel::setIsSecure (const int &secure) { - mIsSecure = secure; -} - -bool ChatRoomProxyModel::getIsRemoteComposing () const { - return mChatRoomModel ? mChatRoomModel->getIsRemoteComposing() : false; -} - -QString ChatRoomProxyModel::getCachedText() const{ - return gCachedText; -} - -// ----------------------------------------------------------------------------- - -void ChatRoomProxyModel::reload () { - mMaxDisplayedEntries = EntriesChunkSize; - - if (mChatRoomModel) { - ChatRoomModel *chatRoomModel = mChatRoomModel.get(); - QObject::disconnect(chatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); - QObject::disconnect(chatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); - QObject::disconnect(chatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); - } - - //mChatRoomModel = CoreManager::getInstance()->getChatRoomModel(mPeerAddress, mLocalAddress, mIsSecure); - //if(mChatRoom) - mChatRoomModel = CoreManager::getInstance()->getChatRoomModel(mChatRoom); - - - if (mChatRoomModel) { - - ChatRoomModel *chatRoomModel = mChatRoomModel.get(); - QObject::connect(chatRoomModel, &ChatRoomModel::isRemoteComposingChanged, this, &ChatRoomProxyModel::handleIsRemoteComposingChanged); - QObject::connect(chatRoomModel, &ChatRoomModel::messageReceived, this, &ChatRoomProxyModel::handleMessageReceived); - QObject::connect(chatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent); - } - - static_cast(sourceModel())->setSourceModel(mChatRoomModel.get()); -} -void ChatRoomProxyModel::resetMessageCount(){ - if( mChatRoomModel){ - mChatRoomModel->resetMessageCount(); +void ParticipantProxyModel::setChatRoomModel(ChatRoomModel * chatRoomModel){ + if(!mChatRoomModel || mChatRoomModel != chatRoomModel){ + mChatRoomModel = chatRoomModel; + if(mChatRoomModel) + setSourceModel(mChatRoomModel->getParticipants()); + else { + setSourceModel(new ParticipantListModel(nullptr, this)); + } + sort(0); + emit chatRoomModelChanged(); } } -std::shared_ptr ChatRoomProxyModel::getChatRoomModel () const{ - return mChatRoomModel; - +void ParticipantProxyModel::add(const QString& address){ + ParticipantListModel * participantsModel = dynamic_cast(sourceModel()); + if(!participantsModel->contains(address)){ + std::shared_ptr participant = std::make_shared(nullptr, this); + participant->setSipAddress(address); + participantsModel->add(participant); + } } -void ChatRoomProxyModel::setChatRoomModel (std::shared_ptr chatRoomModel){ - mChatRoom = chatRoomModel->getChatRoom(); - reload(); - emit chatRoomModelChanged(); + +void ParticipantProxyModel::remove(ParticipantModel * participant){ + if(participant) + dynamic_cast(sourceModel())->remove(participant); } + // ----------------------------------------------------------------------------- -static inline QWindow *getParentWindow (QObject *object) { - App *app = App::getInstance(); - const QWindow *mainWindow = app->getMainWindow(); - const QWindow *callsWindow = app->getCallsWindow(); - for (QObject *parent = object->parent(); parent; parent = parent->parent()) - if (parent == mainWindow || parent == callsWindow) - return static_cast(parent); - return nullptr; +bool ParticipantProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { + //const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + return true; } -void ChatRoomProxyModel::handleIsActiveChanged (QWindow *window) { - if (mChatRoomModel && window->isActive() && getParentWindow(this) == window) { - mChatRoomModel->resetMessageCount(); - mChatRoomModel->focused(); - } -} - -void ChatRoomProxyModel::handleIsRemoteComposingChanged (bool status) { - emit isRemoteComposingChanged(status); -} - -void ChatRoomProxyModel::handleMessageReceived (const shared_ptr &) { - mMaxDisplayedEntries++; - - QWindow *window = getParentWindow(this); - if (window && window->isActive()) - mChatRoomModel->resetMessageCount(); -} - -void ChatRoomProxyModel::handleMessageSent (const shared_ptr &) { - mMaxDisplayedEntries++; +bool ParticipantProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { + const ParticipantModel* a = sourceModel()->data(left).value(); + const ParticipantModel* b = sourceModel()->data(right).value(); + + return a->getCreationTime() >= b->getCreationTime(); } diff --git a/linphone-app/src/components/participant/ParticipantProxyModel.hpp b/linphone-app/src/components/participant/ParticipantProxyModel.hpp index 91ed8fe76..46bb1bb94 100644 --- a/linphone-app/src/components/participant/ParticipantProxyModel.hpp +++ b/linphone-app/src/components/participant/ParticipantProxyModel.hpp @@ -22,9 +22,11 @@ #define PARTICIPANT_PROXY_MODEL_H_ #include +#include -#include "ParticipantModel.hpp" - +class ParticipantModel; +class ChatRoomModel; +class ParticipantListModel; // ============================================================================= class QWindow; @@ -35,30 +37,35 @@ class ParticipantProxyModel : public QSortFilterProxyModel { public: - ParticipantProxyModel (QObject *parent = Q_NULLPTR); + ParticipantProxyModel ( QObject *parent = Q_NULLPTR); - void reset(); - void update(); - std::shared_ptr getTimeline(std::shared_ptr chatRoom, const bool &create); - - int rowCount (const QModelIndex &index = QModelIndex()) const override; - - QHash roleNames () const override; - QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; - -// Remove a chatroom - Q_INVOKABLE void remove (TimelineModel *importer); + Q_PROPERTY(ChatRoomModel* chatRoomModel READ getChatRoomModel WRITE setChatRoomModel NOTIFY chatRoomModelChanged) + bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; + bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; + + ChatRoomModel *getChatRoomModel() const; + Q_INVOKABLE QStringList getSipAddresses() const; + Q_INVOKABLE QVariantList getParticipants() const; + Q_INVOKABLE int count(); + + void setChatRoomModel(ChatRoomModel * chatRoomModel); + + Q_INVOKABLE void add(const QString& address); + Q_INVOKABLE void remove(ParticipantModel * participant); + + + +signals: + void chatRoomModelChanged(); + private: + /* bool removeRow (int row, const QModelIndex &parent = QModelIndex()); - bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; - - - - void initTimeline (); - void updateTimelines(); + bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override;*/ - QList> mParticipantlines; + //std::shared_ptr mParticipantListModel; + ChatRoomModel *mChatRoomModel; }; #endif // PARTICIPANT_PROXY_MODEL_H_ diff --git a/linphone-app/src/components/presence/Presence.cpp b/linphone-app/src/components/presence/Presence.cpp index e8c18ee97..5be2f1b64 100644 --- a/linphone-app/src/components/presence/Presence.cpp +++ b/linphone-app/src/components/presence/Presence.cpp @@ -23,12 +23,15 @@ // ============================================================================= Presence::PresenceLevel Presence::getPresenceLevel (const PresenceStatus &status) { + return getPresenceLevel(static_cast(status)); +} +Presence::PresenceLevel Presence::getPresenceLevel (const linphone::ConsolidatedPresence &status) { switch (status) { - case Online: + case linphone::ConsolidatedPresence::Online: return Green; - case Busy: + case linphone::ConsolidatedPresence::Busy: return Orange; - case DoNotDisturb: + case linphone::ConsolidatedPresence::DoNotDisturb: return Red; default: break; @@ -38,12 +41,15 @@ Presence::PresenceLevel Presence::getPresenceLevel (const PresenceStatus &status } QString Presence::getPresenceStatusAsString (const PresenceStatus &status) { + return getPresenceStatusAsString(static_cast(status)); +} +QString Presence::getPresenceStatusAsString (const linphone::ConsolidatedPresence &status) { switch (status) { - case Online: + case linphone::ConsolidatedPresence::Online: return tr("presenceOnline"); - case Busy: + case linphone::ConsolidatedPresence::Busy: return tr("presenceBusy"); - case DoNotDisturb: + case linphone::ConsolidatedPresence::DoNotDisturb: return tr("presenceDoNotDisturb"); default: break; @@ -52,6 +58,21 @@ QString Presence::getPresenceStatusAsString (const PresenceStatus &status) { return tr("presenceOffline"); } +QString Presence::getBetterPresenceLevelIconName (const PresenceLevel &level) { + switch (level) { + case Green: + return QStringLiteral("current_account_status_available"); + case Orange: + return QStringLiteral("led_orange"); + case Red: + return QStringLiteral("led_red"); + case White: + return QStringLiteral("led_white"); + } + + return QString(""); +} + QString Presence::getPresenceLevelIconName (const PresenceLevel &level) { switch (level) { case Green: diff --git a/linphone-app/src/components/presence/Presence.hpp b/linphone-app/src/components/presence/Presence.hpp index 3471d7579..adb44d3a8 100644 --- a/linphone-app/src/components/presence/Presence.hpp +++ b/linphone-app/src/components/presence/Presence.hpp @@ -52,9 +52,12 @@ public: Presence (QObject *parent = Q_NULLPTR) : QObject(parent) {} Q_INVOKABLE static PresenceLevel getPresenceLevel (const PresenceStatus &status); + static Presence::PresenceLevel getPresenceLevel (const linphone::ConsolidatedPresence &status); Q_INVOKABLE static QString getPresenceStatusAsString (const PresenceStatus &status); - Q_INVOKABLE static QString getPresenceLevelIconName (const PresenceLevel &level); + static QString getPresenceStatusAsString (const linphone::ConsolidatedPresence &status); + Q_INVOKABLE static QString getBetterPresenceLevelIconName (const PresenceLevel &level);// Get a better set of icons + Q_INVOKABLE static QString getPresenceLevelIconName (const PresenceLevel &level);// Get a "background" icons (not very visible) }; #endif // PRESENCE_H_ diff --git a/linphone-app/src/components/settings/AccountSettingsModel.cpp b/linphone-app/src/components/settings/AccountSettingsModel.cpp index 164ea0fac..ad4f2e77c 100644 --- a/linphone-app/src/components/settings/AccountSettingsModel.cpp +++ b/linphone-app/src/components/settings/AccountSettingsModel.cpp @@ -174,6 +174,7 @@ void AccountSettingsModel::setDefaultProxyConfig (const shared_ptrgetDefaultProxyConfig() != proxyConfig) { core->setDefaultProxyConfig(proxyConfig); emit accountSettingsUpdated(); + emit defaultProxyChanged(); } } @@ -335,17 +336,18 @@ QString AccountSettingsModel::getUsername () const { } void AccountSettingsModel::setUsername (const QString &username) { - shared_ptr address = getUsedSipAddress(); - shared_ptr newAddress = address->clone(); - - if (newAddress->setDisplayName(username.toStdString())) { - qWarning() << QStringLiteral("Unable to set displayName on sip address: `%1`.") - .arg(Utils::coreStringToAppString(newAddress->asStringUriOnly())); - } else { - setUsedSipAddress(newAddress); - } - - emit accountSettingsUpdated(); + shared_ptr address = getUsedSipAddress(); + shared_ptr newAddress = address->clone(); + QString oldUsername = Utils::coreStringToAppString(newAddress->getUsername()); + if( oldUsername != username) { + if (newAddress->setDisplayName(username.toStdString())) { + qWarning() << QStringLiteral("Unable to set displayName on sip address: `%1`.") + .arg(Utils::coreStringToAppString(newAddress->asStringUriOnly())); + } else { + setUsedSipAddress(newAddress); + emit accountSettingsUpdated(); + } + } } AccountSettingsModel::RegistrationState AccountSettingsModel::getRegistrationState () const { @@ -362,15 +364,17 @@ QString AccountSettingsModel::getPrimaryUsername () const { } void AccountSettingsModel::setPrimaryUsername (const QString &username) { - shared_ptr core = CoreManager::getInstance()->getCore(); - shared_ptr primary = core->createPrimaryContactParsed(); + shared_ptr core = CoreManager::getInstance()->getCore(); + shared_ptr primary = core->createPrimaryContactParsed(); - primary->setUsername(Utils::appStringToCoreString( - username.isEmpty() ? APPLICATION_NAME : username - )); - core->setPrimaryContact(primary->asString()); - - emit accountSettingsUpdated(); + QString oldUsername = Utils::coreStringToAppString(primary->getUsername()); + if(oldUsername != username){ + primary->setUsername(Utils::appStringToCoreString( + username.isEmpty() ? APPLICATION_NAME : username + )); + core->setPrimaryContact(primary->asString()); + emit accountSettingsUpdated(); + } } QString AccountSettingsModel::getPrimaryDisplayName () const { @@ -378,13 +382,15 @@ QString AccountSettingsModel::getPrimaryDisplayName () const { } void AccountSettingsModel::setPrimaryDisplayName (const QString &displayName) { - shared_ptr core = CoreManager::getInstance()->getCore(); - shared_ptr primary = core->createPrimaryContactParsed(); + shared_ptr core = CoreManager::getInstance()->getCore(); + shared_ptr primary = core->createPrimaryContactParsed(); - primary->setDisplayName(displayName.toStdString()); - core->setPrimaryContact(primary->asString()); - - emit accountSettingsUpdated(); + QString oldDisplayName = Utils::coreStringToAppString(primary->getDisplayName()); + if(oldDisplayName != displayName){ + primary->setDisplayName(displayName.toStdString()); + core->setPrimaryContact(primary->asString()); + emit accountSettingsUpdated(); + } } QString AccountSettingsModel::getPrimarySipAddress () const { diff --git a/linphone-app/src/components/settings/AccountSettingsModel.hpp b/linphone-app/src/components/settings/AccountSettingsModel.hpp index 151d3cd63..ca740e70e 100644 --- a/linphone-app/src/components/settings/AccountSettingsModel.hpp +++ b/linphone-app/src/components/settings/AccountSettingsModel.hpp @@ -84,6 +84,7 @@ public: signals: void accountSettingsUpdated (); + void defaultProxyChanged(); void publishPresenceChanged(); private: diff --git a/linphone-app/src/components/settings/SettingsModel.hpp b/linphone-app/src/components/settings/SettingsModel.hpp index b92671df4..05be6243b 100644 --- a/linphone-app/src/components/settings/SettingsModel.hpp +++ b/linphone-app/src/components/settings/SettingsModel.hpp @@ -430,9 +430,9 @@ public: bool getExitOnClose () const; void setExitOnClose (bool value); - bool getShowLocalSipAccount () const; - bool getShowStartChatButton () const; - bool getShowStartVideoCallButton () const; + Q_INVOKABLE bool getShowLocalSipAccount () const; + Q_INVOKABLE bool getShowStartChatButton () const; + Q_INVOKABLE bool getShowStartVideoCallButton () const; // Advanced. --------------------------------------------------------------------------- diff --git a/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.cpp b/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.cpp index 1ef63fc27..5dafd2036 100644 --- a/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.cpp +++ b/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.cpp @@ -32,7 +32,6 @@ #include "components/core/CoreManager.hpp" #include "components/history/HistoryModel.hpp" #include "components/settings/AccountSettingsModel.hpp" -#include "utils/LinphoneUtils.hpp" #include "utils/Utils.hpp" #include "SearchSipAddressesModel.hpp" diff --git a/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.hpp b/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.hpp index 4c44f679d..61b512858 100644 --- a/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.hpp +++ b/linphone-app/src/components/sip-addresses/SearchSipAddressesModel.hpp @@ -33,7 +33,7 @@ class SearchSipAddressesModel : public QAbstractListModel { - Q_OBJECT; + Q_OBJECT public: SearchSipAddressesModel (QObject *parent = Q_NULLPTR); diff --git a/linphone-app/src/components/sip-addresses/SearchSipAddressesProxyModel.cpp b/linphone-app/src/components/sip-addresses/SearchSipAddressesProxyModel.cpp new file mode 100644 index 000000000..daf422131 --- /dev/null +++ b/linphone-app/src/components/sip-addresses/SearchSipAddressesProxyModel.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "components/contact/ContactModel.hpp" +#include "components/contact/VcardModel.hpp" +#include "components/core/CoreManager.hpp" + +#include "SipAddressesModel.hpp" +#include "SearchSipAddressesProxyModel.hpp" +#include "SearchSipAddressesModel.hpp" +#include "SipAddressesSorter.hpp" + +#include "utils/Utils.hpp" + +#include + + +// ----------------------------------------------------------------------------- + +SearchSipAddressesProxyModel::SearchSipAddressesProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { + setSourceModel(new SearchSipAddressesModel(this)); + sort(0); +} + +// ----------------------------------------------------------------------------- + +SearchSipAddressesModel * SearchSipAddressesProxyModel::getModel(){ + return dynamic_cast(sourceModel()); +} + +void SearchSipAddressesProxyModel::setFilter (const QString &pattern){ + mFilter = pattern; + getModel()->setFilter(pattern); +} + +void SearchSipAddressesProxyModel::addAddressToIgnore(const QString& address){ + std::shared_ptr a = Utils::interpretUrl(address); + mResultsToIgnore[Utils::coreStringToAppString(a->asStringUriOnly())] = true; + invalidate(); +} + +void SearchSipAddressesProxyModel::removeAddressToIgnore(const QString& address){ + std::shared_ptr a = Utils::interpretUrl(address); + mResultsToIgnore.remove(Utils::coreStringToAppString(a->asStringUriOnly())); + invalidate(); +} + +bool SearchSipAddressesProxyModel::isIgnored(const QString& address) const{ + if(address != ""){ + std::shared_ptr a = Utils::interpretUrl(address); + return mResultsToIgnore.contains(Utils::coreStringToAppString(a->asStringUriOnly())); + } + return false; +} + +bool SearchSipAddressesProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { + const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + const QVariantMap data = sourceModel()->data(index).toMap(); + + std::shared_ptr a = Utils::interpretUrl(data["sipAddress"].toString()); + + return !mResultsToIgnore.contains(Utils::coreStringToAppString(a->asStringUriOnly())); +} + +bool SearchSipAddressesProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { + const QVariantMap mapA = sourceModel()->data(left).toMap(); + const QVariantMap mapB = sourceModel()->data(right).toMap(); + return SipAddressesSorter::lessThan(mFilter, mapA, mapB); +} + diff --git a/linphone-app/src/components/sip-addresses/SearchSipAddressesProxyModel.hpp b/linphone-app/src/components/sip-addresses/SearchSipAddressesProxyModel.hpp new file mode 100644 index 000000000..8e88118a1 --- /dev/null +++ b/linphone-app/src/components/sip-addresses/SearchSipAddressesProxyModel.hpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SEARCH_SIP_ADDRESSES_PROXY_MODEL_H_ +#define SEARCH_SIP_ADDRESSES_PROXY_MODEL_H_ + +#include + +class SearchSipAddressesModel; + +// ============================================================================= + +class SearchSipAddressesProxyModel : public QSortFilterProxyModel { + Q_OBJECT + +public: + SearchSipAddressesProxyModel (QObject *parent = Q_NULLPTR); + + Q_PROPERTY(SearchSipAddressesModel * model READ getModel CONSTANT) + + Q_INVOKABLE void addAddressToIgnore(const QString& address); + Q_INVOKABLE void removeAddressToIgnore(const QString& address); + Q_INVOKABLE bool isIgnored(const QString& address) const; + + SearchSipAddressesModel * getModel(); + + + Q_INVOKABLE void setFilter (const QString &pattern); + void setResultExceptions(QAbstractListModel* exceptionList); + +signals: + void resultExceptionsChanged(); + +protected: + bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; + bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; + +private: + QMap mResultsToIgnore; + QString mFilter; + +}; + +#endif diff --git a/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp b/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp index 353894c61..049bc0dd7 100644 --- a/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp +++ b/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp @@ -32,7 +32,6 @@ #include "components/core/CoreManager.hpp" #include "components/history/HistoryModel.hpp" #include "components/settings/AccountSettingsModel.hpp" -#include "utils/LinphoneUtils.hpp" #include "utils/Utils.hpp" #include "SipAddressesModel.hpp" @@ -46,7 +45,7 @@ using namespace std; static inline QVariantMap buildVariantMap (const SipAddressesModel::SipAddressEntry &sipAddressEntry) { return QVariantMap{ { "sipAddress", sipAddressEntry.sipAddress }, - { "contact", QVariant::fromValue(sipAddressEntry.contact) }, + { "contactModel", QVariant::fromValue(sipAddressEntry.contact) }, { "presenceStatus", sipAddressEntry.presenceStatus }, { "__localToConferenceEntry", QVariant::fromValue(&sipAddressEntry.localAddressToConferenceEntry) } }; @@ -180,7 +179,7 @@ QString SipAddressesModel::addTransportToSipAddress (const QString &sipAddress, if (!address) return QString(""); - address->setTransport(LinphoneUtils::stringToTransportType(transport.toUpper())); + address->setTransport(Utils::stringToTransportType(transport.toUpper())); return QString::fromStdString(address->asString()); } @@ -286,8 +285,10 @@ void SipAddressesModel::handleHistoryModelCreated (HistoryModel *historyModel) { }); } void SipAddressesModel::handleContactAdded (ContactModel *contact) { - for (const auto &sipAddress : contact->getVcardModel()->getSipAddresses()) - addOrUpdateSipAddress(sipAddress.toString(), contact); + for (const auto &sipAddress : contact->getVcardModel()->getSipAddresses()) { + qWarning() << "handleContactAdded " << sipAddress.toString(); + addOrUpdateSipAddress(sipAddress.toString(), contact); + } } void SipAddressesModel::handleContactRemoved (const ContactModel *contact) { @@ -301,7 +302,7 @@ void SipAddressesModel::handleSipAddressAdded (ContactModel *contact, const QStr qWarning() << "Unable to map sip address" << sipAddress << "to" << contact << "- already used by" << mappedContact; return; } - + qWarning() << "handleSipAddressAdded " << sipAddress; addOrUpdateSipAddress(sipAddress, contact); } diff --git a/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp b/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp index 090c88cef..adc7c17e1 100644 --- a/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp +++ b/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp @@ -60,6 +60,8 @@ public: QHash roleNames () const override; QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + + //Q_PROPERTY(QList resultExceptions READ getResultExceptions WRITE setResultExceptions NOTIFY resultExceptionsChanged) Q_INVOKABLE QVariantMap find (const QString &sipAddress) const; Q_INVOKABLE ContactModel *mapSipAddressToContact (const QString &sipAddress) const; diff --git a/linphone-app/src/components/sip-addresses/SipAddressesSorter.cpp b/linphone-app/src/components/sip-addresses/SipAddressesSorter.cpp new file mode 100644 index 000000000..1c4cbad9e --- /dev/null +++ b/linphone-app/src/components/sip-addresses/SipAddressesSorter.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "components/contact/ContactModel.hpp" +#include "components/contact/VcardModel.hpp" +#include "components/core/CoreManager.hpp" + +#include "SipAddressesSorter.hpp" + +// ============================================================================= + +namespace { + constexpr int WeightPos0 = 5; + constexpr int WeightPos1 = 4; + constexpr int WeightPos2 = 3; + constexpr int WeightPos3 = 2; + constexpr int WeightPosOther = 1; +} + +const QRegExp SipAddressesSorter::SearchSeparators("^[^_.-;@ ][_.-;@ ]"); + +// ----------------------------------------------------------------------------- + +SipAddressesSorter::SipAddressesSorter (QObject *parent) : QObject(parent) { +} + +// ----------------------------------------------------------------------------- + +bool SipAddressesSorter::lessThan (const QString& filter, const QVariantMap &left, const QVariantMap &right) { + const QString sipAddressA = left["sipAddress"].toString(); + const QString sipAddressB = right["sipAddress"].toString(); + + // TODO: Use a cache, do not compute the same value as `filterAcceptsRow`. + int weightA = computeEntryWeight(filter, left); + int weightB = computeEntryWeight(filter, right); + + // 1. Not the same weight. + if (weightA != weightB) + return weightA > weightB; + + const ContactModel *contactA = left.value("contact").value(); + const ContactModel *contactB = right.value("contact").value(); + + // 2. No contacts. + if (!contactA && !contactB) + return sipAddressA <= sipAddressB; + + // 3. No contact for a or b. + if (!contactA || !contactB) + return !!contactA; + + // 4. Same contact (address). + if (contactA == contactB) + return sipAddressA <= sipAddressB; + + // 5. Not the same contact name. + int diff = contactA->mLinphoneFriend->getName().compare(contactB->mLinphoneFriend->getName()); + if (diff) + return diff <= 0; + + // 6. Same contact name, so compare sip addresses. + return sipAddressA <= sipAddressB; +} + +int SipAddressesSorter::computeEntryWeight (const QString& filter, const QVariantMap &entry) { + int weight = computeStringWeight(filter, entry["sipAddress"].toString().mid(4)); + + const ContactModel *contact = entry.value("contact").value(); + if (contact) + weight += computeStringWeight(filter, contact->getVcardModel()->getUsername()); + + return weight; +} + +int SipAddressesSorter::computeStringWeight (const QString& filter, const QString &string) { + int index = -1; + int offset = -1; + + while ((index = string.indexOf(filter, index + 1, Qt::CaseInsensitive)) != -1) { + int tmpOffset = index - string.lastIndexOf(SearchSeparators, index) - 1; + if ((tmpOffset != -1 && tmpOffset < offset) || offset == -1) + if ((offset = tmpOffset) == 0) break; + } + + switch (offset) { + case -1: return 0; + case 0: return WeightPos0; + case 1: return WeightPos1; + case 2: return WeightPos2; + case 3: return WeightPos3; + default: break; + } + + return WeightPosOther; +} diff --git a/linphone-app/src/components/sip-addresses/SipAddressesSorter.hpp b/linphone-app/src/components/sip-addresses/SipAddressesSorter.hpp new file mode 100644 index 000000000..634cde640 --- /dev/null +++ b/linphone-app/src/components/sip-addresses/SipAddressesSorter.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SIP_ADDRESSES_SORTER_H_ +#define SIP_ADDRESSES_SORTER_H_ + +#include +#include +#include + +// ============================================================================= + +class SipAddressesSorter : public QObject{ + Q_OBJECT + +public: + SipAddressesSorter (QObject *parent = Q_NULLPTR); + + static bool lessThan( const QString& filter, const QVariantMap &left, const QVariantMap &right); + +private: + static int computeEntryWeight (const QString& filter, const QVariantMap &entry); + static int computeStringWeight (const QString& filter, const QString &string); + + static const QRegExp SearchSeparators; +}; + +#endif diff --git a/linphone-app/src/components/timeline/TimelineListModel.cpp b/linphone-app/src/components/timeline/TimelineListModel.cpp index 3e4debdae..530b0a324 100644 --- a/linphone-app/src/components/timeline/TimelineListModel.cpp +++ b/linphone-app/src/components/timeline/TimelineListModel.cpp @@ -18,12 +18,16 @@ * along with this program. If not, see . */ +#include "TimelineListModel.hpp" + #include "components/core/CoreManager.hpp" +#include "components/core/CoreHandlers.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" +#include "components/contacts/ContactsListModel.hpp" #include "utils/Utils.hpp" -#include "TimelineListModel.hpp" + #include "TimelineModel.hpp" #include @@ -34,6 +38,11 @@ TimelineListModel::TimelineListModel (QObject *parent) : QAbstractListModel(parent) { //initTimeline(); mSelectedCount = 0; + CoreHandlers* coreHandlers= CoreManager::getInstance()->getHandlers().get(); + connect(coreHandlers, &CoreHandlers::chatRoomStateChanged, this, &TimelineListModel::onChatRoomStateChanged); + connect(coreHandlers, &CoreHandlers::messageReceived, this, &TimelineListModel::update); + connect(coreHandlers, &CoreHandlers::messageReceived, this, &TimelineListModel::updated); + updateTimelines (); } @@ -157,12 +166,41 @@ std::shared_ptr TimelineListModel::getTimeline(std::shared_ptr
  • model = std::make_shared(chatRoom); chatRoom->addListener(model); connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(selectedHasChanged(bool))); + //connect(model.get(), SIGNAL(conferenceLeft()), this, SLOT(selectedHasChanged(bool))); return model; } } return nullptr; } +QVariantList TimelineListModel::getLastChatRooms(const int& maxCount) const{ + QVariantList contacts; + QMultiMap sortedData; + int count = 0; + + QDateTime currentDateTime = QDateTime::currentDateTime(); + auto contactList = CoreManager::getInstance()->getContactsListModel(); + + for(auto timeline : mTimelines){ + auto chatRoom = timeline->getChatRoomModel(); + if(chatRoom && !chatRoom->isGroupEnabled() && !chatRoom->haveEncryption()) { + //ContactModel * contact = contactList->findContactModelFromSipAddress(chatRoom->getPeerAddress()); + //if(contact) + sortedData.insert(chatRoom->mLastUpdateTime.secsTo(currentDateTime),chatRoom); + } + } + for(auto contact : sortedData){ + ++count; + contacts << QVariant::fromValue(contact); + if(count >= maxCount) + return contacts; + } + + return contacts; +} + +//------------------------------------------------------------------------------------------------- + void TimelineListModel::setSelectedCount(int selectedCount){ if(mSelectedCount != selectedCount) { mSelectedCount = selectedCount; @@ -185,20 +223,50 @@ void TimelineListModel::selectedHasChanged(bool selected){ } void TimelineListModel::updateTimelines () { - CoreManager *coreManager = CoreManager::getInstance(); - auto currentAddress = coreManager->getAccountSettingsModel()->getUsedSipAddress(); - - std::list> allChatRooms = coreManager->getCore()->getChatRooms(); + //CoreManager *coreManager = CoreManager::getInstance(); + //auto currentAddress = coreManager->getAccountSettingsModel()->getUsedSipAddress(); + /* + //std::list> allChatRooms = coreManager->getCore()->getChatRooms(); QList > models; for(auto itAllChatRooms = allChatRooms.begin() ; itAllChatRooms != allChatRooms.end() ; ++itAllChatRooms){ if((*itAllChatRooms)->getMe()->getAddress()->weakEqual(currentAddress)){ models << getTimeline(*itAllChatRooms, true); - //models << new TimelineModel(*itAllChatRooms); + ; } } //beginInsertRows(QModelIndex(), 0, models.count()-1); mTimelines = models; + */ + CoreManager *coreManager = CoreManager::getInstance(); + auto currentAddress = coreManager->getAccountSettingsModel()->getUsedSipAddress(); + std::list> allChatRooms = coreManager->getCore()->getChatRooms(); + +//Remove left participants + auto itTimeline = mTimelines.begin(); + while(itTimeline != mTimelines.end()) { + auto itDbTimeline = allChatRooms.begin(); + auto timeline = (*itTimeline)->getChatRoomModel()->getChatRoom(); + while(itDbTimeline != allChatRooms.end() && *itDbTimeline != timeline ){ + ++itDbTimeline; + } + if( itDbTimeline == allChatRooms.end()){ + int index = itTimeline - mTimelines.begin(); + removeRow(index); + ++itTimeline; + //itTimeline = mTimelines.erase(itTimeline); + }else + ++itTimeline; + } +// Add new + for(auto dbChatRoom : allChatRooms){ + if(!getTimeline(dbChatRoom, false)){// Create a new Timeline if needed + std::shared_ptr model = std::make_shared(dbChatRoom); + dbChatRoom->addListener(model); + connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(selectedHasChanged(bool))); + add(model); + } + } } /* // Create a new TimelineModel and put it in the list @@ -212,11 +280,42 @@ void TimelineListModel::add(){ resetInternalData(); } */ -void TimelineListModel::remove (TimelineModel *model) { + +void TimelineListModel::add (std::shared_ptr timeline){ + int row = mTimelines.count(); + beginInsertRows(QModelIndex(), row, row); + mTimelines << timeline; + endInsertRows(); + resetInternalData(); +} + +void TimelineListModel::remove (TimelineModel* model) { /* - int index = mTimelines.indexOf(ldap); + int index = mTimelines.indexOf(model); if (index >=0){ ldap->unsave(); removeRow(index); }*/ } +void TimelineListModel::remove(std::shared_ptr model){ + int index = mTimelines.indexOf(model); + if (index >=0){ + removeRow(index); + } +} + +void TimelineListModel::onChatRoomStateChanged(const std::shared_ptr &chatRoom,linphone::ChatRoom::State state){ + if( state == linphone::ChatRoom::State::Created + && !getTimeline(chatRoom, false)){// Create a new Timeline if needed + std::shared_ptr model = std::make_shared(chatRoom); + chatRoom->addListener(model); + connect(model.get(), SIGNAL(selectedChanged(bool)), this, SLOT(selectedHasChanged(bool))); + add(model); + }else if(state == linphone::ChatRoom::State::Deleted){ + updateTimelines(); + } +} +/* +void TimelineListModel::onConferenceLeft(const std::shared_ptr &chatRoom, , const std::shared_ptr & eventLog){ + remove(getTimeline(chatRoom, false).get()); +}*/ \ No newline at end of file diff --git a/linphone-app/src/components/timeline/TimelineListModel.hpp b/linphone-app/src/components/timeline/TimelineListModel.hpp index 798838f67..956cb7e6a 100644 --- a/linphone-app/src/components/timeline/TimelineListModel.hpp +++ b/linphone-app/src/components/timeline/TimelineListModel.hpp @@ -36,26 +36,33 @@ public: TimelineListModel (QObject *parent = Q_NULLPTR); void reset(); - void update(); void selectAll(const bool& selected); TimelineModel * getAt(const int& index); std::shared_ptr getTimeline(std::shared_ptr chatRoom, const bool &create); + Q_INVOKABLE QVariantList getLastChatRooms(const int& maxCount) const; int rowCount (const QModelIndex &index = QModelIndex()) const override; QHash roleNames () const override; QVariant data (const QModelIndex &index, int role = Qt::DisplayRole) const override; + void add (std::shared_ptr timeline); // Use to add a timeline that is not in Linphone list (like empty chat rooms that were hide by configuration) // Remove a chatroom Q_INVOKABLE void remove (TimelineModel *importer); + void remove(std::shared_ptr model); int mSelectedCount; void setSelectedCount(int selectedCount); public slots: + void update(); void selectedHasChanged(bool selected); + void onChatRoomStateChanged(const std::shared_ptr &chatRoom,linphone::ChatRoom::State state); + //void onConferenceLeft(); + signals: void selectedCountChanged(int selectedCount); + void updated(); private: bool removeRow (int row, const QModelIndex &parent = QModelIndex()); diff --git a/linphone-app/src/components/timeline/TimelineModel.cpp b/linphone-app/src/components/timeline/TimelineModel.cpp index 99d00cf68..a8f82d167 100644 --- a/linphone-app/src/components/timeline/TimelineModel.cpp +++ b/linphone-app/src/components/timeline/TimelineModel.cpp @@ -36,6 +36,7 @@ TimelineModel::TimelineModel (std::shared_ptr chatRoom, QObj QObject::connect(mChatRoomModel.get(), &ChatRoomModel::unreadMessagesCountChanged, this, &TimelineModel::updateUnreadCount); QObject::connect(mChatRoomModel.get(), &ChatRoomModel::missedCallsCountChanged, this, &TimelineModel::updateUnreadCount); + QObject::connect(mChatRoomModel.get(), &ChatRoomModel::conferenceLeft, this, &TimelineModel::onConferenceLeft); //mTimestamp = QDateTime::fromMSecsSinceEpoch(mChatRoomModel->getChatRoom()->getLastUpdateTime()); mSelected = false; } @@ -112,8 +113,11 @@ void TimelineModel::onSubjectChanged(const std::shared_ptr & void TimelineModel::onUndecryptableMessageReceived(const std::shared_ptr & chatRoom, const std::shared_ptr & message){} void TimelineModel::onParticipantDeviceAdded(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onParticipantDeviceRemoved(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void TimelineModel::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} -void TimelineModel::onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} +void TimelineModel::onConferenceJoined(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ +} +void TimelineModel::onConferenceLeft(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){ + +} void TimelineModel::onEphemeralEvent(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onEphemeralMessageTimerStarted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} void TimelineModel::onEphemeralMessageDeleted(const std::shared_ptr & chatRoom, const std::shared_ptr & eventLog){} diff --git a/linphone-app/src/components/timeline/TimelineModel.hpp b/linphone-app/src/components/timeline/TimelineModel.hpp index 1e8a38660..c240cd713 100644 --- a/linphone-app/src/components/timeline/TimelineModel.hpp +++ b/linphone-app/src/components/timeline/TimelineModel.hpp @@ -105,6 +105,7 @@ signals: void avatarChanged(); void presenceStatusChanged(); void selectedChanged(bool selected); + void conferenceLeft(); }; diff --git a/linphone-app/src/components/timeline/TimelineProxyModel.cpp b/linphone-app/src/components/timeline/TimelineProxyModel.cpp index 82ab7d3fe..40874c11d 100644 --- a/linphone-app/src/components/timeline/TimelineProxyModel.cpp +++ b/linphone-app/src/components/timeline/TimelineProxyModel.cpp @@ -40,10 +40,11 @@ TimelineProxyModel::TimelineProxyModel (QObject *parent) : QSortFilterProxyModel TimelineListModel * model = CoreManager::getInstance()->getTimelineListModel(); connect(model, SIGNAL(selectedCountChanged(int)), this, SIGNAL(selectedCountChanged(int))); + connect(model, &TimelineListModel::updated, this, &TimelineProxyModel::invalidate); setSourceModel(model); - QObject::connect(accountSettingsModel, &AccountSettingsModel::accountSettingsUpdated, this, [this]() { + QObject::connect(accountSettingsModel, &AccountSettingsModel::defaultProxyChanged, this, [this]() { dynamic_cast(sourceModel())->update(); invalidate(); //updateCurrentSelection(); diff --git a/linphone-app/src/utils/LinphoneEnums.cpp b/linphone-app/src/utils/LinphoneEnums.cpp new file mode 100644 index 000000000..943b157d8 --- /dev/null +++ b/linphone-app/src/utils/LinphoneEnums.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "LinphoneEnums.hpp" + +// ============================================================================= + +void LinphoneEnums::registerMetaTypes(){ + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); +} + + +linphone::MediaEncryption LinphoneEnums::toLinphone(const LinphoneEnums::MediaEncryption& encryption){ + return static_cast(encryption); +} +LinphoneEnums::MediaEncryption LinphoneEnums::fromLinphone(const linphone::MediaEncryption& encryption){ + return static_cast(encryption); +} + +linphone::FriendCapability LinphoneEnums::toLinphone(const LinphoneEnums::FriendCapability& capability){ + return static_cast(capability); +} +LinphoneEnums::FriendCapability LinphoneEnums::fromLinphone(const linphone::FriendCapability& capability){ + return static_cast(capability); +} + +linphone::EventLog::Type LinphoneEnums::toLinphone(const LinphoneEnums::EventLogType& capability){ + return static_cast(capability); +} +LinphoneEnums::EventLogType LinphoneEnums::fromLinphone(const linphone::EventLog::Type& capability){ + return static_cast(capability); +} \ No newline at end of file diff --git a/linphone-app/src/utils/LinphoneEnums.hpp b/linphone-app/src/utils/LinphoneEnums.hpp new file mode 100644 index 000000000..4008fce6b --- /dev/null +++ b/linphone-app/src/utils/LinphoneEnums.hpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LINPHONE_ENUMS_H_ +#define LINPHONE_ENUMS_H_ + +#include +#include + +// ============================================================================= + +namespace LinphoneEnums { +Q_NAMESPACE + +void registerMetaTypes(); + +enum MediaEncryption { + MediaEncryptionNone = int(linphone::MediaEncryption::None), + MediaEncryptionDtls = int(linphone::MediaEncryption::DTLS), + MediaEncryptionSrtp = int(linphone::MediaEncryption::SRTP), + MediaEncryptionZrtp = int(linphone::MediaEncryption::ZRTP) +}; +Q_ENUM_NS(MediaEncryption) + +linphone::MediaEncryption toLinphone(const LinphoneEnums::MediaEncryption& encryption); +LinphoneEnums::MediaEncryption fromLinphone(const linphone::MediaEncryption& encryption); + +enum FriendCapability { + FriendCapabilityNone = int(linphone::FriendCapability::None), + FriendCapabilityGroupChat = int(linphone::FriendCapability::GroupChat), + FriendCapabilityLimeX3Dh = int(linphone::FriendCapability::LimeX3Dh), + FriendCapabilityEphemeralMessages = int(linphone::FriendCapability::EphemeralMessages) +}; +Q_ENUM_NS(FriendCapability) + +linphone::FriendCapability toLinphone(const LinphoneEnums::FriendCapability& capability); +LinphoneEnums::FriendCapability fromLinphone(const linphone::FriendCapability& capability); + + +enum EventLogType { + EventLogTypeNone = int(linphone::EventLog::Type::None), + EventLogTypeConferenceCreated = int(linphone::EventLog::Type::ConferenceCreated), + EventLogTypeConferenceTerminated = int(linphone::EventLog::Type::ConferenceTerminated), + EventLogTypeConferenceCallStart = int(linphone::EventLog::Type::ConferenceCallStart), + EventLogTypeConferenceCallEnd = int(linphone::EventLog::Type::ConferenceCallEnd), + EventLogTypeConferenceChatMessage = int(linphone::EventLog::Type::ConferenceChatMessage), + EventLogTypeConferenceParticipantAdded = int(linphone::EventLog::Type::ConferenceParticipantAdded), + EventLogTypeConferenceParticipantRemoved = int(linphone::EventLog::Type::ConferenceParticipantRemoved), + EventLogTypeConferenceParticipantSetAdmin = int(linphone::EventLog::Type::ConferenceParticipantSetAdmin), + EventLogTypeConferenceParticipantUnsetAdmin = int(linphone::EventLog::Type::ConferenceParticipantUnsetAdmin), + EventLogTypeConferenceParticipantDeviceAdded = int(linphone::EventLog::Type::ConferenceParticipantDeviceAdded), + EventLogTypeConferenceParticipantDeviceRemoved = int(linphone::EventLog::Type::ConferenceParticipantDeviceRemoved), + EventLogTypeConferenceParticipantDeviceMediaChanged = int(linphone::EventLog::Type::ConferenceParticipantDeviceMediaChanged), + EventLogTypeConferenceSubjectChanged= int(linphone::EventLog::Type::ConferenceSubjectChanged), + EventLogTypeConferenceAvailableMediaChanged = int(linphone::EventLog::Type::ConferenceAvailableMediaChanged), + EventLogTypeConferenceSecurityEvent = int(linphone::EventLog::Type::ConferenceSecurityEvent), + EventLogTypeConferenceEphemeralMessageLifetimeChanged = int(linphone::EventLog::Type::ConferenceEphemeralMessageLifetimeChanged), + EventLogTypeConferenceEphemeralMessageEnabled = int(linphone::EventLog::Type::ConferenceEphemeralMessageEnabled), + EventLogTypeConferenceEphemeralMessageDisabled = int(linphone::EventLog::Type::ConferenceEphemeralMessageDisabled) +}; +Q_ENUM_NS(EventLogType) + +linphone::EventLog::Type toLinphone(const LinphoneEnums::EventLogType& capability); +LinphoneEnums::EventLogType fromLinphone(const linphone::EventLog::Type& capability); +} + +Q_DECLARE_METATYPE(LinphoneEnums::MediaEncryption) +Q_DECLARE_METATYPE(LinphoneEnums::FriendCapability) + +#endif diff --git a/linphone-app/src/utils/LinphoneUtils.cpp b/linphone-app/src/utils/LinphoneUtils.cpp index 9001f3503..50bb01c38 100644 --- a/linphone-app/src/utils/LinphoneUtils.cpp +++ b/linphone-app/src/utils/LinphoneUtils.cpp @@ -19,8 +19,14 @@ */ #include +#include #include "LinphoneUtils.hpp" +#include "utils/Utils.hpp" +#include "components/core/CoreManager.hpp" + +#include "components/contacts/ContactsListModel.hpp" +#include "components/contact/ContactModel.hpp" // ============================================================================= @@ -34,3 +40,16 @@ linphone::TransportType LinphoneUtils::stringToTransportType (const QString &tra return linphone::TransportType::Dtls; } + +std::shared_ptr LinphoneUtils::interpretUrl(const QString& address){ + return CoreManager::getInstance()->getCore()->interpretUrl(Utils::appStringToCoreString(address)); +} +/* +bool LinphoneUtils::hasCapability(const QString& address, const LinphoneEnums::FriendCapability& capability){ + auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(address); + if(contact) + return contact->hasCapability(capability); + else + return false; +} +*/ \ No newline at end of file diff --git a/linphone-app/src/utils/LinphoneUtils.hpp b/linphone-app/src/utils/LinphoneUtils.hpp index 1eeb73d1d..a7773d04e 100644 --- a/linphone-app/src/utils/LinphoneUtils.hpp +++ b/linphone-app/src/utils/LinphoneUtils.hpp @@ -23,15 +23,19 @@ #include +#include "LinphoneEnums.hpp" + // ============================================================================= class QString; namespace LinphoneUtils { - linphone::TransportType stringToTransportType (const QString &transport); - + + std::shared_ptr interpretUrl(const QString& address); + static constexpr char WindowIconPath[] = ":/assets/images/linphone_logo.svg"; } + #endif // ifndef LINPHONE_UTILS_H_ diff --git a/linphone-app/src/utils/Utils.cpp b/linphone-app/src/utils/Utils.cpp index 3ce2acbe8..fa5d54f9e 100644 --- a/linphone-app/src/utils/Utils.cpp +++ b/linphone-app/src/utils/Utils.cpp @@ -26,13 +26,32 @@ #include "Utils.hpp" #include "components/core/CoreManager.hpp" +#include "components/contacts/ContactsListModel.hpp" +#include "components/contact/ContactModel.hpp" +#include "components/contact/VcardModel.hpp" // ============================================================================= namespace { constexpr int SafeFilePathLimit = 100; + } +constexpr char Utils::WindowIconPath[]; +linphone::TransportType Utils::stringToTransportType (const QString &transport) { + if (transport == QLatin1String("TCP")) + return linphone::TransportType::Tcp; + if (transport == QLatin1String("UDP")) + return linphone::TransportType::Udp; + if (transport == QLatin1String("TLS")) + return linphone::TransportType::Tls; + + return linphone::TransportType::Dtls; +} + +std::shared_ptr Utils::interpretUrl(const QString& address){ + return CoreManager::getInstance()->getCore()->interpretUrl(Utils::appStringToCoreString(address)); +} char *Utils::rstrstr (const char *a, const char *b) { size_t a_len = strlen(a); size_t b_len = strlen(b); @@ -49,6 +68,16 @@ char *Utils::rstrstr (const char *a, const char *b) { } // ----------------------------------------------------------------------------- + +bool Utils::hasCapability(const QString& address, const LinphoneEnums::FriendCapability& capability){ + auto contact = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(address); + if(contact) + return contact->hasCapability(capability); + else + return false; +} + + QImage Utils::getImage(const QString &pUri) { QImage image(pUri); if(image.isNull()){// Try to determine format from headers instead of using suffix @@ -416,3 +445,17 @@ void Utils::copyDir(QString from, QString to) { copyDir(from + nextDir, toDir);//Go up } } + +QString Utils::getDisplayName(const std::shared_ptr& address){ + QString qtAddress = Utils::coreStringToAppString(address->asString()); + QString displayName; + ContactModel * model = CoreManager::getInstance()->getContactsListModel()->findContactModelFromSipAddress(qtAddress); + if(model && model->getVcardModel()) + displayName = model->getVcardModel()->getUsername(); + else{ + QString displayName = Utils::coreStringToAppString(address->getDisplayName()); + if(displayName == "") + displayName = Utils::coreStringToAppString(address->getUsername()); + } + return displayName; +} diff --git a/linphone-app/src/utils/Utils.hpp b/linphone-app/src/utils/Utils.hpp index 12026e207..07dfa20bd 100644 --- a/linphone-app/src/utils/Utils.hpp +++ b/linphone-app/src/utils/Utils.hpp @@ -28,6 +28,8 @@ #include +#include "LinphoneEnums.hpp" + // ============================================================================= @@ -36,75 +38,91 @@ * in switch block. */ #ifndef UTILS_NO_BREAK - #if defined(__GNUC__) && __GNUC__ >= 7 - #define UTILS_NO_BREAK __attribute__((fallthrough)) - #else - #define UTILS_NO_BREAK - #endif // if defined(__GNUC__) && __GNUC__ >= 7 +#if defined(__GNUC__) && __GNUC__ >= 7 +#define UTILS_NO_BREAK __attribute__((fallthrough)) +#else +#define UTILS_NO_BREAK +#endif // if defined(__GNUC__) && __GNUC__ >= 7 #endif // ifndef UTILS_NO_BREAK -namespace Utils { - inline QString coreStringToAppString (const std::string &str) { - return QString::fromLocal8Bit(str.c_str(), int(str.size())); - } - inline std::string appStringToCoreString (const QString &str) { - return qPrintable(str); - } - - // Reverse function of strstr. - char *rstrstr (const char *a, const char *b); - // Return the path if it is an image else an empty path. - QImage getImage(const QString &pUri); - // Returns the same path given in parameter if `filePath` exists. - // Otherwise returns a safe path with a unique number before the extension. - QString getSafeFilePath (const QString &filePath, bool *soFarSoGood = nullptr); - std::shared_ptr getMatchingLocalAddress(std::shared_ptr p_localAddress); - QString cleanSipAddress (const QString &sipAddress);// Return at most : sip:username@domain - // Test if the process exists - bool processExists(const quint64& p_processId); - - // Connect once to a member function. - template - static inline QMetaObject::Connection connectOnce ( - typename QtPrivate::FunctionPointer::Object *sender, - Func1 signal, - typename QtPrivate::FunctionPointer::Object *receiver, - Func2 slot - ) { - QMetaObject::Connection connection = QObject::connect(sender, signal, receiver, slot); - QMetaObject::Connection *deleter = new QMetaObject::Connection(); - - *deleter = QObject::connect(sender, signal, [connection, deleter] { - QObject::disconnect(connection); - QObject::disconnect(*deleter); - delete deleter; - }); - - return connection; - } - - // Connect once to a function. - template - static inline QMetaObject::Connection connectOnce ( - typename QtPrivate::FunctionPointer::Object *sender, - Func1 signal, - const QObject *receiver, - Func2 slot - ) { - QMetaObject::Connection connection = QObject::connect(sender, signal, receiver, slot); - QMetaObject::Connection *deleter = new QMetaObject::Connection(); - - *deleter = QObject::connect(sender, signal, [connection, deleter] { - QObject::disconnect(connection); - QObject::disconnect(*deleter); - delete deleter; - }); - - return connection; - } - QString getCountryName(const QLocale::Country& country); - void copyDir(QString from, QString to);// Copy a folder recursively without erasing old file -} +class Utils : public QObject{ + Q_OBJECT +public: + Utils(QObject * parent = nullptr) : QObject(parent){} + // Qt interfaces + Q_INVOKABLE static bool hasCapability(const QString& address, const LinphoneEnums::FriendCapability& capability); + +//---------------------------------------------------------------------------------- + + static constexpr char WindowIconPath[] = ":/assets/images/linphone_logo.svg"; + + static inline QString coreStringToAppString (const std::string &str) { + return QString::fromLocal8Bit(str.c_str(), int(str.size())); + } + + static inline std::string appStringToCoreString (const QString &str) { + return qPrintable(str); + } + + // Reverse function of strstr. + static char *rstrstr (const char *a, const char *b); + // Return the path if it is an image else an empty path. + static QImage getImage(const QString &pUri); + // Returns the same path given in parameter if `filePath` exists. + // Otherwise returns a safe path with a unique number before the extension. + static QString getSafeFilePath (const QString &filePath, bool *soFarSoGood = nullptr); + static std::shared_ptr getMatchingLocalAddress(std::shared_ptr p_localAddress); + static QString cleanSipAddress (const QString &sipAddress);// Return at most : sip:username@domain + // Test if the process exists + static bool processExists(const quint64& p_processId); + + // Connect once to a member function. + template + static inline QMetaObject::Connection connectOnce ( + typename QtPrivate::FunctionPointer::Object *sender, + Func1 signal, + typename QtPrivate::FunctionPointer::Object *receiver, + Func2 slot + ) { + QMetaObject::Connection connection = QObject::connect(sender, signal, receiver, slot); + QMetaObject::Connection *deleter = new QMetaObject::Connection(); + + *deleter = QObject::connect(sender, signal, [connection, deleter] { + QObject::disconnect(connection); + QObject::disconnect(*deleter); + delete deleter; + }); + + return connection; + } + + // Connect once to a function. + template + static inline QMetaObject::Connection connectOnce ( + typename QtPrivate::FunctionPointer::Object *sender, + Func1 signal, + const QObject *receiver, + Func2 slot + ) { + QMetaObject::Connection connection = QObject::connect(sender, signal, receiver, slot); + QMetaObject::Connection *deleter = new QMetaObject::Connection(); + + *deleter = QObject::connect(sender, signal, [connection, deleter] { + QObject::disconnect(connection); + QObject::disconnect(*deleter); + delete deleter; + }); + + return connection; + } + static linphone::TransportType stringToTransportType (const QString &transport); + static std::shared_ptr interpretUrl(const QString& address); + + + static QString getCountryName(const QLocale::Country& country); + static void copyDir(QString from, QString to);// Copy a folder recursively without erasing old file + static QString getDisplayName(const std::shared_ptr& address); // Get the displayname from addres in this order : Friends, Contact, Display address, Username address +}; #endif // UTILS_H_ diff --git a/linphone-app/ui/modules/Common/Dialog/ConfirmDialog.qml b/linphone-app/ui/modules/Common/Dialog/ConfirmDialog.qml index 7a88e8a82..b040a7b60 100644 --- a/linphone-app/ui/modules/Common/Dialog/ConfirmDialog.qml +++ b/linphone-app/ui/modules/Common/Dialog/ConfirmDialog.qml @@ -19,7 +19,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter height: DialogStyle.confirmDialog.height width: DialogStyle.confirmDialog.width diff --git a/linphone-app/ui/modules/Common/Dialog/DialogPlus.qml b/linphone-app/ui/modules/Common/Dialog/DialogPlus.qml index 5d09cde75..b6f9d4440 100644 --- a/linphone-app/ui/modules/Common/Dialog/DialogPlus.qml +++ b/linphone-app/ui/modules/Common/Dialog/DialogPlus.qml @@ -12,8 +12,10 @@ Rectangle { id: dialog property alias buttons: buttons.data // Optionnal. + property alias title : titleBar.text //Optionnal. Show a title bar with a close button. property alias descriptionText: description.text // Optionnal. - property bool centeredButtons: false + property int buttonsAlignment : Qt.AlignLeft + property bool flat : false // Remove margins default property alias _content: content.data property bool _disableExitStatus @@ -56,35 +58,45 @@ Rectangle { ColumnLayout { anchors.fill: parent spacing: 0 - + + DialogTitle{ + id:titleBar + //Layout.fillHeight: dialog.contentIsEmpty + Layout.fillWidth: true + onClose: exitStatus(0) + + } DialogDescription { id: description Layout.fillHeight: dialog.contentIsEmpty Layout.fillWidth: true + visible: text!='' } Item { id: content - Layout.fillHeight: !dialog.contentIsEmpty + Layout.fillHeight: (flat ? true : !dialog.contentIsEmpty) Layout.fillWidth: true - Layout.leftMargin: DialogStyle.content.leftMargin - Layout.rightMargin: DialogStyle.content.rightMargin + Layout.leftMargin: (flat ? 0 : DialogStyle.content.leftMargin) + Layout.rightMargin: (flat ? 0 : DialogStyle.content.rightMargin) } Row { id: buttons - Layout.alignment: centeredButtons - ? Qt.AlignHCenter - : Qt.AlignLeft + Layout.alignment: buttonsAlignment Layout.bottomMargin: DialogStyle.buttons.bottomMargin - Layout.leftMargin: !centeredButtons + Layout.leftMargin: buttonsAlignment == Qt.AlignLeft ? DialogStyle.buttons.leftMargin - : undefined + : buttonsAlignment == Qt.AlignRight + ? DialogStyle.buttons.rightMargin + : undefined + Layout.rightMargin: DialogStyle.buttons.rightMargin Layout.topMargin: DialogStyle.buttons.topMargin spacing: DialogStyle.buttons.spacing + visible: children.length>0 } } } diff --git a/linphone-app/ui/modules/Common/Dialog/DialogTitle.qml b/linphone-app/ui/modules/Common/Dialog/DialogTitle.qml new file mode 100644 index 000000000..1fd919a41 --- /dev/null +++ b/linphone-app/ui/modules/Common/Dialog/DialogTitle.qml @@ -0,0 +1,53 @@ +import QtQuick 2.7 + +import Common 1.0 +import Linphone 1.0 + +import Common.Styles 1.0 +import Units 1.0 + +// ============================================================================= +// Title bar used by dialogs. +// ============================================================================= + +Item { + property alias text: title.text + signal close() + + height: text ? 30 : undefined + + Rectangle{ + anchors.fill:parent + gradient: Gradient { + GradientStop { position: 0.0; color: "white" } + GradientStop { position: 1.0; color: "#E2E2E2" } + } + } + Text { + id: title + + anchors { + fill: parent + leftMargin: DialogStyle.description.leftMargin + rightMargin: DialogStyle.description.rightMargin + } + + color: DialogStyle.description.color + //font.pointSize: DialogStyle.description.pointSize + font.pointSize: Units.dp * 10 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + } + ActionButton{ + anchors.right:parent.right + anchors.rightMargin: 14 + anchors.top:parent.top + anchors.bottom:parent.bottom + icon: 'close' + iconSize: 12 + useStates: false + + onClicked: close() + } +} diff --git a/linphone-app/ui/modules/Common/Form/ActionButton.qml b/linphone-app/ui/modules/Common/Form/ActionButton.qml index 1d03184de..c663fa0a7 100644 --- a/linphone-app/ui/modules/Common/Form/ActionButton.qml +++ b/linphone-app/ui/modules/Common/Form/ActionButton.qml @@ -1,5 +1,6 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.12 import Common 1.0 @@ -15,9 +16,11 @@ Item { property bool enabled: true property bool updating: false property bool useStates: true + //property bool autoIcon : false // hovered/pressed : use an automatic layer instead of specific icon image property int iconSize // Optional. readonly property alias hovered: button.hovered property alias text: button.text + property alias tooltipText : tooltip.text // If `useStates` = true, the used icons are: // `icon`_pressed, `icon`_hovered and `icon`_normal. @@ -41,12 +44,14 @@ Item { if (!wrappedButton.enabled) { return wrappedButton.icon + '_disabled' } - - return wrappedButton.icon + ( - button.down - ? '_pressed' - : (button.hovered ? '_hovered' : '_normal') - ) + // if(!autoIcon) { + return wrappedButton.icon + ( + button.down + ? '_pressed' + : (button.hovered ? '_hovered' : '_normal') + ) + // } + // return wrappedButton.icon; } // --------------------------------------------------------------------------- @@ -61,7 +66,7 @@ Item { background: Rectangle { color: 'transparent' } - hoverEnabled: !wrappedButton.updating + hoverEnabled: !wrappedButton.updating//|| wrappedButton.autoIcon onClicked: !wrappedButton.updating && wrappedButton.enabled && wrappedButton.clicked() @@ -73,6 +78,67 @@ Item { iconSize: wrappedButton.iconSize || ( parent.width > parent.height ? parent.height : parent.width ) + + + /* + Rectangle { + anchors.fill:parent + color: button.down?'white':'black' + opacity: 0.2 + visible:autoIcon && (button.down || button.hovered) + }*/ + } + /* + Colorize{ + anchors.fill:icon + source:icon + hue:0.0 + saturation:0.0 + lightness: 0.5 + visible:autoIcon && button.down + }*/ + /* + GammaAdjust{ + anchors.fill:icon + source:icon + gamma:1.6 + visible:autoIcon && button.down + }*/ + /* + Colorize{ + anchors.fill:icon + source:icon + hue:0.0 + saturation:0.0 + lightness: -0.5 + visible:autoIcon && button.hovered && !button.down + }*/ + /* + Desaturate{ + anchors.fill:icon + source:icon + desaturation: 1.0 + visible:autoIcon && button.hovered && !button.down + }*/ + /* + GammaAdjust{ + anchors.fill:icon + source:icon + gamma:0.4 + visible:autoIcon && button.hovered && !button.down + }*/ + /* + ColorOverlay{ + anchors.fill:icon + source:icon + color:button.down?'white':'orange' + visible:autoIcon && (button.down || button.hovered) + }*/ + TooltipArea { + id:tooltip + text: '' + visible:text!='' } } + } diff --git a/linphone-app/ui/modules/Common/Form/Buttons/AbstractTextButton.qml b/linphone-app/ui/modules/Common/Form/Buttons/AbstractTextButton.qml index 2500b2d3f..700d3c692 100644 --- a/linphone-app/ui/modules/Common/Form/Buttons/AbstractTextButton.qml +++ b/linphone-app/ui/modules/Common/Form/Buttons/AbstractTextButton.qml @@ -49,8 +49,8 @@ Item { // --------------------------------------------------------------------------- - height: AbstractTextButtonStyle.background.height - width: AbstractTextButtonStyle.background.width + height: button.contentItem.implicitHeight + 20//AbstractTextButtonStyle.background.height + width: button.contentItem.implicitWidth +60 //AbstractTextButtonStyle.background.width // --------------------------------------------------------------------------- @@ -68,8 +68,8 @@ Item { bold: true pointSize: AbstractTextButtonStyle.text.pointSize } - - elide: Text.ElideRight + wrapMode: Text.WordWrap + //elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter text: button.text verticalAlignment: Text.AlignVCenter diff --git a/linphone-app/ui/modules/Common/Form/Buttons/FileChooserButton.qml b/linphone-app/ui/modules/Common/Form/Buttons/FileChooserButton.qml index e04524890..86e5e5949 100644 --- a/linphone-app/ui/modules/Common/Form/Buttons/FileChooserButton.qml +++ b/linphone-app/ui/modules/Common/Form/Buttons/FileChooserButton.qml @@ -37,7 +37,7 @@ TextField { Rectangle { anchors { fill: parent - margins: TextFieldStyle.background.border.width + margins: TextFieldStyle.normal.background.border.width } color: mouseArea.pressed diff --git a/linphone-app/ui/modules/Common/Form/DroppableTextArea.qml b/linphone-app/ui/modules/Common/Form/DroppableTextArea.qml index b35c77cfc..2fdf02fbc 100644 --- a/linphone-app/ui/modules/Common/Form/DroppableTextArea.qml +++ b/linphone-app/ui/modules/Common/Form/DroppableTextArea.qml @@ -1,6 +1,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.12 import Common 1.0 import Common.Styles 1.0 @@ -9,166 +10,239 @@ import Utils 1.0 // ============================================================================= Item { - id: droppableTextArea - - property alias placeholderText: textArea.placeholderText - property alias text: textArea.text - property alias cursorPosition: textArea.cursorPosition - - property bool dropEnabled: true - property string dropDisabledReason - - // --------------------------------------------------------------------------- - - signal dropped (var files) - signal validText (string text) - - // --------------------------------------------------------------------------- - - function _emitFiles (files) { - // Filtering files, other urls are forbidden. - files = files.reduce(function (files, file) { - if (file.startsWith('file:')) { - files.push(Utils.getSystemPathFromUri(file)) - } - - return files - }, []) - - if (files.length > 0) { - dropped(files) - } - } - - // --------------------------------------------------------------------------- - - // Text area. - Flickable { - anchors.fill: parent - boundsBehavior: Flickable.StopAtBounds - - ScrollBar.vertical: ForceScrollBar { - id: scrollBar - } - - TextArea.flickable: TextArea { - id: textArea - - function handleValidation () { - if (text.length !== 0) { - validText(text) - } - } - - background: Rectangle { - color: DroppableTextAreaStyle.backgroundColor - } - - color: DroppableTextAreaStyle.text.color - font.pointSize: DroppableTextAreaStyle.text.pointSize - rightPadding: fileChooserButton.width + - fileChooserButton.anchors.rightMargin + - DroppableTextAreaStyle.fileChooserButton.margins - selectByMouse: true - wrapMode: TextArea.Wrap - - // Workaround. Without this line, the scrollbar is not linked correctly - // to the text area. - width: parent.width - - Component.onCompleted: forceActiveFocus() - - property var isAutoRepeating : false // shutdown repeating key feature to let optional menu appears and do normal stuff (like accents menu) - Keys.onReleased: { - if( event.isAutoRepeat){// We begin or are currently repeating a key - if(!isAutoRepeating){// We start repeat. Check if this is an "ignore" character - if(event.key > Qt.Key_Any && event.key <= Qt.Key_ydiaeresis)// Remove the previous character if it is a printable character - textArea.remove(cursorPosition-1, cursorPosition) + id: droppableTextArea + + property int minimumHeight + property int maximumHeight + + property alias placeholderText: textArea.placeholderText + property alias text: textArea.text + property alias cursorPosition: textArea.cursorPosition + + property bool dropEnabled: true + property string dropDisabledReason + + // --------------------------------------------------------------------------- + + signal dropped (var files) + signal validText (string text) + + // --------------------------------------------------------------------------- + + function _emitFiles (files) { + // Filtering files, other urls are forbidden. + files = files.reduce(function (files, file) { + if (file.startsWith('file:')) { + files.push(Utils.getSystemPathFromUri(file)) } - }else - isAutoRepeating = false// We are no more repeating. Final decision is done on Releasing - } - Keys.onPressed: { - if(event.isAutoRepeat){ - isAutoRepeating = true// Where are repeating the key. Set the state. - if(event.key > Qt.Key_Any && event.key <= Qt.Key_ydiaeresis){// Ignore character if it is repeating and printable character - event.accepted = true + + return files + }, []) + + if (files.length > 0) { + dropped(files) } - }else if (event.matches(StandardKey.InsertLineSeparator)) { - insert(cursorPosition, '') - } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { - handleValidation() - event.accepted = true - } - } - } - } + } + + Rectangle{ + anchors.fill: parent + color:'#E1E1E1' + // --------------------------------------------------------------------------- + RowLayout{ + anchors.fill: parent + spacing: DroppableTextAreaStyle.fileChooserButton.margins + // Handle click to select files. + ActionButton { + id: fileChooserButton + + Layout.leftMargin: DroppableTextAreaStyle.fileChooserButton.margins + Layout.alignment: Qt.AlignVCenter + //anchors.verticalCenter: parent.verticalCenter + enabled: droppableTextArea.dropEnabled + icon: 'attachment' + iconSize: DroppableTextAreaStyle.fileChooserButton.size + + onClicked: fileDialog.open() + + FileDialog { + id: fileDialog + + folder: shortcuts.home + title: qsTr('fileChooserTitle') + + onAccepted: _emitFiles(fileDialog.fileUrls) + } + tooltipText: droppableTextArea.dropEnabled + ? qsTr('attachmentTooltip') + : droppableTextArea.dropDisabledReason + /* + TooltipArea { + text: droppableTextArea.dropEnabled + ? qsTr('attachmentTooltip') + : droppableTextArea.dropDisabledReason + }*/ + } + // Record audio + ActionButton { + id: recordAudioButton + + //anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter - // Handle click to select files. - ActionButton { - id: fileChooserButton + enabled: droppableTextArea.dropEnabled + icon: 'micro' + iconSize: DroppableTextAreaStyle.fileChooserButton.size + useStates:false + + onClicked: {console.log('Record audio request')} + + } + + // Text area. + Flickable { + id:flickableArea + Layout.fillWidth: true + Layout.fillHeight: true + Layout.maximumHeight: parent.height-20 + Layout.topMargin: 10 + Layout.bottomMargin: 10 + //anchors.fill: parent + boundsBehavior: Flickable.StopAtBounds + clip:true + + ScrollBar.vertical: ForceScrollBar { + id: scrollBar + visible:false + } + + TextArea.flickable: TextArea { + id: textArea + onLineCountChanged: { + if(textArea.contentHeight+20 Qt.Key_Any && event.key <= Qt.Key_ydiaeresis)// Remove the previous character if it is a printable character + textArea.remove(cursorPosition-1, cursorPosition) + } + }else + isAutoRepeating = false// We are no more repeating. Final decision is done on Releasing + } + Keys.onPressed: { + if(event.isAutoRepeat){ + isAutoRepeating = true// Where are repeating the key. Set the state. + if(event.key > Qt.Key_Any && event.key <= Qt.Key_ydiaeresis){// Ignore character if it is repeating and printable character + event.accepted = true + } + }else if (event.matches(StandardKey.InsertLineSeparator)) { + insert(cursorPosition, '') + } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + handleValidation() + event.accepted = true + } + } + } + } + + // Handle click to select files. + ActionButton { + id: sendButton + Layout.rightMargin: DroppableTextAreaStyle.fileChooserButton.margins + Layout.alignment: Qt.AlignVCenter + //anchors.verticalCenter: parent.verticalCenter + /*{ + right: parent.right + rightMargin: scrollBar.width + + DroppableTextAreaStyle.fileChooserButton.margins + verticalCenter: parent.verticalCenter + }*/ + enabled: droppableTextArea.dropEnabled + icon: 'send' + iconSize: DroppableTextAreaStyle.fileChooserButton.size + useStates:false + onClicked: handleValidation() + } + }/* + MouseArea{ + anchors.top:parent.top + anchors.verticalCenter: parent.verticalCenter + //icon: 'burger_menu' + //iconSize: 5 + }*/ + // Hovered style. + Rectangle { + id: hoverContent + + anchors.fill: parent + color: DroppableTextAreaStyle.hoverContent.backgroundColor + visible: false + + Text { + anchors.centerIn: parent + color: DroppableTextAreaStyle.hoverContent.text.color + font.pointSize: DroppableTextAreaStyle.hoverContent.text.pointSize + text: qsTr('dropYourAttachment') + } + } + DropArea { + anchors.fill: parent + keys: [ 'text/uri-list' ] + visible: droppableTextArea.dropEnabled + + onDropped: { + state = '' + if (drop.hasUrls) { + _emitFiles(drop.urls) + } + } + onEntered: state = 'hover' + onExited: state = '' + + states: State { + name: 'hover' + PropertyChanges { target: hoverContent; visible: true } + } + } + } } diff --git a/linphone-app/ui/modules/Common/Form/Fields/HexField.qml b/linphone-app/ui/modules/Common/Form/Fields/HexField.qml index 6e0b41b09..b08b6de93 100644 --- a/linphone-app/ui/modules/Common/Form/Fields/HexField.qml +++ b/linphone-app/ui/modules/Common/Form/Fields/HexField.qml @@ -25,7 +25,7 @@ Item { // --------------------------------------------------------------------------- implicitHeight: textField.height - width: TextFieldStyle.background.width + width: TextFieldStyle.normal.background.width // --------------------------------------------------------------------------- diff --git a/linphone-app/ui/modules/Common/Form/Fields/NumericField.qml b/linphone-app/ui/modules/Common/Form/Fields/NumericField.qml index f6fa97847..06f2a1b96 100644 --- a/linphone-app/ui/modules/Common/Form/Fields/NumericField.qml +++ b/linphone-app/ui/modules/Common/Form/Fields/NumericField.qml @@ -52,7 +52,7 @@ TextField { anchors { fill: parent - margins: TextFieldStyle.background.border.width + margins: TextFieldStyle.normal.background.border.width } // ----------------------------------------------------------------------- diff --git a/linphone-app/ui/modules/Common/Form/Fields/PortField.qml b/linphone-app/ui/modules/Common/Form/Fields/PortField.qml index 92f7c3ca3..a13f63f42 100644 --- a/linphone-app/ui/modules/Common/Form/Fields/PortField.qml +++ b/linphone-app/ui/modules/Common/Form/Fields/PortField.qml @@ -48,7 +48,7 @@ Item { // --------------------------------------------------------------------------- implicitHeight: textField.height - width: TextFieldStyle.background.width + width: TextFieldStyle.normal.background.width // --------------------------------------------------------------------------- diff --git a/linphone-app/ui/modules/Common/Form/Fields/ScrollableListViewField.qml b/linphone-app/ui/modules/Common/Form/Fields/ScrollableListViewField.qml index 180f1b1da..d877183ff 100644 --- a/linphone-app/ui/modules/Common/Form/Fields/ScrollableListViewField.qml +++ b/linphone-app/ui/modules/Common/Form/Fields/ScrollableListViewField.qml @@ -11,9 +11,11 @@ Rectangle { property bool readOnly: false default property alias _content: content.data + + property QtObject textFieldStyle : TextFieldStyle.normal - color: TextFieldStyle.background.color.normal - radius: TextFieldStyle.background.radius + color: textFieldStyle.background.color.normal + radius: textFieldStyle.background.radius Item { id: content @@ -25,8 +27,8 @@ Rectangle { anchors.fill: parent border { - color: TextFieldStyle.background.border.color.normal - width: TextFieldStyle.background.border.width + color: textFieldStyle.background.border.color.normal + width: textFieldStyle.background.border.width } color: 'transparent' @@ -35,7 +37,7 @@ Rectangle { Rectangle { anchors.fill: parent - color: TextFieldStyle.background.color.readOnly + color: textFieldStyle.background.color.readOnly opacity: 0.8 visible: field.readOnly } diff --git a/linphone-app/ui/modules/Common/Form/Fields/TextField.qml b/linphone-app/ui/modules/Common/Form/Fields/TextField.qml index ff86e8d9b..19f503ab0 100644 --- a/linphone-app/ui/modules/Common/Form/Fields/TextField.qml +++ b/linphone-app/ui/modules/Common/Form/Fields/TextField.qml @@ -16,29 +16,30 @@ Controls.TextField { property alias icon: icon.icon property string error: '' property var tools + property QtObject textFieldStyle : TextFieldStyle.normal // --------------------------------------------------------------------------- background: Rectangle { border { color: textField.error.length > 0 - ? TextFieldStyle.background.border.color.error + ? textFieldStyle.background.border.color.error : ( textField.activeFocus && !textField.readOnly - ? TextFieldStyle.background.border.color.selected - : TextFieldStyle.background.border.color.normal + ? textFieldStyle.background.border.color.selected + : textFieldStyle.background.border.color.normal ) - width: TextFieldStyle.background.border.width + width: textFieldStyle.background.border.width } color: textField.readOnly - ? TextFieldStyle.background.color.readOnly - : TextFieldStyle.background.color.normal + ? textFieldStyle.background.color.readOnly + : textFieldStyle.background.color.normal - implicitHeight: TextFieldStyle.background.height - implicitWidth: TextFieldStyle.background.width + implicitHeight: textFieldStyle.background.height + implicitWidth: textFieldStyle.background.width - radius: TextFieldStyle.background.radius + radius: textFieldStyle.background.radius MouseArea { anchors.right: parent.right @@ -51,9 +52,9 @@ Controls.TextField { border { color: textField.error.length > 0 - ? TextFieldStyle.background.border.color.error - : TextFieldStyle.background.border.color.normal - width: TextFieldStyle.background.border.width + ? textFieldStyle.background.border.color.error + : textFieldStyle.background.border.color.normal + width: textFieldStyle.background.border.width } anchors.fill: parent @@ -63,23 +64,13 @@ Controls.TextField { } } - color: TextFieldStyle.text.color - font.pointSize: TextFieldStyle.text.pointSize - rightPadding: TextFieldStyle.text.rightPadding + toolsContainer.width + color: textFieldStyle.text.color + font.pointSize: textFieldStyle.text.pointSize + rightPadding: textFieldStyle.text.rightPadding + toolsContainer.width selectByMouse: true // --------------------------------------------------------------------------- - onEditingFinished: cursorPosition = 0 - - onTextChanged: { - if (!focus) { - cursorPosition = 0 - } - } - - // --------------------------------------------------------------------------- - Icon { id: icon @@ -92,20 +83,22 @@ Controls.TextField { iconSize: parent.contentHeight visible: !parent.text } - bottomPadding: (statusItem.visible?statusItem.height:0) + bottomPadding: (statusItem.visible?statusItem.height:2) TextEdit{ id:statusItem selectByMouse: true readOnly:true - color: TextFieldStyle.background.border.color.error + color: textFieldStyle.background.border.color.error width:parent.width anchors.bottom:parent.bottom + anchors.bottomMargin: 2 anchors.right:parent.right anchors.rightMargin:10 + toolsContainer.width horizontalAlignment:Text.AlignRight + verticalAlignment: Text.AlignVCenter font { italic: true - pointSize: TextFieldStyle.text.pointSize + pointSize: textFieldStyle.text.pointSize } visible:error!= '' text:error diff --git a/linphone-app/ui/modules/Common/Form/Switch.qml b/linphone-app/ui/modules/Common/Form/Switch.qml index e4a922f61..947043a17 100644 --- a/linphone-app/ui/modules/Common/Form/Switch.qml +++ b/linphone-app/ui/modules/Common/Form/Switch.qml @@ -12,6 +12,7 @@ Controls.Switch { // --------------------------------------------------------------------------- property bool enabled: true + property QtObject indicatorStyle : SwitchStyle.normal // --------------------------------------------------------------------------- @@ -22,32 +23,32 @@ Controls.Switch { checked: false indicator: Rectangle { - implicitHeight: SwitchStyle.indicator.height - implicitWidth: SwitchStyle.indicator.width + implicitHeight: indicatorStyle.indicator.height + implicitWidth: indicatorStyle.indicator.width border.color: control.enabled ? ( control.checked - ? SwitchStyle.indicator.border.color.checked - : SwitchStyle.indicator.border.color.normal - ) : SwitchStyle.indicator.border.color.disabled + ? indicatorStyle.indicator.border.color.checked + : indicatorStyle.indicator.border.color.normal + ) : indicatorStyle.indicator.border.color.disabled color: control.enabled ? ( control.checked - ? SwitchStyle.indicator.color.checked - : SwitchStyle.indicator.color.normal - ) : SwitchStyle.indicator.color.disabled + ? indicatorStyle.indicator.color.checked + : indicatorStyle.indicator.color.normal + ) : indicatorStyle.indicator.color.disabled - radius: SwitchStyle.indicator.radius + radius: indicatorStyle.indicator.radius x: control.leftPadding y: parent.height / 2 - height / 2 Rectangle { id: sphere - height: SwitchStyle.sphere.size - width: SwitchStyle.sphere.size + height: indicatorStyle.sphere.size + width: indicatorStyle.sphere.size anchors.verticalCenter: parent.verticalCenter @@ -57,18 +58,18 @@ Controls.Switch { control.checked ? ( control.down - ? SwitchStyle.sphere.border.color.pressed - : SwitchStyle.sphere.border.color.checked - ) : SwitchStyle.sphere.border.color.normal - ) : SwitchStyle.sphere.border.color.disabled + ? indicatorStyle.sphere.border.color.pressed + : indicatorStyle.sphere.border.color.checked + ) : indicatorStyle.sphere.border.color.normal + ) : indicatorStyle.sphere.border.color.disabled color: control.enabled ? ( control.down - ? SwitchStyle.sphere.color.pressed - : SwitchStyle.sphere.color.normal - ) : SwitchStyle.sphere.color.disabled + ? indicatorStyle.sphere.color.pressed + : indicatorStyle.sphere.color.normal + ) : indicatorStyle.sphere.color.disabled radius: width / 2 @@ -87,7 +88,7 @@ Controls.Switch { NumberAnimation { properties: 'x' - duration: SwitchStyle.animation.duration + duration: indicatorStyle.animation.duration easing.type: Easing.InOutQuad } } @@ -99,7 +100,7 @@ Controls.Switch { MouseArea { anchors.fill: parent - onClicked: control.enabled && control.clicked() + onClicked: control.enabled && control.clicked() onPressed: control.enabled && control.forceActiveFocus() } } diff --git a/linphone-app/ui/modules/Common/Image/Icon.qml b/linphone-app/ui/modules/Common/Image/Icon.qml index 8dd24b2ff..f63c8c668 100644 --- a/linphone-app/ui/modules/Common/Image/Icon.qml +++ b/linphone-app/ui/modules/Common/Image/Icon.qml @@ -18,7 +18,7 @@ Item { mipmap: Qt.platform.os === 'osx' function getIconSize () { Utils.assert( - iconSize != null && iconSize >= 0, + (icon == null || icon == '' || iconSize != null && iconSize >= 0), '`iconSize` must be defined and must be positive. (icon=`' + icon + '`, iconSize=' + iconSize + ')' ) diff --git a/linphone-app/ui/modules/Common/Menus/ApplicationMenuEntry.qml b/linphone-app/ui/modules/Common/Menus/ApplicationMenuEntry.qml index ebdee78a7..3df2b1f02 100644 --- a/linphone-app/ui/modules/Common/Menus/ApplicationMenuEntry.qml +++ b/linphone-app/ui/modules/Common/Menus/ApplicationMenuEntry.qml @@ -15,6 +15,7 @@ Rectangle { property alias name: text.text readonly property bool isSelected: parent.parent._selected === this + property alias iconSize : mainIcon.iconSize // --------------------------------------------------------------------------- @@ -56,6 +57,7 @@ Rectangle { spacing: ApplicationMenuStyle.entry.spacing Icon { + id:mainIcon icon: entry.icon + ( entry.isSelected ? '_selected' diff --git a/linphone-app/ui/modules/Common/Menus/MenuItem.qml b/linphone-app/ui/modules/Common/Menus/MenuItem.qml index 368a7d414..ff8028071 100644 --- a/linphone-app/ui/modules/Common/Menus/MenuItem.qml +++ b/linphone-app/ui/modules/Common/Menus/MenuItem.qml @@ -1,43 +1,61 @@ import QtQuick 2.7 -import QtQuick.Controls 2.2 +import QtQuick.Controls 2.2 as Controls + +import Common 1.0 import Common.Styles 1.0 // ============================================================================= -MenuItem { - id: button +// Using MenuItem.icon doesn't seem to work as of 07/2021 +// Workaround : Implement own Icon - background: Rectangle { - color: button.down - ? MenuItemStyle.background.color.pressed - : ( - button.hovered - ? MenuItemStyle.background.color.hovered - : MenuItemStyle.background.color.normal - ) - implicitHeight: MenuItemStyle.background.height - } - - contentItem: Text { - color: button.enabled - ? MenuItemStyle.text.color.enabled - : MenuItemStyle.text.color.disabled - - elide: Text.ElideRight - - font { - bold: true - pointSize: MenuItemStyle.text.pointSize - } - - text: button.text - - leftPadding: MenuItemStyle.leftPadding - rightPadding: MenuItemStyle.rightPadding - - verticalAlignment: Text.AlignVCenter - } - - hoverEnabled: true +Controls.MenuItem { + id: button + property alias iconMenu : iconArea.icon + property alias iconSizeMenu : iconArea.iconSize + + background: Rectangle { + color: button.down + ? MenuItemStyle.background.color.pressed + : ( + button.hovered + ? MenuItemStyle.background.color.hovered + : MenuItemStyle.background.color.normal + ) + implicitHeight: MenuItemStyle.background.height + } + contentItem:Row{ + leftPadding: MenuItemStyle.leftPadding + rightPadding: MenuItemStyle.rightPadding + spacing:20 + Icon{ + id: iconArea + visible: icon + width: icon?iconSize:0 + } + Text { + color: button.enabled + ? MenuItemStyle.text.color.enabled + : MenuItemStyle.text.color.disabled + + elide: Text.ElideRight + + font { + bold: true + pointSize: MenuItemStyle.text.pointSize + } + + text: button.text + + //leftPadding: MenuItemStyle.leftPadding + //rightPadding: MenuItemStyle.rightPadding + + verticalAlignment: Text.AlignVCenter + anchors.top: parent.top + anchors.bottom : parent.bottom + } + } + + hoverEnabled: true } diff --git a/linphone-app/ui/modules/Common/Misc/ForceScrollBar.qml b/linphone-app/ui/modules/Common/Misc/ForceScrollBar.qml index cbbdbd653..48c1e3661 100644 --- a/linphone-app/ui/modules/Common/Misc/ForceScrollBar.qml +++ b/linphone-app/ui/modules/Common/Misc/ForceScrollBar.qml @@ -12,7 +12,8 @@ ScrollBar { background: Rectangle { anchors.fill: parent - color: ForceScrollBarStyle.backgroundColor + color: ForceScrollBarStyle.background.color + radius: ForceScrollBarStyle.background.radius } contentItem: Rectangle { color: scrollBar.pressed diff --git a/linphone-app/ui/modules/Common/Styles/Dialog/DialogStyle.qml b/linphone-app/ui/modules/Common/Styles/Dialog/DialogStyle.qml index 7e3dde85a..6b9e444e7 100644 --- a/linphone-app/ui/modules/Common/Styles/Dialog/DialogStyle.qml +++ b/linphone-app/ui/modules/Common/Styles/Dialog/DialogStyle.qml @@ -12,6 +12,7 @@ QtObject { property QtObject buttons: QtObject { property int bottomMargin: 15 property int leftMargin: 50 + property int rightMargin: 50 property int spacing: 20 property int topMargin: 15 } diff --git a/linphone-app/ui/modules/Common/Styles/Form/Buttons/TextButtonAStyle.qml b/linphone-app/ui/modules/Common/Styles/Form/Buttons/TextButtonAStyle.qml index fc2f5f50e..68c4372d2 100644 --- a/linphone-app/ui/modules/Common/Styles/Form/Buttons/TextButtonAStyle.qml +++ b/linphone-app/ui/modules/Common/Styles/Form/Buttons/TextButtonAStyle.qml @@ -9,7 +9,7 @@ QtObject { property QtObject backgroundColor: QtObject { property color disabled: Colors.o property color hovered: Colors.j - property color normal: Colors.g + property color normal: Colors.r property color pressed: Colors.i } diff --git a/linphone-app/ui/modules/Common/Styles/Form/Fields/TextFieldStyle.qml b/linphone-app/ui/modules/Common/Styles/Form/Fields/TextFieldStyle.qml index 837fa28d7..6463c568e 100644 --- a/linphone-app/ui/modules/Common/Styles/Form/Fields/TextFieldStyle.qml +++ b/linphone-app/ui/modules/Common/Styles/Form/Fields/TextFieldStyle.qml @@ -5,33 +5,63 @@ import Colors 1.0 import Units 1.0 // ============================================================================= - QtObject { - property QtObject background: QtObject { - property int height: 36 - property int width: 200 - - property int radius: 4 - - property QtObject border: QtObject { - property QtObject color: QtObject { - property color error: Colors.error - property color normal: Colors.c - property color selected: Colors.i - } - - property int width: 1 - } - - property QtObject color: QtObject { - property color normal: Colors.q - property color readOnly: Colors.e - } - } - - property QtObject text: QtObject { - property color color: Colors.d - property int pointSize: Units.dp * 10 - property int rightPadding: 10 - } + property QtObject normal : QtObject { + property QtObject background: QtObject { + property int height: 36 + property int width: 200 + + property int radius: 4 + + property QtObject border: QtObject { + property QtObject color: QtObject { + property color error: Colors.error + property color normal: Colors.c + property color selected: Colors.i + } + + property int width: 1 + } + + property QtObject color: QtObject { + property color normal: Colors.q + property color readOnly: Colors.e + } + } + + property QtObject text: QtObject { + property color color: Colors.d + property int pointSize: Units.dp * 10 + property int rightPadding: 10 + } + } + property QtObject unbordered : QtObject { + property QtObject background: QtObject { + property int height: 36 + property int width: 200 + + property int radius: 4 + + property QtObject border: QtObject { + property QtObject color: QtObject { + property color error: Colors.error + property color normal: Colors.c + property color selected: Colors.i + } + + property int width: 0 + } + + property QtObject color: QtObject { + property color normal: Colors.q + property color readOnly: Colors.e + } + } + + property QtObject text: QtObject { + property color color: Colors.d + property int pointSize: Units.dp * 10 + property int rightPadding: 10 + } + } } diff --git a/linphone-app/ui/modules/Common/Styles/Form/SwitchStyle.qml b/linphone-app/ui/modules/Common/Styles/Form/SwitchStyle.qml index 7f060efbf..b426408a1 100644 --- a/linphone-app/ui/modules/Common/Styles/Form/SwitchStyle.qml +++ b/linphone-app/ui/modules/Common/Styles/Form/SwitchStyle.qml @@ -4,47 +4,91 @@ import QtQml 2.2 import Colors 1.0 // ============================================================================= - -QtObject { - property QtObject animation: QtObject { - property int duration: 200 - } - - property QtObject indicator: QtObject { - property int height: 18 - property int radius: 10 - property int width: 48 - property QtObject border: QtObject { - property QtObject color: QtObject { - property color checked: Colors.i - property color disabled: Colors.c - property color normal: Colors.c - } - } - - property QtObject color: QtObject { - property color checked: Colors.i - property color disabled: Colors.e - property color normal: Colors.q - } - } - - property QtObject sphere: QtObject { - property int size: 22 - - property QtObject border: QtObject { - property QtObject color: QtObject { - property color checked: Colors.i - property color disabled: Colors.c - property color normal: Colors.n - property color pressed: Colors.n - } - } - - property QtObject color: QtObject { - property color disabled: Colors.e - property color pressed: Colors.c - property color normal: Colors.q - } - } +QtObject{ + property QtObject normal :QtObject { + property QtObject animation: QtObject { + property int duration: 200 + } + + property QtObject indicator: QtObject { + property int height: 18 + property int radius: 10 + property int width: 48 + property QtObject border: QtObject { + property QtObject color: QtObject { + property color checked: Colors.i + property color disabled: Colors.c + property color normal: Colors.c + } + } + + property QtObject color: QtObject { + property color checked: Colors.i + property color disabled: Colors.e + property color normal: Colors.q + } + } + + property QtObject sphere: QtObject { + property int size: 22 + + property QtObject border: QtObject { + property QtObject color: QtObject { + property color checked: Colors.i + property color disabled: Colors.c + property color normal: Colors.n + property color pressed: Colors.n + } + } + + property QtObject color: QtObject { + property color pressed: Colors.c + property color disabled: Colors.e + property color normal: Colors.q + } + } + } + property QtObject aux : QtObject{ + property QtObject animation: QtObject { + property int duration: 200 + } + + property QtObject indicator: QtObject { + property int height: 18 + property int radius: 10 + property int width: 48 + property QtObject border: QtObject { + property QtObject color: QtObject { + property color checked: '#96be64' + property color disabled: Colors.c + property color normal: Colors.c + } + } + + property QtObject color: QtObject { + property color checked: '#96be64' + property color disabled: Colors.e + property color normal: Colors.q + } + } + + property QtObject sphere: QtObject { + property int size: 22 + + property QtObject border: QtObject { + property QtObject color: QtObject { + property color checked: '#96be64' + property color disabled: Colors.c + property color normal: Colors.n + property color pressed: Colors.n + } + } + + property QtObject color: QtObject { + property color pressed: Colors.c + property color disabled: Colors.e + property color normal: Colors.q + } + } + } } diff --git a/linphone-app/ui/modules/Common/Styles/Misc/ForceScrollBarStyle.qml b/linphone-app/ui/modules/Common/Styles/Misc/ForceScrollBarStyle.qml index 4c8e1fc76..9ce327dbf 100644 --- a/linphone-app/ui/modules/Common/Styles/Misc/ForceScrollBarStyle.qml +++ b/linphone-app/ui/modules/Common/Styles/Misc/ForceScrollBarStyle.qml @@ -6,17 +6,20 @@ import Colors 1.0 // ============================================================================= QtObject { - property color backgroundColor: Colors.g20 - - property QtObject contentItem: QtObject { - property int implicitHeight: 8 - property int implicitWidth: 8 - property int radius: 10 - } - - property QtObject color: QtObject { - property color hovered: Colors.h - property color normal: Colors.g20 - property color pressed: Colors.d - } + property QtObject background : QtObject { + property color color: Colors.g20 + property int radius : 10 + } + + property QtObject contentItem: QtObject { + property int implicitHeight: 8 + property int implicitWidth: 8 + property int radius: 10 + } + + property QtObject color: QtObject { + property color hovered: Colors.h + property color normal: Colors.g20 + property color pressed: Colors.d + } } diff --git a/linphone-app/ui/modules/Common/Tooltip/TooltipArea.qml b/linphone-app/ui/modules/Common/Tooltip/TooltipArea.qml index ee7094a45..3b559c048 100644 --- a/linphone-app/ui/modules/Common/Tooltip/TooltipArea.qml +++ b/linphone-app/ui/modules/Common/Tooltip/TooltipArea.qml @@ -14,7 +14,7 @@ MouseArea { property var tooltipParent: parent property bool _visible: false - property int hoveringCursor : Qt.ArrowCursor + property int hoveringCursor : Qt.PointingHandCursor anchors.fill:parent diff --git a/linphone-app/ui/modules/Common/View/ScrollableListView.qml b/linphone-app/ui/modules/Common/View/ScrollableListView.qml index da4f6020a..77ce55b00 100644 --- a/linphone-app/ui/modules/Common/View/ScrollableListView.qml +++ b/linphone-app/ui/modules/Common/View/ScrollableListView.qml @@ -14,13 +14,14 @@ ListView { id: vScrollBar onPressedChanged: pressed ? view.movementStarted() : view.movementEnded() + visible:view.contentHeight > view.height } // --------------------------------------------------------------------------- boundsBehavior: Flickable.StopAtBounds clip: true - contentWidth: width - vScrollBar.width + contentWidth: width - (vScrollBar.visible?vScrollBar.width:0) spacing: 0 // --------------------------------------------------------------------------- diff --git a/linphone-app/ui/modules/Linphone/Account/AccountStatus (copy).qml b/linphone-app/ui/modules/Linphone/Account/AccountStatus (copy).qml new file mode 100644 index 000000000..60e122aae --- /dev/null +++ b/linphone-app/ui/modules/Linphone/Account/AccountStatus (copy).qml @@ -0,0 +1,118 @@ +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +import Linphone.Styles 1.0 + +// ============================================================================= + +Item { + id: accountStatus + + // --------------------------------------------------------------------------- + + signal clicked + property alias cursorShape:mouseArea.cursorShape + property alias betterIcon : presenceLevel.betterIcon + + // --------------------------------------------------------------------------- + + Row { + anchors.fill: parent + + Column { + //Layout.fillWidth: true + //Layout.fillHeight: true + anchors.fill:parent + spacing: AccountStatusStyle.verticalSpacing + + Row { + height: parent.height / 2 + spacing: AccountStatusStyle.horizontalSpacing + //width: parent.width + + Item { + //Layout.alignment: Qt.AlignBottom + //Layout.bottomMargin: AccountStatusStyle.presenceLevel.bottomMargin + //Layout.preferredHeight: AccountStatusStyle.presenceLevel.size + //Layout.preferredWidth: AccountStatusStyle.presenceLevel.size + height: AccountStatusStyle.presenceLevel.size + width: AccountStatusStyle.presenceLevel.size + anchors.bottom:parent.bottom + anchors.bottomMargin: AccountStatusStyle.presenceLevel.bottomMargin + + PresenceLevel { + id:presenceLevel + anchors.fill: parent + level: OwnPresenceModel.presenceStatus===Presence.Offline?Presence.White:( SettingsModel.rlsUriEnabled ? OwnPresenceModel.presenceLevel : Presence.Green) + visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateRegistered + } + + BusyIndicator { + anchors.fill: parent + running: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateInProgress + } + + Icon { + iconSize: parent.width + icon: 'generic_error' + visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateNotRegistered || AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateNoProxy + TooltipArea{ + text : 'Not Registered' + } + } + } + + Text { + id:username + anchors.bottom:parent.bottom + + //Layout.fillHeight: true + //Layout.fillWidth: true + color: AccountStatusStyle.username.color + elide: Text.ElideRight + font.bold: true + font.pointSize: AccountStatusStyle.username.pointSize + text: AccountSettingsModel.username + verticalAlignment: Text.AlignBottom + } + Item { + // Layout.bottomMargin: -AccountStatusStyle.presenceLevel.bottomMargin + //Layout.preferredHeight: AccountStatusStyle.presenceLevel.size + //Layout.preferredWidth: AccountStatusStyle.presenceLevel.size + height: AccountStatusStyle.messageCounter.size + width: AccountStatusStyle.messageCounter.size + anchors.bottom:parent.bottom + anchors.bottomMargin: AccountStatusStyle.messageCounter.bottomMargin + MessageCounter { + id: messageCounter + //iconSize: parent.height + anchors.fill: parent + count: CoreManager.eventCount + } + } + } + + Text { + color: AccountStatusStyle.sipAddress.color + elide: Text.ElideRight + font.pointSize: AccountStatusStyle.sipAddress.pointSize + height: parent.height / 2 + text: AccountSettingsModel.sipAddress + verticalAlignment: Text.AlignTop + width: parent.width + } + } + + + } + + MouseArea { + id:mouseArea + anchors.fill: parent + + onClicked: accountStatus.clicked() + } +} diff --git a/linphone-app/ui/modules/Linphone/Account/AccountStatus.qml b/linphone-app/ui/modules/Linphone/Account/AccountStatus.qml index 06dd3acb1..1057e4323 100644 --- a/linphone-app/ui/modules/Linphone/Account/AccountStatus.qml +++ b/linphone-app/ui/modules/Linphone/Account/AccountStatus.qml @@ -9,95 +9,97 @@ import Linphone.Styles 1.0 // ============================================================================= Item { - id: accountStatus - - // --------------------------------------------------------------------------- - - signal clicked - property alias cursorShape:mouseArea.cursorShape - - // --------------------------------------------------------------------------- - - RowLayout { - anchors.fill: parent - - Column { - Layout.fillWidth: true - Layout.fillHeight: true - - RowLayout { - height: parent.height / 2 - spacing: AccountStatusStyle.horizontalSpacing - width: parent.width - - Item { - Layout.alignment: Qt.AlignBottom - Layout.bottomMargin: AccountStatusStyle.presenceLevel.bottomMargin - Layout.preferredHeight: AccountStatusStyle.presenceLevel.size - Layout.preferredWidth: AccountStatusStyle.presenceLevel.size - - PresenceLevel { - - anchors.fill: parent - level: OwnPresenceModel.presenceStatus===Presence.Offline?Presence.White:( SettingsModel.rlsUriEnabled ? OwnPresenceModel.presenceLevel : Presence.Green) - visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateRegistered - } - - BusyIndicator { - anchors.fill: parent - running: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateInProgress - } - - Icon { - iconSize: parent.width - icon: 'generic_error' - visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateNotRegistered || AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateNoProxy - TooltipArea{ - text : 'Not Registered' - } - } - } - - Text { - Layout.fillHeight: true - Layout.fillWidth: true - color: AccountStatusStyle.username.color - elide: Text.ElideRight - font.bold: true - font.pointSize: AccountStatusStyle.username.pointSize - text: AccountSettingsModel.username - verticalAlignment: Text.AlignBottom - } - } - - Text { - color: AccountStatusStyle.sipAddress.color - elide: Text.ElideRight - font.pointSize: AccountStatusStyle.sipAddress.pointSize - height: parent.height / 2 - text: AccountSettingsModel.sipAddress - verticalAlignment: Text.AlignTop - width: parent.width - } - } - - Item { - Layout.preferredWidth: MessageCounterStyle.iconSize.message - Layout.preferredHeight: MessageCounterStyle.iconSize.message - - MessageCounter { - id: messageCounter - - anchors.fill: parent - count: CoreManager.eventCount - } - } - } - - MouseArea { - id:mouseArea - anchors.fill: parent - - onClicked: accountStatus.clicked() - } + id: accountStatus + Layout.fillWidth: true + Layout.fillHeight: true + // --------------------------------------------------------------------------- + + signal clicked + property alias cursorShape:mouseArea.cursorShape + property alias betterIcon : presenceLevel.betterIcon + + // --------------------------------------------------------------------------- + + ColumnLayout { + anchors.fill:parent + spacing: AccountStatusStyle.verticalSpacing + + RowLayout { + Layout.preferredHeight: parent.height / 2 + Layout.maximumWidth: parent.width + Layout.alignment: Qt.AlignBottom | Qt.AlignLeft + spacing: AccountStatusStyle.horizontalSpacing + + Item { + Layout.alignment: Qt.AlignBottom | Qt.AlignLeft + Layout.bottomMargin: AccountStatusStyle.presenceLevel.bottomMargin + Layout.preferredHeight: AccountStatusStyle.presenceLevel.size + Layout.preferredWidth: AccountStatusStyle.presenceLevel.size + + PresenceLevel { + id:presenceLevel + anchors.fill:parent + level: OwnPresenceModel.presenceStatus===Presence.Offline?Presence.White:( SettingsModel.rlsUriEnabled ? OwnPresenceModel.presenceLevel : Presence.Green) + visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateRegistered + } + + BusyIndicator { + anchors.fill:parent + running: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateInProgress + } + + Icon { + iconSize: parent.width + icon: 'generic_error' + visible: AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateNotRegistered || AccountSettingsModel.registrationState === AccountSettingsModel.RegistrationStateNoProxy + TooltipArea{ + text : 'Not Registered' + } + } + } + + Text { + id:username + Layout.alignment: Qt.AlignBottom | Qt.AlignLeft + color: AccountStatusStyle.username.color + elide: Text.ElideRight + font.bold: true + font.pointSize: AccountStatusStyle.username.pointSize + text: AccountSettingsModel.username + verticalAlignment: Text.AlignBottom + } + Item { + Layout.alignment: Qt.AlignBottom | Qt.AlignLeft + Layout.bottomMargin: 5 + Layout.preferredHeight: AccountStatusStyle.presenceLevel.size + Layout.preferredWidth: AccountStatusStyle.presenceLevel.size + MessageCounter { + id: messageCounter + anchors.fill: parent + count: CoreManager.eventCount + } + } + Item{//Spacer + Layout.fillHeight: true + Layout.fillWidth: true + } + }//RowLayout + + Text { + Layout.preferredHeight:parent.height / 2 + Layout.preferredWidth:parent.width + color: AccountStatusStyle.sipAddress.color + elide: Text.ElideRight + font.pointSize: AccountStatusStyle.sipAddress.pointSize + text: AccountSettingsModel.sipAddress + verticalAlignment: Text.AlignTop + } + }//ColumnLayout + + MouseArea { + id:mouseArea + anchors.fill:parent + + onClicked: accountStatus.clicked() + } } diff --git a/linphone-app/ui/modules/Linphone/Calls/CallControls.qml b/linphone-app/ui/modules/Linphone/Calls/CallControls.qml index 9adefdf78..6a3017f20 100644 --- a/linphone-app/ui/modules/Linphone/Calls/CallControls.qml +++ b/linphone-app/ui/modules/Linphone/Calls/CallControls.qml @@ -66,7 +66,7 @@ Rectangle { displayUnreadMessageCount: true - entry: SipAddressesModel.getSipAddressObserver((fullPeerAddress?fullPeerAddress:peerAddress), (fullLocalAddress?fullLocalAddress:localAddress)) + //entry: SipAddressesModel.getSipAddressObserver((fullPeerAddress?fullPeerAddress:peerAddress), (fullLocalAddress?fullLocalAddress:localAddress)) } Item { diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.js b/linphone-app/ui/modules/Linphone/Chat/Chat.js index e57745b69..fa92e1fa1 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.js +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.js @@ -50,6 +50,10 @@ function getComponentFromEntry (chatEntry) { if (chatEntry.type === Linphone.ChatRoomModel.CallEntry) { return 'Event.qml' } + + if (chatEntry.type === Linphone.ChatRoomModel.NoticeEntry) { + return 'Notice.qml' + } return chatEntry.isOutgoing ? 'OutgoingMessage.qml' : 'IncomingMessage.qml' } @@ -93,5 +97,10 @@ function handleTextChanged (text) { function sendMessage (text) { textArea.text = '' chat.bindToEnd = true - container.proxyModel.sendMessage(text) + if(container.proxyModel) + container.proxyModel.sendMessage(text) + /* + else{// Create a chat room + CallsListModel.createChat() + }*/ } diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.qml b/linphone-app/ui/modules/Linphone/Chat/Chat.qml index 2708cc425..102dff712 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.qml @@ -13,7 +13,7 @@ import 'Chat.js' as Logic Rectangle { id: container - property alias proxyModel: chat.model + property alias proxyModel: chat.model // ChatRoomProxyModel // --------------------------------------------------------------------------- @@ -67,6 +67,7 @@ Rectangle { // more entries. onEntryTypeFilterChanged: Logic.initView() onMoreEntriesLoaded: Logic.handleMoreEntriesLoaded(n) + onPeerAddressChanged:console.log("connection : "+proxyModel.peerAddress) } // ----------------------------------------------------------------------- @@ -119,6 +120,7 @@ Rectangle { delegate: Rectangle { id: entry + property bool isNotice : $chatEntry.type === ChatRoomModel.NoticeEntry function isHoverEntry () { return mouseArea.containsMouse @@ -130,10 +132,10 @@ Rectangle { anchors { left: parent ? parent.left : undefined - leftMargin: ChatStyle.entry.leftMargin + leftMargin: isNotice?0:ChatStyle.entry.leftMargin right: parent ? parent.right : undefined - rightMargin: ChatStyle.entry.deleteIconSize + + rightMargin: isNotice?0:ChatStyle.entry.deleteIconSize + ChatStyle.entry.message.extraContent.spacing + ChatStyle.entry.message.extraContent.rightMargin + ChatStyle.entry.message.extraContent.leftMargin + @@ -178,6 +180,7 @@ Rectangle { TooltipArea { text: $chatEntry.timestamp.toLocaleString(Qt.locale(App.locale)) } + visible:!isNotice } // Display content. @@ -206,7 +209,7 @@ Rectangle { Borders { Layout.fillWidth: true - Layout.preferredHeight: ChatStyle.sendArea.height + ChatStyle.sendArea.border.width + Layout.preferredHeight: textArea.height borderColor: ChatStyle.sendArea.border.color topWidth: ChatStyle.sendArea.border.width @@ -214,8 +217,16 @@ Rectangle { DroppableTextArea { id: textArea + + enabled:!proxyModel.chatRoomModel.hasBeenLeft - anchors.fill: parent + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + height:ChatStyle.sendArea.height + ChatStyle.sendArea.border.width + minimumHeight:ChatStyle.sendArea.height + ChatStyle.sendArea.border.width + maximumHeight:container.height/2 dropEnabled: SettingsModel.fileTransferUrl.length > 0 dropDisabledReason: qsTr('noFileTransferUrl') @@ -223,8 +234,24 @@ Rectangle { onDropped: Logic.handleFilesDropped(files) onTextChanged: Logic.handleTextChanged(text) - onValidText: Logic.sendMessage(text) + onValidText: { + textArea.text = '' + chat.bindToEnd = true + if(proxyModel.chatRoomModel) + proxyModel.sendMessage(text) + else{ + console.log("Peer : " +proxyModel.peerAddress+ "/"+chat.model.peerAddress) + proxyModel.chatRoomModel = CallsListModel.createChat(proxyModel.peerAddress) + proxyModel.sendMessage(text) + } + } Component.onCompleted: {text = proxyModel.cachedText; cursorPosition=text.length} + Rectangle{ + anchors.fill:parent + color:'white' + opacity: 0.5 + visible:!textArea.enabled + } } } } diff --git a/linphone-app/ui/modules/Linphone/Chat/Message.qml b/linphone-app/ui/modules/Linphone/Chat/Message.qml index 119415e11..e5a836b37 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Message.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Message.qml @@ -67,7 +67,18 @@ Item { onLinkActivated: Qt.openUrlExternally(link) onActiveFocusChanged: deselect() - + Row{ + anchors.right:parent.right + anchors.bottom:parent.bottom + visible:$chatEntry.chatMessageModel.isEphemeral + Text{ + text: $chatEntry.chatMessageModel.ephemeralExpireTime + } + Icon{ + icon:'timer' + iconSize: 15 + } + } Menu { id: messageMenu @@ -84,6 +95,7 @@ Item { } } + // Handle hovered link. MouseArea { height: parent.height diff --git a/linphone-app/ui/modules/Linphone/Chat/Notice.qml b/linphone-app/ui/modules/Linphone/Chat/Notice.qml new file mode 100644 index 000000000..22239d553 --- /dev/null +++ b/linphone-app/ui/modules/Linphone/Chat/Notice.qml @@ -0,0 +1,52 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +import Linphone.Styles 1.0 +import Utils 1.0 + +// ============================================================================= + +RowLayout{ + property string _type: { + var status = $chatEntry.status + + if (status === ChatRoomModel.NoticeMessage) { + return 'message'; + } + if (status === ChatRoomModel.NoticeError) { + return 'error'; + } + return 'unknown_notice' + } + + Layout.preferredHeight: ChatStyle.entry.lineHeight + spacing: ChatStyle.entry.message.extraContent.spacing + Rectangle{ + height:1 + Layout.fillWidth: true + color:( $chatEntry.status == ChatRoomModel.NoticeError ? 'red' : 'black' ) + } + + Text { + Layout.preferredWidth: contentWidth + id:message + color:( $chatEntry.status == ChatRoomModel.NoticeError ? 'red' : 'black' ) + font { + bold: true + pointSize: ChatStyle.entry.event.text.pointSize + } + height: parent.height + text: $chatEntry.name?$chatEntry.message.arg($chatEntry.name):$chatEntry.message + verticalAlignment: Text.AlignVCenter + TooltipArea { + text: $chatEntry.timestamp.toLocaleString(Qt.locale(App.locale)) + } + } + Rectangle{ + height:1 + Layout.fillWidth: true + color:( $chatEntry.status == ChatRoomModel.NoticeError ? 'red' : 'black' ) + } +} diff --git a/linphone-app/ui/modules/Linphone/Contact/Avatar.qml b/linphone-app/ui/modules/Linphone/Contact/Avatar.qml index f618770f6..1e0ed243d 100644 --- a/linphone-app/ui/modules/Linphone/Contact/Avatar.qml +++ b/linphone-app/ui/modules/Linphone/Contact/Avatar.qml @@ -11,13 +11,15 @@ Item { // --------------------------------------------------------------------------- - property alias presenceLevel: presenceLevel.level + property alias presenceLevel: presenceLevelIcon.level property color backgroundColor: AvatarStyle.backgroundColor property color foregroundColor: 'transparent' property string username property var image property var _initialsRegex: /^\s*([^\s\.]+)(?:[\s\.]+([^\s\.]+))?/ + + //onPresenceLevelChanged: console.log(Presence.getPresenceLevelIconName(presenceLevel)+' => '+username) // --------------------------------------------------------------------------- @@ -68,7 +70,7 @@ Item { } PresenceLevel { - id: presenceLevel + id: presenceLevelIcon anchors { bottom: parent.bottom diff --git a/linphone-app/ui/modules/Linphone/Contact/Contact.qml b/linphone-app/ui/modules/Linphone/Contact/Contact.qml index ff8c1d7d0..3b816e1ff 100644 --- a/linphone-app/ui/modules/Linphone/Contact/Contact.qml +++ b/linphone-app/ui/modules/Linphone/Contact/Contact.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts 1.3 import Linphone 1.0 import LinphoneUtils 1.0 import Linphone.Styles 1.0 +import Common 1.0 // ============================================================================= @@ -16,6 +17,7 @@ Rectangle { property alias usernameColor: description.usernameColor property bool displayUnreadMessageCount: false + property bool showContactAddress : true // A entry from `SipAddressesModel` or an `SipAddressObserver`. property var entry @@ -43,17 +45,60 @@ Rectangle { Layout.preferredWidth: ContactStyle.contentHeight //image: _contact && _contact.vcard.avatar - image: (entry.contactModel?entry.contactModel.vcard.avatar:entry.avatar?entry.avatar: '') + image: entry?(entry.contactModel?entry.contactModel.vcard.avatar:entry.avatar?entry.avatar: ''):'' - presenceLevel: (entry.contactModel ? Presence.getPresenceLevel(entry.contactModel.presenceStatus) - : entry.presenceStatus ? Presence.getPresenceLevel(entry.presenceStatus) - :-1) + presenceLevel: entry?(entry.contactModel ? Presence.getPresenceLevel(entry.contactModel.presenceStatus) + : Presence.getPresenceLevel(entry.presenceStatus) + ) + :-1 + /* + Connections{ + target: entry.contactModel?entry.contactModel:entry + onPresenceStatusChanged:{ + if(entry){ + if(entry.contactModel){ + avatar.presenceLevel = Presence.getPresenceLevel(entry.contactModel.presenceStatus); + }else { + avatar.presenceLevel = Presence.getPresenceLevel(entry.presenceStatus); + } + }else { + avatar.presenceLevel = -1; + } + } + }*/ //username: LinphoneUtils.getContactUsername(_contact || entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '') - username: (entry.contactModel ? entry.contactModel.vcard.username - :entry.username?entry.username: + username: entry != undefined ?(entry.contactModel != undefined ? entry.contactModel.vcard.username + :entry.username != undefined ?entry.username: LinphoneUtils.getContactUsername(entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '') - ) + ):'' + visible:!groupChat.visible + Icon{ + anchors.right: parent.right + anchors.top:parent.top + anchors.topMargin: -5 + visible: entry!=undefined && entry.haveEncryption != undefined && entry.haveEncryption + icon: entry?(entry.securityLevel === 2?'secure_level_1': entry.securityLevel===3? 'secure_level_2' : 'secure_level_unsafe'):'secure_level_unsafe' + iconSize:15 + } + } + Icon { + id: groupChat + + Layout.preferredHeight: ContactStyle.contentHeight + Layout.preferredWidth: ContactStyle.contentHeight + + icon:'chat_room' + iconSize: ContactStyle.contentHeight + visible: entry!=undefined && entry.groupEnabled != undefined && entry.groupEnabled + Icon{ + anchors.right: parent.right + anchors.top:parent.top + anchors.topMargin: -5 + visible: entry!=undefined && entry.haveEncryption != undefined && entry.haveEncryption + icon: entry?(entry.securityLevel === 2?'secure_level_1': entry.securityLevel===3? 'secure_level_2' : 'secure_level_unsafe'):'secure_level_unsafe' + iconSize:15 + } } ContactDescription { @@ -64,18 +109,27 @@ Rectangle { Layout.leftMargin: ContactStyle.spacing //sipAddress: entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '' - sipAddress: (entry.contactModel ? entry.contactModel.vcard.sipAddress - :entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '') + sipAddress: (entry && showContactAddress? entry.sipAddress : '') + /* + sipAddress: (entry && showContactAddress? + (entry.contactModel != undefined ? + entry.contactModel.vcard.sipAddress + : (entry.groupEnabled != undefined && entry.groupEnabled ? '': + (entry.isSecure != undefined && entry.haveEncryption? + entry.participants.addressesToString() + : entry.sipAddress || entry.fullPeerAddress || entry.peerAddress || '') + ) + ):'')*/ username: avatar.username } ContactMessageCounter { Layout.alignment: Qt.AlignTop - count: Number(entry.unreadMessagesCount) + Number(entry.missedCallsCount) - isComposing: Boolean(entry.isComposing) + count: entry?Number(entry.unreadMessagesCount) + Number(entry.missedCallsCount):0 + isComposing: Boolean(entry && entry.isComposing) - visible: (entry.unreadMessagesCount !== null || entry.missedCallsCount !== null) && item.displayUnreadMessageCount + visible: entry?(entry.unreadMessagesCount !== null || entry.missedCallsCount !== null) && item.displayUnreadMessageCount:false } } } diff --git a/linphone-app/ui/modules/Linphone/Contact/ContactDescription.qml b/linphone-app/ui/modules/Linphone/Contact/ContactDescription.qml index ff0476e6d..741265a58 100644 --- a/linphone-app/ui/modules/Linphone/Contact/ContactDescription.qml +++ b/linphone-app/ui/modules/Linphone/Contact/ContactDescription.qml @@ -2,40 +2,49 @@ import QtQuick 2.7 import Linphone 1.0 import Linphone.Styles 1.0 +import Common 1.0 // ============================================================================= Column { - property alias username: username.text - property string sipAddress - - property color sipAddressColor: ContactDescriptionStyle.sipAddress.color - property color usernameColor: ContactDescriptionStyle.username.color - property int horizontalTextAlignment - - // --------------------------------------------------------------------------- - - Text { - id: username - - color: usernameColor - elide: Text.ElideRight - font.bold: true - font.pointSize: ContactDescriptionStyle.username.pointSize - horizontalAlignment: horizontalTextAlignment - verticalAlignment: Text.AlignBottom - width: parent.width - height: (parent.height-parent.topPadding-parent.bottomPadding)/parent.visibleChildren.length - } - - Text { - text: SipAddressesModel.cleanSipAddress(sipAddress) - color: sipAddressColor - elide: Text.ElideRight - font.pointSize: ContactDescriptionStyle.sipAddress.pointSize - horizontalAlignment: horizontalTextAlignment - verticalAlignment: Text.AlignTop - width: parent.width - height: (parent.height-parent.topPadding-parent.bottomPadding)/parent.visibleChildren.length - } + property alias username: username.text + property string sipAddress + + property color sipAddressColor: ContactDescriptionStyle.sipAddress.color + property color usernameColor: ContactDescriptionStyle.username.color + property int horizontalTextAlignment + property int contentWidth : username.contentWidth + address.contentWidth + + // --------------------------------------------------------------------------- + + Text { + id: username + + color: usernameColor + elide: Text.ElideRight + font.bold: true + font.pointSize: ContactDescriptionStyle.username.pointSize + horizontalAlignment: horizontalTextAlignment + verticalAlignment: (address.visible?Text.AlignBottom:Text.AlignVCenter) + width: parent.width + height: (parent.height-parent.topPadding-parent.bottomPadding)/parent.visibleChildren.length + //onTextChanged: console.log("username width: "+text+"=>"+contentWidth+"/"+width) + //onContentWidthChanged: console.log("usr : "+text+"=>"+contentWidth) + } + + Text { + id:address + text: SipAddressesModel.cleanSipAddress(sipAddress) + color: sipAddressColor + elide: Text.ElideRight + font.pointSize: ContactDescriptionStyle.sipAddress.pointSize + horizontalAlignment: horizontalTextAlignment + verticalAlignment: (username.visible?Text.AlignTop:Text.AlignVCenter) + width: parent.width + height: (parent.height-parent.topPadding-parent.bottomPadding)/parent.visibleChildren.length + visible: text != '' + //onTextChanged: console.log("address width: "+text+"=>"+contentWidth+"/"+width) + //onContentWidthChanged: console.log("addr : "+text+"=>"+contentWidth) + } + } diff --git a/linphone-app/ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml b/linphone-app/ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml index 3a3c5cc86..8598163b3 100644 --- a/linphone-app/ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml +++ b/linphone-app/ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml @@ -55,7 +55,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter descriptionText: { var str diff --git a/linphone-app/ui/modules/Linphone/Presence/PresenceLevel.qml b/linphone-app/ui/modules/Linphone/Presence/PresenceLevel.qml index 10c7f91e3..b55278cc5 100644 --- a/linphone-app/ui/modules/Linphone/Presence/PresenceLevel.qml +++ b/linphone-app/ui/modules/Linphone/Presence/PresenceLevel.qml @@ -8,15 +8,18 @@ import Linphone 1.0 // Wrapper to use `icon` property. Item { property var level: null + property bool betterIcon : false + //onLevelChanged: console.log("Level change : "+Presence.getPresenceStatusAsString(level)) Icon { anchors.centerIn: parent icon: (level !== -1 && level != null) - ? Presence.getPresenceLevelIconName(level) + ? (betterIcon? Presence.getBetterPresenceLevelIconName(level) : Presence.getPresenceLevelIconName(level)) : '' iconSize: parent.height > parent.width ? parent.width : parent.height + //onIconChanged: console.log(Presence.getPresenceStatusAsString(level)+ "//" +icon) } } diff --git a/linphone-app/ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml b/linphone-app/ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml index d845e44d2..5cdfb4170 100644 --- a/linphone-app/ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml +++ b/linphone-app/ui/modules/Linphone/SmartSearchBar/SmartSearchBar.qml @@ -14,6 +14,21 @@ SearchBox { readonly property alias isOpen: searchBox._isOpen property alias header : view.headerItem + property alias actions : view.actions + property alias showHeader : view.showHeader + + function addAddressToIgnore(entry){ + searchModel.addAddressToIgnore(entry) + } + + function removeAddressToIgnore(entry){ + searchModel.removeAddressToIgnore(entry) + } + + function isIgnored(address){ + return searchModel.isIgnored(address) + } + property var resultExceptions : [] // --------------------------------------------------------------------------- @@ -42,6 +57,7 @@ SearchBox { actions: [{ icon: 'video_call', + secure:0, handler: function (entry) { searchBox.closeMenu() searchBox.launchVideoCall(entry.sipAddress) @@ -49,18 +65,29 @@ SearchBox { visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.showStartVideoCallButton }, { icon: 'call', + secure:0, handler: function (entry) { searchBox.closeMenu() searchBox.launchCall(entry.sipAddress) }, visible: SettingsModel.outgoingCallsEnabled }, { - icon: SettingsModel.chatEnabled && SettingsModel.showStartChatButton ? 'chat' : 'history', + icon: SettingsModel.chatEnabled && SettingsModel.getShowStartChatButton() ? 'chat' : 'history', + secure:0, handler: function (entry) { searchBox.closeMenu() searchBox.launchChat(entry.sipAddress) } - }] + }, { + icon: SettingsModel.chatEnabled && SettingsModel.getShowStartChatButton() ? 'chat' : 'history', + secure:1, + handler: function (entry) { + searchBox.closeMenu() + searchBox.launchChat(entry.sipAddress) + } + } + + ] headerButtonDescription: qsTr('addContact') headerButtonIcon: 'contact_add' @@ -71,7 +98,9 @@ SearchBox { genSipAddress: searchBox.filter - model: SearchSipAddressesModel {} + model: SearchSipAddressesProxyModel { + id:searchModel + } onEntryClicked: { searchBox.closeMenu() diff --git a/linphone-app/ui/modules/Linphone/Styles/Account/AccountStatusStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Account/AccountStatusStyle.qml index 3f75b36a1..3652edc48 100644 --- a/linphone-app/ui/modules/Linphone/Styles/Account/AccountStatusStyle.qml +++ b/linphone-app/ui/modules/Linphone/Styles/Account/AccountStatusStyle.qml @@ -7,10 +7,11 @@ import Units 1.0 // ============================================================================= QtObject { - property int horizontalSpacing: 6 + property int horizontalSpacing: 8 + property int verticalSpacing: 2 property QtObject presenceLevel: QtObject { - property int bottomMargin: 2 + property int bottomMargin: 1 property int size: 16 } @@ -23,4 +24,8 @@ QtObject { property color color: Colors.j property int pointSize: Units.dp * 11 } + property QtObject messageCounter: QtObject { + property int bottomMargin: 4 + property int size: 16 + } } diff --git a/linphone-app/ui/modules/Linphone/Styles/Misc/MessageCounterStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Misc/MessageCounterStyle.qml index f632f6458..62ca53a13 100644 --- a/linphone-app/ui/modules/Linphone/Styles/Misc/MessageCounterStyle.qml +++ b/linphone-app/ui/modules/Linphone/Styles/Misc/MessageCounterStyle.qml @@ -8,12 +8,12 @@ import Units 1.0 QtObject { property QtObject iconSize: QtObject { - property int amount: 16 + property int amount: 12 property int message: 18 } property QtObject text: QtObject { property color color: Colors.q - property int pointSize: Units.dp * 7 + property int pointSize: Units.dp * 6 } } diff --git a/linphone-app/ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml index a70b59ad0..d3ddbf0ab 100644 --- a/linphone-app/ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml +++ b/linphone-app/ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml @@ -39,7 +39,7 @@ QtObject { property color hovered: Colors.c } property color color: Colors.d - property int pointSize: Units.dp * 11 + property int pointSize: Units.dp * 10 property int height: 30 property int iconSize: 14 property int leftMargin: 17 diff --git a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml index 4a6d1e78d..c6a308957 100644 --- a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml +++ b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml @@ -1,5 +1,6 @@ import QtQuick 2.7 import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.5 import Common 1.0 import Linphone 1.0 @@ -10,154 +11,243 @@ import 'Timeline.js' as Logic // ============================================================================= Rectangle { - id: timeline - - // --------------------------------------------------------------------------- - - property alias model: view.model - property string _selectedSipAddress - - // --------------------------------------------------------------------------- - - //signal entrySelected (string entry) - signal entrySelected (TimelineModel entry) - - // --------------------------------------------------------------------------- -/* - function setSelectedEntry (peerAddress, localAddress) { - Logic.setSelectedEntry(peerAddress, localAddress) - } - - function resetSelectedEntry () { - Logic.resetSelectedEntry() - } -*/ - // --------------------------------------------------------------------------- - - color: TimelineStyle.color - - ColumnLayout { - anchors.fill: parent - spacing: 0 - - // ------------------------------------------------------------------------- + id: timeline - Connections { - target: model - onSelectedCountChanged:if(selectedCount<=0) view.currentIndex = -1 - // onCurrentTimelineChanged:entrySelected(currentTimeline) - } -/* - Connections { - target: model - - onDataChanged: Logic.handleDataChanged(topLeft, bottomRight, roles) - onRowsAboutToBeRemoved: Logic.handleRowsAboutToBeRemoved(parent, first, last) - } -*/ - // ------------------------------------------------------------------------- - // Legend. - // ------------------------------------------------------------------------- - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: TimelineStyle.legend.height - color: showHistory.containsMouse?TimelineStyle.legend.backgroundColor.hovered:TimelineStyle.legend.backgroundColor.normal - - MouseArea{ - id:showHistory - anchors.fill:parent - onClicked: { - view.currentIndex = -1 - timeline.entrySelected('',false) - } - } - - Row { - anchors { - fill: parent - leftMargin: TimelineStyle.legend.leftMargin - rightMargin: TimelineStyle.legend.rightMargin - } - spacing: TimelineStyle.legend.spacing - - Icon { - anchors.verticalCenter: parent.verticalCenter - icon: 'timeline_history' - iconSize: TimelineStyle.legend.iconSize - } - - Text { - color: TimelineStyle.legend.color - font.pointSize: TimelineStyle.legend.pointSize - height: parent.height - text: qsTr('timelineTitle') - verticalAlignment: Text.AlignVCenter - } - } - } - - // ------------------------------------------------------------------------- - // History. - // ------------------------------------------------------------------------- - - ScrollableListView { - id: view - - Layout.fillHeight: true - Layout.fillWidth: true - currentIndex: -1 - - delegate: Item { - height: TimelineStyle.contact.height - width: parent ? parent.width : 0 - - Contact { - property bool isSelected: modelData.selected //view.currentIndex === index - - anchors.fill: parent - color: isSelected - ? TimelineStyle.contact.backgroundColor.selected - : ( - index % 2 == 0 - ? TimelineStyle.contact.backgroundColor.a - : TimelineStyle.contact.backgroundColor.b - ) - displayUnreadMessageCount: SettingsModel.chatEnabled - //entry: $timelineEntry - //entry: SipAddressesModel.getSipAddressObserver(modelData.fullPeerAddress, modelData.fullLocalAddress) - entry: modelData.chatRoomModel - sipAddressColor: isSelected - ? TimelineStyle.contact.sipAddress.color.selected - : TimelineStyle.contact.sipAddress.color.normal - usernameColor: isSelected - ? TimelineStyle.contact.username.color.selected - : TimelineStyle.contact.username.color.normal - - Loader { - anchors.fill: parent - sourceComponent: TooltipArea { - - //text: $timelineEntry.timestamp.toLocaleString( - //Qt.locale(App.locale), - //Locale.ShortFormat - //) - } - } - } - - MouseArea { - anchors.fill: parent - onClicked: { - //timeline.model.unselectAll() - modelData.selected = true - view.currentIndex = index; - timeline.entrySelected(modelData) - //timeline.entrySelected($timelineEntry.sipAddress, $timelineEntry.isSecure) - } - } - } - // onCountChanged: Logic.handleCountChanged(count) - } + // --------------------------------------------------------------------------- + + property alias model: view.model + property string _selectedSipAddress + + // --------------------------------------------------------------------------- + + //signal entrySelected (string entry) + signal entrySelected (TimelineModel entry) + + // --------------------------------------------------------------------------- + /* + function setSelectedEntry (peerAddress, localAddress) { + Logic.setSelectedEntry(peerAddress, localAddress) } + + function resetSelectedEntry () { + Logic.resetSelectedEntry() + } +*/ + // --------------------------------------------------------------------------- + + color: TimelineStyle.color + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + + // ------------------------------------------------------------------------- + + Connections { + target: model + onSelectedCountChanged:if(selectedCount<=0) view.currentIndex = -1 + // onCurrentTimelineChanged:entrySelected(currentTimeline) + } + /* + Connections { + target: model + + onDataChanged: Logic.handleDataChanged(topLeft, bottomRight, roles) + onRowsAboutToBeRemoved: Logic.handleRowsAboutToBeRemoved(parent, first, last) + } +*/ + // ------------------------------------------------------------------------- + // Legend. + // ------------------------------------------------------------------------- + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: TimelineStyle.legend.height + Layout.alignment: Qt.AlignTop + color: showHistory.containsMouse?TimelineStyle.legend.backgroundColor.hovered:TimelineStyle.legend.backgroundColor.normal + + MouseArea{ + id:showHistory + anchors.fill:parent + onClicked: { + view.currentIndex = -1 + timeline.entrySelected('',false) + } + } + RowLayout{ + anchors.fill:parent + spacing:TimelineStyle.legend.spacing + Text { + Layout.preferredHeight: parent.height + Layout.fillWidth: true + Layout.leftMargin: TimelineStyle.legend.leftMargin + color: TimelineStyle.legend.color + font.pointSize: TimelineStyle.legend.pointSize + //height: parent.height + text: 'Filter : All' + verticalAlignment: Text.AlignVCenter + } + + Icon { + id:filterButton + Layout.alignment: Qt.AlignRight + icon: 'timeline_filter' + iconSize: TimelineStyle.legend.iconSize + MouseArea{ + anchors.fill:parent + onClicked:{ + filterView.visible = !filterView.visible + } + } + } + + Icon { + id:searchButton + Layout.alignment: Qt.AlignRight + Layout.rightMargin: TimelineStyle.legend.rightMargin + icon: (searchView.visible? 'timeline_close': 'timeline_search') + iconSize: TimelineStyle.legend.iconSize + MouseArea{ + anchors.fill:parent + onClicked:{ + searchView.visible = !searchView.visible + } + } + } + } + } + // ------------------------------------------------------------------------- + // Filter. + // ------------------------------------------------------------------------- + Rectangle{ + id:filterView + Layout.fillWidth: true + Layout.preferredHeight: filterChoices.height + Layout.alignment: Qt.AlignCenter + border.color: 'black' + border.width: 2 + visible:false + + ColumnLayout{ + id:filterChoices + anchors.leftMargin: 20 + anchors.left:parent.left + anchors.right:parent.right + spacing:-4 + CheckBoxText { + text:'Appels Simples' + } + CheckBoxText { + text:'Conférences' + } + CheckBoxText { + text:'Messages Simples' + } + CheckBoxText { + text:'Chat de groupe' + } + } + } + // ------------------------------------------------------------------------- + // Search. + // ------------------------------------------------------------------------- + Rectangle{ + id:searchView + Layout.fillWidth: true + Layout.preferredHeight: 40 + Layout.alignment: Qt.AlignCenter + border.color: 'black' + border.width: 2 + visible:false + //color: ContactsStyle.bar.backgroundColor + + TextField { + id:searchBar + anchors { + fill: parent + margins: 7 + } + Layout.fillWidth: true + icon: 'search' + placeholderText: 'Search in the list' + + onTextChanged: console.log(text) + } + + } + // ------------------------------------------------------------------------- + // History. + // ------------------------------------------------------------------------- + + ScrollableListView { + id: view + Layout.fillHeight: true + Layout.fillWidth: true + //anchors.left:parent.left + //anchors.right:parent.right + //anchors.bottom:parent.bottom + currentIndex: -1 + + delegate: Item { + height: TimelineStyle.contact.height + width: parent ? parent.width : 0 + + Contact { + property bool isSelected: modelData.selected //view.currentIndex === index + + anchors.fill: parent + color: isSelected + ? TimelineStyle.contact.backgroundColor.selected + : ( + index % 2 == 0 + ? TimelineStyle.contact.backgroundColor.a + : TimelineStyle.contact.backgroundColor.b + ) + displayUnreadMessageCount: SettingsModel.chatEnabled + //entry: $timelineEntry + //entry: SipAddressesModel.getSipAddressObserver(modelData.fullPeerAddress, modelData.fullLocalAddress) + entry: modelData.chatRoomModel + sipAddressColor: isSelected + ? TimelineStyle.contact.sipAddress.color.selected + : TimelineStyle.contact.sipAddress.color.normal + usernameColor: isSelected + ? TimelineStyle.contact.username.color.selected + : TimelineStyle.contact.username.color.normal + + Loader { + anchors.fill: parent + sourceComponent: TooltipArea { + + //text: $timelineEntry.timestamp.toLocaleString( + //Qt.locale(App.locale), + //Locale.ShortFormat + //) + } + } + Icon{ + icon:'timer' + iconSize: 10 + anchors.right:parent.right + anchors.bottom:parent.bottom + anchors.bottomMargin: 5 + anchors.rightMargin: 5 + visible: modelData.chatRoomModel.ephemeralEnabled + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + //timeline.model.unselectAll() + modelData.selected = true + view.currentIndex = index; + timeline.entrySelected(modelData) + //timeline.entrySelected($timelineEntry.sipAddress, $timelineEntry.isSecure) + } + } + } + // onCountChanged: Logic.handleCountChanged(count) + } + } } diff --git a/linphone-app/ui/modules/Linphone/View/ParticipantsView.qml b/linphone-app/ui/modules/Linphone/View/ParticipantsView.qml new file mode 100644 index 000000000..868bbdde3 --- /dev/null +++ b/linphone-app/ui/modules/Linphone/View/ParticipantsView.qml @@ -0,0 +1,313 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +import Linphone.Styles 1.0 +import Common.Styles 1.0 + +// ============================================================================= + +ScrollableListView { + id: sipAddressesView + + // --------------------------------------------------------------------------- + + // Contains a list of: { + // icon: 'string', + // handler: function () { ... } + // } + property var actions: [] + + property string genSipAddress + + // Optional parameters. + property string headerButtonDescription + property string headerButtonIcon + property var headerButtonAction + property bool showHeader : true + property bool showContactAddress : true + property bool showSwitch : false + property bool showSeparator : true + property bool isSelectable : true + + property var switchHandler : function(checked, index){ + } + + + readonly property string interpretableSipAddress: SipAddressesModel.interpretSipAddress( + genSipAddress, false + ) + + // --------------------------------------------------------------------------- + + signal entryClicked (var entry, var index) + + + // --------------------------------------------------------------------------- + // Header. + // --------------------------------------------------------------------------- + + header: MouseArea { + height: { + var height = headerButton.visible ? SipAddressesViewStyle.header.button.height : 0 + if (defaultContact.visible) { + height += SipAddressesViewStyle.entry.height + } + return height + } + width: parent.width + + // Workaround to handle mouse. + // Without it, the mouse can be given to items list when mouse is hover header. + hoverEnabled: true + cursorShape: Qt.ArrowCursor + + Column { + anchors.fill: parent + + spacing: 0 + + // ----------------------------------------------------------------------- + // Default contact. + // ----------------------------------------------------------------------- + + Loader { + id: defaultContact + + height: SipAddressesViewStyle.entry.height + width: parent.width + + visible: sipAddressesView.interpretableSipAddress.length > 0 + + sourceComponent: Rectangle { + anchors.fill: parent + color: SipAddressesViewStyle.entry.color.normal + + RowLayout { + anchors { + fill: parent + rightMargin: SipAddressesViewStyle.entry.rightMargin + } + spacing: 0 + + Contact { + id: contact + + Layout.fillHeight: true + Layout.fillWidth: true + + entry: ({ + sipAddress: sipAddressesView.interpretableSipAddress, + groupEnabled:false, + haveEncryption:false + }) + } + + ActionBar { + id: defaultContactActionBar + + iconSize: SipAddressesViewStyle.entry.iconSize + + Repeater { + model: sipAddressesView.actions + + ActionButton { + icon: modelData.icon + visible: { + var visible = sipAddressesView.actions[index].visible + return visible === undefined || visible + } + + onClicked: sipAddressesView.actions[index].handler({ + sipAddress: sipAddressesView.interpretableSipAddress + }) + } + } + } + } + } + } + + // ----------------------------------------------------------------------- + // Header button. + // ----------------------------------------------------------------------- + + MouseArea { + id: headerButton + + height: SipAddressesViewStyle.header.button.height + width: parent.width + + visible: sipAddressesView.showHeader && !!sipAddressesView.headerButtonAction + + onClicked: sipAddressesView.headerButtonAction(sipAddressesView.interpretableSipAddress) + + Rectangle { + anchors.fill: parent + color: parent.pressed + ? SipAddressesViewStyle.header.color.pressed + : SipAddressesViewStyle.header.color.normal + + Text { + anchors { + left: parent.left + leftMargin: SipAddressesViewStyle.header.leftMargin + verticalCenter: parent.verticalCenter + } + + font { + bold: true + pointSize: SipAddressesViewStyle.header.text.pointSize + } + + color: headerButton.pressed + ? SipAddressesViewStyle.header.text.color.pressed + : SipAddressesViewStyle.header.text.color.normal + text: sipAddressesView.headerButtonDescription + } + + Icon { + anchors { + right: parent.right + rightMargin: SipAddressesViewStyle.header.rightMargin + verticalCenter: parent.verticalCenter + } + + icon: sipAddressesView.headerButtonIcon + iconSize: SipAddressesViewStyle.header.iconSize + + visible: icon.length > 0 + } + } + } + } + } + + // --------------------------------------------------------------------------- + // Entries. + // --------------------------------------------------------------------------- + + delegate: Rectangle { + id: sipAddressEntry + + color: SipAddressesViewStyle.entry.color.normal + height: SipAddressesViewStyle.entry.height + width: parent ? parent.width : 0 + + Rectangle { + id: indicator + + anchors.left: parent.left + color: 'transparent' + height: parent.height + width: SipAddressesViewStyle.entry.indicator.width + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + cursorShape: Qt.ArrowCursor + + RowLayout { + anchors { + fill: parent + rightMargin: SipAddressesViewStyle.entry.rightMargin + } + spacing: 0 + + // --------------------------------------------------------------------- + // Contact or address info. + // --------------------------------------------------------------------- + + Contact { + id:contactView + Layout.fillHeight: true + Layout.fillWidth: true + showContactAddress: sipAddressesView.showContactAddress + + entry: modelData + + MouseArea { + anchors.fill: parent + onClicked: sipAddressesView.entryClicked(entry) + } + } + + + // --------------------------------------------------------------------- + // Actions + // --------------------------------------------------------------------- + + ActionBar { + iconSize: SipAddressesViewStyle.entry.iconSize + + Switch{ + anchors.verticalCenter: parent.verticalCenter + width:50 + //Layout.preferredWidth: 50 + indicatorStyle: SwitchStyle.aux + + visible: sipAddressesView.showSwitch + + enabled:true + checked: modelData.adminStatus + onClicked: { + modelData.adminStatus = !checked + } + } + + Repeater { + model: sipAddressesView.actions + + ActionButton { + icon: modelData.icon + tooltipText:modelData.tooltipText?modelData.tooltipText:'' + visible: { + var visible = sipAddressesView.actions[index].visible + return visible === undefined || visible + } + + onClicked: { + sipAddressesView.actions[index].handler(contactView.entry) + } + Icon{ + visible: modelData.secure>0 + icon:modelData.secure === 2?'secure_level_2':'secure_level_1' + iconSize:15 + anchors.right:parent.right + anchors.top:parent.top + anchors.topMargin: -3 + } + } + } + } + } + } + + // Separator. + Rectangle { + color: SipAddressesViewStyle.entry.separator.color + height: SipAddressesViewStyle.entry.separator.height + width: parent.width + visible: sipAddressesView.showSeparator + } + + // ------------------------------------------------------------------------- + + states: State { + when: mouseArea.containsMouse && sipAddressesView.isSelectable + + PropertyChanges { + color: SipAddressesViewStyle.entry.color.hovered + target: sipAddressEntry + } + + PropertyChanges { + color: SipAddressesViewStyle.entry.indicator.color + target: indicator + } + } + } +} diff --git a/linphone-app/ui/modules/Linphone/View/SipAddressesView.qml b/linphone-app/ui/modules/Linphone/View/SipAddressesView.qml index 4cac04ac0..f60c846e9 100644 --- a/linphone-app/ui/modules/Linphone/View/SipAddressesView.qml +++ b/linphone-app/ui/modules/Linphone/View/SipAddressesView.qml @@ -4,6 +4,7 @@ import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 import Linphone.Styles 1.0 +import Common.Styles 1.0 // ============================================================================= @@ -24,6 +25,15 @@ ScrollableListView { property string headerButtonDescription property string headerButtonIcon property var headerButtonAction + property bool showHeader : true + property bool showContactAddress : true + property bool showSwitch : false + property bool showSeparator : true + property bool isSelectable : true + + property var switchHandler : function(checked, index){ + } + readonly property string interpretableSipAddress: SipAddressesModel.interpretSipAddress( genSipAddress, false @@ -31,7 +41,7 @@ ScrollableListView { // --------------------------------------------------------------------------- - signal entryClicked (var entry) + signal entryClicked (var entry, var index) // --------------------------------------------------------------------------- // Header. @@ -87,7 +97,9 @@ ScrollableListView { Layout.fillWidth: true entry: ({ - sipAddress: sipAddressesView.interpretableSipAddress + sipAddress: sipAddressesView.interpretableSipAddress, + groupEnabled:false, + haveEncryption:false }) } @@ -126,7 +138,7 @@ ScrollableListView { height: SipAddressesViewStyle.header.button.height width: parent.width - visible: !!sipAddressesView.headerButtonAction + visible: sipAddressesView.showHeader && !!sipAddressesView.headerButtonAction onClicked: sipAddressesView.headerButtonAction(sipAddressesView.interpretableSipAddress) @@ -211,33 +223,61 @@ ScrollableListView { Contact { Layout.fillHeight: true Layout.fillWidth: true + showContactAddress: sipAddressesView.showContactAddress - entry: $sipAddress + entry: $sipAddress MouseArea { anchors.fill: parent - onClicked: sipAddressesView.entryClicked($sipAddress) + onClicked: sipAddressesView.entryClicked($sipAddress, ) } } - + + // --------------------------------------------------------------------- // Actions // --------------------------------------------------------------------- ActionBar { iconSize: SipAddressesViewStyle.entry.iconSize + + Switch{ + anchors.verticalCenter: parent.verticalCenter + width:50 + //Layout.preferredWidth: 50 + indicatorStyle: SwitchStyle.aux + + visible: sipAddressesView.showSwitch + + enabled:true + checked: false + onClicked: { + //checked = !checked + switchHandler(!checked, index) + } + + } Repeater { model: sipAddressesView.actions ActionButton { icon: modelData.icon + tooltipText:modelData.tooltipText?modelData.tooltipText:'' visible: { var visible = sipAddressesView.actions[index].visible return visible === undefined || visible } onClicked: sipAddressesView.actions[index].handler($sipAddress) + Icon{ + visible: modelData.secure>0 + icon:modelData.secure === 2?'secure_level_2':'secure_level_1' + iconSize:15 + anchors.right:parent.right + anchors.top:parent.top + anchors.topMargin: -3 + } } } } @@ -249,12 +289,13 @@ ScrollableListView { color: SipAddressesViewStyle.entry.separator.color height: SipAddressesViewStyle.entry.separator.height width: parent.width + visible: sipAddressesView.showSeparator } // ------------------------------------------------------------------------- states: State { - when: mouseArea.containsMouse + when: mouseArea.containsMouse && sipAddressesView.isSelectable PropertyChanges { color: SipAddressesViewStyle.entry.color.hovered diff --git a/linphone-app/ui/modules/Linphone/qmldir b/linphone-app/ui/modules/Linphone/qmldir index 8f41a1d39..1e1ae148d 100644 --- a/linphone-app/ui/modules/Linphone/qmldir +++ b/linphone-app/ui/modules/Linphone/qmldir @@ -37,3 +37,5 @@ TelKeypad 1.0 TelKeypad/TelKeypad.qml Timeline 1.0 Timeline/Timeline.qml SipAddressesView 1.0 View/SipAddressesView.qml + +ParticipantsView 1.0 View/ParticipantsView.qml diff --git a/linphone-app/ui/views/App/Calls/Dialogs/CallSipAddress.qml b/linphone-app/ui/views/App/Calls/Dialogs/CallSipAddress.qml index 34f39e349..6d72cb53f 100644 --- a/linphone-app/ui/views/App/Calls/Dialogs/CallSipAddress.qml +++ b/linphone-app/ui/views/App/Calls/Dialogs/CallSipAddress.qml @@ -17,7 +17,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter descriptionText: qsTr('callSipAddressDescription') height: CallSipAddressStyle.height diff --git a/linphone-app/ui/views/App/Calls/Dialogs/CallTransfer.qml b/linphone-app/ui/views/App/Calls/Dialogs/CallTransfer.qml index ac399c348..f97dcf571 100644 --- a/linphone-app/ui/views/App/Calls/Dialogs/CallTransfer.qml +++ b/linphone-app/ui/views/App/Calls/Dialogs/CallTransfer.qml @@ -25,7 +25,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter descriptionText: qsTr('callTransferDescription') height: CallTransferStyle.height diff --git a/linphone-app/ui/views/App/Calls/Dialogs/ConferenceManager.qml b/linphone-app/ui/views/App/Calls/Dialogs/ConferenceManager.qml index 5662296fb..5cf0e76c5 100644 --- a/linphone-app/ui/views/App/Calls/Dialogs/ConferenceManager.qml +++ b/linphone-app/ui/views/App/Calls/Dialogs/ConferenceManager.qml @@ -31,7 +31,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter descriptionText: qsTr('conferenceManagerDescription') height: ConferenceManagerStyle.height diff --git a/linphone-app/ui/views/App/Calls/Dialogs/MultimediaParameters.qml b/linphone-app/ui/views/App/Calls/Dialogs/MultimediaParameters.qml index 3118616d1..c5e919abd 100644 --- a/linphone-app/ui/views/App/Calls/Dialogs/MultimediaParameters.qml +++ b/linphone-app/ui/views/App/Calls/Dialogs/MultimediaParameters.qml @@ -27,7 +27,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter height: MultimediaParametersStyle.height width: MultimediaParametersStyle.width diff --git a/linphone-app/ui/views/App/Main/Contacts.qml b/linphone-app/ui/views/App/Main/Contacts.qml index fe4373053..0afab2522 100644 --- a/linphone-app/ui/views/App/Main/Contacts.qml +++ b/linphone-app/ui/views/App/Main/Contacts.qml @@ -10,298 +10,332 @@ import App.Styles 1.0 // ============================================================================= ColumnLayout { - function _removeContact (contact) { - window.attachVirtualWindow(Utils.buildDialogUri('ConfirmDialog'), { - descriptionText: qsTr('removeContactDescription'), - }, function (status) { - if (status) { - ContactsListModel.removeContact(contact) - } - }) - } - - spacing: 0 - - // --------------------------------------------------------------------------- - // Search Bar & actions. - // --------------------------------------------------------------------------- - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: ContactsStyle.bar.height - - color: ContactsStyle.bar.backgroundColor - - RowLayout { - anchors { - fill: parent - leftMargin: ContactsStyle.bar.leftMargin - rightMargin: ContactsStyle.bar.rightMargin - } - spacing: ContactsStyle.spacing - - TextField { - Layout.fillWidth: true - icon: 'filter' - placeholderText: qsTr('searchContactPlaceholder') - - onTextChanged: contacts.setFilter(text) - } - - ExclusiveButtons { - texts: [ - qsTr('selectAllContacts'), - qsTr('selectConnectedContacts') - ] - - onClicked: contacts.useConnectedFilter = !!button - } - - TextButtonB { - text: qsTr('addContact') - onClicked: window.setView('ContactEdit') - } - } - } - - // --------------------------------------------------------------------------- - // Contacts list. - // --------------------------------------------------------------------------- - - Rectangle { - Layout.fillWidth: true - Layout.fillHeight: true - color: ContactsStyle.backgroundColor - - ScrollableListView { - anchors.fill: parent - spacing: 0 - - model: ContactsListProxyModel { - id: contacts - } - - delegate: Borders { - bottomColor: ContactsStyle.contact.border.color - bottomWidth: ContactsStyle.contact.border.width - height: ContactsStyle.contact.height - width: parent ? parent.width : 0 - - // --------------------------------------------------------------------- - - Rectangle { - id: contact - - anchors.fill: parent - color: ContactsStyle.contact.backgroundColor.normal - - // ------------------------------------------------------------------- - - Component { - id: container1 - - RowLayout { - spacing: ContactsStyle.contact.spacing - - PresenceLevel { - Layout.preferredHeight: ContactsStyle.contact.presenceLevelSize - Layout.preferredWidth: ContactsStyle.contact.presenceLevelSize - level: $contact.presenceLevel - } - - Text { - Layout.fillWidth: true - color: ContactsStyle.contact.presence.color - elide: Text.ElideRight - font.pointSize: ContactsStyle.contact.presence.pointSize - text: Presence.getPresenceStatusAsString($contact.presenceStatus) - } - } - } - - Component { - id: container2 - - Item { - ActionBar { - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } - iconSize: ContactsStyle.contact.actionButtonsSize - - ActionButton { - icon: 'video_call' - visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.showStartVideoCallButton - - onClicked: actions.itemAt(0).open() - } - - ActionButton { - icon: 'call' - visible: SettingsModel.outgoingCallsEnabled - - onClicked: actions.itemAt(1).open() - } - - ActionButton { - icon: SettingsModel.chatEnabled && SettingsModel.showStartChatButton ? 'chat' : 'history' - onClicked: actions.itemAt(2).open() - } + function _removeContact (contact) { + window.attachVirtualWindow(Utils.buildDialogUri('ConfirmDialog'), { + descriptionText: qsTr('removeContactDescription'), + }, function (status) { + if (status) { + ContactsListModel.removeContact(contact) + } + }) + } + + spacing: 0 + + // --------------------------------------------------------------------------- + // Search Bar & actions. + // --------------------------------------------------------------------------- + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: ContactsStyle.bar.height + + color: ContactsStyle.bar.backgroundColor + + RowLayout { + anchors { + fill: parent + leftMargin: ContactsStyle.bar.leftMargin + rightMargin: ContactsStyle.bar.rightMargin + } + spacing: ContactsStyle.spacing + + TextField { + Layout.fillWidth: true + icon: 'filter' + placeholderText: qsTr('searchContactPlaceholder') - ActionButton { - icon: 'call_chat_unsecure' - onClicked: {actions.itemAt(3).open()} + onTextChanged: contacts.setFilter(text) + } + + ExclusiveButtons { + texts: [ + qsTr('selectAllContacts'), + qsTr('selectConnectedContacts') + ] + + onClicked: contacts.useConnectedFilter = !!button + } + + TextButtonB { + text: qsTr('addContact') + onClicked: window.setView('ContactEdit') + } + } + } + + // --------------------------------------------------------------------------- + // Contacts list. + // --------------------------------------------------------------------------- + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + color: ContactsStyle.backgroundColor + + ScrollableListView { + anchors.fill: parent + spacing: 0 + + model: ContactsListProxyModel { + id: contacts + } + + delegate: Borders { + bottomColor: ContactsStyle.contact.border.color + bottomWidth: ContactsStyle.contact.border.width + height: ContactsStyle.contact.height + width: parent ? parent.width : 0 + + // --------------------------------------------------------------------- + + Rectangle { + id: contact + + anchors.fill: parent + color: ContactsStyle.contact.backgroundColor.normal + + // ------------------------------------------------------------------- + + Component { + id: container1 + + RowLayout { + spacing: ContactsStyle.contact.spacing + + PresenceLevel { + Layout.preferredHeight: ContactsStyle.contact.presenceLevelSize + Layout.preferredWidth: ContactsStyle.contact.presenceLevelSize + level: $contact.presenceLevel + } + + Text { + Layout.fillWidth: true + color: ContactsStyle.contact.presence.color + elide: Text.ElideRight + font.pointSize: ContactsStyle.contact.presence.pointSize + text: Presence.getPresenceStatusAsString($contact.presenceStatus) + } + } + } + + Component { + id: container2 + + Item { + ActionBar { + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + iconSize: ContactsStyle.contact.actionButtonsSize + + ActionButton { + icon: 'video_call' + visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.getShowStartVideoCallButton() + + onClicked: actions.itemAt(0).open() + } + + ActionButton { + icon: 'call' + visible: SettingsModel.outgoingCallsEnabled + + onClicked: actions.itemAt(1).open() + } + + ActionButton { + icon: SettingsModel.chatEnabled && SettingsModel.getShowStartChatButton() ? 'chat' : 'history' + onClicked: actions.itemAt(2).open() + } + + ActionButton { + icon: 'chat' + visible:SettingsModel.chatEnabled && SettingsModel.getShowStartChatButton() + Icon{ + icon:'secure_level_1' + iconSize:15 + anchors.right:parent.right + anchors.top:parent.top + anchors.topMargin: -3 + } + onClicked: {actions.itemAt(3).open()} + } + } + + ActionButton { + anchors { + right: parent.right + verticalCenter: parent.verticalCenter + } + icon: 'delete' + iconSize: ContactsStyle.contact.deleteButtonSize + + onClicked: _removeContact($contact) + } + } + } + + // ------------------------------------------------------------------- + + Repeater { + id: actions + property ChatRoomModel lastChatRoom + + Connections{ + target: lastChatRoom + onStateChanged: if(state === 1) { + window.setView('Conversation', { + chatRoomModel: lastChatRoom + }) + } + } + + readonly property var handlers: [ + CallsListModel.launchVideoCall, + CallsListModel.launchAudioCall, + function (sipAddress) { + window.setView('Conversation', { + peerAddress: sipAddress, + localAddress: AccountSettingsModel.sipAddress, + fullPeerAddress: sipAddress, + fullLocalAddress: AccountSettingsModel.fullSipAddress + }) + }, + function(sipAddress){ + lastChatRoom = CallsListModel.launchSecureChat(sipAddress); + if( !lastChatRoom) + console.log("Cannot create Secure Chat with "+sipAddress); + } + /* + function (sipAddress) { + CallsListModel.launchSecureChat(sipAddress) + window.setView('Conversation', { + chatRoomModel: CallsListModel.createSecureChat("", sipAddress), + //peerAddress: sipAddress, + //localAddress: AccountSettingsModel.sipAddress, + //fullPeerAddress: sipAddress, + //fullLocalAddress: AccountSettingsModel.fullSipAddress, + isSecure:1 + }) + + //Logic.manageAccounts() + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ManageChatRoom.qml'), { + //window.setView('Dialogs/ManageChatRoom', { + participantAddress: sipAddress + }) + }*/ + ] + + model: handlers + + SipAddressesMenu { + relativeTo: loader + relativeY: loader.height + + sipAddresses: $contact.vcard.sipAddresses + + onSipAddressClicked: actions.handlers[index](sipAddress) + } + } + + // ------------------------------------------------------------------- + + Rectangle { + id: indicator + + anchors.left: parent.left + color: 'transparent' + height: parent.height + width: ContactsStyle.contact.indicator.width + } + + MouseArea { + id: mouseArea + + anchors.fill: parent + cursorShape: Qt.ArrowCursor + + MouseArea { + anchors.fill: parent + + onClicked: window.setView('ContactEdit', { + sipAddress: $contact.vcard.sipAddresses[0] + }) + } + + RowLayout { + anchors { + fill: parent + leftMargin: ContactsStyle.contact.leftMargin + rightMargin: ContactsStyle.contact.rightMargin + } + spacing: ContactsStyle.contact.spacing + + Item { + Layout.preferredHeight: parent.height + Layout.preferredWidth: parent.height + + Avatar { + anchors.centerIn: parent + + image: $contact.vcard.avatar + username: $contact.vcard.username + + height: ContactsStyle.contact.avatarSize + width: ContactsStyle.contact.avatarSize + } + } + + Text { + Layout.fillHeight: true + Layout.preferredWidth: ContactsStyle.contact.username.width + + color: ContactsStyle.contact.username.color + elide: Text.ElideRight + + font { + bold: true + pointSize: ContactsStyle.contact.username.pointSize + } + + text: $contact.vcard.username + verticalAlignment: Text.AlignVCenter + } + + // Container. + Loader { + id: loader + + Layout.fillWidth: true + Layout.fillHeight: true + sourceComponent: container1 + } + } + } + + // ------------------------------------------------------------------- + + states: State { + when: mouseArea.containsMouse + + PropertyChanges { + color: ContactsStyle.contact.backgroundColor.hovered + target: contact + } + + PropertyChanges { + color: ContactsStyle.contact.indicator.color + target: indicator + } + + PropertyChanges { + sourceComponent: container2 + target: loader + } + } } - } - - ActionButton { - anchors { - right: parent.right - verticalCenter: parent.verticalCenter - } - icon: 'delete' - iconSize: ContactsStyle.contact.deleteButtonSize - - onClicked: _removeContact($contact) - } - } - } - - // ------------------------------------------------------------------- - - Repeater { - id: actions - - readonly property var handlers: [ - CallsListModel.launchVideoCall, - CallsListModel.launchAudioCall, - function (sipAddress) { - window.setView('Conversation', { - peerAddress: sipAddress, - localAddress: AccountSettingsModel.sipAddress, - fullPeerAddress: sipAddress, - fullLocalAddress: AccountSettingsModel.fullSipAddress - }) - }, - function (sipAddress) { - //Logic.manageAccounts() - window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ManageChatRoom.qml'), { - //window.setView('Dialogs/ManageChatRoom', { - participantAddress: sipAddress - }) - } - ] - - model: handlers - - SipAddressesMenu { - relativeTo: loader - relativeY: loader.height - - sipAddresses: $contact.vcard.sipAddresses - - onSipAddressClicked: actions.handlers[index](sipAddress) - } - } - - // ------------------------------------------------------------------- - - Rectangle { - id: indicator - - anchors.left: parent.left - color: 'transparent' - height: parent.height - width: ContactsStyle.contact.indicator.width - } - - MouseArea { - id: mouseArea - - anchors.fill: parent - cursorShape: Qt.ArrowCursor - - MouseArea { - anchors.fill: parent - - onClicked: window.setView('ContactEdit', { - sipAddress: $contact.vcard.sipAddresses[0] - }) - } - - RowLayout { - anchors { - fill: parent - leftMargin: ContactsStyle.contact.leftMargin - rightMargin: ContactsStyle.contact.rightMargin - } - spacing: ContactsStyle.contact.spacing - - Item { - Layout.preferredHeight: parent.height - Layout.preferredWidth: parent.height - - Avatar { - anchors.centerIn: parent - - image: $contact.vcard.avatar - username: $contact.vcard.username - - height: ContactsStyle.contact.avatarSize - width: ContactsStyle.contact.avatarSize - } - } - - Text { - Layout.fillHeight: true - Layout.preferredWidth: ContactsStyle.contact.username.width - - color: ContactsStyle.contact.username.color - elide: Text.ElideRight - - font { - bold: true - pointSize: ContactsStyle.contact.username.pointSize - } - - text: $contact.vcard.username - verticalAlignment: Text.AlignVCenter - } - - // Container. - Loader { - id: loader - - Layout.fillWidth: true - Layout.fillHeight: true - sourceComponent: container1 - } - } - } - - // ------------------------------------------------------------------- - - states: State { - when: mouseArea.containsMouse - - PropertyChanges { - color: ContactsStyle.contact.backgroundColor.hovered - target: contact - } - - PropertyChanges { - color: ContactsStyle.contact.indicator.color - target: indicator - } - - PropertyChanges { - sourceComponent: container2 - target: loader - } - } - } - } - } - } + } + } + } } diff --git a/linphone-app/ui/views/App/Main/Conversation.qml b/linphone-app/ui/views/App/Main/Conversation.qml index 8c44b8f83..b31516375 100644 --- a/linphone-app/ui/views/App/Main/Conversation.qml +++ b/linphone-app/ui/views/App/Main/Conversation.qml @@ -3,6 +3,7 @@ import QtQuick.Layouts 1.3 import Common 1.0 import Linphone 1.0 +import Utils 1.0 import App.Styles 1.0 @@ -11,91 +12,121 @@ import 'Conversation.js' as Logic // ============================================================================= ColumnLayout { - id: conversation -/* - property string peerAddress - property string localAddress - property string fullPeerAddress - property string fullLocalAddress - property int isSecure*/ - property ChatRoomModel chatRoomModel - property string peerAddress : chatRoomModel.getPeerAddress() - property string localAddress : chatRoomModel.getLocalAddress() - property string fullPeerAddress : chatRoomModel.getFullPeerAddress() - property string fullLocalAddress : chatRoomModel.getFullLocalAddress() - - readonly property var _sipAddressObserver: SipAddressesModel.getSipAddressObserver((fullPeerAddress?fullPeerAddress:peerAddress), (fullLocalAddress?fullLocalAddress:localAddress)) - - // --------------------------------------------------------------------------- - - spacing: 0 - - // --------------------------------------------------------------------------- - // Contact bar. - // --------------------------------------------------------------------------- - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: ConversationStyle.bar.height - - color: ConversationStyle.bar.backgroundColor - - RowLayout { - anchors { - fill: parent - leftMargin: ConversationStyle.bar.leftMargin - rightMargin: ConversationStyle.bar.rightMargin - } - spacing: ConversationStyle.bar.spacing - - Avatar { - id: avatar - - Layout.preferredHeight: ConversationStyle.bar.avatarSize - Layout.preferredWidth: ConversationStyle.bar.avatarSize - - image: Logic.getAvatar() - - presenceLevel: Presence.getPresenceLevel( - conversation._sipAddressObserver.presenceStatus - ) - - //username: Logic.getUsername() - username: chatRoomModel.username - } - - ContactDescription { - Layout.fillHeight: true - Layout.fillWidth: true - - sipAddress: conversation.peerAddress - sipAddressColor: ConversationStyle.bar.description.sipAddressColor - username: avatar.username - usernameColor: ConversationStyle.bar.description.usernameColor - } - - Row { - Layout.fillHeight: true - - spacing: ConversationStyle.bar.actions.spacing - - ActionBar { - anchors.verticalCenter: parent.verticalCenter - iconSize: ConversationStyle.bar.actions.call.iconSize - - ActionButton { - icon: 'video_call' - visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.showStartVideoCallButton - - onClicked: CallsListModel.launchVideoCall(conversation.peerAddress) - } - - ActionButton { - icon: 'call' - visible: SettingsModel.outgoingCallsEnabled - - onClicked: CallsListModel.launchAudioCall(conversation.peerAddress) - } + id: conversation + // 1) chatRoomModel : chat + calls + conference + // 2) no chatRoomModel : calls + property string defaultPeerAddress + property string defaultLocalAddress + property string defaultFullPeerAddress + property string defaultFullLocalAddress + + + property ChatRoomModel chatRoomModel + property string peerAddress : chatRoomModel?chatRoomModel.getPeerAddress() : defaultPeerAddress + property string localAddress : chatRoomModel?chatRoomModel.getLocalAddress() : defaultLocalAddress + property string fullPeerAddress : chatRoomModel?chatRoomModel.getFullPeerAddress() : defaultFullPeerAddress + property string fullLocalAddress : chatRoomModel?chatRoomModel.getFullLocalAddress() : defaultFullLocalAddress + + property int securityLevel : chatRoomModel ? chatRoomModel.securityLevel : 1 + + readonly property var _sipAddressObserver: SipAddressesModel.getSipAddressObserver((fullPeerAddress?fullPeerAddress:peerAddress), (fullLocalAddress?fullLocalAddress:localAddress)) + + // --------------------------------------------------------------------------- + + spacing: 0 + clip:false + + // --------------------------------------------------------------------------- + // Contact bar. + // --------------------------------------------------------------------------- + + Rectangle { + id:mainBar + Layout.fillWidth: true + Layout.preferredHeight: ConversationStyle.bar.height + + color: ConversationStyle.bar.backgroundColor + clip:false + + RowLayout { + id:contactBar + anchors { + fill: parent + leftMargin: ConversationStyle.bar.leftMargin + rightMargin: ConversationStyle.bar.rightMargin + } + spacing: ConversationStyle.bar.spacing + + Avatar { + id: avatar + + Layout.preferredHeight: ConversationStyle.bar.avatarSize + Layout.preferredWidth: ConversationStyle.bar.avatarSize + + image: Logic.getAvatar() + /* + presenceLevel: Presence.getPresenceLevel( + conversation._sipAddressObserver.presenceStatus + )*/ + presenceLevel: chatRoomModel.presenceStatus + + //username: Logic.getUsername() + username: chatRoomModel?chatRoomModel.username:Logic.getUsername() + } + RowLayout{ + Layout.fillHeight: true + Layout.fillWidth: true + spacing:0 + ContactDescription { + Layout.fillHeight: true + Layout.minimumWidth: 20 + Layout.maximumWidth: contactBar.width-avatar.width-actionBar.width-3*ConversationStyle.bar.spacing + Layout.preferredWidth: contentWidth + //sipAddress: conversation.peerAddress + sipAddressColor: ConversationStyle.bar.description.sipAddressColor + username: avatar.username + usernameColor: ConversationStyle.bar.description.usernameColor + + sipAddress: (chatRoomModel? + (chatRoomModel.groupEnabled || chatRoomModel.isSecure()? + chatRoomModel.participants.usernamesToString() + : chatRoomModel.sipAddress + ):conversation.sipAddress || conversation.fullPeerAddress || conversation.peerAddress || '') + } + Icon{ + Layout.alignment: Qt.AlignVCenter + visible: securityLevel != 1 + icon: securityLevel === 2?'secure_level_1': securityLevel===3? 'secure_level_2' : 'secure_level_unsafe' + iconSize:30 + } + Item{//Spacer + Layout.fillWidth: true + } + } + + Row { + id:actionBar + Layout.fillHeight: true + + spacing: ConversationStyle.bar.actions.spacing + + ActionBar { + anchors.verticalCenter: parent.verticalCenter + iconSize: ConversationStyle.bar.actions.call.iconSize + + ActionButton { + icon: 'video_call' + visible: SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled && SettingsModel.showStartVideoCallButton + + onClicked: CallsListModel.launchVideoCall(conversation.peerAddress) + } + + ActionButton { + icon: 'call' + visible: SettingsModel.outgoingCallsEnabled + + onClicked: CallsListModel.launchAudioCall(conversation.peerAddress) + }/* ActionButton { icon: 'call_chat_unsecure' onClicked: { @@ -103,108 +134,161 @@ ColumnLayout { //window.setView('Dialogs/ManageChatRoom', { chatRoomModel:conversation.chatRoomModel })} - } - } - - ActionBar { - anchors.verticalCenter: parent.verticalCenter - - ActionButton { - icon: Logic.getEditIcon() - iconSize: ConversationStyle.bar.actions.edit.iconSize - visible: SettingsModel.contactsEnabled - - onClicked: window.setView('ContactEdit', { - sipAddress: conversation.peerAddress - }) - TooltipArea { - text: Logic.getEditTooltipText() - } - } - - ActionButton { - icon: 'delete' - iconSize: ConversationStyle.bar.actions.edit.iconSize - - onClicked: Logic.removeAllEntries() - - TooltipArea { - text: qsTr('cleanHistory') - } - } - } - } - } - } - - // --------------------------------------------------------------------------- - // Messages/Calls filters. - // --------------------------------------------------------------------------- - - Borders { - Layout.fillWidth: true - Layout.preferredHeight: active ? ConversationStyle.filters.height : 0 - - borderColor: ConversationStyle.filters.border.color - bottomWidth: ConversationStyle.filters.border.bottomWidth - color: ConversationStyle.filters.backgroundColor - topWidth: ConversationStyle.filters.border.topWidth - visible: SettingsModel.chatEnabled - - ExclusiveButtons { - anchors { - left: parent.left - leftMargin: ConversationStyle.filters.leftMargin - verticalCenter: parent.verticalCenter - } - - texts: [ - qsTr('displayCallsAndMessages'), - qsTr('displayCalls'), - qsTr('displayMessages') - ] - - onClicked: Logic.updateChatFilter(button) - } - } - - // --------------------------------------------------------------------------- - // Chat. - // --------------------------------------------------------------------------- - - Chat { - Layout.fillHeight: true - Layout.fillWidth: true - - proxyModel: ChatRoomProxyModel { - id: chatRoomProxyModel - - Component.onCompleted: { - if (!SettingsModel.chatEnabled) { - setEntryTypeFilter(ChatRoomModel.CallEntry) - } - resetMessageCount() - } - chatRoomModel: conversation.chatRoomModel - peerAddress: conversation.peerAddress - fullPeerAddress: conversation.fullPeerAddress - fullLocalAddress: conversation.fullLocalAddress - localAddress: conversation.localAddress// Reload is done on localAddress. Use this order - - } - } - + }*/ + } + + ActionBar { + id:actionsBar + anchors.verticalCenter: parent.verticalCenter + + ActionButton { + icon: Logic.getEditIcon() + iconSize: ConversationStyle.bar.actions.edit.iconSize + visible: SettingsModel.contactsEnabled + + onClicked: window.setView('ContactEdit', { + sipAddress: conversation.peerAddress + }) + TooltipArea { + text: Logic.getEditTooltipText() + } + } + + ActionButton { + icon: 'delete' + iconSize: ConversationStyle.bar.actions.edit.iconSize + + onClicked: Logic.removeAllEntries() + + TooltipArea { + text: qsTr('cleanHistory') + } + } + ActionButton { + id:dotButton + icon: 'menu_vdots' + iconSize: ConversationStyle.bar.actions.edit.iconSize + //autoIcon: true + + onClicked: { + conversationMenu.open() + } + + } + } + Menu{ + id:conversationMenu + x:mainBar.width-width + y:mainBar.height + width:250 + MenuItem{ + text:'Groupe informations' + iconMenu: 'menu_infos' + iconSizeMenu: 20 + onTriggered: { + window.detachVirtualWindow() + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/InfoChatRoom.qml') + ,{chatRoomModel:chatRoomModel}) + } + } + MenuItem{ + text:"Conversation's devices" + iconMenu: 'menu_devices' + iconSizeMenu: 20 + onTriggered: { + window.detachVirtualWindow() + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/ParticipantsDevices.qml') + ,{chatRoomModel:chatRoomModel}) + } + } + MenuItem{ + text:'Ephemeral messages' + iconMenu: 'menu_ephemeral' + iconSizeMenu: 20 + onTriggered: { + window.detachVirtualWindow() + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/EphemeralChatRoom.qml') + ,{chatRoomModel:chatRoomModel}) + } + } + } + } + } + + + } + + // --------------------------------------------------------------------------- + // Messages/Calls filters. + // --------------------------------------------------------------------------- + + Borders { + id:filtersBar + Layout.fillWidth: true + Layout.preferredHeight: active ? ConversationStyle.filters.height : 0 + + borderColor: ConversationStyle.filters.border.color + bottomWidth: ConversationStyle.filters.border.bottomWidth + color: ConversationStyle.filters.backgroundColor + topWidth: ConversationStyle.filters.border.topWidth + visible: SettingsModel.chatEnabled + + ExclusiveButtons { + anchors { + left: parent.left + leftMargin: ConversationStyle.filters.leftMargin + verticalCenter: parent.verticalCenter + } + + texts: [ + qsTr('displayCallsAndMessages'), + qsTr('displayCalls'), + qsTr('displayMessages') + ] + + onClicked: Logic.updateChatFilter(button) + } + } + + // --------------------------------------------------------------------------- + // Chat. + // --------------------------------------------------------------------------- + + Chat { + id:chatArea + Layout.fillHeight: true + Layout.fillWidth: true + + proxyModel: ChatRoomProxyModel { + id: chatRoomProxyModel + + Component.onCompleted: { + if (!SettingsModel.chatEnabled) { + setEntryTypeFilter(ChatRoomModel.CallEntry) + } + resetMessageCount() + } + chatRoomModel: conversation.chatRoomModel + peerAddress: conversation.peerAddress + fullPeerAddress: conversation.fullPeerAddress + fullLocalAddress: conversation.fullLocalAddress + localAddress: conversation.localAddress// Reload is done on localAddress. Use this order + } + } + /* Connections { - target: SettingsModel - onChatEnabledChanged: chatRoomProxyModel.setEntryTypeFilter(status ? ChatRoomModel.GenericEntry : ChatRoomModel.CallEntry) - } - - Connections { - target: AccountSettingsModel - onAccountSettingsUpdated: { - if (conversation.localAddress !== AccountSettingsModel.sipAddress) { - window.setView('Home') - } - } - } + target: SettingsModel + onChatEnabledChanged: chatRoomProxyModel.setEntryTypeFilter(status ? ChatRoomModel.GenericEntry : ChatRoomModel.CallEntry) + }*/ + + Connections { + target: AccountSettingsModel + onAccountSettingsUpdated: { + if (conversation.localAddress !== AccountSettingsModel.sipAddress) { + window.setView('Home') + } + } + } + + } diff --git a/linphone-app/ui/views/App/Main/Dialogs/About.qml b/linphone-app/ui/views/App/Main/Dialogs/About.qml index 7635e50eb..1a3729831 100644 --- a/linphone-app/ui/views/App/Main/Dialogs/About.qml +++ b/linphone-app/ui/views/App/Main/Dialogs/About.qml @@ -17,7 +17,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter objectName: '__about' height: AboutStyle.height diff --git a/linphone-app/ui/views/App/Main/Dialogs/AuthenticationRequest.qml b/linphone-app/ui/views/App/Main/Dialogs/AuthenticationRequest.qml index bc935ff9c..1a92e2651 100644 --- a/linphone-app/ui/views/App/Main/Dialogs/AuthenticationRequest.qml +++ b/linphone-app/ui/views/App/Main/Dialogs/AuthenticationRequest.qml @@ -34,7 +34,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter descriptionText: qsTr('authenticationRequestDescription') height: AuthenticationRequestStyle.height diff --git a/linphone-app/ui/views/App/Main/Dialogs/EphemeralChatRoom.qml b/linphone-app/ui/views/App/Main/Dialogs/EphemeralChatRoom.qml new file mode 100644 index 000000000..73bb15254 --- /dev/null +++ b/linphone-app/ui/views/App/Main/Dialogs/EphemeralChatRoom.qml @@ -0,0 +1,97 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +//import LinphoneUtils 1.0 +import LinphoneEnums 1.0 + +import App.Styles 1.0 +import Common.Styles 1.0 +import Colors 1.0 +import Units 1.0 + + +// ============================================================================= + +DialogPlus { + id:dialog + buttons: [ + TextButtonA { + text: 'CANCEL' + + onClicked:{ + exit(0) + } + }, + TextButtonB { + text: 'START' + + onClicked: { + console.log("Timer selected : " +dialog.timer) + if(dialog.timer=== 0) + chatRoomModel.ephemeralEnabled = false + else { + chatRoomModel.ephemeralLifetime = dialog.timer + chatRoomModel.ephemeralEnabled = true + } + exit(1) + } + } + ] + flat : true + + title: "Ephemeral messages" + + property ChatRoomModel chatRoomModel + property int timer : 0 + buttonsAlignment: Qt.AlignCenter + + height: ManageAccountsStyle.height + width: ManageAccountsStyle.width + + // --------------------------------------------------------------------------- + ColumnLayout { + anchors.fill: parent + anchors.topMargin: 15 + anchors.leftMargin: 10 + anchors.rightMargin: 10 + spacing: 0 + + Layout.alignment: Qt.AlignCenter + Icon{ + icon:'timer' + iconSize:50 + Layout.preferredHeight: 50 + Layout.preferredWidth: 50 + } + Text{ + Layout.fillWidth: true + maximumLineCount: 2 + text: 'New messages will be deleted on both ends once it has been read by your contact. Select a timeout.' + } + ComboBox{ + id:timerPicker + textRole: "text" + model:ListModel{ + ListElement{ text:'Disabled' + value:0} + ListElement{ text:'1 minute' + value:60} + ListElement{ text:'1 heure' + value:3600} + ListElement{ text:'1 jour' + value:86400} + ListElement{ text:'3 jours' + value:259200} + ListElement{ text:'1 semaine' + value:604800} + } + + onActivated: dialog.timer = model.get(index).value + + } + + } + +} \ No newline at end of file diff --git a/linphone-app/ui/views/App/Main/Dialogs/InfoChatRoom.qml b/linphone-app/ui/views/App/Main/Dialogs/InfoChatRoom.qml new file mode 100644 index 000000000..8f0f38447 --- /dev/null +++ b/linphone-app/ui/views/App/Main/Dialogs/InfoChatRoom.qml @@ -0,0 +1,136 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +//import LinphoneUtils 1.0 +import LinphoneEnums 1.0 + +import App.Styles 1.0 +import Common.Styles 1.0 +import Colors 1.0 +import Units 1.0 + + +// ============================================================================= + +DialogPlus { + id:dialog + buttons: [ + TextButtonA { + text: 'QUITTER LE GROUPE' + + onClicked:{ + chatRoomModel.leaveChatRoom(); + exit(0) + } + }, + TextButtonB { + text: 'OK' + + onClicked: { + exit(1) + } + } + ] + flat : true + + title: "Group information" + + property ChatRoomModel chatRoomModel + buttonsAlignment: Qt.AlignCenter + + height: ManageAccountsStyle.height + width: ManageAccountsStyle.width + + // --------------------------------------------------------------------------- + ColumnLayout { + anchors.fill: parent + anchors.topMargin: 15 + anchors.leftMargin: 10 + anchors.rightMargin: 10 + spacing: 0 + + SmartSearchBar { + id: smartSearchBar + + Layout.fillWidth: true + Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing + + showHeader:false + + maxMenuHeight: MainWindowStyle.searchBox.maxHeight + placeholderText: 'toto' + tooltipText: 'tooltip' + actions:[{ + icon: 'add_participant', + secure:0, + handler: function (entry) { + selectedParticipants.add(entry.sipAddress) + smartSearchBar.addAddressToIgnore(entry.sipAddress); + ++lastContacts.reloadCount + }, + }] + + onEntryClicked: { + selectedParticipants.append({$sipAddress:entry}) + } + } + + Text{ + Layout.preferredHeight: 20 + Layout.rightMargin: 65 + Layout.alignment: Qt.AlignRight | Qt.AlignBottom + Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing + text : 'Admin' + + color: Colors.g + font.pointSize: Units.dp * 11 + font.weight: Font.Light + visible: participantView.count > 0 + + } + ScrollableListViewField { + Layout.fillHeight: true + Layout.fillWidth: true + Layout.bottomMargin: 5 + + //readOnly: toAddView.count >= conferenceManager.maxParticipants + textFieldStyle: TextFieldStyle.unbordered + + ParticipantsView { + id: participantView + anchors.fill: parent + + showContactAddress:false + showSwitch : true + showSeparator: false + isSelectable: false + + + actions: [{ + icon: 'remove_participant', + tooltipText: 'Remove this participant from the selection', + handler: function (entry) { + smartSearchBar.removeAddressToIgnore(entry.sipAddress) + selectedParticipants.remove(entry) + ++lastContacts.reloadCount + } + }] + + genSipAddress: '' + + model: ParticipantProxyModel { + id:selectedParticipants + chatRoomModel:dialog.chatRoomModel + + } + + onEntryClicked: actions[0].handler(entry) + + } + } + + } + +} \ No newline at end of file diff --git a/linphone-app/ui/views/App/Main/Dialogs/ManageAccounts.qml b/linphone-app/ui/views/App/Main/Dialogs/ManageAccounts.qml index 38a486d20..84282c6bf 100644 --- a/linphone-app/ui/views/App/Main/Dialogs/ManageAccounts.qml +++ b/linphone-app/ui/views/App/Main/Dialogs/ManageAccounts.qml @@ -19,7 +19,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter objectName: '__manageAccounts' height: SettingsModel.rlsUriEnabled ? ManageAccountsStyle.height : ManageAccountsStyle.heightWithoutPresence diff --git a/linphone-app/ui/views/App/Main/Dialogs/ManageChatRoom.qml b/linphone-app/ui/views/App/Main/Dialogs/ManageChatRoom.qml index e3da10eaa..6518f66a5 100644 --- a/linphone-app/ui/views/App/Main/Dialogs/ManageChatRoom.qml +++ b/linphone-app/ui/views/App/Main/Dialogs/ManageChatRoom.qml @@ -40,7 +40,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter height: ManageAccountsStyle.height width: ManageAccountsStyle.width diff --git a/linphone-app/ui/views/App/Main/Dialogs/NewChatRoom.qml b/linphone-app/ui/views/App/Main/Dialogs/NewChatRoom.qml new file mode 100644 index 000000000..fd6ae9cc1 --- /dev/null +++ b/linphone-app/ui/views/App/Main/Dialogs/NewChatRoom.qml @@ -0,0 +1,486 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +//import LinphoneUtils 1.0 +import LinphoneEnums 1.0 + +import App.Styles 1.0 +import Common.Styles 1.0 +import Colors 1.0 +import Units 1.0 +import Tools 1.0 + +// ============================================================================= + +DialogPlus { + id: conferenceManager + property ChatRoomModel chatRoomModel + + readonly property int maxParticipants: 20 + readonly property int minParticipants: 1 + + buttons: [ + TextButtonA { + text: 'CANCEL' + + onClicked: exit(0) + }, + TextButtonB { + //enabled: toAddView.count >= conferenceManager.minParticipants + text: 'LANCER' + + onClicked: { + if(CallsListModel.createChatRoom(subject.text, secureSwitch.checked, selectedParticipants.getParticipants() )) + exit(1) + } + } + ] + + buttonsAlignment: Qt.AlignRight + title:'Lancer un chat de groupe' + + height: 500 + width: 800 + + // --------------------------------------------------------------------------- + + RowLayout { + anchors.fill: parent + spacing: 0 + + // ------------------------------------------------------------------------- + // Address selector. + // ------------------------------------------------------------------------- + + Item { + Layout.fillHeight: true + Layout.fillWidth: true + + ColumnLayout { + anchors.fill: parent + spacing: 20 + + Item{ + Layout.fillHeight: true + } + ColumnLayout{ + Layout.fillWidth: true + Layout.topMargin:15 + spacing:4 + Text { + Layout.fillWidth: true + text:'Would you like to encrypt your chat?' + color: Colors.g + font.pointSize: Units.dp * 11 + font.weight: Font.DemiBold + } + Item{ + Layout.fillWidth: true + Layout.preferredHeight: 50 + Icon{ + id:secureOff + anchors.left:parent.left + anchors.leftMargin : 5 + anchors.verticalCenter: parent.verticalCenter + width:20 + height:20 + icon: 'secure_off' + iconSize:20 + } + Switch{ + id:secureSwitch + anchors.left:secureOff.right + anchors.leftMargin : 5 + anchors.verticalCenter: parent.verticalCenter + width:50 + //Layout.preferredWidth: 50 + enabled:true + onClicked: checked = !checked + indicatorStyle: SwitchStyle.aux + } + Icon{ + id:secureOn + anchors.left:secureSwitch.right + anchors.leftMargin : 15 + anchors.verticalCenter: parent.verticalCenter + width:20 + height:20 + icon: 'secure_on' + iconSize:20 + } + } + } + + Item{ + Layout.fillHeight:true + } + ColumnLayout { + Layout.fillWidth: true + //Layout.preferredHeight: 90 + spacing:10 + Text{ + textFormat: Text.RichText + text :'Nom du groupe' +'*' + color: Colors.g + font.pointSize: Units.dp * 11 + font.weight: Font.DemiBold + } + TextField { + id:subject + Layout.fillWidth: true + Layout.rightMargin: 15 + placeholderText :"Nommer le groupe" + text:(chatRoomModel?chatRoomModel.getSubject():'') + Keys.onReturnPressed: nextItemInFocusChain().forceActiveFocus() + //error : text == '' + TooltipArea{ + text : 'Current subject of the ChatRoom. It cannot be empty' + } + } + + } + Item{ + Layout.fillHeight:true + } + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + //Layout.preferredHeight: 200 + spacing:20 + Text{ + text :'Contacts récents' + color: Colors.g + font.pointSize: Units.dp * 11 + font.weight: Font.DemiBold + } + RowLayout{ + Layout.fillWidth: true + Layout.fillHeight : true + spacing:10 + + Repeater{ + id:lastContacts + property int reloadCount : 0 + model:TimelineListModel.getLastChatRooms(5) + //[{username:'Danyl Robertson'}, {username:'Toto harrytop'}] + delegate : + Item{ + Layout.fillHeight: true + Layout.preferredWidth: 60 + ColumnLayout{ + anchors.fill:parent + Avatar{ + id:avatar2 + Layout.preferredHeight: 50 + Layout.preferredWidth: 50 + Layout.alignment: Qt.AlignCenter + username: modelData.username + image:modelData.avatar + Icon{ + property int securityLevel : 2 + anchors.right: parent.right + anchors.top:parent.top + anchors.topMargin: -5 + visible: Tools.hasCapability(modelData.sipAddress, LinphoneEnums.FriendCapabilityLimeX3Dh) + icon: 'secure_on' + iconSize:20 + Rectangle{ + id:secureMask + anchors.fill:parent + color:'white' + opacity: 0.5 + visible: smartSearchBar.isIgnored(modelData.sipAddress) + Connections{// Workaround for refreshing data on events + target:lastContacts + onReloadCountChanged: { + secureMask.visible=smartSearchBar.isIgnored(modelData.sipAddress) + } + } + } + } + } + Text{ + Layout.fillHeight: true + //Layout.maximumHeight: 100 + Layout.preferredWidth: 60 + Layout.alignment: Qt.AlignVCenter | Qt.AlignTop + maximumLineCount: 5 + wrapMode:Text.Wrap + text: modelData.username + verticalAlignment: Text.AlignTop + horizontalAlignment: Text.AlignHCenter + + font.weight: Font.DemiBold + lineHeight: 0.8 + color: Colors.g + font.pointSize: Units.dp * 9 + clip:false + } + } + + Rectangle{ + id:mask + anchors.fill:parent + color:'white' + opacity: 0.5 + visible: smartSearchBar.isIgnored(modelData.sipAddress) + Connections{// Workaround for refreshing data on events + target:lastContacts + onReloadCountChanged: { + mask.visible=smartSearchBar.isIgnored(modelData.sipAddress) + } + } + } + MouseArea{ + anchors.fill:parent + visible:!mask.visible + onClicked: { + selectedParticipants.add(modelData.sipAddress) + smartSearchBar.addAddressToIgnore(modelData.sipAddress); + ++lastContacts.reloadCount + } + } + } + } + } + } + Item{ + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } + /* + ScrollableListViewField { + Layout.fillHeight: true + Layout.fillWidth: true + + readOnly: toAddView.count >= conferenceManager.maxParticipants + + SipAddressesView { + anchors.fill: parent + + actions: [{ + icon: 'transfer', + handler: function (entry) { + conferenceHelperModel.toAdd.addToConference(entry.sipAddress) + } + }] + + genSipAddress: filter.text + + model: ConferenceHelperModel { + id: conferenceHelperModel + } + + onEntryClicked: actions[0].handler(entry) + } + } + } + } +*/ + // ------------------------------------------------------------------------- + // Separator. + // ------------------------------------------------------------------------- + /* + Rectangle { + Layout.fillHeight: true + Layout.leftMargin: ConferenceManagerStyle.columns.separator.leftMargin + Layout.preferredWidth: ConferenceManagerStyle.columns.separator.width + Layout.rightMargin: ConferenceManagerStyle.columns.separator.rightMargin + + color: ConferenceManagerStyle.columns.separator.color + } +*/ + // ------------------------------------------------------------------------- + // See and remove selected addresses. + // ------------------------------------------------------------------------- + ColumnLayout{ + Layout.fillHeight: true + Layout.fillWidth: true + Layout.topMargin: 10 + Layout.bottomMargin: 10 + Rectangle{ + Layout.fillHeight: true + Layout.fillWidth: true + border.width: 1 + border.color: "black" + + ColumnLayout { + anchors.fill: parent + anchors.topMargin: 15 + anchors.leftMargin: 10 + anchors.rightMargin: 10 + spacing: 0 + + SmartSearchBar { + id: smartSearchBar + + Layout.fillWidth: true + Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing + + showHeader:false + + maxMenuHeight: MainWindowStyle.searchBox.maxHeight + placeholderText: 'toto' + tooltipText: 'tooltip' + actions:[{ + icon: 'add_participant', + secure:0, + handler: function (entry) { + selectedParticipants.add(entry.sipAddress) + smartSearchBar.addAddressToIgnore(entry.sipAddress); + ++lastContacts.reloadCount + }, + }] + + onEntryClicked: { + selectedParticipants.append({$sipAddress:entry}) + } + //resultExceptions: selectedParticipants + } + + /* + TextField { + id: filter + + Layout.fillWidth: true + + icon: 'search' + + onTextChanged: conferenceHelperModel.setFilter(text) + } + */ + Text{ + Layout.preferredHeight: 20 + Layout.rightMargin: 65 + Layout.alignment: Qt.AlignRight | Qt.AlignBottom + Layout.topMargin: ConferenceManagerStyle.columns.selector.spacing + text : 'Admin' + + color: Colors.g + font.pointSize: Units.dp * 11 + font.weight: Font.Light + visible: participantView.count > 0 + + } + ScrollableListViewField { + Layout.fillHeight: true + Layout.fillWidth: true + Layout.bottomMargin: 5 + + //readOnly: toAddView.count >= conferenceManager.maxParticipants + textFieldStyle: TextFieldStyle.unbordered + + ParticipantsView { + id: participantView + anchors.fill: parent + + showContactAddress:false + showSwitch : true + showSeparator: false + isSelectable: false + + + actions: [{ + icon: 'remove_participant', + tooltipText: 'Remove this participant from the selection', + handler: function (entry) { + smartSearchBar.removeAddressToIgnore(entry.sipAddress) + selectedParticipants.remove(entry) + ++lastContacts.reloadCount + } + }] + + genSipAddress: '' + + model: ParticipantProxyModel { + id:selectedParticipants + chatRoomModel:null + + } + + onEntryClicked: actions[0].handler(entry) + + } + } + + } + + } + + /* + SearchBox{ + id: searchBox + anchors.left:parent.left + anchors.right:parent.right + anchors.top:parent.top + anchors.topMargin: 30 + anchors.leftMargin:15 + anchors.rightMargin: 15 + + placeholderText:'Search contact or enter SIP address' + + entryHeight: 200 + SipAddressesView { + id: view + actions: [{ + icon: 'add', + secure:0, + handler: function (entry) { + //searchBox.closeMenu() + //searchBox.launchVideoCall(entry.sipAddress) + }, + visible: true + }] + genSipAddress: searchBox.filter + + model: SearchSipAddressesModel {} + } + + } + ScrollableListViewField { + anchors.top:search.bottom + anchors.bottom:parent.bottom + anchors.left:parent.left + anchors.right:parent.right + anchors.leftMargin:15 + anchors.rightMargin: 15 + anchors.topMargin: 15 + + + SipAddressesView { + id: toAddView + + anchors.fill: parent + + actions: [{ + icon: 'cancel', + handler: function (entry) { + //model.removeFromConference(entry.sipAddress) + } + }] + + //model: conferenceHelperModel.toAdd + + //onEntryClicked: actions[0].handler(entry) + } + } + }*/ + Item{ + Layout.fillWidth: true + Layout.preferredHeight: 20 + Text{ + anchors.fill:parent + textFormat: Text.RichText + text : '* Obligatoire' + //font.weight: Font.DemiBold + color: Colors.g + font.pointSize: Units.dp * 8 + } + } + } + } +} \ No newline at end of file diff --git a/linphone-app/ui/views/App/Main/Dialogs/ParticipantsDevices.qml b/linphone-app/ui/views/App/Main/Dialogs/ParticipantsDevices.qml new file mode 100644 index 000000000..20ec23f5e --- /dev/null +++ b/linphone-app/ui/views/App/Main/Dialogs/ParticipantsDevices.qml @@ -0,0 +1,159 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 +import Utils 1.0 +import LinphoneUtils 1.0 +import LinphoneEnums 1.0 + +import App.Styles 1.0 +import Units 1.0 + + +// ============================================================================= + +DialogPlus { + id:dialog + buttons: [] + flat : true + + title: "Conversation's devices" + + property ChatRoomModel chatRoomModel + buttonsAlignment: Qt.AlignCenter + + height: ManageAccountsStyle.height + width: ManageAccountsStyle.width + + // --------------------------------------------------------------------------- + + ScrollableListViewField { + anchors.fill:parent + radius: 0 + + ScrollableListView { + id: view + anchors.fill: parent + model: ParticipantProxyModel{ + chatRoomModel : dialog.chatRoomModel + } + + delegate: + ColumnLayout{ + width:parent.width + spacing: 0 + RowLayout { + id: item + Layout.fillWidth: true + Layout.preferredHeight: 50 + Avatar{ + id:avatar + Layout.preferredHeight: 32 + Layout.preferredWidth: 32 + Layout.leftMargin: 14 + username: modelData?(modelData.contactModel ? modelData.contactModel.vcard.username + :modelData.username?modelData.username: + LinphoneUtils.getContactUsername(modelData.sipAddress) + ):'' + onUsernameChanged: console.log(username) + Icon{ + anchors.right: parent.right + anchors.top:parent.top + anchors.topMargin: -5 + visible: modelData && modelData.securityLevel !== 1 + icon: modelData?(modelData.securityLevel === 2?'secure_level_1': modelData.securityLevel===3? 'secure_level_2' : 'secure_level_unsafe'):'secure_level_unsafe' + iconSize:15 + } + } + ContactDescription{ + Layout.fillHeight: true + Layout.fillWidth: true + Layout.leftMargin: 14 + username: avatar.username + } + + ActionButton { + Layout.preferredHeight: 20 + Layout.preferredWidth: 20 + Layout.leftMargin: 14 + Layout.rightMargin: 14 + icon: modelData.deviceCount > 1? + (participantDevices.visible ? 'expanded' : 'collapsed') + : (modelData.securityLevel === 2?'secure_level_1': + (modelData.securityLevel===3? 'secure_level_2' : 'secure_level_unsafe') + ) + iconSize: 20 + visible:true + useStates: false + onClicked: participantDevices.visible = !participantDevices.visible + } + } + Rectangle { + color: "#ebebeb" + Layout.preferredHeight: 1 + Layout.fillWidth: true + } + ListView{ + id:participantDevices + + Layout.fillWidth: true + Layout.preferredHeight: item.height * count + + interactive: false + model: modelData.getProxyDevices() + + delegate: Rectangle{ + width:parent.width + height:50 + color: '#f5f5f5' + RowLayout{ + anchors.fill:parent + Text{ + Layout.fillWidth: true + Layout.fillHeight: true + Layout.leftMargin: avatar.width+14*2 + font.weight: Font.Light + //color: DialogStyle.description.color + font.pointSize: Units.dp * 11 + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WordWrap + + text:modelData.name + + MouseArea{ + anchors.fill:parent + onClicked: CallsListModel.launchSecureAudioCall(modelData.address, LinphoneEnums.MediaEncryptionZrtp) + } + } + Icon{ + property int securityLevel : modelData.securityLevel + Layout.rightMargin: 14 + Layout.preferredHeight: 20 + Layout.preferredWidth: 20 + visible: securityLevel !== 1 + icon: securityLevel === 2?'secure_level_1': securityLevel===3? 'secure_level_2' : 'secure_level_unsafe' + iconSize:20 + Timer{// Workaround : no security events are send when device's security change. + onTriggered: parent.securityLevel = modelData.securityLevel + repeat:true + running:true + interval:500 + } + } + } + Rectangle { + color: "#ebebeb" + anchors.left : parent.left + anchors.right :parent.right + anchors.bottom: parent.bottom + height: 1 + visible: (index !== (model.count - 1)) + } + } + visible:true + } + } + } + } +} \ No newline at end of file diff --git a/linphone-app/ui/views/App/Main/MainWindow.js b/linphone-app/ui/views/App/Main/MainWindow.js index 44cd7dce9..c1ed28999 100644 --- a/linphone-app/ui/views/App/Main/MainWindow.js +++ b/linphone-app/ui/views/App/Main/MainWindow.js @@ -110,13 +110,12 @@ function updateSelectedEntry (view, props) { var timeline = item.timeline if (view === 'Home') { - item.homeEntry.select() - timeline.resetSelectedEntry() + menu.resetSelectedEntry() } else if (view === 'Contacts') { item.contactsEntry.select() //timeline.resetSelectedEntry() } else { - menu.resetSelectedEntry() + //menu.resetSelectedEntry() /* if (view === 'Conversation') { timeline.setSelectedEntry(props.peerAddress, props.localAddress) diff --git a/linphone-app/ui/views/App/Main/MainWindow.qml b/linphone-app/ui/views/App/Main/MainWindow.qml index b6033d8bc..8f2cbe3a8 100644 --- a/linphone-app/ui/views/App/Main/MainWindow.qml +++ b/linphone-app/ui/views/App/Main/MainWindow.qml @@ -71,7 +71,7 @@ ApplicationWindow { // Workaround to get these properties in `MainWindow.js`. readonly property alias contactsEntry: contactsEntry readonly property alias contentLoader: contentLoader - readonly property alias homeEntry: homeEntry + readonly property alias conferencesEntry: conferencesEntry readonly property alias menu: menu readonly property alias timeline: timeline @@ -91,6 +91,7 @@ ApplicationWindow { ToolBar { Layout.fillWidth: true Layout.preferredHeight: MainWindowStyle.toolBar.height + hoverEnabled : true background: MainWindowStyle.toolBar.background @@ -101,12 +102,30 @@ ApplicationWindow { rightMargin: MainWindowStyle.toolBar.rightMargin } spacing: MainWindowStyle.toolBar.spacing + + ActionButton { + icon: 'panel_shown' + tooltipText : 'Open Timeline' + iconSize: MainWindowStyle.panelButtonSize + //autoIcon: true + onClicked: Logic.openTimeline() + } + ActionButton { + icon: 'home' + tooltipText : 'Open Home' + iconSize: MainWindowStyle.homeButtonSize + //autoIcon: true + onClicked: setView('Home') + } AccountStatus { id: accountStatus - - Layout.fillHeight: parent.height + betterIcon:true + Layout.preferredHeight: parent.height Layout.preferredWidth: MainWindowStyle.accountStatus.width + Layout.fillWidth: false + //height: parent.height + //width: MainWindowStyle.accountStatus.width TooltipArea { text: AccountSettingsModel.sipAddress @@ -119,8 +138,9 @@ ApplicationWindow { } } - Column { + ColumnLayout { Layout.preferredWidth: MainWindowStyle.autoAnswerStatus.width + visible: SettingsModel.autoAnswerStatus Icon { icon: SettingsModel.autoAnswerStatus @@ -181,19 +201,30 @@ ApplicationWindow { onLaunchVideoCall: CallsListModel.launchVideoCall(sipAddress) } + + + ActionButton { + icon: 'new_chat_group' + tooltipText : 'Open Conference' + iconSize: MainWindowStyle.newConferenceSize + //autoIcon: true + onClicked: { + window.detachVirtualWindow() + window.attachVirtualWindow(Qt.resolvedUrl('Dialogs/NewChatRoom.qml') + ,{}) + } + } ActionButton { icon: 'new_conference' iconSize: MainWindowStyle.newConferenceSize visible: SettingsModel.conferenceEnabled + tooltipText:qsTr('newConferenceButton') + //autoIcon: true onClicked: Logic.openConferenceManager() - - TooltipArea { - text: qsTr('newConferenceButton') - } } - +/* ActionButton { icon: 'burger_menu' iconSize: MainWindowStyle.menuBurgerSize @@ -205,6 +236,7 @@ ApplicationWindow { } } + */ } } Loader{ @@ -231,20 +263,11 @@ ApplicationWindow { ApplicationMenu { id: menu - defaultSelectedEntry: homeEntry + defaultSelectedEntry: null entryHeight: MainWindowStyle.menu.height entryWidth: MainWindowStyle.menu.width - ApplicationMenuEntry { - id: homeEntry - - icon: 'home' - name: qsTr('homeEntry') - - onSelected: setView('Home') - } - ApplicationMenuEntry { id: contactsEntry @@ -254,6 +277,16 @@ ApplicationWindow { onSelected: setView('Contacts') } + + ApplicationMenuEntry { + id: conferencesEntry + + icon: 'conferences' + iconSize: 32 + name: 'MES CONFERENCES' + + onSelected: setView('HistoryView') + } } // History. diff --git a/linphone-app/ui/views/App/Settings/Dialogs/SettingsLdapEdit.qml b/linphone-app/ui/views/App/Settings/Dialogs/SettingsLdapEdit.qml index 46dcca50e..d77946da9 100644 --- a/linphone-app/ui/views/App/Settings/Dialogs/SettingsLdapEdit.qml +++ b/linphone-app/ui/views/App/Settings/Dialogs/SettingsLdapEdit.qml @@ -30,7 +30,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter height: SettingsSipAccountsEditStyle.height width: SettingsSipAccountsEditStyle.width diff --git a/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml b/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml index 1580baaf7..a15f24944 100644 --- a/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml +++ b/linphone-app/ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml @@ -32,7 +32,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter height: SettingsSipAccountsEditStyle.height width: SettingsSipAccountsEditStyle.width diff --git a/linphone-app/ui/views/App/Settings/Dialogs/SettingsVideoPreview.qml b/linphone-app/ui/views/App/Settings/Dialogs/SettingsVideoPreview.qml index d21260626..96e6efdb4 100644 --- a/linphone-app/ui/views/App/Settings/Dialogs/SettingsVideoPreview.qml +++ b/linphone-app/ui/views/App/Settings/Dialogs/SettingsVideoPreview.qml @@ -18,7 +18,7 @@ DialogPlus { } ] - centeredButtons: true + buttonsAlignment: Qt.AlignCenter height: SettingsVideoPreviewStyle.height width: SettingsVideoPreviewStyle.width diff --git a/linphone-app/ui/views/App/Styles/Main/MainWindowStyle.qml b/linphone-app/ui/views/App/Styles/Main/MainWindowStyle.qml index 2cb803be9..644fb1474 100644 --- a/linphone-app/ui/views/App/Styles/Main/MainWindowStyle.qml +++ b/linphone-app/ui/views/App/Styles/Main/MainWindowStyle.qml @@ -12,6 +12,8 @@ QtObject { property int minimumHeight: 610 property int minimumWidth: 950 property int width: 950 + property int panelButtonSize : 20 + property int homeButtonSize: 40 property QtObject accountStatus: QtObject { property int width: 200 @@ -38,9 +40,9 @@ QtObject { property QtObject toolBar: QtObject { property int height: 70 - property int leftMargin: 20 - property int rightMargin: 20 - property int spacing: 20 + property int leftMargin: 18 + property int rightMargin: 18 + property int spacing: 16 property var background: Rectangle { color: Colors.f