From f82931d6c6be90c1198c2cf17ff880e5b1a7d12a Mon Sep 17 00:00:00 2001 From: Gaelle Braud Date: Fri, 13 Jun 2025 17:16:09 +0200 Subject: [PATCH] record message auto download attached files setting --- Linphone/core/App.cpp | 2 + Linphone/core/CMakeLists.txt | 3 + Linphone/core/chat/ChatCore.cpp | 18 +- Linphone/core/chat/ChatCore.hpp | 3 +- .../core/chat/message/ChatMessageCore.cpp | 18 +- .../core/chat/message/ChatMessageCore.hpp | 13 +- .../content/ChatMessageContentProxy.cpp | 2 +- Linphone/core/notifier/Notifier.cpp | 21 +- Linphone/core/recorder/RecorderCore.cpp | 150 ++ Linphone/core/recorder/RecorderCore.hpp | 82 ++ Linphone/core/recorder/RecorderGui.cpp | 49 + Linphone/core/recorder/RecorderGui.hpp | 52 + Linphone/core/setting/SettingsCore.cpp | 23 + Linphone/core/setting/SettingsCore.hpp | 9 + .../core/sound-player/SoundPlayerCore.cpp | 15 +- Linphone/core/sound-player/SoundPlayerGui.cpp | 1 + Linphone/core/sound-player/SoundPlayerGui.hpp | 1 + Linphone/data/emoji/emoji.json | 13 + Linphone/data/emoji/emojiSvgs/1f979.svg | 56 + Linphone/data/languages/de.ts | 1101 ++++++++------- Linphone/data/languages/en.ts | 1227 ++++++++--------- Linphone/data/languages/fr_FR.ts | 1107 ++++++++------- Linphone/model/CMakeLists.txt | 2 + .../model/call-history/CallHistoryModel.cpp | 2 +- Linphone/model/chat/ChatModel.cpp | 9 + Linphone/model/chat/ChatModel.hpp | 3 + .../model/chat/message/ChatMessageModel.cpp | 4 + .../model/chat/message/ChatMessageModel.hpp | 2 + Linphone/model/recorder/RecorderModel.cpp | 118 ++ Linphone/model/recorder/RecorderModel.hpp | 60 + Linphone/model/setting/SettingsModel.cpp | 23 + Linphone/model/setting/SettingsModel.hpp | 7 + .../model/sound-player/SoundPlayerModel.cpp | 8 +- .../model/sound-player/SoundPlayerModel.hpp | 2 +- Linphone/tool/Utils.cpp | 51 + Linphone/tool/Utils.hpp | 4 + Linphone/view/CMakeLists.txt | 1 + Linphone/view/Control/Button/Button.qml | 15 +- Linphone/view/Control/Button/RoundButton.qml | 4 +- .../Control/Display/Chat/ChatAudioContent.qml | 153 +- .../Control/Display/Chat/ChatListView.qml | 4 +- .../Display/Chat/ChatMessageContent.qml | 2 +- .../view/Control/Display/MediaProgressBar.qml | 10 +- .../Input/Chat/ChatDroppableTextArea.qml | 295 ++-- .../view/Page/Form/Chat/SelectedChatView.qml | 277 ++-- .../Layout/Settings/ChatSettingsLayout.qml | 39 + .../Settings/ContactsSettingsLayout.qml | 2 +- Linphone/view/Style/DefaultStyle.qml | 1 + Linphone/view/Style/buttonStyle.js | 17 + 49 files changed, 3097 insertions(+), 1984 deletions(-) create mode 100644 Linphone/core/recorder/RecorderCore.cpp create mode 100644 Linphone/core/recorder/RecorderCore.hpp create mode 100644 Linphone/core/recorder/RecorderGui.cpp create mode 100644 Linphone/core/recorder/RecorderGui.hpp create mode 100644 Linphone/data/emoji/emojiSvgs/1f979.svg create mode 100644 Linphone/model/recorder/RecorderModel.cpp create mode 100644 Linphone/model/recorder/RecorderModel.hpp create mode 100644 Linphone/view/Page/Layout/Settings/ChatSettingsLayout.qml diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index 431e101a8..a0fb5a40d 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -78,6 +78,7 @@ #include "core/payload-type/PayloadTypeProxy.hpp" #include "core/phone-number/PhoneNumber.hpp" #include "core/phone-number/PhoneNumberProxy.hpp" +#include "core/recorder/RecorderGui.hpp" #include "core/register/RegisterPage.hpp" #include "core/screen/ScreenList.hpp" #include "core/screen/ScreenProxy.hpp" @@ -686,6 +687,7 @@ void App::initCppInterfaces() { qmlRegisterType(Constants::MainQmlUri, 1, 0, "FPSCounter"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "EmojiModel"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "SoundPlayerGui"); + qmlRegisterType(Constants::MainQmlUri, 1, 0, "RecorderGui"); qmlRegisterType(Constants::MainQmlUri, 1, 0, "TimeZoneProxy"); diff --git a/Linphone/core/CMakeLists.txt b/Linphone/core/CMakeLists.txt index 268bbead7..45a0def9e 100644 --- a/Linphone/core/CMakeLists.txt +++ b/Linphone/core/CMakeLists.txt @@ -86,6 +86,9 @@ list(APPEND _LINPHONEAPP_SOURCES core/sound-player/SoundPlayerCore.cpp core/sound-player/SoundPlayerGui.cpp + core/recorder/RecorderCore.cpp + core/recorder/RecorderGui.cpp + core/videoSource/VideoSourceDescriptorCore.cpp core/videoSource/VideoSourceDescriptorGui.cpp diff --git a/Linphone/core/chat/ChatCore.cpp b/Linphone/core/chat/ChatCore.cpp index 009d4df84..3af0e5d29 100644 --- a/Linphone/core/chat/ChatCore.cpp +++ b/Linphone/core/chat/ChatCore.cpp @@ -117,6 +117,15 @@ void ChatCore::setSelf(QSharedPointer me) { mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteHistory, [this]() { mChatModelConnection->invokeToModel([this]() { mChatModel->deleteHistory(); }); }); + mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteMessage, [this](ChatMessageGui *message) { + mChatModelConnection->invokeToModel([this, core = message ? message->mCore : nullptr]() { + auto messageModel = core ? core->getModel() : nullptr; + if (messageModel) { + mChatModel->deleteMessage(messageModel->getMonitor()); + } + }); + }); + mChatModelConnection->makeConnectToCore( &ChatCore::lLeave, [this]() { mChatModelConnection->invokeToModel([this]() { mChatModel->leave(); }); }); mChatModelConnection->makeConnectToModel(&ChatModel::historyDeleted, [this]() { @@ -458,8 +467,13 @@ void ChatCore::appendEventLogsToEventLogList(QList> void ChatCore::appendEventLogToEventLogList(QSharedPointer e) { if (mEventLogList.contains(e)) return; - mEventLogList.append(e); - emit eventsInserted({e}); + auto it = std::find_if(mEventLogList.begin(), mEventLogList.end(), [e](QSharedPointer event) { + return e->getEventLogId() == event->getEventLogId(); + }); + if (it == mEventLogList.end()) { + mEventLogList.append(e); + emit eventsInserted({e}); + } } void ChatCore::removeEventLogsFromEventLogList(QList> list) { diff --git a/Linphone/core/chat/ChatCore.hpp b/Linphone/core/chat/ChatCore.hpp index 12568efa9..e7573925e 100644 --- a/Linphone/core/chat/ChatCore.hpp +++ b/Linphone/core/chat/ChatCore.hpp @@ -150,7 +150,7 @@ signals: void meAdminChanged(); void participantsChanged(); - void lDeleteMessage(); + void lDeleteMessage(ChatMessageGui *message); void lDelete(); void lDeleteHistory(); void lMarkAsRead(); @@ -159,6 +159,7 @@ signals: void lUpdateLastUpdatedTime(); void lSendTextMessage(QString message); void lSendMessage(QString message, QVariantList files); + void lSendVoiceMessage(); void lCompose(); void lLeave(); void lSetMuted(bool muted); diff --git a/Linphone/core/chat/message/ChatMessageCore.cpp b/Linphone/core/chat/message/ChatMessageCore.cpp index c69091805..0f4eb923a 100644 --- a/Linphone/core/chat/message/ChatMessageCore.cpp +++ b/Linphone/core/chat/message/ChatMessageCore.cpp @@ -90,6 +90,12 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr &c for (auto content : chatmessage->getContents()) { auto contentCore = ChatMessageContentCore::create(content, mChatMessageModel); mChatMessageContentList.push_back(contentCore); + if (content->isFile() && !content->isVoiceRecording()) mHasFileContent = true; + if (content->isIcalendar()) mIsCalendarInvite = true; + if (content->isVoiceRecording()) { + mIsVoiceRecording = true; + mVoiceRecordingContent = contentCore; + } } auto reac = chatmessage->getOwnReaction(); mOwnReaction = reac ? Utils::coreStringToAppString(reac->getBody()) : QString(); @@ -122,11 +128,6 @@ ChatMessageCore::ChatMessageCore(const std::shared_ptr &c mIsForward = chatmessage->isForward(); mIsReply = chatmessage->isReply(); - for (auto &content : chatmessage->getContents()) { - if (content->isFile() && !content->isVoiceRecording()) mHasFileContent = true; - if (content->isIcalendar()) mIsCalendarInvite = true; - if (content->isVoiceRecording()) mIsVoiceRecording = true; - } } ChatMessageCore::~ChatMessageCore() { @@ -156,6 +157,9 @@ void ChatMessageCore::setSelf(QSharedPointer me) { mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lRemoveReaction, [this]() { mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->removeReaction(); }); }); + mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lSend, [this]() { + mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->send(); }); + }); mChatMessageModelConnection->makeConnectToModel( &ChatMessageModel::newMessageReaction, [this](const std::shared_ptr &message, @@ -431,3 +435,7 @@ std::shared_ptr ChatMessageCore::getModel() const { // ConferenceInfoGui *ChatMessageCore::getConferenceInfoGui() const { // return mConferenceInfo ? new ConferenceInfoGui(mConferenceInfo) : nullptr; // } + +ChatMessageContentGui *ChatMessageCore::getVoiceRecordingContent() const { + return new ChatMessageContentGui(mVoiceRecordingContent); +} diff --git a/Linphone/core/chat/message/ChatMessageCore.hpp b/Linphone/core/chat/message/ChatMessageCore.hpp index c61666912..64dfefd20 100644 --- a/Linphone/core/chat/message/ChatMessageCore.hpp +++ b/Linphone/core/chat/message/ChatMessageCore.hpp @@ -18,11 +18,11 @@ * along with this program. If not, see . */ -#ifndef CHATMESSAGECORE_H_ -#define CHATMESSAGECORE_H_ +#ifndef CHAT_MESSAGE_CORE_H_ +#define CHAT_MESSAGE_CORE_H_ #include "EventLogCore.hpp" -#include "core/chat/message/content/ChatMessageContentCore.hpp" +#include "core/chat/message/content/ChatMessageContentGui.hpp" #include "core/chat/message/content/ChatMessageContentProxy.hpp" #include "core/conference/ConferenceInfoCore.hpp" #include "core/conference/ConferenceInfoGui.hpp" @@ -118,7 +118,7 @@ public: void setMessageState(LinphoneEnums::ChatMessageState state); std::shared_ptr getModel() const; - // ConferenceInfoGui *getConferenceInfoGui() const; + Q_INVOKABLE ChatMessageContentGui *getVoiceRecordingContent() const; signals: void timestampChanged(QDateTime timestamp); @@ -136,6 +136,7 @@ signals: void readChanged(); void lSendReaction(const QString &reaction); void lRemoveReaction(); + void lSend(); private: DECLARE_ABSTRACT_OBJECT @@ -164,10 +165,12 @@ private: bool mIsOutgoing = false; LinphoneEnums::ChatMessageState mMessageState; QList> mChatMessageContentList; + // for voice recording creation message + QSharedPointer mVoiceRecordingContent; // QSharedPointer mConferenceInfo = nullptr; std::shared_ptr mChatMessageModel; QSharedPointer> mChatMessageModelConnection; }; -#endif // CHATMESSAGECORE_H_ +#endif // CHAT_MESSAGE_CORE_H_ diff --git a/Linphone/core/chat/message/content/ChatMessageContentProxy.cpp b/Linphone/core/chat/message/content/ChatMessageContentProxy.cpp index fbcea8c11..3e721fb91 100644 --- a/Linphone/core/chat/message/content/ChatMessageContentProxy.cpp +++ b/Linphone/core/chat/message/content/ChatMessageContentProxy.cpp @@ -88,7 +88,7 @@ bool ChatMessageContentProxy::SortFilterList::filterAcceptsRow(int sourceRow, co if (contentCore) { if (mFilterType == (int)FilterContentType::Unknown) return false; else if (mFilterType == (int)FilterContentType::File) { - return contentCore->isFile() || contentCore->isFileTransfer(); + return !contentCore->isVoiceRecording() && (contentCore->isFile() || contentCore->isFileTransfer()); } else if (mFilterType == (int)FilterContentType::Text) return contentCore->isText(); else if (mFilterType == (int)FilterContentType::Voice) return contentCore->isVoiceRecording(); else if (mFilterType == (int)FilterContentType::Conference) return contentCore->isCalendar(); diff --git a/Linphone/core/notifier/Notifier.cpp b/Linphone/core/notifier/Notifier.cpp index fb5f48532..e38851a6d 100644 --- a/Linphone/core/notifier/Notifier.cpp +++ b/Linphone/core/notifier/Notifier.cpp @@ -324,7 +324,6 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr if (messages.size() > 0) { shared_ptr message = messages.front(); - auto receiverAccount = ToolModel::findAccount(message->getToAddress()); if (receiverAccount) { auto senderAccount = ToolModel::findAccount(message->getFromAddress()); @@ -340,7 +339,8 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr } } - if (messages.size() == 1) { // Display only sender on mono message. + auto getMessage = [this, &remoteAddress, &txt](const shared_ptr &message) { + if (message->isRead()) return; auto remoteAddr = message->getFromAddress()->clone(); remoteAddr->clean(); remoteAddress = Utils::coreStringToAppString(remoteAddr->asStringUriOnly()); @@ -356,9 +356,22 @@ void Notifier::notifyReceivedMessages(const std::shared_ptr if (txt.isEmpty() && message->hasConferenceInvitationContent()) //: 'Conference invitation received!' : Notification about receiving an invitation to a conference. txt = tr("new_conference_invitation"); + }; + + if (messages.size() == 1) { // Display only sender on mono message. + getMessage(message); } else { - //: 'New messages received!' Notification that warn the user of new messages. - txt = tr("new_chat_room_messages"); + int unreadCount = 0; + for (auto &message : messages) { + if (!message->isRead()) { + ++unreadCount; + if (unreadCount == 1) getMessage(message); + } + } + if (unreadCount == 0) return; + if (unreadCount > 1) + //: 'New messages received!' Notification that warn the user of new messages. + txt = tr("new_chat_room_messages"); } auto chatCore = ChatCore::create(room); diff --git a/Linphone/core/recorder/RecorderCore.cpp b/Linphone/core/recorder/RecorderCore.cpp new file mode 100644 index 000000000..986db5aab --- /dev/null +++ b/Linphone/core/recorder/RecorderCore.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +#include "core/App.hpp" +#include "core/path/Paths.hpp" +#include "model/core/CoreModel.hpp" +#include "model/setting/SettingsModel.hpp" +#include "tool/Utils.hpp" + +#include "RecorderCore.hpp" + +DEFINE_ABSTRACT_OBJECT(RecorderCore) + +// ============================================================================= + +QSharedPointer RecorderCore::create(QObject *parent) { + auto sharedPointer = QSharedPointer(new RecorderCore(), &QObject::deleteLater); + sharedPointer->setSelf(sharedPointer); + sharedPointer->moveToThread(App::getInstance()->thread()); + return sharedPointer; +} + +RecorderCore::RecorderCore(QObject *parent) : QObject(parent) { + App::getInstance()->mEngine->setObjectOwnership( + this, QQmlEngine::CppOwnership); // Avoid QML to destroy it when passing by Q_INVOKABLE +} + +RecorderCore::~RecorderCore() { +} + +void RecorderCore::buildRecorder(QSharedPointer me) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + auto core = CoreModel::getInstance()->getCore(); + std::shared_ptr params = core->createRecorderParams(); + params->setFileFormat(linphone::MediaFileFormat::Mkv); + params->setVideoCodec(""); + auto recorder = core->createRecorder(params); + if (recorder) { + mDuration = recorder->getDuration(); + mCaptureVolume = recorder->getCaptureVolume(); + if (mRecorderModelConnection) mRecorderModelConnection->disconnect(); + mRecorderModel = Utils::makeQObject_ptr(recorder); + mRecorderModelConnection = SafeConnection::create(me, mRecorderModel); + mRecorderModelConnection->makeConnectToCore(&RecorderCore::lStart, [this] { + mRecorderModelConnection->invokeToModel([this] { mRecorderModel->start(); }); + }); + mRecorderModelConnection->makeConnectToCore(&RecorderCore::lPause, [this] { + mRecorderModelConnection->invokeToModel([this] { mRecorderModel->pause(); }); + }); + mRecorderModelConnection->makeConnectToCore(&RecorderCore::lStop, [this] { + mRecorderModelConnection->invokeToModel([this] { mRecorderModel->stop(); }); + }); + mRecorderModelConnection->makeConnectToCore(&RecorderCore::lRefresh, [this] { + mRecorderModelConnection->invokeToModel([this] { + auto duration = mRecorderModel->getDuration(); + auto volume = mRecorderModel->getCaptureVolume(); + mRecorderModelConnection->invokeToModel([this, duration, volume] { + setDuration(duration); + setCaptureVolume(volume); + }); + }); + }); + mRecorderModelConnection->makeConnectToModel(&RecorderModel::stateChanged, [this] { + auto state = LinphoneEnums::fromLinphone(mRecorderModel->getState()); + mRecorderModelConnection->invokeToCore([this, state] { setState(state); }); + }); + mRecorderModelConnection->makeConnectToModel(&RecorderModel::fileChanged, [this] { + auto file = mRecorderModel->getFile(); + mRecorderModelConnection->invokeToCore([this, file] { setFile(file); }); + }); + mRecorderModelConnection->makeConnectToModel(&RecorderModel::errorChanged, [this](QString error) { + mRecorderModelConnection->invokeToCore([this, error] { emit errorChanged(error); }); + }); + emit ready(); + } +} + +void RecorderCore::setSelf(QSharedPointer me) { + auto coreModel = CoreModel::getInstance(); + + mCoreModelConnection = SafeConnection::create(me, coreModel); + mCoreModelConnection->invokeToModel([this, me, coreModel] { buildRecorder(me); }); +} + +void RecorderCore::setCaptureVolume(float volume) { + if (mCaptureVolume != volume) { + mCaptureVolume = volume; + emit captureVolumeChanged(); + } +} + +int RecorderCore::getDuration() const { + return mDuration; +} + +void RecorderCore::setDuration(int duration) { + if (mDuration != duration) { + mDuration = duration; + emit durationChanged(); + } +} + +float RecorderCore::getCaptureVolume() const { + return mCaptureVolume; +} + +LinphoneEnums::RecorderState RecorderCore::getState() const { + return mState; +} + +void RecorderCore::setState(LinphoneEnums::RecorderState state) { + if (mState != state) { + mState = state; + emit stateChanged(state); + } +} + +QString RecorderCore::getFile() const { + return mFile; +} + +void RecorderCore::setFile(QString file) { + if (mFile != file) { + mFile = file; + emit fileChanged(); + } +} + +const std::shared_ptr &RecorderCore::getModel() const { + return mRecorderModel; +} diff --git a/Linphone/core/recorder/RecorderCore.hpp b/Linphone/core/recorder/RecorderCore.hpp new file mode 100644 index 000000000..87b68a437 --- /dev/null +++ b/Linphone/core/recorder/RecorderCore.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef RECORDER_CORE_H +#define RECORDER_CORE_H + +#include "model/recorder/RecorderModel.hpp" +#include "tool/LinphoneEnums.hpp" +#include "tool/thread/SafeConnection.hpp" +#include + +// ============================================================================= + +class RecorderCore : public QObject, public AbstractObject { + Q_OBJECT + +public: + static QSharedPointer create(QObject *parent = nullptr); + RecorderCore(QObject *parent = nullptr); + ~RecorderCore(); + void setSelf(QSharedPointer me); + + Q_PROPERTY(LinphoneEnums::RecorderState state READ getState NOTIFY stateChanged) + Q_PROPERTY(QString file READ getFile NOTIFY fileChanged) + Q_PROPERTY(int duration READ getDuration NOTIFY durationChanged) + Q_PROPERTY(int captureVolume READ getCaptureVolume NOTIFY captureVolumeChanged) + + void buildRecorder(QSharedPointer me); + + int getDuration() const; + void setDuration(int duration); + float getCaptureVolume() const; + void setCaptureVolume(float volume); + LinphoneEnums::RecorderState getState() const; + void setState(LinphoneEnums::RecorderState state); + QString getFile() const; + void setFile(QString file); + const std::shared_ptr &getModel() const; + +signals: + void lStart(); + void lPause(); + void lStop(); + void lRefresh(); + + void stateChanged(LinphoneEnums::RecorderState state); + void fileChanged(); + void durationChanged(); + void captureVolumeChanged(); + void errorChanged(QString error); + void ready(); + +private: + DECLARE_ABSTRACT_OBJECT + + std::shared_ptr mRecorderModel; + QString mFile; + LinphoneEnums::RecorderState mState; + int mDuration = 0; + int mCaptureVolume = 0; + bool mIsReady = false; + QSharedPointer> mRecorderModelConnection; + QSharedPointer> mCoreModelConnection; +}; +#endif diff --git a/Linphone/core/recorder/RecorderGui.cpp b/Linphone/core/recorder/RecorderGui.cpp new file mode 100644 index 000000000..f96017db7 --- /dev/null +++ b/Linphone/core/recorder/RecorderGui.cpp @@ -0,0 +1,49 @@ +/* + * 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 . + */ + +#include "RecorderGui.hpp" +#include "core/App.hpp" + +DEFINE_ABSTRACT_OBJECT(RecorderGui) + +RecorderGui::RecorderGui(QObject *parent) : QObject(parent) { + mustBeInMainThread(getClassName()); + mCore = RecorderCore::create(); + if (mCore) connect(mCore.get(), &RecorderCore::errorChanged, this, &RecorderGui::errorChanged); + if (mCore) connect(mCore.get(), &RecorderCore::stateChanged, this, &RecorderGui::stateChanged); + if (mCore) connect(mCore.get(), &RecorderCore::ready, this, &RecorderGui::ready); +} +RecorderGui::RecorderGui(QSharedPointer core) { + App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); + mCore = core; + if (isInLinphoneThread()) moveToThread(App::getInstance()->thread()); +} + +LinphoneEnums::RecorderState RecorderGui::getState() const { + return mCore ? mCore->getState() : LinphoneEnums::RecorderState::Closed; +} + +RecorderGui::~RecorderGui() { + mustBeInMainThread("~" + getClassName()); +} + +RecorderCore *RecorderGui::getCore() const { + return mCore.get(); +} \ No newline at end of file diff --git a/Linphone/core/recorder/RecorderGui.hpp b/Linphone/core/recorder/RecorderGui.hpp new file mode 100644 index 000000000..e33f8d8a2 --- /dev/null +++ b/Linphone/core/recorder/RecorderGui.hpp @@ -0,0 +1,52 @@ +/* + * 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 . + */ + +#ifndef RECORDER_GUI_H_ +#define RECORDER_GUI_H_ + +#include "RecorderCore.hpp" +#include "tool/AbstractObject.hpp" + +#include +#include + +class RecorderGui : public QObject, public AbstractObject { + Q_OBJECT + + Q_PROPERTY(RecorderCore *core READ getCore CONSTANT) + +public: + RecorderGui(QObject *parent = nullptr); + RecorderGui(QSharedPointer core); + ~RecorderGui(); + RecorderCore *getCore() const; + LinphoneEnums::RecorderState getState() const; + QSharedPointer mCore; + +signals: + void errorChanged(QString error); + void ready(); + void stateChanged(LinphoneEnums::RecorderState state); + +private: + DECLARE_ABSTRACT_OBJECT +}; + +#endif diff --git a/Linphone/core/setting/SettingsCore.cpp b/Linphone/core/setting/SettingsCore.cpp index 9c79f37af..e36657716 100644 --- a/Linphone/core/setting/SettingsCore.cpp +++ b/Linphone/core/setting/SettingsCore.cpp @@ -49,6 +49,7 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) { // Call mVideoEnabled = settingsModel->getVideoEnabled(); mEchoCancellationEnabled = settingsModel->getEchoCancellationEnabled(); + mAutoDownloadReceivedFiles = settingsModel->getAutoDownloadReceivedFiles(); mAutomaticallyRecordCallsEnabled = settingsModel->getAutomaticallyRecordCallsEnabled(); // Audio @@ -143,6 +144,7 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) { // Call mVideoEnabled = settingsCore.mVideoEnabled; mEchoCancellationEnabled = settingsCore.mEchoCancellationEnabled; + mAutoDownloadReceivedFiles = settingsCore.mAutoDownloadReceivedFiles; mAutomaticallyRecordCallsEnabled = settingsCore.mAutomaticallyRecordCallsEnabled; // Audio @@ -233,6 +235,12 @@ void SettingsCore::setSelf(QSharedPointer me) { mSettingsModelConnection->invokeToCore([this, enabled]() { setEchoCancellationEnabled(enabled); }); }); + // Auto download incoming files + mSettingsModelConnection->makeConnectToModel( + &SettingsModel::autoDownloadReceivedFilesChanged, [this](const bool enabled) { + mSettingsModelConnection->invokeToCore([this, enabled]() { setAutoDownloadReceivedFiles(enabled); }); + }); + // Auto recording mSettingsModelConnection->makeConnectToModel( &SettingsModel::automaticallyRecordCallsEnabledChanged, [this](const bool enabled) { @@ -462,6 +470,7 @@ void SettingsCore::reset(const SettingsCore &settingsCore) { setEchoCancellationEnabled(settingsCore.mEchoCancellationEnabled); setAutomaticallyRecordCallsEnabled(settingsCore.mAutomaticallyRecordCallsEnabled); + setAutoDownloadReceivedFiles(settingsCore.mAutoDownloadReceivedFiles); // Audio setCaptureDevices(settingsCore.mCaptureDevices); setPlaybackDevices(settingsCore.mPlaybackDevices); @@ -576,6 +585,14 @@ void SettingsCore::setEchoCancellationEnabled(bool enabled) { } } +void SettingsCore::setAutoDownloadReceivedFiles(bool enabled) { + if (mAutoDownloadReceivedFiles != enabled) { + mAutoDownloadReceivedFiles = enabled; + emit autoDownloadReceivedFilesChanged(); + setIsSaved(false); + } +} + void SettingsCore::setAutomaticallyRecordCallsEnabled(bool enabled) { if (mAutomaticallyRecordCallsEnabled != enabled) { mAutomaticallyRecordCallsEnabled = enabled; @@ -960,6 +977,9 @@ void SettingsCore::writeIntoModel(std::shared_ptr model) const { model->setEchoCancellationEnabled(mEchoCancellationEnabled); model->setAutomaticallyRecordCallsEnabled(mAutomaticallyRecordCallsEnabled); + // Chat + model->setAutoDownloadReceivedFiles(mAutoDownloadReceivedFiles); + // Audio model->setRingerDevice(mRingerDevice); model->setCaptureDevice(mCaptureDevice); @@ -1022,6 +1042,9 @@ void SettingsCore::writeFromModel(const std::shared_ptr &model) { mEchoCancellationEnabled = model->getEchoCancellationEnabled(); mAutomaticallyRecordCallsEnabled = model->getAutomaticallyRecordCallsEnabled(); + // Chat + mAutoDownloadReceivedFiles = model->getAutoDownloadReceivedFiles(); + // Audio mCaptureDevices = model->getCaptureDevices(); mPlaybackDevices = model->getPlaybackDevices(); diff --git a/Linphone/core/setting/SettingsCore.hpp b/Linphone/core/setting/SettingsCore.hpp index 20af22833..aa9401372 100644 --- a/Linphone/core/setting/SettingsCore.hpp +++ b/Linphone/core/setting/SettingsCore.hpp @@ -40,6 +40,8 @@ public: Q_PROPERTY(bool videoEnabled READ getVideoEnabled WRITE setVideoEnabled NOTIFY videoEnabledChanged) Q_PROPERTY(bool echoCancellationEnabled READ getEchoCancellationEnabled WRITE setEchoCancellationEnabled NOTIFY echoCancellationEnabledChanged) + Q_PROPERTY(bool autoDownloadReceivedFiles READ getAutoDownloadReceivedFiles WRITE setAutoDownloadReceivedFiles + NOTIFY autoDownloadReceivedFilesChanged) Q_PROPERTY( int echoCancellationCalibration READ getEchoCancellationCalibration NOTIFY echoCancellationCalibrationChanged) Q_PROPERTY(bool automaticallyRecordCallsEnabled READ getAutomaticallyRecordCallsEnabled WRITE @@ -127,6 +129,11 @@ public: } void setEchoCancellationEnabled(bool enabled); + bool getAutoDownloadReceivedFiles() { + return mAutoDownloadReceivedFiles; + } + void setAutoDownloadReceivedFiles(bool enabled); + bool getAutomaticallyRecordCallsEnabled() { return mAutomaticallyRecordCallsEnabled; } @@ -248,6 +255,7 @@ signals: void videoEnabledChanged(); void echoCancellationEnabledChanged(); + void autoDownloadReceivedFilesChanged(); void automaticallyRecordCallsEnabledChanged(); @@ -327,6 +335,7 @@ private: // Call bool mVideoEnabled; bool mEchoCancellationEnabled; + bool mAutoDownloadReceivedFiles; bool mAutomaticallyRecordCallsEnabled; // Audio diff --git a/Linphone/core/sound-player/SoundPlayerCore.cpp b/Linphone/core/sound-player/SoundPlayerCore.cpp index f5b0829bb..1115d35df 100644 --- a/Linphone/core/sound-player/SoundPlayerCore.cpp +++ b/Linphone/core/sound-player/SoundPlayerCore.cpp @@ -29,14 +29,6 @@ DEFINE_ABSTRACT_OBJECT(SoundPlayerCore) -// ============================================================================= - -using namespace std; - -namespace { -int ForceCloseTimerInterval = 20; -} - // ----------------------------------------------------------------------------- QSharedPointer SoundPlayerCore::create() { @@ -114,7 +106,7 @@ void SoundPlayerCore::buildInternalPlayer(QSharedPointer me) { mSoundPlayerModelConnection->invokeToModel([this] { mSoundPlayerModel->play(mSource); }); }); mSoundPlayerModelConnection->makeConnectToCore(&SoundPlayerCore::lSeek, [this](int offset) { - mSoundPlayerModelConnection->invokeToModel([this, offset] { mSoundPlayerModel->seek(offset); }); + mSoundPlayerModelConnection->invokeToModel([this, offset] { mSoundPlayerModel->seek(mSource, offset); }); }); mSoundPlayerModelConnection->makeConnectToModel(&SoundPlayerModel::positionChanged, [this](int pos) { mSoundPlayerModelConnection->invokeToCore([this, pos] { setPosition(pos); }); @@ -126,12 +118,15 @@ void SoundPlayerCore::buildInternalPlayer(QSharedPointer me) { }); }); mSoundPlayerModelConnection->makeConnectToModel(&SoundPlayerModel::eofReached, - [this](const shared_ptr &player) { + [this](const std::shared_ptr &player) { mSoundPlayerModelConnection->invokeToCore([this] { mForceClose = true; handleEof(); }); }); + mSoundPlayerModelConnection->makeConnectToModel(&SoundPlayerModel::errorChanged, [this](QString error) { + mSoundPlayerModelConnection->invokeToCore([this, error] { setError(error); }); + }); } // ----------------------------------------------------------------------------- diff --git a/Linphone/core/sound-player/SoundPlayerGui.cpp b/Linphone/core/sound-player/SoundPlayerGui.cpp index c4a7a11b7..6761a4ccc 100644 --- a/Linphone/core/sound-player/SoundPlayerGui.cpp +++ b/Linphone/core/sound-player/SoundPlayerGui.cpp @@ -29,6 +29,7 @@ SoundPlayerGui::SoundPlayerGui(QObject *parent) : QObject(parent) { if (mCore) connect(mCore.get(), &SoundPlayerCore::sourceChanged, this, &SoundPlayerGui::sourceChanged); if (mCore) connect(mCore.get(), &SoundPlayerCore::stopped, this, &SoundPlayerGui::stopped); if (mCore) connect(mCore.get(), &SoundPlayerCore::positionChanged, this, &SoundPlayerGui::positionChanged); + if (mCore) connect(mCore.get(), &SoundPlayerCore::errorChanged, this, &SoundPlayerGui::errorChanged); } SoundPlayerGui::SoundPlayerGui(QSharedPointer core) { App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership); diff --git a/Linphone/core/sound-player/SoundPlayerGui.hpp b/Linphone/core/sound-player/SoundPlayerGui.hpp index 7bf4f5a23..c93e9ab9b 100644 --- a/Linphone/core/sound-player/SoundPlayerGui.hpp +++ b/Linphone/core/sound-player/SoundPlayerGui.hpp @@ -44,6 +44,7 @@ signals: void sourceChanged(); void stopped(); void positionChanged(); + void errorChanged(QString error); private: DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/data/emoji/emoji.json b/Linphone/data/emoji/emoji.json index 8c99813d9..b4203bae4 100644 --- a/Linphone/data/emoji/emoji.json +++ b/Linphone/data/emoji/emoji.json @@ -823,6 +823,19 @@ "puppy eyes" ] }, + { + "code": "1f979", + "char": "🥹", + "name": "face holding back tears", + "keywords": [ + "tears", + "emotive", + "admiration", + "face with tears", + "gratitude", + "admiration" + ] + }, { "code": "1f626", "char": "😦", diff --git a/Linphone/data/emoji/emojiSvgs/1f979.svg b/Linphone/data/emoji/emojiSvgs/1f979.svg new file mode 100644 index 000000000..3cc2c8c11 --- /dev/null +++ b/Linphone/data/emoji/emojiSvgs/1f979.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Linphone/data/languages/de.ts b/Linphone/data/languages/de.ts index f744df0cc..9519f4e0a 100644 --- a/Linphone/data/languages/de.ts +++ b/Linphone/data/languages/de.ts @@ -523,74 +523,74 @@ App - + remote_provisioning_dialog Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? Möchten Sie die Remote-Konfiguration von dieser Adresse herunterladen und anwenden? - + application_description "A free and open source SIP video-phone." Ein kostenloses Open-Source SIP Video-Telefon. - + command_line_arg_order "Send an order to the application towards a command line" Kommandozeilen-Befehl an die Anwendung schicken - + command_line_option_show_help Zeige Hilfe - + command_line_option_show_app_version Zeige App-Version - + command_line_option_config_to_fetch "Specify the linphone configuration file to be fetched. It will be merged with the current configuration." Abzurufende Linphone-Konfigurationsdatei angeben. Sie wird mit der aktuellen Konfiguration zusammengeführt. - + command_line_option_config_to_fetch_arg "URL, path or file" URL, Pfad oder Datei - + command_line_option_minimized - + command_line_option_log_to_stdout Debug-Informationen auf der Standardausgabe ausgeben - + command_line_option_print_app_logs_only "Print only logs from the application" Nur Anwendungs-Logs ausgeben - + hide_action "Cacher" "Afficher" Ausblenden - + show_action Zeigen - + quit_action "Quitter" Beenden @@ -809,25 +809,25 @@ Offline - + meeting_info_join_title "Rejoindre la réunion" Besprechung beitreten - + contact_call_action "Appel" Anrufen - + contact_message_action "Message" Nachricht - + contact_video_call_action "Appel Video" Videoanruf @@ -1709,16 +1709,32 @@ Nur Audio + + ChatAudioContent + + + + information_popup_error_title + Error + + + + + information_popup_voice_message_error_message + Failed to create voice message : error in recorder + + + ChatCore - + info_toast_deleted_title Deleted - + info_toast_deleted_message_history Message history has been deleted @@ -1727,41 +1743,47 @@ ChatDroppableTextArea - + chat_view_send_area_placeholder_text Say something… : placeholder text for sending message text area + + + cannot_record_while_in_call_tooltip + Cannot record a message while a call is ongoing + + ChatListView - + chat_message_is_writing_info %1 is writing… - + chat_message_draft_sending_text - + chat_room_delete "Supprimer" - + chat_list_delete_chat_popup_title - Delete the chat ? + Delete the conversation ? - + chat_list_delete_chat_popup_message - This chat and all its messages will be deleted. Do You want to continue ? + This conversation and all its messages will be deleted. Do You want to continue ? @@ -1840,7 +1862,7 @@ Error popup_error_max_files_count_message - You can send 12 files maximum at a time. %1 files were ignored + You can send 12 files maximum at a time. %n files were ignored @@ -1882,13 +1904,13 @@ Error ChatMessageCore - + info_toast_deleted_title Deleted - + info_toast_deleted_message The message has been deleted @@ -2047,6 +2069,27 @@ Error Sie sind nicht verbunden + + ChatSettingsLayout + + + settings_chat_attached_files_title + Attached files + + + + + settings_chat_attached_files_auto_download_title + "Automatic download" + + + + + settings_chat_attached_files_auto_download_subtitle + "Automatically download transferred or received files in conversations" + + + CliModel @@ -2957,6 +3000,153 @@ Error Ungültige Adresse + + GroupChatInfoParticipants + + + group_infos_participant_is_admin + Admin + + + + group_infos_add_participants_title + "Ajouter des participants" + Add Participants + + + + menu_see_existing_contact + "Show contact" + Kontakt anzeigen + + + + menu_add_address_to_contacts + "Add to contacts" + Zu Kontakten hinzufügen + + + + group_infos_give_admin_rights + Give admin rights + + + + group_infos_remove_admin_rights + Remove admin rights + + + + group_infos_copy_sip_address + Copy SIP Address + + + + group_infos_remove_participant + Remove participant + + + + group_infos_remove_participants_toast_title + Remove participant ? + + + + group_infos_remove_participants_toast_message + Participant will be removed from chat room. + + + + GroupConversationInfos + + + group_infos_call + "Appel" + Anrufen + + + + group_infos_mute + "Sourdine" + Stummschalten + + + + group_infos_meeting + "Réunion" + Meeting + + + + group_infos_participants + Participants (%1) + + + + + group_infos_media_docs + Medien & Dokumente + + + group_infos_shared_media + Shared medias + Geteilte Medien + + + + group_infos_shared_docs + Geteilte Dokumente + + + + group_infos_other_actions + Weitere Aktionen + + + + group_infos_enable_ephemerals + Flüchtige Nachrichten aktivieren + + + + group_infos_disable_ephemerals + Flüchtige Nachrichten deaktivieren + + + + group_infos_leave_room + + + + + group_infos_delete_history + Verlauf löschen + + + + group_infos_delete_history_toast_title + Delete history ? + Verlauf löschen? + + + + group_infos_delete_history_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + Alle Nachrichten werden aus dem Chat entfernt. Möchten Sie fortfahren? + + + + group_infos_leave_room_toast_title + Leave Chat Room ? + Chatraum verlassen? + + + + group_infos_leave_room_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + Alle Nachrichten werden aus dem Chat entfernt. Möchten Sie fortfahren? + + GroupCreationFormLayout @@ -3447,60 +3637,60 @@ Error interoperabel - + call_transfer_successful_toast_title "Appel transféré" Anruf weitergeleitet - + call_transfer_successful_toast_message "Votre correspondant a été transféré au contact sélectionné" Ihr Gesprächspartner wurde an den ausgewählten Kontakt weitergeleitet - + information_popup_success_title Gespeichert - + information_popup_changes_saved "Les changements ont été sauvegardés" Änderungen wurden gespeichert - + captcha_validation_loading_message "Veuillez valider le captcha sur la page web" Bitte das Captcha auf der Webseite bestätigen - + assistant_register_error_title "Erreur lors de la création" Fehler bei der Erstellung - + assistant_register_success_title "Compte créé" Konto erstellt - + assistant_register_success_message "Le compte a été créé. Vous pouvez maintenant vous connecter" Das Konto wurde erstellt. Sie können sich jetzt anmelden. - + assistant_register_error_code "Erreur dans le code de validation" Fehler im Bestätigungscode - + information_popup_error_title Fehler @@ -3902,7 +4092,7 @@ Error - + new_chat_room_messages 'New messages received!' Notification that warn the user of new messages. @@ -4023,6 +4213,84 @@ Error + + OneOneConversationInfos + + + one_one_infos_call + "Appel" + Anrufen + + + + one_one_infos_mute + "Sourdine" + Stummschalten + + + + one_one_infos_search + "Rechercher" + Suchen + + + + one_one_infos_media_docs + Medien & Dokumente + + + + one_one_infos_shared_media + Geteilte Medien + + + + one_one_infos_shared_docs + Geteilte Dokumente + + + + one_one_infos_other_actions + Weitere Aktionen + + + + one_one_infos_enable_ephemerals + Flüchtige Nachrichten aktivieren + + + + one_one_infos_disable_ephemerals + Flüchtige Nachrichten deaktivieren + + + + one_one_infos_delete_history + Verlauf löschen + + + + one_one_infos_delete_history_toast_title + Delete history ? + Verlauf löschen? + + + + one_one_infos_delete_history_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + Alle Nachrichten werden aus dem Chat entfernt. Möchten Sie fortfahren? + + + + one_one_infos_open_contact + Kontakt öffnen + + + + one_one_infos_create_contact + Kontakt erstellen + + ParticipantListView @@ -4102,52 +4370,52 @@ Error SRTP - + media_encryption_dtls - + media_encryption_none - + media_encryption_srtp - + media_encryption_post_quantum "ZRTP - Post quantique" Post-quantum ZRTP - + incoming "Entrant" Eingehend - + outgoing "Sortant" Ausgehend - + conference_layout_active_speaker "Participant actif" Aktiver Sprecher - + conference_layout_grid "Mosaïque" Raster - + conference_layout_audio_only "Audio uniquement" Nur Audio @@ -4500,16 +4768,10 @@ Pour les activer dans un projet commercial, merci de nous contacter. SelectedChatView - - chat_view_group_call_toast_message - "Start a group call ?" - Start a group call ? - - - chat_view_send_area_placeholder_text - Say something… : placeholder text for sending message text area - + + chat_view_group_call_toast_message + Start a group call ? @@ -4649,16 +4911,16 @@ Pour les activer dans un projet commercial, merci de nous contacter. Utils - + information_popup_call_not_created_message "L'appel n'a pas pu être créé" Anruf konnte nicht erstellt werden - - - - + + + + information_popup_error_title Error ---------- @@ -4666,12 +4928,12 @@ Failed to create 1-1 conversation with %1 ! - + information_popup_group_call_not_created_message - + number_of_years %n an(s) @@ -4680,7 +4942,7 @@ Failed to create 1-1 conversation with %1 ! - + number_of_month "%n mois" @@ -4689,7 +4951,7 @@ Failed to create 1-1 conversation with %1 ! - + number_of_weeks %n semaine(s) @@ -4698,7 +4960,7 @@ Failed to create 1-1 conversation with %1 ! - + number_of_days %n jour(s) @@ -4707,55 +4969,85 @@ Failed to create 1-1 conversation with %1 ! - + today "Aujourd'hui" Heute - + yesterday "Hier Gestern - + call_zrtp_token_verification_possible_characters "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 - - + + information_popup_chatroom_creation_error_message Failed to create 1-1 conversation with %1 ! - + contact_presence_status_available - + contact_presence_status_away - + contact_presence_status_busy Beschäftigt - + contact_presence_status_do_not_disturb Nicht stören - + contact_presence_status_offline Offline + + + recorder_error + Error with the recorder + + + + + chat_error + + + + + + info_popup_error_title + Error + + + + + info_popup_send_voice_message_error_message + Could not send voice message : %1 + + + + + info_popup_send_voice_message_sending_error_message + Failed to create message from record + + WaitingRoom @@ -4919,1117 +5211,1117 @@ Failed to create 1-1 conversation with %1 ! country - + Afghanistan Afghanistan - + Albania Albanien - + Algeria Algerien - + AmericanSamoa Amerikanisch-Samoa - + Andorra Andorra - + Angola Angola - + Anguilla Anguilla - + AntiguaAndBarbuda Antigua und Barbuda - + Argentina Argentinien - + Armenia Armenien - + Aruba Aruba - + Australia Australien - + Austria Österreich - + Azerbaijan Aserbaidschan - + Bahamas Bahamas - + Bahrain Bahrain - + Bangladesh Bangladesch - + Barbados Barbados - + Belarus Belarus - + Belgium Belgien - + Belize Belize - + Benin Benin - + Bermuda Bermuda - + Bhutan Bhutan - + Bolivia Bolivien - + BosniaAndHerzegowina Bosnien und Herzegowina - + Botswana Botswana - + Brazil Brasilien - + Brunei Brunei - + Bulgaria Bulgarien - + BurkinaFaso Burkina Faso - + Burundi Burundi - + Cambodia Kambodscha - + Cameroon Kamerun - + Canada Kanada - + CapeVerde Kap Verde - + CaymanIslands Caymaninseln - + CentralAfricanRepublic Zentralafrikanische Republik - + Chad Tschad - + Chile Chile - + China China - + Colombia Kolumbien - + Comoros Komoren - + PeoplesRepublicOfCongo Volksrepublik Kongo - + CookIslands Cookinseln - + CostaRica Kosta Rica - + IvoryCoast Elfenbeinküste - + Croatia Kroatien - + Cuba Kuba - + Cyprus Zypern - + CzechRepublic Tschechische Republik - + Denmark Dänemark - + Djibouti Dschibuti - + Dominica Dominica - + DominicanRepublic Dominikanische Republik - + Ecuador Ecuador - + Egypt Ägypten - + ElSalvador El Salvador - + EquatorialGuinea Äquatorialguinea - + Eritrea Eritrea - + Estonia Estland - + Ethiopia Äthiopien - + FalklandIslands Falklandinseln - + FaroeIslands Färöer-Inseln - + Fiji Fidschi - + Finland Finnland - + France Frankreich - + FrenchGuiana Französisch-Guayana - + FrenchPolynesia Französisch-Polynesien - + Gabon Gabon - + Gambia Gambia - + Georgia Georgien - + Germany Deutschland - + Ghana Ghana - + Gibraltar Gibraltar - + Greece Griechenland - + Greenland Grönland - + Grenada Grenada - + Guadeloupe Guadeloupe - + Guam Guam - + Guatemala Guatemala - + Guinea Guinea - + GuineaBissau Guinea-Bissau - + Guyana Guyana - + Haiti Haiti - + Honduras Honduras - + DemocraticRepublicOfCongo Demokratische Republik Kongo - + HongKong Hongkong - + Hungary Ungarn - + Iceland Island - + India Indien - + Indonesia Indonesien - + Iran Iran - + Iraq Irak - + Ireland Irland - + Israel Israel - + Italy Italien - + Jamaica Jamaika - + Japan Japan - + Jordan Jordanien - + Kazakhstan Kasachstan - + Kenya Kenia - + Kiribati Kiribati - + DemocraticRepublicOfKorea Demokratische Volksrepublik Korea - + RepublicOfKorea Republik Korea - + Kuwait Kuwait - + Kyrgyzstan Kirgisistan - + Laos Laos - + Latvia Lettland - + Lebanon Libanon - + Lesotho Lesotho - + Liberia Liberien - + Libya Libyen - + Liechtenstein Liechtenstein - + Lithuania Litauen - + Luxembourg Luxemburg - + Macau Macau - + Macedonia Mazedonien - + Madagascar Madagaskar - + Malawi Malawi - + Malaysia Malaysien - + Maldives Malediven - + Mali Mali - + Malta Malta - + MarshallIslands Marshallinseln - + Martinique Martinique - + Mauritania Mauretanien - + Mauritius Mauritius - + Mayotte Mayotte - + Mexico Mexiko - + Micronesia Föderierte Staaten von Mikronesien - + Moldova Moldawien - + Monaco Monaco - + Mongolia Mongolei - + Montenegro Montenegro - + Montserrat Montserrat - + Morocco Marokko - + Mozambique Mosambik - + Myanmar Myanmar - + Namibia Namibia - + NauruCountry Nauru - + Nepal Nepal - + Netherlands Niederlande - + NewCaledonia Neukaledonien - + NewZealand Neuseeland - + Nicaragua Nicaragua - + Niger Niger - + Nigeria Nigeria - + Niue Niue - + NorfolkIsland Norfolkinsel - + NorthernMarianaIslands Nördliche Marianeninseln - + Norway Norwegen - + Oman Oman - + Pakistan Pakistan - + Palau Palau - + PalestinianTerritories Palästinensische Gebiete - + Panama Panama - + PapuaNewGuinea Papua-Neuguinea - + Paraguay Paraguay - + Peru Peru - + Philippines Philippinen - + Poland Polen - + Portugal Portugal - + PuertoRico Puerto Rico - + Qatar Katar - + Reunion Réunion - + Romania Rumänien - + RussianFederation Russische Föderation - + Rwanda Ruanda - + SaintHelena Sankt Helena - + SaintKittsAndNevis Sankt Kitts und Nevis - + SaintLucia Sankt Lucia - + SaintPierreAndMiquelon Sankt Pierre und Miquelon - + SaintVincentAndTheGrenadines Sankt Vincent und die Grenadinen - + Samoa Samoa - + SanMarino San Marino - + SaoTomeAndPrincipe São Tomé und Príncipe - + SaudiArabia Saudi-Arabien - + Senegal Senegal - + Serbia Serbien - + Seychelles Seychellen - + SierraLeone Sierra Leone - + Singapore Singapur - + Slovakia Slowakei - + Slovenia Slowenien - + SolomonIslands Salomonen - + Somalia Somalia - + SouthAfrica Südafrika - + Spain Spanien - + SriLanka Sri Lanka - + Sudan Sudan - + Suriname Suriname - + Swaziland Eswatini - + Sweden Schweden - + Switzerland Schweiz - + Syria Syrien - + Taiwan Taiwan - + Tajikistan Tadschikistan - + Tanzania Tansania - + Thailand Thailand - + Togo Togo - + Tokelau Tokelau - + Tonga Tonga - + TrinidadAndTobago Trinidad und Tobago - + Tunisia Tunesien - + Turkey Türkei - + Turkmenistan Turkmenistan - + TurksAndCaicosIslands Turks- und Caicosinseln - + Tuvalu Tuvalu - + Uganda Uganda - + Ukraine Ukraine - + UnitedArabEmirates Vereinigte Arabische Emirate - + UnitedKingdom Vereinigtes Königreich - + UnitedStates Vereinigte Staaten - + Uruguay Uruguay - + Uzbekistan Usbekistan - + Vanuatu Vanuatu - + Venezuela Venezuela - + Vietnam Vietnam - + WallisAndFutunaIslands Wallis und Futuna Inseln - + Yemen Jemen - + Zambia Sambia - + Zimbabwe Simbabwe @@ -6160,209 +6452,4 @@ Failed to create 1-1 conversation with %1 ! Ok - - OneOneConversationInfos - - one_one_infos_call - Call - Anrufen - - - one_one_infos_mute - Mute - Stummschalten - - - one_one_infos_search - Search - Suchen - - - one_one_infos_media_docs - Medias & documents - Medien & Dokumente - - - one_one_infos_shared_media - Shared medias - Geteilte Medien - - - one_one_infos_shared_docs - Shared documents - Geteilte Dokumente - - - one_one_infos_other_actions - Other actions - Weitere Aktionen - - - one_one_infos_enable_ephemerals - Enable ephemeral messages - Flüchtige Nachrichten aktivieren - - - one_one_infos_disable_ephemerals - Disable ephemeral messages - Flüchtige Nachrichten deaktivieren - - - one_one_infos_delete_history - Delete history - Verlauf löschen - - - one_one_infos_delete_history_toast_title - Delete history ? - Verlauf löschen? - - - one_one_infos_delete_history_toast_message - All the messages will be removed from the chat room. Do you want to continue ? - Alle Nachrichten werden aus dem Chat entfernt. Möchten Sie fortfahren? - - - one_one_infos_open_contact - Open contact - Kontakt öffnen - - - one_one_infos_create_contact - Create contact - Kontakt erstellen - - - - GroupConversationInfos - - group_infos_call - Call - Anrufen - - - group_infos_mute - Mute - Stummschalten - - - group_infos_meeting - Meeting - Meeting - - - group_infos_participants - Participants - Participants (%1) - - - group_infos_media_docs - Medias & documents - Medien & Dokumente - - - group_infos_shared_media - Shared medias - Geteilte Medien - - - group_infos_shared_docs - Shared documents - Geteilte Dokumente - - - group_infos_other_actions - Other actions - Weitere Aktionen - - - group_infos_enable_ephemerals - Enable ephemeral messages - Flüchtige Nachrichten aktivieren - - - group_infos_disable_ephemerals - Disable ephemeral messages - Flüchtige Nachrichten deaktivieren - - - group_infos_delete_history - Delete history - Verlauf löschen - - - group_infos_delete_history_toast_title - Delete history ? - Verlauf löschen? - - - group_infos_delete_history_toast_message - All the messages will be removed from the chat room. Do you want to continue ? - Alle Nachrichten werden aus dem Chat entfernt. Möchten Sie fortfahren? - - - group_infos_leave_room_toast_title - Leave Chat Room ? - Chatraum verlassen? - - - group_infos_leave_room_toast_message - All the messages will be removed from the chat room. Do you want to continue ? - Alle Nachrichten werden aus dem Chat entfernt. Möchten Sie fortfahren? - - - - GroupChatInfoParticipants - - group_infos_participant_is_admin - Admin - Admin - - - group_infos_add_participants_title - Add Participants - Add Participants - - - menu_see_existing_contact - "Show contact" - Kontakt anzeigen - - - menu_add_address_to_contacts - "Add to contacts" - Zu Kontakten hinzufügen - - - group_infos_give_admin_rights - "Give admin rights" - Give admin rights - - - group_infos_remove_admin_rights - "Remove admin rights" - Remove admin rights - - - group_infos_copy_sip_address - "Copy SIP Address" - Copy SIP Address - - - group_infos_remove_participant - "Remove participant" - Remove participant - - - group_infos_remove_participants_toast_title - "Remove participant ?" - Remove participant ? - - - group_infos_remove_participants_toast_message - "Participant will be removed from chat room." - Participant will be removed from chat room. - - - diff --git a/Linphone/data/languages/en.ts b/Linphone/data/languages/en.ts index a60f8b92a..c36532cff 100644 --- a/Linphone/data/languages/en.ts +++ b/Linphone/data/languages/en.ts @@ -523,74 +523,74 @@ App - + remote_provisioning_dialog Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? Do you want to download and apply remote provisioning from this address ? - + application_description "A free and open source SIP video-phone." A free and open source SIP video-phone. - + command_line_arg_order "Send an order to the application towards a command line" Send an order to the application towards a command line - + command_line_option_show_help Show this help - + command_line_option_show_app_version Show app version - + command_line_option_config_to_fetch "Specify the linphone configuration file to be fetched. It will be merged with the current configuration." Specify the linphone configuration file to be fetched. It will be merged with the current configuration. - + command_line_option_config_to_fetch_arg "URL, path or file" URL, path or file - + command_line_option_minimized Minimize - + command_line_option_log_to_stdout Log to stdout some debug information while running - + command_line_option_print_app_logs_only "Print only logs from the application" Print only logs from the application - + hide_action "Cacher" "Afficher" Hide - + show_action Show - + quit_action "Quitter" Quit @@ -789,25 +789,25 @@ CallHistoryLayout - + meeting_info_join_title "Rejoindre la réunion" Join meeting - + contact_call_action "Appel" Call - + contact_message_action "Message" Message - + contact_video_call_action "Appel Video" Video call @@ -1502,7 +1502,7 @@ call_open_chat_hint Open chat… - Open chat… + Open conversation… @@ -1671,16 +1671,32 @@ Audio only + + ChatAudioContent + + + + information_popup_error_title + Error + Error + + + + information_popup_voice_message_error_message + Failed to create voice message : error in recorder + Failed to create voice message : error in recorder + + ChatCore - + info_toast_deleted_title Deleted Deleted - + info_toast_deleted_message_history Message history has been deleted Message history has been deleted @@ -1689,42 +1705,48 @@ ChatDroppableTextArea - + chat_view_send_area_placeholder_text Say something… : placeholder text for sending message text area Say something… + + + cannot_record_while_in_call_tooltip + Cannot record a message while a call is ongoing + Cannot record a message while a call is ongoing + ChatListView - + chat_message_is_writing_info %1 is writing… %1 is writing… - + chat_message_draft_sending_text Draft : %1 - + chat_room_delete "Supprimer" Delete - + chat_list_delete_chat_popup_title - Delete the chat ? - Delete the chat ? + Delete the conversation ? + Delete the conversation ? - + chat_list_delete_chat_popup_message - This chat and all its messages will be deleted. Do You want to continue ? - This chat and all its messages will be deleted. Do You want to continue ? + This conversation and all its messages will be deleted. Do You want to continue ? + This conversation and all its messages will be deleted. Do You want to continue ? @@ -1802,7 +1824,7 @@ Error popup_error_max_files_count_message - You can send 12 files maximum at a time. %1 files were ignored + You can send 12 files maximum at a time. %n files were ignored You can send 12 files maximum at a time. %n file was ignored You can send 12 files maximum at a time. %n files were ignored @@ -1844,13 +1866,13 @@ Error ChatMessageCore - + info_toast_deleted_title Deleted Deleted - + info_toast_deleted_message The message has been deleted The message has been deleted @@ -1941,19 +1963,19 @@ Only your correspondent can decrypt them. chat_dialog_delete_chat_title Supprimer la conversation ? - Delete chat ? + Delete conversation ? chat_dialog_delete_chat_message "La conversation et tous ses messages seront supprimés." - This chat and all its messages will be deleted. + This conversation and all its messages will be deleted. chat_list_title "Conversations" - Chat + Conversations @@ -1971,13 +1993,13 @@ Only your correspondent can decrypt them. chat_list_empty_history "Aucune conversation dans votre historique" - No chat in history + No conversation in history chat_action_start_new_chat "New chat" - New chat + New conversation @@ -2010,6 +2032,27 @@ Only your correspondent can decrypt them. You are not connected + + ChatSettingsLayout + + + settings_chat_attached_files_title + Attached files + Attached files + + + + settings_chat_attached_files_auto_download_title + "Automatic download" + Automatic download + + + + settings_chat_attached_files_auto_download_subtitle + "Automatically download transferred or received files in conversations" + Automatically download transferred or received files in conversations + + CliModel @@ -2880,6 +2923,153 @@ Only your correspondent can decrypt them. Invalid address + + GroupChatInfoParticipants + + + group_infos_add_participants_title + "Ajouter des participants" + Add Participants + + + + group_infos_participant_is_admin + Admin + + + + menu_see_existing_contact + "Show contact" + Show contact + + + + menu_add_address_to_contacts + "Add to contacts" + Add to contacts + + + + group_infos_give_admin_rights + Give admin rights + + + + group_infos_remove_admin_rights + Remove admin rights + + + + group_infos_copy_sip_address + Copy SIP Address + + + + group_infos_remove_participant + Remove participant + + + + group_infos_remove_participants_toast_title + Remove participant ? + + + + group_infos_remove_participants_toast_message + Participant will be removed from chat room. + + + + GroupConversationInfos + + + group_infos_call + "Appel" + Call + + + + group_infos_mute + "Sourdine" + Mute + + + + group_infos_meeting + "Réunion" + Meeting + + + + group_infos_participants + Participants (%1) + + + + + group_infos_media_docs + Medias & documents + + + group_infos_shared_media + Shared medias + Shared medias + + + + group_infos_shared_docs + Shared documents + + + + group_infos_other_actions + Other actions + + + + group_infos_enable_ephemerals + Enable ephemeral messages + + + + group_infos_disable_ephemerals + Disable ephemeral messages + + + + group_infos_delete_history + Delete history + + + + group_infos_delete_history_toast_title + Delete history ? + Delete history ? + + + + group_infos_delete_history_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + All the messages will be removed from the chat room. Do you want to continue ? + + + + group_infos_leave_room + Leave Chat Room + + + + group_infos_leave_room_toast_title + Leave Chat Room ? + Leave Chat Room ? + + + + group_infos_leave_room_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + All the messages will be removed from the chat room. Do you want to continue ? + + GroupCreationFormLayout @@ -3365,60 +3555,60 @@ Only your correspondent can decrypt them. interoperable - + call_transfer_successful_toast_title "Appel transféré" Call forwarded - + call_transfer_successful_toast_message "Votre correspondant a été transféré au contact sélectionné" Your correspondent has been transferred to the selected contact - + information_popup_success_title Saved - + information_popup_changes_saved "Les changements ont été sauvegardés" Changes have been saved - + captcha_validation_loading_message "Veuillez valider le captcha sur la page web" Please validate the captcha on the web page - + assistant_register_error_title "Erreur lors de la création" Error while creating - + assistant_register_success_title "Compte créé" Account created - + assistant_register_success_message "Le compte a été créé. Vous pouvez maintenant vous connecter" The account has been created. You can now log in. - + assistant_register_error_code "Erreur dans le code de validation" Error in validation code - + information_popup_error_title Error @@ -3747,7 +3937,7 @@ Only your correspondent can decrypt them. settings_network_allow_ipv6 "Autoriser l'IPv6" - Enable l'IPv6 + Enable IPv6 @@ -3815,7 +4005,7 @@ Only your correspondent can decrypt them. Conference invitation received ! - + new_chat_room_messages 'New messages received!' Notification that warn the user of new messages. New messages received ! @@ -3936,6 +4126,84 @@ Only your correspondent can decrypt them. No token endpoint found in OpenID configuration + + OneOneConversationInfos + + + one_one_infos_call + "Appel" + Call + + + + one_one_infos_mute + "Sourdine" + Mute + + + + one_one_infos_search + "Rechercher" + Search + + + + one_one_infos_media_docs + Medias & documents + + + + one_one_infos_shared_media + Shared medias + + + + one_one_infos_shared_docs + Shared documents + + + + one_one_infos_other_actions + Other actions + + + + one_one_infos_enable_ephemerals + Enable ephemeral messages + + + + one_one_infos_disable_ephemerals + Disable ephemeral messages + + + + one_one_infos_delete_history + Delete history + + + + one_one_infos_delete_history_toast_title + Delete history ? + Delete history ? + + + + one_one_infos_delete_history_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + All the messages will be removed from the chat room. Do you want to continue ? + + + + one_one_infos_open_contact + Show contact + + + + one_one_infos_create_contact + Create contact + + ParticipantListView @@ -4003,52 +4271,52 @@ Only your correspondent can decrypt them. QObject - + media_encryption_dtls DTLS - + media_encryption_none None - + media_encryption_srtp SRTP - + media_encryption_post_quantum "ZRTP - Post quantique" Post quantum ZRTP - + incoming "Entrant" Incoming - + outgoing "Sortant" Outgoing - + conference_layout_active_speaker "Participant actif" Active speaker - + conference_layout_grid "Mosaïque" Grid - + conference_layout_audio_only "Audio uniquement" Audio only @@ -4401,26 +4669,10 @@ To enable them in a commercial project, please contact us. SelectedChatView - - chat_view_group_call_toast_message - "Start a group call ?" - Start a group call ? - - Dites quelque chose… - Say something… : placeholder text for sending message text area - Say something… - - - Autres actions - Other actions - Other actions - - - - chat_view_send_area_placeholder_text - Say something… : placeholder text for sending message text area - Say something… + + chat_view_group_call_toast_message + Start a group call ? @@ -4560,41 +4812,41 @@ To enable them in a commercial project, please contact us. Utils - + contact_presence_status_available Available - + contact_presence_status_busy Busy - + contact_presence_status_do_not_disturb Do not disturb - + contact_presence_status_offline Offline - + contact_presence_status_away Idle/Away - + information_popup_call_not_created_message "L'appel n'a pas pu être créé" Call could not be created - - - - + + + + information_popup_error_title Error ---------- @@ -4602,12 +4854,12 @@ Failed to create 1-1 conversation with %1 ! Error - + information_popup_group_call_not_created_message Group call couldn't be created - + number_of_years %n an(s) @@ -4616,7 +4868,7 @@ Failed to create 1-1 conversation with %1 ! - + number_of_month "%n mois" @@ -4625,7 +4877,7 @@ Failed to create 1-1 conversation with %1 ! - + number_of_weeks %n semaine(s) @@ -4634,7 +4886,7 @@ Failed to create 1-1 conversation with %1 ! - + number_of_days %n jour(s) @@ -4643,30 +4895,60 @@ Failed to create 1-1 conversation with %1 ! - + today "Aujourd'hui" Today - + yesterday "Hier Yesterday - + call_zrtp_token_verification_possible_characters "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 - - + + information_popup_chatroom_creation_error_message Failed to create 1-1 conversation with %1 ! Failed to create 1-1 conversation with %1 ! + + + recorder_error + Error with the recorder + Error with the recorder + + + + chat_error + Error in the chat + + + + + info_popup_error_title + Error + Error + + + + info_popup_send_voice_message_error_message + Could not send voice message : %1 + Could not send voice message : %1 + + + + info_popup_send_voice_message_sending_error_message + Failed to create message from record + Failed to create message from record + WaitingRoom @@ -4830,1117 +5112,1117 @@ Failed to create 1-1 conversation with %1 ! country - + Afghanistan Afghanistan - + Albania Albania - + Algeria Algeria - + AmericanSamoa American Samoa - + Andorra Andorra - + Angola Angola - + Anguilla Anguilla - + AntiguaAndBarbuda Antigua-et-Barbuda - + Argentina Argentina - + Armenia Armenia - + Aruba Aruba - + Australia Australia - + Austria Austria - + Azerbaijan Azerbaijan - + Bahamas Bahamas - + Bahrain Bahrain - + Bangladesh Bangladesh - + Barbados Barbados - + Belarus Belarus - + Belgium Belgium - + Belize Belize - + Benin Benin - + Bermuda Bermuda - + Bhutan Bhutan - + Bolivia Bolivia - + BosniaAndHerzegowina Bosnia And Herzegowina - + Botswana Botswana - + Brazil Brazil - + Brunei Brunei - + Bulgaria Bulgaria - + BurkinaFaso Burkina Faso - + Burundi Burundi - + Cambodia Cambodia - + Cameroon Cameroon - + Canada Canada - + CapeVerde Cape Verde - + CaymanIslands Cayman Islands - + CentralAfricanRepublic Central African Republic - + Chad Chad - + Chile Chile - + China China - + Colombia Colombia - + Comoros Comoros - + PeoplesRepublicOfCongo Peoples Republic Of Congo - + CookIslands Cook Islands - + CostaRica Costa Rica - + IvoryCoast Ivory Coast - + Croatia Croatia - + Cuba Cuba - + Cyprus Cyprus - + CzechRepublic Czech Republic - + Denmark Denmark - + Djibouti Djibouti - + Dominica Dominica - + DominicanRepublic Dominican Republic - + Ecuador Ecuador - + Egypt Egypt - + ElSalvador El Salvador - + EquatorialGuinea Equatorial Guinea - + Eritrea Eritrea - + Estonia Estonia - + Ethiopia Ethiopia - + FalklandIslands Falkland Islands - + FaroeIslands Faroe Islands - + Fiji Fiji - + Finland Finland - + France France - + FrenchGuiana French Guiana - + FrenchPolynesia French Polynesia - + Gabon Gabon - + Gambia Gambia - + Georgia Georgia - + Germany Germany - + Ghana Ghana - + Gibraltar Gibraltar - + Greece Greece - + Greenland Greenland - + Grenada Grenada - + Guadeloupe Guadeloupe - + Guam Guam - + Guatemala Guatemala - + Guinea Guinea - + GuineaBissau Guinea-Bissau - + Guyana Guyana - + Haiti Haiti - + Honduras Honduras - + DemocraticRepublicOfCongo Democratic Republic Of Congo - + HongKong Hong Kong - + Hungary Hungary - + Iceland Iceland - + India India - + Indonesia Indonesia - + Iran Iran - + Iraq Iraq - + Ireland Ireland - + Israel Israel - + Italy Italie - + Jamaica Jamaica - + Japan Japan - + Jordan Jordan - + Kazakhstan Kazakhstan - + Kenya Kenya - + Kiribati Kiribati - + DemocraticRepublicOfKorea Democratic Republic Of Korea - + RepublicOfKorea Republic Of Korea - + Kuwait Kuwait - + Kyrgyzstan Kyrgyzstan - + Laos Laos - + Latvia Latvia - + Lebanon Lebanon - + Lesotho Lesotho - + Liberia Liberia - + Libya Libya - + Liechtenstein Liechtenstein - + Lithuania Lithuania - + Luxembourg Luxembourg - + Macau Macau - + Macedonia Macedonia - + Madagascar Madagascar - + Malawi Malawi - + Malaysia Malaysia - + Maldives Maldives - + Mali Mali - + Malta Malta - + MarshallIslands Marshall Islands - + Martinique Martinique - + Mauritania Mauritania - + Mauritius Mauritius - + Mayotte Mayotte - + Mexico Mexico - + Micronesia Micronesia - + Moldova Moldova - + Monaco Monaco - + Mongolia Mongolia - + Montenegro Montenegro - + Montserrat Montserrat - + Morocco Morocco - + Mozambique Mozambique - + Myanmar Myanmar - + Namibia Namibia - + NauruCountry Nauru Country - + Nepal Nepal - + Netherlands Netherlands - + NewCaledonia New-Caledonia - + NewZealand New-Zealand - + Nicaragua Nicaragua - + Niger Niger - + Nigeria Nigeria - + Niue Niue - + NorfolkIsland Norfolk Island - + NorthernMarianaIslands Northern Mariana Islands - + Norway Norway - + Oman Oman - + Pakistan Pakistan - + Palau Palau - + PalestinianTerritories Palestinian Territories - + Panama Panama - + PapuaNewGuinea Papua-New-Guinea - + Paraguay Paraguay - + Peru Peru - + Philippines Philippines - + Poland Poland - + Portugal Portugal - + PuertoRico Puerto Rico - + Qatar Qatar - + Reunion Reunion - + Romania Romania - + RussianFederation Russian Federation - + Rwanda Rwanda - + SaintHelena Saint-Helena - + SaintKittsAndNevis Saint-Kitts-And-Nevis - + SaintLucia Saint-Lucia - + SaintPierreAndMiquelon Saint-Pierre-And-Miquelon - + SaintVincentAndTheGrenadines Saint-Vincent And The Grenadines - + Samoa Samoa - + SanMarino San-Marino - + SaoTomeAndPrincipe Sao Tome-And-Principe - + SaudiArabia Saudi Arabia - + Senegal Senegal - + Serbia Serbia - + Seychelles Seychelles - + SierraLeone Sierra Leone - + Singapore Singapore - + Slovakia Slovakia - + Slovenia Slovenia - + SolomonIslands Solomon Islands - + Somalia Somalia - + SouthAfrica South Africa - + Spain Spain - + SriLanka Sri Lanka - + Sudan Sudan - + Suriname Suriname - + Swaziland Swaziland - + Sweden Sweden - + Switzerland Switzerland - + Syria Syria - + Taiwan Taiwan - + Tajikistan Tajikistan - + Tanzania Tanzania - + Thailand Thailand - + Togo Togo - + Tokelau Tokelau - + Tonga Tonga - + TrinidadAndTobago Trinidad-And-Tobago - + Tunisia Tunisia - + Turkey Turkey - + Turkmenistan Turkmenistan - + TurksAndCaicosIslands Turks And Caicos Islands - + Tuvalu Tuvalu - + Uganda Uganda - + Ukraine Ukraine - + UnitedArabEmirates United Arab Emirates - + UnitedKingdom United-Kingdom - + UnitedStates United-States - + Uruguay Uruguay - + Uzbekistan Uzbekistan - + Vanuatu Vanuatu - + Venezuela Venezuela - + Vietnam Vietnam - + WallisAndFutunaIslands Wallis And Futuna Islands - + Yemen Yemen - + Zambia Zambia - + Zimbabwe Zimbabwe @@ -6071,307 +6353,4 @@ Failed to create 1-1 conversation with %1 ! Ok - - EventLogCore - - conference_created_event - 'You have joined the group' : Little message to show on the event when the user join the chat group. - You have joined the group - - - conference_created_terminated - 'You have left the group' : Little message to show on the event when the user leave the chat group. - You have left the group - - - conference_participant_added_event - '%1 has joined' : Little message to show on the event when someone join the chat group. - %1 has joined - - - conference_participant_removed_event - '%1 has left' : Little message to show on the event when someone leave the chat group - %1 has left - - - conference_participant_set_admin_event - '%1 is now an admin' : Little message to show on the event when someone get the admin status. %1 is somebody - %1 is now an admin - - - conference_participant_unset_admin_event - '%1 is no longer an admin' : Little message to show on the event when somebody lost its admin status. %1 is somebody - %1 is no longer an admin - - - conference_security_event - 'Security level degraded by %1': Little message to show on the event when a security level has been lost. - Security level degraded by %1 - - - conference_ephemeral_message_enabled_event - 'Ephemeral messages have been enabled: %1' : Little message to show on the event when ephemeral has been activated. %1 is a date time - Ephemeral messages have been enabled: %1 - - - conference_ephemeral_message_disabled_event - 'Ephemeral messages have been disabled': Little message to show on the event when ephemeral has been deactivated. - Ephemeral messages have been disabled - - - conference_subject_changed_event - 'New subject : %1' : Little message to show on the event when the subject of the chat room has been changed. %1 is the new subject. - New subject: %1 - - - conference_ephemeral_message_lifetime_changed_event - 'Ephemeral messages have been updated: %1' : Little message to show on the event when ephemeral has been updated. %1 is a date time - Ephemeral messages have been updated: %1 - - - nSeconds - - %1 second - %1 seconds - - - - nMinute - - %1 minute - %1 minutes - - - - nHour - - %1 hour - %1 hours - - - - nDay - - %1 day - %1 days - - - - nWeek - - %1 week - %1 weeks - - - - - OneOneConversationInfos - - one_one_infos_call - Call - Call - - - one_one_infos_mute - Mute - Mute - - - one_one_infos_search - Search - Search - - - one_one_infos_media_docs - Medias & documents - Medias & documents - - - one_one_infos_shared_media - Shared medias - Shared medias - - - one_one_infos_shared_docs - Shared documents - Shared documents - - - one_one_infos_other_actions - Other actions - Other actions - - - one_one_infos_enable_ephemerals - Enable ephemeral messages - Enable ephemeral messages - - - one_one_infos_disable_ephemerals - Disable ephemeral messages - Disable ephemeral messages - - - one_one_infos_delete_history - Delete history - Delete history - - - one_one_infos_delete_history_toast_title - Delete history ? - Delete history ? - - - one_one_infos_delete_history_toast_message - All the messages will be removed from the chat room. Do you want to continue ? - All the messages will be removed from the chat room. Do you want to continue ? - - - one_one_infos_open_contact - Open contact - Show contact - - - one_one_infos_create_contact - Create contact - Create contact - - - - GroupConversationInfos - - group_infos_call - Call - Call - - - group_infos_mute - Mute - Mute - - - group_infos_meeting - Meeting - Meeting - - - group_infos_participants - Participants - Participants (%1) - - - group_infos_media_docs - Medias & documents - Medias & documents - - - group_infos_shared_media - Shared medias - Shared medias - - - group_infos_shared_docs - Shared documents - Shared documents - - - group_infos_other_actions - Other actions - Other actions - - - group_infos_enable_ephemerals - Enable ephemeral messages - Enable ephemeral messages - - - group_infos_disable_ephemerals - Disable ephemeral messages - Disable ephemeral messages - - - group_infos_delete_history - Delete history - Delete history - - - group_infos_delete_history_toast_title - Delete history ? - Delete history ? - - - group_infos_delete_history_toast_message - All the messages will be removed from the chat room. Do you want to continue ? - All the messages will be removed from the chat room. Do you want to continue ? - - - group_infos_leave_room - Leave Chat Room - Leave Chat Room - - - group_infos_leave_room_toast_title - Leave Chat Room ? - Leave Chat Room ? - - - group_infos_leave_room_toast_message - All the messages will be removed from the chat room. Do you want to continue ? - All the messages will be removed from the chat room. Do you want to continue ? - - - - GroupChatInfoParticipants - - group_infos_add_participants_title - Add Participants - Add Participants - - - group_infos_participant_is_admin - Admin - Admin - - - menu_see_existing_contact - "Show contact" - Show contact - - - menu_add_address_to_contacts - "Add to contacts" - Add to contacts - - - group_infos_give_admin_rights - "Give admin rights" - Give admin rights - - - group_infos_remove_admin_rights - "Remove admin rights" - Remove admin rights - - - group_infos_copy_sip_address - "Copy SIP Address" - Copy SIP Address - - - group_infos_remove_participant - "Remove participant" - Remove participant - - - group_infos_remove_participants_toast_title - "Remove participant ?" - Remove participant ? - - - group_infos_remove_participants_toast_message - "Participant will be removed from chat room." - Participant will be removed from chat room. - - - diff --git a/Linphone/data/languages/fr_FR.ts b/Linphone/data/languages/fr_FR.ts index 2b9cc0b43..1d0a601cb 100644 --- a/Linphone/data/languages/fr_FR.ts +++ b/Linphone/data/languages/fr_FR.ts @@ -523,74 +523,74 @@ App - + remote_provisioning_dialog Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? Voulez-vous télécharger et appliquer la configuration depuis cette adresse ? - + application_description "A free and open source SIP video-phone." A free and open source SIP video-phone. - + command_line_arg_order "Send an order to the application towards a command line" Send an order to the application towards a command line - + command_line_option_show_help Show this help - + command_line_option_show_app_version Afficher la version de l'application - + command_line_option_config_to_fetch "Specify the linphone configuration file to be fetched. It will be merged with the current configuration." Specify the linphone configuration file to be fetched. It will be merged with the current configuration. - + command_line_option_config_to_fetch_arg "URL, path or file" URL, path or file - + command_line_option_minimized Minimiser - + command_line_option_log_to_stdout Log to stdout some debug information while running - + command_line_option_print_app_logs_only "Print only logs from the application" Print only logs from the application - + hide_action "Cacher" "Afficher" Cacher - + show_action Afficher - + quit_action "Quitter" Quitter @@ -789,25 +789,25 @@ CallHistoryLayout - + meeting_info_join_title "Rejoindre la réunion" Rejoindre la réunion - + contact_call_action "Appel" Appel - + contact_message_action "Message" Message - + contact_video_call_action "Appel Video" Appel Vidéo @@ -1671,16 +1671,32 @@ Audio uniquement + + ChatAudioContent + + + + information_popup_error_title + Error + Erreur + + + + information_popup_voice_message_error_message + Failed to create voice message : error in recorder + Impossible de créer le message vocal : erreur avec l'enregistreur + + ChatCore - + info_toast_deleted_title Deleted Supprimé - + info_toast_deleted_message_history Message history has been deleted L'historique des messages a été supprimé @@ -1689,41 +1705,47 @@ ChatDroppableTextArea - + chat_view_send_area_placeholder_text Say something… : placeholder text for sending message text area Dites quelque chose… + + + cannot_record_while_in_call_tooltip + Cannot record a message while a call is ongoing + Impossible d'enregistrer un message vocal pendant un appel + ChatListView - + chat_message_is_writing_info %1 is writing… %1 est en train d'écrire… - + chat_message_draft_sending_text Brouillon : %1 - + chat_room_delete "Supprimer" Supprimer - + chat_list_delete_chat_popup_title - Delete the chat ? + Delete the conversation ? Supprimer la conversation ? - + chat_list_delete_chat_popup_message - This chat and all its messages will be deleted. Do You want to continue ? + This conversation and all its messages will be deleted. Do You want to continue ? La conversation et tous ses messages seront supprimés. Souhaitez-vous continuer ? @@ -1802,7 +1824,7 @@ Error popup_error_max_files_count_message - You can send 12 files maximum at a time. %1 files were ignored + You can send 12 files maximum at a time. %n files were ignored Vous pouvez envoyer 12 fichiers maximum. Un fichier a été ignoré. Vous pouvez envoyer 12 fichiers maximum. %n fichiers ont été ignorés. @@ -1844,13 +1866,13 @@ Error ChatMessageCore - + info_toast_deleted_title Deleted Supprimé - + info_toast_deleted_message The message has been deleted Le message a été supprimé @@ -2010,6 +2032,27 @@ en bout. Seul votre correspondant peut les déchiffrer. Vous n'êtes pas connecté + + ChatSettingsLayout + + + settings_chat_attached_files_title + Attached files + Fichiers joints + + + + settings_chat_attached_files_auto_download_title + "Automatic download" + Téléchargement automatique + + + + settings_chat_attached_files_auto_download_subtitle + "Automatically download transferred or received files in conversations" + Télécharger automatiquement les fichiers échangés dans les conversations + + CliModel @@ -2880,6 +2923,153 @@ en bout. Seul votre correspondant peut les déchiffrer. Adresse invalide + + GroupChatInfoParticipants + + + group_infos_add_participants_title + "Ajouter des participants" + Ajouter des Participants + + + + group_infos_participant_is_admin + Admin + + + + menu_see_existing_contact + "Show contact" + Voir le contact + + + + menu_add_address_to_contacts + "Add to contacts" + Ajouter aux contacts + + + + group_infos_give_admin_rights + Donner les droits admins + + + + group_infos_remove_admin_rights + Retirer les droits admins + + + + group_infos_copy_sip_address + Copier l’adresse SIP + + + + group_infos_remove_participant + Retirer le participant + + + + group_infos_remove_participants_toast_title + Retirer le participant ? + + + + group_infos_remove_participants_toast_message + La participant sere retiré de la conversation + + + + GroupConversationInfos + + + group_infos_call + "Appel" + Appel + + + + group_infos_mute + "Sourdine" + Sourdine + + + + group_infos_meeting + "Réunion" + Réunion + + + + group_infos_participants + Participants (%1) + + + + + group_infos_media_docs + Medias & documents + + + group_infos_shared_media + Shared medias + Médias partagés + + + + group_infos_shared_docs + Documents partagés + + + + group_infos_other_actions + Autres actions + + + + group_infos_enable_ephemerals + Activer les messages éphémères + + + + group_infos_disable_ephemerals + Désactiver les messages éphémères + + + + group_infos_delete_history + Supprimer l'historique + + + + group_infos_delete_history_toast_title + Delete history ? + Supprimer l'historique ? + + + + group_infos_delete_history_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + Tous les messages seront supprimés. Souhaitez-vous continuer ? + + + + group_infos_leave_room + Quitter la conversation + + + + group_infos_leave_room_toast_title + Leave Chat Room ? + Quitter la conversation ? + + + + group_infos_leave_room_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + Vous ne recevrez ni pourrez envoyer des messages dans cette conversation, quitter ? + + GroupCreationFormLayout @@ -3365,60 +3555,60 @@ en bout. Seul votre correspondant peut les déchiffrer. interopérable - + call_transfer_successful_toast_title "Appel transféré" Appel transféré - + call_transfer_successful_toast_message "Votre correspondant a été transféré au contact sélectionné" Votre correspondant a été transféré au contact sélectionné - + information_popup_success_title Enregistré - + information_popup_changes_saved "Les changements ont été sauvegardés" Les changements ont été sauvegardés - + captcha_validation_loading_message "Veuillez valider le captcha sur la page web" Veuillez valider le captcha sur la page web - + assistant_register_error_title "Erreur lors de la création" Erreur lors de la création - + assistant_register_success_title "Compte créé" Compte créé - + assistant_register_success_message "Le compte a été créé. Vous pouvez maintenant vous connecter" Le compte a été créé. Vous pouvez maintenant vous connecter - + assistant_register_error_code "Erreur dans le code de validation" Erreur dans le code de validation - + information_popup_error_title Erreur @@ -3815,7 +4005,7 @@ en bout. Seul votre correspondant peut les déchiffrer. Nouvelle invitation à une conférence ! - + new_chat_room_messages 'New messages received!' Notification that warn the user of new messages. Nouveaux messages reçus ! @@ -3936,6 +4126,84 @@ en bout. Seul votre correspondant peut les déchiffrer. Pas de token trouvé dans la configuration OpenID + + OneOneConversationInfos + + + one_one_infos_call + "Appel" + Appel + + + + one_one_infos_mute + "Sourdine" + Sourdine + + + + one_one_infos_search + "Rechercher" + Rechercher + + + + one_one_infos_media_docs + Medias & documents + + + + one_one_infos_shared_media + Médias partagés + + + + one_one_infos_shared_docs + Documents partagés + + + + one_one_infos_other_actions + Autres actions + + + + one_one_infos_enable_ephemerals + Activer les messages éphémères + + + + one_one_infos_disable_ephemerals + Désactiver les messages éphémères + + + + one_one_infos_delete_history + Supprimer l'historique + + + + one_one_infos_delete_history_toast_title + Delete history ? + Supprimer l'historique ? + + + + one_one_infos_delete_history_toast_message + All the messages will be removed from the chat room. Do you want to continue ? + Tous les messages seront supprimés. Souhaitez-vous continuer ? + + + + one_one_infos_open_contact + Voir le contact + + + + one_one_infos_create_contact + Créer un contact + + ParticipantListView @@ -4003,52 +4271,52 @@ en bout. Seul votre correspondant peut les déchiffrer. QObject - + media_encryption_dtls DTLS - + media_encryption_none None - + media_encryption_srtp SRTP - + media_encryption_post_quantum "ZRTP - Post quantique" ZRTP - Post quantique - + incoming "Entrant" Entrant - + outgoing "Sortant" Sortant - + conference_layout_active_speaker "Participant actif" Intervenant actif - + conference_layout_grid "Mosaïque" Mosaïque - + conference_layout_audio_only "Audio uniquement" Audio uniquement @@ -4401,16 +4669,10 @@ Pour les activer dans un projet commercial, merci de nous contacter. SelectedChatView - - chat_view_group_call_toast_message - "Start a group call ?" - Démarrer un appel de groupe ? - - - chat_view_send_area_placeholder_text - Say something… : placeholder text for sending message text area - Dites quelque chose… + + chat_view_group_call_toast_message + Démarrer un appel de groupe ? @@ -4550,41 +4812,41 @@ Pour les activer dans un projet commercial, merci de nous contacter. Utils - + contact_presence_status_available Disponible - + contact_presence_status_busy Occupé - + contact_presence_status_do_not_disturb Ne pas déranger - + contact_presence_status_offline Hors ligne - + contact_presence_status_away Inactif/Absent - + information_popup_call_not_created_message "L'appel n'a pas pu être créé" L'appel n'a pas pu être créé - - - - + + + + information_popup_error_title Error ---------- @@ -4592,12 +4854,12 @@ Failed to create 1-1 conversation with %1 ! Erreur - + information_popup_group_call_not_created_message L'appel de groupe n'a pas pu être créé - + number_of_years %n an(s) @@ -4606,7 +4868,7 @@ Failed to create 1-1 conversation with %1 ! - + number_of_month "%n mois" @@ -4615,7 +4877,7 @@ Failed to create 1-1 conversation with %1 ! - + number_of_weeks %n semaine(s) @@ -4624,7 +4886,7 @@ Failed to create 1-1 conversation with %1 ! - + number_of_days %n jour(s) @@ -4633,30 +4895,60 @@ Failed to create 1-1 conversation with %1 ! - + today "Aujourd'hui" Aujourd'hui - + yesterday "Hier Hier - + call_zrtp_token_verification_possible_characters "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 - - + + information_popup_chatroom_creation_error_message Failed to create 1-1 conversation with %1 ! Erreur lors de la création de la conversation avec %1 + + + recorder_error + Error with the recorder + Erreur avec l'enregistreur + + + + chat_error + Erreur dans le chat + + + + + info_popup_error_title + Error + Erreur + + + + info_popup_send_voice_message_error_message + Could not send voice message : %1 + Impossible d'envoyer le message vocal : %1 + + + + info_popup_send_voice_message_sending_error_message + Failed to create message from record + Impossible de créer le message vocal + WaitingRoom @@ -4820,1117 +5112,1117 @@ Failed to create 1-1 conversation with %1 ! country - + Afghanistan Afghanistan - + Albania Albanie - + Algeria Algérie - + AmericanSamoa Samoa américaines - + Andorra Andorre - + Angola Angola - + Anguilla Anguilla - + AntiguaAndBarbuda Antigua-et-Barbuda - + Argentina Argentine - + Armenia Arménie - + Aruba Aruba - + Australia Australie - + Austria Autriche - + Azerbaijan Azerbaïdjan - + Bahamas Bahamas - + Bahrain Bahreïn - + Bangladesh Bangladesh - + Barbados Barbade - + Belarus Biélorussie - + Belgium Belgique - + Belize Belize - + Benin Bénin - + Bermuda Bermudes - + Bhutan Bhoutan - + Bolivia Bolivie - + BosniaAndHerzegowina Bosnie-Herzégovine - + Botswana Botswana - + Brazil Brésil - + Brunei Brunéi - + Bulgaria Bulgarie - + BurkinaFaso Burkina Faso - + Burundi Burundi - + Cambodia Cambodge - + Cameroon Cameroun - + Canada Canada - + CapeVerde Cap-Vert - + CaymanIslands Îles Caïmans - + CentralAfricanRepublic République centrafricaine - + Chad Tchad - + Chile Chili - + China Chine - + Colombia Colombie - + Comoros Comores - + PeoplesRepublicOfCongo République populaire du Congo - + CookIslands Îles Cook - + CostaRica Costa Rica - + IvoryCoast Côte d'Ivoire - + Croatia Croatie - + Cuba Cuba - + Cyprus Chypre - + CzechRepublic République Tchèque - + Denmark Danemark - + Djibouti Djibouti - + Dominica Dominique - + DominicanRepublic République dominicaine - + Ecuador Équateur - + Egypt Égypte - + ElSalvador El Salvador - + EquatorialGuinea Guinée équatoriale - + Eritrea Érythrée - + Estonia Estonie - + Ethiopia Éthiopie - + FalklandIslands Îles Falkland - + FaroeIslands Îles Féroé - + Fiji Fidji - + Finland Finlande - + France France - + FrenchGuiana Guyane française - + FrenchPolynesia Polynésie française - + Gabon Gabon - + Gambia Gambie - + Georgia Géorgie - + Germany Allemagne - + Ghana Ghana - + Gibraltar Gibraltar - + Greece Grèce - + Greenland Groenland - + Grenada Grenade - + Guadeloupe Guadeloupe - + Guam Guam - + Guatemala Guatemala - + Guinea Guinée - + GuineaBissau Guinée-Bissau - + Guyana Guyana - + Haiti Haïti - + Honduras Honduras - + DemocraticRepublicOfCongo République démocratique du Congo - + HongKong Hong Kong - + Hungary Hongrie - + Iceland Islande - + India Inde - + Indonesia Indonésie - + Iran Iran - + Iraq Irak - + Ireland Irlande - + Israel Israël - + Italy Italie - + Jamaica Jamaïque - + Japan Japon - + Jordan Jordanie - + Kazakhstan Kazakhstan - + Kenya Kenya - + Kiribati Kiribati - + DemocraticRepublicOfKorea Corée du Nord - + RepublicOfKorea Corée du Sud - + Kuwait Koweït - + Kyrgyzstan Kirghizistan - + Laos Laos - + Latvia Lettonie - + Lebanon Liban - + Lesotho Lesotho - + Liberia Libéria - + Libya Libye - + Liechtenstein Liechtenstein - + Lithuania Lituanie - + Luxembourg Luxembourg - + Macau Macao - + Macedonia Macédoine - + Madagascar Madagascar - + Malawi Malawi - + Malaysia Malaisie - + Maldives Maldives - + Mali Mali - + Malta Malte - + MarshallIslands Îles Marshall - + Martinique Martinique - + Mauritania Mauritanie - + Mauritius Maurice - + Mayotte Mayotte - + Mexico Mexique - + Micronesia Micronésie - + Moldova Moldavie - + Monaco Monaco - + Mongolia Mongolie - + Montenegro Montenegro - + Montserrat Montserrat - + Morocco Maroc - + Mozambique Mozambique - + Myanmar Myanmar - + Namibia Namibie - + NauruCountry Nauru - + Nepal Népal - + Netherlands Pays-Bas - + NewCaledonia Nouvelle-Calédonie - + NewZealand Nouvelle-Zélande - + Nicaragua Nicaragua - + Niger Niger - + Nigeria Nigeria - + Niue Niué - + NorfolkIsland Île Norfolk - + NorthernMarianaIslands Îles Mariannes du Nord - + Norway Norvège - + Oman Oman - + Pakistan Pakistan - + Palau Palaos - + PalestinianTerritories Palestine - + Panama Panama - + PapuaNewGuinea Papouasie-Nouvelle-Guinée - + Paraguay Paraguay - + Peru Pérou - + Philippines Philippines - + Poland Pologne - + Portugal Portugal - + PuertoRico Porto Rico - + Qatar Qatar - + Reunion La Réunion - + Romania Roumanie - + RussianFederation Russie - + Rwanda Rwanda - + SaintHelena Sainte-Hélène - + SaintKittsAndNevis Saint-Christophe-et-Niévès - + SaintLucia Sainte-Lucie - + SaintPierreAndMiquelon Saint-Pierre-et-Miquelon - + SaintVincentAndTheGrenadines Saint-Vincent et les Grenadines - + Samoa Samoa - + SanMarino Saint-Marin - + SaoTomeAndPrincipe Sao Tomé-et-Principe - + SaudiArabia Arabie saoudite - + Senegal Sénégal - + Serbia Serbie - + Seychelles Seychelles - + SierraLeone Sierra Leone - + Singapore Singapour - + Slovakia Slovaquie - + Slovenia Slovénie - + SolomonIslands Îles Salomon - + Somalia Somalie - + SouthAfrica Afrique du Sud - + Spain Espagne - + SriLanka Sri Lanka - + Sudan Soudan - + Suriname Suriname - + Swaziland Eswatini - + Sweden Suède - + Switzerland Suisse - + Syria Syrie - + Taiwan Taïwan - + Tajikistan Tadjikistan - + Tanzania Tanzanie - + Thailand Thaïlande - + Togo Togo - + Tokelau Tokelau - + Tonga Tonga - + TrinidadAndTobago Trinité-et-Tobago - + Tunisia Tunisie - + Turkey Turquie - + Turkmenistan Turkménistan - + TurksAndCaicosIslands Îles Turks et Caïques - + Tuvalu Tuvalu - + Uganda Ouganda - + Ukraine Ukraine - + UnitedArabEmirates Émirats arabes unis - + UnitedKingdom Royaume-Uni - + UnitedStates États-Unis - + Uruguay Uruguay - + Uzbekistan Ouzbékistan - + Vanuatu Vanuatu - + Venezuela Venezuela - + Vietnam Vietnam - + WallisAndFutunaIslands Wallis et Futuna - + Yemen Yémen - + Zambia Zambie - + Zimbabwe Zimbabwe @@ -6061,215 +6353,4 @@ Failed to create 1-1 conversation with %1 ! Ok - - OneOneConversationInfos - - one_one_infos_call - Call - Appel - - - one_one_infos_mute - Mute - Sourdine - - - one_one_infos_search - Search - Rechercher - - - one_one_infos_media_docs - Medias & documents - Medias & documents - - - one_one_infos_shared_media - Shared medias - Médias partagés - - - one_one_infos_shared_docs - Shared documents - Documents partagés - - - one_one_infos_other_actions - Other actions - Autres actions - - - one_one_infos_enable_ephemerals - Enable ephemeral messages - Activer les messages éphémères - - - one_one_infos_disable_ephemerals - Disable ephemeral messages - Désactiver les messages éphémères - - - one_one_infos_delete_history - Delete history - Supprimer l'historique - - - one_one_infos_delete_history_toast_title - Delete history ? - Supprimer l'historique ? - - - one_one_infos_delete_history_toast_message - All the messages will be removed from the chat room. Do you want to continue ? - Tous les messages seront supprimés. Souhaitez-vous continuer ? - - - one_one_infos_open_contact - Open contact - Voir le contact - - - one_one_infos_create_contact - Create contact - Créer un contact - - - - GroupConversationInfos - - group_infos_call - Call - Appel - - - group_infos_mute - Mute - Sourdine - - - group_infos_meeting - Meeting - Réunion - - - group_infos_participants - Participants - Participants (%1) - - - group_infos_media_docs - Medias & documents - Medias & documents - - - group_infos_shared_media - Shared medias - Médias partagés - - - group_infos_shared_docs - Shared documents - Documents partagés - - - group_infos_other_actions - Other actions - Autres actions - - - group_infos_enable_ephemerals - Enable ephemeral messages - Activer les messages éphémères - - - group_infos_disable_ephemerals - Disable ephemeral messages - Désactiver les messages éphémères - - - group_infos_delete_history - Delete history - Supprimer l'historique - - - group_infos_delete_history_toast_title - Delete history ? - Supprimer l'historique ? - - - group_infos_delete_history_toast_message - All the messages will be removed from the chat room. Do you want to continue ? - Tous les messages seront supprimés. Souhaitez-vous continuer ? - - - group_infos_leave_room - Leave Chat Room - Quitter la conversation - - - group_infos_leave_room_toast_title - Leave Chat Room ? - Quitter la conversation ? - - - group_infos_leave_room_toast_message - All the messages will be removed from the chat room. Do you want to continue ? - Vous ne recevrez ni pourrez envoyer des messages dans cette conversation, quitter ? - - - - GroupChatInfoParticipants - - group_infos_add_participants_title - Add Participants - Ajouter des Participants - - - group_infos_participant_is_admin - Admin - Admin - - - menu_see_existing_contact - "Show contact" - Voir le contact - - - menu_add_address_to_contacts - "Add to contacts" - Ajouter aux contacts - - - group_infos_give_admin_rights - "Give admin rights" - Donner les droits admins - - - group_infos_remove_admin_rights - "Remove admin rights" - Retirer les droits admins - - - group_infos_copy_sip_address - "Copy SIP Address" - Copier l’adresse SIP - - - group_infos_remove_participant - "Remove participant" - Retirer le participant - - - group_infos_remove_participants_toast_title - "Remove participant ?" - Retirer le participant ? - - - group_infos_remove_participants_toast_message - "Participant will be removed from chat room." - La participant sere retiré de la conversation - - - - diff --git a/Linphone/model/CMakeLists.txt b/Linphone/model/CMakeLists.txt index 07de11305..63e437cc5 100644 --- a/Linphone/model/CMakeLists.txt +++ b/Linphone/model/CMakeLists.txt @@ -42,6 +42,8 @@ list(APPEND _LINPHONEAPP_SOURCES model/setting/MediastreamerUtils.cpp model/sound-player/SoundPlayerModel.cpp + + model/recorder/RecorderModel.cpp model/tool/ToolModel.cpp model/tool/VfsUtils.cpp diff --git a/Linphone/model/call-history/CallHistoryModel.cpp b/Linphone/model/call-history/CallHistoryModel.cpp index 78ca0aa0d..d902946d4 100644 --- a/Linphone/model/call-history/CallHistoryModel.cpp +++ b/Linphone/model/call-history/CallHistoryModel.cpp @@ -40,4 +40,4 @@ void CallHistoryModel::removeCallHistory() { mustBeInLinphoneThread(getClassName() + "::removeCallHistory"); qInfo() << "Removing call log: " << Utils::coreStringToAppString(callLog->getCallId()); CoreModel::getInstance()->getCore()->removeCallLog(callLog); -} +} \ No newline at end of file diff --git a/Linphone/model/chat/ChatModel.cpp b/Linphone/model/chat/ChatModel.cpp index e7bd9d13b..08c00a87a 100644 --- a/Linphone/model/chat/ChatModel.cpp +++ b/Linphone/model/chat/ChatModel.cpp @@ -119,6 +119,10 @@ void ChatModel::deleteHistory() { emit historyDeleted(); } +void ChatModel::deleteMessage(std::shared_ptr message) { + mMonitor->deleteMessage(message); +} + void ChatModel::leave() { mMonitor->leave(); } @@ -128,6 +132,11 @@ void ChatModel::deleteChatRoom() { emit deleted(); } +std::shared_ptr +ChatModel::createVoiceRecordingMessage(const std::shared_ptr &recorder) { + return mMonitor->createVoiceRecordingMessage(recorder); +} + std::shared_ptr ChatModel::createTextMessageFromText(QString text) { return mMonitor->createMessageFromUtf8(Utils::appStringToCoreString(text)); } diff --git a/Linphone/model/chat/ChatModel.hpp b/Linphone/model/chat/ChatModel.hpp index cc7b60e27..a2d789e91 100644 --- a/Linphone/model/chat/ChatModel.hpp +++ b/Linphone/model/chat/ChatModel.hpp @@ -47,8 +47,11 @@ public: std::list> getHistory() const; QString getIdentifier() const; void deleteHistory(); + void deleteMessage(std::shared_ptr message); void deleteChatRoom(); void leave(); + std::shared_ptr + createVoiceRecordingMessage(const std::shared_ptr &recorder); std::shared_ptr createTextMessageFromText(QString text); std::shared_ptr createMessage(QString text, QList> filesContent); diff --git a/Linphone/model/chat/message/ChatMessageModel.cpp b/Linphone/model/chat/message/ChatMessageModel.cpp index 856cc17d9..dc5cbed81 100644 --- a/Linphone/model/chat/message/ChatMessageModel.cpp +++ b/Linphone/model/chat/message/ChatMessageModel.cpp @@ -102,6 +102,10 @@ void ChatMessageModel::removeReaction() { sendReaction(QString()); } +void ChatMessageModel::send() { + mMonitor->send(); +} + QString ChatMessageModel::getOwnReaction() const { auto reaction = mMonitor->getOwnReaction(); return reaction ? Utils::coreStringToAppString(reaction->getBody()) : QString(); diff --git a/Linphone/model/chat/message/ChatMessageModel.hpp b/Linphone/model/chat/message/ChatMessageModel.hpp index a0f83d9b4..73e145c12 100644 --- a/Linphone/model/chat/message/ChatMessageModel.hpp +++ b/Linphone/model/chat/message/ChatMessageModel.hpp @@ -59,6 +59,8 @@ public: void removeReaction(); + void send(); + linphone::ChatMessage::State getState() const; QString getOwnReaction() const; diff --git a/Linphone/model/recorder/RecorderModel.cpp b/Linphone/model/recorder/RecorderModel.cpp new file mode 100644 index 000000000..dde78a0d8 --- /dev/null +++ b/Linphone/model/recorder/RecorderModel.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "core/App.hpp" +#include "model/core/CoreModel.hpp" +#include "model/setting/SettingsModel.hpp" +#include "tool/Utils.hpp" + +#include +#include + +#include "RecorderModel.hpp" + +DEFINE_ABSTRACT_OBJECT(RecorderModel) + +// ============================================================================= + +RecorderModel::RecorderModel(std::shared_ptr recorder, QObject *parent) : QObject(parent) { + mustBeInLinphoneThread(getClassName()); + mRecorder = recorder; +} + +RecorderModel::~RecorderModel() { +} + +std::shared_ptr RecorderModel::getRecorder() { + return mRecorder; +} + +int RecorderModel::getDuration() const { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return mRecorder->getDuration(); +} + +float RecorderModel::getCaptureVolume() const { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return mRecorder->getCaptureVolume(); +} + +linphone::Recorder::State RecorderModel::getState() const { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return mRecorder->getState(); +} + +QString RecorderModel::getFile() const { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return Utils::coreStringToAppString(mRecorder->getFile()); +} + +QStringList RecorderModel::splitSavedFilename(const QString &filename) { + QStringList fields = filename.split('_'); + if (fields.size() == 3 && fields[0] == "vocal" && fields[1].split('-').size() == 3 && + fields[2].split('-').size() == 4) { + return fields; + } else return QStringList(filename); +} + +QDateTime RecorderModel::getDateTimeSavedFilename(const QString &filename) { + auto fields = splitSavedFilename(filename); + if (fields.size() > 1) return QDateTime::fromString(fields[1] + "_" + fields[2], "yyyy-MM-dd_hh-mm-ss-zzz"); + else return QDateTime(); +} + +void RecorderModel::start() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + bool soFarSoGood; + QString filename = + QStringLiteral("vocal_%1.mkv").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm-ss-zzz")); + const QString safeFilePath = Utils::getSafeFilePath( + QStringLiteral("%1%2").arg(SettingsModel::getInstance()->getSavedCallsFolder()).arg(filename), &soFarSoGood); + + if (!soFarSoGood) { + qWarning() << QStringLiteral("Unable to create safe file path for: %1.").arg(filename); + emit errorChanged(QString("Unable to create safe file path for : %1.").arg(filename)); + } else if (mRecorder->open(Utils::appStringToCoreString(safeFilePath)) < 0) { + qWarning() << QStringLiteral("Unable to open safe file path for: %1.").arg(filename); + emit errorChanged(QString("Unable to open safe file path for : %1.").arg(filename)); + } else if (mRecorder->start() < 0) { + qWarning() << QStringLiteral("Unable to start recording to : %1.").arg(filename); + emit errorChanged(QString("Unable to start recording to : %1.").arg(filename)); + } + emit stateChanged(); + emit fileChanged(); +} + +void RecorderModel::pause() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + mRecorder->pause(); + emit stateChanged(); +} + +void RecorderModel::stop() { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + // if (mRecorder->getState() == linphone::Recorder::State::Running) // Remove these tests when the SDK do them. + // mRecorder->pause(); + // if (mRecorder->getState() == linphone::Recorder::State::Paused) { + mRecorder->close(); + emit stateChanged(); +} + +//-------------------------------------------------------------------------------------------------------------------------- diff --git a/Linphone/model/recorder/RecorderModel.hpp b/Linphone/model/recorder/RecorderModel.hpp new file mode 100644 index 000000000..d129fea97 --- /dev/null +++ b/Linphone/model/recorder/RecorderModel.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 Belledonne Communications SARL. + * + * This file is part of linphone-desktop + * (see https://www.linphone.org). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef RECORDER_MODEL_H +#define RECORDER_MODEL_H + +#include "tool/AbstractObject.hpp" +#include + +// ============================================================================= + +class RecorderModel : public QObject, public AbstractObject { + Q_OBJECT + +public: + RecorderModel(std::shared_ptr recorder, QObject *parent = nullptr); + virtual ~RecorderModel(); + + std::shared_ptr getRecorder(); + + int getDuration() const; + float getCaptureVolume() const; + linphone::Recorder::State getState() const; + QString getFile() const; + + static QStringList + splitSavedFilename(const QString &filename); // If doesn't match to generateSavedFilename, return filename + static QDateTime getDateTimeSavedFilename(const QString &filename); + + void start(); + void pause(); + void stop(); + +signals: + void stateChanged(); + void fileChanged(); + void errorChanged(QString error); + +private: + DECLARE_ABSTRACT_OBJECT + std::shared_ptr mRecorder; +}; +#endif diff --git a/Linphone/model/setting/SettingsModel.cpp b/Linphone/model/setting/SettingsModel.cpp index ea3fcbf69..a04270412 100644 --- a/Linphone/model/setting/SettingsModel.cpp +++ b/Linphone/model/setting/SettingsModel.cpp @@ -428,6 +428,17 @@ void SettingsModel::setVideoEnabled(const bool enabled) { // ----------------------------------------------------------------------------- +bool SettingsModel::getAutoDownloadReceivedFiles() const { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + return CoreModel::getInstance()->getCore()->getMaxSizeForAutoDownloadIncomingFiles() == 0; +} + +void SettingsModel::setAutoDownloadReceivedFiles(bool status) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + CoreModel::getInstance()->getCore()->setMaxSizeForAutoDownloadIncomingFiles(status ? 0 : -1); + emit autoDownloadReceivedFilesChanged(status); +} + bool SettingsModel::getEchoCancellationEnabled() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); return CoreModel::getInstance()->getCore()->echoCancellationEnabled(); @@ -558,6 +569,18 @@ QString SettingsModel::getLogsFolder(const shared_ptr &config) : Paths::getLogsDirPath(); } +static inline std::string getLegacySavedCallsFolder(const shared_ptr &config) { + auto path = config->getString(SettingsModel::UiSection, "saved_videos_folder", ""); + if (path == "") path = Utils::appStringToCoreString(Paths::getCapturesDirPath()); + return path; +} + +QString SettingsModel::getSavedCallsFolder() const { + auto path = mConfig->getString(UiSection, "saved_calls_folder", ""); // Avoid to call default function if exist. + if (path == "") path = getLegacySavedCallsFolder(mConfig); + return QDir::cleanPath(Utils::coreStringToAppString(path)) + QDir::separator(); +} + QString SettingsModel::getLogsUploadUrl() const { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); auto core = CoreModel::getInstance()->getCore(); diff --git a/Linphone/model/setting/SettingsModel.hpp b/Linphone/model/setting/SettingsModel.hpp index 947d853bd..ea1cb0ebd 100644 --- a/Linphone/model/setting/SettingsModel.hpp +++ b/Linphone/model/setting/SettingsModel.hpp @@ -61,6 +61,9 @@ public: bool getEchoCancellationEnabled() const; void setEchoCancellationEnabled(bool enabled); + void setAutoDownloadReceivedFiles(bool enabled); + bool getAutoDownloadReceivedFiles() const; + // Audio. -------------------------------------------------------------------- bool getIsInCall() const; @@ -137,6 +140,7 @@ public: QString getLogsFolder() const; void setLogsFolder(const QString &folder); static QString getLogsFolder(const std::shared_ptr &config); + QString getSavedCallsFolder() const; QString getLogsUploadUrl() const; void setLogsUploadUrl(const QString &url); @@ -238,6 +242,9 @@ signals: void dndChanged(bool value); + // Messages. -------------------------------------------------------------------- + void autoDownloadReceivedFilesChanged(bool enabled); + private: void notifyConfigReady(); MediastreamerUtils::SimpleCaptureGraph *mSimpleCaptureGraph = nullptr; diff --git a/Linphone/model/sound-player/SoundPlayerModel.cpp b/Linphone/model/sound-player/SoundPlayerModel.cpp index 0baba87e5..f0b5e3d2f 100644 --- a/Linphone/model/sound-player/SoundPlayerModel.cpp +++ b/Linphone/model/sound-player/SoundPlayerModel.cpp @@ -99,7 +99,13 @@ bool SoundPlayerModel::play(QString source) { // ----------------------------------------------------------------------------- -void SoundPlayerModel::seek(int offset) { +void SoundPlayerModel::seek(QString source, int offset) { + if (!open(source)) { + qWarning() << QStringLiteral("Unable to open: `%1`").arg(source); + //: Unable to open: `%1` + emit errorChanged(QString("sound_player_open_error").arg(source)); + return; + } mMonitor->seek(offset); emit positionChanged(mMonitor->getCurrentPosition()); } diff --git a/Linphone/model/sound-player/SoundPlayerModel.hpp b/Linphone/model/sound-player/SoundPlayerModel.hpp index acaa77d9a..8ded897b1 100644 --- a/Linphone/model/sound-player/SoundPlayerModel.hpp +++ b/Linphone/model/sound-player/SoundPlayerModel.hpp @@ -46,7 +46,7 @@ public: void pause(); bool play(QString source); void stop(bool force = false); - void seek(int offset); + void seek(QString source, int offset); int getPosition() const; bool hasVideo() const; // Call it after playing a video because the detection is not outside this scope. diff --git a/Linphone/tool/Utils.cpp b/Linphone/tool/Utils.cpp index 7c6630ebe..873cf8de7 100644 --- a/Linphone/tool/Utils.cpp +++ b/Linphone/tool/Utils.cpp @@ -32,6 +32,7 @@ #include "core/participant/ParticipantDeviceCore.hpp" #include "core/path/Paths.hpp" #include "core/payload-type/DownloadablePayloadTypeCore.hpp" +#include "core/recorder/RecorderGui.hpp" #include "model/object/VariantObject.hpp" #include "model/tool/ToolModel.hpp" #include "tool/providers/AvatarProvider.hpp" @@ -1948,6 +1949,56 @@ QString Utils::getSafeFilePath(const QString &filePath, bool *soFarSoGood) { return QString(""); } +VariantObject *Utils::createVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui) { + VariantObject *data = new VariantObject("createVoiceRecordingMessage"); + if (!data) return nullptr; + data->makeRequest([recorderCore = recorderGui ? recorderGui->getCore() : nullptr, + chatCore = chatGui ? chatGui->getCore() : nullptr]() { + if (!recorderCore || !chatCore) return QVariant(); + auto model = recorderCore->getModel(); + auto chatModel = chatCore->getModel(); + if (!model || !chatModel) return QVariant(); + auto recorder = model->getRecorder(); + auto linMessage = chatModel->createVoiceRecordingMessage(recorder); + if (linMessage) { + auto messageCore = ChatMessageCore::create(linMessage); + return QVariant::fromValue(new ChatMessageGui(messageCore)); + } + return QVariant(); + }); + data->requestValue(); + return data; +} + +void Utils::sendVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui) { + auto chatModel = chatGui && chatGui->mCore ? chatGui->mCore->getModel() : nullptr; + auto recorderModel = recorderGui && recorderGui->mCore ? recorderGui->mCore->getModel() : nullptr; + if (!chatModel || !recorderModel) { + //: Error with the recorder + QString error = !recorderModel ? tr("recorder_error") + //: Error in the chat + : tr("chat_error"); + //: Error + showInformationPopup(tr("info_popup_error_title"), + //: Could not send voice message : %1 + tr("info_popup_send_voice_message_error_message").arg(error)); + return; + } + App::postModelAsync([chatModel, recorderModel] { + mustBeInLinphoneThread(sLog().arg(Q_FUNC_INFO)); + auto chat = chatModel->getMonitor(); + auto recorder = recorderModel->getRecorder(); + auto linMessage = chatModel->createVoiceRecordingMessage(recorder); + if (linMessage) { + linMessage->send(); + } else + //: Error + showInformationPopup(tr("info_popup_error_title"), + //: Failed to create message from record + tr("info_popup_send_voice_message_sending_error_message")); + }); +} + bool Utils::isVideo(const QString &path) { if (path.isEmpty()) return false; return QMimeDatabase().mimeTypeForFile(path).name().contains("video/"); diff --git a/Linphone/tool/Utils.hpp b/Linphone/tool/Utils.hpp index 7c4b276a3..4aa589157 100644 --- a/Linphone/tool/Utils.hpp +++ b/Linphone/tool/Utils.hpp @@ -52,6 +52,7 @@ class ConferenceCore; class ParticipantDeviceCore; class DownloadablePayloadTypeCore; class ChatGui; +class RecorderGui; class Utils : public QObject, public AbstractObject { Q_OBJECT @@ -174,6 +175,9 @@ public: static QDateTime getOffsettedUTC(const QDateTime &date); Q_INVOKABLE static QString toTimeString(QDateTime date, const QString &format = "hh:mm:ss"); + Q_INVOKABLE static VariantObject *createVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui); + Q_INVOKABLE static void sendVoiceRecordingMessage(RecorderGui *recorderGui, ChatGui *chatGui); + // QDir findDirectoryByName(QString startPath, QString name); static QString getApplicationProduct(); diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index da253ba93..91dd629bf 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -149,6 +149,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Page/Layout/Settings/DebugSettingsLayout.qml view/Page/Layout/Settings/LdapSettingsLayout.qml view/Page/Layout/Settings/CarddavSettingsLayout.qml + view/Page/Layout/Settings/ChatSettingsLayout.qml view/Page/Layout/Settings/SecuritySettingsLayout.qml view/Page/Layout/Settings/NetworkSettingsLayout.qml view/Page/Layout/Settings/AdvancedSettingsLayout.qml diff --git a/Linphone/view/Control/Button/Button.qml b/Linphone/view/Control/Button/Button.qml index f5ab3a5c1..0203df5ff 100644 --- a/Linphone/view/Control/Button/Button.qml +++ b/Linphone/view/Control/Button/Button.qml @@ -19,7 +19,7 @@ Control.Button { property color pressedTextColor: style?.text?.pressed || Qt.darker(textColor, 1.1) property color borderColor: style?.borderColor || "transparent" ToolTip.visible: hovered && ToolTip.text != "" - ToolTip.delay: 1000 + ToolTip.delay: 500 property color disabledFilterColor: color.hslLightness > 0.5 ? DefaultStyle.grey_0 : DefaultStyle.grey_400 @@ -199,9 +199,20 @@ Control.Button { } Component{ id: imageComponent - ButtonImage{ + Item { width: stacklayout.width height: stacklayout.height + ButtonImage { + id: buttonIcon + anchors.fill: parent + } + ButtonImage { + z: buttonIcon.z + 1 + visible: !mainItem.enabled + anchors.fill: parent + colorizationColor: DefaultStyle.grey_0 + opacity: 0.5 + } } } Component{ diff --git a/Linphone/view/Control/Button/RoundButton.qml b/Linphone/view/Control/Button/RoundButton.qml index b5f22a0c8..349123099 100644 --- a/Linphone/view/Control/Button/RoundButton.qml +++ b/Linphone/view/Control/Button/RoundButton.qml @@ -12,8 +12,8 @@ Button { // bottomPadding: Math.round(16 * DefaultStyle.dp) // leftPadding: Math.round(16 * DefaultStyle.dp) // rightPadding: Math.round(16 * DefaultStyle.dp) - icon.width: width - icon.height: width + // icon.width: width + // icon.height: width radius: width * 2 // width: Math.round(24 * DefaultStyle.dp) height: width diff --git a/Linphone/view/Control/Display/Chat/ChatAudioContent.qml b/Linphone/view/Control/Display/Chat/ChatAudioContent.qml index 1a3be1191..37c6d5dc3 100644 --- a/Linphone/view/Control/Display/Chat/ChatAudioContent.qml +++ b/Linphone/view/Control/Display/Chat/ChatAudioContent.qml @@ -7,41 +7,45 @@ import UtilsCpp // ============================================================================= -Loader{ +Item { id: mainItem property ChatMessageContentGui chatMessageContentGui - property int availableWidth : parent.width - - // property string filePath : tempFile.filePath - - active: chatMessageContentGui && chatMessageContentGui.core.isVoiceRecording - - // onChatMessageContentGuiChanged: if(chatMessageContentGui){ - // tempFile.createFileFromContentModel(chatMessageContentGui, false); - // } - - // TemporaryFile { - // id: tempFile - // } - - sourceComponent: Item { - id: loadedItem - property bool isPlaying : soundPlayerGui && soundPlayerGui.core.playbackState === LinphoneEnums.PlaybackState.PlayingState - onIsPlayingChanged: isPlaying ? mediaProgressBar.resume() : mediaProgressBar.stop() - - width: mainItem.width - height: mainItem.height - - clip: false + property var chatMessageObj + property ChatMessageGui chatMessage: chatMessageObj && chatMessageObj.value || null + property bool isPlaying : soudPlayerLoader.item && soudPlayerLoader.item.core.playbackState === LinphoneEnums.PlaybackState.PlayingState + onIsPlayingChanged: isPlaying ? mediaProgressBar.resume() : mediaProgressBar.stop() + property bool recording: false + property RecorderGui recorderGui: recorderLoader.item || null - SoundPlayerGui { + signal voiceRecordingMessageCreationRequested(RecorderGui recorderGui) + signal stopRecording() + + function createVoiceMessageInChat(chat) { + if (recorderLoader.item) { + mainItem.chatMessageObj = UtilsCpp.createVoiceRecordingMessage(recorderLoader.item, chat) + } else { + //: Error + UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"), + //: Failed to create voice message : error in recorder + qsTr("information_popup_voice_message_error_message"), false) + } + } + + Loader { + id: soudPlayerLoader + property int duration: mainItem.chatMessageContentGui + ? mainItem.chatMessageContentGui.core.fileDuration + : item + ? item.core.duration + : 0 + property int position: item?.core.position || 0 + active: mainItem.chatMessageContentGui && mainItem.chatMessageContentGui.core.isVoiceRecording + sourceComponent: SoundPlayerGui { id: soundPlayerGui - property int duration: mainItem.chatMessageContentGui ? mainItem.chatMessageContentGui.core.fileDuration : core.duration - property int position: core.position source: mainItem.chatMessageContentGui && mainItem.chatMessageContentGui.core.filePath function play(){ - if(loadedItem.isPlaying){// Pause the play + if(mainItem.isPlaying){// Pause the play soundPlayerGui.core.lPause() }else{// Play the audio soundPlayerGui.core.lPlay() @@ -51,41 +55,82 @@ Loader{ mediaProgressBar.value = 101 } onPositionChanged: { - mediaProgressBar.progressPosition = position - mediaProgressBar.value = 100 * ( mediaProgressBar.progressPosition / duration) + mediaProgressBar.progressPosition = soudPlayerLoader.position + mediaProgressBar.value = 100 * ( mediaProgressBar.progressPosition / soudPlayerLoader.duration) } onSourceChanged: if (source != "") { - // core.lPlay()// This will open the file and allow seeking - // core.lPause() - core.lOpen() + core.lOpen() // Open the file and allow seeking mediaProgressBar.value = 0 mediaProgressBar.refresh() } + onErrorChanged: (error) => { + //: Error + UtilsCpp.showInformationPopup(qsTr("information_popup_error_title"), error, false) + } } - + } + + Loader { + id: recorderLoader + active: mainItem.recording && !mainItem.chatMessageContentGui + property int duration: item?.core.duration || 0 + property int captureVolume: item?.core.captureVolume || 0 + property var state: item?.core.state - MediaProgressBar{ - id: mediaProgressBar - anchors.fill: parent - progressDuration: soundPlayerGui ? soundPlayerGui.duration : chatMessageContentGui.core.fileDuration - progressPosition: 0 - value: 0 - function refresh(){ - if(soundPlayerGui){ - soundPlayerGui.core.lRefreshPosition() - } + Connections { + target: mainItem + function onStopRecording() { + recorderLoader.item.core.lStop() } - onEndReached:{ - if(soundPlayerGui) - soundPlayerGui.core.lStop() - } - onPlayStopButtonToggled: soundPlayerGui.play() - onRefreshPositionRequested: refresh() - onSeekRequested: (ms) => { - if(soundPlayerGui) { - soundPlayerGui.core.lSeek(ms) + } + + sourceComponent: RecorderGui { + id: recorderGui + onReady: core.lStart() + onStateChanged: (state) => { + if (state === LinphoneEnums.RecorderState.Running) mediaProgressBar.start() + if (state === LinphoneEnums.RecorderState.Closed) { + mediaProgressBar.stop() + mainItem.voiceRecordingMessageCreationRequested(recorderGui) } } } } -} + + MediaProgressBar{ + id: mediaProgressBar + anchors.fill: parent + progressDuration: soudPlayerLoader.active + ? soudPlayerLoader.duration + : recorderLoader + ? recorderLoader.duration + : chatMessageContentGui.core.fileDuration + progressPosition: 0 + value: 0 + recording: recorderLoader.state === LinphoneEnums.RecorderState.Running + function refresh(){ + if(soudPlayerLoader.item){ + soudPlayerLoader.item.core.lRefreshPosition() + } else if (recorderLoader.item) { + recorderLoader.item.core.lRefresh() + } + } + onEndReached:{ + if(soudPlayerLoader.item) + soudPlayerLoader.item.core.lStop() + } + onPlayStopButtonToggled: { + if(soudPlayerLoader.item) { + soudPlayerLoader.item.play() + } else if (recorderLoader.item) { + recorderLoader.item.core.lStop() + } + } + onRefreshPositionRequested: refresh() + onSeekRequested: (ms) => { + if(soudPlayerLoader.active) { + soudPlayerLoader.item.core.lSeek(ms) + } + } + } +} \ No newline at end of file diff --git a/Linphone/view/Control/Display/Chat/ChatListView.qml b/Linphone/view/Control/Display/Chat/ChatListView.qml index 2e34cae23..280b427e6 100644 --- a/Linphone/view/Control/Display/Chat/ChatListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatListView.qml @@ -342,9 +342,9 @@ ListView { spacing: Math.round(10 * DefaultStyle.dp) Layout.fillWidth: true onClicked: { - //: Delete the chat ? + //: Delete the conversation ? mainWindow.showConfirmationLambdaPopup(qsTr("chat_list_delete_chat_popup_title"), - //: This chat and all its messages will be deleted. Do You want to continue ? + //: This conversation and all its messages will be deleted. Do You want to continue ? qsTr("chat_list_delete_chat_popup_message"), "", function(confirmed) { diff --git a/Linphone/view/Control/Display/Chat/ChatMessageContent.qml b/Linphone/view/Control/Display/Chat/ChatMessageContent.qml index 590ed7d4d..4c4140bd0 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessageContent.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessageContent.qml @@ -28,7 +28,7 @@ ColumnLayout { // VOICE MESSAGES Repeater { id: messagesVoicesList - visible: mainItem.chatMessageGui.core.isVoiceRecording && count > 0 + visible: count > 0 model: ChatMessageContentProxy{ filterType: ChatMessageContentProxy.FilterContentType.Voice chatMessageGui: mainItem.chatMessageGui diff --git a/Linphone/view/Control/Display/MediaProgressBar.qml b/Linphone/view/Control/Display/MediaProgressBar.qml index 78323702c..6850b16cc 100644 --- a/Linphone/view/Control/Display/MediaProgressBar.qml +++ b/Linphone/view/Control/Display/MediaProgressBar.qml @@ -30,7 +30,7 @@ ProgressBar { animationTest.start() } function resume(){ - if(mainItem.value >= 100) + if (mainItem.value >= 100) mainItem.value = 0 animationTest.start() } @@ -41,7 +41,7 @@ ProgressBar { signal endReached() signal refreshPositionRequested() signal seekRequested(int ms) - Timer{ + Timer { id: animationTest repeat: true onTriggered: mainItem.refreshPositionRequested() @@ -60,7 +60,6 @@ ProgressBar { mainItem.value = 100// Stay at 100 progressPosition = progressDuration } - console.log("end reached") mainItem.endReached() } } @@ -128,8 +127,7 @@ ProgressBar { onClicked: { mainItem.playStopButtonToggled() } - borderColor: "transparent" - style: ButtonStyle.secondary + style: ButtonStyle.player } Control.Control { anchors.right: parent.right @@ -150,6 +148,8 @@ ProgressBar { visible: mainItem.recording colorizationColor: DefaultStyle.danger_500main imageSource: AppIcons.recordFill + Layout.preferredWidth: Math.round(14 * DefaultStyle.dp) + Layout.preferredHeight: Math.round(14 * DefaultStyle.dp) } Text { id: durationText diff --git a/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml b/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml index 972288629..8ab8f7319 100644 --- a/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml +++ b/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml @@ -11,26 +11,27 @@ import 'qrc:/qt/qml/Linphone/view/Control/Tool/Helper/utils.js' as Utils Control.Control { id: mainItem - property alias placeholderText: sendingTextArea.placeholderText - property alias text: sendingTextArea.text - property alias textArea: sendingTextArea - property alias cursorPosition: sendingTextArea.cursorPosition - property alias emojiPickerButtonChecked: emojiPickerButton.checked + // property alias placeholderText: sendingTextArea.placeholderText + property string text + property var textArea + // property alias cursorPosition: sendingTextArea.cursorPosition + property bool emojiPickerButtonChecked property bool dropEnabled: true property string dropDisabledReason property bool isEphemeral : false property bool emojiVisible: false + + property ChatGui chat // --------------------------------------------------------------------------- signal dropped (var files) signal validText (string text) - signal sendText() - signal audioRecordRequest() + signal sendMessage() signal emojiClicked() signal composing() - + // --------------------------------------------------------------------------- function _emitFiles (files) { @@ -63,118 +64,196 @@ Control.Control { background: Rectangle { anchors.fill: parent color: DefaultStyle.grey_100 - MediumButton { - id: expandButton - anchors.top: parent.top - anchors.topMargin: Math.round(4 * DefaultStyle.dp) - anchors.horizontalCenter: parent.horizontalCenter - style: ButtonStyle.noBackgroundOrange - icon.source: checked ? AppIcons.downArrow : AppIcons.upArrow - checkable: true - } } - contentItem: RowLayout { - spacing: Math.round(20 * DefaultStyle.dp) - RowLayout { - spacing: Math.round(16 * DefaultStyle.dp) - BigButton { - id: emojiPickerButton - style: ButtonStyle.noBackground - checkable: true - icon.source: checked ? AppIcons.closeX : AppIcons.smiley - } - BigButton { - style: ButtonStyle.noBackground - icon.source: AppIcons.paperclip - onClicked: { - fileDialog.open() + contentItem: Control.StackView { + id: sendingAreaStackView + initialItem: textAreaComp + Component { + id: textAreaComp + RowLayout { + // disable record button if call ongoing + CallProxy { + id: callsModel + sourceModel: AppCpp.calls } - } - Control.Control { - Layout.fillWidth: true - leftPadding: Math.round(15 * DefaultStyle.dp) - rightPadding: Math.round(15 * DefaultStyle.dp) - topPadding: Math.round(15 * DefaultStyle.dp) - bottomPadding: Math.round(15 * DefaultStyle.dp) - background: Rectangle { - id: inputBackground - anchors.fill: parent - radius: Math.round(35 * DefaultStyle.dp) - color: DefaultStyle.grey_0 - MouseArea { - anchors.fill: parent - onPressed: sendingTextArea.forceActiveFocus() - cursorShape: Qt.IBeamCursor + spacing: Math.round(16 * DefaultStyle.dp) + BigButton { + id: emojiPickerButton + style: ButtonStyle.noBackground + checkable: true + icon.source: checked ? AppIcons.closeX : AppIcons.smiley + onCheckedChanged: mainItem.emojiPickerButtonChecked = checked + Connections { + target: mainItem + function onEmojiPickerButtonCheckedChanged() { + emojiPickerButton.checked = mainItem.emojiPickerButtonChecked + } } } - contentItem: RowLayout { - Flickable { - id: sendingAreaFlickable - Layout.fillWidth: true - Layout.preferredHeight: Math.min(Math.round(60 * DefaultStyle.dp), contentHeight) - Binding { - target: sendingAreaFlickable - when: expandButton.checked - property: "Layout.preferredHeight" - value: Math.round(250 * DefaultStyle.dp) - restoreMode: Binding.RestoreBindingOrValue + BigButton { + style: ButtonStyle.noBackground + icon.source: AppIcons.paperclip + onClicked: { + fileDialog.open() + } + } + Control.Control { + Layout.fillWidth: true + leftPadding: Math.round(15 * DefaultStyle.dp) + rightPadding: Math.round(15 * DefaultStyle.dp) + topPadding: Math.round(15 * DefaultStyle.dp) + bottomPadding: Math.round(15 * DefaultStyle.dp) + background: Rectangle { + id: inputBackground + anchors.fill: parent + radius: Math.round(35 * DefaultStyle.dp) + color: DefaultStyle.grey_0 + MouseArea { + anchors.fill: parent + onPressed: sendingTextArea.forceActiveFocus() + cursorShape: Qt.IBeamCursor } - Layout.fillHeight: true - contentHeight: sendingTextArea.contentHeight - contentWidth: width + } + contentItem: RowLayout { + Flickable { + id: sendingAreaFlickable + Layout.preferredHeight: Math.min(Math.round(60 * DefaultStyle.dp), contentHeight) + Layout.fillHeight: true + Layout.fillWidth: true + contentHeight: sendingTextArea.contentHeight + contentWidth: width - function ensureVisible(r) { - if (contentX >= r.x) - contentX = r.x; - else if (contentX+width <= r.x+r.width) - contentX = r.x+r.width-width; - if (contentY >= r.y) - contentY = r.y; - else if (contentY+height <= r.y+r.height) - contentY = r.y+r.height-height; - } - - TextArea { - id: sendingTextArea - width: sendingAreaFlickable.width - height: sendingAreaFlickable.height - textFormat: TextEdit.AutoText - //: Say something… : placeholder text for sending message text area - placeholderText: qsTr("chat_view_send_area_placeholder_text") - placeholderTextColor: DefaultStyle.main2_400 - color: DefaultStyle.main2_700 - font { - pixelSize: Typography.p1.pixelSize - weight: Typography.p1.weight + function ensureVisible(r) { + if (contentX >= r.x) + contentX = r.x; + else if (contentX+width <= r.x+r.width) + contentX = r.x+r.width-width; + if (contentY >= r.y) + contentY = r.y; + else if (contentY+height <= r.y+r.height) + contentY = r.y+r.height-height; } - onCursorRectangleChanged: sendingAreaFlickable.ensureVisible(cursorRectangle) - wrapMode: TextEdit.WordWrap - Keys.onPressed: (event) => { - if ((event.key == Qt.Key_Enter || event.key == Qt.Key_Return)) - if(!(event.modifiers & Qt.ShiftModifier)) { - mainItem.sendText() - event.accepted = true + + TextArea { + id: sendingTextArea + width: sendingAreaFlickable.width + height: sendingAreaFlickable.height + textFormat: TextEdit.AutoText + onTextChanged: mainItem.text = text + Component.onCompleted: mainItem.textArea = sendingTextArea + //: Say something… : placeholder text for sending message text area + placeholderText: qsTr("chat_view_send_area_placeholder_text") + placeholderTextColor: DefaultStyle.main2_400 + color: DefaultStyle.main2_700 + font { + pixelSize: Typography.p1.pixelSize + weight: Typography.p1.weight + } + onCursorRectangleChanged: sendingAreaFlickable.ensureVisible(cursorRectangle) + wrapMode: TextEdit.WordWrap + Keys.onPressed: (event) => { + if ((event.key == Qt.Key_Enter || event.key == Qt.Key_Return)) + if(!(event.modifiers & Qt.ShiftModifier)) { + mainItem.sendMessage() + event.accepted = true + } + } + Connections { + target: mainItem + function onTextChanged() { + if (mainItem.text !== text) text = mainItem.text + } + function onSendMessage() { + sendingTextArea.clear() + } + } + } + } + RowLayout { + id: stackButton + spacing: 0 + BigButton { + id: recordButton + enabled: !callsModel.currentCall + ToolTip.visible: !enabled && hovered + //: Cannot record a message while a call is ongoing + ToolTip.text: qsTr("cannot_record_while_in_call_tooltip") + visible: sendingTextArea.text.length === 0 + style: ButtonStyle.noBackground + hoverEnabled: true + icon.source: AppIcons.microphone + onClicked: { + sendingAreaStackView.push(voiceMessageRecordComp) + } + } + BigButton { + visible: sendingTextArea.text.length !== 0 + style: ButtonStyle.noBackgroundOrange + icon.source: AppIcons.paperPlaneRight + onClicked: { + mainItem.sendMessage() } } } } - RowLayout { - id: stackButton - spacing: 0 - BigButton { - visible: sendingTextArea.text.length === 0 - style: ButtonStyle.noBackground - icon.source: AppIcons.microphone - onClicked: { - console.log("TODO : go to record message") - } + } + } + } + Component { + id: voiceMessageRecordComp + RowLayout { + spacing: Math.round(16 * DefaultStyle.dp) + RoundButton { + style: ButtonStyle.player + shadowEnabled: true + padding: Math.round(4 * DefaultStyle.dp) + icon.width: Math.round(22 * DefaultStyle.dp) + icon.height: Math.round(22 * DefaultStyle.dp) + icon.source: AppIcons.closeX + width: Math.round(30 * DefaultStyle.dp) + Layout.preferredWidth: width + Layout.preferredHeight: height + onClicked: { + if (voiceMessage.chatMessage) mainItem.chat.core.lDeleteMessage(voiceMessage.chatMessage) + sendingAreaStackView.pop() + } + } + ChatAudioContent { + id: voiceMessage + recording: true + Layout.fillWidth: true + Layout.preferredHeight: Math.round(48 * DefaultStyle.dp) + chatMessageContentGui: chatMessage ? chatMessage.core.getVoiceRecordingContent() : null + onVoiceRecordingMessageCreationRequested: (recorderGui) => { + chatMessageObj = UtilsCpp.createVoiceRecordingMessage(recorderGui, mainItem.chat) + } + } + BigButton { + id: sendButton + style: ButtonStyle.noBackgroundOrange + icon.source: AppIcons.paperPlaneRight + icon.width: Math.round(22 * DefaultStyle.dp) + icon.height: Math.round(22 * DefaultStyle.dp) + // Layout.preferredWidth: icon.width + // Layout.preferredHeight: icon.height + property bool sendVoiceRecordingOnCreated: false + onClicked: { + if (voiceMessage.chatMessage) { + voiceMessage.chatMessage.core.lSend() + sendingAreaStackView.pop() } - BigButton { - visible: sendingTextArea.text.length !== 0 - style: ButtonStyle.noBackgroundOrange - icon.source: AppIcons.paperPlaneRight - onClicked: { - mainItem.sendText() + else { + sendVoiceRecordingOnCreated = true + voiceMessage.stopRecording() + } + } + Connections { + target: voiceMessage + function onChatMessageChanged() { + if (sendButton.sendVoiceRecordingOnCreated) { + voiceMessage.chatMessage.core.lSend() + sendButton.sendVoiceRecordingOnCreated = false + sendingAreaStackView.pop() } } } diff --git a/Linphone/view/Page/Form/Chat/SelectedChatView.qml b/Linphone/view/Page/Form/Chat/SelectedChatView.qml index 11885f143..a1808d6ed 100644 --- a/Linphone/view/Page/Form/Chat/SelectedChatView.qml +++ b/Linphone/view/Page/Form/Chat/SelectedChatView.qml @@ -29,7 +29,7 @@ RowLayout { } onGroupCall: { - mainWindow.showConfirmationLambdaPopup(qsTr(""), + mainWindow.showConfirmationLambdaPopup("", qsTr("chat_view_group_call_toast_message"), "", function(confirmed) { @@ -115,153 +115,163 @@ RowLayout { } ] - content: ColumnLayout { - spacing: 0 + content: Control.SplitView { anchors.fill: parent - Item { - Layout.fillWidth: true - Layout.fillHeight: true - ChatMessagesListView { - id: chatMessagesListView - clip: true - height: contentHeight - backgroundColor: splitPanel.panelColor - width: parent.width - anchors.leftMargin - anchors.rightMargin - chat: mainItem.chat - anchors.fill: parent - anchors.leftMargin: Math.round(18 * DefaultStyle.dp) - anchors.rightMargin: Math.round(18 * DefaultStyle.dp) - Control.ScrollBar.vertical: scrollbar - - Popup { - id: emojiPickerPopup - y: Math.round(chatMessagesListView.y + chatMessagesListView.height - height - 8*DefaultStyle.dp) - x: Math.round(chatMessagesListView.x + 8*DefaultStyle.dp) - width: Math.round(393 * DefaultStyle.dp) - height: Math.round(291 * DefaultStyle.dp) - visible: messageSender.emojiPickerButtonChecked - closePolicy: Popup.CloseOnPressOutside - onClosed: messageSender.emojiPickerButtonChecked = false - padding: 10 * DefaultStyle.dp - background: Item { - anchors.fill: parent - Rectangle { - id: buttonBackground - anchors.fill: parent - color: DefaultStyle.grey_0 - radius: Math.round(20 * DefaultStyle.dp) - } - MultiEffect { - anchors.fill: buttonBackground - source: buttonBackground - shadowEnabled: true - shadowColor: DefaultStyle.grey_1000 - shadowBlur: 0.1 - shadowOpacity: 0.5 - } - } - contentItem: EmojiPicker { - id: emojiPicker - editor: messageSender.textArea - } - } - } - ScrollBar { - id: scrollbar - visible: chatMessagesListView.contentHeight > parent.height - active: visible - anchors.top: chatMessagesListView.top - anchors.bottom: chatMessagesListView.bottom - anchors.right: parent.right - anchors.rightMargin: Math.round(5 * DefaultStyle.dp) - policy: Control.ScrollBar.AsNeeded - } + orientation: Qt.Vertical + handle: Rectangle { + implicitHeight: Math.round(8 * DefaultStyle.dp) + color: Control.SplitHandle.hovered ? DefaultStyle.grey_200 : DefaultStyle.grey_100 } - Control.Control { - id: selectedFilesArea - visible: selectedFiles.count > 0 - Layout.fillWidth: true - Layout.preferredHeight: Math.round(104 * DefaultStyle.dp) - topPadding: Math.round(12 * DefaultStyle.dp) - bottomPadding: Math.round(12 * DefaultStyle.dp) - leftPadding: Math.round(19 * DefaultStyle.dp) - rightPadding: Math.round(19 * DefaultStyle.dp) - - Button { - anchors.top: parent.top - anchors.right: parent.right - anchors.topMargin: selectedFilesArea.topPadding - anchors.rightMargin: selectedFilesArea.rightPadding - icon.source: AppIcons.closeX - style: ButtonStyle.noBackground - onClicked: { - contents.clear() + ColumnLayout { + spacing: 0 + Control.SplitView.fillHeight: true + Item { + Layout.fillWidth: true + Layout.fillHeight: true + ChatMessagesListView { + id: chatMessagesListView + clip: true + height: contentHeight + backgroundColor: splitPanel.panelColor + width: parent.width - anchors.leftMargin - anchors.rightMargin + chat: mainItem.chat + anchors.fill: parent + anchors.leftMargin: Math.round(18 * DefaultStyle.dp) + anchors.rightMargin: Math.round(18 * DefaultStyle.dp) + Control.ScrollBar.vertical: scrollbar + + Popup { + id: emojiPickerPopup + y: Math.round(chatMessagesListView.y + chatMessagesListView.height - height - 8*DefaultStyle.dp) + x: Math.round(chatMessagesListView.x + 8*DefaultStyle.dp) + width: Math.round(393 * DefaultStyle.dp) + height: Math.round(291 * DefaultStyle.dp) + visible: messageSender.emojiPickerButtonChecked + closePolicy: Popup.CloseOnPressOutside + onClosed: messageSender.emojiPickerButtonChecked = false + padding: 10 * DefaultStyle.dp + background: Item { + anchors.fill: parent + Rectangle { + id: buttonBackground + anchors.fill: parent + color: DefaultStyle.grey_0 + radius: Math.round(20 * DefaultStyle.dp) + } + MultiEffect { + anchors.fill: buttonBackground + source: buttonBackground + shadowEnabled: true + shadowColor: DefaultStyle.grey_1000 + shadowBlur: 0.1 + shadowOpacity: 0.5 + } + } + contentItem: EmojiPicker { + id: emojiPicker + editor: messageSender.textArea + } + } + } + ScrollBar { + id: scrollbar + visible: chatMessagesListView.contentHeight > parent.height + active: visible + anchors.top: chatMessagesListView.top + anchors.bottom: chatMessagesListView.bottom + anchors.right: parent.right + anchors.rightMargin: Math.round(5 * DefaultStyle.dp) + policy: Control.ScrollBar.AsNeeded } } - background: Item{ - anchors.fill: parent - Rectangle { - color: DefaultStyle.grey_0 - border.color: DefaultStyle.main2_100 - border.width: Math.round(2 * DefaultStyle.dp) - radius: Math.round(20 * DefaultStyle.dp) - height: parent.height / 2 + Control.Control { + id: selectedFilesArea + visible: selectedFiles.count > 0 + Layout.fillWidth: true + Layout.preferredHeight: Math.round(104 * DefaultStyle.dp) + topPadding: Math.round(12 * DefaultStyle.dp) + bottomPadding: Math.round(12 * DefaultStyle.dp) + leftPadding: Math.round(19 * DefaultStyle.dp) + rightPadding: Math.round(19 * DefaultStyle.dp) + + Button { anchors.top: parent.top - anchors.left: parent.left anchors.right: parent.right - } - Rectangle { - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: parent.right - height: 2 * parent.height / 3 - } - } - contentItem: ListView { - id: selectedFiles - orientation: ListView.Horizontal - spacing: Math.round(16 * DefaultStyle.dp) - model: ChatMessageContentProxy { - id: contents - filterType: ChatMessageContentProxy.FilterContentType.File - } - delegate: Item { - width: Math.round(80 * DefaultStyle.dp) - height: Math.round(80 * DefaultStyle.dp) - FileView { - contentGui: modelData - anchors.left: parent.left - anchors.bottom: parent.bottom - width: Math.round(69 * DefaultStyle.dp) - height: Math.round(69 * DefaultStyle.dp) + anchors.topMargin: selectedFilesArea.topPadding + anchors.rightMargin: selectedFilesArea.rightPadding + icon.source: AppIcons.closeX + style: ButtonStyle.noBackground + onClicked: { + contents.clear() } - RoundButton { - icon.source: AppIcons.closeX - icon.width: Math.round(12 * DefaultStyle.dp) - icon.height: Math.round(12 * DefaultStyle.dp) + } + background: Item{ + anchors.fill: parent + Rectangle { + color: DefaultStyle.grey_0 + border.color: DefaultStyle.main2_100 + border.width: Math.round(2 * DefaultStyle.dp) + radius: Math.round(20 * DefaultStyle.dp) + height: parent.height / 2 anchors.top: parent.top + anchors.left: parent.left anchors.right: parent.right - style: ButtonStyle.numericPad - shadowEnabled: true - padding: Math.round(3 * DefaultStyle.dp) - onClicked: contents.removeContent(modelData) + } + Rectangle { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + height: 2 * parent.height / 3 } } - Control.ScrollBar.horizontal: selectedFilesScrollbar - } - ScrollBar { - id: selectedFilesScrollbar - active: true - anchors.bottom: selectedFilesArea.bottom - anchors.left: selectedFilesArea.left - anchors.right: selectedFilesArea.right + contentItem: ListView { + id: selectedFiles + orientation: ListView.Horizontal + spacing: Math.round(16 * DefaultStyle.dp) + model: ChatMessageContentProxy { + id: contents + filterType: ChatMessageContentProxy.FilterContentType.File + } + delegate: Item { + width: Math.round(80 * DefaultStyle.dp) + height: Math.round(80 * DefaultStyle.dp) + FileView { + contentGui: modelData + anchors.left: parent.left + anchors.bottom: parent.bottom + width: Math.round(69 * DefaultStyle.dp) + height: Math.round(69 * DefaultStyle.dp) + } + RoundButton { + icon.source: AppIcons.closeX + icon.width: Math.round(12 * DefaultStyle.dp) + icon.height: Math.round(12 * DefaultStyle.dp) + anchors.top: parent.top + anchors.right: parent.right + style: ButtonStyle.numericPad + shadowEnabled: true + padding: Math.round(3 * DefaultStyle.dp) + onClicked: contents.removeContent(modelData) + } + } + Control.ScrollBar.horizontal: selectedFilesScrollbar + } + ScrollBar { + id: selectedFilesScrollbar + active: true + anchors.bottom: selectedFilesArea.bottom + anchors.left: selectedFilesArea.left + anchors.right: selectedFilesArea.right + } } } ChatDroppableTextArea { id: messageSender - Layout.fillWidth: true - Layout.preferredHeight: mainItem.chat.core.isReadOnly ? 0 : height + Control.SplitView.preferredHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp) + Control.SplitView.minimumHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp) + chat: mainItem.chat Component.onCompleted: { + if (mainItem.chat) text = mainItem.chat.core.sendingText } onTextChanged: { @@ -270,12 +280,11 @@ RowLayout { } mainItem.chat.core.sendingText = text } - onSendText: { + onSendMessage: { var filesContents = contents.getAll() if (filesContents.length === 0) mainItem.chat.core.lSendTextMessage(text) else mainItem.chat.core.lSendMessage(text, filesContents) - messageSender.textArea.clear() contents.clear() } onDropped: (files) => { diff --git a/Linphone/view/Page/Layout/Settings/ChatSettingsLayout.qml b/Linphone/view/Page/Layout/Settings/ChatSettingsLayout.qml new file mode 100644 index 000000000..f1b8142cd --- /dev/null +++ b/Linphone/view/Page/Layout/Settings/ChatSettingsLayout.qml @@ -0,0 +1,39 @@ + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls.Basic as Control +import SettingsCpp +import Linphone + +AbstractSettingsLayout { + id: mainItem + width: parent?.width + contentModel: [ + { + //: Attached files + title: qsTr("settings_chat_attached_files_title"), + subTitle: "", + contentComponent: attachedFilesParamComp, + // hideTopMargin: true + } + ] + + Component { + id: attachedFilesParamComp + SwitchSetting { + //: "Automatic download" + titleText: qsTr("settings_chat_attached_files_auto_download_title") + //: "Automatically download transferred or received files in conversations" + subTitleText: qsTr("settings_chat_attached_files_auto_download_subtitle") + propertyName: "autoDownloadReceivedFiles" + propertyOwner: SettingsCpp + + Connections { + target: mainItem + function onSave() { + SettingsCpp.save() + } + } + } + } +} \ No newline at end of file diff --git a/Linphone/view/Page/Layout/Settings/ContactsSettingsLayout.qml b/Linphone/view/Page/Layout/Settings/ContactsSettingsLayout.qml index 60f776667..e764dece2 100644 --- a/Linphone/view/Page/Layout/Settings/ContactsSettingsLayout.qml +++ b/Linphone/view/Page/Layout/Settings/ContactsSettingsLayout.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls.Basic as Control -import SettingsCpp 1.0 +import SettingsCpp import Linphone AbstractSettingsLayout { diff --git a/Linphone/view/Style/DefaultStyle.qml b/Linphone/view/Style/DefaultStyle.qml index 8c8478111..205b6da1f 100644 --- a/Linphone/view/Style/DefaultStyle.qml +++ b/Linphone/view/Style/DefaultStyle.qml @@ -1,5 +1,6 @@ pragma Singleton import QtQuick +import Linphone QtObject { property color main1_100: "#FFEACB" diff --git a/Linphone/view/Style/buttonStyle.js b/Linphone/view/Style/buttonStyle.js index ccecee2b0..bc9810e23 100644 --- a/Linphone/view/Style/buttonStyle.js +++ b/Linphone/view/Style/buttonStyle.js @@ -38,6 +38,23 @@ } } + // White with orange icon + var player = { + color: { + normal: Linphone.DefaultStyle.grey_0, + hovered: Linphone.DefaultStyle.main1_100, + pressed: Linphone.DefaultStyle.main1_500_main + }, + text: { + normal: Linphone.DefaultStyle.main1_500_main, + pressed: Linphone.DefaultStyle.main1_500_main + }, + image: { + normal: Linphone.DefaultStyle.main1_500_main, + pressed: Linphone.DefaultStyle.main1_500_main + } + } + // Light orange var tertiary = { color: {