mirror of
				https://git.jami.net/savoirfairelinux/jami-client-qt.git
				synced 2025-10-30 07:53:33 +08:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			stable/202
			...
			beta/20230
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c7c8e2e8f6 | |||
| b16131e63a | |||
| 86b84ea17e | |||
| 9ffbf4ae1c | 
| @ -449,12 +449,6 @@ else() # APPLE | ||||
|       HINTS ${sparkle_dir}) | ||||
|     add_definitions(-DENABLE_SPARKLE) | ||||
|     message("Sparkle is here:" ${SPARKLE_FRAMEWORK}) | ||||
|     set(PUBLIC_KEY_PATH "${sparkle_dir}/dsa_pub.pem") | ||||
|     set_source_files_properties( | ||||
|       ${PUBLIC_KEY_PATH} | ||||
|       PROPERTIES | ||||
|       MACOSX_PACKAGE_LOCATION Resources) | ||||
|     set(PUBLIC_KEY ${PUBLIC_KEY_PATH}) | ||||
|   endif() | ||||
|   if(BETA) | ||||
|     message(STATUS "Beta config enabled") | ||||
| @ -722,7 +716,7 @@ else() | ||||
|     ${CMAKE_CURRENT_SOURCE_DIR}/resources/images/jami.icns) | ||||
|   set(libs ${QT_LIBS} ${SYSTEM_CONFIGURATUION} qrencode ${LIBCLIENT_NAME}) | ||||
|   if(ENABLE_SPARKLE) | ||||
|     set(resources ${resources} ${PUBLIC_KEY} ${SPARKLE_FRAMEWORK}) | ||||
|     set(resources ${resources} ${SPARKLE_FRAMEWORK}) | ||||
|     set(libs ${libs} ${SPARKLE_FRAMEWORK}) | ||||
|   endif(ENABLE_SPARKLE) | ||||
|   target_sources(${PROJECT_NAME} PRIVATE ${resources}) | ||||
| @ -766,6 +760,7 @@ else() | ||||
|       else() | ||||
|           set_target_properties(${PROJECT_NAME} PROPERTIES | ||||
|                 SPARKLE_URL "${SPARKLE_URL}" | ||||
|                 SPARKLE_PUBLIC_KEY "${SPARKLE_PUBLIC_KEY}" | ||||
|                 XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/Jami.entitlements" | ||||
|                 XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME TRUE) | ||||
|       endif() | ||||
|  | ||||
							
								
								
									
										2
									
								
								daemon
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								daemon
									
									
									
									
									
								
							 Submodule daemon updated: 33f089ef50...5e35e71d09
									
								
							| @ -1,36 +0,0 @@ | ||||
