Warning, /plasma/bluedevil/src/kcm/ui/main.qml is written in an unsupported language. File is not indexed.
0001 /** 0002 * SPDX-FileCopyrightText: 2020 Nicolas Fella <nicolas.fella@gmx.de> 0003 * SPDX-FileCopyrightText: 2021 Tom Zander <tom@flowee.org> 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 import QtQuick 2.2 0008 import QtQuick.Layouts 1.1 0009 import QtQuick.Controls 2.10 as QQC2 0010 0011 import org.kde.kirigami 2.20 as Kirigami 0012 import org.kde.kirigami.delegates as KD 0013 import org.kde.kcmutils 0014 0015 import org.kde.bluezqt 1.0 as BluezQt 0016 0017 import org.kde.plasma.private.bluetooth 0018 0019 import "script.js" as Script; 0020 0021 ScrollViewKCM { 0022 id: root 0023 0024 actions: [ 0025 Kirigami.Action { 0026 id: enableAction 0027 text: i18nc("@action: button as in, 'enable Bluetooth'", "Enabled") 0028 icon.name: "network-bluetooth-symbolic" 0029 checkable: true 0030 checked: BluezQt.Manager.bluetoothOperational 0031 visible: BluezQt.Manager.rfkill.state !== BluezQt.Rfkill.Unknown 0032 onTriggered: { 0033 root.setBluetoothEnabled(!BluezQt.Manager.bluetoothOperational) 0034 } 0035 displayComponent: QQC2.Switch { 0036 text: enableAction.text 0037 checked: enableAction.checked 0038 visible: enableAction.visible 0039 onToggled: enableAction.trigger() 0040 } 0041 }, 0042 Kirigami.Action { 0043 text: i18n("Add New Device…") 0044 icon.name: "list-add-symbolic" 0045 onTriggered: kcm.runWizard() 0046 visible: BluezQt.Manager.bluetoothOperational 0047 }, 0048 Kirigami.Action { 0049 text: i18n("Configure…") 0050 icon.name: "configure-symbolic" 0051 onTriggered: kcm.push("General.qml") 0052 visible: BluezQt.Manager.bluetoothOperational 0053 } 0054 ] 0055 0056 function makeCall(call) { 0057 busyIndicator.running = true 0058 call.finished.connect(call => { 0059 busyIndicator.running = false 0060 if (call.error) { 0061 errorMessage.text = call.errorText 0062 errorMessage.visible = true 0063 } 0064 }) 0065 } 0066 0067 Connections { 0068 target: kcm 0069 0070 function onErrorOccured(errorText) { 0071 errorMessage.text = errorText 0072 errorMessage.visible = true 0073 } 0074 } 0075 0076 Component { 0077 id: forgetDialogComponent 0078 0079 Kirigami.PromptDialog { 0080 id: dialog 0081 0082 required property BluezQt.Adapter adapter 0083 required property BluezQt.Device device 0084 required property string name 0085 0086 signal call(BluezQt.PendingCall pc) 0087 0088 title: i18n("Forget this Device?") 0089 subtitle: i18n("Are you sure you want to forget \"%1\"?", dialog.name) 0090 0091 showCloseButton: false 0092 0093 // Need to use fully custom actions because it's not possible to override 0094 // the text and icon of a single standardbutton, and if we use just a 0095 // custom action for that one, then it's in the wrong visual position 0096 // relative to the StandardButton-provided Cancel button 0097 standardButtons: Kirigami.Dialog.NoButton 0098 customFooterActions: [ 0099 Kirigami.Action { 0100 text: i18nc("@action:button", "Forget Device") 0101 icon.name: "edit-delete-remove-symbolic" 0102 onTriggered: { 0103 dialog.accept(); 0104 } 0105 }, 0106 Kirigami.Action { 0107 text: i18nc("@action:button", "Cancel") 0108 icon.name: "dialog-cancel-symbolic" 0109 onTriggered: { 0110 dialog.reject(); 0111 } 0112 shortcut: StandardKey.Cancel 0113 } 0114 ] 0115 0116 onAccepted: call(adapter.removeDevice(device)) 0117 0118 contentData: [ 0119 Connections { 0120 target: dialog.device 0121 function onDeviceRemoved() { 0122 dialog.reject(); 0123 } 0124 }, 0125 Connections { 0126 target: dialog.adapter 0127 function onAdapterRemoved() { 0128 dialog.reject(); 0129 } 0130 function onPoweredChanged() { 0131 if (!dialog.adapter.powered) { 0132 dialog.reject(); 0133 } 0134 } 0135 } 0136 ] 0137 } 0138 } 0139 0140 implicitHeight: Kirigami.Units.gridUnit * 28 0141 implicitWidth: Kirigami.Units.gridUnit * 28 0142 0143 function setBluetoothEnabled(enabled) { 0144 BluezQt.Manager.bluetoothBlocked = !enabled 0145 0146 for (var i = 0; i < BluezQt.Manager.adapters.length; ++i) { 0147 var adapter = BluezQt.Manager.adapters[i]; 0148 adapter.powered = enabled; 0149 } 0150 } 0151 0152 header: Kirigami.InlineMessage { 0153 id: errorMessage 0154 type: Kirigami.MessageType.Error 0155 showCloseButton: true 0156 } 0157 0158 view: ListView { 0159 id: list 0160 clip: true 0161 0162 Kirigami.PlaceholderMessage { 0163 id: noBluetoothMessage 0164 // We cannot use the adapter count here because that can be zero when 0165 // bluetooth is disabled even when there are physical devices 0166 visible: BluezQt.Manager.rfkill.state === BluezQt.Rfkill.Unknown 0167 icon.name: "edit-none-symbolic" 0168 text: i18n("No Bluetooth adapters found") 0169 width: parent.width - (Kirigami.Units.largeSpacing * 4) 0170 anchors.centerIn: parent 0171 } 0172 0173 Kirigami.PlaceholderMessage { 0174 id: bluetoothDisabledMessage 0175 visible: BluezQt.Manager.operational && !BluezQt.Manager.bluetoothOperational && !noBluetoothMessage.visible 0176 icon.name: "network-bluetooth-inactive-symbolic" 0177 text: i18n("Bluetooth is disabled") 0178 width: parent.width - (Kirigami.Units.largeSpacing * 4) 0179 anchors.centerIn: parent 0180 0181 helpfulAction: Kirigami.Action { 0182 icon.name: "network-bluetooth-symbolic" 0183 text: i18n("Enable") 0184 onTriggered: { 0185 root.setBluetoothEnabled(true) 0186 } 0187 } 0188 } 0189 0190 Kirigami.PlaceholderMessage { 0191 visible: !noBluetoothMessage.visible && !bluetoothDisabledMessage.visible && list.count === 0 0192 icon.name: "network-bluetooth-activated-symbolic" 0193 text: i18n("No devices paired") 0194 width: parent.width - (Kirigami.Units.largeSpacing * 4) 0195 anchors.centerIn: parent 0196 } 0197 0198 model: BluezQt.Manager.bluetoothOperational ? devicesModel : null 0199 0200 QQC2.BusyIndicator { 0201 id: busyIndicator 0202 running: false 0203 anchors.centerIn: parent 0204 } 0205 0206 0207 DevicesProxyModel { 0208 id: devicesModel 0209 sourceModel: BluezQt.DevicesModel { } 0210 } 0211 0212 section.property: "Connected" 0213 section.delegate: Kirigami.ListSectionHeader { 0214 width: ListView.view.width 0215 text: section === "true" ? i18n("Connected") : i18n("Available") 0216 } 0217 0218 delegate: QQC2.ItemDelegate { 0219 width: ListView.view.width 0220 0221 onClicked: kcm.push("Device.qml", {device: model.Device}) 0222 0223 contentItem: RowLayout { 0224 spacing: Kirigami.Units.smallSpacing 0225 0226 KD.IconTitleSubtitle { 0227 title: model.Name 0228 subtitle: infoText(model.Device.type, model.Device.battery, model.Device.uuids) 0229 icon.name: model.Icon 0230 icon.width: Kirigami.Units.iconSizes.medium 0231 Layout.fillWidth: true 0232 } 0233 0234 QQC2.ToolButton { 0235 text: model.Connected ? i18n("Disconnect") : i18n("Connect") 0236 icon.name: model.Connected ? "network-disconnect-symbolic" : "network-connect-symbolic" 0237 display: QQC2.AbstractButton.IconOnly 0238 QQC2.ToolTip.text: text 0239 QQC2.ToolTip.visible: hovered 0240 0241 onClicked: { 0242 if (model.Connected) { 0243 root.makeCall(model.Device.disconnectFromDevice()) 0244 } else { 0245 root.makeCall(model.Device.connectToDevice()) 0246 } 0247 } 0248 } 0249 0250 QQC2.ToolButton { 0251 text: i18nc("@action:button %1 is the name of a Bluetooth device", "Forget \"%1\"", model.Name) 0252 icon.name: "edit-delete-remove-symbolic" 0253 display: QQC2.AbstractButton.IconOnly 0254 QQC2.ToolTip.text: text 0255 QQC2.ToolTip.visible: hovered 0256 0257 onClicked: { 0258 const dialog = forgetDialogComponent.createObject(root, { 0259 adapter: model.Adapter, 0260 device: model.Device, 0261 name: model.Name, 0262 }); 0263 // Use IIFE (Immediately Invoked Function Expression) to hard-copy a reference 0264 // to root object, to avoid it being lost if the delegate is destroyed. 0265 dialog.call.connect((function (root) { 0266 return (call => root.makeCall(call)); 0267 })(root)); 0268 dialog.closed.connect(() => dialog.destroy()); 0269 dialog.open(); 0270 } 0271 } 0272 } 0273 } 0274 } 0275 0276 function infoText(type, battery, uuids): string { 0277 const labels = []; 0278 0279 labels.push(Script.deviceTypeToString(type)); 0280 0281 if (battery) { 0282 labels.push(i18n("%1% Battery", battery.percentage)); 0283 } 0284 0285 return labels.join(" · "); 0286 } 0287 }