From e55f2183524e73c92195f761d7dc52845bbee171 Mon Sep 17 00:00:00 2001 From: Julien Wadel Date: Wed, 18 Nov 2020 12:04:06 +0100 Subject: [PATCH] - Fix : display name didn't support UTF8. Address created from Factory take a UTF8 string, aswell as setDisplayName - Show Historic calls - Handle cursor shapes on mousearea --- linphone-app/CMakeLists.txt | 4 + linphone-app/assets/languages/da.ts | 19 ++ linphone-app/assets/languages/de.ts | 19 ++ linphone-app/assets/languages/en.ts | 19 ++ linphone-app/assets/languages/es.ts | 19 ++ linphone-app/assets/languages/fr_FR.ts | 19 ++ linphone-app/assets/languages/hu.ts | 19 ++ linphone-app/assets/languages/it.ts | 19 ++ linphone-app/assets/languages/ja.ts | 19 ++ linphone-app/assets/languages/lt.ts | 19 ++ linphone-app/assets/languages/pt_BR.ts | 19 ++ linphone-app/assets/languages/ru.ts | 19 ++ linphone-app/assets/languages/sv.ts | 19 ++ linphone-app/assets/languages/tr.ts | 19 ++ linphone-app/assets/languages/uk.ts | 19 ++ linphone-app/assets/languages/zh_CN.ts | 19 ++ linphone-app/resources.qrc | 10 +- linphone-app/src/app/App.cpp | 2 + linphone-app/src/components/Components.hpp | 1 + .../src/components/calls/CallsListModel.cpp | 4 +- .../src/components/core/CoreManager.cpp | 8 + .../src/components/core/CoreManager.hpp | 11 +- .../AbstractEventCountNotifier.cpp | 24 +- .../AbstractEventCountNotifier.hpp | 7 +- .../src/components/history/HistoryModel.cpp | 256 ++++++++++++++++++ .../src/components/history/HistoryModel.hpp | 96 +++++++ .../components/history/HistoryProxyModel.cpp | 161 +++++++++++ .../components/history/HistoryProxyModel.hpp | 68 +++++ .../sip-addresses/SipAddressesModel.cpp | 18 +- .../sip-addresses/SipAddressesModel.hpp | 6 + .../src/components/timeline/TimelineModel.cpp | 6 +- .../Form/Buttons/AbstractTextButton.qml | 8 +- .../Common/Form/Buttons/FileChooserButton.qml | 1 - .../Common/Form/Buttons/SmallButton.qml | 8 +- .../Form/Fields/ScrollableListViewField.qml | 2 +- .../modules/Common/Form/Fields/TextField.qml | 2 +- .../ui/modules/Common/Form/MouseArea.qml | 11 + .../ui/modules/Common/Form/Switch.qml | 3 +- .../ui/modules/Common/Helpers/DragBox.qml | 1 + .../Common/Helpers/InvertedMouseArea.qml | 1 + .../Common/Helpers/InvertedMouseArea.spec.qml | 2 +- .../Common/Menus/ApplicationMenuEntry.qml | 1 - .../Common/Menus/DropDownStaticMenuEntry.qml | 2 +- linphone-app/ui/modules/Common/Misc/Paned.qml | 1 + .../modules/Common/Window/VirtualWindow.qml | 2 + linphone-app/ui/modules/Common/qmldir | 1 + .../Linphone/Account/AccountStatus.qml | 6 +- .../ui/modules/Linphone/Chat/Chat.qml | 1 + .../ui/modules/Linphone/Chat/FileMessage.qml | 8 - .../modules/Linphone/Codecs/CodecsViewer.qml | 3 +- .../ui/modules/Linphone/History/Event.qml | 105 +++++++ .../ui/modules/Linphone/History/History.js | 67 +++++ .../ui/modules/Linphone/History/History.qml | 205 ++++++++++++++ .../Linphone/Menus/SipAddressesMenu.qml | 1 - .../Notifications/NotificationBasic.qml | 2 - .../NotificationReceivedFileMessage.qml | 2 - .../NotificationReceivedMessage.qml | 2 - .../Linphone/Styles/History/HistoryStyle.qml | 61 +++++ .../Styles/Timeline/TimelineStyle.qml | 5 +- .../ui/modules/Linphone/Styles/qmldir | 2 + .../ui/modules/Linphone/Timeline/Timeline.qml | 11 +- .../Linphone/View/SipAddressesView.qml | 9 +- linphone-app/ui/modules/Linphone/qmldir | 2 + .../scripts/LinphoneUtils/linphone-utils.js | 47 ++-- .../App/Calls/IncallFullscreenWindow.qml | 2 +- linphone-app/ui/views/App/Main/Contacts.qml | 6 +- .../ui/views/App/Main/Conversation.qml | 6 +- linphone-app/ui/views/App/Main/HistoryView.js | 65 +++++ .../ui/views/App/Main/HistoryView.qml | 150 ++++++++++ linphone-app/ui/views/App/Main/MainWindow.qml | 19 +- .../ui/views/App/Settings/SettingsWindow.qml | 1 + .../App/Styles/Main/HistoryViewStyle.qml | 50 ++++ linphone-app/ui/views/App/Styles/qmldir | 1 + 73 files changed, 1753 insertions(+), 99 deletions(-) create mode 100644 linphone-app/src/components/history/HistoryModel.cpp create mode 100644 linphone-app/src/components/history/HistoryModel.hpp create mode 100644 linphone-app/src/components/history/HistoryProxyModel.cpp create mode 100644 linphone-app/src/components/history/HistoryProxyModel.hpp create mode 100644 linphone-app/ui/modules/Common/Form/MouseArea.qml create mode 100644 linphone-app/ui/modules/Linphone/History/Event.qml create mode 100644 linphone-app/ui/modules/Linphone/History/History.js create mode 100644 linphone-app/ui/modules/Linphone/History/History.qml create mode 100644 linphone-app/ui/modules/Linphone/Styles/History/HistoryStyle.qml create mode 100644 linphone-app/ui/views/App/Main/HistoryView.js create mode 100644 linphone-app/ui/views/App/Main/HistoryView.qml create mode 100644 linphone-app/ui/views/App/Styles/Main/HistoryViewStyle.qml diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt index 709cd1ead..3bc9420ae 100644 --- a/linphone-app/CMakeLists.txt +++ b/linphone-app/CMakeLists.txt @@ -138,6 +138,8 @@ set(SOURCES src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp src/components/file/FileDownloader.cpp src/components/file/FileExtractor.cpp + src/components/history/HistoryModel.cpp + src/components/history/HistoryProxyModel.cpp src/components/notifier/Notifier.cpp src/components/other/clipboard/Clipboard.cpp src/components/other/colors/Colors.cpp @@ -198,6 +200,8 @@ set(HEADERS src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp src/components/file/FileDownloader.hpp src/components/file/FileExtractor.hpp + src/components/history/HistoryModel.hpp + src/components/history/HistoryProxyModel.hpp src/components/notifier/Notifier.hpp src/components/other/clipboard/Clipboard.hpp src/components/other/colors/Colors.hpp diff --git a/linphone-app/assets/languages/da.ts b/linphone-app/assets/languages/da.ts index 589bf7cb0..8309e0067 100644 --- a/linphone-app/assets/languages/da.ts +++ b/linphone-app/assets/languages/da.ts @@ -830,6 +830,25 @@ Server url ikke konfigureret. Det er nødvendigt at genstarte applikationen. Vil du gøre det nu? + + HistoryView + + removeAllEntriesDescription + Er du sikker på at du vil rydde op historikken? + + + tooltipContactEdit + Rediger kontakt + + + tooltipContactAdd + Tilføj kontakt + + + cleanHistory + Slet historik + + Home diff --git a/linphone-app/assets/languages/de.ts b/linphone-app/assets/languages/de.ts index 8bd3dcf5c..a616dde32 100644 --- a/linphone-app/assets/languages/de.ts +++ b/linphone-app/assets/languages/de.ts @@ -830,6 +830,25 @@ Server URL ist nicht konfiguriert. Ein Neustart der Anwendung ist notwendig. Möchten Sie die Anwendung jetzt neu starten? + + HistoryView + + removeAllEntriesDescription + Möchten Sie diese Historie wirklich löschen? + + + tooltipContactEdit + Kontakt bearbeiten + + + tooltipContactAdd + Kontakt hinzufügen + + + cleanHistory + Verlauf löschen + + Home diff --git a/linphone-app/assets/languages/en.ts b/linphone-app/assets/languages/en.ts index 393afd92d..1dcd7d652 100644 --- a/linphone-app/assets/languages/en.ts +++ b/linphone-app/assets/languages/en.ts @@ -832,6 +832,25 @@ Server URL not configured. It is necessary to restart the application. Do you want to restart now? + + HistoryView + + removeAllEntriesDescription + Are you sure you want to clear this history? + + + tooltipContactEdit + Edit contact + + + tooltipContactAdd + Add contact + + + cleanHistory + Delete history + + Home diff --git a/linphone-app/assets/languages/es.ts b/linphone-app/assets/languages/es.ts index e40aa9bbf..4b035a9f5 100644 --- a/linphone-app/assets/languages/es.ts +++ b/linphone-app/assets/languages/es.ts @@ -830,6 +830,25 @@ URL del servidor no configurada. Es necesario reiniciar la aplicación. ¿Desea reiniciarla ahora? + + HistoryView + + removeAllEntriesDescription + ¿Estás seguro de que quieres limpiar este historial? + + + tooltipContactEdit + Editar contacto + + + tooltipContactAdd + Añadir contacto + + + cleanHistory + Eliminar historial + + Home diff --git a/linphone-app/assets/languages/fr_FR.ts b/linphone-app/assets/languages/fr_FR.ts index 4fca75aed..7d9db2d5b 100644 --- a/linphone-app/assets/languages/fr_FR.ts +++ b/linphone-app/assets/languages/fr_FR.ts @@ -830,6 +830,25 @@ Url du serveur non configurée. Voulez-vous redémarrer maintenant pour prendre en compte ces modifications ? + + HistoryView + + removeAllEntriesDescription + Êtes-vous sûr de vouloir supprimer cet historique ? + + + tooltipContactEdit + Editer le contact + + + tooltipContactAdd + Ajouter le contact + + + cleanHistory + Supprimer l'historique + + Home diff --git a/linphone-app/assets/languages/hu.ts b/linphone-app/assets/languages/hu.ts index 27c3c8924..7f2596d6e 100644 --- a/linphone-app/assets/languages/hu.ts +++ b/linphone-app/assets/languages/hu.ts @@ -830,6 +830,25 @@ A kiszolgáló URL-je nincs konfigurálva. Az alkalmazás újraindítása szükséges. Szeretné most újraindítani? + + HistoryView + + removeAllEntriesDescription + Biztosan törölni kívánja ezt az előzményt? + + + tooltipContactEdit + Kapcsolat szerkesztése + + + tooltipContactAdd + Kapcsolat hozzáadása + + + cleanHistory + Előzmények törlése + + Home diff --git a/linphone-app/assets/languages/it.ts b/linphone-app/assets/languages/it.ts index b03a0227d..929c7e0ce 100644 --- a/linphone-app/assets/languages/it.ts +++ b/linphone-app/assets/languages/it.ts @@ -830,6 +830,25 @@ URL del server non configurato. È necessario riavviare l'applicazione. Vuoi riavviare ora? + + HistoryView + + removeAllEntriesDescription + Sei sicuro di voler cancellare questa cronologia? + + + tooltipContactEdit + Modifica contatto + + + tooltipContactAdd + Aggiungi contatto + + + cleanHistory + Cancella cronologia + + Home diff --git a/linphone-app/assets/languages/ja.ts b/linphone-app/assets/languages/ja.ts index 42d6584a1..a51c1dfa5 100644 --- a/linphone-app/assets/languages/ja.ts +++ b/linphone-app/assets/languages/ja.ts @@ -830,6 +830,25 @@ アプリケーションを再起動する必要があります。今すぐ再起動しますか? + + HistoryView + + removeAllEntriesDescription + 履歴をクリアしてよろしいですか? + + + tooltipContactEdit + 連絡先の編集 + + + tooltipContactAdd + 連絡先の追加 + + + cleanHistory + 履歴の削除 + + Home diff --git a/linphone-app/assets/languages/lt.ts b/linphone-app/assets/languages/lt.ts index db71da49b..95f54834f 100644 --- a/linphone-app/assets/languages/lt.ts +++ b/linphone-app/assets/languages/lt.ts @@ -830,6 +830,25 @@ Nesukonfigūruotas serverio url. Yra būtina paleisti programą iš naujo. Ar norite tai atlikti dabar? + + HistoryView + + removeAllEntriesDescription + Ar tikrai norite išvalyti šią istoriją? + + + tooltipContactEdit + Redaguoti kontaktą + + + tooltipContactAdd + Pridėti kontaktą + + + cleanHistory + Ištrinti istoriją + + Home diff --git a/linphone-app/assets/languages/pt_BR.ts b/linphone-app/assets/languages/pt_BR.ts index 5ca09d079..08c92897e 100644 --- a/linphone-app/assets/languages/pt_BR.ts +++ b/linphone-app/assets/languages/pt_BR.ts @@ -830,6 +830,25 @@ URL do servidor não configurado. É necessário reiniciar o aplicativo. Deseja reiniciar agora? + + HistoryView + + removeAllEntriesDescription + Tem certeza de que deseja limpar esse histórico? + + + tooltipContactEdit + Editar contato + + + tooltipContactAdd + Adicionar contato + + + cleanHistory + Excluir histórico + + Home diff --git a/linphone-app/assets/languages/ru.ts b/linphone-app/assets/languages/ru.ts index 28edf5c36..790b08f30 100644 --- a/linphone-app/assets/languages/ru.ts +++ b/linphone-app/assets/languages/ru.ts @@ -830,6 +830,25 @@ Требуется перезапустить приложение. Хотите перезапустить сейчас? + + HistoryView + + removeAllEntriesDescription + Вы уверены, что хотите очистить эту историю? + + + tooltipContactEdit + Изменить контакт + + + tooltipContactAdd + Добавить контакт + + + cleanHistory + Удалить историю + + Home diff --git a/linphone-app/assets/languages/sv.ts b/linphone-app/assets/languages/sv.ts index 1f5581297..464896f62 100644 --- a/linphone-app/assets/languages/sv.ts +++ b/linphone-app/assets/languages/sv.ts @@ -830,6 +830,25 @@ Serverwebbadressen är inte konfigurerad. Det är nödvändigt att starta om programmet. Vill du starta om nu? + + HistoryView + + removeAllEntriesDescription + Är du säker på att du vill rensa den här historiken? + + + tooltipContactEdit + Redigera kontakt + + + tooltipContactAdd + Lägg till kontakt + + + cleanHistory + Radera historik + + Home diff --git a/linphone-app/assets/languages/tr.ts b/linphone-app/assets/languages/tr.ts index f102f97ac..671688298 100644 --- a/linphone-app/assets/languages/tr.ts +++ b/linphone-app/assets/languages/tr.ts @@ -830,6 +830,25 @@ Sunucu url'si yapılandırılmadı. Uygulamanın yeniden başlaması gerekiyor. Şimdi yeniden başlatmak ister misiniz? + + HistoryView + + removeAllEntriesDescription + Bu geçmişi temizlemek istediğinize emin misiniz? + + + tooltipContactEdit + Kişi düzenle + + + tooltipContactAdd + Kişi ekle + + + cleanHistory + Geçmişi sil + + Home diff --git a/linphone-app/assets/languages/uk.ts b/linphone-app/assets/languages/uk.ts index 89a6f781e..1281eb03b 100644 --- a/linphone-app/assets/languages/uk.ts +++ b/linphone-app/assets/languages/uk.ts @@ -830,6 +830,25 @@ Потрібно перезапустити застосунок. Бажаєте перезапустити зараз? + + HistoryView + + removeAllEntriesDescription + Ви впевнені, що волієте вичистити цю історію? + + + tooltipContactEdit + Редагувати контакт + + + tooltipContactAdd + Додати контакт + + + cleanHistory + Вилучити історію + + Home diff --git a/linphone-app/assets/languages/zh_CN.ts b/linphone-app/assets/languages/zh_CN.ts index 0b7459d5f..52327110f 100644 --- a/linphone-app/assets/languages/zh_CN.ts +++ b/linphone-app/assets/languages/zh_CN.ts @@ -830,6 +830,25 @@ 需要重启应用程序。您想要立刻重启吗? + + HistoryView + + removeAllEntriesDescription + 你确定要清除该历史吗? + + + tooltipContactEdit + 编辑联系人 + + + tooltipContactAdd + 添加联系人 + + + cleanHistory + 删除历史记录 + + Home diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc index aac0c7fd2..1948cf996 100644 --- a/linphone-app/resources.qrc +++ b/linphone-app/resources.qrc @@ -229,6 +229,7 @@ ui/modules/Common/Form/ListForm.qml ui/modules/Common/Form/ListItemSelector.js ui/modules/Common/Form/ListItemSelector.qml + ui/modules/Common/Form/MouseArea.qml ui/modules/Common/Form/Placements/FormEmptyLine.qml ui/modules/Common/Form/Placements/FormGroup.qml ui/modules/Common/Form/Placements/FormHGroup.qml @@ -338,6 +339,9 @@ ui/modules/Linphone/Contact/ContactMessageCounter.qml ui/modules/Linphone/Contact/Contact.qml ui/modules/Linphone/Dialog/OnlineInstallerDialog.qml + ui/modules/Linphone/History/History.qml + ui/modules/Linphone/History/History.js + ui/modules/Linphone/History/Event.qml ui/modules/Linphone/Menus/SipAddressesMenu.qml ui/modules/Linphone/Misc/MessageCounter.qml ui/modules/Linphone/Notifications/NotificationBasic.qml @@ -365,6 +369,7 @@ ui/modules/Linphone/Styles/Contact/ContactMessageCounterStyle.qml ui/modules/Linphone/Styles/Contact/ContactStyle.qml ui/modules/Linphone/Styles/Dialog/OnlineInstallerDialogStyle.qml + ui/modules/Linphone/Styles/History/HistoryStyle.qml ui/modules/Linphone/Styles/Menus/SipAddressesMenuStyle.qml ui/modules/Linphone/Styles/Misc/MessageCounterStyle.qml ui/modules/Linphone/Styles/Notifications/NotificationBasicStyle.qml @@ -428,10 +433,13 @@ ui/views/App/Main/Dialogs/ManageAccount.js ui/views/App/Main/Dialogs/ManageAccounts.qml ui/views/App/Main/Home.qml + ui/views/App/Main/HistoryView.qml + ui/views/App/Main/HistoryView.js ui/views/App/Main/InviteFriends.qml ui/views/App/Main/MainWindow.js ui/views/App/Main/MainWindowMenuBar.qml ui/views/App/Main/MainWindow.qml + ui/views/App/Main/MainWindowTopMenuBar.qml ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.js ui/views/App/Settings/Dialogs/SettingsSipAccountsEdit.qml ui/views/App/Settings/Dialogs/SettingsVideoPreview.qml @@ -469,6 +477,7 @@ ui/views/App/Styles/Main/Dialogs/ManageAccountsStyle.qml ui/views/App/Styles/Main/HomeStyle.qml ui/views/App/Styles/Main/InviteFriendsStyle.qml + ui/views/App/Styles/Main/HistoryViewStyle.qml ui/views/App/Styles/Main/MainWindowStyle.qml ui/views/App/Styles/qmldir ui/views/App/Styles/Settings/Dialogs/SettingsSipAccountsEditStyle.qml @@ -477,7 +486,6 @@ ui/views/App/Styles/Settings/SettingsAudioStyle.qml ui/views/App/Styles/Settings/SettingsWindowStyle.qml assets/images/linphone_logo.svg - ui/views/App/Main/MainWindowTopMenuBar.qml ui/dev-modules/Colors/Colors.qml ui/dev-modules/Units/Units.qml assets/icon.ico diff --git a/linphone-app/src/app/App.cpp b/linphone-app/src/app/App.cpp index 7342eab24..353fe126f 100644 --- a/linphone-app/src/app/App.cpp +++ b/linphone-app/src/app/App.cpp @@ -520,6 +520,7 @@ void App::registerTypes () { registerType("ContactsListProxyModel"); registerType("FileDownloader"); registerType("FileExtractor"); + registerType("HistoryProxyModel"); registerType("SipAddressesProxyModel"); registerType("SoundPlayer"); registerType("TelephoneNumbersModel"); @@ -535,6 +536,7 @@ void App::registerTypes () { registerUncreatableType("ChatModel"); registerUncreatableType("ConferenceAddModel"); registerUncreatableType("ContactModel"); + registerUncreatableType("HistoryModel"); registerUncreatableType("SipAddressObserver"); registerUncreatableType("VcardModel"); } diff --git a/linphone-app/src/components/Components.hpp b/linphone-app/src/components/Components.hpp index 8147d8432..05feab2c9 100644 --- a/linphone-app/src/components/Components.hpp +++ b/linphone-app/src/components/Components.hpp @@ -41,6 +41,7 @@ #include "core/CoreManager.hpp" #include "file/FileDownloader.hpp" #include "file/FileExtractor.hpp" +#include "history/HistoryProxyModel.hpp" #include "notifier/Notifier.hpp" #include "presence/OwnPresenceModel.hpp" #include "settings/AccountSettingsModel.hpp" diff --git a/linphone-app/src/components/calls/CallsListModel.cpp b/linphone-app/src/components/calls/CallsListModel.cpp index d50ae296a..d4f9e2bdc 100644 --- a/linphone-app/src/components/calls/CallsListModel.cpp +++ b/linphone-app/src/components/calls/CallsListModel.cpp @@ -111,7 +111,7 @@ void CallsListModel::launchAudioCall (const QString &sipAddress, const QHashaddCustomHeader(Utils::appStringToCoreString(iterator.key()), Utils::appStringToCoreString(iterator.value())); } - + params->setProxyConfig(core->getDefaultProxyConfig()); CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername())); core->inviteAddressWithParams(address, params); } @@ -130,7 +130,7 @@ void CallsListModel::launchVideoCall (const QString &sipAddress) const { shared_ptr params = core->createCallParams(nullptr); params->enableVideo(true); - + params->setProxyConfig(core->getDefaultProxyConfig()); CallModel::setRecordFile(params, Utils::coreStringToAppString(address->getUsername())); core->inviteAddressWithParams(address, params); } diff --git a/linphone-app/src/components/core/CoreManager.cpp b/linphone-app/src/components/core/CoreManager.cpp index fa2a35c42..bcd1d0df0 100644 --- a/linphone-app/src/components/core/CoreManager.cpp +++ b/linphone-app/src/components/core/CoreManager.cpp @@ -32,6 +32,7 @@ #include "components/chat/ChatModel.hpp" #include "components/contact/VcardModel.hpp" #include "components/contacts/ContactsListModel.hpp" +#include "components/history/HistoryModel.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "components/settings/SettingsModel.hpp" #include "components/sip-addresses/SipAddressesModel.hpp" @@ -145,6 +146,13 @@ bool CoreManager::chatModelExists (const QString &peerAddress, const QString &lo return mChatModels.contains({ peerAddress, localAddress }); } +HistoryModel* CoreManager::getHistoryModel(){ + if(!mHistoryModel){ + mHistoryModel = new HistoryModel(this); + emit historyModelCreated(mHistoryModel); + } + return mHistoryModel; +} // ----------------------------------------------------------------------------- void CoreManager::init (QObject *parent, const QString &configPath) { diff --git a/linphone-app/src/components/core/CoreManager.hpp b/linphone-app/src/components/core/CoreManager.hpp index 27fb88997..0f5544a43 100644 --- a/linphone-app/src/components/core/CoreManager.hpp +++ b/linphone-app/src/components/core/CoreManager.hpp @@ -37,6 +37,7 @@ class ChatModel; class ContactsListModel; class CoreHandlers; class EventCountNotifier; +class HistoryModel; class SettingsModel; class SipAddressesModel; class VcardModel; @@ -44,9 +45,9 @@ class VcardModel; class CoreManager : public QObject { Q_OBJECT; - Q_PROPERTY(QString version READ getVersion CONSTANT); - Q_PROPERTY(QString downloadUrl READ getDownloadUrl CONSTANT); - Q_PROPERTY(int eventCount READ getEventCount NOTIFY eventCountChanged); + Q_PROPERTY(QString version READ getVersion CONSTANT) + Q_PROPERTY(QString downloadUrl READ getDownloadUrl CONSTANT) + Q_PROPERTY(int eventCount READ getEventCount NOTIFY eventCountChanged) public: bool started () const { @@ -65,6 +66,8 @@ public: std::shared_ptr getChatModel (const QString &peerAddress, const QString &localAddress); bool chatModelExists (const QString &sipAddress, const QString &localAddress); + + HistoryModel* getHistoryModel(); // --------------------------------------------------------------------------- // Video render lock. @@ -138,6 +141,7 @@ signals: void coreStarted (); void chatModelCreated (const std::shared_ptr &chatModel); + void historyModelCreated (HistoryModel *historyModel); void logsUploaded (const QString &url); @@ -177,6 +181,7 @@ private: EventCountNotifier *mEventCountNotifier = nullptr; QHash, std::weak_ptr> mChatModels; + HistoryModel * mHistoryModel = nullptr; QTimer *mCbsTimer = nullptr; diff --git a/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp b/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp index 575ddb05d..81469bb68 100644 --- a/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp +++ b/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.cpp @@ -25,6 +25,7 @@ #include "components/chat/ChatModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" +#include "components/history/HistoryModel.hpp" #include "components/settings/SettingsModel.hpp" #include "utils/Utils.hpp" @@ -40,6 +41,10 @@ AbstractEventCountNotifier::AbstractEventCountNotifier (QObject *parent) : QObje coreManager, &CoreManager::chatModelCreated, this, &AbstractEventCountNotifier::handleChatModelCreated ); + QObject::connect( + coreManager, &CoreManager::historyModelCreated, + this, &AbstractEventCountNotifier::handleHistoryModelCreated + ); QObject::connect( coreManager->getHandlers().get(), &CoreHandlers::messageReceived, this, &AbstractEventCountNotifier::updateUnreadMessageCount @@ -98,11 +103,26 @@ void AbstractEventCountNotifier::handleChatModelCreated (const shared_ptrgetPeerAddress()), Utils::cleanSipAddress(chatModel->getLocalAddress()) }); if (it != mMissedCalls.cend()) { mMissedCalls.erase(it); diff --git a/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp b/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp index 923640323..4af9bb61d 100644 --- a/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp +++ b/linphone-app/src/components/core/event-count-notifier/AbstractEventCountNotifier.hpp @@ -35,6 +35,7 @@ namespace linphone { class CallModel; class ChatModel; +class HistoryModel; class AbstractEventCountNotifier : public QObject { Q_OBJECT; @@ -68,7 +69,11 @@ private: void handleChatModelCreated (const std::shared_ptr &chatModel); - void handleChatModelFocused (ChatModel *chatModel); + void handleHistoryModelCreated (HistoryModel *historyModel); + + + void handleResetAllMissedCalls (); + void handleResetMissedCalls (ChatModel *chatModel); void handleCallMissed (CallModel *callModel); QHash mMissedCalls; diff --git a/linphone-app/src/components/history/HistoryModel.cpp b/linphone-app/src/components/history/HistoryModel.cpp new file mode 100644 index 000000000..6c341f7cb --- /dev/null +++ b/linphone-app/src/components/history/HistoryModel.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "app/App.hpp" +#include "app/paths/Paths.hpp" +#include "app/providers/ThumbnailProvider.hpp" +#include "components/core/CoreHandlers.hpp" +#include "components/core/CoreManager.hpp" +#include "components/notifier/Notifier.hpp" +#include "components/settings/SettingsModel.hpp" +#include "utils/QExifImageHeader.hpp" +#include "utils/Utils.hpp" + +#include "HistoryModel.hpp" + +// ============================================================================= + +using namespace std; + +static inline void fillCallStartEntry (QVariantMap &dest, const shared_ptr &callLog) { + dest["type"] = HistoryModel::CallEntry; + dest["timestamp"] = QDateTime::fromMSecsSinceEpoch(callLog->getStartDate() * 1000); + dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; + dest["status"] = static_cast(callLog->getStatus()); + dest["isStart"] = true; + dest["sipAddress"] = Utils::coreStringToAppString(callLog->getRemoteAddress()->asString()); +} + +static inline void fillCallEndEntry (QVariantMap &dest, const shared_ptr &callLog) { + dest["type"] = HistoryModel::CallEntry; + dest["timestamp"] = QDateTime::fromMSecsSinceEpoch((callLog->getStartDate() + callLog->getDuration()) * 1000); + dest["isOutgoing"] = callLog->getDir() == linphone::Call::Dir::Outgoing; + dest["status"] = static_cast(callLog->getStatus()); + dest["isStart"] = false; + dest["sipAddress"] = Utils::coreStringToAppString(callLog->getRemoteAddress()->asString()); +} + +// ----------------------------------------------------------------------------- + +HistoryModel::HistoryModel (QObject *parent) :QAbstractListModel(parent){ + CoreManager *coreManager = CoreManager::getInstance(); + + mCoreHandlers = coreManager->getHandlers(); + + setSipAddresses(); + CoreHandlers *coreHandlers = mCoreHandlers.get(); + QObject::connect(coreHandlers, &CoreHandlers::callStateChanged, this, &HistoryModel::handleCallStateChanged); + +} + +HistoryModel::~HistoryModel () { +} + +QHash HistoryModel::roleNames () const { + QHash roles; + roles[Roles::HistoryEntry] = "$historyEntry"; + roles[Roles::SectionDate] = "$sectionDate"; + return roles; +} + +int HistoryModel::rowCount (const QModelIndex &) const { + return mEntries.count(); +} + +QVariant HistoryModel::data (const QModelIndex &index, int role) const { + int row = index.row(); + + if (!index.isValid() || row < 0 || row >= mEntries.count()) + return QVariant(); + + switch (role) { + case Roles::HistoryEntry: { + auto &data = mEntries[row].first; + return QVariant::fromValue(data); + } + case Roles::SectionDate: + return QVariant::fromValue(mEntries[row].first["timestamp"].toDate()); + } + + return QVariant(); +} + +bool HistoryModel::removeRow (int row, const QModelIndex &) { + return removeRows(row, 1); +} + +bool HistoryModel::removeRows (int row, int count, const QModelIndex &parent) { + int limit = row + count - 1; + + if (row < 0 || count < 0 || limit >= mEntries.count()) + return false; + + beginRemoveRows(parent, row, limit); + + for (int i = 0; i < count; ++i) { + removeEntry(mEntries[row]); + mEntries.removeAt(row); + } + + endRemoveRows(); + + if (mEntries.count() == 0) + emit allEntriesRemoved(); + else if (limit == mEntries.count()) + emit lastEntryRemoved(); + emit focused();// Removing rows is like having focus. Don't wait asynchronous events. + return true; +} + +void HistoryModel::setSipAddresses () { + shared_ptr core = CoreManager::getInstance()->getCore(); + mEntries.clear(); + + QElapsedTimer timer; + timer.start(); + + // Get calls. + for (auto &callLog : core->getCallLogs()) + insertCall(callLog); + + qInfo() << QStringLiteral("HistoryModel loaded in %3 milliseconds.").arg(timer.elapsed()); + +} + +// ----------------------------------------------------------------------------- + +void HistoryModel::removeEntry (int id) { + qInfo() << QStringLiteral("Removing call entry: %1.").arg(id); + + if (!removeRow(id)) + qWarning() << QStringLiteral("Unable to remove call entry: %1").arg(id); +} + +void HistoryModel::removeAllEntries () { + qInfo() << QStringLiteral("Removing all call entries."); + + beginResetModel(); + + for (auto &entry : mEntries) + removeEntry(entry); + + mEntries.clear(); + + endResetModel(); + + emit allEntriesRemoved(); + emit focused();// Removing all entries is like having focus. Don't wait asynchronous events. +} + +// ----------------------------------------------------------------------------- + +void HistoryModel::removeEntry (HistoryEntryData &entry) { + int type = entry.first["type"].toInt(); + + switch (type) { + + case HistoryModel::CallEntry: { + if (entry.first["status"].toInt() == CallStatusSuccess) { + // WARNING: Unable to remove symmetric call here. (start/end) + // We are between `beginRemoveRows` and `endRemoveRows`. + // A solution is to schedule a `removeEntry` call in the Qt main loop. + shared_ptr linphonePtr = entry.second; + QTimer::singleShot(0, this, [this, linphonePtr]() { + auto it = find_if(mEntries.begin(), mEntries.end(), [linphonePtr](const HistoryEntryData &entry) { + return entry.second == linphonePtr; + }); + + if (it != mEntries.end()) + removeEntry(int(distance(mEntries.begin(), it))); + }); + } + + CoreManager::getInstance()->getCore()->removeCallLog(static_pointer_cast(entry.second)); + break; + } + + default: + qWarning() << QStringLiteral("Unknown history entry type: %1.").arg(type); + } +} + +void HistoryModel::insertCall (const shared_ptr &callLog) { + linphone::Call::Status status = callLog->getStatus(); + + auto insertEntry = [this]( + const HistoryEntryData &entry, + const QList::iterator *start = nullptr + ) { + auto it = lower_bound(start ? *start : mEntries.begin(), mEntries.end(), entry, [](const HistoryEntryData &a, const HistoryEntryData &b) { + return a.first["timestamp"] < b.first["timestamp"]; + }); + + int row = int(distance(mEntries.begin(), it)); + + beginInsertRows(QModelIndex(), row, row); + it = mEntries.insert(it, entry); + endInsertRows(); + + return it; + }; + + // Add start call. + QVariantMap start; + fillCallStartEntry(start, callLog); + auto it = insertEntry(qMakePair(start, static_pointer_cast(callLog))); + + if (status == linphone::Call::Status::Success) { + QVariantMap end; + fillCallEndEntry(end, callLog); + insertEntry(qMakePair(end, static_pointer_cast(callLog)), &it); + } +} + +// ----------------------------------------------------------------------------- + +void HistoryModel::resetMessageCount () { + emit callCountReset(); +} + +// ----------------------------------------------------------------------------- + +void HistoryModel::handleCallStateChanged (const shared_ptr &call, linphone::Call::State state) { + if (state == linphone::Call::State::End || state == linphone::Call::State::Error) + insertCall(call->getCallLog()); +} + diff --git a/linphone-app/src/components/history/HistoryModel.hpp b/linphone-app/src/components/history/HistoryModel.hpp new file mode 100644 index 000000000..67652d204 --- /dev/null +++ b/linphone-app/src/components/history/HistoryModel.hpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HISTORY_MODEL_H_ +#define HISTORY_MODEL_H_ + +#include +#include + +// ============================================================================= +// Fetch all N messages of the History. +// ============================================================================= + +class CoreHandlers; + +class HistoryModel : public QAbstractListModel { + + Q_OBJECT + +public: + enum Roles { + HistoryEntry = Qt::DisplayRole, + SectionDate + }; + + enum EntryType { + GenericEntry, + CallEntry + }; + Q_ENUM(EntryType) + + enum CallStatus { + CallStatusDeclined = int(linphone::Call::Status::Declined), + CallStatusMissed = int(linphone::Call::Status::Missed), + CallStatusSuccess = int(linphone::Call::Status::Success), + CallStatusAborted = int(linphone::Call::Status::Aborted), + CallStatusEarlyAborted = int(linphone::Call::Status::EarlyAborted), + CallStatusAcceptedElsewhere = int(linphone::Call::Status::AcceptedElsewhere), + CallStatusDeclinedElsewhere = int(linphone::Call::Status::DeclinedElsewhere) + }; + Q_ENUM(CallStatus) + + HistoryModel (QObject *parent = Q_NULLPTR); + virtual ~HistoryModel (); + + int rowCount (const QModelIndex &index = QModelIndex()) const override; + + QHash roleNames () const override; + QVariant data (const QModelIndex &index, int role) const override; + + bool removeRow (int row, const QModelIndex &parent = QModelIndex()); + bool removeRows (int row, int count, const QModelIndex &parent = QModelIndex()) override; + + void removeEntry (int id); + void removeAllEntries (); + + void resetMessageCount (); + +signals: + void allEntriesRemoved (); + void lastEntryRemoved (); + + void focused (); + void callCountReset(); + +private: + typedef QPair> HistoryEntryData; + + void setSipAddresses (); + void removeEntry (HistoryEntryData &entry); + void insertCall (const std::shared_ptr &callLog); + void handleCallStateChanged (const std::shared_ptr &call, linphone::Call::State state); + + mutable QList mEntries; + + std::shared_ptr mCoreHandlers; +}; + +#endif // HISTORY_MODEL_H_ diff --git a/linphone-app/src/components/history/HistoryProxyModel.cpp b/linphone-app/src/components/history/HistoryProxyModel.cpp new file mode 100644 index 000000000..e7f490b58 --- /dev/null +++ b/linphone-app/src/components/history/HistoryProxyModel.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "app/App.hpp" +#include "components/core/CoreManager.hpp" + +#include "HistoryProxyModel.hpp" +#include "components/sip-addresses/SipAddressesModel.hpp" + +// ============================================================================= + +using namespace std; + +// Fetch the L last filtered history entries. +class HistoryProxyModel::HistoryModelFilter : public QSortFilterProxyModel { +public: + HistoryModelFilter (QObject *parent) : QSortFilterProxyModel(parent) { } + + HistoryModel::EntryType getEntryTypeFilter () { + return mEntryTypeFilter; + } + + void setEntryTypeFilter (HistoryModel::EntryType type) { + mEntryTypeFilter = type; + invalidate(); + } + +protected: + bool filterAcceptsRow (int sourceRow, const QModelIndex &) const override { + if (mEntryTypeFilter == HistoryModel::EntryType::GenericEntry) + return true; + + QModelIndex index = sourceModel()->index(sourceRow, 0, QModelIndex()); + const QVariantMap data = index.data().toMap(); + + return data["type"].toInt() == mEntryTypeFilter; + } + +private: + HistoryModel::EntryType mEntryTypeFilter = HistoryModel::EntryType::GenericEntry; +}; + +// ============================================================================= + +HistoryProxyModel::HistoryProxyModel (QObject *parent) : QSortFilterProxyModel(parent) { + + setSourceModel(new HistoryModelFilter(this)); + reload(); + + App *app = App::getInstance(); + QObject::connect(app->getMainWindow(), &QWindow::activeChanged, this, [this]() { + handleIsActiveChanged(App::getInstance()->getMainWindow()); + }); + + QQuickWindow *callsWindow = app->getCallsWindow(); + if (callsWindow) + QObject::connect(callsWindow, &QWindow::activeChanged, this, [this, callsWindow]() { + handleIsActiveChanged(callsWindow); + }); +} + +// ----------------------------------------------------------------------------- + +void HistoryProxyModel::removeAllEntries(){ + auto model = CoreManager::getInstance()->getHistoryModel(); + if (!model) + return; + model->removeAllEntries(); +} +void HistoryProxyModel::removeEntry (int id){ + auto model = CoreManager::getInstance()->getHistoryModel(); + if (!model) + return; + QModelIndex sourceIndex = mapToSource(index(id, 0)); + model->removeEntry(static_cast(sourceModel())->mapToSource(sourceIndex).row() ); +} +// ----------------------------------------------------------------------------- + +void HistoryProxyModel::loadMoreEntries () { + int count = rowCount(); + int parentCount = sourceModel()->rowCount(); + + if (count < parentCount) { + // Do not increase `mMaxDisplayedEntries` if it's not necessary... + // Limit qml calls. + if (count == mMaxDisplayedEntries) + mMaxDisplayedEntries += EntriesChunkSize; + + invalidateFilter(); + + count = rowCount() - count; + if (count > 0) + emit moreEntriesLoaded(count); + } +} + +void HistoryProxyModel::setEntryTypeFilter (HistoryModel::EntryType type) { + HistoryModelFilter *HistoryModelFilter = static_cast(sourceModel()); + + if (HistoryModelFilter->getEntryTypeFilter() != type) { + HistoryModelFilter->setEntryTypeFilter(type); + emit entryTypeFilterChanged(type); + } +} + +// ----------------------------------------------------------------------------- + +bool HistoryProxyModel::filterAcceptsRow (int sourceRow, const QModelIndex &) const { + return sourceModel()->rowCount() - sourceRow <= mMaxDisplayedEntries; +} + +// ----------------------------------------------------------------------------- + +void HistoryProxyModel::reload () { + mMaxDisplayedEntries = EntriesChunkSize; + static_cast(sourceModel())->setSourceModel(CoreManager::getInstance()->getHistoryModel()); +} +void HistoryProxyModel::resetMessageCount(){ + auto model = CoreManager::getInstance()->getHistoryModel(); + if( model){ + model->resetMessageCount(); + } +} +// ----------------------------------------------------------------------------- + +static inline QWindow *getParentWindow (QObject *object) { + App *app = App::getInstance(); + const QWindow *mainWindow = app->getMainWindow(); + const QWindow *callsWindow = app->getCallsWindow(); + for (QObject *parent = object->parent(); parent; parent = parent->parent()) + if (parent == mainWindow || parent == callsWindow) + return static_cast(parent); + return nullptr; +} + +void HistoryProxyModel::handleIsActiveChanged (QWindow *window) { + auto model = CoreManager::getInstance()->getHistoryModel(); + if (model && window->isActive() && getParentWindow(this) == window) { + model->focused(); + model->resetMessageCount(); + } +} diff --git a/linphone-app/src/components/history/HistoryProxyModel.hpp b/linphone-app/src/components/history/HistoryProxyModel.hpp new file mode 100644 index 000000000..ddd6fa5a1 --- /dev/null +++ b/linphone-app/src/components/history/HistoryProxyModel.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HISTORY_PROXY_MODEL_H_ +#define HISTORY_PROXY_MODEL_H_ + +#include + +#include "HistoryModel.hpp" + +// ============================================================================= + +class QWindow; + +class HistoryProxyModel : public QSortFilterProxyModel { + class HistoryModelFilter; + + Q_OBJECT; + +public: + HistoryProxyModel (QObject *parent = Q_NULLPTR); + + Q_INVOKABLE void loadMoreEntries (); + Q_INVOKABLE void setEntryTypeFilter (HistoryModel::EntryType type = HistoryModel::EntryType::CallEntry); + Q_INVOKABLE void removeEntry (int id); + + Q_INVOKABLE void removeAllEntries (); + + Q_INVOKABLE void resetMessageCount(); + +signals: + + void moreEntriesLoaded (int n); + + void entryTypeFilterChanged (HistoryModel::EntryType type); + +protected: + bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override; + +private: + + void reload (); + + void handleIsActiveChanged (QWindow *window); + + int mMaxDisplayedEntries = EntriesChunkSize; + + static constexpr int EntriesChunkSize = 50; +}; + +#endif // HISTORY_PROXY_MODEL_H_ diff --git a/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp b/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp index 847e20eb2..4de3697ac 100644 --- a/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp +++ b/linphone-app/src/components/sip-addresses/SipAddressesModel.cpp @@ -30,6 +30,7 @@ #include "components/contacts/ContactsListModel.hpp" #include "components/core/CoreHandlers.hpp" #include "components/core/CoreManager.hpp" +#include "components/history/HistoryModel.hpp" #include "components/settings/AccountSettingsModel.hpp" #include "utils/LinphoneUtils.hpp" #include "utils/Utils.hpp" @@ -59,6 +60,7 @@ SipAddressesModel::SipAddressesModel (QObject *parent) : QAbstractListModel(pare mCoreHandlers = coreManager->getHandlers(); QObject::connect(coreManager, &CoreManager::chatModelCreated, this, &SipAddressesModel::handleChatModelCreated); + QObject::connect(coreManager, &CoreManager::historyModelCreated, this, &SipAddressesModel::handleHistoryModelCreated); ContactsListModel *contacts = CoreManager::getInstance()->getContactsListModel(); QObject::connect(contacts, &ContactsListModel::contactAdded, this, &SipAddressesModel::handleContactAdded); @@ -261,6 +263,11 @@ void SipAddressesModel::handleChatModelCreated (const shared_ptr &cha QObject::connect(ptr, &ChatModel::messageSent, this, &SipAddressesModel::handleMessageSent); } +void SipAddressesModel::handleHistoryModelCreated (HistoryModel *historyModel) { + QObject::connect(historyModel, &HistoryModel::callCountReset, this, [this] { + handleAllCallCountReset(); + }); +} void SipAddressesModel::handleContactAdded (ContactModel *contact) { for (const auto &sipAddress : contact->getVcardModel()->getSipAddresses()) addOrUpdateSipAddress(sipAddress.toString(), contact); @@ -385,7 +392,16 @@ void SipAddressesModel::handleLastEntryRemoved (ChatModel *chatModel) { it2->timestamp = map["timestamp"].toDateTime(); emit dataChanged(index(row, 0), index(row, 0)); } - +void SipAddressesModel::handleAllCallCountReset () { + for( auto peer = mPeerAddressToSipAddressEntry.begin() ; peer != mPeerAddressToSipAddressEntry.end() ; ++peer){ + for( auto local = peer->localAddressToConferenceEntry.begin() ; local != peer->localAddressToConferenceEntry.end() ; ++local){ + local->missedCallCount = 0; + updateObservers(peer.key(), local.key(), local->unreadMessageCount, local->missedCallCount); + } + int row = mRefs.indexOf(&(*peer)); + emit dataChanged(index(row, 0), index(row, 0)); + } +} void SipAddressesModel::handleMessageCountReset (ChatModel *chatModel) { const QString &peerAddress = Utils::cleanSipAddress(chatModel->getPeerAddress()); auto it = mPeerAddressToSipAddressEntry.find(peerAddress); diff --git a/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp b/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp index 2c0200dfa..841b7c43e 100644 --- a/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp +++ b/linphone-app/src/components/sip-addresses/SipAddressesModel.hpp @@ -32,6 +32,7 @@ class QUrl; class ChatModel; class CoreHandlers; +class HistoryModel; class SipAddressesModel : public QAbstractListModel { Q_OBJECT; @@ -82,6 +83,9 @@ public: // --------------------------------------------------------------------------- signals: void sipAddressReset();// The model has been reset + +public slots: + void handleAllCallCountReset (); private: bool removeRow (int row, const QModelIndex &parent = QModelIndex()); @@ -90,6 +94,7 @@ private: // --------------------------------------------------------------------------- void handleChatModelCreated (const std::shared_ptr &chatModel); + void handleHistoryModelCreated (HistoryModel *historyModel) ; void handleContactAdded (ContactModel *contact); void handleContactRemoved (const ContactModel *contact); @@ -103,6 +108,7 @@ private: void handleAllEntriesRemoved (ChatModel *chatModel); void handleLastEntryRemoved (ChatModel *chatModel); + void handleMessageCountReset (ChatModel *chatModel); void handleMessageSent (const std::shared_ptr &message); diff --git a/linphone-app/src/components/timeline/TimelineModel.cpp b/linphone-app/src/components/timeline/TimelineModel.cpp index fc22fc447..839ff6ba2 100644 --- a/linphone-app/src/components/timeline/TimelineModel.cpp +++ b/linphone-app/src/components/timeline/TimelineModel.cpp @@ -25,6 +25,8 @@ #include "TimelineModel.hpp" +#include + // ============================================================================= @@ -72,8 +74,8 @@ QVariant TimelineModel::data (const QModelIndex &index, int role) const { } bool TimelineModel::filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const { - const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); - return getLocalToConferenceEntry(index.data().toMap())->contains(getCleanedLocalAddress()); + const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + return getLocalToConferenceEntry(index.data().toMap())->contains(getCleanedLocalAddress()); } bool TimelineModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { diff --git a/linphone-app/ui/modules/Common/Form/Buttons/AbstractTextButton.qml b/linphone-app/ui/modules/Common/Form/Buttons/AbstractTextButton.qml index 196c86fff..2500b2d3f 100644 --- a/linphone-app/ui/modules/Common/Form/Buttons/AbstractTextButton.qml +++ b/linphone-app/ui/modules/Common/Form/Buttons/AbstractTextButton.qml @@ -1,6 +1,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 +import Common 1.0 import Common.Styles 1.0 // ============================================================================= @@ -76,15 +77,10 @@ Item { hoverEnabled: true - MouseArea - { + MouseArea { id: mouseArea anchors.fill: parent onPressed: mouse.accepted = false - hoverEnabled: true - cursorShape: containsMouse - ? Qt.PointingHandCursor - : Qt.ArrowCursor } height: parent.height diff --git a/linphone-app/ui/modules/Common/Form/Buttons/FileChooserButton.qml b/linphone-app/ui/modules/Common/Form/Buttons/FileChooserButton.qml index f5b8b1e70..e04524890 100644 --- a/linphone-app/ui/modules/Common/Form/Buttons/FileChooserButton.qml +++ b/linphone-app/ui/modules/Common/Form/Buttons/FileChooserButton.qml @@ -101,7 +101,6 @@ TextField { anchors.fill: parent enabled: !textField.readOnly - hoverEnabled: true onClicked: fileDialog.open() } diff --git a/linphone-app/ui/modules/Common/Form/Buttons/SmallButton.qml b/linphone-app/ui/modules/Common/Form/Buttons/SmallButton.qml index 96a01d758..0c310de93 100644 --- a/linphone-app/ui/modules/Common/Form/Buttons/SmallButton.qml +++ b/linphone-app/ui/modules/Common/Form/Buttons/SmallButton.qml @@ -1,6 +1,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 +import Common 1.0 import Common.Styles 1.0 // ============================================================================= @@ -32,14 +33,9 @@ Button { rightPadding: SmallButtonStyle.rightPadding } hoverEnabled: true - MouseArea - { + MouseArea { id: mouseArea anchors.fill: parent onPressed: mouse.accepted = false - hoverEnabled: true - cursorShape: containsMouse - ? Qt.PointingHandCursor - : Qt.ArrowCursor } } diff --git a/linphone-app/ui/modules/Common/Form/Fields/ScrollableListViewField.qml b/linphone-app/ui/modules/Common/Form/Fields/ScrollableListViewField.qml index ebd3b6c82..180f1b1da 100644 --- a/linphone-app/ui/modules/Common/Form/Fields/ScrollableListViewField.qml +++ b/linphone-app/ui/modules/Common/Form/Fields/ScrollableListViewField.qml @@ -42,7 +42,7 @@ Rectangle { MouseArea { anchors.fill: parent - hoverEnabled: true + cursorShape: Qt.ArrowCursor visible: field.readOnly onWheel: wheel.accepted = true diff --git a/linphone-app/ui/modules/Common/Form/Fields/TextField.qml b/linphone-app/ui/modules/Common/Form/Fields/TextField.qml index b13d0455d..0ecc35df6 100644 --- a/linphone-app/ui/modules/Common/Form/Fields/TextField.qml +++ b/linphone-app/ui/modules/Common/Form/Fields/TextField.qml @@ -43,7 +43,7 @@ Controls.TextField { MouseArea { anchors.right: parent.right height: parent.height - hoverEnabled: true + cursorShape: Qt.ArrowCursor implicitWidth: tools ? tools.width : 0 Rectangle { diff --git a/linphone-app/ui/modules/Common/Form/MouseArea.qml b/linphone-app/ui/modules/Common/Form/MouseArea.qml new file mode 100644 index 000000000..17433106c --- /dev/null +++ b/linphone-app/ui/modules/Common/Form/MouseArea.qml @@ -0,0 +1,11 @@ +import QtQuick 2.7 as Quick + +import Common 1.0 +import Common.Styles 1.0 + +Quick.MouseArea { + cursorShape: containsMouse + ? Qt.PointingHandCursor + : Qt.ArrowCursor + hoverEnabled: true +} diff --git a/linphone-app/ui/modules/Common/Form/Switch.qml b/linphone-app/ui/modules/Common/Form/Switch.qml index ec897e620..0e174b6e4 100644 --- a/linphone-app/ui/modules/Common/Form/Switch.qml +++ b/linphone-app/ui/modules/Common/Form/Switch.qml @@ -1,6 +1,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.2 +import Common 1.0 import Common.Styles 1.0 // ============================================================================= @@ -97,7 +98,7 @@ Switch { MouseArea { anchors.fill: parent - + onClicked: control.enabled && control.clicked() } } diff --git a/linphone-app/ui/modules/Common/Helpers/DragBox.qml b/linphone-app/ui/modules/Common/Helpers/DragBox.qml index 0b3ade33a..b330c889b 100644 --- a/linphone-app/ui/modules/Common/Helpers/DragBox.qml +++ b/linphone-app/ui/modules/Common/Helpers/DragBox.qml @@ -1,5 +1,6 @@ import QtQuick 2.7 +import Common 1.0 // ============================================================================= Item { diff --git a/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.qml b/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.qml index 9b9a17e17..33d9c0fca 100644 --- a/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.qml +++ b/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.qml @@ -68,6 +68,7 @@ Item { id: builder MouseArea { + cursorShape: Qt.ArrowCursor property var _timeout function _checkPosition (positionEvent) { diff --git a/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.spec.qml b/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.spec.qml index 5749234e9..6fb0a809e 100644 --- a/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.spec.qml +++ b/linphone-app/ui/modules/Common/Helpers/InvertedMouseArea.spec.qml @@ -2,7 +2,7 @@ import QtQuick 2.7 import QtTest 1.1 import Utils 1.0 - +import Common 1.0 // ============================================================================= Rectangle { diff --git a/linphone-app/ui/modules/Common/Menus/ApplicationMenuEntry.qml b/linphone-app/ui/modules/Common/Menus/ApplicationMenuEntry.qml index e3c3fab7a..ebdee78a7 100644 --- a/linphone-app/ui/modules/Common/Menus/ApplicationMenuEntry.qml +++ b/linphone-app/ui/modules/Common/Menus/ApplicationMenuEntry.qml @@ -94,7 +94,6 @@ Rectangle { id: mouseArea anchors.fill: parent - hoverEnabled: true onClicked: entry.select() } diff --git a/linphone-app/ui/modules/Common/Menus/DropDownStaticMenuEntry.qml b/linphone-app/ui/modules/Common/Menus/DropDownStaticMenuEntry.qml index 18e5a4f77..8023cadc4 100644 --- a/linphone-app/ui/modules/Common/Menus/DropDownStaticMenuEntry.qml +++ b/linphone-app/ui/modules/Common/Menus/DropDownStaticMenuEntry.qml @@ -1,5 +1,6 @@ import QtQuick 2.7 +import Common 1.0 import Common.Styles 1.0 // ============================================================================= @@ -42,7 +43,6 @@ Rectangle { id: mouseArea anchors.fill: parent - hoverEnabled: true onClicked: entry.clicked() } diff --git a/linphone-app/ui/modules/Common/Misc/Paned.qml b/linphone-app/ui/modules/Common/Misc/Paned.qml index 0b3c1beb4..37f8c6995 100644 --- a/linphone-app/ui/modules/Common/Misc/Paned.qml +++ b/linphone-app/ui/modules/Common/Misc/Paned.qml @@ -1,5 +1,6 @@ import QtQuick 2.7 +import Common 1.0 import Common.Styles 1.0 import Utils 1.0 diff --git a/linphone-app/ui/modules/Common/Window/VirtualWindow.qml b/linphone-app/ui/modules/Common/Window/VirtualWindow.qml index feb031029..b9f08ddf8 100644 --- a/linphone-app/ui/modules/Common/Window/VirtualWindow.qml +++ b/linphone-app/ui/modules/Common/Window/VirtualWindow.qml @@ -1,6 +1,7 @@ import QtQuick 2.7 import QtQuick.Controls 2.5 +import Common 1.0 import Common.Styles 1.0 import 'Window.js' as Logic @@ -57,6 +58,7 @@ StackView{ anchors.fill: parent hoverEnabled: true onWheel: wheel.accepted = true + cursorShape: Qt.ArrowCursor } Rectangle { id: content diff --git a/linphone-app/ui/modules/Common/qmldir b/linphone-app/ui/modules/Common/qmldir index 5f8e5a071..fc8cc3d76 100644 --- a/linphone-app/ui/modules/Common/qmldir +++ b/linphone-app/ui/modules/Common/qmldir @@ -24,6 +24,7 @@ CommonItemDelegate 1.0 Form/CommonItemDelegate.qml DroppableTextArea 1.0 Form/DroppableTextArea.qml ListForm 1.0 Form/ListForm.qml ListItemSelector 1.0 Form/ListItemSelector.qml +MouseArea 1.0 Form/MouseArea.qml SearchBox 1.0 Form/SearchBox.qml Slider 1.0 Form/Slider.qml StaticListForm 1.0 Form/StaticListForm.qml diff --git a/linphone-app/ui/modules/Linphone/Account/AccountStatus.qml b/linphone-app/ui/modules/Linphone/Account/AccountStatus.qml index 450bbc2b9..06dd3acb1 100644 --- a/linphone-app/ui/modules/Linphone/Account/AccountStatus.qml +++ b/linphone-app/ui/modules/Linphone/Account/AccountStatus.qml @@ -14,6 +14,7 @@ Item { // --------------------------------------------------------------------------- signal clicked + property alias cursorShape:mouseArea.cursorShape // --------------------------------------------------------------------------- @@ -94,11 +95,8 @@ Item { } MouseArea { + id:mouseArea anchors.fill: parent - cursorShape: containsMouse - ? Qt.PointingHandCursor - : Qt.ArrowCursor - hoverEnabled: true onClicked: accountStatus.clicked() } diff --git a/linphone-app/ui/modules/Linphone/Chat/Chat.qml b/linphone-app/ui/modules/Linphone/Chat/Chat.qml index e36f33005..0d72d1761 100644 --- a/linphone-app/ui/modules/Linphone/Chat/Chat.qml +++ b/linphone-app/ui/modules/Linphone/Chat/Chat.qml @@ -148,6 +148,7 @@ Rectangle { MouseArea { id: mouseArea + cursorShape: Qt.ArrowCursor hoverEnabled: true implicitHeight: layout.height width: parent.width + parent.anchors.rightMargin diff --git a/linphone-app/ui/modules/Linphone/Chat/FileMessage.qml b/linphone-app/ui/modules/Linphone/Chat/FileMessage.qml index 4a544e612..158d5a4d1 100644 --- a/linphone-app/ui/modules/Linphone/Chat/FileMessage.qml +++ b/linphone-app/ui/modules/Linphone/Chat/FileMessage.qml @@ -253,10 +253,6 @@ Row { } anchors.fill: parent - cursorShape: containsMouse - ? Qt.PointingHandCursor - : Qt.ArrowCursor - hoverEnabled: true visible: (rectangle.isUploaded || rectangle.isRead) && !$chatEntry.isOutgoing onClicked: { @@ -296,10 +292,6 @@ Row { MouseArea { anchors.fill: parent - cursorShape: containsMouse - ? Qt.PointingHandCursor - : Qt.ArrowCursor - hoverEnabled: true visible: (rectangle.isError || $chatEntry.status === ChatModel.MessageStatusIdle) && $chatEntry.isOutgoing onClicked: proxyModel.resendMessage(index) } diff --git a/linphone-app/ui/modules/Linphone/Codecs/CodecsViewer.qml b/linphone-app/ui/modules/Linphone/Codecs/CodecsViewer.qml index f61304eaa..ef3234919 100644 --- a/linphone-app/ui/modules/Linphone/Codecs/CodecsViewer.qml +++ b/linphone-app/ui/modules/Linphone/Codecs/CodecsViewer.qml @@ -89,6 +89,7 @@ Column { delegate: MouseArea { id: dragArea + cursorShape: Qt.ArrowCursor property bool held: false @@ -189,7 +190,7 @@ Column { id: mouseArea anchors.fill: parent - hoverEnabled: true + cursorShape: Qt.ArrowCursor onPressed: mouse.accepted = false } diff --git a/linphone-app/ui/modules/Linphone/History/Event.qml b/linphone-app/ui/modules/Linphone/History/Event.qml new file mode 100644 index 000000000..aec5f7e44 --- /dev/null +++ b/linphone-app/ui/modules/Linphone/History/Event.qml @@ -0,0 +1,105 @@ +import QtQuick 2.7 + +import Common 1.0 +import Linphone 1.0 +import LinphoneUtils 1.0 +import Linphone.Styles 1.0 +import Utils 1.0 + +// ============================================================================= + +Row { + signal entryClicked(string sipAddress) + + readonly property var _sipAddressObserver: SipAddressesModel.getSipAddressObserver($historyEntry.sipAddress, '') + + property string _type: { + var status = $historyEntry.status + + if (status === HistoryModel.CallStatusSuccess) { + if (!$historyEntry.isStart) { + return 'ended_call' + } + return $historyEntry.isOutgoing ? 'outgoing_call' : 'incoming_call' + } + if (status === HistoryModel.CallStatusDeclined) { + return $historyEntry.isOutgoing ? 'declined_outgoing_call' : 'declined_incoming_call' + } + if (status === HistoryModel.CallStatusMissed) { + return $historyEntry.isOutgoing ? 'missed_outgoing_call' : 'missed_incoming_call' + } + if (status === HistoryModel.CallStatusAborted) { + return $historyEntry.isOutgoing ? 'outgoing_call' : 'incoming_call' + } + if (status === HistoryModel.CallStatusEarlyAborted) { + return $historyEntry.isOutgoing ? 'missed_outgoing_call' : 'missed_incoming_call' + } + if (status === HistoryModel.CallStatusAcceptedElsewhere) { + return $historyEntry.isOutgoing ? 'outgoing_call' : 'incoming_call' + } + if (status === HistoryModel.CallStatusDeclinedElsewhere) { + return $historyEntry.isOutgoing ? 'declined_outgoing_call' : 'declined_incoming_call' + } + + return 'unknown_call_event' + } + + height: HistoryStyle.entry.lineHeight + spacing: HistoryStyle.entry.message.extraContent.spacing + + Icon { + height: parent.height + icon: _type + iconSize: HistoryStyle.entry.event.iconSize + width: HistoryStyle.entry.metaWidth + } + + Text { + Component { + // Never created. + // Private data for `lupdate`. + Item { + property var i18n: [ + QT_TR_NOOP('declinedIncomingCall'), + QT_TR_NOOP('declinedOutgoingCall'), + QT_TR_NOOP('endedCall'), + QT_TR_NOOP('incomingCall'), + QT_TR_NOOP('missedIncomingCall'), + QT_TR_NOOP('missedOutgoingCall'), + QT_TR_NOOP('outgoingCall') + ] + } + } + + color: HistoryStyle.entry.event.text.color + font { + bold: true + pointSize: HistoryStyle.entry.event.text.pointSize + } + height: parent.height + text: qsTr(Utils.snakeToCamel(_type)) +' - ' + verticalAlignment: Text.AlignVCenter + } + Text { + color: HistoryStyle.entry.event.text.color + font { + bold: true + pointSize: HistoryStyle.entry.event.text.pointSize + } + height: parent.height + text: LinphoneUtils.getContactUsername(_sipAddressObserver) + verticalAlignment: Text.AlignVCenter + MouseArea{ + anchors.fill:parent + onClicked:entryClicked($historyEntry.sipAddress) + } + } + ActionButton { + height: HistoryStyle.entry.lineHeight + icon: 'delete' + iconSize: HistoryStyle.entry.deleteIconSize + visible: isHoverEntry() + + onClicked: removeEntry() + } +} diff --git a/linphone-app/ui/modules/Linphone/History/History.js b/linphone-app/ui/modules/Linphone/History/History.js new file mode 100644 index 000000000..6cef48676 --- /dev/null +++ b/linphone-app/ui/modules/Linphone/History/History.js @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +// ============================================================================= +// `Chat.qml` Logic. +// ============================================================================= + +.import QtQuick 2.7 as QtQuick + +.import Linphone 1.0 as Linphone + +.import 'qrc:/ui/scripts/LinphoneUtils/linphone-utils.js' as LinphoneUtils + +// ============================================================================= + +function initView () { + history.tryToLoadMoreEntries = false + history.bindToEnd = true +} + +function loadMoreEntries () { + if (history.atYBeginning && !history.tryToLoadMoreEntries) { + history.tryToLoadMoreEntries = true + history.positionViewAtBeginning() + container.proxyModel.loadMoreEntries() + } +} + +function getComponentFromEntry (historyEntry) { + + if (historyEntry.type === Linphone.HistoryModel.CallEntry) { + return 'Event.qml' + } + + return '' +} + +function handleMoreEntriesLoaded (n) { + history.positionViewAtIndex(n - 1, QtQuick.ListView.Beginning) + history.tryToLoadMoreEntries = false +} + +function handleMovementEnded () { + if (history.atYEnd) { + history.bindToEnd = true + } +} + +function handleMovementStarted () { + history.bindToEnd = false +} diff --git a/linphone-app/ui/modules/Linphone/History/History.qml b/linphone-app/ui/modules/Linphone/History/History.qml new file mode 100644 index 000000000..d6b8a76f1 --- /dev/null +++ b/linphone-app/ui/modules/Linphone/History/History.qml @@ -0,0 +1,205 @@ +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 + +import 'History.js' as Logic + +// ============================================================================= + +Rectangle { + id: container + + property alias proxyModel: history.model + signal entryClicked(string sipAddress) + + // --------------------------------------------------------------------------- + + color: HistoryStyle.color + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + ScrollableListView { + id: history + + // ----------------------------------------------------------------------- + + property bool bindToEnd: false + property bool tryToLoadMoreEntries: true + + // ----------------------------------------------------------------------- + + Layout.fillHeight: true + Layout.fillWidth: true + + highlightFollowsCurrentItem: false + + section { + criteria: ViewSection.FullString + delegate: sectionHeading + property: '$sectionDate' + } + + // ----------------------------------------------------------------------- + + Component.onCompleted: Logic.initView() + + onContentYChanged: Logic.loadMoreEntries() + onMovementEnded: Logic.handleMovementEnded() + onMovementStarted: Logic.handleMovementStarted() + + // ----------------------------------------------------------------------- + + Connections { + target: proxyModel + + // When the view is changed (for example `Calls` -> `Messages`), + // the position is set at end and it can be possible to load + // more entries. + onEntryTypeFilterChanged: Logic.initView() + onMoreEntriesLoaded: Logic.handleMoreEntriesLoaded(n) + } + + // ----------------------------------------------------------------------- + // Heading. + // ----------------------------------------------------------------------- + + Component { + id: sectionHeading + + Item { + implicitHeight: container.height + HistoryStyle.sectionHeading.bottomMargin + width: parent.width + + Borders { + id: container + + borderColor: HistoryStyle.sectionHeading.border.color + bottomWidth: HistoryStyle.sectionHeading.border.width + implicitHeight: text.contentHeight + + HistoryStyle.sectionHeading.padding * 2 + + HistoryStyle.sectionHeading.border.width * 2 + topWidth: HistoryStyle.sectionHeading.border.width + width: parent.width + + Text { + id: text + + anchors.fill: parent + color: HistoryStyle.sectionHeading.text.color + font { + bold: true + pointSize: HistoryStyle.sectionHeading.text.pointSize + } + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + // Cast section to integer because Qt converts the + // sectionDate in string!!! + text: new Date(section).toLocaleDateString( + Qt.locale(App.locale) + ) + } + } + } + } + + // ----------------------------------------------------------------------- + // Message/Event renderer. + // ----------------------------------------------------------------------- + + delegate: Rectangle { + id: entry + + function isHoverEntry () { + return mouseArea.containsMouse + } + + function removeEntry () { + proxyModel.removeEntry(index) + } + + anchors { + left: parent ? parent.left : undefined + leftMargin: HistoryStyle.entry.leftMargin + right: parent ? parent.right : undefined + + rightMargin: HistoryStyle.entry.deleteIconSize + + HistoryStyle.entry.message.extraContent.spacing + + HistoryStyle.entry.message.extraContent.rightMargin + + HistoryStyle.entry.message.extraContent.leftMargin + } + + color: HistoryStyle.color + implicitHeight: layout.height + HistoryStyle.entry.bottomMargin + + // --------------------------------------------------------------------- + + MouseArea { + id: mouseArea + + cursorShape: Qt.ArrowCursor + hoverEnabled: true + implicitHeight: layout.height + width: parent.width + parent.anchors.rightMargin + + RowLayout { + id: layout + + spacing: 0 + width: entry.width + + // Display time. + Text { + Layout.alignment: Qt.AlignTop + Layout.preferredHeight: HistoryStyle.entry.lineHeight + Layout.preferredWidth: HistoryStyle.entry.time.width + + color: HistoryStyle.entry.time.color + font.pointSize: HistoryStyle.entry.time.pointSize + + text: $historyEntry.timestamp.toLocaleString( + Qt.locale(App.locale), + 'hh:mm' + ) + + verticalAlignment: Text.AlignVCenter + + TooltipArea { + text: $historyEntry.timestamp.toLocaleString(Qt.locale(App.locale)) + } + } + + // Display content. + Loader { + id:entryLoader + Layout.fillWidth: true + source: Logic.getComponentFromEntry($historyEntry) + } + Connections{ + target:entryLoader.item + onEntryClicked:{entryClicked(sipAddress)} + } + } + } + } + } + } + + // --------------------------------------------------------------------------- + // Scroll at end if necessary. + // --------------------------------------------------------------------------- + + Timer { + interval: 100 + repeat: true + running: true + + onTriggered: history.bindToEnd && history.positionViewAtEnd() + } +} diff --git a/linphone-app/ui/modules/Linphone/Menus/SipAddressesMenu.qml b/linphone-app/ui/modules/Linphone/Menus/SipAddressesMenu.qml index 448716d09..f194974e3 100644 --- a/linphone-app/ui/modules/Linphone/Menus/SipAddressesMenu.qml +++ b/linphone-app/ui/modules/Linphone/Menus/SipAddressesMenu.qml @@ -101,7 +101,6 @@ Item { id: mouseArea anchors.fill: parent - hoverEnabled: true onClicked: { menu.close() diff --git a/linphone-app/ui/modules/Linphone/Notifications/NotificationBasic.qml b/linphone-app/ui/modules/Linphone/Notifications/NotificationBasic.qml index c2ec8cd5f..369b4cea0 100644 --- a/linphone-app/ui/modules/Linphone/Notifications/NotificationBasic.qml +++ b/linphone-app/ui/modules/Linphone/Notifications/NotificationBasic.qml @@ -35,8 +35,6 @@ Notification { MouseArea { anchors.fill: parent - cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor - hoverEnabled: true onClicked: notification._close(notification.handler) } diff --git a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedFileMessage.qml b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedFileMessage.qml index d1a381218..28a89c92c 100644 --- a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedFileMessage.qml +++ b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedFileMessage.qml @@ -65,8 +65,6 @@ Notification { MouseArea { anchors.fill: parent - cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor - hoverEnabled: true onClicked: notification._close(function () { var uri = Utils.getUriFromSystemPath(notification.fileUri) diff --git a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml index b15b03588..553c56dff 100644 --- a/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml +++ b/linphone-app/ui/modules/Linphone/Notifications/NotificationReceivedMessage.qml @@ -71,8 +71,6 @@ Notification { MouseArea { anchors.fill: parent - cursorShape: containsMouse ? Qt.PointingHandCursor : Qt.ArrowCursor - hoverEnabled: true onClicked: notification._close(function () { AccountSettingsModel.setDefaultProxyConfigFromSipAddress(notification.localAddress) diff --git a/linphone-app/ui/modules/Linphone/Styles/History/HistoryStyle.qml b/linphone-app/ui/modules/Linphone/Styles/History/HistoryStyle.qml new file mode 100644 index 000000000..e459a0ef3 --- /dev/null +++ b/linphone-app/ui/modules/Linphone/Styles/History/HistoryStyle.qml @@ -0,0 +1,61 @@ +pragma Singleton +import QtQml 2.2 + +import Colors 1.0 +import Units 1.0 + +// ============================================================================= + +QtObject { + property color color: Colors.q + + property QtObject sectionHeading: QtObject { + property int padding: 5 + property int bottomMargin: 20 + + property QtObject border: QtObject { + property color color: Colors.g10 + property int width: 1 + } + + property QtObject text: QtObject { + property int pointSize: Units.dp * 10 + property color color: Colors.g + } + } + + + property QtObject entry: QtObject { + property int bottomMargin: 10 + property int deleteIconSize: 22 + property int leftMargin: 18 + property int lineHeight: 30 + property int metaWidth: 40 + + property QtObject event: QtObject { + property int iconSize: 18 + + property QtObject text: QtObject { + property color color: Colors.d + property int pointSize: Units.dp * 10 + } + } + + property QtObject message: QtObject { + property int padding: 8 + property int radius: 4 + + property QtObject extraContent: QtObject { + property int leftMargin: 10 + property int spacing: 5 + property int rightMargin: 5 + } + } + + property QtObject time: QtObject { + property color color: Colors.d + property int pointSize: Units.dp * 10 + property int width: 44 + } + } +} diff --git a/linphone-app/ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml b/linphone-app/ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml index 78f033b97..a70b59ad0 100644 --- a/linphone-app/ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml +++ b/linphone-app/ui/modules/Linphone/Styles/Timeline/TimelineStyle.qml @@ -34,7 +34,10 @@ QtObject { } property QtObject legend: QtObject { - property color backgroundColor: Colors.f + property QtObject backgroundColor: QtObject { + property color normal: Colors.f + property color hovered: Colors.c + } property color color: Colors.d property int pointSize: Units.dp * 11 property int height: 30 diff --git a/linphone-app/ui/modules/Linphone/Styles/qmldir b/linphone-app/ui/modules/Linphone/Styles/qmldir index 416f69706..e6ee7fb0c 100644 --- a/linphone-app/ui/modules/Linphone/Styles/qmldir +++ b/linphone-app/ui/modules/Linphone/Styles/qmldir @@ -25,6 +25,8 @@ singleton ContactStyle 1.0 Contact/ContactStyle.qml singleton OnlineInstallerDialogStyle 1.0 Dialog/OnlineInstallerDialogStyle.qml +singleton HistoryStyle 1.0 History/HistoryStyle.qml + singleton SipAddressesMenuStyle 1.0 Menus/SipAddressesMenuStyle.qml singleton MessageCounterStyle 1.0 Misc/MessageCounterStyle.qml diff --git a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml index b618dc899..fe90a60f0 100644 --- a/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml +++ b/linphone-app/ui/modules/Linphone/Timeline/Timeline.qml @@ -55,7 +55,16 @@ Rectangle { Rectangle { Layout.fillWidth: true Layout.preferredHeight: TimelineStyle.legend.height - color: TimelineStyle.legend.backgroundColor + color: showHistory.containsMouse?TimelineStyle.legend.backgroundColor.hovered:TimelineStyle.legend.backgroundColor.normal + + MouseArea{ + id:showHistory + anchors.fill:parent + onClicked: { + view.currentIndex = -1 + timeline.entrySelected('') + } + } Row { anchors { diff --git a/linphone-app/ui/modules/Linphone/View/SipAddressesView.qml b/linphone-app/ui/modules/Linphone/View/SipAddressesView.qml index 4bf1dc6ea..4cac04ac0 100644 --- a/linphone-app/ui/modules/Linphone/View/SipAddressesView.qml +++ b/linphone-app/ui/modules/Linphone/View/SipAddressesView.qml @@ -50,6 +50,7 @@ ScrollableListView { // 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 @@ -194,7 +195,7 @@ ScrollableListView { id: mouseArea anchors.fill: parent - hoverEnabled: true + cursorShape: Qt.ArrowCursor RowLayout { anchors { @@ -215,12 +216,6 @@ ScrollableListView { MouseArea { anchors.fill: parent - - cursorShape: containsMouse - ? Qt.PointingHandCursor - : Qt.ArrowCursor - hoverEnabled: true - onClicked: sipAddressesView.entryClicked($sipAddress) } } diff --git a/linphone-app/ui/modules/Linphone/qmldir b/linphone-app/ui/modules/Linphone/qmldir index 57f05a355..8f41a1d39 100644 --- a/linphone-app/ui/modules/Linphone/qmldir +++ b/linphone-app/ui/modules/Linphone/qmldir @@ -16,6 +16,8 @@ CallStatistics 1.0 Calls/CallStatistics.qml Chat 1.0 Chat/Chat.qml +History 1.0 History/History.qml + CodecsViewer 1.0 Codecs/CodecsViewer.qml Avatar 1.0 Contact/Avatar.qml diff --git a/linphone-app/ui/scripts/LinphoneUtils/linphone-utils.js b/linphone-app/ui/scripts/LinphoneUtils/linphone-utils.js index 93b49afae..3c16a6ac3 100644 --- a/linphone-app/ui/scripts/LinphoneUtils/linphone-utils.js +++ b/linphone-app/ui/scripts/LinphoneUtils/linphone-utils.js @@ -84,28 +84,31 @@ function _getUsername (str) { // Returns the username of a contact/sipAddressObserver object or URI string. function getContactUsername (contact) { - var object = contact.contact || // Contact object from `SipAddressObserver`. - (contact.vcard && contact) // Contact object. - - // 1. `object` is a contact. - if (object) { - return object.vcard.username - } - - // 2. `object` is just a string. - object = Utils.isString(contact.peerAddress) - ? contact.peerAddress // String from `SipAddressObserver`. - : contact // Just a String. - - // Use display name. - var name = _getDisplayName(object) - if (name != null) { - return name - } - - // Use username. - name = _getUsername(object) - return name == null ? 'Bad EGG' : name + if(contact){ + var object = contact.contact || // Contact object from `SipAddressObserver`. + (contact.vcard && contact) // Contact object. + + // 1. `object` is a contact. + if (object) { + return object.vcard.username + } + + // 2. `object` is just a string. + object = Utils.isString(contact.peerAddress) + ? contact.peerAddress // String from `SipAddressObserver`. + : contact // Just a String. + + // Use display name. + var name = _getDisplayName(object) + if (name != null) { + return name + } + + // Use username. + name = _getUsername(object) + return name == null ? 'Bad EGG' : name + }else + return ''; } // ============================================================================= diff --git a/linphone-app/ui/views/App/Calls/IncallFullscreenWindow.qml b/linphone-app/ui/views/App/Calls/IncallFullscreenWindow.qml index bbd2df5f3..98e6e34e3 100644 --- a/linphone-app/ui/views/App/Calls/IncallFullscreenWindow.qml +++ b/linphone-app/ui/views/App/Calls/IncallFullscreenWindow.qml @@ -101,8 +101,8 @@ Window { anchors.fill: parent acceptedButtons: Qt.NoButton - hoverEnabled: true propagateComposedEvents: true + cursorShape: Qt.ArrowCursor onEntered: hideButtonsTimer.start() onExited: hideButtonsTimer.stop() diff --git a/linphone-app/ui/views/App/Main/Contacts.qml b/linphone-app/ui/views/App/Main/Contacts.qml index ce4c70c39..378c5a776 100644 --- a/linphone-app/ui/views/App/Main/Contacts.qml +++ b/linphone-app/ui/views/App/Main/Contacts.qml @@ -208,14 +208,10 @@ ColumnLayout { id: mouseArea anchors.fill: parent - hoverEnabled: true + cursorShape: Qt.ArrowCursor MouseArea { anchors.fill: parent - cursorShape: containsMouse - ? Qt.PointingHandCursor - : Qt.ArrowCursor - hoverEnabled: true onClicked: window.setView('ContactEdit', { sipAddress: $contact.vcard.sipAddresses[0] diff --git a/linphone-app/ui/views/App/Main/Conversation.qml b/linphone-app/ui/views/App/Main/Conversation.qml index 26320e079..937890419 100644 --- a/linphone-app/ui/views/App/Main/Conversation.qml +++ b/linphone-app/ui/views/App/Main/Conversation.qml @@ -102,9 +102,9 @@ ColumnLayout { onClicked: window.setView('ContactEdit', { sipAddress: conversation.peerAddress }) - TooltipArea { - text: Logic.getEditTooltipText() - } + TooltipArea { + text: Logic.getEditTooltipText() + } } ActionButton { diff --git a/linphone-app/ui/views/App/Main/HistoryView.js b/linphone-app/ui/views/App/Main/HistoryView.js new file mode 100644 index 000000000..bc657b104 --- /dev/null +++ b/linphone-app/ui/views/App/Main/HistoryView.js @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010-2020 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +// ============================================================================= +// `Conversation.qml` Logic. +// ============================================================================= + +.import Linphone 1.0 as Linphone + +.import 'qrc:/ui/scripts/LinphoneUtils/linphone-utils.js' as LinphoneUtils +.import 'qrc:/ui/scripts/Utils/utils.js' as Utils + +// ============================================================================= + +function removeAllEntries () { + window.attachVirtualWindow(Utils.buildDialogUri('ConfirmDialog'), { + descriptionText: qsTr('removeAllEntriesDescription'), + }, function (status) { + if (status) { + historyProxyModel.removeAllEntries() + } + }) +} + +function getAvatar () { + var contact = historyView._sipAddressObserver.contact + return contact ? contact.vcard.avatar : '' +} + +function getEditIcon () { + return historyView._sipAddressObserver && historyView._sipAddressObserver.contact ? 'contact_edit' : 'contact_add' +} + +function getEditTooltipText() { + return historyView._sipAddressObserver && historyView._sipAddressObserver.contact ? qsTr('tooltipContactEdit') : qsTr('tooltipContactAdd') +} + +function getUsername () { + return LinphoneUtils.getContactUsername(historyView._sipAddressObserver) +} + +function updateHistoryFilter (button) { + var HistoryModel = Linphone.HistoryModel + if (button === 0) { + historyProxyModel.setEntryTypeFilter(HistoryModel.GenericEntry) + } else if (button === 1) { + historyProxyModel.setEntryTypeFilter(HistoryModel.CallEntry) + } +} diff --git a/linphone-app/ui/views/App/Main/HistoryView.qml b/linphone-app/ui/views/App/Main/HistoryView.qml new file mode 100644 index 000000000..837bfc5ba --- /dev/null +++ b/linphone-app/ui/views/App/Main/HistoryView.qml @@ -0,0 +1,150 @@ +import QtQuick 2.7 +import QtQuick.Layouts 1.3 + +import Common 1.0 +import Linphone 1.0 + +import App.Styles 1.0 + +import 'HistoryView.js' as Logic + +// ============================================================================= + +ColumnLayout { + id: historyView + + property string peerAddress + property string fullPeerAddress + + readonly property var _sipAddressObserver: peerAddress?SipAddressesModel.getSipAddressObserver((fullPeerAddress?fullPeerAddress:peerAddress), ''):null + + + // --------------------------------------------------------------------------- + + spacing: 0 + + // --------------------------------------------------------------------------- + // Contact bar. + // --------------------------------------------------------------------------- + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: peerAddress?HistoryViewStyle.bar.height:HistoryViewStyle.bar.height/2 + + color: HistoryViewStyle.bar.backgroundColor + + RowLayout { + anchors { + fill: parent + leftMargin: HistoryViewStyle.bar.leftMargin + rightMargin: HistoryViewStyle.bar.rightMargin + } + spacing: HistoryViewStyle.bar.spacing + + layoutDirection: peerAddress?Qt.LeftToRight :Qt.RightToLeft + + Avatar { + id: avatar + + Layout.preferredHeight: HistoryViewStyle.bar.avatarSize + Layout.preferredWidth: HistoryViewStyle.bar.avatarSize + + image: peerAddress?Logic.getAvatar():null + + presenceLevel: historyView._sipAddressObserver?Presence.getPresenceLevel( + historyView._sipAddressObserver.presenceStatus + ):null + + username: peerAddress?Logic.getUsername():null + visible:peerAddress + } + + ContactDescription { + Layout.fillHeight: true + Layout.fillWidth: true + + sipAddress: historyView.peerAddress + sipAddressColor: HistoryViewStyle.bar.description.sipAddressColor + username: avatar.username + usernameColor: HistoryViewStyle.bar.description.usernameColor + visible:peerAddress + } + + Row { + Layout.fillHeight: true + + spacing: HistoryViewStyle.bar.actions.spacing + + ActionBar { + anchors.verticalCenter: parent.verticalCenter + iconSize: HistoryViewStyle.bar.actions.call.iconSize + + ActionButton { + icon: 'video_call' + visible: peerAddress && SettingsModel.videoSupported && SettingsModel.outgoingCallsEnabled + + onClicked: CallsListModel.launchVideoCall(historyView.peerAddress) + } + + ActionButton { + icon: 'call' + visible: peerAddress && SettingsModel.outgoingCallsEnabled + + onClicked: CallsListModel.launchAudioCall(historyView.peerAddress) + } + } + + ActionBar { + anchors.verticalCenter: parent.verticalCenter + + ActionButton { + icon: Logic.getEditIcon() + iconSize: HistoryViewStyle.bar.actions.edit.iconSize + visible: peerAddress && SettingsModel.contactsEnabled + + onClicked: window.setView('ContactEdit', { sipAddress: historyView.peerAddress }) + TooltipArea { + text: peerAddress?Logic.getEditTooltipText():'' + } + } + + ActionButton { + icon: 'delete' + iconSize: HistoryViewStyle.bar.actions.edit.iconSize + + onClicked: Logic.removeAllEntries() + + TooltipArea { + text: qsTr('cleanHistory') + } + } + } + } + } + } + + // --------------------------------------------------------------------------- + // History. + // --------------------------------------------------------------------------- + + History { + Layout.fillHeight: true + Layout.fillWidth: true + + onEntryClicked:{ + historyView.fullPeerAddress=sipAddress + historyView.peerAddress=sipAddress + historyProxyModel.resetMessageCount() + } + + proxyModel: HistoryProxyModel { + id: historyProxyModel + + Component.onCompleted: { + setEntryTypeFilter() + resetMessageCount() + } + } + } + +} diff --git a/linphone-app/ui/views/App/Main/MainWindow.qml b/linphone-app/ui/views/App/Main/MainWindow.qml index e69f6b8d6..cba397da8 100644 --- a/linphone-app/ui/views/App/Main/MainWindow.qml +++ b/linphone-app/ui/views/App/Main/MainWindow.qml @@ -110,6 +110,7 @@ ApplicationWindow { TooltipArea { text: AccountSettingsModel.sipAddress + hoveringCursor: Qt.PointingHandCursor } onClicked: { @@ -260,12 +261,14 @@ ApplicationWindow { Layout.fillWidth: true model: TimelineModel - onEntrySelected: setView('Conversation', { - peerAddress: entry, - localAddress: AccountSettingsModel.sipAddress, - fullPeerAddress: entry, - fullLocalAddress: AccountSettingsModel.fullSipAddress - }) + onEntrySelected: (entry?setView('Conversation', { + peerAddress: entry, + localAddress: AccountSettingsModel.sipAddress, + fullPeerAddress: entry, + fullLocalAddress: AccountSettingsModel.fullSipAddress + }): + setView('HistoryView', {}) + ) } } @@ -279,7 +282,7 @@ ApplicationWindow { Layout.fillWidth: true source: 'Home.qml' - } + } } } } @@ -287,7 +290,7 @@ ApplicationWindow { // --------------------------------------------------------------------------- // Url handlers. // --------------------------------------------------------------------------- - + Connections { target: UrlHandlers diff --git a/linphone-app/ui/views/App/Settings/SettingsWindow.qml b/linphone-app/ui/views/App/Settings/SettingsWindow.qml index 374f3a81a..0227c4e33 100644 --- a/linphone-app/ui/views/App/Settings/SettingsWindow.qml +++ b/linphone-app/ui/views/App/Settings/SettingsWindow.qml @@ -116,6 +116,7 @@ ApplicationWindow { anchors.fill: parent onClicked: konami.forceActiveFocus() + cursorShape: Qt.ArrowCursor Konami { id: konami diff --git a/linphone-app/ui/views/App/Styles/Main/HistoryViewStyle.qml b/linphone-app/ui/views/App/Styles/Main/HistoryViewStyle.qml new file mode 100644 index 000000000..0cbc42ecc --- /dev/null +++ b/linphone-app/ui/views/App/Styles/Main/HistoryViewStyle.qml @@ -0,0 +1,50 @@ +pragma Singleton +import QtQml 2.2 + +import Colors 1.0 + +// ============================================================================= + +QtObject { + property QtObject bar: QtObject { + property color backgroundColor: Colors.e + property int avatarSize: 60 + property int height: 80 + property int leftMargin: 40 + property int rightMargin: 30 + property int spacing: 20 + + property QtObject actions: QtObject { + property int spacing: 40 + + property QtObject call: QtObject { + property int iconSize: 40 + } + + property QtObject del: QtObject { + property int iconSize: 22 + } + + property QtObject edit: QtObject { + property int iconSize: 22 + } + } + + property QtObject description: QtObject { + property color sipAddressColor: Colors.g + property color usernameColor: Colors.j + } + } + + property QtObject filters: QtObject { + property color backgroundColor: Colors.q + property int height: 51 + property int leftMargin: 40 + + property QtObject border: QtObject { + property color color: Colors.g10 + property int bottomWidth: 1 + property int topWidth: 0 + } + } +} diff --git a/linphone-app/ui/views/App/Styles/qmldir b/linphone-app/ui/views/App/Styles/qmldir index b750e834b..6eafce9e8 100644 --- a/linphone-app/ui/views/App/Styles/qmldir +++ b/linphone-app/ui/views/App/Styles/qmldir @@ -29,6 +29,7 @@ singleton ContactEditStyle 1.0 Main/ContactEditSty singleton ContactsStyle 1.0 Main/ContactsStyle.qml singleton ConversationStyle 1.0 Main/ConversationStyle.qml singleton HomeStyle 1.0 Main/HomeStyle.qml +singleton HistoryViewStyle 1.0 Main/HistoryViewStyle.qml singleton InviteFriendsStyle 1.0 Main/InviteFriendsStyle.qml singleton MainWindowStyle 1.0 Main/MainWindowStyle.qml