main.qml 11.9 KB
Newer Older
1
/*
2
3
4
5
6
7
8
    SPDX-FileCopyrightText: 2011 Viranch Mehta <viranch.mehta@gmail.com>
    SPDX-FileCopyrightText: 2012 Jacopo De Simoi <wilderkde@gmail.com>
    SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
    SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/
9
10
11
12
13
14
15

import QtQuick 2.0
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras

16
import org.kde.kquickcontrolsaddons 2.0 // For KCMShell
17

18
19
Item {
    id: devicenotifier
20

21
    readonly property bool openAutomounterKcmAuthorized: KCMShell.authorize("device_automounter_kcm.desktop").length > 0
22

23
    property string devicesType: {
24
        if (Plasmoid.configuration.allDevices) {
25
            return "all"
26
        } else if (Plasmoid.configuration.removableDevices) {
27
28
29
30
31
            return "removable"
        } else {
            return "nonRemovable"
        }
    }
32
33
    property string popupIcon: "device-notifier"

Marco Martin's avatar
Marco Martin committed
34
35
    property bool itemClicked: false
    property int currentIndex: -1
36
37
    property var connectedRemovables: []
    property int mountedRemovables: 0
Marco Martin's avatar
Marco Martin committed
38

39
40
41
    // QTBUG-50380: As soon as the item gets removed from the model, all of ListView's
    // properties (count, contentHeight) pretend the delegate doesn't exist anymore
    // causing our "No devices" heading to overlap with the remaining device
42
    property bool isMessageHighlightAnimatorRunning: false
43

Nate Graham's avatar
Nate Graham committed
44
45
    Plasmoid.switchWidth: PlasmaCore.Units.gridUnit * 10
    Plasmoid.switchHeight: PlasmaCore.Units.gridUnit * 10
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

    Plasmoid.toolTipMainText: filterModel.count > 0 && filterModel.get(0) ? i18n("Most Recent Device") : i18n("No Devices Available")
    Plasmoid.toolTipSubText: {
        if (filterModel.count > 0) {
            var data = filterModel.get(0)
            if (data && data.Description) {
                return data.Description
            }
        }
        return ""
    }
    Plasmoid.icon: {
        if (filterModel.count > 0) {
            var data = filterModel.get(0)
            if (data && data.Icon) {
                return data.Icon
            }
        }
        return "device-notifier"
    }

67
    Plasmoid.status: (filterModel.count > 0 || isMessageHighlightAnimatorRunning) ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.PassiveStatus
68
69
70
71
72
73

    PlasmaCore.DataSource {
        id: hpSource
        engine: "hotplug"
        connectedSources: sources
        interval: 0
74
75
76
77

        onSourceAdded: {
            disconnectSource(source);
            connectSource(source);
78
            sdSource.connectedSources = sources
79
80
81
82
        }
        onSourceRemoved: {
            disconnectSource(source);
        }
83
84
85
86
    }

    Plasmoid.compactRepresentation: PlasmaCore.IconItem {
        source: devicenotifier.popupIcon
Nate Graham's avatar
Nate Graham committed
87
88
        width: PlasmaCore.Units.iconSizes.medium;
        height: PlasmaCore.Units.iconSizes.medium;
89
        active: compactMouse.containsMouse
90
        MouseArea {
91
            id: compactMouse
92
            anchors.fill: parent
93
            hoverEnabled: true
94
            onClicked: Plasmoid.expanded = !Plasmoid.expanded
95
96
        }
    }
Marco Martin's avatar
Marco Martin committed
97
    Plasmoid.fullRepresentation: FullRepresentation {}
98
99
100
101

    PlasmaCore.DataSource {
        id: sdSource
        engine: "soliddevice"
102
        interval: 0
103
104
        property string last
        onSourceAdded: {
105
106
            disconnectSource(source);
            connectSource(source);
107
            last = source;
108
            processLastDevice(true);
109
110
111
112
            if (data[source].Removable) {
                devicenotifier.connectedRemovables.push(source);
                devicenotifier.connectedRemovables = devicenotifier.connectedRemovables;
            }
113
114
115
        }

        onSourceRemoved: {
116
            disconnectSource(source);
117
118
119
120
121
            var index = devicenotifier.connectedRemovables.indexOf(source);
            if (index >= 0) {
                devicenotifier.connectedRemovables.splice(index, 1);
                devicenotifier.connectedRemovables = devicenotifier.connectedRemovables;
            }
122
123
124
        }

        onDataChanged: {
125
            processLastDevice(true);
126
127
128
129
130
131
            var counter = 0;
            for (var i = 0; i < devicenotifier.connectedRemovables.length; i++) {
                if (isMounted(devicenotifier.connectedRemovables[i])) {
                    counter++;
                }
            }
Laurent Montel's avatar
Laurent Montel committed
132
            if (counter !== devicenotifier.mountedRemovables) {
133
134
                devicenotifier.mountedRemovables = counter;
            }
135
136
137
138
139
140
141
        }

        onNewData: {
            last = sourceName;
            processLastDevice(false);
        }

142
143
144
145
146
147
        function isViableDevice(udi) {
            if (devicesType === "all") {
                return true;
            }

            var device = data[udi];
148
149
150
151
            if (!device) {
                return false;
            }

152
153
154
155
            return (devicesType === "removable" && device.Removable)
                || (devicesType === "nonRemovable" && !device.Removable);
        }

156
        function processLastDevice(expand) {
157
158
            if (last && isViableDevice(last)) {
                if (expand && hpSource.data[last] && hpSource.data[last].added) {
159
160
                    devicenotifier.popupIcon = "preferences-desktop-notification";
                    expandTimer.restart();
161
                    popupIconTimer.restart();
162
                }
163
                last = "";
164
165
166
167
168
169
170
171
172
173
174
            }
        }
    }

    PlasmaCore.SortFilterModel {
        id: filterModel
        sourceModel: PlasmaCore.DataModel {
            dataSource: sdSource
        }
        filterRole: "Removable"
        filterRegExp: {
175
176
177
178
            if (devicesType === "removable") {
                return "true"
            } else if (devicesType === "nonRemovable") {
                return "false"
179
            } else {
180
                return ""
181
182
183
184
185
            }
        }
        sortRole: "Timestamp"
        sortOrder: Qt.DescendingOrder
    }
186
187
188
189
190
191

    PlasmaCore.DataSource {
        id: statusSource
        engine: "devicenotifications"
        property string last
        property string lastUdi
192
193
194
        property string lastDescription
        property string lastMessage
        property string lastIcon
195
196
197
198
199
200
201
202
203
        onSourceAdded: {
            last = source;
            disconnectSource(source);
            connectSource(source);
        }
        onSourceRemoved: disconnectSource(source)
        onDataChanged: {
            if (last) {
                lastUdi = data[last].udi
204
205
206
                lastDescription = sdSource.data[lastUdi] ? sdSource.data[lastUdi].Description : ""
                lastMessage = data[last].error
                lastIcon = sdSource.data[lastUdi] ? sdSource.data[lastUdi].Icon : "device-notifier"
207
208

                if (sdSource.isViableDevice(lastUdi)) {
209
210
                    Plasmoid.expanded = true
                    Plasmoid.fullRepresentationItem.spontaneousOpen = true;
211
                }
212
213
214
215
216
217
            }
        }

        function clearMessage() {
            last = ""
            lastUdi = ""
218
219
220
            lastDescription = ""
            lastMessage = ""
            lastIcon = ""
221
222
223
        }
    }

224
225
226
    property var showRemovableDevicesAction
    property var showNonRemovableDevicesAction
    property var showAllDevicesAction
227
    property var openAutomaticallyAction
228

229
    Component.onCompleted: {
Laurent Montel's avatar
Laurent Montel committed
230
        if (sdSource.connectedSources.count === 0) {
231
232
            Plasmoid.status = PlasmaCore.Types.PassiveStatus;
        }
233

234
235
        Plasmoid.setAction("unmountAllDevices", i18n("Remove All"), "media-eject");
        Plasmoid.action("unmountAllDevices").visible = Qt.binding(() => {
236
237
238
            return devicenotifier.mountedRemovables > 1;
        });
 
239
        Plasmoid.setActionSeparator("sep0");
240

241
242
        Plasmoid.setAction("showRemovableDevices", i18n("Removable Devices"), "drive-removable-media");
        devicenotifier.showRemovableDevicesAction = Plasmoid.action("showRemovableDevices");
243
        devicenotifier.showRemovableDevicesAction.checkable = true;
244
245
        devicenotifier.showRemovableDevicesAction.checked = Qt.binding(() => {return Plasmoid.configuration.removableDevices;});
        Plasmoid.setActionGroup("showRemovableDevices", "devicesShown");
246

247
248
        Plasmoid.setAction("showNonRemovableDevices", i18n("Non Removable Devices"), "drive-harddisk");
        devicenotifier.showNonRemovableDevicesAction = Plasmoid.action("showNonRemovableDevices");
249
        devicenotifier.showNonRemovableDevicesAction.checkable = true;
250
251
        devicenotifier.showNonRemovableDevicesAction.checked = Qt.binding(() => {return Plasmoid.configuration.nonRemovableDevices;});
        Plasmoid.setActionGroup("showNonRemovableDevices", "devicesShown");
252

253
254
        Plasmoid.setAction("showAllDevices", i18n("All Devices"));
        devicenotifier.showAllDevicesAction = Plasmoid.action("showAllDevices");
255
        devicenotifier.showAllDevicesAction.checkable = true;
256
257
        devicenotifier.showAllDevicesAction.checked = Qt.binding(() => {return Plasmoid.configuration.allDevices;});
        Plasmoid.setActionGroup("showAllDevices", "devicesShown");
258

259
        Plasmoid.setActionSeparator("sep");
260

261
262
        Plasmoid.setAction("openAutomatically", i18n("Show popup when new device is plugged in"));
        devicenotifier.openAutomaticallyAction = Plasmoid.action("openAutomatically");
263
        devicenotifier.openAutomaticallyAction.checkable = true;
264
        devicenotifier.openAutomaticallyAction.checked = Qt.binding(() => {return Plasmoid.configuration.popupOnNewDevice;});
265

266
        Plasmoid.setActionSeparator("sep2");
267

268
        if (devicenotifier.openAutomounterKcmAuthorized) {
269
270
            Plasmoid.removeAction("configure");
            Plasmoid.setAction("configure", i18nc("Open auto mounter kcm", "Configure Removable Devices…"), "configure")
271
272
273
        }
    }

274
    function action_configure() {
275
        KCMShell.openSystemSettings("kcm_device_automounter")
276
277
    }

278
    function action_showRemovableDevices() {
279
280
281
        Plasmoid.configuration.removableDevices = true;
        Plasmoid.configuration.nonRemovableDevices = false;
        Plasmoid.configuration.allDevices = false;
282
283
284
    }

    function action_showNonRemovableDevices() {
285
286
287
        Plasmoid.configuration.removableDevices = false;
        Plasmoid.configuration.nonRemovableDevices = true;
        Plasmoid.configuration.allDevices = false;
288
289
290
    }

    function action_showAllDevices() {
291
292
293
        Plasmoid.configuration.removableDevices = false;
        Plasmoid.configuration.nonRemovableDevices = false;
        Plasmoid.configuration.allDevices = true;
294
295
    }

296
    function action_openAutomatically() {
297
        Plasmoid.configuration.popupOnNewDevice = !Plasmoid.configuration.popupOnNewDevice;
298
299
    }

Marco Martin's avatar
Marco Martin committed
300
    Plasmoid.onExpandedChanged: {
301
        popupEventSlot(Plasmoid.expanded);
Marco Martin's avatar
Marco Martin committed
302
303
304
305
306
307
308
309
310
311
312
    }

    function popupEventSlot(popped) {
        if (!popped) {
            // reset the property that lets us remember if an item was clicked
            // (versus only hovered) for autohide purposes
            devicenotifier.itemClicked = true;
            devicenotifier.currentIndex = -1;
        }
    }

313
    function isMounted(udi) {
314
315
316
317
        if (!sdSource.data[udi]) {
            return false;
        }

318
        var types = sdSource.data[udi]["Device Types"];
319
320
        if (types.indexOf("Storage Access") >= 0) {
            return sdSource.data[udi]["Accessible"];
321
        }
322
323

        return (types.indexOf("Storage Volume") >= 0 && types.indexOf("OpticalDisc") >= 0)
324
325
326
327
    }

    Timer {
        id: popupIconTimer
328
        interval: 3000
329
330
331
        onTriggered: devicenotifier.popupIcon  = "device-notifier";
    }

Marco Martin's avatar
Marco Martin committed
332
333
334
335
    Timer {
        id: expandTimer
        interval: 250
        onTriggered: {
336
337
338
339
            // We don't show a UI for it, but there is a hidden option to not
            // show the popup on new device attachment if the user has added
            // the text "popupOnNewDevice=false" to their
            // plasma-org.kde.plasma.desktop-appletsrc file.
340
341
342
            if (Plasmoid.configuration.popupOnNewDevice) { // Bug 351592
                Plasmoid.expanded = true;
                Plasmoid.fullRepresentationItem.spontaneousOpen = true;
343
            }
344
        }
Marco Martin's avatar
Marco Martin committed
345
    }
346
347

}