devicenotifier.qml 9.34 KB
Newer Older
1
2
3
4
/*
 *   Copyright 2011 Viranch Mehta <viranch.mehta@gmail.com>
 *   Copyright 2012 Jacopo De Simoi <wilderkde@gmail.com>
 *   Copyright 2014 David Edmundson <davidedmundson@kde.org>
5
 *   Copyright 2016 Kai Uwe Broulik <kde@privat.broulik.de>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as
 *   published by the Free Software Foundation; either version 2 or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

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.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras

30
31
import org.kde.kquickcontrolsaddons 2.0

32
33
Item {
    id: devicenotifier
34
35
36

    readonly property string automounterKcmName: "device_automounter_kcm"

37
38
39
40
41
42
43
44
45
    property string devicesType: {
        if (plasmoid.configuration.allDevices) {
            return "all"
        } else if (plasmoid.configuration.removableDevices) {
            return "removable"
        } else {
            return "nonRemovable"
        }
    }
46
47
48
    property string expandedDevice
    property string popupIcon: "device-notifier"

Marco Martin's avatar
Marco Martin committed
49
50
    property bool itemClicked: false
    property int currentIndex: -1
51
52
    property var connectedRemovables: []
    property int mountedRemovables: 0
Marco Martin's avatar
Marco Martin committed
53

54
55
56
57
58
    // 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
    property int pendingDelegateRemoval: 0

59
    Plasmoid.switchWidth: units.gridUnit * 10
Andreas Kainz's avatar
Andreas Kainz committed
60
    Plasmoid.switchHeight: units.gridUnit * 10
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

    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"
    }

    Plasmoid.status: (filterModel.count > 0 || pendingDelegateRemoval > 0) ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.PassiveStatus
83
84
85
86
87
88

    PlasmaCore.DataSource {
        id: hpSource
        engine: "hotplug"
        connectedSources: sources
        interval: 0
89
90
91
92
93
94
95
96

        onSourceAdded: {
            disconnectSource(source);
            connectSource(source);
        }
        onSourceRemoved: {
            disconnectSource(source);
        }
97
98
99
100
    }

    Plasmoid.compactRepresentation: PlasmaCore.IconItem {
        source: devicenotifier.popupIcon
Andreas Kainz's avatar
Andreas Kainz committed
101
102
        width: units.iconSizes.medium;
        height: units.iconSizes.medium;
103
        active: compactMouse.containsMouse
104
        MouseArea {
105
            id: compactMouse
106
            anchors.fill: parent
107
            hoverEnabled: true
108
109
110
            onClicked: plasmoid.expanded = !plasmoid.expanded
        }
    }
Marco Martin's avatar
Marco Martin committed
111
    Plasmoid.fullRepresentation: FullRepresentation {}
112
113
114
115
116

    PlasmaCore.DataSource {
        id: sdSource
        engine: "soliddevice"
        connectedSources: hpSource.sources
117
        interval: 0
118
119
        property string last
        onSourceAdded: {
120
121
            disconnectSource(source);
            connectSource(source);
122
            last = source;
123
            processLastDevice(true);
124
125
126
127
            if (data[source].Removable) {
                devicenotifier.connectedRemovables.push(source);
                devicenotifier.connectedRemovables = devicenotifier.connectedRemovables;
            }
128
129
130
131
132
133
        }

        onSourceRemoved: {
            if (expandedDevice == source) {
                expandedDevice = "";
            }
134
            disconnectSource(source);
135
136
137
138
139
            var index = devicenotifier.connectedRemovables.indexOf(source);
            if (index >= 0) {
                devicenotifier.connectedRemovables.splice(index, 1);
                devicenotifier.connectedRemovables = devicenotifier.connectedRemovables;
            }
140
141
142
        }

        onDataChanged: {
143
            processLastDevice(true);
144
145
146
147
148
149
            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
150
            if (counter !== devicenotifier.mountedRemovables) {
151
152
                devicenotifier.mountedRemovables = counter;
            }
153
154
155
156
157
158
159
        }

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

160
161
162
163
164
165
        function isViableDevice(udi) {
            if (devicesType === "all") {
                return true;
            }

            var device = data[udi];
166
167
168
169
            if (!device) {
                return false;
            }

170
171
172
173
            return (devicesType === "removable" && device.Removable)
                || (devicesType === "nonRemovable" && !device.Removable);
        }

174
        function processLastDevice(expand) {
175
176
177
178
            if (last) {
                if (isViableDevice(last)) {
                    if (expand && hpSource.data[last].added) {
                        expandDevice(last);
179
180
181
182
183
184
185
186
187
188
189
190
191
192
                    }
                    last = "";
                }
            }
        }
    }

    PlasmaCore.SortFilterModel {
        id: filterModel
        sourceModel: PlasmaCore.DataModel {
            dataSource: sdSource
        }
        filterRole: "Removable"
        filterRegExp: {
193
194
195
196
            if (devicesType === "removable") {
                return "true"
            } else if (devicesType === "nonRemovable") {
                return "false"
197
            } else {
198
                return ""
199
200
201
202
203
            }
        }
        sortRole: "Timestamp"
        sortOrder: Qt.DescendingOrder
    }
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218

    PlasmaCore.DataSource {
        id: statusSource
        engine: "devicenotifications"
        property string last
        property string lastUdi
        onSourceAdded: {
            last = source;
            disconnectSource(source);
            connectSource(source);
        }
        onSourceRemoved: disconnectSource(source)
        onDataChanged: {
            if (last) {
                lastUdi = data[last].udi
219
220
221

                if (sdSource.isViableDevice(lastUdi)) {
                    plasmoid.expanded = true
222
                    plasmoid.fullRepresentationItem.spontaneousOpen = true;
223
                }
224
225
226
227
228
229
230
231
232
            }
        }

        function clearMessage() {
            last = ""
            lastUdi = ""
        }
    }

233
    Component.onCompleted: {
Laurent Montel's avatar
Laurent Montel committed
234
        if (sdSource.connectedSources.count === 0) {
235
236
            Plasmoid.status = PlasmaCore.Types.PassiveStatus;
        }
237
238
239
240
241
242
243
244

        if (KCMShell.authorize(devicenotifier.automounterKcmName + ".desktop").length > 0) {
            plasmoid.setAction("openAutomounterKcm", i18nc("Open auto mounter kcm", "Configure Removable Devices"), "drive-removable-media")
        }
    }

    function action_openAutomounterKcm() {
        KCMShell.open([devicenotifier.automounterKcmName])
245
246
    }

Marco Martin's avatar
Marco Martin committed
247
248
249
250
251
252
253
254
255
256
257
258
259
260
    Plasmoid.onExpandedChanged: {
        popupEventSlot(plasmoid.expanded);
    }

    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;
            expandedDevice = "";
            devicenotifier.currentIndex = -1;
        }
    }

261
    function expandDevice(udi) {
262
263
264
265
        if (hpSource.data[udi]["actions"].length > 1) {
            expandedDevice = udi
        }

Marco Martin's avatar
Marco Martin committed
266
267
268
269
        // reset the property that lets us remember if an item was clicked
        // (versus only hovered) for autohide purposes
        devicenotifier.itemClicked = false;

270
        devicenotifier.popupIcon = "preferences-desktop-notification";
Marco Martin's avatar
Marco Martin committed
271
272
        //plasmoid.expanded = true;
        expandTimer.restart();
273
274
275
        popupIconTimer.restart()
    }

276
    function isMounted(udi) {
277
278
279
280
        if (!sdSource.data[udi]) {
            return false;
        }

281
        var types = sdSource.data[udi]["Device Types"];
282
283
        if (types.indexOf("Storage Access") >= 0) {
            return sdSource.data[udi]["Accessible"];
284
        }
285
286

        return (types.indexOf("Storage Volume") >= 0 && types.indexOf("OpticalDisc") >= 0)
287
288
289
290
    }

    Timer {
        id: popupIconTimer
291
        interval: 3000
292
293
294
        onTriggered: devicenotifier.popupIcon  = "device-notifier";
    }

Marco Martin's avatar
Marco Martin committed
295
296
297
298
    Timer {
        id: expandTimer
        interval: 250
        onTriggered: {
299
300
            if (plasmoid.configuration.popupOnNewDevice) { // Bug 351592
                plasmoid.expanded = true;
301
                plasmoid.fullRepresentationItem.spontaneousOpen = true;
302
            }
303
        }
Marco Martin's avatar
Marco Martin committed
304
    }
305
306

}