| -----BEGIN PUBLIC KEY----- | ||||
| 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 | ||||
| ZnO5P9x/rPIJLCXY4xQYBryQCMu6JC5ibWzP | ||||
| -----END PUBLIC KEY----- | ||||
| @ -1,11 +0,0 @@ | ||||
| #!/bin/bash | ||||
| set -e | ||||
| set -o pipefail | ||||
| if [ "$#" -ne 2 ]; then | ||||
|   echo "Usage: $0 update_archive private_key" | ||||
|   exit 1 | ||||
| fi | ||||
|  | ||||
| openssl=/usr/bin/openssl | ||||
| $openssl dgst -sha1 -binary < "$1" | $openssl dgst -dss1 -sign "$2" | base64 $BASE64_OPTS | ||||
|  | ||||
| @ -6,13 +6,12 @@ REPO_FOLDER=$1 | ||||
| SPARKLE_FILE=$2 | ||||
| REPO_URL=$3 | ||||
| PACKAGE=$4 | ||||
| DSA_KEY=$5 | ||||
| CHANNEL_NAME=$6 | ||||
| VERSION=$7 | ||||
| BUILD=$8 | ||||
| CHANNEL_NAME=$5 | ||||
| VERSION=$6 | ||||
| BUILD=$7 | ||||
|  | ||||
| if [ ! -f ${PACKAGE} -o ! -f ${DSA_KEY} ]; then | ||||
|     echo "Can't find package or dsa key, aborting..." | ||||
| if [ ! -f ${PACKAGE} ]; then | ||||
|     echo "Can't find package, aborting..." | ||||
|     exit 1 | ||||
| fi | ||||
|  | ||||
| @ -20,7 +19,6 @@ if [ -f ${REPO_FOLDER}/${SPARKLE_FILE} ]; then | ||||
|     ITEMS=$(sed -n "/<item>/,/<\/item>/p" ${REPO_FOLDER}/${SPARKLE_FILE}) | ||||
| fi | ||||
|  | ||||
| PACKAGE_SIZE=`stat -f%z ${PACKAGE}` | ||||
| DATE_RFC2822=`date "+%a, %d %b %Y %T %z"` | ||||
|  | ||||
| cat << EOFILE > ${REPO_FOLDER}/${SPARKLE_FILE} | ||||
| @ -37,7 +35,7 @@ cat << EOFILE > ${REPO_FOLDER}/${SPARKLE_FILE} | ||||
|             <sparkle:version>${BUILD}</sparkle:version> | ||||
|             <sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString> | ||||
|             <sparkle:minimumSystemVersion>10.15.0</sparkle:minimumSystemVersion> | ||||
|             <enclosure url="${REPO_URL}/$(basename ${PACKAGE})" length="$PACKAGE_SIZE" type="application/octet-stream" sparkle:dsaSignature="$(./sign_update.sh ${PACKAGE} ${DSA_KEY})" /> | ||||
|             <enclosure url="${REPO_URL}/$(basename ${PACKAGE})" type="application/octet-stream" $(./sign_update ${PACKAGE}) /> | ||||
|         </item> | ||||
| $(echo -e "${ITEMS}") | ||||
|     </channel> | ||||
|  | ||||
| @ -24,8 +24,8 @@ | ||||
| 	<string>public.app-category.social-networking</string> | ||||
| 	<key>NSHumanReadableCopyright</key> | ||||
| 	<string>${MACOSX_BUNDLE_COPYRIGHT}</string> | ||||
| 	<key>SUPublicDSAKeyFile</key> | ||||
| 	<string>dsa_pub.pem</string> | ||||
| 	<key>SUPublicEDKey</key> | ||||
| 	<string>${SPARKLE_PUBLIC_KEY}</string> | ||||
| 	<key>SUFeedURL</key> | ||||
| 	<string>${SPARKLE_URL}</string> | ||||
| 	<key>NSPrincipalClass</key> | ||||
|  | ||||
| @ -36,6 +36,7 @@ extern const QString defaultDownloadPath; | ||||
| #define KEYS \ | ||||
|     X(MinimizeOnClose, true) \ | ||||
|     X(DownloadPath, defaultDownloadPath) \ | ||||
|     X(ScreenshotPath, {}) \ | ||||
|     X(EnableNotifications, true) \ | ||||
|     X(EnableTypingIndicator, true) \ | ||||
|     X(EnableReadReceipt, true) \ | ||||
|  | ||||
| @ -553,7 +553,7 @@ CallAdapter::fillParticipantData(QJsonObject& participant) const | ||||
|     auto uri = participant[URI].toString(); | ||||
|     participant[ISLOCAL] = false; | ||||
|     if (uri == accInfo.profileInfo.uri && participant[DEVICE] == getCurrentDeviceId(accInfo)) { | ||||
|         participant[BESTNAME] = tr("me"); | ||||
|         participant[BESTNAME] = tr("Me"); | ||||
|         participant[ISLOCAL] = true; | ||||
|     } else { | ||||
|         try { | ||||
| @ -1145,6 +1145,27 @@ CallAdapter::updateAdvancedInformation() | ||||
|     } | ||||
| } | ||||
|  | ||||
| bool | ||||
| CallAdapter::takeScreenshot(const QImage& image, const QString& path) | ||||
| { | ||||
|     QString name = QString("%1 %2") | ||||
|                        .arg(tr("Screenshot")) | ||||
|                        .arg(QDateTime::currentDateTime().toString(Qt::ISODate)); | ||||
|  | ||||
|     bool fileAlreadyExists = true; | ||||
|     int nb = 0; | ||||
|     QString filePath = QString("%1%2.png").arg(path).arg(name); | ||||
|     while (fileAlreadyExists) { | ||||
|         filePath = QString("%1%2.png").arg(path).arg(name); | ||||
|         if (nb) | ||||
|             filePath = QString("%1(%2).png").arg(filePath).arg(QString::number(nb)); | ||||
|         QFileInfo check_file(filePath); | ||||
|         fileAlreadyExists = check_file.exists() && check_file.isFile(); | ||||
|         nb++; | ||||
|     } | ||||
|     return image.save(filePath, "PNG"); | ||||
| } | ||||
|  | ||||
| void | ||||
| CallAdapter::preventScreenSaver(bool state) | ||||
| { | ||||
|  | ||||
| @ -98,6 +98,8 @@ public: | ||||
|     Q_INVOKABLE void setCallInfo(); | ||||
|     Q_INVOKABLE void updateAdvancedInformation(); | ||||
|  | ||||
|     Q_INVOKABLE bool takeScreenshot(const QImage &image, const QString &path); | ||||
|  | ||||
| Q_SIGNALS: | ||||
|     void callStatusChanged(int index, const QString& accountId, const QString& convUid); | ||||
|     void callInfosChanged(const QVariant& infos, const QString& accountId, const QString& convUid); | ||||
|  | ||||
| @ -55,8 +55,9 @@ BaseModalDialog { | ||||
|                     anchors.centerIn: parent | ||||
|  | ||||
|                     Text { | ||||
|                         Layout.preferredWidth: root.width / 4 | ||||
|                         Layout.maximumWidth: root.width / 2 | ||||
|                         Layout.leftMargin: JamiTheme.settingsMarginSize | ||||
|                         elide: Text.ElideRight | ||||
|  | ||||
|                         text: MessagesAdapter.getFormattedDay(modelData.timestamp.toString()) | ||||
|                               + " - " + MessagesAdapter.getFormattedTime(modelData.timestamp.toString()) | ||||
| @ -72,7 +73,7 @@ BaseModalDialog { | ||||
|                             id: metrics | ||||
|                             elide: Text.ElideRight | ||||
|                             elideWidth: 3 * rowBody.width / 4 - 2 * JamiTheme.preferredMarginSize | ||||
|                             text: modelData.body | ||||
|                             text: modelData.body === "" ? JamiStrings.deletedMessage : modelData.body | ||||
|                         } | ||||
|  | ||||
|                         text: metrics.elidedText | ||||
|  | ||||
| @ -31,7 +31,7 @@ Item { | ||||
|     property real contentWidth: bubble.width | ||||
|     property var emojiTexts: ownEmojiList | ||||
|  | ||||
|     visible: emojis ? emojis.length : false | ||||
|     visible: emojis.length && Body !== "" | ||||
|  | ||||
|     property string emojis: { | ||||
|         var space = "" | ||||
|  | ||||
| @ -88,7 +88,7 @@ Item { | ||||
|                     id: metrics | ||||
|                     elide: Text.ElideRight | ||||
|                     elideWidth: JamiTheme.preferredFieldWidth - JamiTheme.preferredMarginSize | ||||
|                     text: ReplyToBody | ||||
|                     text: ReplyToBody === "" && ReplyToAuthor !== "" ? "*(Deleted Message)*" : ReplyToBody | ||||
|                 } | ||||
|  | ||||
|                 textFormat: Text.MarkdownText | ||||
|  | ||||
| @ -174,10 +174,10 @@ Control { | ||||
|                         anchors.verticalCenter: parent.verticalCenter | ||||
|                         anchors.right: isOutgoing ? optionButtonItem.right : undefined | ||||
|                         anchors.left: !isOutgoing ? optionButtonItem.left : undefined | ||||
|                         visible: bubbleArea.bubbleHovered | ||||
|                         visible: Body !== "" && (bubbleArea.bubbleHovered | ||||
|                                  || hovered | ||||
|                                  || reply.hovered | ||||
|                                  || bgHandler.hovered | ||||
|                                  || bgHandler.hovered) | ||||
|                         source: JamiResources.more_vert_24dp_svg | ||||
|                         width: optionButtonItem.width / 2 | ||||
|                         height: optionButtonItem.height | ||||
| @ -202,10 +202,10 @@ Control { | ||||
|                         anchors.verticalCenter: parent.verticalCenter | ||||
|                         anchors.right: isOutgoing ? more.left : undefined | ||||
|                         anchors.left: !isOutgoing ? more.right : undefined | ||||
|                         visible: bubbleArea.bubbleHovered | ||||
|                         visible: Body !== "" && (bubbleArea.bubbleHovered | ||||
|                                  || hovered | ||||
|                                  || more.hovered | ||||
|                                  || bgHandler.hovered | ||||
|                                  || bgHandler.hovered) | ||||
|  | ||||
|                         onClicked: { | ||||
|                             MessagesAdapter.editId = "" | ||||
|  | ||||
| @ -58,7 +58,7 @@ SBSMessageBase { | ||||
|  | ||||
|             padding: isEmojiOnly ? 0 : JamiTheme.preferredMarginSize | ||||
|             anchors.right: isOutgoing ? parent.right : undefined | ||||
|             text: Body | ||||
|             text: Body === "" ? "*("+ JamiStrings.deletedMessage +")*" : Body | ||||
|             horizontalAlignment: Text.AlignLeft | ||||
|  | ||||
|             HoverHandler { | ||||
|  | ||||
| @ -49,6 +49,7 @@ MenuItem { | ||||
|     property int itemTextMargin: 20 | ||||
|  | ||||
|     signal clicked | ||||
|     property bool itemHovered: menuItemContentRect.hovered | ||||
|  | ||||
|     contentItem: AbstractButton { | ||||
|         id: menuItemContentRect | ||||
|  | ||||
| @ -298,6 +298,8 @@ Item { | ||||
|     property string lowerHand: qsTr("Lower hand") | ||||
|     property string raiseHand: qsTr("Raise hand") | ||||
|     property string layoutSettings: qsTr("Layout settings") | ||||
|     property string tileScreenshot: qsTr("Take tile screenshot") | ||||
|     property string screenshotTaken: qsTr("Screenshot saved to %1") | ||||
|  | ||||
|     //advanced information | ||||
|     property string renderersInformation: qsTr("Renderers information") | ||||
| @ -350,6 +352,7 @@ Item { | ||||
|     property string backendError: qsTr("This is the error from the backend: %0") | ||||
|     property string disabledAccount: qsTr("The account is disabled") | ||||
|     property string noNetworkConnectivity: qsTr("No network connectivity") | ||||
|     property string deletedMessage: qsTr("Deleted message") | ||||
|  | ||||
|     // Chatview footer | ||||
|     property string jumpToLatest: qsTr("Jump to latest") | ||||
| @ -508,15 +511,16 @@ Item { | ||||
|     // Context Menu | ||||
|     property string saveFile: qsTr("Save file") | ||||
|     property string openLocation: qsTr("Open location") | ||||
|     property string me: qsTr("Me") | ||||
|  | ||||
|     // Updates | ||||
|     property string betaInstall: qsTr("Install beta version") | ||||
|     property string checkForUpdates: qsTr("Check for updates now") | ||||
|     property string enableAutoUpdates: qsTr("Enable/Disable automatic updates") | ||||
|     property string tipAutoUpdate: qsTr("toggle automatic updates") | ||||
|     property string tipAutoUpdate: qsTr("Toggle automatic updates") | ||||
|     property string updatesTitle: qsTr("Updates") | ||||
|     property string updateDialogTitle: qsTr("Update") | ||||
|     property string updateFound: qsTr("A new version of Jami was found\n Would you like to update now?") | ||||
|     property string updateFound: qsTr("A new version of Jami was found\nWould you like to update now?") | ||||
|     property string updateNotFound: qsTr("No new version of Jami was found") | ||||
|     property string updateCheckError: qsTr("An error occured when checking for a new version") | ||||
|     property string updateNetworkError: qsTr("Network error") | ||||
| @ -538,7 +542,8 @@ Item { | ||||
|     // Recording Settings | ||||
|     property string tipRecordFolder: qsTr("Select a record directory") | ||||
|     property string quality: qsTr("Quality") | ||||
|     property string saveIn: qsTr("Save in") | ||||
|     property string saveRecordingsTo: qsTr("Save recordings to") | ||||
|     property string saveScreenshotsTo: qsTr("Save screenshots to") | ||||
|     property string callRecording: qsTr("Call Recording") | ||||
|     property string alwaysRecordCalls: qsTr("Always record calls") | ||||
|  | ||||
|  | ||||
| @ -146,6 +146,11 @@ Item { | ||||
|     property color spinboxBackgroundColor: darkTheme ? editBackgroundColor : selectedColor | ||||
|     property color spinboxBorderColor: darkTheme ? tintedBlue : Qt.rgba(0, 0.34, 0.6, 0.36) | ||||
|  | ||||
|     //Toast | ||||
|     property color toastColor: darkTheme ? "#f0f0f0" : "#000000" | ||||
|     property color toastRectColor: !darkTheme ? "#f0f0f0" : "#000000" | ||||
|     property real toastFontSize: calcSize(15) | ||||
|  | ||||
|     // Call buttons | ||||
|     property color acceptButtonGreen: "#4caf50" | ||||
|     property color acceptButtonHoverGreen: "#5db761" | ||||
|  | ||||
| @ -174,10 +174,12 @@ MainApplication::init() | ||||
|         Qt::DirectConnection); | ||||
|  | ||||
|     auto downloadPath = settingsManager_->getValue(Settings::Key::DownloadPath); | ||||
|     auto screenshotPath = settingsManager_->getValue(Settings::Key::ScreenshotPath); | ||||
|     auto allowTransferFromTrusted = settingsManager_->getValue(Settings::Key::AutoAcceptFiles) | ||||
|                                         .toBool(); | ||||
|     auto acceptTransferBelow = settingsManager_->getValue(Settings::Key::AcceptTransferBelow).toInt(); | ||||
|     lrcInstance_->accountModel().downloadDirectory = downloadPath.toString() + "/"; | ||||
|     lrcInstance_->accountModel().screenshotDirectory = screenshotPath.toString(); | ||||
|     lrcInstance_->accountModel().autoTransferFromTrusted = allowTransferFromTrusted; | ||||
|     lrcInstance_->accountModel().autoTransferSizeThreshold = acceptTransferBelow; | ||||
|  | ||||
|  | ||||
| @ -39,6 +39,7 @@ Item { | ||||
|  | ||||
|     signal chatButtonClicked | ||||
|     signal fullScreenClicked | ||||
|     signal closeClicked | ||||
|  | ||||
|     function closeContextMenuAndRelatedWindows() { | ||||
|         ContactPickerCreation.closeContactPicker() | ||||
| @ -46,14 +47,22 @@ Item { | ||||
|         SelectScreenWindowCreation.destroySelectScreenWindow() | ||||
|         ScreenRubberBandCreation.destroyScreenRubberBandWindow() | ||||
|         PluginHandlerPickerCreation.closePluginHandlerPicker() | ||||
|         root.closeClicked() | ||||
|         callInformationOverlay.close() | ||||
|     } | ||||
|  | ||||
|     // x, y position does not need to be translated | ||||
|     // since they all fill the call page | ||||
|     function openCallViewContextMenuInPos(x, y) { | ||||
|     function openCallViewContextMenuInPos(x, y, | ||||
|                                           hoveredOverlayUri, | ||||
|                                           hoveredOverlaySinkId, | ||||
|                                           hoveredOverVideoMuted) | ||||
|     { | ||||
|         callViewContextMenu.x = x | ||||
|         callViewContextMenu.y = y | ||||
|         callViewContextMenu.hoveredOverlayUri = hoveredOverlayUri | ||||
|         callViewContextMenu.hoveredOverlaySinkId = hoveredOverlaySinkId | ||||
|         callViewContextMenu.hoveredOverVideoMuted = hoveredOverVideoMuted | ||||
|         callViewContextMenu.openMenu() | ||||
|     } | ||||
|  | ||||
| @ -171,10 +180,16 @@ Item { | ||||
|  | ||||
|         onTransferCallButtonClicked: openContactPicker(ContactList.TRANSFER) | ||||
|         onPluginItemClicked: openPluginsMenu() | ||||
|         onScreenshotTaken: { | ||||
|             toastManager.instantiateToast(); | ||||
|         } | ||||
|         onRecordCallClicked: CallAdapter.recordThisCallToggle() | ||||
|         onOpenSelectionWindow: { | ||||
|             SelectScreenWindowCreation.createSelectScreenWindowObject(appWindow) | ||||
|             SelectScreenWindowCreation.showSelectScreenWindow(callPreviewId, windowSelection) | ||||
|         } | ||||
|         onScreenshotButtonHoveredChanged: { | ||||
|             participantsLayer.screenshotButtonHovered = screenshotButtonHovered | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -37,6 +37,12 @@ ContextMenuAutoLoader { | ||||
|     signal transferCallButtonClicked | ||||
|     signal recordCallClicked | ||||
|     signal openSelectionWindow | ||||
|     signal screenshotTaken | ||||
|     property bool screenshotButtonHovered: screenShot.itemHovered | ||||
|  | ||||
|     property string hoveredOverlayUri: "" | ||||
|     property string hoveredOverlaySinkId: "" | ||||
|     property bool hoveredOverVideoMuted: true | ||||
|  | ||||
|     property list<GeneralMenuItem> menuItems: [ | ||||
|         GeneralMenuItem { | ||||
| @ -194,8 +200,34 @@ ContextMenuAutoLoader { | ||||
|                 CallAdapter.startTimerInformation(); | ||||
|                 callInformationOverlay.open() | ||||
|             } | ||||
|         }, | ||||
|         GeneralMenuItem { | ||||
|             id: screenShot | ||||
|  | ||||
|             canTrigger: hoveredOverlayUri !== "" && hoveredOverVideoMuted === false | ||||
|             itemName: JamiStrings.tileScreenshot | ||||
|             iconSource: JamiResources.baseline_camera_alt_24dp_svg | ||||
|  | ||||
|             MaterialToolTip { | ||||
|                 id: tooltip | ||||
|  | ||||
|                 parent: screenShot | ||||
|                 visible: screenShot.itemHovered | ||||
|                 delay: Qt.styleHints.mousePressAndHoldInterval | ||||
|                 property bool isMe: CurrentAccount.uri === hoveredOverlayUri | ||||
|                 text: isMe ? JamiStrings.me | ||||
|                            : UtilsAdapter.getBestNameForUri(CurrentAccount.id, hoveredOverlayUri) | ||||
|             } | ||||
|  | ||||
|             onClicked: { | ||||
|                 if (CallAdapter.takeScreenshot(videoProvider.captureRawVideoFrame(hoveredOverlaySinkId), | ||||
|                                                UtilsAdapter.getDirScreenshot())) { | ||||
|                     screenshotTaken() | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     ] | ||||
|  | ||||
|  | ||||
|     Component.onCompleted: menuItemsToLoad = menuItems | ||||
| } | ||||
|  | ||||
| @ -62,7 +62,9 @@ RowLayout { | ||||
|  | ||||
|             anchors.centerIn: parent | ||||
|  | ||||
|             text: shortcut | ||||
|             text: shortcut2 === "" ? | ||||
|                       shortcut : | ||||
|                       shortcut + " + " + shortcut2 | ||||
|             font.pointSize: JamiTheme.textFontSize + 3 | ||||
|             font.weight: Font.DemiBold | ||||
|             color: JamiTheme.textColor | ||||
|  | ||||
| @ -40,42 +40,52 @@ Window { | ||||
|  | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + J" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Open account list") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + L" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Focus conversations list") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + R" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Requests list") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + ↑" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Previous conversation") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + ↓" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Next conversation") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + F" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Search bar") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "F11" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Full screen") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + +" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Increase font size") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + -" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Decrease font size") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + 0" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Reset font size") | ||||
|         } | ||||
|     } | ||||
| @ -85,34 +95,42 @@ Window { | ||||
|  | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + Shift + C" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Start an audio call") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + Shift + X" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Start a video call") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + Shift + L" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Clear history") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + Shift + B" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Block contact") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + Shift + Delete" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Remove conversation") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Shift + Ctrl + A" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Accept contact request") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "↑" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Edit last message") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Esc" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Cancel message edition") | ||||
|         } | ||||
|     } | ||||
| @ -122,26 +140,32 @@ Window { | ||||
|  | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + M" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Media settings") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + G" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("General settings") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + I" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Account settings") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + P" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Plugin settings") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + Shift + N" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Open account creation wizard") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "F10" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Open keyboard shortcut table") | ||||
|         } | ||||
|     } | ||||
| @ -151,24 +175,34 @@ Window { | ||||
|  | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + Y" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Answer an incoming call") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + D" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("End call") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl + Shift + D" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Decline the call request") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "M" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Mute microphone") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "V" | ||||
|             shortcut2: "" | ||||
|             description: qsTr("Stop camera") | ||||
|         } | ||||
|         ListElement { | ||||
|             shortcut: "Ctrl" | ||||
|             shortcut2: qsTr("Mouse middle click") | ||||
|             description: qsTr("Take tile screenshot") | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Rectangle { | ||||
|  | ||||
| @ -215,8 +215,7 @@ JamiListView { | ||||
|             readonly property int mergeType: Interaction.Type.MERGE | ||||
|             readonly property int editedType: Interaction.Type.EDITED | ||||
|             readonly property int reactionType: Interaction.Type.REACTION | ||||
|             expression: Body !== "" | ||||
|                         && Type !== mergeType | ||||
|             expression: Type !== mergeType | ||||
|                         && Type !== editedType | ||||
|                         && Type !== reactionType | ||||
|         } | ||||
|  | ||||
| @ -166,7 +166,10 @@ Rectangle { | ||||
|                 onTapped: function (eventPoint, button) { | ||||
|                     if (button === Qt.RightButton) { | ||||
|                         callOverlay.openCallViewContextMenuInPos(eventPoint.position.x, | ||||
|                                                                  eventPoint.position.y) | ||||
|                                                                  eventPoint.position.y, | ||||
|                                                                  participantsLayer.hoveredOverlayUri, | ||||
|                                                                  participantsLayer.hoveredOverlaySinkId, | ||||
|                                                                  participantsLayer.hoveredOverVideoMuted) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| @ -184,6 +187,7 @@ Rectangle { | ||||
|  | ||||
|             ParticipantsLayer { | ||||
|                 id: participantsLayer | ||||
|  | ||||
|                 anchors.fill: parent | ||||
|                 anchors.centerIn: parent | ||||
|                 anchors.margins: 1 | ||||
| @ -191,9 +195,18 @@ Rectangle { | ||||
|                 participantsSide: callOverlay.participantsSide | ||||
|             } | ||||
|  | ||||
|             ToastManager { | ||||
|                 id: toastManager | ||||
|  | ||||
|                 anchors.fill: parent | ||||
|  | ||||
|                 function instantiateToast() { | ||||
|                     instantiate(JamiStrings.screenshotTaken.arg(UtilsAdapter.getDirScreenshot()),1000,400) | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             LocalVideo { | ||||
|                 id: previewRenderer | ||||
|  | ||||
|                 visible: (CurrentCall.isSharing || !CurrentCall.isVideoMuted) | ||||
|                          && !CurrentCall.isConference | ||||
|  | ||||
| @ -329,6 +342,11 @@ Rectangle { | ||||
|                             openInCallConversation() | ||||
|                     } | ||||
|                 } | ||||
|                 onCloseClicked: { | ||||
|                     participantsLayer.hoveredOverlayUri = "" | ||||
|                     participantsLayer.hoveredOverlaySinkId = "" | ||||
|                     participantsLayer.hoveredOverVideoMuted = true | ||||
|                 } | ||||
|  | ||||
|                 onChatButtonClicked: { | ||||
|                     inCallMessageWebViewStack.visible ? | ||||
|  | ||||
| @ -67,6 +67,18 @@ Item { | ||||
|     property string muteAlertMessage: "" | ||||
|     property bool muteAlertActive: false | ||||
|  | ||||
|     property bool participantHovered: hoverIndicator.hovered | ||||
|     property bool isScreenshotButtonHovered: false | ||||
|  | ||||
|     function takeScreenshot() { | ||||
|         if (!hoveredOverVideoMuted) { | ||||
|             if (CallAdapter.takeScreenshot(videoProvider.captureRawVideoFrame(hoveredOverlaySinkId), | ||||
|                                            UtilsAdapter.getDirScreenshot())) { | ||||
|                 toastManager.instantiateToast(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     onMuteAlertActiveChanged: { | ||||
|         if (muteAlertActive) { | ||||
|             alertTimer.restart() | ||||
| @ -94,9 +106,11 @@ Item { | ||||
|  | ||||
|     Rectangle { | ||||
|         z: -1 | ||||
|         color: JamiTheme.buttonTintedBlue | ||||
|         border.color: JamiTheme.buttonTintedBlue | ||||
|         border.width: 2 | ||||
|         color: "transparent" | ||||
|         radius: 10 | ||||
|         visible:voiceActive | ||||
|         visible: voiceActive || isScreenshotButtonHovered | ||||
|         width: participantIsActive ? mediaDistRender.contentRect.width + 2 : undefined | ||||
|         height: participantIsActive ? mediaDistRender.contentRect.height + 2 : undefined | ||||
|         anchors.centerIn: participantIsActive ? parent : undefined | ||||
| @ -109,7 +123,6 @@ Item { | ||||
|         anchors.margins: 2 | ||||
|         rendererId: root.sinkId | ||||
|         crop: !participantIsActive | ||||
|  | ||||
|         underlayItems: Avatar { | ||||
|             property real componentSize: Math.min(mediaDistRender.contentRect.width / 2, mediaDistRender.contentRect.height / 2) | ||||
|             height:  componentSize | ||||
| @ -140,7 +153,25 @@ Item { | ||||
|             anchors.centerIn: participantIsActive ? parent : undefined | ||||
|             anchors.fill: participantIsActive ? undefined : parent | ||||
|  | ||||
|             TapHandler { | ||||
|                 acceptedButtons: Qt.MiddleButton | ||||
|                 acceptedModifiers: Qt.ControlModifier | ||||
|                 onTapped: { | ||||
|                     takeScreenshot() | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             MultiPointTouchArea { | ||||
|                 anchors.fill: parent | ||||
|                 minimumTouchPoints: 3 | ||||
|                 onPressed: { | ||||
|                     takeScreenshot() | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             HoverHandler { | ||||
|                 id: hoverIndicator | ||||
|  | ||||
|                 onPointChanged: { | ||||
|                     participantRect.opacity = 1 | ||||
|                     fadeOutTimer.restart() | ||||
| @ -164,6 +195,7 @@ Item { | ||||
|                 // Participant buttons for moderation | ||||
|                 ParticipantOverlayMenu { | ||||
|                     id: overlayMenu | ||||
|  | ||||
|                     visible: isMe || meModerator | ||||
|                     anchors.fill: parent | ||||
|  | ||||
| @ -209,6 +241,7 @@ Item { | ||||
|  | ||||
|                     RowLayout { | ||||
|                         id: participantFootInfo | ||||
|  | ||||
|                         height: parent.height | ||||
|                         anchors.verticalCenter: parent.verticalCenter | ||||
|                         Text { | ||||
|  | ||||
| @ -37,6 +37,10 @@ Item { | ||||
|     property bool inLine: CallParticipantsModel.conferenceLayout === CallParticipantsModel.ONE_WITH_SMALL | ||||
|     property bool participantsSide | ||||
|     property bool enableHideSpectators: CallParticipantsModel.count > 1 && CurrentCall.hideSpectators | ||||
|     property string hoveredOverlayUri: "" | ||||
|     property string hoveredOverlaySinkId: "" | ||||
|     property bool hoveredOverVideoMuted: true | ||||
|     property bool screenshotButtonHovered: false | ||||
|  | ||||
|     onVisibleChanged: { | ||||
|         CurrentCall.hideSelf = UtilsAdapter.getAppValue(Settings.HideSelf) | ||||
| @ -51,7 +55,10 @@ Item { | ||||
|  | ||||
|             anchors.fill: parent | ||||
|             anchors.leftMargin: leftMargin_ | ||||
|  | ||||
|             isScreenshotButtonHovered: screenshotButtonHovered && hoveredOverlaySinkId === sinkId_ | ||||
|             opacity: screenshotButtonHovered | ||||
|                      ? hoveredOverlaySinkId !== sinkId ? 0.1 : 1 | ||||
|                      : 1 | ||||
|             sinkId: sinkId_ | ||||
|             uri: uri_ | ||||
|             deviceId: deviceId_ | ||||
| @ -70,6 +77,14 @@ Item { | ||||
|             participantIsModeratorMuted: audioModeratorMuted_ | ||||
|             participantHandIsRaised: isHandRaised_ | ||||
|  | ||||
|             onParticipantHoveredChanged:  { | ||||
|                 if (participantHovered) { | ||||
|                     hoveredOverlayUri = overlay.uri | ||||
|                     hoveredOverlaySinkId = overlay.sinkId | ||||
|                     hoveredOverVideoMuted = videoMuted_ | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Connections { | ||||
|                 id: registeredNameFoundConnection | ||||
|  | ||||
|  | ||||
| @ -284,6 +284,7 @@ Window { | ||||
|  | ||||
|                         border.color: selectedScreenNumber === index ? JamiTheme.screenSelectionBorderColor : JamiTheme.tabbarBorderColor | ||||
|                         visible: root.window && JamiStrings.selectScreen !== screens[index] && index >= Qt.application.screens.length | ||||
|                                  && screenName2.text !=  JamiStrings.selectWindow | ||||
|  | ||||
|                         Text { | ||||
|                             id: screenName2 | ||||
|  | ||||
							
								
								
									
										72
									
								
								src/app/mainview/components/Toast.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/app/mainview/components/Toast.qml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | ||||
| /* | ||||
|  * Copyright (C) 2023 Savoir-faire Linux Inc. | ||||
|  * Author: Vengeon Nicolas <nicolas.vengeon@savoirfairelinux.com> | ||||
|  * | ||||
|  * 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 <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| import QtQuick | ||||
| import net.jami.Constants 1.1 | ||||
|  | ||||
| Rectangle { | ||||
|     id: root | ||||
|  | ||||
|     anchors.top: parent.top | ||||
|     anchors.horizontalCenter: parent.horizontalCenter | ||||
|     width: textMessage.width + 20 | ||||
|     height: textMessage.height + 10 | ||||
|     anchors.topMargin: 10 | ||||
|     radius: 15 | ||||
|     color: JamiTheme.toastRectColor | ||||
|  | ||||
|     property int duration | ||||
|     property int fadingTime | ||||
|     property string message | ||||
|  | ||||
|     Component.onCompleted: { | ||||
|         anim.start(); | ||||
|     } | ||||
|  | ||||
|     Text { | ||||
|         id: textMessage | ||||
|  | ||||
|         anchors.centerIn: root | ||||
|         text: message | ||||
|         font.pointSize: JamiTheme.toastFontSize | ||||
|         color: JamiTheme.toastColor | ||||
|     } | ||||
|  | ||||
|     SequentialAnimation on opacity { | ||||
|         id: anim | ||||
|  | ||||
|         running: false | ||||
|  | ||||
|         NumberAnimation { | ||||
|             to: 0.9 | ||||
|             duration: root.fadingTime | ||||
|         } | ||||
|         PauseAnimation { | ||||
|             duration: root.duration | ||||
|         } | ||||
|         NumberAnimation { | ||||
|             to: 0 | ||||
|             duration: root.fadingTime | ||||
|         } | ||||
|  | ||||
|         onRunningChanged: { | ||||
|             if (!running) | ||||
|                 root.destroy(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/app/mainview/components/ToastManager.qml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/app/mainview/components/ToastManager.qml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * Copyright (C) 2023 Savoir-faire Linux Inc. | ||||
|  * Author: Vengeon Nicolas <nicolas.vengeon@savoirfairelinux.com> | ||||
|  * | ||||
|  * 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 <https://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| import QtQuick | ||||
|  | ||||
| Item { | ||||
|     id: root | ||||
|  | ||||
|     function instantiate(message, duration, fadingTime) { | ||||
|         var component = Qt.createComponent("Toast.qml"); | ||||
|         var sprite = component.createObject(root, {message: message, duration: duration, fadingTime: fadingTime}); | ||||
|     } | ||||
| } | ||||
| @ -28,24 +28,32 @@ import net.jami.Constants 1.1 | ||||
| import "../../commoncomponents" | ||||
|  | ||||
| ColumnLayout { | ||||
|     id:root | ||||
|     id: root | ||||
|  | ||||
|     property int itemWidth | ||||
|     property string recordPath: AVModel.getRecordPath() | ||||
|     property string screenshotPath: UtilsAdapter.getDirScreenshot() | ||||
|  | ||||
|     onRecordPathChanged: { | ||||
|         if(recordPath === "") return | ||||
|         if(recordPath === "") | ||||
|             return | ||||
|  | ||||
|         if(AVModel){ | ||||
|         if(AVModel) { | ||||
|             AVModel.setRecordPath(recordPath) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     onScreenshotPathChanged: { | ||||
|         if (screenshotPath === "") | ||||
|             return | ||||
|         UtilsAdapter.setScreenshotPath(screenshotPath) | ||||
|     } | ||||
|  | ||||
|     FolderDialog { | ||||
|         id: recordPathDialog | ||||
|  | ||||
|         title: JamiStrings.selectFolder | ||||
|         currentFolder: StandardPaths.writableLocation(StandardPaths.HomeLocation) | ||||
|         currentFolder: UtilsAdapter.getDirScreenshot() | ||||
|         options: FolderDialog.ShowDirsOnly | ||||
|  | ||||
|         onAccepted: { | ||||
| @ -54,6 +62,19 @@ ColumnLayout { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     FolderDialog { | ||||
|         id: screenshotPathDialog | ||||
|  | ||||
|         title: JamiStrings.selectFolder | ||||
|         currentFolder: StandardPaths.writableLocation(StandardPaths.PicturesLocation) | ||||
|         options: FolderDialog.ShowDirsOnly | ||||
|  | ||||
|         onAccepted: { | ||||
|             var dir = UtilsAdapter.getAbsPath(folder.toString()) | ||||
|             screenshotPath = dir | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Timer{ | ||||
|         id: updateRecordQualityTimer | ||||
|  | ||||
| @ -172,7 +193,7 @@ ColumnLayout { | ||||
|             Layout.fillWidth: true | ||||
|             Layout.fillHeight: true | ||||
|  | ||||
|             text: JamiStrings.saveIn | ||||
|             text: JamiStrings.saveRecordingsTo | ||||
|             color: JamiTheme.textColor | ||||
|             font.pointSize: JamiTheme.settingsFontSize | ||||
|             font.kerning: true | ||||
| @ -199,4 +220,41 @@ ColumnLayout { | ||||
|             onClicked: recordPathDialog.open() | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     RowLayout { | ||||
|         Layout.fillWidth: true | ||||
|         Layout.preferredHeight: JamiTheme.preferredFieldHeight | ||||
|         Layout.leftMargin: JamiTheme.preferredMarginSize | ||||
|  | ||||
|         Label { | ||||
|             Layout.fillWidth: true | ||||
|             Layout.fillHeight: true | ||||
|  | ||||
|             text: JamiStrings.saveScreenshotsTo | ||||
|             color: JamiTheme.textColor | ||||
|             font.pointSize: JamiTheme.settingsFontSize | ||||
|             font.kerning: true | ||||
|  | ||||
|             horizontalAlignment: Text.AlignLeft | ||||
|             verticalAlignment: Text.AlignVCenter | ||||
|         } | ||||
|  | ||||
|         MaterialButton { | ||||
|             id: screenshotPathButton | ||||
|  | ||||
|             Layout.alignment: Qt.AlignRight | ||||
|  | ||||
|             preferredWidth: itemWidth | ||||
|             preferredHeight: JamiTheme.preferredFieldHeight | ||||
|  | ||||
|             toolTipText: UtilsAdapter.getDirScreenshot() | ||||
|             text: screenshotPath | ||||
|             iconSource: JamiResources.round_folder_24dp_svg | ||||
|             color: JamiTheme.buttonTintedGrey | ||||
|             hoveredColor: JamiTheme.buttonTintedGreyHovered | ||||
|             pressedColor: JamiTheme.buttonTintedGreyPressed | ||||
|  | ||||
|             onClicked: screenshotPathDialog.open() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -387,6 +387,22 @@ UtilsAdapter::getDirDocument() | ||||
|         QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); | ||||
| } | ||||
|  | ||||
| QString | ||||
| UtilsAdapter::getDirScreenshot() | ||||
| { | ||||
|     QString screenshotPath = lrcInstance_->accountModel().screenshotDirectory; | ||||
|     if (screenshotPath.isEmpty()) { | ||||
|         QString folderName = "Jami"; | ||||
|         auto picture = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); | ||||
|         QDir dir; | ||||
|         dir.mkdir(picture + QDir::separator() + folderName); | ||||
|         screenshotPath = picture + QDir::separator() + folderName; | ||||
|         setScreenshotPath(screenshotPath); | ||||
|         lrcInstance_->accountModel().screenshotDirectory = screenshotPath; | ||||
|     } | ||||
|     return screenshotPath; | ||||
| } | ||||
|  | ||||
| QString | ||||
| UtilsAdapter::getDirDownload() | ||||
| { | ||||
| @ -425,6 +441,13 @@ UtilsAdapter::setDownloadPath(QString dir) | ||||
|     lrcInstance_->accountModel().downloadDirectory = dir + "/"; | ||||
| } | ||||
|  | ||||
| void | ||||
| UtilsAdapter::setScreenshotPath(QString dir) | ||||
| { | ||||
|     setAppValue(Settings::Key::ScreenshotPath, dir); | ||||
|     lrcInstance_->accountModel().screenshotDirectory = dir + QDir::separator(); | ||||
| } | ||||
|  | ||||
| void | ||||
| UtilsAdapter::monitor(const bool& continuous) | ||||
| { | ||||
|  | ||||
| @ -107,9 +107,11 @@ public: | ||||
|     Q_INVOKABLE QVariant getAppValue(const Settings::Key key); | ||||
|     Q_INVOKABLE void setAppValue(const Settings::Key key, const QVariant& value); | ||||
|     Q_INVOKABLE QString getDirDocument(); | ||||
|     Q_INVOKABLE QString getDirScreenshot(); | ||||
|     Q_INVOKABLE QString getDirDownload(); | ||||
|     Q_INVOKABLE void setRunOnStartUp(bool state); | ||||
|     Q_INVOKABLE void setDownloadPath(QString dir); | ||||
|     Q_INVOKABLE void setScreenshotPath(QString dir); | ||||
|     Q_INVOKABLE void monitor(const bool& continuous); | ||||
|     Q_INVOKABLE void clearInteractionsCache(const QString& accountId, const QString& convUid); | ||||
|     Q_INVOKABLE QVariantMap supportedLang(); | ||||
|  | ||||
| @ -117,6 +117,13 @@ VideoProvider::frame(const QString& id) | ||||
|  | ||||
| QString | ||||
| VideoProvider::captureVideoFrame(const QString& id) | ||||
| { | ||||
|     auto img = captureRawVideoFrame(id); | ||||
|     return Utils::byteArrayToBase64String(Utils::QImageToByteArray(img)); | ||||
| } | ||||
|  | ||||
| QImage | ||||
| VideoProvider::captureRawVideoFrame(const QString& id) | ||||
| { | ||||
|     QMutexLocker framesLk(&framesObjsMutex_); | ||||
|     if (auto* videoFrame = frame(id)) { | ||||
| @ -127,7 +134,7 @@ VideoProvider::captureVideoFrame(const QString& id) | ||||
|                           videoFrame->height(), | ||||
|                           videoFrame->bytesPerLine(0), | ||||
|                           imageFormat); | ||||
|         return Utils::byteArrayToBase64String(Utils::QImageToByteArray(img)); | ||||
|         return img; | ||||
|     } | ||||
|     return {}; | ||||
| } | ||||
|  | ||||
| @ -46,6 +46,7 @@ public: | ||||
|     Q_INVOKABLE void registerSink(const QString& id, QVideoSink* obj); | ||||
|     Q_INVOKABLE void unregisterSink(QVideoSink* obj); | ||||
|     Q_INVOKABLE QString captureVideoFrame(const QString& id); | ||||
|     Q_INVOKABLE QImage captureRawVideoFrame(const QString& id); | ||||
|  | ||||
| private Q_SLOTS: | ||||
|     void onRendererStarted(const QString& id, const QSize& size); | ||||
|  | ||||
| @ -73,6 +73,7 @@ public: | ||||
|      * Should contains the full directory with the end marker (/ on linux for example) | ||||
|      */ | ||||
|     QString downloadDirectory; | ||||
|     QString screenshotDirectory; | ||||
|     /** | ||||
|      * Accept transfer from trusted contacts | ||||
|      */ | ||||
|  | ||||
| @ -564,10 +564,13 @@ AVModel::stopPreview(const QString& resource) | ||||
| BOOL | ||||
| IsAltTabWindow(HWND hwnd) | ||||
| { | ||||
|     LONG style = GetWindowLong(hwnd, GWL_STYLE); | ||||
|     if (!((style & WS_DISABLED) != WS_DISABLED)) { | ||||
|     auto styles = (DWORD) GetWindowLongPtr(hwnd, GWL_STYLE); | ||||
|     auto ex_styles = (DWORD) GetWindowLongPtr(hwnd, GWL_EXSTYLE); | ||||
|  | ||||
|     if (ex_styles & WS_EX_TOOLWINDOW) | ||||
|         return false; | ||||
|     if (styles & WS_CHILD) | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     DWORD cloaked = FALSE; | ||||
|     HRESULT hrTemp = DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked)); | ||||
| @ -591,20 +594,9 @@ IsAltTabWindow(HWND hwnd) | ||||
| BOOL CALLBACK | ||||
| CbEnumAltTab(HWND hwnd, LPARAM lParam) | ||||
| { | ||||
|     // Do not show invisible windows | ||||
|     if (!IsWindowVisible(hwnd)) | ||||
|         return TRUE; | ||||
|  | ||||
|     // Alt-tab test as described by Raymond Chen | ||||
|     if (!IsAltTabWindow(hwnd)) | ||||
|         return TRUE; | ||||
|  | ||||
|     const size_t MAX_WINDOW_NAME = 256; | ||||
|     TCHAR windowName[MAX_WINDOW_NAME]; | ||||
|     if (hwnd == GetShellWindow()) | ||||
|         return TRUE; | ||||
|     else | ||||
|         GetWindowText(hwnd, windowName, MAX_WINDOW_NAME); | ||||
|     GetWindowText(hwnd, windowName, MAX_WINDOW_NAME); | ||||
|  | ||||
|     // Do not show windows that has no caption | ||||
|     if (0 == windowName[0]) | ||||
| @ -613,14 +605,27 @@ CbEnumAltTab(HWND hwnd, LPARAM lParam) | ||||
|     std::wstring msg = std::wstring(windowName); | ||||
|     auto name = QString::fromStdWString(msg); | ||||
|  | ||||
|     // Do not show invisible windows | ||||
|     if (!IsWindowVisible(hwnd)) | ||||
|         return TRUE; | ||||
|  | ||||
|     // Alt-tab test as described by Raymond Chen | ||||
|     if (!IsAltTabWindow(hwnd)) | ||||
|         return TRUE; | ||||
|  | ||||
|     auto isShellWindow = hwnd == GetShellWindow(); | ||||
|  | ||||
|     if (isShellWindow) | ||||
|         return TRUE; | ||||
|  | ||||
|     QMap<QString, QVariant>* windowList = reinterpret_cast<QMap<QString, QVariant>*>(lParam); | ||||
|     auto keys = windowList->keys(); | ||||
|     if (keys.indexOf(name) > 0) { | ||||
|         return FALSE; | ||||
|     } else { | ||||
|         DWORD processId; | ||||
|         GetWindowThreadProcessId(hwnd, &processId); | ||||
|         windowList->insert(name, QVariant::fromValue(processId)); | ||||
|         std::stringstream ss; | ||||
|         ss << hwnd; | ||||
|         windowList->insert(name, QString::fromStdString(ss.str())); | ||||
|     } | ||||
|  | ||||
|     return TRUE; | ||||
|  | ||||
| @ -949,30 +949,6 @@ CallModel::getDisplay(int idx, int x, int y, int w, int h) | ||||
|         .arg(h); | ||||
| } | ||||
|  | ||||
| #ifdef WIN32 | ||||
| BOOL CALLBACK | ||||
| EnumWindowsProcMy(HWND hwnd, LPARAM lParam) | ||||
| { | ||||
|     std::pair<DWORD, QString>* dataPair = reinterpret_cast<std::pair<DWORD, QString>*>(lParam); | ||||
|     DWORD lpdwProcessId; | ||||
|     if (auto parent = GetWindow(hwnd, GW_OWNER)) | ||||
|         GetWindowThreadProcessId(parent, &lpdwProcessId); | ||||
|     else | ||||
|         GetWindowThreadProcessId(hwnd, &lpdwProcessId); | ||||
|     int len = GetWindowTextLength(hwnd) + 1; | ||||
|     std::vector<wchar_t> buf(len); | ||||
|     GetWindowText(hwnd, &buf[0], len); | ||||
|  | ||||
|     if (lpdwProcessId == dataPair->first) { | ||||
|         if (!IsWindowVisible(hwnd)) | ||||
|             return TRUE; | ||||
|         dataPair->second = QString::fromStdWString(&buf[0]); | ||||
|         return FALSE; | ||||
|     } | ||||
|     return TRUE; | ||||
| } | ||||
| #endif | ||||
|  | ||||
| QString | ||||
| CallModel::getDisplay(const QString& windowProcessId, const QString& windowId) | ||||
| { | ||||
| @ -986,22 +962,10 @@ CallModel::getDisplay(const QString& windowProcessId, const QString& windowId) | ||||
|               .arg(windowProcessId); | ||||
| #endif | ||||
| #ifdef WIN32 | ||||
|     // If window changed the name we must look for the parent process window | ||||
|     QString newWindowId = windowId; | ||||
|     auto hwnd = FindWindow(NULL, windowId.toStdWString().c_str()); | ||||
|     if (!hwnd) { | ||||
|         std::pair<DWORD, QString> idName(windowProcessId.toInt(), {}); | ||||
|         LPARAM lParam = reinterpret_cast<LPARAM>(&idName); | ||||
|         EnumWindows(EnumWindowsProcMy, lParam); | ||||
|         if (!idName.second.isEmpty()) { | ||||
|             newWindowId = idName.second; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     ret = QString("%1%2:+0,0 window-id:title=%3") | ||||
|               .arg(libjami::Media::VideoProtocolPrefix::DISPLAY) | ||||
|               .arg(sep) | ||||
|               .arg(newWindowId); | ||||
|     ret = QString("%1%2:+0,0 window-id:hwnd=%3") | ||||
|         .arg(libjami::Media::VideoProtocolPrefix::DISPLAY) | ||||
|         .arg(sep) | ||||
|         .arg(windowProcessId); | ||||
| #endif | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| @ -394,6 +394,8 @@ MessageListModel::roleNames() const | ||||
| bool | ||||
| MessageListModel::isOnlyEmoji(const QString& text) const | ||||
| { | ||||
|     if (text.isEmpty()) | ||||
|         return false; | ||||
|     auto codepointList = text.toUcs4(); | ||||
|     for (QList<uint>::iterator it = codepointList.begin(); it != codepointList.end(); it++) { | ||||
|         auto cur = false; | ||||
| @ -418,7 +420,7 @@ QVariant | ||||
| MessageListModel::dataForItem(item_t item, int, int role) const | ||||
| { | ||||
|     QString replyId = item.second.commit["reply-to"]; | ||||
|     int repliedMsg; | ||||
|     int repliedMsg = -1; | ||||
|     if (!replyId.isEmpty() && (role == Role::ReplyToAuthor || role == Role::ReplyToBody)) { | ||||
|         repliedMsg = getIndexOfMessage(replyId); | ||||
|     } | ||||
| @ -695,7 +697,10 @@ MessageListModel::editMessage(const QString& msgId, interaction::Info& info) | ||||
|         } | ||||
|         info.body = it->rbegin()->body; | ||||
|         editedBodies_.erase(it); | ||||
|         emitDataChanged(msgId, {MessageList::Role::Body, MessageList::Role::PreviousBodies}); | ||||
|         emitDataChanged(msgId, | ||||
|                         {MessageList::Role::Body, | ||||
|                          MessageList::Role::PreviousBodies, | ||||
|                          MessageList::Role::IsEmojiOnly}); | ||||
|  | ||||
|         // Body changed, replies should update | ||||
|         for (const auto& replyId : replyTo_[msgId]) { | ||||
|  | ||||
| @ -30,16 +30,16 @@ VideoManagerInterface::VideoManagerInterface() | ||||
|                                                                     int width, | ||||
|                                                                     int height, | ||||
|                                                                     bool isMixer) { | ||||
|                Q_EMIT decodingStarted(QString::fromLatin1(QByteArray::fromStdString(id)), | ||||
|                                       QString::fromLatin1(QByteArray::fromStdString(shmPath)), | ||||
|                Q_EMIT decodingStarted(QString(id.c_str()), | ||||
|                                       QString(shmPath.c_str()), | ||||
|                                       width, | ||||
|                                       height, | ||||
|                                       isMixer); | ||||
|            }), | ||||
|            exportable_callback<VideoSignal::DecodingStopped>( | ||||
|                [this](const std::string& id, const std::string& shmPath, bool isMixer) { | ||||
|                    Q_EMIT decodingStopped(QString::fromLatin1(QByteArray::fromStdString(id)), | ||||
|                                           QString::fromLatin1(QByteArray::fromStdString(shmPath)), | ||||
|                    Q_EMIT decodingStopped(QString(id.c_str()), | ||||
|                                           QString(shmPath.c_str()), | ||||
|                                           isMixer); | ||||
|                })}; | ||||
| #endif | ||||
|  | ||||
| @ -126,7 +126,7 @@ public Q_SLOTS: // METHODS | ||||
|     QString openVideoInput(const QString& resource) | ||||
|     { | ||||
| #ifdef ENABLE_VIDEO | ||||
|         return QString::fromLatin1(QByteArray::fromStdString(libjami::openVideoInput(resource.toStdString()))); | ||||
|         return libjami::openVideoInput(resource.toLatin1().toStdString()).c_str(); | ||||
| #endif | ||||
|     } | ||||
|  | ||||
| @ -150,7 +150,7 @@ public Q_SLOTS: // METHODS | ||||
|     bool registerSinkTarget(const QString& sinkID, const libjami::SinkTarget& target) | ||||
|     { | ||||
| #ifdef ENABLE_VIDEO | ||||
|         return libjami::registerSinkTarget(sinkID.toLatin1().toStdString(), target); | ||||
|         return libjami::registerSinkTarget(sinkID.toStdString(), target); | ||||
| #else | ||||
|         Q_UNUSED(sinkID) | ||||
|         Q_UNUSED(target) | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	