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 }