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 }