@@ -0,0 +1,372 @@
1+/*
2+ * This file is part of system-settings
3+ *
4+ * Copyright (C) 2016 Canonical Ltd.
5+ *
6+ * Contact: Jonas G. Drange <jonas.drange@canonical.com>
7+ *
8+ * This program is free software: you can redistribute it and/or modify it
9+ * under the terms of the GNU General Public License version 3, as published
10+ * by the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful, but
13+ * WITHOUT ANY WARRANTY; without even the implied warranties of
14+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15+ * PURPOSE. See the GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License along
18+ * with this program. If not, see <http://www.gnu.org/licenses/>.
19+ *
20+ */
21+
22+import QtQuick 2.4
23+import QtQuick.Layouts 1.1
24+import Ubuntu.Components 1.3
25+import Ubuntu.Components.ListItems 1.3 as ListItems
26+import Ubuntu.Components.Themes 1.3
27+import Ubuntu.SystemSettings.Update 1.0
28+
29+ListItem {
30+ id: update
31+
32+ property int updateState // This is an Update::State
33+ property int kind // This is an Update::Kind
34+ property real size
35+ property string version
36+ property string downloadId
37+ property date updatedAt
38+ property bool launchable: false
39+
40+ property alias name: nameLabel.text
41+ property alias error: errorElementDetail.text
42+ property alias iconUrl: icon.source
43+ property alias progress: progressBar.value
44+
45+ signal retry()
46+ signal download()
47+ signal pause()
48+ signal resume()
49+ signal install()
50+ signal launch()
51+
52+ height: layout.height + (divider.visible ? divider.height : 0)
53+ Behavior on height {
54+ animation: UbuntuNumberAnimation {}
55+ }
56+
57+ SlotsLayout {
58+ id: layout
59+ mainSlot: ColumnLayout {
60+ spacing: units.gu(1)
61+ // Width the parent, minus the icon and some padding
62+ width: parent.width - (icon.width + (layout.padding.top * 3))
63+
64+ RowLayout {
65+ spacing: units.gu(2)
66+
67+ Label {
68+ id: nameLabel
69+ verticalAlignment: Text.AlignVCenter
70+ elide: Text.ElideMiddle
71+ Layout.fillWidth: true
72+ Layout.fillHeight: true
73+ }
74+
75+ Button {
76+ id: button
77+ objectName: "updateButton"
78+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
79+ visible: {
80+ switch (updateState) {
81+ case Update.StateInstalled:
82+ return launchable;
83+ default:
84+ return true;
85+ }
86+ }
87+ enabled: {
88+ switch(updateState) {
89+ case Update.StateAvailable:
90+ case Update.StateDownloading:
91+ case Update.StateDownloadingAutomatically:
92+ case Update.StateDownloadPaused:
93+ case Update.StateAutomaticDownloadPaused:
94+ case Update.StateInstallPaused:
95+ case Update.StateDownloaded:
96+ case Update.StateFailed:
97+ return true;
98+
99+ // Enabled if installed and a click app (can launch).
100+ case Update.StateInstalled:
101+ return kind === Update.KindClick;
102+
103+ case Update.StateInstalling:
104+ case Update.StateInstallingAutomatically:
105+ case Update.StateUnavailable:
106+ case Update.StateInstallFinished:
107+ case Update.StateQueuedForDownload:
108+ case Update.StateUnknown:
109+ default:
110+ return false;
111+ }
112+ }
113+ text: {
114+ switch(updateState) {
115+ case Update.StateUnknown:
116+ case Update.StateUnavailable:
117+ case Update.StateFailed:
118+ return i18n.tr("Retry");
119+
120+ case Update.StateAvailable:
121+ case Update.StateQueuedForDownload:
122+ if (update.kind === Update.KindClick) {
123+ return i18n.tr("Update");
124+ } else {
125+ return i18n.tr("Download");
126+ }
127+
128+ case Update.StateDownloadPaused:
129+ case Update.StateAutomaticDownloadPaused:
130+ case Update.StateInstallPaused:
131+ return i18n.tr("Resume");
132+
133+ case Update.StateDownloading:
134+ case Update.StateDownloadingAutomatically:
135+ case Update.StateInstalling:
136+ case Update.StateInstallingAutomatically:
137+ case Update.StateInstallFinished:
138+ return i18n.tr("Pause");
139+
140+ case Update.StateDownloaded:
141+ if (kind === Update.KindImage) {
142+ return i18n.tr("Install…");
143+ } else {
144+ return i18n.tr("Install");
145+ }
146+
147+ case Update.StateInstalled:
148+ return i18n.tr("Open");
149+
150+ default:
151+ console.error("Unknown update state", updateState);
152+ }
153+ }
154+
155+ onClicked: {
156+ switch (updateState) {
157+
158+ // Retries.
159+ case Update.StateUnknown:
160+ case Update.StateUnavailable:
161+ case Update.StateFailed:
162+ update.retry();
163+ break;
164+
165+ case Update.StateDownloadPaused:
166+ case Update.StateAutomaticDownloadPaused:
167+ case Update.StateInstallPaused:
168+ update.resume();
169+ break;
170+
171+ case Update.StateAvailable:
172+ if (kind === Update.KindClick) {
173+ update.install();
174+ } else {
175+ update.download();
176+ }
177+ break;
178+
179+ case Update.StateDownloaded:
180+ update.install();
181+ break;
182+
183+ case Update.StateDownloading:
184+ case Update.StateDownloadingAutomatically:
185+ case Update.StateInstalling:
186+ case Update.StateInstallingAutomatically:
187+ update.pause();
188+ break;
189+
190+ case Update.StateInstalled:
191+ update.launch();
192+ break;
193+ }
194+ }
195+ } // Button
196+ } // Name/button RowLayout
197+
198+ RowLayout {
199+ spacing: units.gu(2)
200+
201+ Label {
202+ id: downloadLabel
203+ objectName: "updateDownloadLabel"
204+
205+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
206+
207+ visible: text !== ""
208+
209+ fontSize: "small"
210+ text: {
211+ switch (updateState) {
212+
213+ case Update.StateInstalling:
214+ case Update.StateInstallingAutomatically:
215+ case Update.StateInstallPaused:
216+ return i18n.tr("Installing");
217+
218+ case Update.StateInstallPaused:
219+ case Update.StateDownloadPaused:
220+ return i18n.tr("Paused");
221+
222+ case Update.StateQueuedForDownload:
223+ return i18n.tr("Waiting to download");
224+
225+ case Update.StateDownloading:
226+ return i18n.tr("Downloading");
227+
228+ default:
229+ return "";
230+ }
231+ }
232+ }
233+
234+ Label {
235+ id: statusLabel
236+ objectName: "updateStatusLabel"
237+
238+ visible: text !== ""
239+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
240+ Layout.fillWidth: true
241+
242+ horizontalAlignment: Text.AlignRight
243+ fontSize: "small"
244+
245+ text: {
246+ switch (updateState) {
247+ case Update.StateAvailable:
248+ return Utilities.formatSize(size);
249+
250+ case Update.StateDownloading:
251+ case Update.StateDownloadingAutomatically:
252+ case Update.StateDownloadPaused:
253+ case Update.StateAutomaticDownloadPaused:
254+ var down = Utilities.formatSize((size / 100) * progress);
255+ var left = Utilities.formatSize(size);
256+ if (progress > 100) {
257+ return left;
258+ } else {
259+ /* TRANSLATORS: %1 is the human readable amount
260+ of bytes downloaded, and %2 is the total to be
261+ downloaded. */
262+ return i18n.tr("%1 of %2").arg(down).arg(left);
263+ }
264+
265+ case Update.StateDownloaded:
266+ return i18n.tr("Downloaded");
267+
268+ case Update.StateInstallFinished:
269+ return i18n.tr("Installed");
270+
271+ case Update.StateInstalled:
272+ /* TRANSLATORS: %1 is the date at which this
273+ update was applied. */
274+ return i18n.tr("Updated %1").arg(
275+ updatedAt.toLocaleDateString(Qt.locale(), "d MMMM")
276+ );
277+ default:
278+ return "";
279+ }
280+ }
281+
282+ /* Seems AlignRight causes a rendering issue that can be
283+ fixed by doing an explicit doLayout. */
284+ Component.onCompleted: doLayout()
285+ }
286+ }
287+
288+ Column {
289+ id: error
290+ objectName: "updateError"
291+ spacing: units.gu(1)
292+ height: childrenRect.height
293+ Layout.fillWidth: true
294+ visible: errorElementDetail.text
295+
296+ Label {
297+ id: errorElementTitle
298+ text: i18n.tr("Update failed")
299+ color: UbuntuColors.red
300+ }
301+
302+ Label {
303+ id: errorElementDetail
304+ anchors { left: parent.left; right: parent.right }
305+ fontSize: "small"
306+ wrapMode: Text.WrapAnywhere
307+ }
308+ } // Error column
309+
310+ ProgressBar {
311+ id: progressBar
312+ objectName: "updateProgressbar"
313+
314+ visible: {
315+ switch (updateState) {
316+ case Update.StateQueuedForDownload:
317+ case Update.StateDownloading:
318+ case Update.StateDownloadPaused:
319+ case Update.StateInstalling:
320+ case Update.StateInstallPaused:
321+ return true;
322+
323+ default:
324+ return false;
325+ }
326+ }
327+ Layout.maximumHeight: units.gu(0.5)
328+ Layout.fillWidth: true
329+ indeterminate: update.progress < 0 || update.progress > 100
330+ minimumValue: 0
331+ maximumValue: 100
332+ showProgressPercentage: false
333+
334+ Behavior on value {
335+ animation: UbuntuNumberAnimation {}
336+ }
337+ } // Progress bar
338+ } // Layout for the rest of the stuff
339+
340+ Item {
341+ SlotsLayout.position: SlotsLayout.Leading;
342+ width: units.gu(4)
343+ height: width
344+
345+ Image {
346+ id: icon
347+ visible: kind === Update.KindImage && !fallback.visible
348+ anchors.fill: parent
349+ asynchronous: true
350+ smooth: true
351+ mipmap: true
352+ }
353+
354+ UbuntuShape {
355+ id: shape
356+ visible: kind !== Update.KindImage && !fallback.visible
357+ anchors.fill: parent
358+ source: icon
359+ }
360+
361+ Image {
362+ id: fallback
363+ visible: icon.status === Image.Error
364+ source : Qt.resolvedUrl("/usr/share/icons/suru/apps/scalable/ubuntu-logo-symbolic.svg")
365+ anchors.fill: parent
366+ asynchronous: true
367+ smooth: true
368+ mipmap: true
369+ }
370+ }
371+ }
372+}