chat list

chat messages view

update sdk
This commit is contained in:
Gaelle Braud 2025-04-22 09:25:17 +02:00
parent 9b9994b358
commit 8fb42c333c
54 changed files with 3508 additions and 497 deletions

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.16)
project(Linphone VERSION 6.0.0 LANGUAGES CXX)
project(Linphone VERSION 6.1.0 LANGUAGES CXX)
################################################################
# PACKAGES

View file

@ -52,6 +52,10 @@
#include "core/call/CallGui.hpp"
#include "core/call/CallList.hpp"
#include "core/call/CallProxy.hpp"
#include "core/chat/ChatProxy.hpp"
#include "core/chat/message/ChatMessageProxy.hpp"
#include "core/chat/message/ChatMessageList.hpp"
#include "core/chat/message/ChatMessageGui.hpp"
#include "core/camera/CameraGui.hpp"
#include "core/conference/ConferenceGui.hpp"
#include "core/conference/ConferenceInfoGui.hpp"
@ -652,6 +656,12 @@ void App::initCppInterfaces() {
qmlRegisterType<CallHistoryProxy>(Constants::MainQmlUri, 1, 0, "CallHistoryProxy");
qmlRegisterType<CallGui>(Constants::MainQmlUri, 1, 0, "CallGui");
qmlRegisterType<CallProxy>(Constants::MainQmlUri, 1, 0, "CallProxy");
qmlRegisterType<ChatList>(Constants::MainQmlUri, 1, 0, "ChatList");
qmlRegisterType<ChatProxy>(Constants::MainQmlUri, 1, 0, "ChatProxy");
qmlRegisterType<ChatGui>(Constants::MainQmlUri, 1, 0, "ChatGui");
qmlRegisterType<ChatMessageGui>(Constants::MainQmlUri, 1, 0, "ChatMessageGui");
qmlRegisterType<ChatMessageList>(Constants::MainQmlUri, 1, 0, "ChatMessageList");
qmlRegisterType<ChatMessageProxy>(Constants::MainQmlUri, 1, 0, "ChatMessageProxy");
qmlRegisterUncreatableType<ConferenceCore>(Constants::MainQmlUri, 1, 0, "ConferenceCore",
QLatin1String("Uncreatable"));
qmlRegisterType<ConferenceGui>(Constants::MainQmlUri, 1, 0, "ConferenceGui");

View file

@ -19,6 +19,14 @@ list(APPEND _LINPHONEAPP_SOURCES
core/camera/CameraGui.cpp
core/camera/CameraDummy.cpp
core/camera/PreviewManager.cpp
core/chat/ChatCore.cpp
core/chat/ChatGui.cpp
core/chat/ChatList.cpp
core/chat/ChatProxy.cpp
core/chat/message/ChatMessageCore.cpp
core/chat/message/ChatMessageGui.cpp
core/chat/message/ChatMessageList.cpp
core/chat/message/ChatMessageProxy.cpp
core/fps-counter/FPSCounter.cpp
core/friend/FriendCore.cpp
core/friend/FriendGui.cpp

View file

@ -129,7 +129,7 @@ void CallList::setSelf(QSharedPointer<CallList> me) {
});
mModelConnection->makeConnectToModel(&CoreModel::firstCallStarted,
[this]() { mModelConnection->invokeToCore([this]() { lUpdate(); }); });
[this]() { mModelConnection->invokeToCore([this]() { lUpdate(); }); });
mModelConnection->makeConnectToModel(&CoreModel::lastCallEnded, [this]() {
mModelConnection->invokeToCore([this]() {
setHaveCall(false);
@ -158,7 +158,7 @@ CallGui *CallList::getCurrentCall() const {
else return nullptr;
}
void CallList::setCurrentCall(CallGui* callGui) {
void CallList::setCurrentCall(CallGui *callGui) {
auto callCore = callGui ? callGui->mCore : nullptr;
if (mCurrentCall != callCore) {
mCurrentCall = callCore;

View file

@ -63,7 +63,7 @@ void CallProxy::setSourceModel(QAbstractItemModel *model) {
connect(newCallList, &CallList::haveCallChanged, this, &CallProxy::haveCallChanged, Qt::QueuedConnection);
connect(this, &CallProxy::lMergeAll, newCallList, &CallList::lMergeAll);
}
setSourceModels(new SortFilterList(model, Qt::AscendingOrder));
setSourceModels(new SortFilterList(model));
}
bool CallProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {

View file

@ -0,0 +1,195 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatCore.hpp"
#include "core/App.hpp"
#include "core/friend/FriendCore.hpp"
#include "core/setting/SettingsCore.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(ChatCore)
/***********************************************************************/
QSharedPointer<ChatCore> ChatCore::create(const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
auto sharedPointer = QSharedPointer<ChatCore>(new ChatCore(chatRoom), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
ChatCore::ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom) : QObject(nullptr) {
lDebug() << "[ChatCore] new" << this;
mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mLastUpdatedTime = QDateTime::fromSecsSinceEpoch(chatRoom->getLastUpdateTime());
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) {
mTitle = ToolModel::getDisplayName(chatRoom->getPeerAddress()->clone());
mAvatarUri = Utils::coreStringToAppString(chatRoom->getPeerAddress()->asStringUriOnly());
auto peerAddress = chatRoom->getPeerAddress();
mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly());
} else {
if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) {
auto peer = chatRoom->getParticipants().front();
if (peer) mTitle = ToolModel::getDisplayName(peer->getAddress()->clone());
mAvatarUri = Utils::coreStringToAppString(peer->getAddress()->asStringUriOnly());
auto participants = chatRoom->getParticipants();
if (participants.size() == 1) {
auto peerAddress = participants.front()->getAddress();
if (peerAddress) mPeerAddress = Utils::coreStringToAppString(peerAddress->asStringUriOnly());
}
} else if (chatRoom->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) {
mTitle = Utils::coreStringToAppString(chatRoom->getSubject());
mAvatarUri = Utils::coreStringToAppString(chatRoom->getSubject());
}
}
mUnreadMessagesCount = chatRoom->getUnreadMessagesCount();
mChatModel = Utils::makeQObject_ptr<ChatModel>(chatRoom);
mChatModel->setSelf(mChatModel);
mLastMessageInHistory = mChatModel->getLastMessageInHistory();
auto history = chatRoom->getHistory(0, (int)linphone::ChatRoom::HistoryFilter::ChatMessage);
std::list<std::shared_ptr<linphone::ChatMessage>> linHistory;
for (auto &eventLog : history) {
if (eventLog->getChatMessage()) linHistory.push_back(eventLog->getChatMessage());
}
for (auto &message : linHistory) {
if (!message) continue;
auto chatMessage = ChatMessageCore::create(message);
mChatMessageList.append(chatMessage);
}
}
ChatCore::~ChatCore() {
lDebug() << "[ChatCore] delete" << this;
mustBeInMainThread("~" + getClassName());
emit mChatModel->removeListener();
}
void ChatCore::setSelf(QSharedPointer<ChatCore> me) {
mChatModelConnection = SafeConnection<ChatCore, ChatModel>::create(me, mChatModel);
// mChatModelConnection->makeConnectToCore(&ChatCore::lSetMicrophoneMuted, [this](bool isMuted) {
// mChatModelConnection->invokeToModel(
// [this, isMuted]() { mChatModel->setMicrophoneMuted(isMuted); });
// });
mChatModelConnection->makeConnectToModel(&ChatModel::chatMessageReceived,
[this](const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
if (mChatModel->getMonitor() != chatRoom) return;
qDebug() << "MESSAGE RECEIVED IN CHATROOM" << mChatModel->getTitle();
// mChatModelConnection->invokeToCore([this, isMuted]() {
// setMicrophoneMuted(isMuted); });
});
}
QDateTime ChatCore::getLastUpdatedTime() const {
return mLastUpdatedTime;
}
void ChatCore::setLastUpdatedTime(QDateTime time) {
if (mLastUpdatedTime != time) {
mLastUpdatedTime = time;
emit lastUpdatedTimeChanged(time);
}
}
QString ChatCore::getTitle() const {
return mTitle;
}
void ChatCore::setTitle(QString title) {
if (mTitle != title) {
mTitle = title;
emit titleChanged(title);
}
}
QString ChatCore::getPeerAddress() const {
return mPeerAddress;
}
void ChatCore::setPeerAddress(QString peerAddress) {
if (mPeerAddress != peerAddress) {
mPeerAddress = peerAddress;
emit peerAddressChanged(peerAddress);
}
}
QString ChatCore::getAvatarUri() const {
return mAvatarUri;
}
void ChatCore::setAvatarUri(QString avatarUri) {
if (mAvatarUri != avatarUri) {
mAvatarUri = avatarUri;
emit avatarUriChanged();
}
}
QString ChatCore::getLastMessageInHistory() const {
return mLastMessageInHistory;
}
void ChatCore::setLastMessageInHistory(QString lastMessageInHistory) {
if (mLastMessageInHistory != lastMessageInHistory) {
mLastMessageInHistory = lastMessageInHistory;
emit lastMessageInHistoryChanged(lastMessageInHistory);
}
}
int ChatCore::getUnreadMessagesCount() const {
return mUnreadMessagesCount;
}
void ChatCore::setUnreadMessagesCount(int count) {
if (mUnreadMessagesCount != count) {
mUnreadMessagesCount = count;
emit unreadMessagesCountChanged(count);
}
}
QList<QSharedPointer<ChatMessageCore>> ChatCore::getChatMessageList() const {
return mChatMessageList;
}
void ChatCore::resetChatMessageList(QList<QSharedPointer<ChatMessageCore>> list) {
mChatMessageList = list;
emit messageListChanged();
}
void ChatCore::appendMessagesToMessageList(QList<QSharedPointer<ChatMessageCore>> list) {
int nbAdded = 0;
for (auto &message : list) {
if (mChatMessageList.contains(message)) continue;
mChatMessageList.append(message);
++nbAdded;
}
if (nbAdded > 0) emit messageListChanged();
}
void ChatCore::removeMessagesFromMessageList(QList<QSharedPointer<ChatMessageCore>> list) {
int nbRemoved = 0;
for (auto &message : list) {
if (mChatMessageList.contains(message)) {
mChatMessageList.removeAll(message);
++nbRemoved;
}
}
if (nbRemoved > 0) emit messageListChanged();
}

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_CORE_H_
#define CHAT_CORE_H_
#include "core/chat/message/ChatMessageCore.hpp"
#include "model/chat/ChatModel.hpp"
#include "model/search/MagicSearchModel.hpp"
#include "tool/LinphoneEnums.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class ChatCore : public QObject, public AbstractObject {
Q_OBJECT
public:
Q_PROPERTY(QString title READ getTitle WRITE setTitle NOTIFY titleChanged)
Q_PROPERTY(QString peerAddress READ getPeerAddress WRITE setPeerAddress NOTIFY peerAddressChanged)
Q_PROPERTY(QString avatarUri READ getAvatarUri WRITE setAvatarUri NOTIFY avatarUriChanged)
Q_PROPERTY(QDateTime lastUpdatedTime READ getLastUpdatedTime WRITE setLastUpdatedTime NOTIFY lastUpdatedTimeChanged)
Q_PROPERTY(QString lastMessageInHistory READ getLastMessageInHistory WRITE setLastMessageInHistory NOTIFY
lastMessageInHistoryChanged)
Q_PROPERTY(int unreadMessagesCount READ getUnreadMessagesCount WRITE setUnreadMessagesCount NOTIFY
unreadMessagesCountChanged)
// Q_PROPERTY(VideoStats videoStats READ getVideoStats WRITE setVideoStats NOTIFY videoStatsChanged)
// Should be call from model Thread. Will be automatically in App thread after initialization
static QSharedPointer<ChatCore> create(const std::shared_ptr<linphone::ChatRoom> &chatRoom);
ChatCore(const std::shared_ptr<linphone::ChatRoom> &chatRoom);
~ChatCore();
void setSelf(QSharedPointer<ChatCore> me);
QDateTime getLastUpdatedTime() const;
void setLastUpdatedTime(QDateTime time);
QString getTitle() const;
void setTitle(QString title);
QString getLastMessageInHistory() const;
void setLastMessageInHistory(QString message);
int getUnreadMessagesCount() const;
void setUnreadMessagesCount(int count);
QString getPeerAddress() const;
void setPeerAddress(QString peerAddress);
QList<QSharedPointer<ChatMessageCore>> getChatMessageList() const;
void resetChatMessageList(QList<QSharedPointer<ChatMessageCore>> list);
void appendMessagesToMessageList(QList<QSharedPointer<ChatMessageCore>> list);
void removeMessagesFromMessageList(QList<QSharedPointer<ChatMessageCore>> list);
QString getAvatarUri() const;
void setAvatarUri(QString avatarUri);
signals:
void lastUpdatedTimeChanged(QDateTime time);
void lastMessageInHistoryChanged(QString time);
void titleChanged(QString title);
void peerAddressChanged(QString address);
void unreadMessagesCountChanged(int count);
void messageListChanged();
void avatarUriChanged();
private:
QString id;
QDateTime mLastUpdatedTime;
QString mLastMessageInHistory;
QString mPeerAddress;
QString mTitle;
QString mAvatarUri;
int mUnreadMessagesCount;
std::shared_ptr<ChatModel> mChatModel;
QList<QSharedPointer<ChatMessageCore>> mChatMessageList;
QSharedPointer<SafeConnection<ChatCore, ChatModel>> mChatModelConnection;
DECLARE_ABSTRACT_OBJECT
};
Q_DECLARE_METATYPE(ChatCore *)
#endif

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(ChatGui)
ChatGui::ChatGui(QSharedPointer<ChatCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
ChatGui::~ChatGui() {
mustBeInMainThread("~" + getClassName());
}
ChatCore *ChatGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_GUI_H_
#define CHAT_GUI_H_
#include "ChatCore.hpp"
#include <QObject>
#include <QSharedPointer>
class ChatGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(ChatCore *core READ getCore CONSTANT)
public:
ChatGui(QSharedPointer<ChatCore> core);
~ChatGui();
ChatCore *getCore() const;
QSharedPointer<ChatCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatList.hpp"
#include "ChatCore.hpp"
#include "ChatGui.hpp"
#include "core/App.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(ChatList)
QSharedPointer<ChatList> ChatList::create() {
auto model = QSharedPointer<ChatList>(new ChatList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
QSharedPointer<ChatCore> ChatList::createChatCore(const std::shared_ptr<linphone::ChatRoom> &chatroom) {
auto chatCore = ChatCore::create(chatroom);
return chatCore;
}
ChatList::ChatList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
}
ChatList::~ChatList() {
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
void ChatList::setSelf(QSharedPointer<ChatList> me) {
mModelConnection = SafeConnection<ChatList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&ChatList::lUpdate, [this]() {
mModelConnection->invokeToModel([this]() {
mustBeInLinphoneThread(getClassName());
// Avoid copy to lambdas
QList<QSharedPointer<ChatCore>> *chats = new QList<QSharedPointer<ChatCore>>();
auto currentAccount = CoreModel::getInstance()->getCore()->getDefaultAccount();
// auto linphoneChatRooms = currentAccount->filterChatRooms(Utils::appStringToCoreString(mFilter));
auto linphoneChatRooms = currentAccount->getChatRooms();
for (auto it : linphoneChatRooms) {
auto model = createChatCore(it);
chats->push_back(model);
}
mModelConnection->invokeToCore([this, chats]() {
mustBeInMainThread(getClassName());
resetData<ChatCore>(*chats);
delete chats;
});
});
});
mModelConnection->makeConnectToModel(&CoreModel::chatRoomStateChanged,
[this](const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
linphone::ChatRoom::State state) {
// check account, filtre, puis ajout si c'est bon
bool toCreate = false;
if (toCreate) {
auto model = createChatCore(chatRoom);
mModelConnection->invokeToCore([this, model]() {
// We set the current here and not on firstChatStarted event
// because we don't want to add unicity check while keeping the
// same model between list and current chat.
add(model);
});
}
});
mModelConnection->makeConnectToModel(&CoreModel::defaultAccountChanged, [this] (std::shared_ptr<linphone::Core> core, std::shared_ptr<linphone::Account> account) {
lUpdate();
});
connect(this, &ChatList::filterChanged, [this](QString filter) {
mFilter = filter;
lUpdate();
});
lUpdate();
}
QVariant ChatList::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(new ChatGui(mList[row].objectCast<ChatCore>()));
return QVariant();
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_LIST_H_
#define CHAT_LIST_H_
#include "../proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
class ChatGui;
class ChatCore;
// =============================================================================
class ChatList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<ChatList> create();
// Create a ChatCore and make connections to List.
QSharedPointer<ChatCore> createChatCore(const std::shared_ptr<linphone::ChatRoom> &chatroom);
ChatList(QObject *parent = Q_NULLPTR);
~ChatList();
void setSelf(QSharedPointer<ChatList> me);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals:
void lUpdate();
void filterChanged(QString filter);
private:
QString mFilter;
QSharedPointer<SafeConnection<ChatList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatProxy.hpp"
#include "ChatGui.hpp"
#include "ChatList.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(ChatProxy)
ChatProxy::ChatProxy(QObject *parent) : LimitProxy(parent) {
mList = ChatList::create();
setSourceModel(mList.get());
}
ChatProxy::~ChatProxy() {
}
void ChatProxy::setSourceModel(QAbstractItemModel *model) {
auto oldChatList = getListModel<ChatList>();
if (oldChatList) {
disconnect(oldChatList);
}
auto newChatList = dynamic_cast<ChatList *>(model);
if (newChatList) {
// connect(this, &ChatProxy::filterTextChanged, newChatList, &ChatList::filterChanged);
}
setSourceModels(new SortFilterList(model));
sort(0);
}
bool ChatProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
// auto l = getItemAtSource<ChatList, ChatCore>(sourceRow);
// return l != nullptr;
return true;
}
bool ChatProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<ChatList, ChatCore>(sourceLeft.row());
auto r = getItemAtSource<ChatList, ChatCore>(sourceRight.row());
if (l && r) return l->getLastUpdatedTime() >= r->getLastUpdatedTime();
else return true;
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_PROXY_H_
#define CHAT_PROXY_H_
#include "../proxy/LimitProxy.hpp"
#include "core/chat/ChatGui.hpp"
#include "core/chat/ChatList.hpp"
#include "tool/AbstractObject.hpp"
// =============================================================================
class ChatProxy : public LimitProxy, public AbstractObject {
Q_OBJECT
public:
DECLARE_SORTFILTER_CLASS()
ChatProxy(QObject *parent = Q_NULLPTR);
~ChatProxy();
void setSourceModel(QAbstractItemModel *sourceModel) override;
protected:
QSharedPointer<ChatList> mList;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatMessageCore.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(ChatMessageCore)
QSharedPointer<ChatMessageCore> ChatMessageCore::create(const std::shared_ptr<linphone::ChatMessage> &chatmessage) {
auto sharedPointer = QSharedPointer<ChatMessageCore>(new ChatMessageCore(chatmessage), &QObject::deleteLater);
sharedPointer->setSelf(sharedPointer);
sharedPointer->moveToThread(App::getInstance()->thread());
return sharedPointer;
}
ChatMessageCore::ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &chatmessage) {
lDebug() << "[ChatMessageCore] new" << this;
mustBeInLinphoneThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mChatMessageModel = Utils::makeQObject_ptr<ChatMessageModel>(chatmessage);
mChatMessageModel->setSelf(mChatMessageModel);
mText = mChatMessageModel->getText();
mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime());
auto from = chatmessage->getFromAddress();
auto to = chatmessage->getLocalAddress();
mIsRemoteMessage = !from->weakEqual(to);
}
ChatMessageCore::~ChatMessageCore() {
}
void ChatMessageCore::setSelf(QSharedPointer<ChatMessageCore> me) {
mChatMessageModelConnection = SafeConnection<ChatMessageCore, ChatMessageModel>::create(me, mChatMessageModel);
}
QDateTime ChatMessageCore::getTimestamp() const {
return mTimestamp;
}
void ChatMessageCore::setTimestamp(QDateTime timestamp) {
if (mTimestamp != timestamp) {
mTimestamp = timestamp;
emit timestampChanged(timestamp);
}
}
QString ChatMessageCore::getText() const {
return mText;
}
void ChatMessageCore::setText(QString text) {
if (mText != text) {
mText = text;
emit textChanged(text);
}
}
bool ChatMessageCore::isRemoteMessage() const {
return mIsRemoteMessage;
}
void ChatMessageCore::setIsRemoteMessage(bool isRemote) {
if (mIsRemoteMessage != isRemote) {
mIsRemoteMessage = isRemote;
emit isRemoteMessageChanged(isRemote);
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHATMESSAGECORE_H_
#define CHATMESSAGECORE_H_
#include "model/chat/message/ChatMessageModel.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QObject>
#include <QSharedPointer>
#include <linphone++/linphone.hh>
class ChatMessageCore : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(QDateTime timestamp READ getTimestamp WRITE setTimestamp NOTIFY timestampChanged)
Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
Q_PROPERTY(bool isRemoteMessage READ isRemoteMessage WRITE setIsRemoteMessage NOTIFY isRemoteMessageChanged)
public:
static QSharedPointer<ChatMessageCore> create(const std::shared_ptr<linphone::ChatMessage> &chatmessage);
ChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &chatmessage);
~ChatMessageCore();
void setSelf(QSharedPointer<ChatMessageCore> me);
QDateTime getTimestamp() const;
void setTimestamp(QDateTime timestamp);
QString getText() const;
void setText(QString text);
bool isRemoteMessage() const;
void setIsRemoteMessage(bool isRemote);
signals:
void timestampChanged(QDateTime timestamp);
void textChanged(QString text);
void isRemoteMessageChanged(bool isRemote);
private:
DECLARE_ABSTRACT_OBJECT
QString mText;
QDateTime mTimestamp;
bool mIsRemoteMessage = false;
std::shared_ptr<ChatMessageModel> mChatMessageModel;
QSharedPointer<SafeConnection<ChatMessageCore, ChatMessageModel>> mChatMessageModelConnection;
};
#endif // CHATMESSAGECORE_H_

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatMessageGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(ChatMessageGui)
ChatMessageGui::ChatMessageGui(QSharedPointer<ChatMessageCore> core) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
mCore = core;
if (isInLinphoneThread()) moveToThread(App::getInstance()->thread());
}
ChatMessageGui::~ChatMessageGui() {
mustBeInMainThread("~" + getClassName());
}
ChatMessageCore *ChatMessageGui::getCore() const {
return mCore.get();
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_MESSAGE_GUI_H_
#define CHAT_MESSAGE_GUI_H_
#include "ChatMessageCore.hpp"
#include <QObject>
#include <QSharedPointer>
class ChatMessageGui : public QObject, public AbstractObject {
Q_OBJECT
Q_PROPERTY(ChatMessageCore *core READ getCore CONSTANT)
public:
ChatMessageGui(QSharedPointer<ChatMessageCore> core);
~ChatMessageGui();
ChatMessageCore *getCore() const;
QSharedPointer<ChatMessageCore> mCore;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatMessageList.hpp"
#include "ChatMessageCore.hpp"
#include "ChatMessageGui.hpp"
#include "core/chat/ChatCore.hpp"
#include "core/chat/ChatGui.hpp"
#include "core/App.hpp"
#include <QSharedPointer>
#include <linphone++/linphone.hh>
// =============================================================================
DEFINE_ABSTRACT_OBJECT(ChatMessageList)
QSharedPointer<ChatMessageList> ChatMessageList::create() {
auto model = QSharedPointer<ChatMessageList>(new ChatMessageList(), &QObject::deleteLater);
model->moveToThread(App::getInstance()->thread());
model->setSelf(model);
return model;
}
QSharedPointer<ChatMessageCore> ChatMessageList::createChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &chatMessage) {
auto chatMessageCore = ChatMessageCore::create(chatMessage);
return chatMessageCore;
}
ChatMessageList::ChatMessageList(QObject *parent) : ListProxy(parent) {
mustBeInMainThread(getClassName());
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
connect(this, &ChatMessageList::chatChanged, this, &ChatMessageList::lUpdate);
}
ChatMessageList::~ChatMessageList() {
mustBeInMainThread("~" + getClassName());
mModelConnection = nullptr;
}
ChatGui* ChatMessageList::getChat() const {
if (mChatCore) return new ChatGui(mChatCore);
else return nullptr;
}
QSharedPointer<ChatCore> ChatMessageList::getChatCore() const {
return mChatCore;
}
void ChatMessageList::setChatCore(QSharedPointer<ChatCore> core) {
if (mChatCore != core) {
mChatCore = core;
emit chatChanged();
}
}
void ChatMessageList::setChatGui(ChatGui* chat) {
auto chatCore = chat ? chat->mCore : nullptr;
setChatCore(chatCore);
}
void ChatMessageList::setSelf(QSharedPointer<ChatMessageList> me) {
mModelConnection = SafeConnection<ChatMessageList, CoreModel>::create(me, CoreModel::getInstance());
mModelConnection->makeConnectToCore(&ChatMessageList::lUpdate, [this]() {
// mModelConnection->invokeToModel([this]() {
// // Avoid copy to lambdas
// QList<QSharedPointer<CallCore>> *calls = new QList<QSharedPointer<CallCore>>();
// mustBeInLinphoneThread(getClassName());
// mModelConnection->invokeToCore([this, calls, currentCallCore]() {
// mustBeInMainThread(getClassName());
// resetData<CallCore>(*calls);
// });
// });
if (!mChatCore) return;
auto messages = mChatCore->getChatMessageList();
resetData<ChatMessageCore>(messages);
});
connect(this, &ChatMessageList::filterChanged, [this](QString filter) {
mFilter = filter;
lUpdate();
});
lUpdate();
}
QVariant ChatMessageList::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(new ChatMessageGui(mList[row].objectCast<ChatMessageCore>()));
return QVariant();
}

View file

@ -0,0 +1,63 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_MESSAGE_LIST_H_
#define CHAT_MESSAGE_LIST_H_
#include "core/proxy/ListProxy.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/thread/SafeConnection.hpp"
#include <QLocale>
class ChatMessageGui;
class ChatMessageCore;
class ChatCore;
class ChatGui;
// =============================================================================
class ChatMessageList : public ListProxy, public AbstractObject {
Q_OBJECT
public:
static QSharedPointer<ChatMessageList> create();
// Create a ChatMessageCore and make connections to List.
QSharedPointer<ChatMessageCore> createChatMessageCore(const std::shared_ptr<linphone::ChatMessage> &chatMessage);
ChatMessageList(QObject *parent = Q_NULLPTR);
~ChatMessageList();
QSharedPointer<ChatCore> getChatCore() const;
ChatGui* getChat() const;
void setChatCore(QSharedPointer<ChatCore> core);
void setChatGui(ChatGui* chat);
void setSelf(QSharedPointer<ChatMessageList> me);
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
signals:
void lUpdate();
void filterChanged(QString filter);
void chatChanged();
private:
QString mFilter;
QSharedPointer<ChatCore> mChatCore;
QSharedPointer<SafeConnection<ChatMessageList, CoreModel>> mModelConnection;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatMessageProxy.hpp"
#include "ChatMessageGui.hpp"
//#include "core/chat/ChatGui.hpp"
#include "core/App.hpp"
DEFINE_ABSTRACT_OBJECT(ChatMessageProxy)
ChatMessageProxy::ChatMessageProxy(QObject *parent) : LimitProxy(parent) {
mList = ChatMessageList::create();
setSourceModel(mList.get());
}
ChatMessageProxy::~ChatMessageProxy() {
}
void ChatMessageProxy::setSourceModel(QAbstractItemModel *model) {
auto oldChatMessageList = getListModel<ChatMessageList>();
if (oldChatMessageList) {
disconnect(oldChatMessageList);
}
auto newChatMessageList = dynamic_cast<ChatMessageList *>(model);
if (newChatMessageList) {
connect(newChatMessageList, &ChatMessageList::chatChanged, this, &ChatMessageProxy::chatChanged);
}
setSourceModels(new SortFilterList(model));
sort(0);
}
ChatGui* ChatMessageProxy::getChatGui() {
auto model = getListModel<ChatMessageList>();
if (!mChatGui && model) mChatGui = model->getChat();
return mChatGui;
}
void ChatMessageProxy::setChatGui(ChatGui* chat) {
getListModel<ChatMessageList>()->setChatGui(chat);
}
bool ChatMessageProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const {
// auto l = getItemAtSource<ChatMessageList, ChatMessageCore>(sourceRow);
// return l != nullptr;
return true;
}
bool ChatMessageProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<ChatMessageList, ChatMessageCore>(sourceLeft.row());
auto r = getItemAtSource<ChatMessageList, ChatMessageCore>(sourceRight.row());
if (l && r) return l->getTimestamp() <= r->getTimestamp();
else return true;
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_MESSAGE_PROXY_H_
#define CHAT_MESSAGE_PROXY_H_
#include "core/proxy/LimitProxy.hpp"
#include "ChatMessageList.hpp"
#include "tool/AbstractObject.hpp"
// =============================================================================
class ChatGui;
class ChatMessageProxy : public LimitProxy, public AbstractObject {
Q_OBJECT
Q_PROPERTY(ChatGui* chatGui READ getChatGui WRITE setChatGui NOTIFY chatChanged)
public:
DECLARE_SORTFILTER_CLASS()
ChatMessageProxy(QObject *parent = Q_NULLPTR);
~ChatMessageProxy();
ChatGui* getChatGui();
void setChatGui(ChatGui* chat);
void setSourceModel(QAbstractItemModel *sourceModel) override;
signals:
void chatChanged();
protected:
QSharedPointer<ChatMessageList> mList;
ChatGui* mChatGui = nullptr;
DECLARE_ABSTRACT_OBJECT
};
#endif

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M231.87,114l-168-95.89A16,16,0,0,0,40.92,37.34L71.55,128,40.92,218.67A16,16,0,0,0,56,240a16.15,16.15,0,0,0,7.93-2.1l167.92-96.05a16,16,0,0,0,.05-27.89ZM56,224a.56.56,0,0,0,0-.12L85.74,136H144a8,8,0,0,0,0-16H85.74L56.06,32.16A.46.46,0,0,0,56,32l168,95.83Z"></path></svg>

After

Width:  |  Height:  |  Size: 378 B

View file

@ -513,74 +513,74 @@
<context>
<name>App</name>
<message>
<location filename="../../core/App.cpp" line="325"/>
<location filename="../../core/App.cpp" line="326"/>
<source>remote_provisioning_dialog</source>
<extracomment>Voulez-vous télécharger et appliquer la configuration depuis cette adresse ?</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="776"/>
<location filename="../../core/App.cpp" line="780"/>
<source>application_description</source>
<extracomment>&quot;A free and open source SIP video-phone.&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="778"/>
<location filename="../../core/App.cpp" line="782"/>
<source>command_line_arg_order</source>
<extracomment>&quot;Send an order to the application towards a command line&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="781"/>
<location filename="../../core/App.cpp" line="785"/>
<source>command_line_option_show_help</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="786"/>
<location filename="../../core/App.cpp" line="790"/>
<source>command_line_option_show_app_version</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="792"/>
<location filename="../../core/App.cpp" line="796"/>
<source>command_line_option_config_to_fetch</source>
<extracomment>&quot;Specify the linphone configuration file to be fetched. It will be merged with the current configuration.&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="795"/>
<location filename="../../core/App.cpp" line="799"/>
<source>command_line_option_config_to_fetch_arg</source>
<extracomment>&quot;URL, path or file&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="799"/>
<location filename="../../core/App.cpp" line="803"/>
<source>command_line_option_minimized</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="802"/>
<location filename="../../core/App.cpp" line="806"/>
<source>command_line_option_log_to_stdout</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="805"/>
<location filename="../../core/App.cpp" line="809"/>
<source>command_line_option_print_app_logs_only</source>
<extracomment>&quot;Print only logs from the application&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="1165"/>
<location filename="../../core/App.cpp" line="1169"/>
<source>hide_action</source>
<extracomment>&quot;Cacher&quot; &quot;Afficher&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="1165"/>
<location filename="../../core/App.cpp" line="1169"/>
<source>show_action</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/App.cpp" line="1180"/>
<location filename="../../core/App.cpp" line="1184"/>
<source>quit_action</source>
<extracomment>&quot;Quitter&quot;</extracomment>
<translation type="unfinished"></translation>
@ -915,7 +915,7 @@
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="168"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="594"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="588"/>
<source>menu_delete_history</source>
<extracomment>&quot;Supprimer l&apos;historique&quot;</extracomment>
<translation type="unfinished"></translation>
@ -979,7 +979,7 @@
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="456"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="460"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="570"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="564"/>
<source>information_popup_error_title</source>
<translation type="unfinished"></translation>
</message>
@ -996,55 +996,55 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="532"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="526"/>
<source>menu_see_existing_contact</source>
<extracomment>&quot;Voir le contact&quot;</extracomment>
<extracomment>&quot;Show contact&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="534"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="528"/>
<source>menu_add_address_to_contacts</source>
<extracomment>&quot;Ajouter aux contacts&quot;</extracomment>
<extracomment>&quot;Add to contacts&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="552"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="546"/>
<source>menu_copy_sip_address</source>
<extracomment>&quot;Copier l&apos;adresse SIP&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="564"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="558"/>
<source>sip_address_copied_to_clipboard_toast</source>
<extracomment>Adresse copiée</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="566"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="560"/>
<source>sip_address_copied_to_clipboard_message</source>
<extracomment>L&apos;adresse a é copié dans le presse_papiers</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="572"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="566"/>
<source>sip_address_copy_to_clipboard_error</source>
<extracomment>&quot;Erreur lors de la copie de l&apos;adresse&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="670"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="664"/>
<source>notification_missed_call_title</source>
<extracomment>&quot;Appel manqué&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="673"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="667"/>
<source>call_outgoing</source>
<extracomment>&quot;Appel sortant&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="675"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="669"/>
<source>call_audio_incoming</source>
<extracomment>&quot;Appel entrant&quot;</extracomment>
<translation type="unfinished"></translation>
@ -1222,276 +1222,276 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="567"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="573"/>
<source>conference_user_is_recording</source>
<extracomment>&quot;Vous enregistrez la réunion&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="569"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="575"/>
<source>call_user_is_recording</source>
<extracomment>&quot;Vous enregistrez l&apos;appel&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="572"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="578"/>
<source>conference_remote_is_recording</source>
<extracomment>&quot;Un participant enregistre la réunion&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="574"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="580"/>
<source>call_remote_recording</source>
<extracomment>&quot;%1 enregistre l&apos;appel&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="582"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="588"/>
<source>call_stop_recording</source>
<extracomment>&quot;Arrêter l&apos;enregistrement&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="615"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="621"/>
<source>add</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="632"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="638"/>
<source>call_transfer_current_call_title</source>
<extracomment>&quot;Transférer %1 à&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="645"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="657"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="651"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="663"/>
<source>call_transfer_confirm_dialog_tittle</source>
<extracomment>&quot;Confirmer le transfert&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="647"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="658"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="653"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="664"/>
<source>call_transfer_confirm_dialog_message</source>
<extracomment>&quot;Vous allez transférer %1 à %2.&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="688"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="694"/>
<source>call_action_start_new_call</source>
<extracomment>&quot;Nouvel appel&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="728"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1417"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="734"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1425"/>
<source>call_action_show_dialer</source>
<extracomment>&quot;Pavé numérique&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="766"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="772"/>
<source>call_action_change_layout</source>
<extracomment>&quot;Modifier la disposition&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="782"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="788"/>
<source>call_action_go_to_calls_list</source>
<extracomment>&quot;Liste d&apos;appel&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="801"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="807"/>
<source>Merger tous les appels</source>
<extracomment>call_action_merge_calls</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="838"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1492"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="844"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1500"/>
<source>call_action_go_to_settings</source>
<extracomment>&quot;Paramètres&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="859"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="865"/>
<source>conference_action_screen_sharing</source>
<extracomment>&quot;Partage de votre écran&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="910"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="916"/>
<source>conference_share_link_title</source>
<extracomment>Partager le lien de la réunion</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="914"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="920"/>
<source>copied</source>
<extracomment>Copié</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="916"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="922"/>
<source>information_popup_meeting_address_copied_to_clipboard</source>
<extracomment>Le lien de la réunion a é copié dans le presse-papier</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="924"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="929"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="930"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="935"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="941"/>
<source>conference_participants_list_title</source>
<extracomment>&quot;Participants (%1)&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="955"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="963"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="961"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="969"/>
<source>group_call_participant_selected</source>
<translation type="unfinished">
<numerusform></numerusform>
</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="962"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="968"/>
<source>meeting_schedule_add_participants_title</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="978"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="985"/>
<source>call_encryption_title</source>
<extracomment>Chiffrement</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="988"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="996"/>
<source>call_stats_title</source>
<extracomment>Statistiques</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1106"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1114"/>
<source>call_action_end_call</source>
<extracomment>&quot;Terminer l&apos;appel&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1137"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1145"/>
<source>call_action_resume_call</source>
<extracomment>&quot;Reprendre l&apos;appel&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1139"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1147"/>
<source>call_action_pause_call</source>
<extracomment>&quot;Mettre l&apos;appel en pause&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1170"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1178"/>
<source>call_action_transfer_call</source>
<extracomment>&quot;Transférer l&apos;appel&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1197"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1205"/>
<source>call_action_start_new_call_hint</source>
<extracomment>&quot;Initier un nouvel appel&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1224"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1232"/>
<source>call_display_call_list_hint</source>
<extracomment>&quot;Afficher la liste d&apos;appels&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1264"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1272"/>
<source>call_deactivate_video_hint</source>
<extracomment>&quot;Désactiver la vidéo&quot; &quot;Activer la vidéo&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1264"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1272"/>
<source>call_activate_video_hint</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1277"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1285"/>
<source>call_activate_microphone</source>
<extracomment>&quot;Activer le micro&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1279"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1287"/>
<source>call_deactivate_microphone</source>
<extracomment>&quot;Désactiver le micro&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1294"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1302"/>
<source>call_share_screen_hint</source>
<extracomment>Partager l&apos;écran</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1313"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1321"/>
<source>call_rise_hand_hint</source>
<extracomment>&quot;Lever la main&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1323"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1331"/>
<source>call_send_reaction_hint</source>
<extracomment>&quot;Envoyer une réaction&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1332"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1340"/>
<source>call_manage_participants_hint</source>
<extracomment>&quot;Gérer les participants&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1356"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1364"/>
<source>call_more_options_hint</source>
<extracomment>&quot;Plus d&apos;options&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1385"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1393"/>
<source>call_action_change_conference_layout</source>
<extracomment>&quot;Modifier la disposition&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1397"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1405"/>
<source>call_action_full_screen</source>
<extracomment>&quot;Mode Plein écran&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1446"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1454"/>
<source>call_action_stop_recording</source>
<extracomment>&quot;Terminer l&apos;enregistrement&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1448"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1456"/>
<source>call_action_record</source>
<extracomment>&quot;Enregistrer l&apos;appel&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1474"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1482"/>
<source>call_activate_speaker_hint</source>
<extracomment>&quot;Activer le son&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1476"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1484"/>
<source>call_deactivate_speaker_hint</source>
<extracomment>&quot;Désactiver le son&quot;</extracomment>
<translation type="unfinished"></translation>
@ -1603,6 +1603,63 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ChatPage</name>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="14"/>
<source>chat_start_title</source>
<extracomment>&quot;Nouvelle conversation&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="16"/>
<source>chat_empty_title</source>
<extracomment>&quot;Aucune conversation&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="49"/>
<source>chat_dialog_delete_chat_title</source>
<extracomment>Supprimer la conversation ?</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="51"/>
<source>chat_dialog_delete_chat_message</source>
<extracomment>&quot;La conversation et tous ses messages seront supprimés.&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="80"/>
<source>chat_list_title</source>
<extracomment>&quot;Conversations&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="110"/>
<source>chat_search_in_history</source>
<extracomment>&quot;Rechercher une conversation&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="133"/>
<source>list_filter_no_result_found</source>
<extracomment>&quot;Aucun résultat&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="135"/>
<source>chat_list_empty_history</source>
<extracomment>&quot;Aucune conversation dans votre historique&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="203"/>
<source>chat_action_start_new_chat</source>
<extracomment>&quot;New chat&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CliModel</name>
<message>
@ -1742,38 +1799,38 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="204"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="212"/>
<source>contact_editor_first_name</source>
<extracomment>&quot;Prénom&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="220"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="228"/>
<source>contact_editor_last_name</source>
<extracomment>&quot;Nom&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="233"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="241"/>
<source>contact_editor_company</source>
<extracomment>&quot;Entreprise&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="246"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="254"/>
<source>contact_editor_job_title</source>
<extracomment>&quot;Fonction&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="296"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="324"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="304"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="332"/>
<source>sip_address</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="390"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="412"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="398"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="420"/>
<source>phone</source>
<extracomment>&quot;Téléphone&quot;</extracomment>
<translation type="unfinished"></translation>
@ -1782,53 +1839,53 @@
<context>
<name>ContactListItem</name>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="191"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="188"/>
<source>contact_details_remove_from_favourites</source>
<extracomment>&quot;Enlever des favoris&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="193"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="190"/>
<source>contact_details_add_to_favourites</source>
<extracomment>&quot;Ajouter aux favoris&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="207"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="204"/>
<source>Partager</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="219"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="216"/>
<source>information_popup_error_title</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="221"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="218"/>
<source>information_popup_vcard_creation_error</source>
<extracomment>La création du fichier vcard a échoué</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="225"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="222"/>
<source>information_popup_vcard_creation_title</source>
<extracomment>VCard créée</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="227"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="224"/>
<source>information_popup_vcard_creation_success</source>
<extracomment>&quot;VCard du contact enregistrée dans %1&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="229"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="226"/>
<source>contact_sharing_email_title</source>
<extracomment>Partage de contact</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="235"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="232"/>
<source>contact_details_delete</source>
<extracomment>&quot;Supprimer&quot;</extracomment>
<translation type="unfinished"></translation>
@ -2385,9 +2442,9 @@
<message>
<location filename="../../core/friend/FriendCore.cpp" line="31"/>
<location filename="../../core/friend/FriendCore.cpp" line="68"/>
<location filename="../../core/friend/FriendCore.cpp" line="189"/>
<location filename="../../core/friend/FriendCore.cpp" line="423"/>
<location filename="../../core/friend/FriendCore.cpp" line="613"/>
<location filename="../../core/friend/FriendCore.cpp" line="191"/>
<location filename="../../core/friend/FriendCore.cpp" line="426"/>
<location filename="../../core/friend/FriendCore.cpp" line="616"/>
<source>sip_address</source>
<extracomment>&quot;Adresse SIP&quot;</extracomment>
<translation type="unfinished"></translation>
@ -2395,18 +2452,18 @@
<message>
<location filename="../../core/friend/FriendCore.cpp" line="33"/>
<location filename="../../core/friend/FriendCore.cpp" line="76"/>
<location filename="../../core/friend/FriendCore.cpp" line="197"/>
<location filename="../../core/friend/FriendCore.cpp" line="199"/>
<source>device_id</source>
<extracomment>&quot;Téléphone&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/friend/FriendCore.cpp" line="419"/>
<location filename="../../core/friend/FriendCore.cpp" line="422"/>
<source>information_popup_error_title</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../core/friend/FriendCore.cpp" line="421"/>
<location filename="../../core/friend/FriendCore.cpp" line="424"/>
<source>information_popup_invalid_address_message</source>
<extracomment>&quot;Adresse invalide&quot;</extracomment>
<translation type="unfinished"></translation>
@ -3275,13 +3332,13 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="70"/>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="71"/>
<source>search_bar_look_for_contact_text</source>
<extracomment>&quot;Rechercher un contact&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="105"/>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="106"/>
<source>call_start_group_call_title</source>
<translation type="unfinished"></translation>
</message>
@ -3321,97 +3378,97 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="113"/>
<location filename="../../model/auth/OIDCModel.cpp" line="117"/>
<source>oidc_authentication_granted_message</source>
<extracomment>Authentication granted</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="120"/>
<location filename="../../model/auth/OIDCModel.cpp" line="124"/>
<source>oidc_authentication_not_authenticated_message</source>
<extracomment>Not authenticated</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="126"/>
<location filename="../../model/auth/OIDCModel.cpp" line="130"/>
<source>oidc_authentication_refresh_message</source>
<extracomment>Refreshing token</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="131"/>
<location filename="../../model/auth/OIDCModel.cpp" line="135"/>
<source>oidc_authentication_temporary_credentials_message</source>
<extracomment>Temporary credentials received</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="149"/>
<location filename="../../model/auth/OIDCModel.cpp" line="153"/>
<source>oidc_authentication_network_error</source>
<extracomment>Network error</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="153"/>
<location filename="../../model/auth/OIDCModel.cpp" line="157"/>
<source>oidc_authentication_server_error</source>
<extracomment>Server error</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="157"/>
<location filename="../../model/auth/OIDCModel.cpp" line="161"/>
<source>oidc_authentication_token_not_found_error</source>
<extracomment>OAuth token not found</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="161"/>
<location filename="../../model/auth/OIDCModel.cpp" line="165"/>
<source>oidc_authentication_token_secret_not_found_error</source>
<extracomment>OAuth token secret not found</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="165"/>
<location filename="../../model/auth/OIDCModel.cpp" line="169"/>
<source>oidc_authentication_callback_not_verified_error</source>
<extracomment>OAuth callback not verified</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="176"/>
<location filename="../../model/auth/OIDCModel.cpp" line="180"/>
<source>oidc_authentication_request_auth_message</source>
<extracomment>Requesting authorization from browser</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="196"/>
<location filename="../../model/auth/OIDCModel.cpp" line="200"/>
<source>oidc_authentication_request_token_message</source>
<extracomment>Requesting access token</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="201"/>
<location filename="../../model/auth/OIDCModel.cpp" line="205"/>
<source>oidc_authentication_refresh_token_message</source>
<extracomment>Refreshing access token</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="206"/>
<location filename="../../model/auth/OIDCModel.cpp" line="210"/>
<source>oidc_authentication_request_authorization_message</source>
<extracomment>Requesting authorization</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="211"/>
<location filename="../../model/auth/OIDCModel.cpp" line="215"/>
<source>oidc_authentication_request_temporary_credentials_message</source>
<extracomment>Requesting temporary credentials</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="238"/>
<location filename="../../model/auth/OIDCModel.cpp" line="242"/>
<source>oidc_authentication_no_auth_found_in_config_error</source>
<extracomment>No authorization endpoint found in OpenID configuration</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="249"/>
<location filename="../../model/auth/OIDCModel.cpp" line="253"/>
<source>oidc_authentication_no_token_found_in_config_error</source>
<extracomment>No token endpoint found in OpenID configuration</extracomment>
<translation type="unfinished"></translation>
@ -4145,25 +4202,25 @@ Pour les activer dans un projet commercial, merci de nous contacter.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="220"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="221"/>
<source>call_dialog_zrtp_validate_trust_letters_do_not_match_text</source>
<extracomment>&quot;Le code fourni ne correspond pas.&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="231"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="232"/>
<source>call_dialog_zrtp_security_alert_message</source>
<extracomment>&quot;La confidentialité de votre appel peut être compromise !&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="244"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="245"/>
<source>call_dialog_zrtp_validate_trust_letters_do_not_match</source>
<extracomment>&quot;Aucune correspondance&quot;</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="261"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="262"/>
<source>call_action_hang_up</source>
<extracomment>&quot;Raccrocher&quot;</extracomment>
<translation type="unfinished"></translation>

View file

@ -513,74 +513,74 @@
<context>
<name>App</name>
<message>
<location filename="../../core/App.cpp" line="325"/>
<location filename="../../core/App.cpp" line="326"/>
<source>remote_provisioning_dialog</source>
<extracomment>Voulez-vous télécharger et appliquer la configuration depuis cette adresse ?</extracomment>
<translation>Do you want to download and apply remote provisioning from this address ?</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="776"/>
<location filename="../../core/App.cpp" line="780"/>
<source>application_description</source>
<extracomment>&quot;A free and open source SIP video-phone.&quot;</extracomment>
<translation>A free and open source SIP video-phone.</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="778"/>
<location filename="../../core/App.cpp" line="782"/>
<source>command_line_arg_order</source>
<extracomment>&quot;Send an order to the application towards a command line&quot;</extracomment>
<translation>Send an order to the application towards a command line</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="781"/>
<location filename="../../core/App.cpp" line="785"/>
<source>command_line_option_show_help</source>
<translation>Show this help</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="786"/>
<location filename="../../core/App.cpp" line="790"/>
<source>command_line_option_show_app_version</source>
<translation>Show app version</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="792"/>
<location filename="../../core/App.cpp" line="796"/>
<source>command_line_option_config_to_fetch</source>
<extracomment>&quot;Specify the linphone configuration file to be fetched. It will be merged with the current configuration.&quot;</extracomment>
<translation>Specify the linphone configuration file to be fetched. It will be merged with the current configuration.</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="795"/>
<location filename="../../core/App.cpp" line="799"/>
<source>command_line_option_config_to_fetch_arg</source>
<extracomment>&quot;URL, path or file&quot;</extracomment>
<translation>URL, path or file</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="799"/>
<location filename="../../core/App.cpp" line="803"/>
<source>command_line_option_minimized</source>
<translation>Minimize</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="802"/>
<location filename="../../core/App.cpp" line="806"/>
<source>command_line_option_log_to_stdout</source>
<translation>Log to stdout some debug information while running</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="805"/>
<location filename="../../core/App.cpp" line="809"/>
<source>command_line_option_print_app_logs_only</source>
<extracomment>&quot;Print only logs from the application&quot;</extracomment>
<translation>Print only logs from the application</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="1165"/>
<location filename="../../core/App.cpp" line="1169"/>
<source>hide_action</source>
<extracomment>&quot;Cacher&quot; &quot;Afficher&quot;</extracomment>
<translation>Hide</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="1165"/>
<location filename="../../core/App.cpp" line="1169"/>
<source>show_action</source>
<translation>Show</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="1180"/>
<location filename="../../core/App.cpp" line="1184"/>
<source>quit_action</source>
<extracomment>&quot;Quitter&quot;</extracomment>
<translation>Quit</translation>
@ -915,7 +915,7 @@
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="168"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="594"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="588"/>
<source>menu_delete_history</source>
<extracomment>&quot;Supprimer l&apos;historique&quot;</extracomment>
<translation>Delete history</translation>
@ -980,7 +980,7 @@
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="456"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="460"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="570"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="564"/>
<source>information_popup_error_title</source>
<translation>Error</translation>
</message>
@ -997,55 +997,55 @@
<translation>You are not connected</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="532"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="526"/>
<source>menu_see_existing_contact</source>
<extracomment>&quot;Voir le contact&quot;</extracomment>
<extracomment>&quot;Show contact&quot;</extracomment>
<translation>Show contact</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="534"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="528"/>
<source>menu_add_address_to_contacts</source>
<extracomment>&quot;Ajouter aux contacts&quot;</extracomment>
<extracomment>&quot;Add to contacts&quot;</extracomment>
<translation>Add to contacts</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="552"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="546"/>
<source>menu_copy_sip_address</source>
<extracomment>&quot;Copier l&apos;adresse SIP&quot;</extracomment>
<translation>Copy SIP address</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="564"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="558"/>
<source>sip_address_copied_to_clipboard_toast</source>
<extracomment>Adresse copiée</extracomment>
<translation>SIP address copied</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="566"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="560"/>
<source>sip_address_copied_to_clipboard_message</source>
<extracomment>L&apos;adresse a é copié dans le presse_papiers</extracomment>
<translation>The address has been copied to the clipboard</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="572"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="566"/>
<source>sip_address_copy_to_clipboard_error</source>
<extracomment>&quot;Erreur lors de la copie de l&apos;adresse&quot;</extracomment>
<translation>Error copying address</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="670"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="664"/>
<source>notification_missed_call_title</source>
<extracomment>&quot;Appel manqué&quot;</extracomment>
<translation>Missed call</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="673"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="667"/>
<source>call_outgoing</source>
<extracomment>&quot;Appel sortant&quot;</extracomment>
<translation>Outgoing call</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="675"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="669"/>
<source>call_audio_incoming</source>
<extracomment>&quot;Appel entrant&quot;</extracomment>
<translation>Incoming call</translation>
@ -1235,133 +1235,133 @@
<translation>Call paused by remote</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="567"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="573"/>
<source>conference_user_is_recording</source>
<extracomment>&quot;Vous enregistrez la réunion&quot;</extracomment>
<translation>You are recording the meeting</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="569"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="575"/>
<source>call_user_is_recording</source>
<extracomment>&quot;Vous enregistrez l&apos;appel&quot;</extracomment>
<translation>You are recording the call</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="572"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="578"/>
<source>conference_remote_is_recording</source>
<extracomment>&quot;Un participant enregistre la réunion&quot;</extracomment>
<translation>Someone is recording the meeting</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="574"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="580"/>
<source>call_remote_recording</source>
<extracomment>&quot;%1 enregistre l&apos;appel&quot;</extracomment>
<translation>%1 records the call</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="582"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="588"/>
<source>call_stop_recording</source>
<extracomment>&quot;Arrêter l&apos;enregistrement&quot;</extracomment>
<translation>Stop recording</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="615"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="621"/>
<source>add</source>
<translation>Add</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="632"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="638"/>
<source>call_transfer_current_call_title</source>
<extracomment>&quot;Transférer %1 à&quot;</extracomment>
<translation>Transfer %1 to</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="645"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="657"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="651"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="663"/>
<source>call_transfer_confirm_dialog_tittle</source>
<extracomment>&quot;Confirmer le transfert&quot;</extracomment>
<translation>Confirm transfer</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="647"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="658"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="653"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="664"/>
<source>call_transfer_confirm_dialog_message</source>
<extracomment>&quot;Vous allez transférer %1 à %2.&quot;</extracomment>
<translation>You are going to transfer %1 to %2.</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="688"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="694"/>
<source>call_action_start_new_call</source>
<extracomment>&quot;Nouvel appel&quot;</extracomment>
<translation>New call</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="728"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1417"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="734"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1425"/>
<source>call_action_show_dialer</source>
<extracomment>&quot;Pavé numérique&quot;</extracomment>
<translation>Dialer</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="766"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="772"/>
<source>call_action_change_layout</source>
<extracomment>&quot;Modifier la disposition&quot;</extracomment>
<translation>Change layout</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="782"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="788"/>
<source>call_action_go_to_calls_list</source>
<extracomment>&quot;Liste d&apos;appel&quot;</extracomment>
<translation>Call list</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="801"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="807"/>
<source>Merger tous les appels</source>
<extracomment>call_action_merge_calls</extracomment>
<translation>Merge all calls</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="838"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1492"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="844"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1500"/>
<source>call_action_go_to_settings</source>
<extracomment>&quot;Paramètres&quot;</extracomment>
<translation>Settings</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="859"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="865"/>
<source>conference_action_screen_sharing</source>
<extracomment>&quot;Partage de votre écran&quot;</extracomment>
<translation>Share your screen</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="910"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="916"/>
<source>conference_share_link_title</source>
<extracomment>Partager le lien de la réunion</extracomment>
<translation>Share meeting link</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="914"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="920"/>
<source>copied</source>
<extracomment>Copié</extracomment>
<translation>Copied</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="916"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="922"/>
<source>information_popup_meeting_address_copied_to_clipboard</source>
<extracomment>Le lien de la réunion a é copié dans le presse-papier</extracomment>
<translation>Meeting link has been copied to the clipboard</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="924"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="929"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="930"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="935"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="941"/>
<source>conference_participants_list_title</source>
<extracomment>&quot;Participants (%1)&quot;</extracomment>
<translation>Participants (%1)</translation>
</message>
<message numerus="yes">
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="955"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="963"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="961"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="969"/>
<source>group_call_participant_selected</source>
<translation>
<numerusform>1 selected participant</numerusform>
@ -1369,143 +1369,143 @@
</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="962"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="968"/>
<source>meeting_schedule_add_participants_title</source>
<translation>Add participants</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="978"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="985"/>
<source>call_encryption_title</source>
<extracomment>Chiffrement</extracomment>
<translation>Encryption</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="988"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="996"/>
<source>call_stats_title</source>
<extracomment>Statistiques</extracomment>
<translation>Statistics</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1106"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1114"/>
<source>call_action_end_call</source>
<extracomment>&quot;Terminer l&apos;appel&quot;</extracomment>
<translation>End call</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1137"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1145"/>
<source>call_action_resume_call</source>
<extracomment>&quot;Reprendre l&apos;appel&quot;</extracomment>
<translation>Resume call</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1139"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1147"/>
<source>call_action_pause_call</source>
<extracomment>&quot;Mettre l&apos;appel en pause&quot;</extracomment>
<translation>Pause call</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1170"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1178"/>
<source>call_action_transfer_call</source>
<extracomment>&quot;Transférer l&apos;appel&quot;</extracomment>
<translation>Transfer call</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1197"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1205"/>
<source>call_action_start_new_call_hint</source>
<extracomment>&quot;Initier un nouvel appel&quot;</extracomment>
<translation>Start new call</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1224"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1232"/>
<source>call_display_call_list_hint</source>
<extracomment>&quot;Afficher la liste d&apos;appels&quot;</extracomment>
<translation>View call list</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1264"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1272"/>
<source>call_deactivate_video_hint</source>
<extracomment>&quot;Désactiver la vidéo&quot; &quot;Activer la vidéo&quot;</extracomment>
<translation>Turn off video</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1264"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1272"/>
<source>call_activate_video_hint</source>
<translation>Enable video</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1277"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1285"/>
<source>call_activate_microphone</source>
<extracomment>&quot;Activer le micro&quot;</extracomment>
<translation>Activate microphone</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1279"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1287"/>
<source>call_deactivate_microphone</source>
<extracomment>&quot;Désactiver le micro&quot;</extracomment>
<translation>Mute microphone</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1294"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1302"/>
<source>call_share_screen_hint</source>
<extracomment>Partager l&apos;écran</extracomment>
<translation>Share screen</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1313"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1321"/>
<source>call_rise_hand_hint</source>
<extracomment>&quot;Lever la main&quot;</extracomment>
<translation>Rise hand</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1323"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1331"/>
<source>call_send_reaction_hint</source>
<extracomment>&quot;Envoyer une réaction&quot;</extracomment>
<translation>Send reaction</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1332"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1340"/>
<source>call_manage_participants_hint</source>
<extracomment>&quot;Gérer les participants&quot;</extracomment>
<translation>Manage participants</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1356"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1364"/>
<source>call_more_options_hint</source>
<extracomment>&quot;Plus d&apos;options&quot;</extracomment>
<translation>More options</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1385"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1393"/>
<source>call_action_change_conference_layout</source>
<extracomment>&quot;Modifier la disposition&quot;</extracomment>
<translation>Change layout</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1397"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1405"/>
<source>call_action_full_screen</source>
<extracomment>&quot;Mode Plein écran&quot;</extracomment>
<translation>Full screen mode</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1446"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1454"/>
<source>call_action_stop_recording</source>
<extracomment>&quot;Terminer l&apos;enregistrement&quot;</extracomment>
<translation>End recording</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1448"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1456"/>
<source>call_action_record</source>
<extracomment>&quot;Enregistrer l&apos;appel&quot;</extracomment>
<translation>Record call</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1474"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1482"/>
<source>call_activate_speaker_hint</source>
<extracomment>&quot;Activer le son&quot;</extracomment>
<translation>Activate speaker</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1476"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1484"/>
<source>call_deactivate_speaker_hint</source>
<extracomment>&quot;Désactiver le son&quot;</extracomment>
<translation>Mute speaker</translation>
@ -1617,6 +1617,63 @@
<translation>Audio only</translation>
</message>
</context>
<context>
<name>ChatPage</name>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="14"/>
<source>chat_start_title</source>
<extracomment>&quot;Nouvelle conversation&quot;</extracomment>
<translation>New conversation</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="16"/>
<source>chat_empty_title</source>
<extracomment>&quot;Aucune conversation&quot;</extracomment>
<translation>No conversation</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="49"/>
<source>chat_dialog_delete_chat_title</source>
<extracomment>Supprimer la conversation ?</extracomment>
<translation>Delete chat ?</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="51"/>
<source>chat_dialog_delete_chat_message</source>
<extracomment>&quot;La conversation et tous ses messages seront supprimés.&quot;</extracomment>
<translation>This chat and all its messages will be deleted.</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="80"/>
<source>chat_list_title</source>
<extracomment>&quot;Conversations&quot;</extracomment>
<translation>Chat</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="110"/>
<source>chat_search_in_history</source>
<extracomment>&quot;Rechercher une conversation&quot;</extracomment>
<translation>Search for a chat</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="133"/>
<source>list_filter_no_result_found</source>
<extracomment>&quot;Aucun résultat&quot;</extracomment>
<translation>No result</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="135"/>
<source>chat_list_empty_history</source>
<extracomment>&quot;Aucune conversation dans votre historique&quot;</extracomment>
<translation>No chat in history</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="203"/>
<source>chat_action_start_new_chat</source>
<extracomment>&quot;New chat&quot;</extracomment>
<translation>New chat</translation>
</message>
</context>
<context>
<name>CliModel</name>
<message>
@ -1756,38 +1813,38 @@
<translation>Delete</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="204"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="212"/>
<source>contact_editor_first_name</source>
<extracomment>&quot;Prénom&quot;</extracomment>
<translation>First name</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="220"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="228"/>
<source>contact_editor_last_name</source>
<extracomment>&quot;Nom&quot;</extracomment>
<translation>Last name</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="233"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="241"/>
<source>contact_editor_company</source>
<extracomment>&quot;Entreprise&quot;</extracomment>
<translation>Company</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="246"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="254"/>
<source>contact_editor_job_title</source>
<extracomment>&quot;Fonction&quot;</extracomment>
<translation>Job</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="296"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="324"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="304"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="332"/>
<source>sip_address</source>
<translation>SIP address</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="390"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="412"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="398"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="420"/>
<source>phone</source>
<extracomment>&quot;Téléphone&quot;</extracomment>
<translation>Phone</translation>
@ -1796,53 +1853,53 @@
<context>
<name>ContactListItem</name>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="191"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="188"/>
<source>contact_details_remove_from_favourites</source>
<extracomment>&quot;Enlever des favoris&quot;</extracomment>
<translation>Remove from favorites</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="193"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="190"/>
<source>contact_details_add_to_favourites</source>
<extracomment>&quot;Ajouter aux favoris&quot;</extracomment>
<translation>Add to favorites</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="207"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="204"/>
<source>Partager</source>
<translation>Share</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="219"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="216"/>
<source>information_popup_error_title</source>
<translation>Error</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="221"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="218"/>
<source>information_popup_vcard_creation_error</source>
<extracomment>La création du fichier vcard a échoué</extracomment>
<translation>VCard creation failed</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="225"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="222"/>
<source>information_popup_vcard_creation_title</source>
<extracomment>VCard créée</extracomment>
<translation>VCard created</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="227"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="224"/>
<source>information_popup_vcard_creation_success</source>
<extracomment>&quot;VCard du contact enregistrée dans %1&quot;</extracomment>
<translation>VCard has been saved in %1</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="229"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="226"/>
<source>contact_sharing_email_title</source>
<extracomment>Partage de contact</extracomment>
<translation>Share contact</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="235"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="232"/>
<source>contact_details_delete</source>
<extracomment>&quot;Supprimer&quot;</extracomment>
<translation>Delete</translation>
@ -2404,9 +2461,9 @@
<message>
<location filename="../../core/friend/FriendCore.cpp" line="31"/>
<location filename="../../core/friend/FriendCore.cpp" line="68"/>
<location filename="../../core/friend/FriendCore.cpp" line="189"/>
<location filename="../../core/friend/FriendCore.cpp" line="423"/>
<location filename="../../core/friend/FriendCore.cpp" line="613"/>
<location filename="../../core/friend/FriendCore.cpp" line="191"/>
<location filename="../../core/friend/FriendCore.cpp" line="426"/>
<location filename="../../core/friend/FriendCore.cpp" line="616"/>
<source>sip_address</source>
<extracomment>&quot;Adresse SIP&quot;</extracomment>
<translation>SIP address</translation>
@ -2414,18 +2471,18 @@
<message>
<location filename="../../core/friend/FriendCore.cpp" line="33"/>
<location filename="../../core/friend/FriendCore.cpp" line="76"/>
<location filename="../../core/friend/FriendCore.cpp" line="197"/>
<location filename="../../core/friend/FriendCore.cpp" line="199"/>
<source>device_id</source>
<extracomment>&quot;Téléphone&quot;</extracomment>
<translation>Phone</translation>
</message>
<message>
<location filename="../../core/friend/FriendCore.cpp" line="419"/>
<location filename="../../core/friend/FriendCore.cpp" line="422"/>
<source>information_popup_error_title</source>
<translation>Error</translation>
</message>
<message>
<location filename="../../core/friend/FriendCore.cpp" line="421"/>
<location filename="../../core/friend/FriendCore.cpp" line="424"/>
<source>information_popup_invalid_address_message</source>
<extracomment>&quot;Adresse invalide&quot;</extracomment>
<translation>Invalid address</translation>
@ -3303,13 +3360,13 @@
<translation>Ongoing call</translation>
</message>
<message>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="70"/>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="71"/>
<source>search_bar_look_for_contact_text</source>
<extracomment>&quot;Rechercher un contact&quot;</extracomment>
<translation>Find contact</translation>
</message>
<message>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="105"/>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="106"/>
<source>call_start_group_call_title</source>
<translation>Group call</translation>
</message>
@ -3349,97 +3406,97 @@
<translation>Timeout: Not authenticated</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="113"/>
<location filename="../../model/auth/OIDCModel.cpp" line="117"/>
<source>oidc_authentication_granted_message</source>
<extracomment>Authentication granted</extracomment>
<translation>Authentication granted</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="120"/>
<location filename="../../model/auth/OIDCModel.cpp" line="124"/>
<source>oidc_authentication_not_authenticated_message</source>
<extracomment>Not authenticated</extracomment>
<translation>Not authenticated</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="126"/>
<location filename="../../model/auth/OIDCModel.cpp" line="130"/>
<source>oidc_authentication_refresh_message</source>
<extracomment>Refreshing token</extracomment>
<translation>Refreshing token</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="131"/>
<location filename="../../model/auth/OIDCModel.cpp" line="135"/>
<source>oidc_authentication_temporary_credentials_message</source>
<extracomment>Temporary credentials received</extracomment>
<translation>Temporary credentials received</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="149"/>
<location filename="../../model/auth/OIDCModel.cpp" line="153"/>
<source>oidc_authentication_network_error</source>
<extracomment>Network error</extracomment>
<translation>Network error</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="153"/>
<location filename="../../model/auth/OIDCModel.cpp" line="157"/>
<source>oidc_authentication_server_error</source>
<extracomment>Server error</extracomment>
<translation>Server error</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="157"/>
<location filename="../../model/auth/OIDCModel.cpp" line="161"/>
<source>oidc_authentication_token_not_found_error</source>
<extracomment>OAuth token not found</extracomment>
<translation>OAuth token not found</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="161"/>
<location filename="../../model/auth/OIDCModel.cpp" line="165"/>
<source>oidc_authentication_token_secret_not_found_error</source>
<extracomment>OAuth token secret not found</extracomment>
<translation>OAuth token secret not found</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="165"/>
<location filename="../../model/auth/OIDCModel.cpp" line="169"/>
<source>oidc_authentication_callback_not_verified_error</source>
<extracomment>OAuth callback not verified</extracomment>
<translation>OAuth callback not verified</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="176"/>
<location filename="../../model/auth/OIDCModel.cpp" line="180"/>
<source>oidc_authentication_request_auth_message</source>
<extracomment>Requesting authorization from browser</extracomment>
<translation>Requesting authorization from browser</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="196"/>
<location filename="../../model/auth/OIDCModel.cpp" line="200"/>
<source>oidc_authentication_request_token_message</source>
<extracomment>Requesting access token</extracomment>
<translation>Requesting access token</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="201"/>
<location filename="../../model/auth/OIDCModel.cpp" line="205"/>
<source>oidc_authentication_refresh_token_message</source>
<extracomment>Refreshing access token</extracomment>
<translation>Refreshing access token</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="206"/>
<location filename="../../model/auth/OIDCModel.cpp" line="210"/>
<source>oidc_authentication_request_authorization_message</source>
<extracomment>Requesting authorization</extracomment>
<translation>Requesting authorization</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="211"/>
<location filename="../../model/auth/OIDCModel.cpp" line="215"/>
<source>oidc_authentication_request_temporary_credentials_message</source>
<extracomment>Requesting temporary credentials</extracomment>
<translation>Requesting temporary credentials</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="238"/>
<location filename="../../model/auth/OIDCModel.cpp" line="242"/>
<source>oidc_authentication_no_auth_found_in_config_error</source>
<extracomment>No authorization endpoint found in OpenID configuration</extracomment>
<translation>No authorization endpoint found in OpenID configuration</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="249"/>
<location filename="../../model/auth/OIDCModel.cpp" line="253"/>
<source>oidc_authentication_no_token_found_in_config_error</source>
<extracomment>No token endpoint found in OpenID configuration</extracomment>
<translation>No token endpoint found in OpenID configuration</translation>
@ -4181,25 +4238,25 @@ To enable them in a commercial project, please contact us.</translation>
<translation>Corresponding code :</translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="220"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="221"/>
<source>call_dialog_zrtp_validate_trust_letters_do_not_match_text</source>
<extracomment>&quot;Le code fourni ne correspond pas.&quot;</extracomment>
<translation>The provided code does not match.</translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="231"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="232"/>
<source>call_dialog_zrtp_security_alert_message</source>
<extracomment>&quot;La confidentialité de votre appel peut être compromise !&quot;</extracomment>
<translation>The confidentiality of your call may be compromised!</translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="244"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="245"/>
<source>call_dialog_zrtp_validate_trust_letters_do_not_match</source>
<extracomment>&quot;Aucune correspondance&quot;</extracomment>
<translation>No match</translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="261"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="262"/>
<source>call_action_hang_up</source>
<extracomment>&quot;Raccrocher&quot;</extracomment>
<translation>Hang up</translation>

View file

@ -513,74 +513,74 @@
<context>
<name>App</name>
<message>
<location filename="../../core/App.cpp" line="325"/>
<location filename="../../core/App.cpp" line="326"/>
<source>remote_provisioning_dialog</source>
<extracomment>Voulez-vous télécharger et appliquer la configuration depuis cette adresse ?</extracomment>
<translation>Voulez-vous télécharger et appliquer la configuration depuis cette adresse ?</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="776"/>
<location filename="../../core/App.cpp" line="780"/>
<source>application_description</source>
<extracomment>&quot;A free and open source SIP video-phone.&quot;</extracomment>
<translation>A free and open source SIP video-phone.</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="778"/>
<location filename="../../core/App.cpp" line="782"/>
<source>command_line_arg_order</source>
<extracomment>&quot;Send an order to the application towards a command line&quot;</extracomment>
<translation>Send an order to the application towards a command line</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="781"/>
<location filename="../../core/App.cpp" line="785"/>
<source>command_line_option_show_help</source>
<translation>Show this help</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="786"/>
<location filename="../../core/App.cpp" line="790"/>
<source>command_line_option_show_app_version</source>
<translation>Afficher la version de l&apos;application</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="792"/>
<location filename="../../core/App.cpp" line="796"/>
<source>command_line_option_config_to_fetch</source>
<extracomment>&quot;Specify the linphone configuration file to be fetched. It will be merged with the current configuration.&quot;</extracomment>
<translation>Specify the linphone configuration file to be fetched. It will be merged with the current configuration.</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="795"/>
<location filename="../../core/App.cpp" line="799"/>
<source>command_line_option_config_to_fetch_arg</source>
<extracomment>&quot;URL, path or file&quot;</extracomment>
<translation>URL, path or file</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="799"/>
<location filename="../../core/App.cpp" line="803"/>
<source>command_line_option_minimized</source>
<translation>Minimiser</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="802"/>
<location filename="../../core/App.cpp" line="806"/>
<source>command_line_option_log_to_stdout</source>
<translation>Log to stdout some debug information while running</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="805"/>
<location filename="../../core/App.cpp" line="809"/>
<source>command_line_option_print_app_logs_only</source>
<extracomment>&quot;Print only logs from the application&quot;</extracomment>
<translation>Print only logs from the application</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="1165"/>
<location filename="../../core/App.cpp" line="1169"/>
<source>hide_action</source>
<extracomment>&quot;Cacher&quot; &quot;Afficher&quot;</extracomment>
<translation>Cacher</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="1165"/>
<location filename="../../core/App.cpp" line="1169"/>
<source>show_action</source>
<translation>Afficher</translation>
</message>
<message>
<location filename="../../core/App.cpp" line="1180"/>
<location filename="../../core/App.cpp" line="1184"/>
<source>quit_action</source>
<extracomment>&quot;Quitter&quot;</extracomment>
<translation>Quitter</translation>
@ -915,7 +915,7 @@
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="168"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="594"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="588"/>
<source>menu_delete_history</source>
<extracomment>&quot;Supprimer l&apos;historique&quot;</extracomment>
<translation>Supprimer l&apos;historique</translation>
@ -980,7 +980,7 @@
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="456"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="460"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="570"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="564"/>
<source>information_popup_error_title</source>
<translation>Erreur</translation>
</message>
@ -997,55 +997,55 @@
<translation>Vous n&apos;etes pas connecté</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="532"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="526"/>
<source>menu_see_existing_contact</source>
<extracomment>&quot;Voir le contact&quot;</extracomment>
<extracomment>&quot;Show contact&quot;</extracomment>
<translation>Voir le contact</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="534"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="528"/>
<source>menu_add_address_to_contacts</source>
<extracomment>&quot;Ajouter aux contacts&quot;</extracomment>
<extracomment>&quot;Add to contacts&quot;</extracomment>
<translation>Ajouter aux contacts</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="552"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="546"/>
<source>menu_copy_sip_address</source>
<extracomment>&quot;Copier l&apos;adresse SIP&quot;</extracomment>
<translation>Copier l&apos;adresse SIP</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="564"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="558"/>
<source>sip_address_copied_to_clipboard_toast</source>
<extracomment>Adresse copiée</extracomment>
<translation>Adresse copiée</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="566"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="560"/>
<source>sip_address_copied_to_clipboard_message</source>
<extracomment>L&apos;adresse a é copié dans le presse_papiers</extracomment>
<translation>L&apos;adresse a é copié dans le presse-papiers</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="572"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="566"/>
<source>sip_address_copy_to_clipboard_error</source>
<extracomment>&quot;Erreur lors de la copie de l&apos;adresse&quot;</extracomment>
<translation>Erreur lors de la copie de l&apos;adresse</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="670"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="664"/>
<source>notification_missed_call_title</source>
<extracomment>&quot;Appel manqué&quot;</extracomment>
<translation>Appel manqué</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="673"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="667"/>
<source>call_outgoing</source>
<extracomment>&quot;Appel sortant&quot;</extracomment>
<translation>Appel sortant</translation>
</message>
<message>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="675"/>
<location filename="../../view/Page/Main/Call/CallPage.qml" line="669"/>
<source>call_audio_incoming</source>
<extracomment>&quot;Appel entrant&quot;</extracomment>
<translation>Appel entrant</translation>
@ -1235,133 +1235,133 @@
<translation>Appel mis en pause par votre correspondant</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="567"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="573"/>
<source>conference_user_is_recording</source>
<extracomment>&quot;Vous enregistrez la réunion&quot;</extracomment>
<translation>Vous enregistrez la réunion</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="569"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="575"/>
<source>call_user_is_recording</source>
<extracomment>&quot;Vous enregistrez l&apos;appel&quot;</extracomment>
<translation>Vous enregistrez l&apos;appel</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="572"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="578"/>
<source>conference_remote_is_recording</source>
<extracomment>&quot;Un participant enregistre la réunion&quot;</extracomment>
<translation>Un participant enregistre la réunion</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="574"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="580"/>
<source>call_remote_recording</source>
<extracomment>&quot;%1 enregistre l&apos;appel&quot;</extracomment>
<translation>%1 enregistre l&apos;appel</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="582"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="588"/>
<source>call_stop_recording</source>
<extracomment>&quot;Arrêter l&apos;enregistrement&quot;</extracomment>
<translation>Arrêter l&apos;enregistrement</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="615"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="621"/>
<source>add</source>
<translation>Ajouter</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="632"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="638"/>
<source>call_transfer_current_call_title</source>
<extracomment>&quot;Transférer %1 à&quot;</extracomment>
<translation>Transférer %1 à</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="645"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="657"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="651"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="663"/>
<source>call_transfer_confirm_dialog_tittle</source>
<extracomment>&quot;Confirmer le transfert&quot;</extracomment>
<translation>Confirmer le transfert</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="647"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="658"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="653"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="664"/>
<source>call_transfer_confirm_dialog_message</source>
<extracomment>&quot;Vous allez transférer %1 à %2.&quot;</extracomment>
<translation>Vous allez transférer %1 à %2.</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="688"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="694"/>
<source>call_action_start_new_call</source>
<extracomment>&quot;Nouvel appel&quot;</extracomment>
<translation>Nouvel appel</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="728"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1417"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="734"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1425"/>
<source>call_action_show_dialer</source>
<extracomment>&quot;Pavé numérique&quot;</extracomment>
<translation>Pavé numérique</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="766"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="772"/>
<source>call_action_change_layout</source>
<extracomment>&quot;Modifier la disposition&quot;</extracomment>
<translation>Modifier la disposition</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="782"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="788"/>
<source>call_action_go_to_calls_list</source>
<extracomment>&quot;Liste d&apos;appel&quot;</extracomment>
<translation>Liste d&apos;appel</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="801"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="807"/>
<source>Merger tous les appels</source>
<extracomment>call_action_merge_calls</extracomment>
<translation>Merger tous les appels</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="838"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1492"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="844"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1500"/>
<source>call_action_go_to_settings</source>
<extracomment>&quot;Paramètres&quot;</extracomment>
<translation>Paramètres</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="859"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="865"/>
<source>conference_action_screen_sharing</source>
<extracomment>&quot;Partage de votre écran&quot;</extracomment>
<translation>Partage de votre écran</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="910"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="916"/>
<source>conference_share_link_title</source>
<extracomment>Partager le lien de la réunion</extracomment>
<translation>Partager le lien de la réunion</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="914"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="920"/>
<source>copied</source>
<extracomment>Copié</extracomment>
<translation>Copié</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="916"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="922"/>
<source>information_popup_meeting_address_copied_to_clipboard</source>
<extracomment>Le lien de la réunion a é copié dans le presse-papier</extracomment>
<translation>Le lien de la réunion a é copié dans le presse-papier</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="924"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="929"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="930"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="935"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="941"/>
<source>conference_participants_list_title</source>
<extracomment>&quot;Participants (%1)&quot;</extracomment>
<translation>Participants (%1)</translation>
</message>
<message numerus="yes">
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="955"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="963"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="961"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="969"/>
<source>group_call_participant_selected</source>
<translation>
<numerusform>un participant sélectionné</numerusform>
@ -1369,143 +1369,143 @@
</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="962"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="968"/>
<source>meeting_schedule_add_participants_title</source>
<translation>Ajouter des participants</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="978"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="985"/>
<source>call_encryption_title</source>
<extracomment>Chiffrement</extracomment>
<translation>Chiffrement</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="988"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="996"/>
<source>call_stats_title</source>
<extracomment>Statistiques</extracomment>
<translation>Statistiques</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1106"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1114"/>
<source>call_action_end_call</source>
<extracomment>&quot;Terminer l&apos;appel&quot;</extracomment>
<translation>Terminer l&apos;appel</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1137"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1145"/>
<source>call_action_resume_call</source>
<extracomment>&quot;Reprendre l&apos;appel&quot;</extracomment>
<translation>Reprendre l&apos;appel</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1139"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1147"/>
<source>call_action_pause_call</source>
<extracomment>&quot;Mettre l&apos;appel en pause&quot;</extracomment>
<translation>Mettre l&apos;appel en pause</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1170"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1178"/>
<source>call_action_transfer_call</source>
<extracomment>&quot;Transférer l&apos;appel&quot;</extracomment>
<translation>Transférer l&apos;appel</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1197"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1205"/>
<source>call_action_start_new_call_hint</source>
<extracomment>&quot;Initier un nouvel appel&quot;</extracomment>
<translation>Initier un nouvel appel</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1224"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1232"/>
<source>call_display_call_list_hint</source>
<extracomment>&quot;Afficher la liste d&apos;appels&quot;</extracomment>
<translation>Afficher la liste d&apos;appels</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1264"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1272"/>
<source>call_deactivate_video_hint</source>
<extracomment>&quot;Désactiver la vidéo&quot; &quot;Activer la vidéo&quot;</extracomment>
<translation>Désactiver la vidéo</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1264"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1272"/>
<source>call_activate_video_hint</source>
<translation>Activer la vidéo</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1277"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1285"/>
<source>call_activate_microphone</source>
<extracomment>&quot;Activer le micro&quot;</extracomment>
<translation>Activer le micro</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1279"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1287"/>
<source>call_deactivate_microphone</source>
<extracomment>&quot;Désactiver le micro&quot;</extracomment>
<translation>Désactiver le micro</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1294"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1302"/>
<source>call_share_screen_hint</source>
<extracomment>Partager l&apos;écran</extracomment>
<translation>Partager l&apos;écran</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1313"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1321"/>
<source>call_rise_hand_hint</source>
<extracomment>&quot;Lever la main&quot;</extracomment>
<translation>Lever la main</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1323"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1331"/>
<source>call_send_reaction_hint</source>
<extracomment>&quot;Envoyer une réaction&quot;</extracomment>
<translation>Envoyer une réaction</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1332"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1340"/>
<source>call_manage_participants_hint</source>
<extracomment>&quot;Gérer les participants&quot;</extracomment>
<translation>Gérer les participants</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1356"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1364"/>
<source>call_more_options_hint</source>
<extracomment>&quot;Plus d&apos;options&quot;</extracomment>
<translation>Plus d&apos;options</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1385"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1393"/>
<source>call_action_change_conference_layout</source>
<extracomment>&quot;Modifier la disposition&quot;</extracomment>
<translation>Modifier la disposition</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1397"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1405"/>
<source>call_action_full_screen</source>
<extracomment>&quot;Mode Plein écran&quot;</extracomment>
<translation>Mode Plein écran</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1446"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1454"/>
<source>call_action_stop_recording</source>
<extracomment>&quot;Terminer l&apos;enregistrement&quot;</extracomment>
<translation>Terminer l&apos;enregistrement</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1448"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1456"/>
<source>call_action_record</source>
<extracomment>&quot;Enregistrer l&apos;appel&quot;</extracomment>
<translation>Enregistrer l&apos;appel</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1474"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1482"/>
<source>call_activate_speaker_hint</source>
<extracomment>&quot;Activer le son&quot;</extracomment>
<translation>Activer le son</translation>
</message>
<message>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1476"/>
<location filename="../../view/Page/Window/Call/CallsWindow.qml" line="1484"/>
<source>call_deactivate_speaker_hint</source>
<extracomment>&quot;Désactiver le son&quot;</extracomment>
<translation>Désactiver le son</translation>
@ -1617,6 +1617,63 @@
<translation>Audio uniquement</translation>
</message>
</context>
<context>
<name>ChatPage</name>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="14"/>
<source>chat_start_title</source>
<extracomment>&quot;Nouvelle conversation&quot;</extracomment>
<translation>Nouvelle conversation</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="16"/>
<source>chat_empty_title</source>
<extracomment>&quot;Aucune conversation&quot;</extracomment>
<translation>Aucune conversation</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="49"/>
<source>chat_dialog_delete_chat_title</source>
<extracomment>Supprimer la conversation ?</extracomment>
<translation>Supprimer la conversation ?</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="51"/>
<source>chat_dialog_delete_chat_message</source>
<extracomment>&quot;La conversation et tous ses messages seront supprimés.&quot;</extracomment>
<translation>La conversation et tous ses messages seront supprimés.</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="80"/>
<source>chat_list_title</source>
<extracomment>&quot;Conversations&quot;</extracomment>
<translation>Conversations</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="110"/>
<source>chat_search_in_history</source>
<extracomment>&quot;Rechercher une conversation&quot;</extracomment>
<translation>Rechercher une conversation</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="133"/>
<source>list_filter_no_result_found</source>
<extracomment>&quot;Aucun résultat&quot;</extracomment>
<translation>Aucun résultat</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="135"/>
<source>chat_list_empty_history</source>
<extracomment>&quot;Aucune conversation dans votre historique&quot;</extracomment>
<translation>Aucune conversation dans votre historique</translation>
</message>
<message>
<location filename="../../view/Page/Main/Chat/ChatPage.qml" line="203"/>
<source>chat_action_start_new_chat</source>
<extracomment>&quot;New chat&quot;</extracomment>
<translation>Nouvelle conversation</translation>
</message>
</context>
<context>
<name>CliModel</name>
<message>
@ -1756,38 +1813,38 @@
<translation>Supprimer</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="204"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="212"/>
<source>contact_editor_first_name</source>
<extracomment>&quot;Prénom&quot;</extracomment>
<translation>Prénom</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="220"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="228"/>
<source>contact_editor_last_name</source>
<extracomment>&quot;Nom&quot;</extracomment>
<translation>Nom</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="233"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="241"/>
<source>contact_editor_company</source>
<extracomment>&quot;Entreprise&quot;</extracomment>
<translation>Entreprise</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="246"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="254"/>
<source>contact_editor_job_title</source>
<extracomment>&quot;Fonction&quot;</extracomment>
<translation>Fonction</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="296"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="324"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="304"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="332"/>
<source>sip_address</source>
<translation>Adresse SIP</translation>
</message>
<message>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="390"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="412"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="398"/>
<location filename="../../view/Page/Form/Contact/ContactEdition.qml" line="420"/>
<source>phone</source>
<extracomment>&quot;Téléphone&quot;</extracomment>
<translation>Téléphone</translation>
@ -1796,53 +1853,53 @@
<context>
<name>ContactListItem</name>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="191"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="188"/>
<source>contact_details_remove_from_favourites</source>
<extracomment>&quot;Enlever des favoris&quot;</extracomment>
<translation>Enlever des favoris</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="193"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="190"/>
<source>contact_details_add_to_favourites</source>
<extracomment>&quot;Ajouter aux favoris&quot;</extracomment>
<translation>Ajouter aux favoris</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="207"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="204"/>
<source>Partager</source>
<translation>Partager</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="219"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="216"/>
<source>information_popup_error_title</source>
<translation>Erreur</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="221"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="218"/>
<source>information_popup_vcard_creation_error</source>
<extracomment>La création du fichier vcard a échoué</extracomment>
<translation>La création du fichier vcard a échoué</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="225"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="222"/>
<source>information_popup_vcard_creation_title</source>
<extracomment>VCard créée</extracomment>
<translation>VCard créée</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="227"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="224"/>
<source>information_popup_vcard_creation_success</source>
<extracomment>&quot;VCard du contact enregistrée dans %1&quot;</extracomment>
<translation>VCard du contact enregistrée dans %1</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="229"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="226"/>
<source>contact_sharing_email_title</source>
<extracomment>Partage de contact</extracomment>
<translation>Partage de contact</translation>
</message>
<message>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="235"/>
<location filename="../../view/Control/Display/Contact/ContactListItem.qml" line="232"/>
<source>contact_details_delete</source>
<extracomment>&quot;Supprimer&quot;</extracomment>
<translation>Supprimer</translation>
@ -2399,9 +2456,9 @@
<message>
<location filename="../../core/friend/FriendCore.cpp" line="31"/>
<location filename="../../core/friend/FriendCore.cpp" line="68"/>
<location filename="../../core/friend/FriendCore.cpp" line="189"/>
<location filename="../../core/friend/FriendCore.cpp" line="423"/>
<location filename="../../core/friend/FriendCore.cpp" line="613"/>
<location filename="../../core/friend/FriendCore.cpp" line="191"/>
<location filename="../../core/friend/FriendCore.cpp" line="426"/>
<location filename="../../core/friend/FriendCore.cpp" line="616"/>
<source>sip_address</source>
<extracomment>&quot;Adresse SIP&quot;</extracomment>
<translation>Adresse SIP</translation>
@ -2409,18 +2466,18 @@
<message>
<location filename="../../core/friend/FriendCore.cpp" line="33"/>
<location filename="../../core/friend/FriendCore.cpp" line="76"/>
<location filename="../../core/friend/FriendCore.cpp" line="197"/>
<location filename="../../core/friend/FriendCore.cpp" line="199"/>
<source>device_id</source>
<extracomment>&quot;Téléphone&quot;</extracomment>
<translation>Téléphone</translation>
</message>
<message>
<location filename="../../core/friend/FriendCore.cpp" line="419"/>
<location filename="../../core/friend/FriendCore.cpp" line="422"/>
<source>information_popup_error_title</source>
<translation>Erreur</translation>
</message>
<message>
<location filename="../../core/friend/FriendCore.cpp" line="421"/>
<location filename="../../core/friend/FriendCore.cpp" line="424"/>
<source>information_popup_invalid_address_message</source>
<extracomment>&quot;Adresse invalide&quot;</extracomment>
<translation>Adresse invalide</translation>
@ -3313,13 +3370,13 @@
<translation>Appels en cours</translation>
</message>
<message>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="70"/>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="71"/>
<source>search_bar_look_for_contact_text</source>
<extracomment>&quot;Rechercher un contact&quot;</extracomment>
<translation>Rechercher un contact</translation>
</message>
<message>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="105"/>
<location filename="../../view/Page/Form/Call/NewCallForm.qml" line="106"/>
<source>call_start_group_call_title</source>
<translation>Appel de groupe</translation>
</message>
@ -3359,97 +3416,97 @@
<translation>Timeout : non authentifié</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="113"/>
<location filename="../../model/auth/OIDCModel.cpp" line="117"/>
<source>oidc_authentication_granted_message</source>
<extracomment>Authentication granted</extracomment>
<translation>Authentification accordée</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="120"/>
<location filename="../../model/auth/OIDCModel.cpp" line="124"/>
<source>oidc_authentication_not_authenticated_message</source>
<extracomment>Not authenticated</extracomment>
<translation>Non authentifié</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="126"/>
<location filename="../../model/auth/OIDCModel.cpp" line="130"/>
<source>oidc_authentication_refresh_message</source>
<extracomment>Refreshing token</extracomment>
<translation>Token en cours de rafraîchissement</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="131"/>
<location filename="../../model/auth/OIDCModel.cpp" line="135"/>
<source>oidc_authentication_temporary_credentials_message</source>
<extracomment>Temporary credentials received</extracomment>
<translation>Identifiants temporaires reçus</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="149"/>
<location filename="../../model/auth/OIDCModel.cpp" line="153"/>
<source>oidc_authentication_network_error</source>
<extracomment>Network error</extracomment>
<translation>Erreur réseau</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="153"/>
<location filename="../../model/auth/OIDCModel.cpp" line="157"/>
<source>oidc_authentication_server_error</source>
<extracomment>Server error</extracomment>
<translation>Erreur de serveur</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="157"/>
<location filename="../../model/auth/OIDCModel.cpp" line="161"/>
<source>oidc_authentication_token_not_found_error</source>
<extracomment>OAuth token not found</extracomment>
<translation>Token OAuth non trouvé</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="161"/>
<location filename="../../model/auth/OIDCModel.cpp" line="165"/>
<source>oidc_authentication_token_secret_not_found_error</source>
<extracomment>OAuth token secret not found</extracomment>
<translation>Token OAuth secret non trouvé</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="165"/>
<location filename="../../model/auth/OIDCModel.cpp" line="169"/>
<source>oidc_authentication_callback_not_verified_error</source>
<extracomment>OAuth callback not verified</extracomment>
<translation>Retour OAuth non vérifié</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="176"/>
<location filename="../../model/auth/OIDCModel.cpp" line="180"/>
<source>oidc_authentication_request_auth_message</source>
<extracomment>Requesting authorization from browser</extracomment>
<translation>En attente d&apos;autorisation du navigateur</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="196"/>
<location filename="../../model/auth/OIDCModel.cpp" line="200"/>
<source>oidc_authentication_request_token_message</source>
<extracomment>Requesting access token</extracomment>
<translation>En attente du token d&apos;accès</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="201"/>
<location filename="../../model/auth/OIDCModel.cpp" line="205"/>
<source>oidc_authentication_refresh_token_message</source>
<extracomment>Refreshing access token</extracomment>
<translation>Token en cours de rafraîchissement</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="206"/>
<location filename="../../model/auth/OIDCModel.cpp" line="210"/>
<source>oidc_authentication_request_authorization_message</source>
<extracomment>Requesting authorization</extracomment>
<translation>Autorisation en cours</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="211"/>
<location filename="../../model/auth/OIDCModel.cpp" line="215"/>
<source>oidc_authentication_request_temporary_credentials_message</source>
<extracomment>Requesting temporary credentials</extracomment>
<translation>En attente d&apos;identifiants temporaires</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="238"/>
<location filename="../../model/auth/OIDCModel.cpp" line="242"/>
<source>oidc_authentication_no_auth_found_in_config_error</source>
<extracomment>No authorization endpoint found in OpenID configuration</extracomment>
<translation>Pas d&apos;autorisation trouvé dans la configuration OpenID</translation>
</message>
<message>
<location filename="../../model/auth/OIDCModel.cpp" line="249"/>
<location filename="../../model/auth/OIDCModel.cpp" line="253"/>
<source>oidc_authentication_no_token_found_in_config_error</source>
<extracomment>No token endpoint found in OpenID configuration</extracomment>
<translation>Pas de token trouvé dans la configuration OpenID</translation>
@ -4191,25 +4248,25 @@ Pour les activer dans un projet commercial, merci de nous contacter.</translatio
<translation>Code correspondant :</translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="220"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="221"/>
<source>call_dialog_zrtp_validate_trust_letters_do_not_match_text</source>
<extracomment>&quot;Le code fourni ne correspond pas.&quot;</extracomment>
<translation>Le code fourni ne correspond pas.</translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="231"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="232"/>
<source>call_dialog_zrtp_security_alert_message</source>
<extracomment>&quot;La confidentialité de votre appel peut être compromise !&quot;</extracomment>
<translation>La confidentialité de votre appel peut être compromise !</translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="244"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="245"/>
<source>call_dialog_zrtp_validate_trust_letters_do_not_match</source>
<extracomment>&quot;Aucune correspondance&quot;</extracomment>
<translation>Aucune correspondance</translation>
</message>
<message>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="261"/>
<location filename="../../view/Control/Popup/Dialog/ZrtpAuthenticationDialog.qml" line="262"/>
<source>call_action_hang_up</source>
<extracomment>&quot;Raccrocher&quot;</extracomment>
<translation>Raccrocher</translation>

View file

@ -11,6 +11,9 @@ list(APPEND _LINPHONEAPP_SOURCES
model/call-history/CallHistoryModel.cpp
model/chat/ChatModel.cpp
model/chat/message/ChatMessageModel.cpp
model/conference/ConferenceInfoModel.cpp
model/conference/ConferenceModel.cpp
model/conference/ConferenceSchedulerModel.cpp

View file

@ -0,0 +1,262 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatModel.hpp"
#include <QDebug>
#include "core/path/Paths.hpp"
#include "model/core/CoreModel.hpp"
#include "model/setting/SettingsModel.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(ChatModel)
ChatModel::ChatModel(const std::shared_ptr<linphone::ChatRoom> &chatroom, QObject *parent)
: ::Listener<linphone::ChatRoom, linphone::ChatRoomListener>(chatroom, parent) {
lDebug() << "[ChatModel] new" << this << " / SDKModel=" << chatroom.get();
mustBeInLinphoneThread(getClassName());
}
ChatModel::~ChatModel() {
mustBeInLinphoneThread("~" + getClassName());
}
QDateTime ChatModel::getLastUpdateTime() {
// TODO : vérifier unité
return QDateTime::fromSecsSinceEpoch(mMonitor->getLastUpdateTime());
}
std::list<std::shared_ptr<linphone::ChatMessage>> ChatModel::getHistory() const {
auto history = mMonitor->getHistory(0, (int)linphone::ChatRoom::HistoryFilter::ChatMessage);
std::list<std::shared_ptr<linphone::ChatMessage>> res;
for (auto &eventLog : history) {
if (!eventLog->getChatMessage()) res.push_back(eventLog->getChatMessage());
}
return res;
}
QString ChatModel::getTitle() {
if (mMonitor->hasCapability((int)linphone::ChatRoom::Capabilities::Basic)) {
return ToolModel::getDisplayName(mMonitor->getPeerAddress()->clone());
} else {
if (mMonitor->hasCapability((int)linphone::ChatRoom::Capabilities::OneToOne)) {
auto peer = mMonitor->getParticipants().front();
return peer ? ToolModel::getDisplayName(peer->getAddress()->clone()) : "Chat";
} else if (mMonitor->hasCapability((int)linphone::ChatRoom::Capabilities::Conference)) {
return Utils::coreStringToAppString(mMonitor->getSubject());
}
}
}
QString ChatModel::getPeerAddress() const {
return Utils::coreStringToAppString(mMonitor->getPeerAddress()->asStringUriOnly());
}
QString ChatModel::getLastMessageInHistory(std::list<std::shared_ptr<linphone::Content>> startList) const {
if (startList.empty()) {
auto lastMessage = mMonitor->getLastMessageInHistory();
if (lastMessage) startList = lastMessage->getContents();
}
for (auto &content : startList) {
if (content->isText()) {
return Utils::coreStringToAppString(content->getUtf8Text());
} else if (content->isFile()) {
return Utils::coreStringToAppString(content->getName());
} else if (content->isIcalendar()) {
return QString("Invitation à une réunion");
} else if (content->isMultipart()) {
return getLastMessageInHistory(content->getParts());
}
}
return QString("");
}
int ChatModel::getUnreadMessagesCount() const {
return mMonitor->getUnreadMessagesCount();
}
//---------------------------------------------------------------//
void ChatModel::onIsComposingReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &remoteAddress,
bool isComposing) {
emit isComposingReceived(chatRoom, remoteAddress, isComposing);
}
// Do not use this api, only manipulate EventLogs
void ChatModel::onMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message) {
// emit messageReceived(chatRoom, message);
}
void ChatModel::onMessagesReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::ChatMessage>> &chatMessages) {
// emit messagesReceived(chatRoom, chatMessages);
}
void ChatModel::onNewEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit newEvent(chatRoom, eventLog);
}
void ChatModel::onNewEvents(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventLogs) {
emit newEvents(chatRoom, eventLogs);
}
void ChatModel::onChatMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit chatMessageReceived(chatRoom, eventLog);
}
void ChatModel::onChatMessagesReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventLogs) {
emit chatMessagesReceived(chatRoom, eventLogs);
}
void ChatModel::onChatMessageSending(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit chatMessageSending(chatRoom, eventLog);
}
void ChatModel::onChatMessageSent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit chatMessageSent(chatRoom, eventLog);
}
void ChatModel::onParticipantAdded(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit participantAdded(chatRoom, eventLog);
}
void ChatModel::onParticipantRemoved(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit participantRemoved(chatRoom, eventLog);
}
void ChatModel::onParticipantAdminStatusChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit participantAdminStatusChanged(chatRoom, eventLog);
}
void ChatModel::onStateChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
linphone::ChatRoom::State newState) {
emit stateChanged(chatRoom, newState);
}
void ChatModel::onSecurityEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit securityEvent(chatRoom, eventLog);
}
void ChatModel::onSubjectChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit subjectChanged(chatRoom, eventLog);
}
void ChatModel::onUndecryptableMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message) {
emit undecryptableMessageReceived(chatRoom, message);
}
void ChatModel::onParticipantDeviceAdded(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit participantDeviceAdded(chatRoom, eventLog);
}
void ChatModel::onParticipantDeviceRemoved(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit participantDeviceRemoved(chatRoom, eventLog);
}
void ChatModel::onParticipantDeviceStateChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog,
linphone::ParticipantDevice::State state) {
emit participantDeviceStateChanged(chatRoom, eventLog, state);
}
void ChatModel::onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit participantDeviceMediaAvailabilityChanged(chatRoom, eventLog);
}
void ChatModel::onConferenceJoined(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit conferenceJoined(chatRoom, eventLog);
}
void ChatModel::onConferenceLeft(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit conferenceLeft(chatRoom, eventLog);
}
void ChatModel::onEphemeralEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit ephemeralEvent(chatRoom, eventLog);
}
void ChatModel::onEphemeralMessageTimerStarted(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit ephemeralMessageTimerStarted(chatRoom, eventLog);
}
void ChatModel::onEphemeralMessageDeleted(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) {
emit onEphemeralMessageDeleted(chatRoom, eventLog);
}
void ChatModel::onConferenceAddressGeneration(const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
emit conferenceAddressGeneration(chatRoom);
}
void ChatModel::onParticipantRegistrationSubscriptionRequested(
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &participantAddress) {
emit participantRegistrationSubscriptionRequested(chatRoom, participantAddress);
}
void ChatModel::onParticipantRegistrationUnsubscriptionRequested(
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &participantAddress) {
emit participantRegistrationUnsubscriptionRequested(chatRoom, participantAddress);
}
void ChatModel::onChatMessageShouldBeStored(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message) {
emit chatMessageShouldBeStored(chatRoom, message);
}
void ChatModel::onChatMessageParticipantImdnStateChanged(
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ParticipantImdnState> &state) {
emit chatMessageParticipantImdnStateChanged(chatRoom, message, state);
}
void ChatModel::onChatRoomRead(const std::shared_ptr<linphone::ChatRoom> &chatRoom) {
emit chatRoomRead(chatRoom);
}
void ChatModel::onNewMessageReaction(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ChatMessageReaction> &reaction) {
emit onNewMessageReaction(chatRoom, message, reaction);
}

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_MODEL_H_
#define CHAT_MODEL_H_
#include "model/listener/Listener.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/LinphoneEnums.hpp"
#include <QObject>
#include <QTimer>
#include <linphone++/linphone.hh>
class ChatModel : public ::Listener<linphone::ChatRoom, linphone::ChatRoomListener>,
public linphone::ChatRoomListener,
public AbstractObject {
Q_OBJECT
public:
ChatModel(const std::shared_ptr<linphone::ChatRoom> &chatRoom, QObject *parent = nullptr);
~ChatModel();
QDateTime getLastUpdateTime();
QString getTitle();
QString getPeerAddress() const;
QString getLastMessageInHistory(std::list<std::shared_ptr<linphone::Content>> startList = {}) const;
int getUnreadMessagesCount() const;
std::list<std::shared_ptr<linphone::ChatMessage>> getHistory() const;
private:
DECLARE_ABSTRACT_OBJECT
//--------------------------------------------------------------------------------
// LINPHONE
//--------------------------------------------------------------------------------
virtual void onIsComposingReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &remoteAddress,
bool isComposing) override;
virtual void onMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message) override;
virtual void onMessagesReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::ChatMessage>> &chatMessages) override;
virtual void onNewEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onNewEvents(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventLogs) override;
virtual void onChatMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onChatMessagesReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventLogs) override;
virtual void onChatMessageSending(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onChatMessageSent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onParticipantAdded(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onParticipantRemoved(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onParticipantAdminStatusChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onStateChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
linphone::ChatRoom::State newState) override;
virtual void onSecurityEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onSubjectChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onUndecryptableMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message) override;
virtual void onParticipantDeviceAdded(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onParticipantDeviceRemoved(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onParticipantDeviceStateChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog,
linphone::ParticipantDevice::State state) override;
virtual void
onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onConferenceJoined(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onConferenceLeft(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onEphemeralEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onEphemeralMessageTimerStarted(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onEphemeralMessageDeleted(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog) override;
virtual void onConferenceAddressGeneration(const std::shared_ptr<linphone::ChatRoom> &chatRoom) override;
virtual void onParticipantRegistrationSubscriptionRequested(
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &participantAddress) override;
virtual void onParticipantRegistrationUnsubscriptionRequested(
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &participantAddress) override;
virtual void onChatMessageShouldBeStored(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message) override;
virtual void onChatMessageParticipantImdnStateChanged(
const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ParticipantImdnState> &state) override;
virtual void onChatRoomRead(const std::shared_ptr<linphone::ChatRoom> &chatRoom) override;
virtual void onNewMessageReaction(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ChatMessageReaction> &reaction) override;
signals:
void isComposingReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &remoteAddress,
bool isComposing);
void messageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message);
void messagesReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::ChatMessage>> &chatMessages);
void newEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void newEvents(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventLogs);
void chatMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void chatMessagesReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::list<std::shared_ptr<linphone::EventLog>> &eventLogs);
void chatMessageSending(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void chatMessageSent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void participantAdded(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void participantRemoved(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void participantAdminStatusChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void stateChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom, linphone::ChatRoom::State newState);
void securityEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void subjectChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void undecryptableMessageReceived(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message);
void participantDeviceAdded(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void participantDeviceRemoved(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void participantDeviceStateChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog,
linphone::ParticipantDevice::State state);
void participantDeviceMediaAvailabilityChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void conferenceJoined(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void conferenceLeft(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void ephemeralEvent(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void ephemeralMessageTimerStarted(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void ephemeralMessageDeleted(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::EventLog> &eventLog);
void conferenceAddressGeneration(const std::shared_ptr<linphone::ChatRoom> &chatRoom);
void
participantRegistrationSubscriptionRequested(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &participantAddress);
void
participantRegistrationUnsubscriptionRequested(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<const linphone::Address> &participantAddress);
void chatMessageShouldBeStored(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message);
void chatMessageParticipantImdnStateChanged(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ParticipantImdnState> &state);
void chatRoomRead(const std::shared_ptr<linphone::ChatRoom> &chatRoom);
void newMessageReaction(const std::shared_ptr<linphone::ChatRoom> &chatRoom,
const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<const linphone::ChatMessageReaction> &reaction);
};
#endif

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ChatMessageModel.hpp"
#include <QDebug>
#include "core/path/Paths.hpp"
#include "model/core/CoreModel.hpp"
#include "model/setting/SettingsModel.hpp"
#include "model/tool/ToolModel.hpp"
#include "tool/Utils.hpp"
DEFINE_ABSTRACT_OBJECT(ChatMessageModel)
ChatMessageModel::ChatMessageModel(const std::shared_ptr<linphone::ChatMessage> &chatMessage, QObject *parent)
: ::Listener<linphone::ChatMessage, linphone::ChatMessageListener>(chatMessage, parent) {
lDebug() << "[ChatMessageModel] new" << this << " / SDKModel=" << chatMessage.get();
mustBeInLinphoneThread(getClassName());
}
ChatMessageModel::~ChatMessageModel() {
mustBeInLinphoneThread("~" + getClassName());
}
QString ChatMessageModel::getText() const {
return ToolModel::getMessageFromContent(mMonitor->getContents());
}
QDateTime ChatMessageModel::getTimestamp() const {
return QDateTime::fromSecsSinceEpoch(mMonitor->getTime());
}
std::shared_ptr<linphone::Buffer>
ChatMessageModel::onFileTransferSend(const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<linphone::Content> &content,
size_t offset,
size_t size) {
return nullptr;
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CHAT_MESSAGE_MODEL_H_
#define CHAT_MESSAGE_MODEL_H_
#include "model/listener/Listener.hpp"
#include "tool/AbstractObject.hpp"
#include "tool/LinphoneEnums.hpp"
#include <QObject>
#include <QTimer>
#include <linphone++/linphone.hh>
class ChatMessageModel : public ::Listener<linphone::ChatMessage, linphone::ChatMessageListener>,
public linphone::ChatMessageListener,
public AbstractObject {
Q_OBJECT
public:
ChatMessageModel(const std::shared_ptr<linphone::ChatMessage> &chatMessage, QObject *parent = nullptr);
~ChatMessageModel();
QString getText() const;
QDateTime getTimestamp() const;
private:
DECLARE_ABSTRACT_OBJECT
virtual std::shared_ptr<linphone::Buffer> onFileTransferSend(const std::shared_ptr<linphone::ChatMessage> &message,
const std::shared_ptr<linphone::Content> &content,
size_t offset,
size_t size) override;
};
#endif

View file

@ -736,7 +736,7 @@ void SettingsModel::notifyConfigReady(){
DEFINE_NOTIFY_CONFIG_READY(disableCommandLine, DisableCommandLine)
}
DEFINE_GETSET_CONFIG(SettingsModel, bool, Bool, disableChatFeature, DisableChatFeature, "disable_chat_feature", true)
DEFINE_GETSET_CONFIG(SettingsModel, bool, Bool, disableChatFeature, DisableChatFeature, "disable_chat_feature", false)
DEFINE_GETSET_CONFIG(
SettingsModel, bool, Bool, disableMeetingsFeature, DisableMeetingsFeature, "disable_meetings_feature", false)
DEFINE_GETSET_CONFIG(SettingsModel,

View file

@ -384,6 +384,22 @@ bool ToolModel::friendIsInFriendList(const std::shared_ptr<linphone::FriendList>
return (it != friends.end());
}
QString ToolModel::getMessageFromContent(std::list<std::shared_ptr<linphone::Content>> contents) {
for (auto &content : contents) {
if (content->isText()) {
return Utils::coreStringToAppString(content->getUtf8Text());
} else if (content->isFile()) {
return Utils::coreStringToAppString(content->getName());
} else if (content->isIcalendar()) {
return QString("Invitation à une réunion");
} else if (content->isMultipart()) {
return getMessageFromContent(content->getParts());
}
}
return QString("");
}
// Load downloaded codecs like OpenH264 (needs to be after core is created and has loaded its plugins, as
// reloadMsPlugins modifies plugin path for the factory)
void ToolModel::loadDownloadedCodecs() {

View file

@ -69,6 +69,9 @@ public:
static bool friendIsInFriendList(const std::shared_ptr<linphone::FriendList> &friendList,
const std::shared_ptr<linphone::Friend> &f);
static QString getMessageFromContent(std::list<std::shared_ptr<linphone::Content>> contents);
static void loadDownloadedCodecs();
static void updateCodecs();

View file

@ -318,7 +318,7 @@ QString Utils::formatElapsedTime(int seconds, bool dotsSeparator) {
else return (h == 0 ? "" : hours + "h ") + (m == 0 ? "" : min + "min ") + sec + "s";
}
QString Utils::formatDate(const QDateTime &date, bool includeTime, QString format) {
QString Utils::formatDate(const QDateTime &date, bool includeTime, bool includeDateIfToday, QString format) {
QString dateDay;
//: "Aujourd'hui"
if (date.date() == QDate::currentDate()) dateDay = tr("today");
@ -333,6 +333,8 @@ QString Utils::formatDate(const QDateTime &date, bool includeTime, QString forma
if (!includeTime) return dateDay;
auto time = date.time().toString("hh:mm");
if (!includeDateIfToday && date.date() == QDate::currentDate()) return time;
return dateDay + " | " + time;
}

View file

@ -89,7 +89,7 @@ public:
Q_INVOKABLE static QString formatElapsedTime(int seconds,
bool dotsSeparator = true); // Return the elapsed time formated
Q_INVOKABLE static QString
formatDate(const QDateTime &date, bool includeTime = true, QString format = ""); // Return the date formated
formatDate(const QDateTime &date, bool includeTime = true, bool includeDateIfToday = true, QString format = ""); // Return the date formated
Q_INVOKABLE static QString formatDateElapsedTime(const QDateTime &date);
Q_INVOKABLE static QString formatTime(const QDateTime &date); // Return the time formated
Q_INVOKABLE static QStringList generateSecurityLettersArray(int arraySize, int correctIndex, QString correctCode);

View file

@ -48,6 +48,9 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Control/Display/Call/CallListView.qml
view/Control/Display/Call/CallHistoryListView.qml
view/Control/Display/Call/CallStatistics.qml
view/Control/Display/Chat/ChatListView.qml
view/Control/Display/Chat/ChatMessage.qml
view/Control/Display/Chat/ChatMessagesListView.qml
view/Control/Display/Contact/Avatar.qml
view/Control/Display/Contact/Contact.qml
view/Control/Display/Contact/ContactListItem.qml
@ -98,6 +101,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Control/Tool/Prototype/PhoneNumberPrototype.qml
view/Page/Form/Call/NewCallForm.qml
view/Page/Form/Chat/SelectedChatView.qml
view/Page/Form/Contact/ContactDescription.qml
view/Page/Form/Contact/ContactEdition.qml
view/Page/Form/Login/LoginPage.qml
@ -132,6 +136,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/Page/Main/Call/CallPage.qml
view/Page/Main/Call/CallSettingsPanel.qml
view/Page/Main/Call/WaitingRoom.qml
view/Page/Main/Chat/ChatPage.qml
view/Page/Main/Contact/ContactPage.qml
view/Page/Main/Help/HelpPage.qml
view/Page/Main/Meeting/MeetingPage.qml

View file

@ -8,10 +8,10 @@ Button {
id: mainItem
textSize: Typography.b1.pixelSize
textWeight: Typography.b1.weight
leftPadding: Math.round(20.04 * DefaultStyle.dp)
rightPadding: Math.round(20.04 * DefaultStyle.dp)
topPadding: Math.round(11.2 * DefaultStyle.dp)
bottomPadding: Math.round(11.2 * DefaultStyle.dp)
icon.width: Math.round(24.89 * DefaultStyle.dp)
icon.height: Math.round(24.89 * DefaultStyle.dp)
leftPadding: Math.round(20 * DefaultStyle.dp)
rightPadding: Math.round(20 * DefaultStyle.dp)
topPadding: Math.round(11 * DefaultStyle.dp)
bottomPadding: Math.round(11 * DefaultStyle.dp)
icon.width: Math.round(24 * DefaultStyle.dp)
icon.height: Math.round(24 * DefaultStyle.dp)
}

View file

@ -12,8 +12,9 @@ Control.Button {
property var style
property color color: style?.color?.normal || DefaultStyle.main1_500_main
property color hoveredColor: style?.color?.hovered || Qt.darker(color, 1.05)
property color pressedColor: style?.color?.pressed || Qt.darker(color, 1.1)
property color textColor: style?.text?.normal || DefaultStyle.grey_0
property color pressedColor: style?.color?.pressed || Qt.darker(color, 1.1)
property color checkedColor: style?.color?.checked || style?.color?.pressed || Qt.darker(color, 1.1)
property color textColor: style?.text?.normal || DefaultStyle.grey_0
property color hoveredTextColor: style?.text?.hovered || Qt.darker(textColor, 1.05)
property color pressedTextColor: style?.text?.pressed || Qt.darker(textColor, 1.1)
property color borderColor: style?.borderColor || "transparent"
@ -53,11 +54,13 @@ Control.Button {
Rectangle {
id: buttonBackground
anchors.fill: parent
color: mainItem.pressed
? mainItem.pressedColor
: mainItem.hovered || mainItem.hasNavigationFocus
? mainItem.hoveredColor
: mainItem.color
color: mainItem.checkable && mainItem.checked
? mainItem.checkedColor || mainItem.pressedColor
: mainItem.pressed
? mainItem.pressedColor
: mainItem.hovered || mainItem.hasNavigationFocus
? mainItem.hoveredColor
: mainItem.color
radius: mainItem.radius
border.color: mainItem.borderColor
}
@ -95,11 +98,13 @@ Control.Button {
wrapMode: Text.WrapAnywhere
text: mainItem.text
maximumLineCount: 1
color: pressed
? mainItem.pressedTextColor
: mainItem.hovered
? mainItem.hoveredTextColor
: mainItem.textColor
color: mainItem.checkable && mainItem.checked
? mainItem.checkedColor || mainItem.pressedColor
: mainItem.pressed
? mainItem.pressedTextColor
: mainItem.hovered
? mainItem.hoveredTextColor
: mainItem.textColor
font {
pixelSize: mainItem.textSize
weight: mainItem.textWeight
@ -120,11 +125,13 @@ Control.Button {
imageSource: mainItem.icon.source
imageWidth: mainItem.icon.width
imageHeight: mainItem.icon.height
colorizationColor: mainItem.pressed
? mainItem.pressedImageColor
: mainItem.hovered
? mainItem.hoveredImageColor
: mainItem.contentImageColor
colorizationColor: mainItem.checkable && mainItem.checked
? mainItem.checkedColor || mainItem.pressedColor
: mainItem.pressed
? mainItem.pressedImageColor
: mainItem.hovered
? mainItem.hoveredImageColor
: mainItem.contentImageColor
}
contentItem: Control.StackView{

View file

@ -13,7 +13,7 @@ ComboBox {
property alias contentText: contentText
contentItem: Text {
id: contentText
text: UtilsCpp.formatDate(calendar.selectedDate, false, "ddd d, MMMM")
text: UtilsCpp.formatDate(calendar.selectedDate, false, true, "ddd d, MMMM")
anchors.fill: parent
anchors.leftMargin: Math.round(15 * DefaultStyle.dp)
anchors.verticalCenter: parent.verticalCenter

View file

@ -36,7 +36,6 @@ Control.TabBar {
}
component UnreadNotification: Rectangle {
id: unreadNotifications
property int unread: 0
visible: unread > 0
width: Math.round(15 * DefaultStyle.dp)

View file

@ -0,0 +1,244 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import Linphone
import UtilsCpp
import SettingsCpp
import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
ListView {
id: mainItem
clip: true
property SearchBar searchBar
property bool loading: false
property string searchText: searchBar?.text
property real busyIndicatorSize: Math.round(60 * DefaultStyle.dp)
signal resultsReceived
onResultsReceived: {
loading = false
// contentY = 0
}
model: ChatProxy {
id: chatProxy
Component.onCompleted: {
loading = true
}
filterText: mainItem.searchText
onFilterTextChanged: maxDisplayItems = initialDisplayItems
initialDisplayItems: Math.max(
20,
2 * mainItem.height / (Math.round(56 * DefaultStyle.dp)))
displayItemsStep: 3 * initialDisplayItems / 2
onModelReset: {
mainItem.resultsReceived()
}
}
// flickDeceleration: 10000
spacing: Math.round(10 * DefaultStyle.dp)
Component.onCompleted: cacheBuffer = Math.max(contentHeight, 0) //contentHeight>0 ? contentHeight : 0// cache all items
// remove binding loop
onContentHeightChanged: Qt.callLater(function () {
if (mainItem)
mainItem.cacheBuffer = Math?.max(contentHeight, 0) || 0
})
onActiveFocusChanged: if (activeFocus && currentIndex < 0 && count > 0)
currentIndex = 0
onCountChanged: {
if (currentIndex < 0 && count > 0) {
mainItem.currentIndex = 0 // Select first item after loading model
}
if (atYBeginning)
positionViewAtBeginning() // Stay at beginning
}
onAtYEndChanged: {
if (atYEnd && count > 0) {
chatProxy.displayMore()
}
}
//----------------------------------------------------------------
function moveToCurrentItem() {
if (mainItem.currentIndex >= 0)
Utils.updatePosition(mainItem, mainItem)
}
onCurrentItemChanged: {
moveToCurrentItem()
}
// Update position only if we are moving to current item and its position is changing.
property var _currentItemY: currentItem?.y
on_CurrentItemYChanged: if (_currentItemY && moveAnimation.running) {
moveToCurrentItem()
}
Behavior on contentY {
NumberAnimation {
id: moveAnimation
duration: 500
easing.type: Easing.OutExpo
alwaysRunToEnd: true
}
}
// //----------------------------------------------------------------
onVisibleChanged: {
// if (!visible)
// currentIndex = -1
}
BusyIndicator {
anchors.horizontalCenter: mainItem.horizontalCenter
visible: mainItem.loading
height: visible ? mainItem.busyIndicatorSize : 0
width: mainItem.busyIndicatorSize
indicatorHeight: mainItem.busyIndicatorSize
indicatorWidth: mainItem.busyIndicatorSize
indicatorColor: DefaultStyle.main1_500_main
}
// Qt bug: sometimes, containsMouse may not be send and update on each MouseArea.
// So we need to use this variable to switch off all hovered items.
property int lastMouseContainsIndex: -1
component UnreadNotification: Item {
id: unreadNotif
property int unread: 0
width: Math.round(22 * DefaultStyle.dp)
height: Math.round(22 * DefaultStyle.dp)
visible: unread > 0
Rectangle {
id: background
anchors.fill: parent
radius: width/2
color: DefaultStyle.danger_500main
Text{
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: DefaultStyle.grey_0
fontSizeMode: Text.Fit
font.pixelSize: Typography.p3.pixelSize
text: parent.unreadNotif > 100 ? '99+' : unreadNotif.unread
}
}
MultiEffect {
id: shadow
anchors.fill: background
source: background
// Crash : https://bugreports.qt.io/browse/QTBUG-124730?
shadowEnabled: true
shadowColor: DefaultStyle.grey_1000
shadowBlur: 1
shadowOpacity: 0.15
z: unreadNotif.z - 1
}
}
delegate: FocusScope {
width: mainItem.width
height: Math.round(63 * DefaultStyle.dp)
RowLayout {
z: 1
anchors.fill: parent
anchors.leftMargin: Math.round(11 * DefaultStyle.dp)
anchors.rightMargin: Math.round(11 * DefaultStyle.dp)
anchors.topMargin: Math.round(9 * DefaultStyle.dp)
anchors.bottomMargin: Math.round(9 * DefaultStyle.dp)
spacing: Math.round(10 * DefaultStyle.dp)
Avatar {
id: historyAvatar
property var contactObj: UtilsCpp.findFriendByAddress(modelData.core.peerAddress)
contact: contactObj?.value || null
onContactChanged: {
if (contact) console.log("found contact", contact.core.defaultAddress)
else console.log("no contact for peer address", modelData.core.peerAddress, modelData.core.avatarUri)
}
displayNameVal: contact ? undefined : modelData.core.avatarUri
// secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
// isConference: modelData.core.isConference
shadowEnabled: false
asynchronous: false
}
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: Math.round(5 * DefaultStyle.dp)
Text {
id: friendAddress
Layout.fillWidth: true
maximumLineCount: 1
text: modelData.core.title
color: DefaultStyle.main2_800
font {
pixelSize: Typography.p1.pixelSize
weight: unreadCount.unread > 0 ? Typography.p2.weight : Typography.p1.weight
capitalization: Font.Capitalize
}
}
Text {
Layout.fillWidth: true
maximumLineCount: 1
text: modelData.core.lastMessageInHistory
color: DefaultStyle.main2_400
font {
pixelSize: Typography.p1.pixelSize
weight: unreadCount.unread > 0 ? Typography.p2.weight : Typography.p1.weight
}
}
}
ColumnLayout {
Text {
color: DefaultStyle.main2_500main
text: UtilsCpp.formatDate(modelData.core.lastUpdatedTime, true, false)
font {
pixelSize: Typography.p3.pixelSize
weight: Typography.p3.weight
capitalization: Font.Capitalize
}
}
RowLayout {
UnreadNotification {
id: unreadCount
unread: modelData.core.unreadMessagesCount
}
//sourdine, éphémère, IMDN
}
}
}
MouseArea {
hoverEnabled: true
anchors.fill: parent
focus: true
onContainsMouseChanged: {
if (containsMouse)
mainItem.lastMouseContainsIndex = index
else if (mainItem.lastMouseContainsIndex == index)
mainItem.lastMouseContainsIndex = -1
}
Rectangle {
anchors.fill: parent
opacity: 0.7
radius: Math.round(8 * DefaultStyle.dp)
color: mainItem.currentIndex
=== index ? DefaultStyle.main2_200 : DefaultStyle.main2_100
visible: mainItem.lastMouseContainsIndex === index
|| mainItem.currentIndex === index
}
onPressed: {
mainItem.currentIndex = model.index
mainItem.forceActiveFocus()
}
}
}
}

View file

@ -0,0 +1,74 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import Linphone
import UtilsCpp
import SettingsCpp
import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
Control.Control {
id: mainItem
property color backgroundColor
property bool isRemoteMessage
property bool isFirstMessage
property string imgUrl
property string contentText
topPadding: Math.round(12 * DefaultStyle.dp)
bottomPadding: Math.round(12 * DefaultStyle.dp)
leftPadding: Math.round(18 * DefaultStyle.dp)
rightPadding: Math.round(18 * DefaultStyle.dp)
background: Item {
anchors.fill: parent
Rectangle {
anchors.fill: parent
color: mainItem.backgroundColor
radius: Math.round(16 * DefaultStyle.dp)
}
Rectangle {
visible: mainItem.isFirstMessage && mainItem.isRemoteMessage
anchors.top: parent.top
anchors.left: parent.left
width: Math.round(parent.width / 4)
height: Math.round(parent.height / 4)
color: mainItem.backgroundColor
}
Rectangle {
visible: mainItem.isFirstMessage && !mainItem.isRemoteMessage
anchors.bottom: parent.bottom
anchors.right: parent.right
width: Math.round(parent.width / 4)
height: Math.round(parent.height / 4)
color: mainItem.backgroundColor
}
}
contentItem: ColumnLayout {
id: contentLayout
Image {
visible: mainItem.imgUrl != undefined
id: contentimage
}
Text {
visible: mainItem.contentText != undefined
text: mainItem.contentText
Layout.fillWidth: true
color: DefaultStyle.main2_700
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
}
}
Text {
Layout.alignment: Qt.AlignRight
text: UtilsCpp.formatDate(modelData.core.timestamp, true, false)
color: DefaultStyle.main2_500main
font {
pixelSize: Typography.p3.pixelSize
weight: Typography.p3.weight
}
}
}
}

View file

@ -0,0 +1,34 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import Linphone
import UtilsCpp
import SettingsCpp
import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
ListView {
id: mainItem
property ChatGui chat
spacing: Math.round(20 * DefaultStyle.dp)
Component.onCompleted: positionViewAtEnd()
model: ChatMessageProxy {
chatGui: mainItem.chat
}
delegate: ChatMessage {
id: chatMessage
width: Math.min(implicitWidth, Math.round(mainItem.width * (3/4)))
// height: childrenRect.height
backgroundColor: isRemoteMessage ? DefaultStyle.main2_100 : DefaultStyle.main1_100
contentText: modelData.core.text
isFirstMessage: true
isRemoteMessage: modelData.core.isRemoteMessage
anchors.right: !isRemoteMessage && parent
? parent.right
: undefined
}
}

View file

@ -141,94 +141,95 @@ Loader{
}
}
Component{
id: initials
Item {
id: avatarItem
height: stackView.height
width: height
Rectangle {
id: initialItem
property string initials: mainItem.isConference ? "" : UtilsCpp.getInitials(mainItem.displayNameVal)
radius: width / 2
color: DefaultStyle.main2_200
height: stackView.height
width: height
Text {
anchors.fill: parent
anchors.centerIn: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: initialItem.initials
font {
pixelSize: initialItem.height * 36 / 120
weight: Typography.h4.weight
capitalization: Font.AllUppercase
}
}
EffectImage {
id: initialImg
visible: initialItem.initials == ''
width: stackView.width/2
height: width
colorizationColor: DefaultStyle.main2_600
imageSource: mainItem.isConference ? AppIcons.usersThree : AppIcons.profile
anchors.centerIn: parent
}
}
MultiEffect {
source: initialItem
anchors.fill: initialItem
shadowEnabled: true
shadowBlur: 0.1
shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.1
}
Connections {
target: mainItem.call?.core ? mainItem.call.core : null
onRemoteNameChanged: initialItem.initials = UtilsCpp.getInitials(mainItem.call.core.remoteName)
}
}
}
Component{
id: avatar
Item {
id: avatarItem
height: stackView.height
width: height
Image {
id: image
z: 200
visible: false
width: parent.width
height: parent.height
sourceSize.width: avatarItem.width
sourceSize.height: avatarItem.height
fillMode: Image.PreserveAspectCrop
anchors.centerIn: parent
source: mainItem.account
? mainItem.account.core.pictureUri
: mainItem.contact
? mainItem.contact.core.pictureUri
: computedAvatarUri
mipmap: true
layer.enabled: true
}
ShaderEffect {
id: roundEffect
property variant src: image
property real edge: 0.9
property real edgeSoftness: 0.9
property real radius: width / 2.0
property real shadowSoftness: 0.5
property real shadowOffset: 0.01
anchors.fill: parent
fragmentShader: 'qrc:/data/shaders/roundEffect.frag.qsb'
}
}
}
}
Component{
id: initials
Item {
id: avatarItem
height: stackView.height
width: height
Rectangle {
id: initialItem
property string initials: mainItem.isConference ? "" : UtilsCpp.getInitials(mainItem.displayNameVal)
radius: width / 2
color: DefaultStyle.main2_200
height: stackView.height
width: height
Text {
anchors.fill: parent
anchors.centerIn: parent
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: initialItem.initials
font {
pixelSize: initialItem.height * 36 / 120
weight: Typography.h4.weight
capitalization: Font.AllUppercase
}
}
EffectImage {
id: initialImg
visible: initialItem.initials == ''
width: stackView.width/2
height: width
colorizationColor: DefaultStyle.main2_600
imageSource: mainItem.isConference ? AppIcons.usersThree : AppIcons.profile
anchors.centerIn: parent
}
}
MultiEffect {
source: initialItem
anchors.fill: initialItem
shadowEnabled: true
shadowBlur: 0.1
shadowColor: DefaultStyle.grey_1000
shadowOpacity: 0.1
}
Connections {
target: mainItem.call?.core ? mainItem.call.core : null
onRemoteNameChanged: initialItem.initials = UtilsCpp.getInitials(mainItem.call.core.remoteName)
}
}
}
Component{
id: avatar
Item {
id: avatarItem
height: stackView.height
width: height
Image {
id: image
z: 200
visible: false
width: parent.width
height: parent.height
sourceSize.width: avatarItem.width
sourceSize.height: avatarItem.height
fillMode: Image.PreserveAspectCrop
anchors.centerIn: parent
source: mainItem.account
? mainItem.account.core.pictureUri
: mainItem.contact
? mainItem.contact.core.pictureUri
: computedAvatarUri
mipmap: true
layer.enabled: true
}
ShaderEffect {
id: roundEffect
property variant src: image
property real edge: 0.9
property real edgeSoftness: 0.9
property real radius: width / 2.0
property real shadowSoftness: 0.5
property real shadowOffset: 0.01
anchors.fill: parent
fragmentShader: 'qrc:/data/shaders/roundEffect.frag.qsb'
}
}
}
}
}
}

View file

@ -0,0 +1,247 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import Linphone
import UtilsCpp
import SettingsCpp
import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
ListView {
id: mainItem
clip: true
property SearchBar searchBar
property bool loading: false
property string searchText: searchBar?.text
property real busyIndicatorSize: Math.round(60 * DefaultStyle.dp)
signal resultsReceived
onResultsReceived: {
loading = false
// contentY = 0
}
model: CallHistoryProxy {
id: callHistoryProxy
Component.onCompleted: {
loading = true
}
filterText: mainItem.searchText
onFilterTextChanged: maxDisplayItems = initialDisplayItems
initialDisplayItems: Math.max(
20,
2 * mainItem.height / (Math.round(56 * DefaultStyle.dp)))
displayItemsStep: 3 * initialDisplayItems / 2
onModelReset: {
mainItem.resultsReceived()
}
}
flickDeceleration: 10000
spacing: Math.round(10 * DefaultStyle.dp)
Keys.onPressed: event => {
if (event.key == Qt.Key_Escape) {
console.log("Back")
searchBar.forceActiveFocus()
event.accepted = true
}
}
Component.onCompleted: cacheBuffer = Math.max(
contentHeight,
0) //contentHeight>0 ? contentHeight : 0// cache all items
// remove binding loop
onContentHeightChanged: Qt.callLater(function () {
if (mainItem)
mainItem.cacheBuffer = Math?.max(contentHeight, 0) || 0
})
onActiveFocusChanged: if (activeFocus && currentIndex < 0 && count > 0)
currentIndex = 0
onCountChanged: {
if (currentIndex < 0 && count > 0) {
mainItem.currentIndex = 0 // Select first item after loading model
}
if (atYBeginning)
positionViewAtBeginning() // Stay at beginning
}
Connections {
target: deleteHistoryPopup
function onAccepted() {
mainItem.model.removeAllEntries()
}
}
onAtYEndChanged: {
if (atYEnd && count > 0) {
callHistoryProxy.displayMore()
}
}
//----------------------------------------------------------------
function moveToCurrentItem() {
if (mainItem.currentIndex >= 0)
Utils.updatePosition(mainItem, mainItem)
}
onCurrentItemChanged: {
moveToCurrentItem()
}
// Update position only if we are moving to current item and its position is changing.
property var _currentItemY: currentItem?.y
on_CurrentItemYChanged: if (_currentItemY && moveAnimation.running) {
moveToCurrentItem()
}
Behavior on contentY {
NumberAnimation {
id: moveAnimation
duration: 500
easing.type: Easing.OutExpo
alwaysRunToEnd: true
}
}
//----------------------------------------------------------------
onVisibleChanged: {
// if (!visible)
// currentIndex = -1
}
BusyIndicator {
anchors.horizontalCenter: mainItem.horizontalCenter
visible: mainItem.loading
height: visible ? mainItem.busyIndicatorSize : 0
width: mainItem.busyIndicatorSize
indicatorHeight: mainItem.busyIndicatorSize
indicatorWidth: mainItem.busyIndicatorSize
indicatorColor: DefaultStyle.main1_500_main
}
// Qt bug: sometimes, containsMouse may not be send and update on each MouseArea.
// So we need to use this variable to switch off all hovered items.
property int lastMouseContainsIndex: -1
delegate: FocusScope {
width: mainItem.width
height: Math.round(56 * DefaultStyle.dp)
RowLayout {
z: 1
anchors.fill: parent
anchors.leftMargin: Math.round(10 * DefaultStyle.dp)
spacing: Math.round(10 * DefaultStyle.dp)
Avatar {
id: historyAvatar
property var contactObj: UtilsCpp.findFriendByAddress(
modelData.core.remoteAddress)
contact: contactObj?.value || null
displayNameVal: modelData.core.displayName
secured: securityLevel === LinphoneEnums.SecurityLevel.EndToEndEncryptedAndVerified
width: Math.round(45 * DefaultStyle.dp)
height: Math.round(45 * DefaultStyle.dp)
isConference: modelData.core.isConference
shadowEnabled: false
asynchronous: false
}
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: Math.round(5 * DefaultStyle.dp)
Text {
id: friendAddress
Layout.fillWidth: true
maximumLineCount: 1
text: historyAvatar.displayNameVal
font {
pixelSize: Typography.p1.pixelSize
weight: Typography.p1.weight
capitalization: Font.Capitalize
}
}
RowLayout {
spacing: Math.round(6 * DefaultStyle.dp)
EffectImage {
id: statusIcon
imageSource: modelData.core.status === LinphoneEnums.CallStatus.Declined
|| modelData.core.status
=== LinphoneEnums.CallStatus.DeclinedElsewhere
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted
|| modelData.core.status === LinphoneEnums.CallStatus.EarlyAborted ? AppIcons.arrowElbow : modelData.core.isOutgoing ? AppIcons.arrowUpRight : AppIcons.arrowDownLeft
colorizationColor: modelData.core.status
=== LinphoneEnums.CallStatus.Declined
|| modelData.core.status
=== LinphoneEnums.CallStatus.DeclinedElsewhere
|| modelData.core.status
=== LinphoneEnums.CallStatus.Aborted
|| modelData.core.status
=== LinphoneEnums.CallStatus.EarlyAborted
|| modelData.core.status === LinphoneEnums.CallStatus.Missed ? DefaultStyle.danger_500main : modelData.core.isOutgoing ? DefaultStyle.info_500_main : DefaultStyle.success_500main
Layout.preferredWidth: Math.round(12 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(12 * DefaultStyle.dp)
transform: Rotation {
angle: modelData.core.isOutgoing
&& (modelData.core.status === LinphoneEnums.CallStatus.Declined
|| modelData.core.status
=== LinphoneEnums.CallStatus.DeclinedElsewhere
|| modelData.core.status === LinphoneEnums.CallStatus.Aborted
|| modelData.core.status
=== LinphoneEnums.CallStatus.EarlyAborted) ? 180 : 0
origin {
x: statusIcon.width / 2
y: statusIcon.height / 2
}
}
}
Text {
// text: modelData.core.date
text: UtilsCpp.formatDate(modelData.core.date)
font {
pixelSize: Math.round(12 * DefaultStyle.dp)
weight: Math.round(300 * DefaultStyle.dp)
}
}
}
}
BigButton {
style: ButtonStyle.noBackground
icon.source: AppIcons.phone
focus: true
activeFocusOnTab: false
asynchronous: false
onClicked: {
if (modelData.core.isConference) {
var callsWindow = UtilsCpp.getCallsWindow()
callsWindow.setupConference(
modelData.core.conferenceInfo)
UtilsCpp.smartShowWindow(callsWindow)
} else {
UtilsCpp.createCall(modelData.core.remoteAddress)
}
}
}
}
MouseArea {
hoverEnabled: true
anchors.fill: parent
focus: true
onContainsMouseChanged: {
if (containsMouse)
mainItem.lastMouseContainsIndex = index
else if (mainItem.lastMouseContainsIndex == index)
mainItem.lastMouseContainsIndex = -1
}
Rectangle {
anchors.fill: parent
opacity: 0.7
radius: Math.round(8 * DefaultStyle.dp)
color: mainItem.currentIndex
=== index ? DefaultStyle.main2_200 : DefaultStyle.main2_100
visible: mainItem.lastMouseContainsIndex === index
|| mainItem.currentIndex === index
}
onPressed: {
mainItem.currentIndex = model.index
mainItem.forceActiveFocus()
}
}
}
}

View file

@ -167,9 +167,9 @@ Control.TextField {
}
}
Keys.onReleased: event => {
if (event.jey == Qt.Key_Control)
mainItem.controlIsDown = false
}
if (event.jey == Qt.Key_Control)
mainItem.controlIsDown = false
}
Button {
id: eyeButton

View file

@ -0,0 +1,186 @@
import QtCore
import QtQuick
import QtQuick.Controls.Basic as Control
import QtQuick.Dialogs
import QtQuick.Effects
import QtQuick.Layouts
import Linphone
import UtilsCpp
import SettingsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
RowLayout {
id: mainItem
property ChatGui chat
spacing: 0
MainRightPanel {
Layout.fillWidth: true
Layout.fillHeight: true
panelColor: DefaultStyle.grey_0
clip: true
headerContent: [
RowLayout {
anchors.left: parent.left
anchors.leftMargin: Math.round(31 * DefaultStyle.dp)
anchors.verticalCenter: parent.verticalCenter
spacing: Math.round(12 * DefaultStyle.dp)
Avatar {
property var contactObj: mainItem.chat ? UtilsCpp.findFriendByAddress(mainItem.chat.core.peerAddress) : null
contact: contactObj?.value || null
Layout.preferredWidth: Math.round(45 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(45 * DefaultStyle.dp)
}
Text {
text: mainItem.chat?.core.title || ""
color: DefaultStyle.main2_600
Layout.fillWidth: true
maximumLineCount: 1
font {
pixelSize: Typography.h4.pixelSize
weight: Math.round(400 * DefaultStyle.dp)
}
}
},
RowLayout {
anchors.right: parent.right
anchors.rightMargin: Math.round(41 * DefaultStyle.dp)
anchors.verticalCenter: parent.verticalCenter
BigButton {
style: ButtonStyle.noBackground
icon.source: AppIcons.phone
}
BigButton {
style: ButtonStyle.noBackground
icon.source: AppIcons.camera
}
BigButton {
style: ButtonStyle.noBackground
checkable: true
icon.source: AppIcons.info
onCheckedChanged: {
}
}
}
]
content: [
ChatMessagesListView {
id: chatMessagesListView
height: contentHeight
width: parent.width - anchors.leftMargin - anchors.rightMargin
chat: mainItem.chat
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: messageSender.top
anchors.leftMargin: Math.round(18 * DefaultStyle.dp)
anchors.rightMargin: Math.round(18 * DefaultStyle.dp)
anchors.bottomMargin: Math.round(18 * DefaultStyle.dp)
Control.ScrollBar.vertical: scrollbar
},
ScrollBar {
id: scrollbar
visible: chatMessagesListView.contentHeight > parent.height
active: visible
anchors.top: parent.top
anchors.bottom: chatMessagesListView.bottom
anchors.right: parent.right
anchors.rightMargin: Math.round(5 * DefaultStyle.dp)
policy: Control.ScrollBar.AsNeeded
},
Control.Control {
id: messageSender
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 79 * DefaultStyle.dp
leftPadding: Math.round(15 * DefaultStyle.dp)
rightPadding: Math.round(15 * DefaultStyle.dp)
topPadding: Math.round(16 * DefaultStyle.dp)
bottomPadding: Math.round(16 * DefaultStyle.dp)
background: Rectangle {
color: DefaultStyle.grey_100
}
contentItem: RowLayout {
spacing: Math.round(20 * DefaultStyle.dp)
RowLayout {
spacing: Math.round(16 * DefaultStyle.dp)
BigButton {
style: ButtonStyle.noBackground
checkable: true
icon.source: AppIcons.smiley
onCheckedChanged: {
console.log("TODO : emoji")
}
}
BigButton {
style: ButtonStyle.noBackground
icon.source: AppIcons.paperclip
onClicked: {
console.log("TODO : open explorer to attach file")
}
}
Control.Control {
Layout.fillWidth: true
Layout.preferredHeight: Math.round(48 * DefaultStyle.dp)
background: Rectangle {
id: inputBackground
anchors.fill: parent
radius: Math.round(30 * DefaultStyle.dp)
color: DefaultStyle.grey_0
}
contentItem: RowLayout {
TextArea {
id: sendingTextArea
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: parent.width - stackButton.width
}
StackLayout {
id: stackButton
currentIndex: sendingTextArea.text.length === 0 ? 0 : 1
BigButton {
style: ButtonStyle.noBackground
icon.source: AppIcons.microphone
onClicked: {
console.log("TODO : go to record message")
}
}
BigButton {
style: ButtonStyle.noBackgroundOrange
icon.source: AppIcons.paperPlaneRight
onClicked: {
console.log("TODO : send message")
}
}
}
}
}
}
}
}
]
}
Rectangle {
visible: detailsPanel.visible
color: DefaultStyle.main2_200
Layout.preferredWidth: Math.round(1 * DefaultStyle.dp)
Layout.fillHeight: true
}
Control.Control {
id: detailsPanel
visible: false
Layout.fillHeight: true
Layout.preferredWidth: Math.round(387 * DefaultStyle.dp)
background: Rectangle {
color: DefaultStyle.grey_0
anchors.fill: parent
}
contentItem: ColumnLayout {
}
}
}

View file

@ -616,8 +616,7 @@ Item {
}
}
}
Item {}
//ConversationPage{}
ChatPage{}
MeetingPage {}
}
}

View file

@ -674,8 +674,7 @@ AbstractMainPage {
}
}
Text {
text: UtilsCpp.formatDate(
modelData.core.date)
text: UtilsCpp.formatDate(modelData.core.date)
color: modelData.core.status === LinphoneEnums.CallStatus.Missed ? DefaultStyle.danger_500main : DefaultStyle.main2_500main
font {
pixelSize: Math.round(12 * DefaultStyle.dp)

View file

@ -0,0 +1,253 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import QtQuick.Controls.Basic as Control
import Linphone
import UtilsCpp
import SettingsCpp
import "qrc:/qt/qml/Linphone/view/Style/buttonStyle.js" as ButtonStyle
import "qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js" as Utils
AbstractMainPage {
id: mainItem
//: "Nouvelle conversation"
noItemButtonText: qsTr("chat_start_title")
//: "Aucune conversation"
emptyListText: qsTr("chat_empty_title")
newItemIconSource: AppIcons.plusCircle
property var selectedChatGui
onSelectedChatGuiChanged: {
if (selectedChatGui)
rightPanelStackView.replace(currentChatComp,
Control.StackView.Immediate)
else
rightPanelStackView.replace(emptySelection,
Control.StackView.Immediate)
}
rightPanelStackView.initialItem: emptySelection
onNoItemButtonPressed: goToNewChat()
showDefaultItem: listStackView.currentItem
&& listStackView.currentItem.objectName == "chatListItem"
&& listStackView.currentItem.listView.count === 0 || false
function goToNewChat() {
if (listStackView.currentItem
&& listStackView.currentItem.objectName != "newChatItem")
listStackView.push(newChatItem)
}
Dialog {
id: deleteChatPopup
width: Math.round(637 * DefaultStyle.dp)
//: Supprimer la conversation ?
title: qsTr("chat_dialog_delete_chat_title")
//: "La conversation et tous ses messages seront supprimés."
text: qsTr("chat_dialog_delete_chat_message")
}
leftPanelContent: Control.StackView {
id: listStackView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: Math.round(45 * DefaultStyle.dp)
clip: true
initialItem: chatListItem
focus: true
onActiveFocusChanged: if (activeFocus) {
currentItem.forceActiveFocus()
}
}
Component {
id: chatListItem
FocusScope {
objectName: "chatListItem"
property alias listView: chatListView
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
spacing: Math.round(16 * DefaultStyle.dp)
Text {
Layout.fillWidth: true
//: "Conversations"
text: qsTr("chat_list_title")
color: DefaultStyle.main2_700
font.pixelSize: Typography.h2.pixelSize
font.weight: Typography.h2.weight
}
Item {
Layout.fillWidth: true
}
Button {
id: newChatButton
style: ButtonStyle.noBackground
icon.source: AppIcons.plusCircle
Layout.preferredWidth: Math.round(28 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(28 * DefaultStyle.dp)
Layout.rightMargin: Math.round(39 * DefaultStyle.dp)
icon.width: Math.round(28 * DefaultStyle.dp)
icon.height: Math.round(28 * DefaultStyle.dp)
KeyNavigation.down: searchBar
onClicked: {
console.debug("[ChatPage]User: create new chat")
mainItem.goToNewChat()
}
}
}
SearchBar {
id: searchBar
Layout.fillWidth: true
Layout.topMargin: Math.round(18 * DefaultStyle.dp)
Layout.rightMargin: Math.round(39 * DefaultStyle.dp)
//: "Rechercher une conversation"
placeholderText: qsTr("chat_search_in_history")
visible: chatListView.count !== 0 || text.length !== 0
focus: true
KeyNavigation.up: newChatButton
KeyNavigation.down: chatListView
Binding {
target: mainItem
property: "showDefaultItem"
when: searchBar.text.length != 0
value: false
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
anchors.fill: parent
anchors.rightMargin: Math.round(39 * DefaultStyle.dp)
Text {
visible: chatListView.count === 0
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: Math.round(137 * DefaultStyle.dp)
//: "Aucun résultat"
text: searchBar.text.length != 0 ? qsTr("list_filter_no_result_found")
//: "Aucune conversation dans votre historique"
: qsTr("chat_list_empty_history")
font {
pixelSize: Typography.h4.pixelSize
weight: Typography.h4.weight
}
}
ChatListView {
id: chatListView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.topMargin: Math.round(39 * DefaultStyle.dp)
searchBar: searchBar
Control.ScrollBar.vertical: scrollbar
onCurrentIndexChanged: {
mainItem.selectedChatGui = model.getAt(currentIndex)
}
onCountChanged: {
mainItem.selectedChatGui = model.getAt(currentIndex)
}
}
}
ScrollBar {
id: scrollbar
visible: chatListView.contentHeight > parent.height
active: visible
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.rightMargin: Math.round(8 * DefaultStyle.dp)
policy: Control.ScrollBar.AsNeeded
}
}
}
}
}
Component {
id: newChatItem
FocusScope {
objectName: "newChatItem"
width: parent?.width
height: parent?.height
Control.StackView.onActivated: {
callContactsList.forceActiveFocus()
}
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
spacing: Math.round(10 * DefaultStyle.dp)
Button {
Layout.preferredWidth: Math.round(24 * DefaultStyle.dp)
Layout.preferredHeight: Math.round(24 * DefaultStyle.dp)
style: ButtonStyle.noBackground
icon.source: AppIcons.leftArrow
focus: true
KeyNavigation.down: listStackView
onClicked: {
console.debug(
"[CallPage]User: return to call history")
listStackView.pop()
listStackView.forceActiveFocus()
}
}
Text {
Layout.fillWidth: true
//: "New chat"
text: qsTr("chat_action_start_new_chat")
color: DefaultStyle.main2_700
font.pixelSize: Typography.h2.pixelSize
font.weight: Typography.h2.weight
}
Item {
Layout.fillWidth: true
}
}
// NewCallForm {
// id: callContactsList
// Layout.topMargin: Math.round(18 * DefaultStyle.dp)
// Layout.fillWidth: true
// Layout.fillHeight: true
// focus: true
// numPadPopup: numericPadPopupItem
// groupCallVisible: true
// searchBarColor: DefaultStyle.grey_100
// onContactClicked: contact => {
// mainWindow.startCallWithContact(contact, false, callContactsList)
// }
// onGroupCallCreationRequested: {
// console.log("groupe call requetsed")
// listStackView.push(groupCallItem)
// }
// Connections {
// target: mainItem
// function onCreateCallFromSearchBarRequested() {
// UtilsCpp.createCall(callContactsList.searchBar.text)
// }
// }
// }
}
}
}
Component {
id: emptySelection
Item {
objectName: "emptySelection"
}
}
Component {
id: currentChatComp
FocusScope {
SelectedChatView {
anchors.fill: parent
chat: mainItem.selectedChatGui
}
}
}
}

View file

@ -72,8 +72,10 @@ QtObject {
property string trustedMask: "image://internal/trusted-mask.svg"
property string avatar: "image://internal/randomAvatar.png"
property string pause: "image://internal/pause.svg"
property string play: "image://internal/play.svg"
property string smiley: "image://internal/smiley.svg"
property string play: "image://internal/play.svg"
property string paperclip: "image://internal/paperclip.svg"
property string paperPlaneRight: "image://internal/paper-plane-right.svg"
property string smiley: "image://internal/smiley.svg"
property string smileySad: "image://internal/smiley-sad.svg"
property string trashCan: "image://internal/trash-simple.svg"
property string copy: "image://internal/copy.svg"

View file

@ -30,6 +30,13 @@ QtObject {
pixelSize: Math.round(36 * DefaultStyle.dp),
weight: Math.min(Math.round(800 * DefaultStyle.dp), 1000)
})
// Text/P3 - Reduced paragraph text
property font p3: Qt.font( {
family: DefaultStyle.defaultFont,
pixelSize: Math.round(12 * DefaultStyle.dp),
weight: Math.min(Math.round(300 * DefaultStyle.dp), 1000)
})
// Text/P2 - Bold, reduced paragraph text
property font p2: Qt.font( {

View file

@ -130,17 +130,20 @@
color: {
normal: "#00000000",
hovered: "#00000000",
pressed: "#00000000"
pressed: "#00000000",
checked: Linphone.DefaultStyle.main1_500main
},
text: {
normal: Linphone.DefaultStyle.main2_600,
hovered: Linphone.DefaultStyle.main2_700,
pressed: Linphone.DefaultStyle.main2_800
pressed: Linphone.DefaultStyle.main2_800,
checked: Linphone.DefaultStyle.main1_500main
},
image: {
normal: Linphone.DefaultStyle.main2_600,
hovered: Linphone.DefaultStyle.main2_700,
pressed: Linphone.DefaultStyle.main2_800
pressed: Linphone.DefaultStyle.main2_800,
checked: Linphone.DefaultStyle.main1_500main
}
}

@ -1 +1 @@
Subproject commit 96a6385bfdb3e6c0ed85df42bda99467e8f6fc5f
Subproject commit c66673ac4b844d4b147c3629629a65f83efb3b38