DeviceItem.qml 9.41 KB
Newer Older
1
2
3
/*
 *   Copyright 2011 Viranch Mehta <viranch.mehta@gmail.com>
 *   Copyright 2012 Jacopo De Simoi <wilderkde@gmail.com>
4
 *   Copyright 2016 Kai Uwe Broulik <kde@privat.broulik.de>
5
 *   Copyright 2020 Nate Graham <nate@kde.org>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 *   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
24
25
import QtQuick.Controls 2.12 as QQC2
import QtQml.Models 2.14
26

27
import org.kde.plasma.core 2.0 as PlasmaCore
28
29
import org.kde.plasma.extras 2.0 as PlasmaExtras

30
31
import org.kde.kquickcontrolsaddons 2.0

32
PlasmaExtras.ExpandableListItem {
33
    id: deviceItem
34

35
    property string udi
36
37
38
39
    readonly property int state: sdSource.data[udi] ? sdSource.data[udi].State : 0
    readonly property int operationResult: (model["Operation result"])

    readonly property bool isMounted: devicenotifier.isMounted(udi)
40
41
42
    readonly property bool hasMessage: statusSource.lastUdi == udi && statusSource.data[statusSource.last] ? true : false
    readonly property var message: hasMessage ? statusSource.data[statusSource.last] || ({}) : ({})

43
44
45
46
47
48
49
50
51
52
53
54
55
    readonly property double freeSpace: sdSource.data[udi] && sdSource.data[udi]["Free Space"] ? sdSource.data[udi]["Free Space"] : -1.0
    readonly property double totalSpace: sdSource.data[udi] && sdSource.data[udi]["Size"] ? sdSource.data[udi]["Size"] : -1.0
    property bool freeSpaceKnown: freeSpace > 0 && totalSpace > 0

    onOperationResultChanged: {
        if (!popupIconTimer.running) {
            if (operationResult == 1) {
                devicenotifier.popupIcon = "dialog-ok"
                popupIconTimer.restart()
            } else if (operationResult == 2) {
                devicenotifier.popupIcon = "dialog-error"
                popupIconTimer.restart()
            }
56
        }
57
58
    }

59
60
61
    onHasMessageChanged: {
        if (deviceItem.hasMessage) {
            messageHighlight.highlight(this)
62
63
64
        }
    }

65
66
    Connections {
        target: unmountAll
Konrad Materka's avatar
Konrad Materka committed
67
        function onClicked() {
68
            if (model["Removable"] && isMounted) {
69
70
71
72
73
                actionTriggered();
            }
        }
    }

74
75
76
    // this keeps the delegate around for 5 seconds after the device has been
    // removed in case there was a message, such as "you can now safely remove this"
    ListView.onRemove: {
77
        deviceItem.isEnabled = false
78
79
80
81
82
83
84

        if (deviceItem.hasMessage) {
            ListView.delayRemove = true
            keepDelegateTimer.restart()

            ++devicenotifier.pendingDelegateRemoval // QTBUG-50380
        }
85
86
        // Otherwise there are briefly multiple highlight effects
        devicenotifier.currentIndex = -1
87
88
89
90
    }

    Timer {
        id: keepDelegateTimer
91
        interval: 5000 // same interval as the auto hide / passive timer
92
93
94
95
96
97
        onTriggered: {
            deviceItem.ListView.delayRemove = false
            // otherwise the last message will show again when this device reappears
            statusSource.clearMessage()

            --devicenotifier.pendingDelegateRemoval // QTBUG-50380
98
        }
99
100
    }

101
102
103
104
    Timer {
        id: updateStorageSpaceTimer
        interval: 5000
        repeat: true
105
        running: isMounted && plasmoid.expanded
106
107
108
109
110
111
112
113
        triggeredOnStart: true     // Update the storage space as soon as we open the plasmoid
        onTriggered: {
            var service = sdSource.serviceForSource(udi);
            var operation = service.operationDescription("updateFreespace");
            service.startOperationCall(operation);
        }
    }

114
115
116
117
    Component {
        id: deviceActionComponent
        QQC2.Action { }
    }
118

119
120
121
122
    function actionTriggered() {
        var service
        var operationName
        var operation
123
124
        var wasMounted = isMounted;
        if (!sdSource.data[udi].Removable || !isMounted) {
125
126
127
128
129
            service = hpSource.serviceForSource(udi);
            operation = service.operationDescription('invokeAction');
            operation.predicate = "test-predicate-openinwindow.desktop";
        } else {
            service = sdSource.serviceForSource(udi);
130
            operation = service.operationDescription("unmount");
131
        }
132
133
134
135
136
        service.startOperationCall(operation);
        if (wasMounted) {
            deviceItem.collapse();
        }
    }
137
138


139
140
141
    // When there's no better icon available, show a placeholder icon instead
    // of nothing
    icon: sdSource.data[udi] == undefined ? "device-notifier" : sdSource.data[udi].Icon
142

143
144
145
146
147
148
149
    iconEmblem: {
        if (sdSource.data[udi] != undefined) {
            if (deviceItem.hasMessage) {
                if (deviceItem.message.solidError === 0) {
                    return "emblem-information"
                } else {
                    return "emblem-error"
150
                }
151
152
153
154
            } else if (deviceItem.state == 0 && Emblems && Emblems[0]) {
                return Emblems[0]
            } else {
                return ""
155
            }
156
157
158
        }
        return ""
    }
159

160
    title: sdSource.data[udi] == undefined ? "" : sdSource.data[udi].Description
161

162
163
164
165
166
167
168
    subtitle: {
        if (deviceItem.hasMessage) {
            return deviceItem.message.error
        }
        if (deviceItem.state == 0) {
            if (!hpSource.data[udi]) {
                return ""
169
            }
170
171
172
173
            if (freeSpaceKnown) {
                var freeSpaceText = sdSource.data[udi]["Free Space Text"]
                var totalSpaceText = sdSource.data[udi]["Size Text"]
                return i18nc("@info:status Free disk space", "%1 free of %2", freeSpaceText, totalSpaceText)
174
            }
175
176
177
178
179
180
181
            return ""
        } else if (deviceItem.state == 1) {
            return i18nc("Accessing is a less technical word for Mounting; translation should be short and mean \'Currently mounting this device\'", "Accessing...")
        } else {
            return i18nc("Removing is a less technical word for Unmounting; translation should be short and mean \'Currently unmounting this device\'", "Removing...")
        }
    }
182

183
    subtitleCanWrap: true
184

185
186
187
188
    // Color the subtitle red for disks with less than 5% free space
    subtitleColor: {
        if (freeSpaceKnown) {
            if (freeSpace / totalSpace <= 0.05) {
189
                return PlasmaCore.Theme.negativeTextColor
190
            }
191
        }
192
        return PlasmaCore.Theme.textColor
193
    }
194

195
196
    defaultActionButtonAction: QQC2.Action {
        icon.name: {
197
            if (!sdSource.data[udi].Removable) {
198
199
                return "document-open-folder"
            } else {
200
                return isMounted ? "media-eject" : "document-open-folder"
201
202
            }
        }
203
        text: {
204
            if (!sdSource.data[udi].Removable) {
205
206
207
208
                return i18n("Open in File Manager")
            } else {
                var types = model["Device Types"];
                if (!isMounted) {
209
                    return i18n("Mount and Open")
210
211
212
213
214
                } else if (types && types.indexOf("OpticalDisc") !== -1) {
                    return i18n("Eject")
                } else {
                    return i18n("Safely remove")
                }
215
            }
216
217
218
        }
        onTriggered: actionTriggered()
    }
219

220
221
222
223
224
225
226
227
228
    isBusy: deviceItem.state != 0

    // We need a JS array full of QQC2 actions; this Instantiator creates them
    // from the actions list of the data source
    Instantiator {
        model: hpSource.data[udi] ? hpSource.data[udi].actions : []
        delegate: QQC2.Action {
            text: modelData.text
            icon.name: modelData.icon
229
230
231
232
233
234
235
236
237
            // We only want to show the "Show in file manager" action for
            // mounted removable disks (for everything else, this action is
            // already the primary one shown on the list item)
            enabled: {
                if (modelData.predicate != "test-predicate-openinwindow.desktop") {
                    return true;
                }
                return sdSource.data[udi].Removable && deviceItem.isMounted;
            }
238
239
240
241
242
243
            onTriggered: {
                var service = hpSource.serviceForSource(udi);
                var operation = service.operationDescription('invokeAction');
                operation.predicate = modelData.predicate;
                service.startOperationCall(operation);
                devicenotifier.currentIndex = -1;
244
245
            }
        }
246
        onObjectAdded: contextualActionsModel.push(object)
247
    }
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268

    // "Mount" action that does not open it in the file manager
    QQC2.Action {
        id: mountWithoutOpeningAction

        text: i18n("Mount")
        icon.name: "media-mount"

        // Only show for unmounted removable devices
        enabled: sdSource.data[udi].Removable && !deviceItem.isMounted

        onTriggered: {
            var service = sdSource.serviceForSource(udi);
            var operation = service.operationDescription("mount");
            service.startOperationCall(operation);
        }
    }

    Component.onCompleted: {
        contextualActionsModel.push(mountWithoutOpeningAction);
    }
269
}