Warning, /plasma/bluedevil/src/applet/package/contents/ui/DeviceItem.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2013-2014 Jan Grulich <jgrulich@redhat.com>
0003     SPDX-FileCopyrightText: 2014-2015 David Rosca <nowrep@gmail.com>
0004     SPDX-FileCopyrightText: 2020 Nate Graham <nate@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 import QtQuick 2.15
0010 import QtQuick.Controls 2.15
0011 import QtQuick.Layouts 1.15
0012 
0013 import org.kde.plasma.components 2.0 as PlasmaComponents // for ContextMenu/MenuItem
0014 import org.kde.plasma.components 3.0 as PlasmaComponents3
0015 import org.kde.plasma.core 2.0 as PlasmaCore
0016 import org.kde.plasma.extras 2.0 as PlasmaExtras
0017 import org.kde.plasma.private.bluetooth 1.0 as PlasmaBt
0018 
0019 import org.kde.bluezqt 1.0 as BluezQt
0020 import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
0021 
0022 PlasmaExtras.ExpandableListItem {
0023     id: expandableListItem
0024 
0025     property bool connecting: false
0026     property var currentDeviceDetails: []
0027 
0028     icon: model.Icon
0029     title: model.DeviceFullName
0030     subtitle: infoText()
0031     isBusy: connecting
0032     isDefault: model.Connected
0033     defaultActionButtonAction: Action {
0034         icon.name: model.Connected ? "network-disconnect" : "network-connect"
0035         text: model.Connected ? i18n("Disconnect") : i18n("Connect")
0036         onTriggered: connectToDevice()
0037     }
0038 
0039     contextualActionsModel: [
0040         Action {
0041             id: browseFilesButton
0042             enabled: Uuids.indexOf(BluezQt.Services.ObexFileTransfer) !== -1
0043             icon.name: "folder"
0044             text: i18n("Browse Files")
0045 
0046             onTriggered: {
0047                 const url = "obexftp://%1/".arg(Address.replace(/:/g, "-"));
0048                 Qt.openUrlExternally(url);
0049             }
0050         },
0051         Action {
0052             id: sendFileButton
0053             enabled: Uuids.indexOf(BluezQt.Services.ObexObjectPush) !== -1
0054             icon.name: "folder-download"
0055             text: i18n("Send File")
0056 
0057             onTriggered: {
0058                 PlasmaBt.LaunchApp.launchSendFile(Ubi);
0059             }
0060         }
0061     ]
0062 
0063     customExpandedViewContent: Component {
0064         id: expandedView
0065 
0066         ColumnLayout {
0067             spacing: 0
0068 
0069             // Media Player
0070             MediaPlayerItem {
0071                 id: mediaPlayer
0072                 Layout.leftMargin: PlasmaCore.Units.gridUnit + PlasmaCore.Units.smallSpacing * 3
0073                 Layout.fillWidth: true
0074                 visible: MediaPlayer
0075             }
0076 
0077             Item {
0078                 Layout.preferredHeight: PlasmaCore.Units.smallSpacing
0079                 visible: mediaPlayer.visible
0080             }
0081 
0082             PlasmaCore.SvgItem {
0083                 id: mediaPlayerSeparator
0084                 Layout.fillWidth: true
0085                 Layout.preferredHeight: lineSvg.elementSize("horizontal-line").height
0086                 elementId: "horizontal-line"
0087                 visible: mediaPlayer.visible
0088                     || (!mediaPlayer.visible && !(browseFilesButton.enabled || sendFileButton.enabled))
0089                 svg: PlasmaCore.Svg {
0090                     id: lineSvg
0091                     imagePath: "widgets/line"
0092                 }
0093             }
0094 
0095             Item {
0096                 Layout.preferredHeight: PlasmaCore.Units.smallSpacing
0097                 visible: mediaPlayerSeparator.visible
0098             }
0099 
0100             KQuickControlsAddons.Clipboard {
0101                 id: clipboard
0102             }
0103 
0104             PlasmaComponents.ContextMenu {
0105                 id: contextMenu
0106                 property string text
0107 
0108                 function show(item, text, x, y) {
0109                     contextMenu.text = text
0110                     visualParent = item
0111                     open(x, y)
0112                 }
0113 
0114                 PlasmaComponents.MenuItem {
0115                     text: i18n("Copy")
0116                     icon: "edit-copy"
0117                     enabled: contextMenu.text !== ""
0118                     onClicked: clipboard.content = contextMenu.text
0119                 }
0120             }
0121 
0122             // Details
0123             MouseArea {
0124                 Layout.fillWidth: true
0125                 Layout.preferredHeight: detailsGrid.implicitHeight
0126 
0127                 acceptedButtons: Qt.RightButton
0128                 activeFocusOnTab: repeater.count > 0
0129 
0130                 Accessible.description: {
0131                     let description = [];
0132                     for (let i = 0; i < currentDeviceDetails.length; i += 2) {
0133                         description.push(currentDeviceDetails[i]);
0134                         description.push(": ");
0135                         description.push(currentDeviceDetails[i + 1]);
0136                         description.push("; ");
0137                     }
0138                     return description.join('');
0139                 }
0140 
0141                 onPressed: {
0142                     const item = detailsGrid.childAt(mouse.x, mouse.y);
0143                     if (!item || !item.isContent) {
0144                         return; // only let users copy the value on the right
0145                     }
0146 
0147                     contextMenu.show(this, item.text, mouse.x, mouse.y);
0148                 }
0149 
0150                 Loader {
0151                     anchors.fill: parent
0152                     active: parent.activeFocus
0153                     asynchronous: true
0154                     z: -1
0155 
0156                     sourceComponent: PlasmaExtras.Highlight {
0157                         hovered: true
0158                     }
0159                 }
0160 
0161                 GridLayout {
0162                     id: detailsGrid
0163                     width: parent.width
0164                     columns: 2
0165                     rowSpacing: PlasmaCore.Units.smallSpacing / 4
0166 
0167                     Repeater {
0168                         id: repeater
0169 
0170                         model: currentDeviceDetails.length
0171 
0172                         PlasmaComponents3.Label {
0173                             id: detailLabel
0174 
0175                             Layout.fillWidth: true
0176 
0177                             readonly property bool isContent: index % 2
0178 
0179                             horizontalAlignment: isContent ? Text.AlignLeft : Text.AlignRight
0180                             elide: isContent ? Text.ElideRight : Text.ElideNone
0181                             font: PlasmaCore.Theme.smallestFont
0182                             opacity: isContent ? 1 : 0.6
0183                             text: isContent ? currentDeviceDetails[index] : `${currentDeviceDetails[index]}:`
0184                             textFormat: isContent ? Text.PlainText : Text.StyledText
0185                         }
0186                     }
0187                 }
0188             }
0189 
0190             Component.onCompleted: createContent()
0191         }
0192     }
0193 
0194     // Hide device details when the device for this delegate changes
0195     // This happens eg. when device connects/disconnects
0196     property QtObject __dev
0197     readonly property QtObject dev : Device
0198     onDevChanged: {
0199         if (__dev === dev) {
0200             return;
0201         }
0202         __dev = dev;
0203 
0204         if (expandedView.status === Component.Ready) {
0205             expandableListItem.collapse();
0206             expandableListItem.ListView.view.currentIndex = -1;
0207         }
0208     }
0209 
0210     function boolToString(v): string {
0211         return v ? i18n("Yes") : i18n("No");
0212     }
0213 
0214     function adapterName(a): string {
0215         const hci = devicesModel.adapterHciString(a.ubi);
0216         return (hci !== "")
0217             ? i18nc("@label %1 is human-readable adapter name, %2 is HCI", "%1 (%2)", a.name, hci)
0218             : a.name;
0219     }
0220 
0221     function createContent() {
0222         const details = [];
0223 
0224         if (Name !== RemoteName) {
0225             details.push(i18n("Remote Name"));
0226             details.push(RemoteName);
0227         }
0228 
0229         details.push(i18n("Address"));
0230         details.push(Address);
0231 
0232         details.push(i18n("Paired"));
0233         details.push(boolToString(Paired));
0234 
0235         details.push(i18n("Trusted"));
0236         details.push(boolToString(Trusted));
0237 
0238         details.push(i18n("Adapter"));
0239         details.push(adapterName(Adapter));
0240 
0241         currentDeviceDetails = details;
0242     }
0243 
0244     function infoText(): string {
0245         if (connecting) {
0246             return Connected ? i18n("Disconnecting") : i18n("Connecting");
0247         }
0248 
0249         const labels = [];
0250 
0251         if (Connected) {
0252             labels.push(i18n("Connected"));
0253         }
0254 
0255         switch (Type) {
0256         case BluezQt.Device.Headset:
0257         case BluezQt.Device.Headphones:
0258         case BluezQt.Device.OtherAudio:
0259             labels.push(i18n("Audio device"));
0260             break;
0261 
0262         case BluezQt.Device.Keyboard:
0263         case BluezQt.Device.Mouse:
0264         case BluezQt.Device.Joypad:
0265         case BluezQt.Device.Tablet:
0266             labels.push(i18n("Input device"));
0267             break;
0268 
0269         case BluezQt.Device.Phone:
0270             labels.push(i18n("Phone"));
0271             break;
0272 
0273         default:
0274             const profiles = [];
0275 
0276             if (Uuids.indexOf(BluezQt.Services.ObexFileTransfer) !== -1) {
0277                 profiles.push(i18n("File transfer"));
0278             }
0279             if (Uuids.indexOf(BluezQt.Services.ObexObjectPush) !== -1) {
0280                 profiles.push(i18n("Send file"));
0281             }
0282             if (Uuids.indexOf(BluezQt.Services.HumanInterfaceDevice) !== -1) {
0283                 profiles.push(i18n("Input"));
0284             }
0285             if (Uuids.indexOf(BluezQt.Services.AdvancedAudioDistribution) !== -1) {
0286                 profiles.push(i18n("Audio"));
0287             }
0288             if (Uuids.indexOf(BluezQt.Services.Nap) !== -1) {
0289                 profiles.push(i18n("Network"));
0290             }
0291 
0292             if (!profiles.length) {
0293                 profiles.push(i18n("Other device"));
0294             }
0295 
0296             labels.push(profiles.join(", "));
0297         }
0298 
0299         if (Battery) {
0300             labels.push(i18n("%1% Battery", Battery.percentage));
0301         }
0302 
0303         return labels.join(" ยท ");
0304     }
0305 
0306     function errorText(/*PendingCall*/ call): string {
0307         switch (call.error) {
0308         case BluezQt.PendingCall.Failed:
0309             return (call.errorText === "Host is down")
0310                 ? i18nc("Notification when the connection failed due to Failed:HostIsDown",
0311                         "The device is unreachable")
0312                 : i18nc("Notification when the connection failed due to Failed",
0313                         "Connection to the device failed");
0314 
0315         case BluezQt.PendingCall.NotReady:
0316             return i18nc("Notification when the connection failed due to NotReady",
0317                          "The device is not ready");
0318 
0319         default:
0320             return "";
0321         }
0322     }
0323 
0324     function connectToDevice() {
0325         if (connecting) {
0326             return;
0327         }
0328 
0329         connecting = true;
0330         runningActions++;
0331 
0332         // Disconnect device
0333         if (Connected) {
0334             Device.disconnectFromDevice().finished.connect(call => {
0335                 connecting = false;
0336                 runningActions--;
0337             });
0338             return;
0339         }
0340 
0341         // Connect device
0342         const /*PendingCall*/call = Device.connectToDevice();
0343         call.userData = Device;
0344 
0345         call.finished.connect(call => {
0346             connecting = false;
0347             runningActions--;
0348 
0349             if (call.error) {
0350                 const device = call.userData;
0351                 const title = i18nc("@label %1 is human-readable device name, %2 is low-level device address", "%1 (%2)", device.name, device.address);
0352                 const text = errorText(call);
0353 
0354                 PlasmaBt.Notify.connectionFailed(title, text);
0355             }
0356         });
0357     }
0358 }