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